diff options
Diffstat (limited to 'sys/dev/uart')
-rw-r--r-- | sys/dev/uart/uart.h | 82 | ||||
-rw-r--r-- | sys/dev/uart/uart_bus.h | 204 | ||||
-rw-r--r-- | sys/dev/uart/uart_bus_acpi.c | 83 | ||||
-rw-r--r-- | sys/dev/uart/uart_bus_ebus.c | 85 | ||||
-rw-r--r-- | sys/dev/uart/uart_bus_isa.c | 167 | ||||
-rw-r--r-- | sys/dev/uart/uart_bus_pci.c | 118 | ||||
-rw-r--r-- | sys/dev/uart/uart_bus_puc.c | 94 | ||||
-rw-r--r-- | sys/dev/uart/uart_core.c | 452 | ||||
-rw-r--r-- | sys/dev/uart/uart_cpu.h | 115 | ||||
-rw-r--r-- | sys/dev/uart/uart_cpu_alpha.c | 111 | ||||
-rw-r--r-- | sys/dev/uart/uart_cpu_amd64.c | 92 | ||||
-rw-r--r-- | sys/dev/uart/uart_cpu_i386.c | 92 | ||||
-rw-r--r-- | sys/dev/uart/uart_cpu_ia64.c | 139 | ||||
-rw-r--r-- | sys/dev/uart/uart_cpu_sparc64.c | 156 | ||||
-rw-r--r-- | sys/dev/uart/uart_dev_ns8250.c | 726 | ||||
-rw-r--r-- | sys/dev/uart/uart_dev_ns8250.h | 153 | ||||
-rw-r--r-- | sys/dev/uart/uart_dev_sab82532.c | 670 | ||||
-rw-r--r-- | sys/dev/uart/uart_dev_sab82532.h | 320 | ||||
-rw-r--r-- | sys/dev/uart/uart_dev_z8530.c | 512 | ||||
-rw-r--r-- | sys/dev/uart/uart_dev_z8530.h | 252 | ||||
-rw-r--r-- | sys/dev/uart/uart_if.m | 138 | ||||
-rw-r--r-- | sys/dev/uart/uart_tty.c | 569 |
22 files changed, 5330 insertions, 0 deletions
diff --git a/sys/dev/uart/uart.h b/sys/dev/uart/uart.h new file mode 100644 index 0000000..1ea90c0 --- /dev/null +++ b/sys/dev/uart/uart.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2003 Marcel Moolenaar + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _DEV_UART_H_ +#define _DEV_UART_H_ + +/* + * Bus access structure. This structure holds the minimum information needed + * to access the UART. The rclk field, although not important to actually + * access the UART, is important for baudrate programming, delay loops and + * other timing related computations. + */ +struct uart_bas { + bus_space_tag_t bst; + bus_space_handle_t bsh; + u_int regshft; + u_int rclk; +}; + +#define uart_regofs(bas, reg) ((reg) << (bas)->regshft) + +#define uart_getreg(bas, reg) \ + bus_space_read_1((bas)->bst, (bas)->bsh, uart_regofs(bas, reg)) +#define uart_setreg(bas, reg, value) \ + bus_space_write_1((bas)->bst, (bas)->bsh, uart_regofs(bas, reg), value) + +/* 16-bit I/O (e.g. to divisor latch) */ +#define uart_getdreg(bas, reg) \ + bus_space_read_2((bas)->bst, (bas)->bsh, uart_regofs(bas, reg)) +#define uart_setdreg(bas, reg, value) \ + bus_space_write_2((bas)->bst, (bas)->bsh, uart_regofs(bas, reg), value) + +/* + * XXX we don't know the length of the bus space address range in use by + * the UART. Since barriers don't use the length field currently, we put + * a zero there for now. + */ +#define uart_barrier(bas) \ + bus_space_barrier((bas)->bst, (bas)->bsh, 0, 0, \ + BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE) + +/* + * Device flags. + */ +#define UART_FLAGS_CONSOLE(f) ((f) & 0x10) +#define UART_FLAGS_DBGPORT(f) ((f) & 0x80) + +/* + * Data parity values (magical numbers related to ns8250). + */ +#define UART_PARITY_NONE 0 +#define UART_PARITY_ODD 1 +#define UART_PARITY_EVEN 3 +#define UART_PARITY_MARK 5 +#define UART_PARITY_SPACE 7 + +#endif /* _DEV_UART_H_ */ diff --git a/sys/dev/uart/uart_bus.h b/sys/dev/uart/uart_bus.h new file mode 100644 index 0000000..71aa1be --- /dev/null +++ b/sys/dev/uart/uart_bus.h @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2003 Marcel Moolenaar + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _DEV_UART_BUS_H_ +#define _DEV_UART_BUS_H_ + +/* Drain and flush targets. */ +#define UART_DRAIN_RECEIVER 0x0001 +#define UART_DRAIN_TRANSMITTER 0x0002 +#define UART_FLUSH_RECEIVER UART_DRAIN_RECEIVER +#define UART_FLUSH_TRANSMITTER UART_DRAIN_TRANSMITTER + +/* + * Interrupt sources (in priority order). See also uart_core.c + * Note that the low order 16 bits are used to pass modem signals + * from the hardware interrupt handler to the software interrupt + * handler. See UART_SIG_* and UART_SIGMASK_* below. + */ +#define UART_IPEND_OVERRUN 0x010000 +#define UART_IPEND_BREAK 0x020000 +#define UART_IPEND_RXREADY 0x040000 +#define UART_IPEND_SIGCHG 0x080000 +#define UART_IPEND_TXIDLE 0x100000 + +#define UART_IPEND_MASK 0x1f0000 +#define UART_IPEND_SIGMASK 0x00ffff + +/* Received character status bits. */ +#define UART_STAT_BREAK 0x0100 +#define UART_STAT_FRAMERR 0x0200 +#define UART_STAT_OVERRUN 0x0400 +#define UART_STAT_PARERR 0x0800 + +/* Modem and line signals. */ +#define UART_SIG_DTR 0x0001 +#define UART_SIG_RTS 0x0002 +#define UART_SIG_DSR 0x0004 +#define UART_SIG_CTS 0x0008 +#define UART_SIG_DCD 0x0010 +#define UART_SIG_RI 0x0020 +#define UART_SIG_DDTR 0x0100 +#define UART_SIG_DRTS 0x0200 +#define UART_SIG_DDSR 0x0400 +#define UART_SIG_DCTS 0x0800 +#define UART_SIG_DDCD 0x1000 +#define UART_SIG_DRI 0x2000 + +#define UART_SIGMASK_DTE 0x0003 +#define UART_SIGMASK_DCE 0x003c +#define UART_SIGMASK_STATE 0x003f +#define UART_SIGMASK_DELTA 0x3f00 + +/* UART_IOCTL() requests */ +#define UART_IOCTL_BREAK 1 +#define UART_IOCTL_IFLOW 2 +#define UART_IOCTL_OFLOW 3 + +/* + * UART class & instance (=softc) + */ +struct uart_class { + KOBJ_CLASS_FIELDS; + u_int uc_range; /* Bus space address range. */ + u_int uc_rclk; /* Default rclk for this device. */ +}; + +extern struct uart_class uart_ns8250_class; +extern struct uart_class uart_sab82532_class; +extern struct uart_class uart_z8530_class; + +struct uart_softc { + KOBJ_FIELDS; + struct uart_class *sc_class; + struct uart_bas sc_bas; + device_t sc_dev; + + struct resource *sc_rres; /* Register resource. */ + int sc_rrid; + int sc_rtype; /* SYS_RES_{IOPORT|MEMORY}. */ + struct resource *sc_ires; /* Interrupt resource. */ + void *sc_icookie; + int sc_irid; + + int sc_callout:1; /* This UART is opened for callout. */ + int sc_fastintr:1; /* This UART uses fast interrupts. */ + int sc_hasfifo:1; /* This UART has FIFOs. */ + int sc_hwiflow:1; /* This UART has HW input flow ctl. */ + int sc_hwoflow:1; /* This UART has HW output flow ctl. */ + int sc_leaving:1; /* This UART is going away. */ + int sc_opened:1; /* This UART is open for business. */ + int sc_polled:1; /* This UART has no interrupts. */ + int sc_txbusy:1; /* This UART is transmitting. */ + + struct uart_devinfo *sc_sysdev; /* System device (or NULL). */ + + int sc_altbrk; /* State for alt break sequence. */ + uint32_t sc_hwsig; /* Signal state. Used by HW driver. */ + + /* Receiver data. */ + uint16_t *sc_rxbuf; + int sc_rxbufsz; + int sc_rxput; + int sc_rxget; + int sc_rxfifosz; /* Size of RX FIFO. */ + + /* Transmitter data. */ + uint8_t *sc_txbuf; + int sc_txdatasz; + int sc_txfifosz; /* Size of TX FIFO and buffer. */ + + /* Upper layer data. */ + void *sc_softih; + uint32_t sc_ttypend; + union { + /* TTY specific data. */ + struct { + dev_t si[2]; /* We have 2 device special files. */ + struct tty *tp; + } u_tty; + /* Keyboard specific data. */ + struct { + } u_kbd; + } sc_u; +}; + +extern devclass_t uart_devclass; +extern char uart_driver_name[]; + +int uart_bus_attach(device_t dev); +int uart_bus_detach(device_t dev); +int uart_bus_probe(device_t dev, int regshft, int rclk, int rid); + +int uart_tty_attach(struct uart_softc *); +int uart_tty_detach(struct uart_softc *); +void uart_tty_intr(void *arg); + +/* + * Receive buffer operations. + */ +static __inline int +uart_rx_empty(struct uart_softc *sc) +{ + return ((sc->sc_rxget == sc->sc_rxput) ? 1 : 0); +} + +static __inline int +uart_rx_full(struct uart_softc *sc) +{ + return ((sc->sc_rxput + 1 < sc->sc_rxbufsz) + ? (sc->sc_rxput + 1 == sc->sc_rxget) : (sc->sc_rxget == 0)); +} + +static __inline int +uart_rx_get(struct uart_softc *sc) +{ + int ptr, xc; + + ptr = sc->sc_rxget; + if (ptr == sc->sc_rxput) + return (-1); + xc = sc->sc_rxbuf[ptr++]; + sc->sc_rxget = (ptr < sc->sc_rxbufsz) ? ptr : 0; + return (xc); +} + +static __inline int +uart_rx_put(struct uart_softc *sc, int xc) +{ + int ptr; + + ptr = (sc->sc_rxput + 1 < sc->sc_rxbufsz) ? sc->sc_rxput + 1 : 0; + if (ptr == sc->sc_rxget) + return (ENOSPC); + sc->sc_rxbuf[sc->sc_rxput] = xc; + sc->sc_rxput = ptr; + return (0); +} + +#endif /* _DEV_UART_BUS_H_ */ diff --git a/sys/dev/uart/uart_bus_acpi.c b/sys/dev/uart/uart_bus_acpi.c new file mode 100644 index 0000000..ac75f0c --- /dev/null +++ b/sys/dev/uart/uart_bus_acpi.c @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2001 M. Warner Losh. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/conf.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <machine/bus.h> +#include <sys/rman.h> +#include <machine/resource.h> + +#include <isa/isavar.h> + +#include <dev/uart/uart.h> +#include <dev/uart/uart_bus.h> + +static int uart_acpi_probe(device_t dev); + +static device_method_t uart_acpi_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, uart_acpi_probe), + DEVMETHOD(device_attach, uart_bus_attach), + DEVMETHOD(device_detach, uart_bus_detach), + { 0, 0 } +}; + +static driver_t uart_acpi_driver = { + uart_driver_name, + uart_acpi_methods, + sizeof(struct uart_softc), +}; + +static struct isa_pnp_id acpi_ns8250_ids[] = { + {0x0005d041, "Standard PC COM port"}, /* PNP0500 */ + {0x0105d041, "16550A-compatible COM port"}, /* PNP0501 */ + {0} +}; + +static int +uart_acpi_probe(device_t dev) +{ + struct uart_softc *sc; + device_t parent; + + parent = device_get_parent(dev); + sc = device_get_softc(dev); + + if (!ISA_PNP_PROBE(parent, dev, acpi_ns8250_ids)) { + sc->sc_class = &uart_ns8250_class; + return (uart_bus_probe(dev, 0, 0, 0)); + } + + /* Add checks for non-ns8250 IDs here. */ + return (ENXIO); +} + +DRIVER_MODULE(uart, acpi, uart_acpi_driver, uart_devclass, 0, 0); diff --git a/sys/dev/uart/uart_bus_ebus.c b/sys/dev/uart/uart_bus_ebus.c new file mode 100644 index 0000000..0eab11f --- /dev/null +++ b/sys/dev/uart/uart_bus_ebus.c @@ -0,0 +1,85 @@ +/*- + * Copyright (c) 2001 by Thomas Moestl <tmm@FreeBSD.org>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/kernel.h> +#include <machine/bus.h> +#include <sys/rman.h> +#include <machine/resource.h> + +#include <dev/ofw/openfirm.h> +#include <sparc64/ebus/ebusvar.h> + +#include <dev/uart/uart.h> +#include <dev/uart/uart_bus.h> +#include <dev/uart/uart_cpu.h> + +static int uart_ebus_probe(device_t dev); + +static device_method_t uart_ebus_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, uart_ebus_probe), + DEVMETHOD(device_attach, uart_bus_attach), + DEVMETHOD(device_detach, uart_bus_detach), + { 0, 0 } +}; + +static driver_t uart_ebus_driver = { + uart_driver_name, + uart_ebus_methods, + sizeof(struct uart_softc), +}; + +static int +uart_ebus_probe(device_t dev) +{ + const char *nm; + struct uart_softc *sc; + int error; + + sc = device_get_softc(dev); + sc->sc_class = NULL; + + nm = ebus_get_name(dev); + if (!strcmp(nm, "su")) { + sc->sc_class = &uart_ns8250_class; + return (uart_bus_probe(dev, 0, 0, 0)); + } + if (!strcmp(nm, "se")) { + sc->sc_class = &uart_sab82532_class; + error = uart_bus_probe(dev, 0, 0, 0); + return ((error) ? error : -1); + } + + return (ENXIO); +} + +DRIVER_MODULE(uart, ebus, uart_ebus_driver, uart_devclass, 0, 0); diff --git a/sys/dev/uart/uart_bus_isa.c b/sys/dev/uart/uart_bus_isa.c new file mode 100644 index 0000000..784d7f9 --- /dev/null +++ b/sys/dev/uart/uart_bus_isa.c @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2001 M. Warner Losh. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/conf.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <machine/bus.h> +#include <sys/rman.h> +#include <machine/resource.h> + +#include <isa/isavar.h> + +#include <dev/uart/uart.h> +#include <dev/uart/uart_bus.h> + +static int uart_isa_probe(device_t dev); + +static device_method_t uart_isa_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, uart_isa_probe), + DEVMETHOD(device_attach, uart_bus_attach), + DEVMETHOD(device_detach, uart_bus_detach), + { 0, 0 } +}; + +static driver_t uart_isa_driver = { + uart_driver_name, + uart_isa_methods, + sizeof(struct uart_softc), +}; + +static struct isa_pnp_id isa_ns8250_ids[] = { + {0x0005d041, "Standard PC COM port"}, /* PNP0500 */ + {0x0105d041, "16550A-compatible COM port"}, /* PNP0501 */ + {0x0205d041, "Multiport serial device (non-intelligent 16550)"}, /* PNP0502 */ + {0x1005d041, "Generic IRDA-compatible device"}, /* PNP0510 */ + {0x1105d041, "Generic IRDA-compatible device"}, /* PNP0511 */ + /* Devices that do not have a compatid */ + {0x12206804, NULL}, /* ACH2012 - 5634BTS 56K Video Ready Modem */ + {0x7602a904, NULL}, /* AEI0276 - 56K v.90 Fax Modem (LKT) */ + {0x00007905, NULL}, /* AKY0000 - 56K Plug&Play Modem */ + {0x21107905, NULL}, /* AKY1021 - 56K Plug&Play Modem */ + {0x01405407, NULL}, /* AZT4001 - AZT3000 PnP SOUND DEVICE, MODEM */ + {0x56039008, NULL}, /* BDP0356 - Best Data 56x2 */ + {0x56159008, NULL}, /* BDP1556 - B.D. Smart One 56SPS,Voice Modem*/ + {0x36339008, NULL}, /* BDP3336 - Best Data Prods. 336F */ + {0x0014490a, NULL}, /* BRI1400 - Boca 33.6 PnP */ + {0x0015490a, NULL}, /* BRI1500 - Internal Fax Data */ + {0x0034490a, NULL}, /* BRI3400 - Internal ACF Modem */ + {0x0094490a, NULL}, /* BRI9400 - Boca K56Flex PnP */ + {0x00b4490a, NULL}, /* BRIB400 - Boca 56k PnP */ + {0x0030320d, NULL}, /* CIR3000 - Cirrus Logic V43 */ + {0x0100440e, NULL}, /* CRD0001 - Cardinal MVP288IV ? */ + {0x01308c0e, NULL}, /* CTL3001 - Creative Labs Phoneblaster */ + {0x36033610, NULL}, /* DAV0336 - DAVICOM 336PNP MODEM */ + {0x01009416, NULL}, /* ETT0001 - E-Tech Bullet 33k6 PnP */ + {0x0000aa1a, NULL}, /* FUJ0000 - FUJITSU Modem 33600 PNP/I2 */ + {0x1200c31e, NULL}, /* GVC0012 - VF1128HV-R9 (win modem?) */ + {0x0303c31e, NULL}, /* GVC0303 - MaxTech 33.6 PnP D/F/V */ + {0x0505c31e, NULL}, /* GVC0505 - GVC 56k Faxmodem */ + {0x0116c31e, NULL}, /* GVC1601 - Rockwell V.34 Plug & Play Modem */ + {0x0050c31e, NULL}, /* GVC5000 - some GVC modem */ + {0x3800f91e, NULL}, /* GWY0038 - Telepath with v.90 */ + {0x9062f91e, NULL}, /* GWY6290 - Telepath with x2 Technology */ + {0x8100e425, NULL}, /* IOD0081 - I-O DATA DEVICE,INC. IFML-560 */ + {0x71004d24, NULL}, /* IBM0071 - IBM ThinkPad 240 IrDA controller*/ + {0x21002534, NULL}, /* MAE0021 - Jetstream Int V.90 56k Voice Series 2*/ + {0x0000f435, NULL}, /* MOT0000 - Motorola ModemSURFR 33.6 Intern */ + {0x5015f435, NULL}, /* MOT1550 - Motorola ModemSURFR 56K Modem */ + {0xf015f435, NULL}, /* MOT15F0 - Motorola VoiceSURFR 56K Modem */ + {0x6045f435, NULL}, /* MOT4560 - Motorola ? */ + {0x61e7a338, NULL}, /* NECE761 - 33.6Modem */ + {0x0160633a, NULL}, /* NSC6001 - National Semi's IrDA Controller*/ + {0x08804f3f, NULL}, /* OZO8008 - Zoom (33.6k Modem) */ + {0x0f804f3f, NULL}, /* OZO800f - Zoom 2812 (56k Modem) */ + {0x39804f3f, NULL}, /* OZO8039 - Zoom 56k flex */ + {0x00914f3f, NULL}, /* OZO9100 - Zoom 2919 (K56 Faxmodem) */ + {0x3024a341, NULL}, /* PMC2430 - Pace 56 Voice Internal Modem */ + {0x1000eb49, NULL}, /* ROK0010 - Rockwell ? */ + {0x1200b23d, NULL}, /* RSS0012 - OMRON ME5614ISA */ + {0x5002734a, NULL}, /* RSS0250 - 5614Jx3(G) Internal Modem */ + {0x6202734a, NULL}, /* RSS0262 - 5614Jx3[G] V90+K56Flex Modem */ + {0x1010104d, NULL}, /* SHP1010 - Rockwell 33600bps Modem */ + {0x10f0a34d, NULL}, /* SMCF010 - SMC IrCC*/ + {0xc100ad4d, NULL}, /* SMM00C1 - Leopard 56k PnP */ + {0x9012b04e, NULL}, /* SUP1290 - Supra ? */ + {0x1013b04e, NULL}, /* SUP1310 - SupraExpress 336i PnP */ + {0x8013b04e, NULL}, /* SUP1380 - SupraExpress 288i PnP Voice */ + {0x8113b04e, NULL}, /* SUP1381 - SupraExpress 336i PnP Voice */ + {0x5016b04e, NULL}, /* SUP1650 - Supra 336i Sp Intl */ + {0x7016b04e, NULL}, /* SUP1670 - Supra 336i V+ Intl */ + {0x7420b04e, NULL}, /* SUP2070 - Supra ? */ + {0x8020b04e, NULL}, /* SUP2080 - Supra ? */ + {0x8420b04e, NULL}, /* SUP2084 - SupraExpress 56i PnP */ + {0x7121b04e, NULL}, /* SUP2171 - SupraExpress 56i Sp? */ + {0x8024b04e, NULL}, /* SUP2480 - Supra ? */ + {0x01007256, NULL}, /* USR0001 - U.S. Robotics Inc., Sportster W */ + {0x02007256, NULL}, /* USR0002 - U.S. Robotics Inc. Sportster 33. */ + {0x04007256, NULL}, /* USR0004 - USR Sportster 14.4k */ + {0x06007256, NULL}, /* USR0006 - USR Sportster 33.6k */ + {0x11007256, NULL}, /* USR0011 - USR ? */ + {0x01017256, NULL}, /* USR0101 - USR ? */ + {0x30207256, NULL}, /* USR2030 - U.S.Robotics Inc. Sportster 560 */ + {0x50207256, NULL}, /* USR2050 - U.S.Robotics Inc. Sportster 33. */ + {0x70207256, NULL}, /* USR2070 - U.S.Robotics Inc. Sportster 560 */ + {0x30307256, NULL}, /* USR3030 - U.S. Robotics 56K FAX INT */ + {0x31307256, NULL}, /* USR3031 - U.S. Robotics 56K FAX INT */ + {0x50307256, NULL}, /* USR3050 - U.S. Robotics 56K FAX INT */ + {0x70307256, NULL}, /* USR3070 - U.S. Robotics 56K Voice INT */ + {0x90307256, NULL}, /* USR3090 - USR ? */ + {0x70917256, NULL}, /* USR9170 - U.S. Robotics 56K FAX INT */ + {0x90917256, NULL}, /* USR9190 - USR 56k Voice INT */ + {0x04f0235c, NULL}, /* WACF004 - Wacom Tablet PC Screen*/ + {0x0300695c, NULL}, /* WCI0003 - Fax/Voice/Modem/Speakphone/Asvd */ + {0x01a0896a, NULL}, /* ZTIA001 - Zoom Internal V90 Faxmodem */ + {0x61f7896a, NULL}, /* ZTIF761 - Zoom ComStar 33.6 */ + {0} +}; + +static int +uart_isa_probe(device_t dev) +{ + struct uart_softc *sc; + device_t parent; + + parent = device_get_parent(dev); + sc = device_get_softc(dev); + + if (!ISA_PNP_PROBE(parent, dev, isa_ns8250_ids)) { + sc->sc_class = &uart_ns8250_class; + return (uart_bus_probe(dev, 0, 0, 0)); + } + + /* Add checks for non-ns8250 IDs here. */ + + sc->sc_class = &uart_ns8250_class; + return (uart_bus_probe(dev, 0, 0, 0)); +} + +DRIVER_MODULE(uart, isa, uart_isa_driver, uart_devclass, 0, 0); diff --git a/sys/dev/uart/uart_bus_pci.c b/sys/dev/uart/uart_bus_pci.c new file mode 100644 index 0000000..3555e5f --- /dev/null +++ b/sys/dev/uart/uart_bus_pci.c @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2001 M. Warner Losh. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/conf.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <machine/bus.h> +#include <sys/rman.h> +#include <machine/resource.h> + +#include <dev/pci/pcivar.h> + +#include <dev/uart/uart.h> +#include <dev/uart/uart_bus.h> + +static int uart_pci_probe(device_t dev); + +static device_method_t uart_pci_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, uart_pci_probe), + DEVMETHOD(device_attach, uart_bus_attach), + DEVMETHOD(device_detach, uart_bus_detach), + { 0, 0 } +}; + +static driver_t uart_pci_driver = { + uart_driver_name, + uart_pci_methods, + sizeof(struct uart_softc), +}; + +struct pci_id { + uint32_t type; + const char *desc; + int rid; +}; + +static struct pci_id pci_ns8250_ids[] = { + { 0x100812b9, "3COM PCI FaxModem", 0x10 }, + { 0x2000131f, "CyberSerial (1-port) 16550", 0x10 }, + { 0x01101407, "Koutech IOFLEX-2S PCI Dual Port Serial", 0x10 }, + { 0x01111407, "Koutech IOFLEX-2S PCI Dual Port Serial", 0x10 }, + { 0x048011c1, "Lucent kermit based PCI Modem", 0x14 }, + { 0x95211415, "Oxford Semiconductor PCI Dual Port Serial", 0x10 }, + { 0x7101135e, "SeaLevel Ultra 530.PCI Single Port Serial", 0x18 }, + { 0x0000151f, "SmartLink 5634PCV SurfRider", 0x10 }, + { 0x0103115d, "Xircom Cardbus modem", 0x10 }, + { 0x98459710, "Netmos Nm9845 PCI Bridge with Dual UART", 0x10 }, + { 0x01c0135c, "Quatech SSCLP-200/300", 0x18 + /* + * NB: You must mount the "SPAD" jumper to correctly detect + * the FIFO on the UART. Set the options on the jumpers, + * we do not support the extra registers on the Quatech. + */ + }, + { 0x00000000, NULL, 0 } +}; + +static struct pci_id * +uart_pci_match(uint32_t type, struct pci_id *id) +{ + + while (id->type && id->type != type) + id++; + return ((id->type) ? id : NULL); +} + +static int +uart_pci_probe(device_t dev) +{ + struct uart_softc *sc; + struct pci_id *id; + + sc = device_get_softc(dev); + + id = uart_pci_match(pci_get_devid(dev), pci_ns8250_ids); + if (id != NULL) { + sc->sc_class = &uart_ns8250_class; + goto match; + } + /* Add checks for non-ns8250 IDs here. */ + return (ENXIO); + + match: + if (id->desc) + device_set_desc(dev, id->desc); + return (uart_bus_probe(dev, 0, 0, id->rid)); +} + +DRIVER_MODULE(uart, pci, uart_pci_driver, uart_devclass, 0, 0); +DRIVER_MODULE(uart, cardbus, uart_pci_driver, uart_devclass, 0, 0); diff --git a/sys/dev/uart/uart_bus_puc.c b/sys/dev/uart/uart_bus_puc.c new file mode 100644 index 0000000..94b042e --- /dev/null +++ b/sys/dev/uart/uart_bus_puc.c @@ -0,0 +1,94 @@ +/*- + * Copyright (c) 2002 JF Hay. All rights reserved. + * Copyright (c) 2001 M. Warner Losh. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/conf.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <machine/bus.h> +#include <sys/rman.h> +#include <machine/resource.h> + +#include <dev/pci/pcivar.h> +#include <dev/puc/pucvar.h> + +#include <dev/uart/uart.h> +#include <dev/uart/uart_bus.h> + +static int uart_puc_probe(device_t dev); + +static device_method_t uart_puc_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, uart_puc_probe), + DEVMETHOD(device_attach, uart_bus_attach), + DEVMETHOD(device_detach, uart_bus_detach), + { 0, 0 } +}; + +static driver_t uart_puc_driver = { + uart_driver_name, + uart_puc_methods, + sizeof(struct uart_softc), +}; + +static int +uart_puc_probe(device_t dev) +{ + device_t parent; + struct uart_softc *sc; + uintptr_t rclk, regshft, type; + + parent = device_get_parent(dev); + sc = device_get_softc(dev); + + if (BUS_READ_IVAR(parent, dev, PUC_IVAR_SUBTYPE, &type)) + return (ENXIO); + switch (type) { + case PUC_PORT_UART_NS8250: + sc->sc_class = &uart_ns8250_class; + break; + case PUC_PORT_UART_SAB82532: + sc->sc_class = &uart_sab82532_class; + break; + case PUC_PORT_UART_Z8530: + sc->sc_class = &uart_z8530_class; + break; + default: + return (ENXIO); + } + + if (BUS_READ_IVAR(parent, dev, PUC_IVAR_FREQ, &rclk)) + rclk = 0; + if (BUS_READ_IVAR(parent, dev, PUC_IVAR_REGSHFT, ®shft)) + regshft = 0; + return (uart_bus_probe(dev, regshft, rclk, 0)); +} + +DRIVER_MODULE(uart, puc, uart_puc_driver, uart_devclass, 0, 0); diff --git a/sys/dev/uart/uart_core.c b/sys/dev/uart/uart_core.c new file mode 100644 index 0000000..450313a --- /dev/null +++ b/sys/dev/uart/uart_core.c @@ -0,0 +1,452 @@ +/* + * Copyright (c) 2003 Marcel Moolenaar + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#ifndef KLD_MODULE +#include "opt_comconsole.h" +#include "opt_ddb.h" +#endif + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/conf.h> +#include <sys/cons.h> +#include <sys/fcntl.h> +#include <sys/interrupt.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/queue.h> +#include <sys/reboot.h> +#include <machine/bus.h> +#include <sys/rman.h> +#include <sys/termios.h> +#include <sys/tty.h> +#include <machine/resource.h> +#include <machine/stdarg.h> + +#include <ddb/ddb.h> + +#include <dev/uart/uart.h> +#include <dev/uart/uart_bus.h> +#include <dev/uart/uart_cpu.h> + +#include "uart_if.h" + +devclass_t uart_devclass; +char uart_driver_name[] = "uart"; + +SLIST_HEAD(uart_devinfo_list, uart_devinfo) uart_sysdevs = + SLIST_HEAD_INITIALIZER(uart_sysdevs); + +MALLOC_DEFINE(M_UART, "UART", "UART driver"); + +void +uart_add_sysdev(struct uart_devinfo *di) +{ + SLIST_INSERT_HEAD(&uart_sysdevs, di, next); +} + +/* + * A break condition has been detected. We treat the break condition as + * a special case that should not happen during normal operation. When + * the break condition is to be passed to higher levels in the form of + * a NUL character, we really want the break to be in the right place in + * the input stream. The overhead to achieve that is not in relation to + * the exceptional nature of the break condition, so we permit ourselves + * to be sloppy. + */ +static void +uart_intr_break(struct uart_softc *sc) +{ + +#if defined(DDB) && defined(BREAK_TO_DEBUGGER) + if (sc->sc_sysdev != NULL && sc->sc_sysdev->type == UART_DEV_CONSOLE) { + breakpoint(); + return; + } +#endif + if (sc->sc_opened) + atomic_set_32(&sc->sc_ttypend, UART_IPEND_BREAK); +} + +/* + * Handle a receiver overrun situation. We lost at least 1 byte in the + * input stream and it's our job to contain the situation. We grab as + * much of the data we can, but otherwise flush the receiver FIFO to + * create some breathing room. The net effect is that we avoid the + * overrun condition to happen for the next X characters, where X is + * related to the FIFO size at the cost of loosing data right away. + * So, instead of having multiple overrun interrupts in close proximity + * to each other and possibly pessimizing UART interrupt latency for + * other UARTs in a multiport configuration, we create a longer segment + * of missing characters by freeing up the FIFO. + * Each overrun condition is marked in the input buffer by a token. The + * token represents the loss of at least one, but possible more bytes in + * the input stream. + */ +static void +uart_intr_overrun(struct uart_softc *sc) +{ + + if (sc->sc_opened) { + UART_RECEIVE(sc); + if (uart_rx_put(sc, UART_STAT_OVERRUN)) + sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN; + atomic_set_32(&sc->sc_ttypend, UART_IPEND_RXREADY); + } + UART_FLUSH(sc, UART_FLUSH_RECEIVER); +} + +/* + * Received data ready. + */ +static void +uart_intr_rxready(struct uart_softc *sc) +{ + int rxp; + + rxp = sc->sc_rxput; + UART_RECEIVE(sc); +#if defined(DDB) && defined(ALT_BREAK_TO_DEBUGGER) + if (sc->sc_sysdev != NULL && sc->sc_sysdev->type == UART_DEV_CONSOLE) { + while (rxp != sc->sc_rxput) { + if (db_alt_break(sc->sc_rxbuf[rxp++], &sc->sc_altbrk)) + breakpoint(); + if (rxp == sc->sc_rxbufsz) + rxp = 0; + } + } +#endif + if (sc->sc_opened) + atomic_set_32(&sc->sc_ttypend, UART_IPEND_RXREADY); + else + sc->sc_rxput = sc->sc_rxget; /* Ignore received data. */ +} + +/* + * Line or modem status change (OOB signalling). + * We pass the signals to the software interrupt handler for further + * processing. Note that we merge the delta bits, but set the state + * bits. This is to avoid loosing state transitions due to having more + * than 1 hardware interrupt between software interrupts. + */ +static void +uart_intr_sigchg(struct uart_softc *sc) +{ + int new, old, sig; + + sig = UART_GETSIG(sc); + do { + old = sc->sc_ttypend; + new = old & ~UART_SIGMASK_STATE; + new |= sig & UART_IPEND_SIGMASK; + new |= UART_IPEND_SIGCHG; + } while (!atomic_cmpset_32(&sc->sc_ttypend, old, new)); +} + +/* + * The transmitter can accept more data. + */ +static void +uart_intr_txidle(struct uart_softc *sc) +{ + if (sc->sc_txbusy) { + sc->sc_txbusy = 0; + atomic_set_32(&sc->sc_ttypend, UART_IPEND_TXIDLE); + } +} + +static void +uart_intr(void *arg) +{ + struct uart_softc *sc = arg; + int ipend; + + if (sc->sc_leaving) + return; + + ipend = UART_IPEND(sc); + if (ipend & UART_IPEND_OVERRUN) + uart_intr_overrun(sc); + if (ipend & UART_IPEND_BREAK) + uart_intr_break(sc); + if (ipend & UART_IPEND_RXREADY) + uart_intr_rxready(sc); + if (ipend & UART_IPEND_SIGCHG) + uart_intr_sigchg(sc); + if (ipend & UART_IPEND_TXIDLE) + uart_intr_txidle(sc); + + if (sc->sc_opened && sc->sc_ttypend != 0) + swi_sched(sc->sc_softih, 0); +} + +int +uart_bus_probe(device_t dev, int regshft, int rclk, int rid) +{ + struct uart_softc *sc; + struct uart_devinfo *sysdev; + int error; + + /* + * Initialize the instance. Note that the instance (=softc) does + * not necessarily match the hardware specific softc. We can't do + * anything about it now, because we may not attach to the device. + * Hardware drivers cannot use any of the class specific fields + * while probing. + */ + sc = device_get_softc(dev); + kobj_init((kobj_t)sc, (kobj_class_t)sc->sc_class); + sc->sc_dev = dev; + if (device_get_desc(dev) == NULL) + device_set_desc(dev, sc->sc_class->name); + + /* + * Allocate the register resource. We assume that all UARTs have + * a single register window in either I/O port space or memory + * mapped I/O space. Any UART that needs multiple windows will + * consequently not be supported by this driver as-is. We try I/O + * port space first because that's the common case. + */ + sc->sc_rrid = rid; + sc->sc_rtype = SYS_RES_IOPORT; + sc->sc_rres = bus_alloc_resource(dev, sc->sc_rtype, &sc->sc_rrid, + 0, ~0, sc->sc_class->uc_range, RF_ACTIVE); + if (sc->sc_rres == NULL) { + sc->sc_rrid = rid; + sc->sc_rtype = SYS_RES_MEMORY; + sc->sc_rres = bus_alloc_resource(dev, sc->sc_rtype, + &sc->sc_rrid, 0, ~0, sc->sc_class->uc_range, RF_ACTIVE); + if (sc->sc_rres == NULL) + return (ENXIO); + } + + /* + * Fill in the bus access structure and compare this device with + * a possible console device and/or a debug port. We set the flags + * in the softc so that the hardware dependent probe can adjust + * accordingly. In general, you don't want to permanently disrupt + * console I/O. + */ + sc->sc_bas.bsh = rman_get_bushandle(sc->sc_rres); + sc->sc_bas.bst = rman_get_bustag(sc->sc_rres); + sc->sc_bas.regshft = regshft; + sc->sc_bas.rclk = (rclk == 0) ? sc->sc_class->uc_rclk : rclk; + + SLIST_FOREACH(sysdev, &uart_sysdevs, next) { + if (uart_cpu_eqres(&sc->sc_bas, &sysdev->bas)) { + /* XXX check if ops matches class. */ + sc->sc_sysdev = sysdev; + break; + } + } + + error = UART_PROBE(sc); + bus_release_resource(dev, sc->sc_rtype, sc->sc_rrid, sc->sc_rres); + return (error); +} + +int +uart_bus_attach(device_t dev) +{ + struct uart_softc *sc, *sc0; + const char *sep; + int error; + + /* + * The sc_class field defines the type of UART we're going to work + * with and thus the size of the softc. Replace the generic softc + * with one that matches the UART now that we're certain we handle + * the device. + */ + sc0 = device_get_softc(dev); + if (sc0->sc_class->size > sizeof(*sc)) { + sc = malloc(sc0->sc_class->size, M_UART, M_WAITOK|M_ZERO); + bcopy(sc0, sc, sizeof(*sc)); + device_set_softc(dev, sc); + } else + sc = sc0; + + /* + * Protect ourselves against interrupts while we're not completely + * finished attaching and initializing. We don't expect interrupts + * until after UART_ATTACH() though. + */ + sc->sc_leaving = 1; + + /* + * Re-allocate. We expect that the softc contains the information + * collected by uart_bus_probe() intact. + */ + sc->sc_rres = bus_alloc_resource(dev, sc->sc_rtype, &sc->sc_rrid, + 0, ~0, sc->sc_class->uc_range, RF_ACTIVE); + if (sc->sc_rres == NULL) + return (ENXIO); + + sc->sc_irid = 0; + sc->sc_ires = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->sc_irid, + 0, ~0, 1, RF_ACTIVE); + if (sc->sc_ires != NULL) { + error = BUS_SETUP_INTR(device_get_parent(dev), dev, + sc->sc_ires, INTR_TYPE_TTY | INTR_FAST, uart_intr, + sc, &sc->sc_icookie); + if (error) + error = BUS_SETUP_INTR(device_get_parent(dev), dev, + sc->sc_ires, INTR_TYPE_TTY, uart_intr, sc, + &sc->sc_icookie); + else + sc->sc_fastintr = 1; + + if (error) { + device_printf(dev, "could not activate interrupt\n"); + bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irid, + sc->sc_ires); + sc->sc_ires = NULL; + } + } + if (sc->sc_ires == NULL) { + /* XXX no interrupt resource. Force polled mode. */ + sc->sc_polled = 1; + } + + sc->sc_rxbufsz = IBUFSIZ; + sc->sc_rxbuf = malloc(sc->sc_rxbufsz * sizeof(*sc->sc_rxbuf), + M_UART, M_WAITOK); + sc->sc_txbuf = malloc(sc->sc_txfifosz * sizeof(*sc->sc_txbuf), + M_UART, M_WAITOK); + + error = UART_ATTACH(sc); + if (error) + goto fail; + + if (sc->sc_hwiflow || sc->sc_hwoflow) { + sep = ""; + device_print_prettyname(dev); + if (sc->sc_hwiflow) { + printf("%sRTS iflow", sep); + sep = ", "; + } + if (sc->sc_hwoflow) { + printf("%sCTS oflow", sep); + sep = ", "; + } + printf("\n"); + } + + if (bootverbose && (sc->sc_fastintr || sc->sc_polled)) { + sep = ""; + device_print_prettyname(dev); + if (sc->sc_fastintr) { + printf("%sfast interrupt", sep); + sep = ", "; + } + if (sc->sc_polled) { + printf("%spolled mode", sep); + sep = ", "; + } + printf("\n"); + } + + if (sc->sc_sysdev != NULL) { + switch (sc->sc_sysdev->type) { + case UART_DEV_CONSOLE: + device_printf(dev, "console"); + break; + case UART_DEV_DBGPORT: + device_printf(dev, "debug port"); + break; + case UART_DEV_KEYBOARD: + device_printf(dev, "keyboard"); + break; + default: + device_printf(dev, "unknown system device"); + break; + } + printf(" (%d,%c,%d,%d)\n", sc->sc_sysdev->baudrate, + "noems"[sc->sc_sysdev->parity], sc->sc_sysdev->databits, + sc->sc_sysdev->stopbits); + } + + error = (sc->sc_sysdev != NULL && sc->sc_sysdev->attach != NULL) + ? (*sc->sc_sysdev->attach)(sc) : uart_tty_attach(sc); + if (error) + goto fail; + + sc->sc_leaving = 0; + uart_intr(sc); + return (0); + + fail: + free(sc->sc_txbuf, M_UART); + free(sc->sc_rxbuf, M_UART); + + if (sc->sc_ires != NULL) { + bus_teardown_intr(dev, sc->sc_ires, sc->sc_icookie); + bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irid, + sc->sc_ires); + } + bus_release_resource(dev, sc->sc_rtype, sc->sc_rrid, sc->sc_rres); + + return (error); +} + +int +uart_bus_detach(device_t dev) +{ + struct uart_softc *sc; + + sc = device_get_softc(dev); + + sc->sc_leaving = 1; + + UART_DETACH(sc); + + if (sc->sc_sysdev != NULL && sc->sc_sysdev->detach != NULL) + (*sc->sc_sysdev->detach)(sc); + else + uart_tty_detach(sc); + + free(sc->sc_txbuf, M_UART); + free(sc->sc_rxbuf, M_UART); + + if (sc->sc_ires != NULL) { + bus_teardown_intr(dev, sc->sc_ires, sc->sc_icookie); + bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irid, + sc->sc_ires); + } + bus_release_resource(dev, sc->sc_rtype, sc->sc_rrid, sc->sc_rres); + + if (sc->sc_class->size > sizeof(*sc)) { + device_set_softc(dev, NULL); + free(sc, M_UART); + } else + device_set_softc(dev, NULL); + + return (0); +} diff --git a/sys/dev/uart/uart_cpu.h b/sys/dev/uart/uart_cpu.h new file mode 100644 index 0000000..bd1bd5f --- /dev/null +++ b/sys/dev/uart/uart_cpu.h @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2003 Marcel Moolenaar + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _DEV_UART_CPU_H_ +#define _DEV_UART_CPU_H_ + +/* + * Low-level operations for use by console and/or debug port support. + */ +struct uart_ops { + int (*probe)(struct uart_bas *); + void (*init)(struct uart_bas *, int, int, int, int); + void (*term)(struct uart_bas *); + void (*putc)(struct uart_bas *, int); + int (*poll)(struct uart_bas *); + int (*getc)(struct uart_bas *); +}; + +extern struct uart_ops uart_ns8250_ops; +extern struct uart_ops uart_sab82532_ops; +extern struct uart_ops uart_z8530_ops; + +/* + * Console and debug port device info. + */ +struct uart_softc; +struct uart_devinfo { + SLIST_ENTRY(uart_devinfo) next; + struct uart_ops ops; + struct uart_bas bas; + int baudrate; + int databits; + int stopbits; + int parity; + int type; +#define UART_DEV_CONSOLE 0 +#define UART_DEV_DBGPORT 1 +#define UART_DEV_KEYBOARD 2 + int (*attach)(struct uart_softc*); + int (*detach)(struct uart_softc*); + void *cookie; /* Type dependent use. */ +}; + +int uart_cpu_getdev(int devtype, struct uart_devinfo *di); +int uart_cpu_eqres(struct uart_bas *b1, struct uart_bas *b2); + +void uart_add_sysdev(struct uart_devinfo*); + +/* + * Operations for low-level access to the UART. Primarily for use + * by console and debug port logic. + */ +static __inline int +uart_probe(struct uart_devinfo *di) +{ + return (di->ops.probe(&di->bas)); +} + +static __inline void +uart_init(struct uart_devinfo *di) +{ + di->ops.init(&di->bas, di->baudrate, di->databits, di->stopbits, + di->parity); +} + +static __inline void +uart_term(struct uart_devinfo *di) +{ + di->ops.term(&di->bas); +} + +static __inline void +uart_putc(struct uart_devinfo *di, int c) +{ + di->ops.putc(&di->bas, c); +} + +static __inline int +uart_poll(struct uart_devinfo *di) +{ + return (di->ops.poll(&di->bas)); +} + +static __inline int +uart_getc(struct uart_devinfo *di) +{ + return (di->ops.getc(&di->bas)); +} + +#endif /* _DEV_UART_CPU_H_ */ diff --git a/sys/dev/uart/uart_cpu_alpha.c b/sys/dev/uart/uart_cpu_alpha.c new file mode 100644 index 0000000..95ee2ff --- /dev/null +++ b/sys/dev/uart/uart_cpu_alpha.c @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2003 Marcel Moolenaar + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/reboot.h> + +#include <machine/bus.h> +#include <machine/md_var.h> +#include <machine/rpb.h> + +#include <dev/uart/uart.h> +#include <dev/uart/uart_cpu.h> + +int +uart_cpu_getdev(int devtype, struct uart_devinfo *di) +{ + struct ctb *ctb; + unsigned int i, ivar; + + if (devtype == UART_DEV_CONSOLE) { + ctb = (struct ctb *)(((caddr_t)hwrpb) + hwrpb->rpb_ctb_off); + if (ctb->ctb_term_type != CTB_PRINTERPORT) + return (ENXIO); + boothowto |= RB_SERIAL; + di->ops = uart_ns8250_ops; + di->bas.bst = busspace_isa_io; + di->bas.bsh = 0x3f8; + di->bas.regshft = 0; + di->bas.rclk = 0; + di->baudrate = 9600; + di->databits = 8; + di->stopbits = 1; + di->parity = UART_PARITY_NONE; + return (0); + } + + /* + * Scan the hints. We only try units 0 to 3 (inclusive). This + * covers the ISA legacy where 4 UARTs had their resources + * predefined. + */ + for (i = 0; i < 4; i++) { + if (resource_int_value("uart", i, "flags", &ivar)) + continue; + if (devtype == UART_DEV_DBGPORT && !UART_FLAGS_DBGPORT(ivar)) + continue; + /* + * We have a possible device. Make sure it's enabled and + * that we have an I/O port. + */ + if (resource_int_value("uart", i, "disabled", &ivar) == 0 && + ivar != 0) + continue; + if (resource_int_value("uart", i, "port", &ivar) != 0 || + ivar == 0) + continue; + /* + * Got it. Fill in the instance and return it. We assume we + * only have ns8250 and successors on alpha. + */ + di->ops = uart_ns8250_ops; + di->bas.bst = busspace_isa_io; + di->bas.bsh = ivar; + di->bas.regshft = 0; + di->bas.rclk = 0; + if (resource_int_value("uart", i, "baud", &ivar) != 0) + ivar = 0; + di->baudrate = ivar; + di->databits = 8; + di->stopbits = 1; + di->parity = UART_PARITY_NONE; + return (0); + } + + return (ENXIO); +} + +int +uart_cpu_eqres(struct uart_bas *b1, struct uart_bas *b2) +{ + + return ((b1->bsh == b2->bsh && b1->bst == b2->bst) ? 1 : 0); +} diff --git a/sys/dev/uart/uart_cpu_amd64.c b/sys/dev/uart/uart_cpu_amd64.c new file mode 100644 index 0000000..a186216 --- /dev/null +++ b/sys/dev/uart/uart_cpu_amd64.c @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2003 Marcel Moolenaar + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> + +#include <machine/bus.h> + +#include <dev/uart/uart.h> +#include <dev/uart/uart_cpu.h> + +int +uart_cpu_getdev(int devtype, struct uart_devinfo *di) +{ + unsigned int i, ivar; + + /* + * Scan the hints. We only try units 0 to 3 (inclusive). This + * covers the ISA legacy where 4 UARTs had their resources + * predefined. + */ + for (i = 0; i < 4; i++) { + if (resource_int_value("uart", i, "flags", &ivar)) + continue; + if (devtype == UART_DEV_CONSOLE && !UART_FLAGS_CONSOLE(ivar)) + continue; + if (devtype == UART_DEV_DBGPORT && !UART_FLAGS_DBGPORT(ivar)) + continue; + /* + * We have a possible device. Make sure it's enabled and + * that we have an I/O port. + */ + if (resource_int_value("uart", i, "disabled", &ivar) == 0 && + ivar != 0) + continue; + if (resource_int_value("uart", i, "port", &ivar) != 0 || + ivar == 0) + continue; + /* + * Got it. Fill in the instance and return it. We only have + * ns8250 and successors on i386. + */ + di->ops = uart_ns8250_ops; + di->bas.bst = AMD64_BUS_SPACE_IO; + di->bas.bsh = ivar; + di->bas.regshft = 0; + di->bas.rclk = 0; + if (resource_int_value("uart", i, "baud", &ivar) != 0) + ivar = 0; + di->baudrate = ivar; + di->databits = 8; + di->stopbits = 1; + di->parity = UART_PARITY_NONE; + return (0); + } + + return (ENXIO); +} + +int +uart_cpu_eqres(struct uart_bas *b1, struct uart_bas *b2) +{ + + return ((b1->bsh == b2->bsh && b1->bst == b2->bst) ? 1 : 0); +} diff --git a/sys/dev/uart/uart_cpu_i386.c b/sys/dev/uart/uart_cpu_i386.c new file mode 100644 index 0000000..04de73a --- /dev/null +++ b/sys/dev/uart/uart_cpu_i386.c @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2003 Marcel Moolenaar + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> + +#include <machine/bus.h> + +#include <dev/uart/uart.h> +#include <dev/uart/uart_cpu.h> + +int +uart_cpu_getdev(int devtype, struct uart_devinfo *di) +{ + unsigned int i, ivar; + + /* + * Scan the hints. We only try units 0 to 3 (inclusive). This + * covers the ISA legacy where 4 UARTs had their resources + * predefined. + */ + for (i = 0; i < 4; i++) { + if (resource_int_value("uart", i, "flags", &ivar)) + continue; + if (devtype == UART_DEV_CONSOLE && !UART_FLAGS_CONSOLE(ivar)) + continue; + if (devtype == UART_DEV_DBGPORT && !UART_FLAGS_DBGPORT(ivar)) + continue; + /* + * We have a possible device. Make sure it's enabled and + * that we have an I/O port. + */ + if (resource_int_value("uart", i, "disabled", &ivar) == 0 && + ivar != 0) + continue; + if (resource_int_value("uart", i, "port", &ivar) != 0 || + ivar == 0) + continue; + /* + * Got it. Fill in the instance and return it. We only have + * ns8250 and successors on i386. + */ + di->ops = uart_ns8250_ops; + di->bas.bst = I386_BUS_SPACE_IO; + di->bas.bsh = ivar; + di->bas.regshft = 0; + di->bas.rclk = 0; + if (resource_int_value("uart", i, "baud", &ivar) != 0) + ivar = 0; + di->baudrate = ivar; + di->databits = 8; + di->stopbits = 1; + di->parity = UART_PARITY_NONE; + return (0); + } + + return (ENXIO); +} + +int +uart_cpu_eqres(struct uart_bas *b1, struct uart_bas *b2) +{ + + return ((b1->bsh == b2->bsh && b1->bst == b2->bst) ? 1 : 0); +} diff --git a/sys/dev/uart/uart_cpu_ia64.c b/sys/dev/uart/uart_cpu_ia64.c new file mode 100644 index 0000000..f3523fb --- /dev/null +++ b/sys/dev/uart/uart_cpu_ia64.c @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2003 Marcel Moolenaar + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> + +#include <machine/bootinfo.h> +#include <machine/bus.h> +#include <machine/dig64.h> +#include <machine/vmparam.h> + +#include <dev/uart/uart.h> +#include <dev/uart/uart_cpu.h> + +static int dig64_to_uart_parity[] = { + UART_PARITY_NONE, UART_PARITY_NONE, UART_PARITY_EVEN, + UART_PARITY_ODD, UART_PARITY_MARK, UART_PARITY_SPACE +}; + +int +uart_cpu_getdev(int devtype, struct uart_devinfo *di) +{ + struct dig64_hcdp_table *tbl; + struct dig64_hcdp_entry *ent; + unsigned int i, ivar; + + /* + * Use the DIG64 HCDP table if present. + */ + if (bootinfo.bi_hcdp != 0) { + tbl = (void*)IA64_PHYS_TO_RR7(bootinfo.bi_hcdp); + for (i = 0; i < tbl->entries; i++) { + ent = tbl->entry + i; + + if (devtype == UART_DEV_CONSOLE && + ent->type != DIG64_HCDP_CONSOLE) + continue; + + if (devtype == UART_DEV_DBGPORT && + ent->type != DIG64_HCDP_DBGPORT) + continue; + + di->ops = uart_ns8250_ops; + di->bas.bst = IA64_BUS_SPACE_IO; + di->bas.bst = (ent->address.addr_space == 0) + ? IA64_BUS_SPACE_MEM : IA64_BUS_SPACE_IO; + di->bas.bsh = ent->address.addr_high; + di->bas.bsh = (di->bas.bsh << 32) + + ent->address.addr_low; + di->bas.regshft = 0; + di->bas.rclk = ent->pclock << 4; + /* We don't deal with 64-bit baud rates. */ + di->baudrate = ent->baud_low; + di->databits = ent->databits; + di->stopbits = ent->stopbits; + di->parity = (ent->parity >= 6) ? UART_PARITY_NONE + : dig64_to_uart_parity[ent->parity]; + return (0); + } + + /* FALLTHROUGH */ + } + + /* + * Scan the hints for backward compatibility. We only try units + * 0 to 3 (inclusive). This covers the ISA legacy where 4 UARTs + * had their resources predefined. + */ + for (i = 0; i < 4; i++) { + if (resource_int_value("uart", i, "flags", &ivar)) + continue; + if (devtype == UART_DEV_CONSOLE && !UART_FLAGS_CONSOLE(ivar)) + continue; + if (devtype == UART_DEV_DBGPORT && !UART_FLAGS_DBGPORT(ivar)) + continue; + /* + * We have a possible device. Make sure it's enabled and + * that we have an I/O port. + */ + if (resource_int_value("uart", i, "disabled", &ivar) == 0 && + ivar != 0) + continue; + if (resource_int_value("uart", i, "port", &ivar) != 0 || + ivar == 0) + continue; + /* + * Got it. Fill in the instance and return it. We only have + * ns8250 and successors on i386. + */ + di->ops = uart_ns8250_ops; + di->bas.bst = IA64_BUS_SPACE_IO; + di->bas.bsh = ivar; + di->bas.regshft = 0; + di->bas.rclk = 0; + if (resource_int_value("uart", i, "baud", &ivar) != 0) + ivar = 0; + di->baudrate = ivar; + di->databits = 8; + di->stopbits = 1; + di->parity = UART_PARITY_NONE; + return (0); + } + + return (ENXIO); +} + +int +uart_cpu_eqres(struct uart_bas *b1, struct uart_bas *b2) +{ + + return ((b1->bsh == b2->bsh && b1->bst == b2->bst) ? 1 : 0); +} diff --git a/sys/dev/uart/uart_cpu_sparc64.c b/sys/dev/uart/uart_cpu_sparc64.c new file mode 100644 index 0000000..fed26a8 --- /dev/null +++ b/sys/dev/uart/uart_cpu_sparc64.c @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2003 Marcel Moolenaar + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> + +#include <machine/bus.h> +#include <machine/bus_private.h> + +#include <dev/ofw/openfirm.h> +#include <machine/ofw_machdep.h> + +#include <dev/uart/uart.h> +#include <dev/uart/uart_cpu.h> + +static struct bus_space_tag bst_store[3]; + +static int +uart_cpu_channel(char *dev) +{ + char alias[64]; + phandle_t aliases; + int len; + + strcpy(alias, dev); + if ((aliases = OF_finddevice("/aliases")) != -1) + OF_getprop(aliases, dev, alias, sizeof(alias)); + len = strlen(alias); + if (len < 2 || alias[len - 2] != ':' || alias[len - 1] < 'a' || + alias[len - 1] > 'b') + return (0); + return (alias[len - 1] - 'a'); +} + +int +uart_cpu_getdev(int devtype, struct uart_devinfo *di) +{ + char buf[32], dev[32]; + phandle_t input, options, output; + bus_addr_t addr; + int baud, bits, ch, error, space, stop; + char flag, par; + + /* + * Get the address of the UART that is selected as the console, if + * the console is an UART of course. Note that we enforce that both + * stdin and stdout are selected. For weird configurations, use + * ofw_console(4). + * Note that the currently active console (ie /chosen/stdout and + * /chosen/stdin) may not be the same as the device selected in the + * environment (ie /options/output-device and /options/input-device) + * because the user may have changed the environment. In that case + * I would assume that the user expects that FreeBSD uses the new + * console setting. There's choice choice, really. + */ + if ((options = OF_finddevice("/options")) == -1) + return (ENXIO); + if (OF_getprop(options, "input-device", dev, sizeof(dev)) == -1) + return (ENXIO); + if ((input = OF_finddevice(dev)) == -1) + return (ENXIO); + if (OF_getprop(input, "device_type", buf, sizeof(buf)) == -1) + return (ENXIO); + if (strcmp(buf, "serial")) + return (ENODEV); + if (devtype == UART_DEV_KEYBOARD) { + if (OF_getprop(input, "keyboard", buf, sizeof(buf)) == -1) + return (ENXIO); + } else if (devtype == UART_DEV_CONSOLE) { + if (OF_getprop(options, "output-device", buf, sizeof(buf)) + == -1) + return (ENXIO); + if ((output = OF_finddevice(buf)) == -1) + return (ENXIO); + if (input != output) + return (ENXIO); + } else + return (ENODEV); + + error = OF_decode_addr(input, &space, &addr); + if (error) + return (error); + + /* Get the device class. */ + if (OF_getprop(input, "name", buf, sizeof(buf)) == -1) + return (ENXIO); + di->bas.regshft = 0; + di->bas.rclk = 0; + if (!strcmp(buf, "se")) { + di->ops = uart_sab82532_ops; + addr += 64 * uart_cpu_channel(dev); + } else if (!strcmp(buf, "zs")) { + di->ops = uart_z8530_ops; + di->bas.regshft = 1; + ch = uart_cpu_channel(dev); + addr += 4 - 4 * ch; + } else if (!strcmp(buf, "su") || !strcmp(buf, "su_pnp")) + di->ops = uart_ns8250_ops; + else + return (ENXIO); + + /* Fill in the device info. */ + di->bas.bst = &bst_store[devtype]; + di->bas.bsh = sparc64_fake_bustag(space, addr, di->bas.bst); + + /* Get the line settings. */ + di->baudrate = 9600; + di->databits = 8; + di->stopbits = 1; + di->parity = UART_PARITY_NONE; + snprintf(buf, sizeof(buf), "%s-mode", dev); + if (OF_getprop(options, buf, buf, sizeof(buf)) == -1) + return (0); + if (sscanf(buf, "%d,%d,%c,%d,%c", &baud, &bits, &par, &stop, &flag) + != 5) + return (0); + di->baudrate = baud; + di->databits = bits; + di->stopbits = stop; + di->parity = (par == 'n') ? UART_PARITY_NONE : + (par == 'o') ? UART_PARITY_ODD : UART_PARITY_EVEN; + return (0); +} + +int +uart_cpu_eqres(struct uart_bas *b1, struct uart_bas *b2) +{ + + return ((b1->bsh == b2->bsh) ? 1 : 0); +} diff --git a/sys/dev/uart/uart_dev_ns8250.c b/sys/dev/uart/uart_dev_ns8250.c new file mode 100644 index 0000000..d09371d --- /dev/null +++ b/sys/dev/uart/uart_dev_ns8250.c @@ -0,0 +1,726 @@ +/* + * Copyright (c) 2003 Marcel Moolenaar + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/conf.h> +#include <machine/bus.h> + +#include <dev/uart/uart.h> +#include <dev/uart/uart_cpu.h> +#include <dev/uart/uart_bus.h> +#include <dev/uart/uart_dev_ns8250.h> + +#include "uart_if.h" + +#define DEFAULT_RCLK 1843200 + +/* + * Clear pending interrupts. THRE is cleared by reading IIR. Data + * that may have been received gets lost here. + */ +static void +ns8250_clrint(struct uart_bas *bas) +{ + uint8_t iir; + + iir = uart_getreg(bas, REG_IIR); + while ((iir & IIR_NOPEND) == 0) { + iir &= IIR_IMASK; + if (iir == IIR_RLS) + (void)uart_getreg(bas, REG_LSR); + else if (iir == IIR_RXRDY || iir == IIR_RXTOUT) + (void)uart_getreg(bas, REG_DATA); + else if (iir == IIR_MLSC) + (void)uart_getreg(bas, REG_MSR); + uart_barrier(bas); + iir = uart_getreg(bas, REG_IIR); + } +} + +static int +ns8250_delay(struct uart_bas *bas) +{ + int divisor; + u_char lcr; + + lcr = uart_getreg(bas, REG_LCR); + uart_setreg(bas, REG_LCR, lcr | LCR_DLAB); + uart_barrier(bas); + divisor = uart_getdreg(bas, REG_DL); + uart_barrier(bas); + uart_setreg(bas, REG_LCR, lcr); + uart_barrier(bas); + + /* 1/10th the time to transmit 1 character (estimate). */ + return (16000000 * divisor / bas->rclk); +} + +static int +ns8250_divisor(int rclk, int baudrate) +{ + int actual_baud, divisor; + int error; + + if (baudrate == 0) + return (0); + + divisor = (rclk / (baudrate << 3) + 1) >> 1; + if (divisor == 0 || divisor >= 65536) + return (0); + actual_baud = rclk / (divisor << 4); + + /* 10 times error in percent: */ + error = ((actual_baud - baudrate) * 2000 / baudrate + 1) >> 1; + + /* 3.0% maximum error tolerance: */ + if (error < -30 || error > 30) + return (0); + + return (divisor); +} + +static int +ns8250_drain(struct uart_bas *bas, int what) +{ + int delay, limit; + + delay = ns8250_delay(bas); + + if (what & UART_DRAIN_TRANSMITTER) { + /* + * Pick an arbitrary high limit to avoid getting stuck in + * an infinite loop when the hardware is broken. Make the + * limit high enough to handle large FIFOs. + */ + limit = 10*1024; + while ((uart_getreg(bas, REG_LSR) & LSR_TEMT) == 0 && --limit) + DELAY(delay); + if (limit == 0) { + /* printf("ns8250: transmitter appears stuck... "); */ + return (EIO); + } + } + + if (what & UART_DRAIN_RECEIVER) { + /* + * Pick an arbitrary high limit to avoid getting stuck in + * an infinite loop when the hardware is broken. Make the + * limit high enough to handle large FIFOs and integrated + * UARTs. The HP rx2600 for example has 3 UARTs on the + * management board that tend to get a lot of data send + * to it when the UART is first activated. + */ + limit=10*4096; + while ((uart_getreg(bas, REG_LSR) & LSR_RXRDY) && --limit) { + (void)uart_getreg(bas, REG_DATA); + uart_barrier(bas); + DELAY(delay << 2); + } + if (limit == 0) { + /* printf("ns8250: receiver appears broken... "); */ + return (EIO); + } + } + + return (0); +} + +/* + * We can only flush UARTs with FIFOs. UARTs without FIFOs should be + * drained. WARNING: this function clobbers the FIFO setting! + */ +static void +ns8250_flush(struct uart_bas *bas, int what) +{ + uint8_t fcr; + + fcr = FCR_ENABLE; + if (what & UART_FLUSH_TRANSMITTER) + fcr |= FCR_XMT_RST; + if (what & UART_FLUSH_RECEIVER) + fcr |= FCR_RCV_RST; + uart_setreg(bas, REG_FCR, fcr); + uart_barrier(bas); +} + +static int +ns8250_param(struct uart_bas *bas, int baudrate, int databits, int stopbits, + int parity) +{ + int divisor; + uint8_t lcr; + + lcr = 0; + if (databits >= 8) + lcr |= LCR_8BITS; + else if (databits == 7) + lcr |= LCR_7BITS; + else if (databits == 6) + lcr |= LCR_6BITS; + else + lcr |= LCR_5BITS; + if (stopbits > 1) + lcr |= LCR_STOPB; + lcr |= parity << 3; + + /* Set baudrate. */ + if (baudrate > 0) { + uart_setreg(bas, REG_LCR, lcr | LCR_DLAB); + uart_barrier(bas); + divisor = ns8250_divisor(bas->rclk, baudrate); + if (divisor == 0) + return (EINVAL); + uart_setdreg(bas, REG_DL, divisor); + uart_barrier(bas); + } + + /* Set LCR and clear DLAB. */ + uart_setreg(bas, REG_LCR, lcr); + uart_barrier(bas); + return (0); +} + +/* + * Low-level UART interface. + */ +static int ns8250_probe(struct uart_bas *bas); +static void ns8250_init(struct uart_bas *bas, int, int, int, int); +static void ns8250_term(struct uart_bas *bas); +static void ns8250_putc(struct uart_bas *bas, int); +static int ns8250_poll(struct uart_bas *bas); +static int ns8250_getc(struct uart_bas *bas); + +struct uart_ops uart_ns8250_ops = { + .probe = ns8250_probe, + .init = ns8250_init, + .term = ns8250_term, + .putc = ns8250_putc, + .poll = ns8250_poll, + .getc = ns8250_getc, +}; + +static int +ns8250_probe(struct uart_bas *bas) +{ + u_char lcr, val; + + /* Check known 0 bits that don't depend on DLAB. */ + val = uart_getreg(bas, REG_IIR); + if (val & 0x30) + return (ENXIO); + val = uart_getreg(bas, REG_MCR); + if (val & 0xe0) + return (ENXIO); + + lcr = uart_getreg(bas, REG_LCR); + uart_setreg(bas, REG_LCR, lcr & ~LCR_DLAB); + uart_barrier(bas); + + /* Check known 0 bits that depend on !DLAB. */ + val = uart_getreg(bas, REG_IER); + if (val & 0xf0) + goto fail; + + uart_setreg(bas, REG_LCR, lcr); + uart_barrier(bas); + return (0); + + fail: + uart_setreg(bas, REG_LCR, lcr); + uart_barrier(bas); + return (ENXIO); +} + +static void +ns8250_init(struct uart_bas *bas, int baudrate, int databits, int stopbits, + int parity) +{ + + if (bas->rclk == 0) + bas->rclk = DEFAULT_RCLK; + ns8250_param(bas, baudrate, databits, stopbits, parity); + + /* Disable all interrupt sources. */ + uart_setreg(bas, REG_IER, 0); + uart_barrier(bas); + + /* Disable the FIFO (if present). */ + uart_setreg(bas, REG_FCR, 0); + uart_barrier(bas); + + /* Set RTS & DTR. */ + uart_setreg(bas, REG_MCR, MCR_IE | MCR_RTS | MCR_DTR); + uart_barrier(bas); + + ns8250_clrint(bas); +} + +static void +ns8250_term(struct uart_bas *bas) +{ + + /* Clear RTS & DTR. */ + uart_setreg(bas, REG_MCR, MCR_IE); + uart_barrier(bas); +} + +static void +ns8250_putc(struct uart_bas *bas, int c) +{ + int delay, limit; + + /* 1/10th the time to transmit 1 character (estimate). */ + delay = ns8250_delay(bas); + + limit = 20; + while ((uart_getreg(bas, REG_LSR) & LSR_THRE) == 0 && --limit) + DELAY(delay); + uart_setreg(bas, REG_DATA, c); + limit = 40; + while ((uart_getreg(bas, REG_LSR) & LSR_TEMT) == 0 && --limit) + DELAY(delay); +} + +static int +ns8250_poll(struct uart_bas *bas) +{ + + if (uart_getreg(bas, REG_LSR) & LSR_RXRDY) + return (uart_getreg(bas, REG_DATA)); + return (-1); +} + +static int +ns8250_getc(struct uart_bas *bas) +{ + int delay; + + /* 1/10th the time to transmit 1 character (estimate). */ + delay = ns8250_delay(bas); + + while ((uart_getreg(bas, REG_LSR) & LSR_RXRDY) == 0) + DELAY(delay); + return (uart_getreg(bas, REG_DATA)); +} + +/* + * High-level UART interface. + */ +struct ns8250_softc { + struct uart_softc base; + uint8_t fcr; + uint8_t ier; + uint8_t mcr; +}; + +static int ns8250_bus_attach(struct uart_softc *); +static int ns8250_bus_detach(struct uart_softc *); +static int ns8250_bus_flush(struct uart_softc *, int); +static int ns8250_bus_getsig(struct uart_softc *); +static int ns8250_bus_ioctl(struct uart_softc *, int, intptr_t); +static int ns8250_bus_ipend(struct uart_softc *); +static int ns8250_bus_param(struct uart_softc *, int, int, int, int); +static int ns8250_bus_probe(struct uart_softc *); +static int ns8250_bus_receive(struct uart_softc *); +static int ns8250_bus_setsig(struct uart_softc *, int); +static int ns8250_bus_transmit(struct uart_softc *); + +static kobj_method_t ns8250_methods[] = { + KOBJMETHOD(uart_attach, ns8250_bus_attach), + KOBJMETHOD(uart_detach, ns8250_bus_detach), + KOBJMETHOD(uart_flush, ns8250_bus_flush), + KOBJMETHOD(uart_getsig, ns8250_bus_getsig), + KOBJMETHOD(uart_ioctl, ns8250_bus_ioctl), + KOBJMETHOD(uart_ipend, ns8250_bus_ipend), + KOBJMETHOD(uart_param, ns8250_bus_param), + KOBJMETHOD(uart_probe, ns8250_bus_probe), + KOBJMETHOD(uart_receive, ns8250_bus_receive), + KOBJMETHOD(uart_setsig, ns8250_bus_setsig), + KOBJMETHOD(uart_transmit, ns8250_bus_transmit), + { 0, 0 } +}; + +struct uart_class uart_ns8250_class = { + "ns8250 class", + ns8250_methods, + sizeof(struct ns8250_softc), + .uc_range = 8, + .uc_rclk = DEFAULT_RCLK +}; + +#define SIGCHG(c, i, s, d) \ + if (c) { \ + i |= (i & s) ? s : s | d; \ + } else { \ + i = (i & s) ? (i & ~s) | d : i; \ + } + +static int +ns8250_bus_attach(struct uart_softc *sc) +{ + struct ns8250_softc *ns8250 = (struct ns8250_softc*)sc; + struct uart_bas *bas; + + bas = &sc->sc_bas; + + ns8250->mcr = uart_getreg(bas, REG_MCR); + ns8250->fcr = FCR_ENABLE | FCR_RX_MEDH; + uart_setreg(bas, REG_FCR, ns8250->fcr); + uart_barrier(bas); + ns8250_bus_flush(sc, UART_FLUSH_RECEIVER|UART_FLUSH_TRANSMITTER); + + if (ns8250->mcr & MCR_DTR) + sc->sc_hwsig |= UART_SIG_DTR; + if (ns8250->mcr & MCR_RTS) + sc->sc_hwsig |= UART_SIG_RTS; + ns8250_bus_getsig(sc); + + ns8250_clrint(bas); + ns8250->ier = IER_EMSC | IER_ERLS | IER_ERXRDY; + uart_setreg(bas, REG_IER, ns8250->ier); + uart_barrier(bas); + return (0); +} + +static int +ns8250_bus_detach(struct uart_softc *sc) +{ + struct uart_bas *bas; + + bas = &sc->sc_bas; + uart_setreg(bas, REG_IER, 0); + uart_barrier(bas); + ns8250_clrint(bas); + return (0); +} + +static int +ns8250_bus_flush(struct uart_softc *sc, int what) +{ + struct ns8250_softc *ns8250 = (struct ns8250_softc*)sc; + struct uart_bas *bas; + + bas = &sc->sc_bas; + if (sc->sc_hasfifo) { + ns8250_flush(bas, what); + uart_setreg(bas, REG_FCR, ns8250->fcr); + uart_barrier(bas); + return (0); + } + return (ns8250_drain(bas, what)); +} + +static int +ns8250_bus_getsig(struct uart_softc *sc) +{ + uint32_t new, old, sig; + uint8_t msr; + + do { + old = sc->sc_hwsig; + sig = old; + msr = uart_getreg(&sc->sc_bas, REG_MSR); + SIGCHG(msr & MSR_DSR, sig, UART_SIG_DSR, UART_SIG_DDSR); + SIGCHG(msr & MSR_CTS, sig, UART_SIG_CTS, UART_SIG_DCTS); + SIGCHG(msr & MSR_DCD, sig, UART_SIG_DCD, UART_SIG_DDCD); + SIGCHG(msr & MSR_RI, sig, UART_SIG_RI, UART_SIG_DRI); + new = sig & ~UART_SIGMASK_DELTA; + } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new)); + return (sig); +} + +static int +ns8250_bus_ioctl(struct uart_softc *sc, int request, intptr_t data) +{ + struct uart_bas *bas; + uint8_t lcr; + + bas = &sc->sc_bas; + switch (request) { + case UART_IOCTL_BREAK: + lcr = uart_getreg(bas, REG_LCR); + if (data) + lcr |= LCR_SBREAK; + else + lcr &= ~LCR_SBREAK; + uart_setreg(bas, REG_LCR, lcr); + uart_barrier(bas); + break; + default: + return (EINVAL); + } + return (0); +} + +static int +ns8250_bus_ipend(struct uart_softc *sc) +{ + struct uart_bas *bas; + int ipend; + uint8_t iir, lsr; + + bas = &sc->sc_bas; + iir = uart_getreg(bas, REG_IIR); + if (iir & IIR_NOPEND) + return (0); + + ipend = 0; + if (iir & IIR_RXRDY) { + lsr = uart_getreg(bas, REG_LSR); + if (lsr & LSR_OE) + ipend |= UART_IPEND_OVERRUN; + if (lsr & LSR_BI) + ipend |= UART_IPEND_BREAK; + if (lsr & LSR_RXRDY) + ipend |= UART_IPEND_RXREADY; + } else { + if (iir & IIR_TXRDY) + ipend |= UART_IPEND_TXIDLE; + else + ipend |= UART_IPEND_SIGCHG; + } + + return ((sc->sc_leaving) ? 0 : ipend); +} + +static int +ns8250_bus_param(struct uart_softc *sc, int baudrate, int databits, + int stopbits, int parity) +{ + struct uart_bas *bas; + + bas = &sc->sc_bas; + return (ns8250_param(bas, baudrate, databits, stopbits, parity)); +} + +static int +ns8250_bus_probe(struct uart_softc *sc) +{ + struct uart_bas *bas; + int count, delay, error, limit; + uint8_t mcr; + + bas = &sc->sc_bas; + + error = ns8250_probe(bas); + if (error) + return (error); + + mcr = MCR_IE; + if (sc->sc_sysdev == NULL) { + /* By using ns8250_init() we also set DTR and RTS. */ + ns8250_init(bas, 9600, 8, 1, UART_PARITY_NONE); + } else + mcr |= MCR_DTR | MCR_RTS; + + error = ns8250_drain(bas, UART_DRAIN_TRANSMITTER); + if (error) + return (error); + + /* + * Set loopback mode. This avoids having garbage on the wire and + * also allows us send and receive data. We set DTR and RTS to + * avoid the possibility that automatic flow-control prevents + * any data from being sent. We clear IE to avoid raising interrupts. + */ + uart_setreg(bas, REG_MCR, MCR_LOOPBACK | MCR_DTR | MCR_RTS); + uart_barrier(bas); + + /* + * Enable FIFOs. And check that the UART has them. If not, we're + * done. Otherwise we set DMA mode with the highest trigger level + * so that we can determine the FIFO size. Since this is the first + * time we enable the FIFOs, we reset them. + */ + uart_setreg(bas, REG_FCR, FCR_ENABLE); + uart_barrier(bas); + sc->sc_hasfifo = (uart_getreg(bas, REG_IIR) & IIR_FIFO_MASK) ? 1 : 0; + if (!sc->sc_hasfifo) { + /* + * NS16450 or INS8250. We don't bother to differentiate + * between them. They're too old to be interesting. + */ + uart_setreg(bas, REG_MCR, mcr); + uart_barrier(bas); + device_set_desc(sc->sc_dev, "8250 or 16450 or compatible"); + return (0); + } + + uart_setreg(bas, REG_FCR, FCR_ENABLE | FCR_DMA | FCR_RX_HIGH | + FCR_XMT_RST | FCR_RCV_RST); + uart_barrier(bas); + + count = 0; + delay = ns8250_delay(bas); + + /* We have FIFOs. Drain the transmitter and receiver. */ + error = ns8250_drain(bas, UART_DRAIN_RECEIVER|UART_DRAIN_TRANSMITTER); + if (error) { + uart_setreg(bas, REG_MCR, mcr); + uart_setreg(bas, REG_FCR, 0); + uart_barrier(bas); + goto describe; + } + + uart_setreg(bas, REG_IER, IER_ERXRDY); + uart_barrier(bas); + + /* + * We should have a sufficiently clean "pipe" to determine the + * size of the FIFOs. We send as much characters as is reasonable + * and wait for the the RX interrupt to be asserted, counting the + * characters as we send them. Based on that count we know the + * FIFO size. + */ + while ((uart_getreg(bas, REG_IIR) & IIR_RXRDY) == 0 && count < 1030) { + uart_setreg(bas, REG_DATA, 0); + uart_barrier(bas); + count++; + + limit = 30; + while ((uart_getreg(bas, REG_LSR) & LSR_TEMT) == 0 && --limit) + DELAY(delay); + if (limit == 0) { + uart_setreg(bas, REG_IER, 0); + uart_setreg(bas, REG_MCR, mcr); + uart_setreg(bas, REG_FCR, 0); + uart_barrier(bas); + count = 0; + goto describe; + } + } + + uart_setreg(bas, REG_IER, 0); + uart_setreg(bas, REG_MCR, mcr); + + /* Reset FIFOs. */ + ns8250_flush(bas, UART_FLUSH_RECEIVER|UART_FLUSH_TRANSMITTER); + + describe: + if (count >= 14 && count < 16) { + sc->sc_rxfifosz = 16; + device_set_desc(sc->sc_dev, "16550 or compatible"); + } else if (count >= 28 && count < 32) { + sc->sc_rxfifosz = 32; + device_set_desc(sc->sc_dev, "16650 or compatible"); + } else if (count >= 56 && count < 64) { + sc->sc_rxfifosz = 64; + device_set_desc(sc->sc_dev, "16750 or compatible"); + } else if (count >= 112 && count < 128) { + sc->sc_rxfifosz = 128; + device_set_desc(sc->sc_dev, "16950 or compatible"); + } else { + sc->sc_rxfifosz = 1; + device_set_desc(sc->sc_dev, + "Non-standard ns8250 class UART with FIFOs"); + } + + /* + * Force the Tx FIFO size to 16 bytes for now. We don't program the + * Tx trigger. Also, we assume that all data has been sent when the + * interrupt happens. + */ + sc->sc_txfifosz = 16; + + return (0); +} + +static int +ns8250_bus_receive(struct uart_softc *sc) +{ + struct uart_bas *bas; + int xc; + uint8_t lsr; + + bas = &sc->sc_bas; + while (!uart_rx_full(sc)) { + lsr = uart_getreg(bas, REG_LSR); + if ((lsr & LSR_RXRDY) == 0) + break; + xc = uart_getreg(bas, REG_DATA); + if (lsr & LSR_FE) + xc |= UART_STAT_FRAMERR; + if (lsr & LSR_PE) + xc |= UART_STAT_PARERR; + uart_rx_put(sc, xc); + } + return (0); +} + +static int +ns8250_bus_setsig(struct uart_softc *sc, int sig) +{ + struct ns8250_softc *ns8250 = (struct ns8250_softc*)sc; + struct uart_bas *bas; + uint32_t new, old; + + bas = &sc->sc_bas; + do { + old = sc->sc_hwsig; + new = old; + if (sig & UART_SIG_DDTR) { + SIGCHG(sig & UART_SIG_DTR, new, UART_SIG_DTR, + UART_SIG_DDTR); + } + if (sig & UART_SIG_DRTS) { + SIGCHG(sig & UART_SIG_RTS, new, UART_SIG_RTS, + UART_SIG_DRTS); + } + } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new)); + ns8250->mcr &= ~(MCR_DTR|MCR_RTS); + if (new & UART_SIG_DTR) + ns8250->mcr |= MCR_DTR; + if (new & UART_SIG_RTS) + ns8250->mcr |= MCR_RTS; + uart_setreg(bas, REG_MCR, ns8250->mcr); + uart_barrier(bas); + return (0); +} + +static int +ns8250_bus_transmit(struct uart_softc *sc) +{ + struct ns8250_softc *ns8250 = (struct ns8250_softc*)sc; + struct uart_bas *bas; + int i; + + bas = &sc->sc_bas; + while ((uart_getreg(bas, REG_LSR) & LSR_THRE) == 0) + ; + uart_setreg(bas, REG_IER, ns8250->ier | IER_ETXRDY); + uart_barrier(bas); + for (i = 0; i < sc->sc_txdatasz; i++) { + uart_setreg(bas, REG_DATA, sc->sc_txbuf[i]); + uart_barrier(bas); + } + sc->sc_txbusy = 1; + return (0); +} diff --git a/sys/dev/uart/uart_dev_ns8250.h b/sys/dev/uart/uart_dev_ns8250.h new file mode 100644 index 0000000..85c4722 --- /dev/null +++ b/sys/dev/uart/uart_dev_ns8250.h @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2003 Marcel Moolenaar + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _DEV_UART_DEV_NS8250_H_ +#define _DEV_UART_DEV_NS8250_H_ + +/* Enhanced Feature Register. */ +#define EFR_CTS 0x80 +#define EFR_RTS 0x40 +#define EFR_SCD 0x20 /* Special Character Detect. */ +#define EFR_EFC 0x10 /* Enhanced Function Control. */ +#define EFR_SFC_MASK 0x0f /* Software Flow Control. */ +#define EFR_SFC_TX12 0x0c /* BIT: Transmit XON1+2/XOFF1+2. */ +#define EFR_SFC_TX1 0x08 /* BIT: Transmit XON1/XOFF1. */ +#define EFR_SFC_TX2 0x04 /* BIT: Transmit XON2/XOFF2. */ +#define EFR_SFC_RX1 0x02 /* BIT: Receive XON1/XOFF1. */ +#define EFR_SFC_RX2 0x01 /* BIT: Receive XON2/XOFF2. */ +#define EFR_SFC_T12R12 0x0f /* VAL: TX 1+2, RX 1+2. */ +#define EFR_SFC_T1R12 0x0b /* VAL: TX 1, RX 1+2. */ +#define EFR_SFC_T2R12 0x07 /* VAL: TX 2, RX 1+2. */ + +/* FIFO Control Register. */ +#define FCR_RX_HIGH 0xc0 +#define FCR_RX_MEDH 0x80 +#define FCR_RX_MEDL 0x40 +#define FCR_RX_LOW 0x00 +#define FCR_TX_HIGH 0x30 +#define FCR_TX_MEDH 0x20 +#define FCR_TX_LOW 0x10 +#define FCR_TX_MEDL 0x00 +#define FCR_DMA 0x08 +#define FCR_XMT_RST 0x04 +#define FCR_RCV_RST 0x02 +#define FCR_ENABLE 0x01 + +/* Interrupt Enable Register. */ +#define IER_CTS 0x80 +#define IER_RTS 0x40 +#define IER_XOFF 0x20 +#define IER_SLEEP 0x10 +#define IER_EMSC 0x08 +#define IER_ERLS 0x04 +#define IER_ETXRDY 0x02 +#define IER_ERXRDY 0x01 + +/* Interrupt Identification Register. */ +#define IIR_FIFO_MASK 0xc0 +#define IIR_RTSCTS 0x20 +#define IIR_XOFF 0x10 +#define IIR_IMASK 0x0f +#define IIR_RXTOUT 0x0c +#define IIR_RLS 0x06 +#define IIR_RXRDY 0x04 +#define IIR_TXRDY 0x02 +#define IIR_MLSC 0x00 +#define IIR_NOPEND 0x01 + +/* Line Control Register. */ +#define LCR_DLAB 0x80 +#define LCR_SBREAK 0x40 +#define LCR_PZERO 0x30 +#define LCR_PONE 0x20 +#define LCR_PEVEN 0x10 +#define LCR_PODD 0x00 +#define LCR_PENAB 0x08 +#define LCR_STOPB 0x04 +#define LCR_8BITS 0x03 +#define LCR_7BITS 0x02 +#define LCR_6BITS 0x01 +#define LCR_5BITS 0x00 + +/* Line Status Register. */ +#define LSR_DERR 0x80 +#define LSR_TEMT 0x40 /* Transmitter Empty. */ +#define LSR_THRE 0x20 /* Transmitter Holding Register Empty. */ +#define LSR_BI 0x10 +#define LSR_FE 0x08 +#define LSR_PE 0x04 +#define LSR_OE 0x02 +#define LSR_RXRDY 0x01 + +/* Modem Control Register. */ +#define MCR_CS 0x80 +#define MCR_IRE 0x40 +#define MCR_ISEL 0x20 +#define MCR_LOOPBACK 0x10 +#define MCR_IE 0x08 +#define MCR_LBDCD MCR_IE +#define MCR_LBRI 0x04 +#define MCR_RTS 0x02 +#define MCR_DTR 0x01 + +/* Modem Status Register. */ +#define MSR_DCD 0x80 +#define MSR_RI 0x40 +#define MSR_DSR 0x20 +#define MSR_CTS 0x10 +#define MSR_DDCD 0x08 +#define MSR_TERI 0x04 +#define MSR_DDSR 0x02 +#define MSR_DCTS 0x01 + +/* General registers. */ +#define REG_DATA 0 /* Data Register. */ +#define REG_RBR REG_DATA /* Receiver Buffer Register (R). */ +#define REG_THR REG_DATA /* Transmitter Holding Register (W). */ +#define REG_IER 1 /* Interrupt Enable Register */ +#define REG_IIR 2 /* Interrupt Ident. Register (R). */ +#define REG_FCR 2 /* FIFO Control Register (W). */ +#define REG_LCR 3 /* Line Control Register. */ +#define REG_MCR 4 /* Modem Control Register. */ +#define REG_LSR 5 /* Line Status Register. */ +#define REG_MSR 6 /* Modem Status Register. */ +#define REG_SPR 7 /* Scratch Pad Register. */ + +/* Baudrate registers (LCR[7] = 1). */ +#define REG_DLBL 0 /* Divisor Latch (LSB). */ +#define REG_DLBH 1 /* Divisor Latch (MSB). */ +#define REG_DL REG_DLBL /* Divisor Latch (16-bit I/O). */ + +/* Enhanced registers (LCR = 0xBF). */ +#define REG_EFR 2 /* Enhanced Feature Register. */ +#define REG_XON1 4 /* XON character 1. */ +#define REG_XON2 5 /* XON character 2. */ +#define REG_XOFF1 6 /* XOFF character 1. */ +#define REG_XOFF2 7 /* XOFF character 2. */ + +#endif /* _DEV_UART_DEV_NS8250_H_ */ diff --git a/sys/dev/uart/uart_dev_sab82532.c b/sys/dev/uart/uart_dev_sab82532.c new file mode 100644 index 0000000..b7ca2e4 --- /dev/null +++ b/sys/dev/uart/uart_dev_sab82532.c @@ -0,0 +1,670 @@ +/* + * Copyright (c) 2003 Marcel Moolenaar + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/conf.h> +#include <machine/bus.h> + +#include <dev/uart/uart.h> +#include <dev/uart/uart_cpu.h> +#include <dev/uart/uart_bus.h> +#include <dev/uart/uart_dev_sab82532.h> + +#include "uart_if.h" + +#define DEFAULT_RCLK 29491200 + +#define IS_CHANNEL_A(bas) (((bas)->bsh & 0x40) == 0x00) +#define IS_CHANNEL_B(bas) (((bas)->bsh & 0x40) == 0x40) + +/* + * NOTE: To allow us to read the baudrate divisor from the chip, we + * copy the value written to the write-only BGR register to an unused + * read-write register. We use TCR for that. + */ + +static int +sab82532_delay(struct uart_bas *bas) +{ + int divisor, m, n; + uint8_t bgr, ccr2; + + bgr = uart_getreg(bas, SAB_TCR); + ccr2 = uart_getreg(bas, SAB_CCR2); + n = (bgr & 0x3f) + 1; + m = (bgr >> 6) | ((ccr2 >> 4) & 0xC); + divisor = n * (1<<m); + + /* 1/10th the time to transmit 1 character (estimate). */ + return (16000000 * divisor / bas->rclk); +} + +static int +sab82532_divisor(int rclk, int baudrate) +{ + int act_baud, act_div, divisor; + int error, m, n; + + if (baudrate == 0) + return (0); + + divisor = (rclk / (baudrate << 3) + 1) >> 1; + if (divisor < 2 || divisor >= 1048576) + return (0); + + /* Find the best (N+1,M) pair. */ + for (m = 1; m < 15; m++) { + n = divisor / (1<<m); + if (n < 1 || n > 63) + continue; + act_div = n * (1<<m); + act_baud = rclk / (act_div << 4); + + /* 10 times error in percent: */ + error = ((act_baud - baudrate) * 2000 / baudrate + 1) >> 1; + + /* 3.0% maximum error tolerance: */ + if (error < -30 || error > 30) + continue; + + /* Got it. */ + return ((n - 1) | (m << 6)); + } + + return (0); +} + +static void +sab82532_flush(struct uart_bas *bas, int what) +{ + + if (what & UART_FLUSH_TRANSMITTER) { + while (uart_getreg(bas, SAB_STAR) & SAB_STAR_CEC) + ; + uart_setreg(bas, SAB_CMDR, SAB_CMDR_XRES); + uart_barrier(bas); + } + if (what & UART_FLUSH_RECEIVER) { + while (uart_getreg(bas, SAB_STAR) & SAB_STAR_CEC) + ; + uart_setreg(bas, SAB_CMDR, SAB_CMDR_RRES); + uart_barrier(bas); + } +} + +static int +sab82532_param(struct uart_bas *bas, int baudrate, int databits, int stopbits, + int parity) +{ + int divisor; + uint8_t ccr2, dafo; + + if (databits >= 8) + dafo = SAB_DAFO_CHL_CS8; + else if (databits == 7) + dafo = SAB_DAFO_CHL_CS7; + else if (databits == 6) + dafo = SAB_DAFO_CHL_CS6; + else + dafo = SAB_DAFO_CHL_CS5; + if (stopbits > 1) + dafo |= SAB_DAFO_STOP; + switch (parity) { + case UART_PARITY_EVEN: dafo |= SAB_DAFO_PAR_EVEN; break; + case UART_PARITY_MARK: dafo |= SAB_DAFO_PAR_MARK; break; + case UART_PARITY_NONE: dafo |= SAB_DAFO_PAR_NONE; break; + case UART_PARITY_ODD: dafo |= SAB_DAFO_PAR_ODD; break; + case UART_PARITY_SPACE: dafo |= SAB_DAFO_PAR_SPACE; break; + default: return (EINVAL); + } + + /* Set baudrate. */ + if (baudrate > 0) { + divisor = sab82532_divisor(bas->rclk, baudrate); + if (divisor == 0) + return (EINVAL); + uart_setreg(bas, SAB_BGR, divisor & 0xff); + uart_barrier(bas); + /* Allow reading the (n-1,m) tuple from the chip. */ + uart_setreg(bas, SAB_TCR, divisor & 0xff); + uart_barrier(bas); + ccr2 = uart_getreg(bas, SAB_CCR2); + ccr2 &= ~(SAB_CCR2_BR9 | SAB_CCR2_BR8); + ccr2 |= (divisor >> 2) & (SAB_CCR2_BR9 | SAB_CCR2_BR8); + uart_setreg(bas, SAB_CCR2, ccr2); + uart_barrier(bas); + } + + uart_setreg(bas, SAB_DAFO, dafo); + uart_barrier(bas); + return (0); +} + +/* + * Low-level UART interface. + */ +static int sab82532_probe(struct uart_bas *bas); +static void sab82532_init(struct uart_bas *bas, int, int, int, int); +static void sab82532_term(struct uart_bas *bas); +static void sab82532_putc(struct uart_bas *bas, int); +static int sab82532_poll(struct uart_bas *bas); +static int sab82532_getc(struct uart_bas *bas); + +struct uart_ops uart_sab82532_ops = { + .probe = sab82532_probe, + .init = sab82532_init, + .term = sab82532_term, + .putc = sab82532_putc, + .poll = sab82532_poll, + .getc = sab82532_getc, +}; + +static int +sab82532_probe(struct uart_bas *bas) +{ + + return (0); +} + +static void +sab82532_init(struct uart_bas *bas, int baudrate, int databits, int stopbits, + int parity) +{ + uint8_t ccr0, pvr; + + if (bas->rclk == 0) + bas->rclk = DEFAULT_RCLK; + + /* + * Set all pins, except the DTR pins (pin 1 and 2) to be inputs. + * Pin 4 is magical, meaning that I don't know what it does, but + * it too has to be set to output. + */ + uart_setreg(bas, SAB_PCR, + ~(SAB_PVR_DTR_A|SAB_PVR_DTR_B|SAB_PVR_MAGIC)); + uart_barrier(bas); + /* Disable port interrupts. */ + uart_setreg(bas, SAB_PIM, 0xff); + uart_barrier(bas); + /* Interrupts are active low. */ + uart_setreg(bas, SAB_IPC, SAB_IPC_ICPL); + uart_barrier(bas); + /* Set DTR. */ + pvr = uart_getreg(bas, SAB_PVR); + pvr &= IS_CHANNEL_A(bas) ? ~SAB_PVR_DTR_A : ~SAB_PVR_DTR_B; + uart_setreg(bas, SAB_PVR, pvr | SAB_PVR_MAGIC); + uart_barrier(bas); + + /* power down */ + uart_setreg(bas, SAB_CCR0, 0); + uart_barrier(bas); + + /* set basic configuration */ + ccr0 = SAB_CCR0_MCE|SAB_CCR0_SC_NRZ|SAB_CCR0_SM_ASYNC; + uart_setreg(bas, SAB_CCR0, ccr0); + uart_barrier(bas); + uart_setreg(bas, SAB_CCR1, SAB_CCR1_ODS|SAB_CCR1_BCR|SAB_CCR1_CM_7); + uart_barrier(bas); + uart_setreg(bas, SAB_CCR2, SAB_CCR2_BDF|SAB_CCR2_SSEL|SAB_CCR2_TOE); + uart_barrier(bas); + uart_setreg(bas, SAB_CCR3, 0); + uart_barrier(bas); + uart_setreg(bas, SAB_CCR4, SAB_CCR4_MCK4|SAB_CCR4_EBRG|SAB_CCR4_ICD); + uart_barrier(bas); + uart_setreg(bas, SAB_MODE, SAB_MODE_FCTS|SAB_MODE_RTS|SAB_MODE_RAC); + uart_barrier(bas); + uart_setreg(bas, SAB_RFC, SAB_RFC_DPS|SAB_RFC_RFDF| + SAB_RFC_RFTH_32CHAR); + uart_barrier(bas); + + sab82532_param(bas, baudrate, databits, stopbits, parity); + + /* Clear interrupts. */ + uart_setreg(bas, SAB_IMR0, 0xff); + uart_setreg(bas, SAB_IMR1, 0xff); + uart_barrier(bas); + uart_getreg(bas, SAB_ISR0); + uart_getreg(bas, SAB_ISR1); + uart_barrier(bas); + + sab82532_flush(bas, UART_FLUSH_TRANSMITTER|UART_FLUSH_RECEIVER); + + /* Power up. */ + uart_setreg(bas, SAB_CCR0, ccr0|SAB_CCR0_PU); + uart_barrier(bas); +} + +static void +sab82532_term(struct uart_bas *bas) +{ + uint8_t pvr; + + pvr = uart_getreg(bas, SAB_PVR); + pvr |= IS_CHANNEL_A(bas) ? SAB_PVR_DTR_A : SAB_PVR_DTR_B; + uart_setreg(bas, SAB_PVR, pvr); + uart_barrier(bas); +} + +static void +sab82532_putc(struct uart_bas *bas, int c) +{ + int delay, limit; + + /* 1/10th the time to transmit 1 character (estimate). */ + delay = sab82532_delay(bas); + + limit = 20; + while ((uart_getreg(bas, SAB_STAR) & SAB_STAR_TEC) && --limit) + DELAY(delay); + uart_setreg(bas, SAB_TIC, c); + limit = 20; + while ((uart_getreg(bas, SAB_STAR) & SAB_STAR_TEC) && --limit) + DELAY(delay); +} + +static int +sab82532_poll(struct uart_bas *bas) +{ + + if (uart_getreg(bas, SAB_STAR) & SAB_STAR_RFNE) + return (sab82532_getc(bas)); + return (-1); +} + +static int +sab82532_getc(struct uart_bas *bas) +{ + int c, delay; + + /* 1/10th the time to transmit 1 character (estimate). */ + delay = sab82532_delay(bas); + + while (!(uart_getreg(bas, SAB_STAR) & SAB_STAR_RFNE)) + DELAY(delay); + + while (uart_getreg(bas, SAB_STAR) & SAB_STAR_CEC) + ; + uart_setreg(bas, SAB_CMDR, SAB_CMDR_RFRD); + uart_barrier(bas); + + while (!(uart_getreg(bas, SAB_ISR0) & SAB_ISR0_TCD)) + DELAY(delay); + + c = uart_getreg(bas, SAB_RFIFO); + uart_barrier(bas); + + /* Blow away everything left in the FIFO... */ + while (uart_getreg(bas, SAB_STAR) & SAB_STAR_CEC) + ; + uart_setreg(bas, SAB_CMDR, SAB_CMDR_RMC); + uart_barrier(bas); + return (c); +} + +/* + * High-level UART interface. + */ +struct sab82532_softc { + struct uart_softc base; +}; + +static int sab82532_bus_attach(struct uart_softc *); +static int sab82532_bus_detach(struct uart_softc *); +static int sab82532_bus_flush(struct uart_softc *, int); +static int sab82532_bus_getsig(struct uart_softc *); +static int sab82532_bus_ioctl(struct uart_softc *, int, intptr_t); +static int sab82532_bus_ipend(struct uart_softc *); +static int sab82532_bus_param(struct uart_softc *, int, int, int, int); +static int sab82532_bus_probe(struct uart_softc *); +static int sab82532_bus_receive(struct uart_softc *); +static int sab82532_bus_setsig(struct uart_softc *, int); +static int sab82532_bus_transmit(struct uart_softc *); + +static kobj_method_t sab82532_methods[] = { + KOBJMETHOD(uart_attach, sab82532_bus_attach), + KOBJMETHOD(uart_detach, sab82532_bus_detach), + KOBJMETHOD(uart_flush, sab82532_bus_flush), + KOBJMETHOD(uart_getsig, sab82532_bus_getsig), + KOBJMETHOD(uart_ioctl, sab82532_bus_ioctl), + KOBJMETHOD(uart_ipend, sab82532_bus_ipend), + KOBJMETHOD(uart_param, sab82532_bus_param), + KOBJMETHOD(uart_probe, sab82532_bus_probe), + KOBJMETHOD(uart_receive, sab82532_bus_receive), + KOBJMETHOD(uart_setsig, sab82532_bus_setsig), + KOBJMETHOD(uart_transmit, sab82532_bus_transmit), + { 0, 0 } +}; + +struct uart_class uart_sab82532_class = { + "sab82532 class", + sab82532_methods, + sizeof(struct sab82532_softc), + .uc_range = 64, + .uc_rclk = DEFAULT_RCLK +}; + +#define SIGCHG(c, i, s, d) \ + if (c) { \ + i |= (i & s) ? s : s | d; \ + } else { \ + i = (i & s) ? (i & ~s) | d : i; \ + } + +static int +sab82532_bus_attach(struct uart_softc *sc) +{ + struct uart_bas *bas; + uint8_t imr0, imr1; + + bas = &sc->sc_bas; + if (sc->sc_sysdev == NULL) + sab82532_init(bas, 9600, 8, 1, UART_PARITY_NONE); + + sc->sc_rxfifosz = 32; + sc->sc_txfifosz = 32; + + imr0 = SAB_IMR0_TCD|SAB_IMR0_TIME|SAB_IMR0_CDSC|SAB_IMR0_RFO| + SAB_IMR0_RPF; + uart_setreg(bas, SAB_IMR0, 0xff & ~imr0); + imr1 = SAB_IMR1_BRKT|SAB_IMR1_ALLS|SAB_IMR1_CSC; + uart_setreg(bas, SAB_IMR1, 0xff & ~imr1); + uart_barrier(bas); + + if (sc->sc_sysdev == NULL) + sab82532_bus_setsig(sc, UART_SIG_DDTR|UART_SIG_DRTS); + (void)sab82532_bus_getsig(sc); + return (0); +} + +static int +sab82532_bus_detach(struct uart_softc *sc) +{ + struct uart_bas *bas; + + bas = &sc->sc_bas; + uart_setreg(bas, SAB_IMR0, 0xff); + uart_setreg(bas, SAB_IMR1, 0xff); + uart_barrier(bas); + uart_getreg(bas, SAB_ISR0); + uart_getreg(bas, SAB_ISR1); + uart_barrier(bas); + uart_setreg(bas, SAB_CCR0, 0); + uart_barrier(bas); + return (0); +} + +static int +sab82532_bus_flush(struct uart_softc *sc, int what) +{ + + sab82532_flush(&sc->sc_bas, what); + return (0); +} + +static int +sab82532_bus_getsig(struct uart_softc *sc) +{ + struct uart_bas *bas; + uint32_t new, old, sig; + uint8_t pvr, star, vstr; + + bas = &sc->sc_bas; + do { + old = sc->sc_hwsig; + sig = old; + star = uart_getreg(bas, SAB_STAR); + SIGCHG(star & SAB_STAR_CTS, sig, UART_SIG_CTS, UART_SIG_DCTS); + vstr = uart_getreg(bas, SAB_VSTR); + SIGCHG(vstr & SAB_VSTR_CD, sig, UART_SIG_DCD, UART_SIG_DDCD); + pvr = uart_getreg(bas, SAB_PVR); + pvr &= (IS_CHANNEL_A(bas)) ? SAB_PVR_DSR_A : SAB_PVR_DSR_B; + SIGCHG(~pvr, sig, UART_SIG_DSR, UART_SIG_DDSR); + new = sig & ~UART_SIGMASK_DELTA; + } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new)); + return (sig); +} + +static int +sab82532_bus_ioctl(struct uart_softc *sc, int request, intptr_t data) +{ + struct uart_bas *bas; + uint8_t dafo, mode; + + bas = &sc->sc_bas; + switch (request) { + case UART_IOCTL_BREAK: + dafo = uart_getreg(bas, SAB_DAFO); + if (data) + dafo |= SAB_DAFO_XBRK; + else + dafo &= ~SAB_DAFO_XBRK; + uart_setreg(bas, SAB_DAFO, dafo); + uart_barrier(bas); + break; + case UART_IOCTL_IFLOW: + mode = uart_getreg(bas, SAB_MODE); + if (data) { + mode &= ~SAB_MODE_RTS; + mode |= SAB_MODE_FRTS; + } else { + mode |= SAB_MODE_RTS; + mode &= ~SAB_MODE_FRTS; + } + uart_setreg(bas, SAB_MODE, mode); + uart_barrier(bas); + break; + case UART_IOCTL_OFLOW: + mode = uart_getreg(bas, SAB_MODE); + if (data) + mode &= ~SAB_MODE_FCTS; + else + mode |= SAB_MODE_FCTS; + uart_setreg(bas, SAB_MODE, mode); + uart_barrier(bas); + break; + default: + return (EINVAL); + } + return (0); +} + +static int +sab82532_bus_ipend(struct uart_softc *sc) +{ + struct uart_bas *bas; + int ipend; + uint8_t isr0, isr1; + + bas = &sc->sc_bas; + isr0 = uart_getreg(bas, SAB_ISR0); + isr1 = uart_getreg(bas, SAB_ISR1); + uart_barrier(bas); + + if (isr0 & SAB_ISR0_TIME) { + while (uart_getreg(bas, SAB_STAR) & SAB_STAR_CEC) + ; + uart_setreg(bas, SAB_CMDR, SAB_CMDR_RFRD); + uart_barrier(bas); + } + + ipend = 0; + if (isr1 & SAB_ISR1_BRKT) + ipend |= UART_IPEND_BREAK; + if (isr0 & SAB_ISR0_RFO) + ipend |= UART_IPEND_OVERRUN; + if (isr0 & (SAB_ISR0_TCD|SAB_ISR0_RPF)) + ipend |= UART_IPEND_RXREADY; + if ((isr0 & SAB_ISR0_CDSC) || (isr1 & SAB_ISR1_CSC)) + ipend |= UART_IPEND_SIGCHG; + if (isr1 & SAB_ISR1_ALLS) + ipend |= UART_IPEND_TXIDLE; + + return (ipend); +} + +static int +sab82532_bus_param(struct uart_softc *sc, int baudrate, int databits, + int stopbits, int parity) +{ + struct uart_bas *bas; + + bas = &sc->sc_bas; + return (sab82532_param(bas, baudrate, databits, stopbits, parity)); +} + +static int +sab82532_bus_probe(struct uart_softc *sc) +{ + char buf[80]; + const char *ch, *vstr; + int error; + + error = sab82532_probe(&sc->sc_bas); + if (error) + return (error); + + /* Assume the address range is naturally aligned. */ + ch = IS_CHANNEL_A(&sc->sc_bas) ? "A" : "B"; + + switch (uart_getreg(&sc->sc_bas, SAB_VSTR) & SAB_VSTR_VMASK) { + case SAB_VSTR_V_1: + vstr = "v1"; + break; + case SAB_VSTR_V_2: + vstr = "v2"; + break; + case SAB_VSTR_V_32: + vstr = "v3.2"; + sc->sc_hwiflow = 0; /* CTS doesn't work with RFC:RFDF. */ + sc->sc_hwoflow = 1; + break; + default: + vstr = "v4?"; + break; + } + + snprintf(buf, sizeof(buf), "SAB 82532 %s, channel %s", vstr, ch); + device_set_desc_copy(sc->sc_dev, buf); + return (0); +} + +static int +sab82532_bus_receive(struct uart_softc *sc) +{ + struct uart_bas *bas; + int i, rbcl, xc; + uint8_t s; + + bas = &sc->sc_bas; + if (uart_getreg(bas, SAB_STAR) & SAB_STAR_RFNE) { + rbcl = uart_getreg(bas, SAB_RBCL) & 31; + if (rbcl == 0) + rbcl = 32; + for (i = 0; i < rbcl; i += 2) { + if (uart_rx_full(sc)) { + sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN; + break; + } + xc = uart_getreg(bas, SAB_RFIFO); + s = uart_getreg(bas, SAB_RFIFO + 1); + if (s & SAB_RSTAT_FE) + xc |= UART_STAT_FRAMERR; + if (s & SAB_RSTAT_PE) + xc |= UART_STAT_PARERR; + uart_rx_put(sc, xc); + } + } + + while (uart_getreg(bas, SAB_STAR) & SAB_STAR_CEC) + ; + uart_setreg(bas, SAB_CMDR, SAB_CMDR_RMC); + uart_barrier(bas); + return (0); +} + +static int +sab82532_bus_setsig(struct uart_softc *sc, int sig) +{ + struct uart_bas *bas; + uint32_t new, old; + uint8_t mode, pvr; + + bas = &sc->sc_bas; + do { + old = sc->sc_hwsig; + new = old; + if (sig & UART_SIG_DDTR) { + SIGCHG(sig & UART_SIG_DTR, new, UART_SIG_DTR, + UART_SIG_DDTR); + } + if (sig & UART_SIG_DRTS) { + SIGCHG(sig & UART_SIG_RTS, new, UART_SIG_RTS, + UART_SIG_DRTS); + } + } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new)); + + /* Set DTR pin. */ + pvr = uart_getreg(bas, SAB_PVR); + if (new & UART_SIG_DTR) + pvr &= (IS_CHANNEL_A(bas)) ? ~SAB_PVR_DTR_A : ~SAB_PVR_DTR_B; + else + pvr |= (IS_CHANNEL_A(bas)) ? SAB_PVR_DTR_A : SAB_PVR_DTR_B; + uart_setreg(bas, SAB_PVR, pvr); + + /* Set RTS pin. */ + mode = uart_getreg(bas, SAB_MODE); + if (new & UART_SIG_RTS) + mode &= ~SAB_MODE_FRTS; + else + mode |= SAB_MODE_FRTS; + uart_setreg(bas, SAB_MODE, mode); + uart_barrier(bas); + return (0); +} + +static int +sab82532_bus_transmit(struct uart_softc *sc) +{ + struct uart_bas *bas; + int i; + + bas = &sc->sc_bas; + while (!(uart_getreg(bas, SAB_STAR) & SAB_STAR_XFW)) + ; + for (i = 0; i < sc->sc_txdatasz; i++) + uart_setreg(bas, SAB_XFIFO + i, sc->sc_txbuf[i]); + uart_barrier(bas); + while (uart_getreg(bas, SAB_STAR) & SAB_STAR_CEC) + ; + uart_setreg(bas, SAB_CMDR, SAB_CMDR_XF); + sc->sc_txbusy = 1; + return (0); +} diff --git a/sys/dev/uart/uart_dev_sab82532.h b/sys/dev/uart/uart_dev_sab82532.h new file mode 100644 index 0000000..8b53371 --- /dev/null +++ b/sys/dev/uart/uart_dev_sab82532.h @@ -0,0 +1,320 @@ +/* $OpenBSD: sab82532reg.h,v 1.2 2002/04/08 17:49:42 jason Exp $ */ + +/* + * Copyright (c) 2001 Jason L. Wright (jason@thought.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Jason L. Wright + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Effort sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F30602-01-2-0537. + * + * $FreeBSD$ + */ + +/* + * Register definitions for SAB82532 based on "Enhanced Serial Communication + * Controller ESCC2 Version 3.2 User's Manual 07.96" from: + * http://www.infineon.com + */ + +#define SAB_NCHAN 2 /* number of channels */ +#define SAB_CHANLEN 0x40 /* length of channel register set */ + +#define SAB_CHAN_A 0x00 /* channel A register offset */ +#define SAB_CHAN_B 0x40 /* channel B register offset */ + +#define SAB_RFIFO 0x00 /* r: rx fifo */ +#define SAB_XFIFO 0x00 /* w: tx fifo */ +#define SAB_STAR 0x20 /* r: status register */ +#define SAB_CMDR 0x20 /* w: command register */ +#define SAB_MODE 0x22 /* rw: mode register */ +#define SAB_TIMR 0x23 /* rw: timer register */ +#define SAB_XON 0x24 /* rw: xon character */ +#define SAB_XOFF 0x25 /* rw: xoff character */ +#define SAB_TCR 0x26 /* rw: termination character */ +#define SAB_DAFO 0x27 /* rw: data format */ +#define SAB_RFC 0x28 /* rw: rfifo control register */ +#define SAB_RBCL 0x2a /* r: rx byte count low */ +#define SAB_TBCL 0x2a /* w: tx byte count low */ +#define SAB_RBCH 0x2b /* r: rx byte count high */ +#define SAB_XBCH 0x2b /* w: tx byte count high */ +#define SAB_CCR0 0x2c /* rw: channel configuration register 0 */ +#define SAB_CCR1 0x2d /* rw: channel configuration register 1 */ +#define SAB_CCR2 0x2e /* rw: channel configuration register 2 */ +#define SAB_CCR3 0x2f /* rw: channel configuration register 3 */ +#define SAB_TSAX 0x30 /* w: time-slot assignment register tx */ +#define SAB_TSAR 0x31 /* w: time-slot assignment register rx */ +#define SAB_XCCR 0x32 /* w: tx channel capacity register */ +#define SAB_RCCR 0x33 /* w: receive channel capacity register */ +#define SAB_VSTR 0x34 /* r: version status */ +#define SAB_BGR 0x34 /* w: baud rate generator */ +#define SAB_TIC 0x35 /* w: transmit immediate character */ +#define SAB_MXN 0x36 /* w: mask xon character */ +#define SAB_MXF 0x37 /* w: mask xoff character */ +#define SAB_GIS 0x38 /* r: global interrupt status */ +#define SAB_IVA 0x38 /* w: interrupt vector address */ +#define SAB_IPC 0x39 /* rw: interrupt port configuration */ +#define SAB_ISR0 0x3a /* r: interrupt status 0 */ +#define SAB_IMR0 0x3a /* w: interrupt mask 0 */ +#define SAB_ISR1 0x3b /* r: interrupt status 1 */ +#define SAB_IMR1 0x3b /* w: interrupt mask 1 */ +#define SAB_PVR 0x3c /* rw: port value register */ +#define SAB_PIS 0x3d /* r: port interrupt status */ +#define SAB_PIM 0x3d /* w: port interrupt mask */ +#define SAB_PCR 0x3e /* w: port configuration register */ +#define SAB_CCR4 0x3f /* rw: channel configuration register 4 */ + +/* SAB_STAR: status register */ +#define SAB_STAR_XDOV 0x80 /* transmit data overflow */ +#define SAB_STAR_XFW 0x40 /* transmit fifo write enable */ +#define SAB_STAR_RFNE 0x20 /* rfifo not empty */ +#define SAB_STAR_FCS 0x10 /* flow control status */ +#define SAB_STAR_TEC 0x08 /* tx immediate char is executing */ +#define SAB_STAR_CEC 0x04 /* command is executing */ +#define SAB_STAR_CTS 0x02 /* cts status: 0:inactive/high,1:active/low */ + +/* SAB_CMDR: command register */ +#define SAB_CMDR_RMC 0x80 /* receive message complete */ +#define SAB_CMDR_RRES 0x40 /* receiver reset */ +#define SAB_CMDR_RFRD 0x20 /* receive fifo read enable */ +#define SAB_CMDR_STI 0x10 /* start timer */ +#define SAB_CMDR_XF 0x08 /* transmit frame */ +#define SAB_CMDR_XRES 0x01 /* transmit reset */ + +/* SAB_MODE: mode register */ +#define SAB_MODE_FRTS 0x40 /* flow control using rts */ +#define SAB_MODE_FCTS 0x20 /* flow control using cts */ +#define SAB_MODE_FLON 0x10 /* flow control on */ +#define SAB_MODE_RAC 0x08 /* receiver active */ +#define SAB_MODE_RTS 0x04 /* request to send */ +#define SAB_MODE_TRS 0x02 /* timer resolution */ +#define SAB_MODE_TLP 0x01 /* test loop */ + +/* SAB_TIMR: timer register */ +#define SAB_TIMR_CNT 0xe0 /* count mask */ +#define SAB_TIMR_VAL 0x1f /* value mask */ + +/* SAB_DAFO: data format */ +#define SAB_DAFO_XBRK 0x40 /* transmit break */ +#define SAB_DAFO_STOP 0x20 /* stop bit: 0:1 bit, 1:2 bits */ +#define SAB_DAFO_PAR1 0x10 /* parity 1, see below */ +#define SAB_DAFO_PAR0 0x08 /* parity 0, see below */ +#define SAB_DAFO_PARE 0x04 /* parity enable */ +#define SAB_DAFO_CHL1 0x02 /* character length 1, see below */ +#define SAB_DAFO_CHL0 0x01 /* character length 0, see below */ + +#define SAB_DAFO_CHL_CSIZE (SAB_DAFO_CHL1|SAB_DAFO_CHL0) +#define SAB_DAFO_CHL_CS5 (SAB_DAFO_CHL1|SAB_DAFO_CHL0) +#define SAB_DAFO_CHL_CS6 (SAB_DAFO_CHL1) +#define SAB_DAFO_CHL_CS7 (SAB_DAFO_CHL0) +#define SAB_DAFO_CHL_CS8 (0) + +#define SAB_DAFO_PARMASK (SAB_DAFO_PAR1|SAB_DAFO_PAR0|SAB_DAFO_PARE) +#define SAB_DAFO_PAR_MARK (SAB_DAFO_PAR1|SAB_DAFO_PAR0|SAB_DAFO_PARE) +#define SAB_DAFO_PAR_EVEN (SAB_DAFO_PAR1|SAB_DAFO_PARE) +#define SAB_DAFO_PAR_ODD (SAB_DAFO_PAR0|SAB_DAFO_PARE) +#define SAB_DAFO_PAR_SPACE (SAB_DAFO_PARE) +#define SAB_DAFO_PAR_NONE (0) + +/* SAB_RFC: rfifo control register */ +#define SAB_RFC_DPS 0x40 /* disable parity storage */ +#define SAB_RFC_DXS 0x20 /* disable storage of xon/xoff characters */ +#define SAB_RFC_RFDF 0x10 /* rfifo data format: 0 data,1 data+stat */ +#define SAB_RFC_RFTH1 0x08 /* rfifo threshold level 1, see below */ +#define SAB_RFC_RFTH0 0x04 /* rfifo threshold level 0, see below */ +#define SAB_RFC_TCDE 0x01 /* termination character detection enable */ + +#define SAB_RFC_RFTH_MASK (SAB_RFC_RFTH1|SAB_RFC_RFTH0) +#define SAB_RFC_RFTH_32CHAR (SAB_RFC_RFTH1|SAB_RFC_RFTH0) +#define SAB_RFC_RFTH_16CHAR (SAB_RFC_RFTH1) +#define SAB_RFC_RFTH_4CHAR (SAB_RFC_RFTH0) +#define SAB_RFC_RFTH_1CHAR (0) + +/* SAB_RBCH: received byte count high */ +#define SAB_RBCH_DMA 0x80 /* read back of XBCH DMA bit */ +#define SAB_RBCH_CAS 0x20 /* read back of XBCH CAS bit */ +#define SAB_RBCH_CNT 0x0f /* ms 4 bits of rx byte count (not used) */ + +/* SAB_XBCH: transmit byte count high */ +#define SAB_XBCH_DMA 0x80 /* dma mode: 1:dma, 0:interrupt */ +#define SAB_XBCH_CAS 0x20 /* carrier detect auto-start */ +#define SAB_XBCH_XC 0x10 /* transmit continuously */ +#define SAB_XBCH_CNT 0x0f /* ms 4 bits of tx byte count */ + +/* SAB_CCR0: channel configuration register 0 */ +#define SAB_CCR0_PU 0x80 /* 0:power-down, 1:power-up */ +#define SAB_CCR0_MCE 0x40 /* master clock enable */ +#define SAB_CCR0_SC2 0x10 /* serial port config 2, see below */ +#define SAB_CCR0_SC1 0x08 /* serial port config 1, see below */ +#define SAB_CCR0_SC0 0x04 /* serial port config 0, see below */ +#define SAB_CCR0_SM1 0x02 /* serial mode 1, see below */ +#define SAB_CCR0_SM0 0x01 /* serial mode 0, see below */ + +#define SAB_CCR0_SC_MASK (SAB_CCR0_SC2|SAB_CCR0_SC1|SAB_CCR0_SC0) +#define SAB_CCR0_SC_NRZ (0) +#define SAB_CCR0_SC_NRZI (SAB_CCR0_SC1) +#define SAB_CCR0_SC_FM0 (SAB_CCR0_SC2) +#define SAB_CCR0_SC_FM1 (SAB_CCR0_SC2|SAB_CCR0_SC0) +#define SAB_CCR0_SC_MANCHESTER (SAB_CCR0_SC2|SAB_CCR0_SC1) + +#define SAB_CCR0_SM_MASK (SAB_CCR0_SM1|SAB_CCR0_SM0) +#define SAB_CCR0_SM_DLC (0) +#define SAB_CCR0_SM_DLCLOOP (SAB_CCR0_SM0) +#define SAB_CCR0_SM_BISYNC (SAB_CCR0_SM1) +#define SAB_CCR0_SM_ASYNC (SAB_CCR0_SM1|SAB_CCR0_SM0) + +/* SAB_CCR1: channel configuration register 1 */ +#define SAB_CCR1_ODS 0x10 /* Output driver select:1:pushpull,0:odrain */ +#define SAB_CCR1_BCR 0x08 /* bit clock rate: 1:async, 0:isochronous */ +#define SAB_CCR1_CM2 0x04 /* clock mode 2, see below */ +#define SAB_CCR1_CM1 0x02 /* clock mode 1, see below */ +#define SAB_CCR1_CM0 0x01 /* clock mode 0, see below */ + +#define SAB_CCR1_CM_MASK (SAB_CCR1_CM2|SAB_CCR1_CM1|SAB_CCR1_CM0) +#define SAB_CCR1_CM_7 (SAB_CCR1_CM2|SAB_CCR1_CM1|SAB_CCR1_CM0) + +/* SAB_CCR2: channel configuration register 2, depends on clock mode above */ +/* clock mode 0a, 1, 4, 5 */ +#define SAB_CCR2_SOC1 0x80 /* special output 1, below */ +#define SAB_CCR2_SOC0 0x40 /* special output 0, below */ +#define SAB_CCR2_SOC_MASK (SAB_CCR2_SOC1|SAB_CCR2_SOC0) +#define SAB_CCR2_SOC_RTSHIGH (SAB_CCR2_SOC1) +#define SAB_CCR2_SOC_RTSNORM (0) +#define SAB_CCR2_SOC_RTSRX (SAB_CCR2_SOC1|SAB_CCR2_SOC0) +/* clock mode 0b, 2, 3, 6, 7 */ +#define SAB_CCR2_BR9 0x80 /* baud rate bit 9 */ +#define SAB_CCR2_BR8 0x40 /* baud rate bit 8 */ +#define SAB_CCR2_BDF 0x20 /* baud rate division factor: 0:1: 1:BRG */ +#define SAB_CCR2_SSEL 0x10 /* clock source select */ +/* clock mode 5 */ +#define SAB_CCR2_XCS0 0x20 /* tx clock shift, bit 0 */ +#define SAB_CCR2_RCS0 0x10 /* rx clock shift, bit 0 */ +/* clock mode 0b, 2, 3, 4, 5, 6, 7 */ +#define SAB_CCR2_TOE 0x08 /* tx clock output enable */ +/* clock mode 0a, 0b, 1, 2, 3, 4, 5, 6, 7 */ +#define SAB_CCR2_RWX 0x04 /* read/write exchange (dma mode only) */ +#define SAB_CCR2_DIV 0x01 /* data inversion (nrz) */ + +/* SAB_CCR3: channel configuration register 3 (v2 or greater) */ +#define SAB_CCR3_PSD 0x01 /* dpll phase shift disable (nrz/nrzi) */ + +/* SAB_TSAX: time-slot assignment register transmit (clock mode 5 only) */ +#define SAB_TSAX_TSNX 0xfc /* time-slot number transmit */ +#define SAB_TSAX_XCS2 0x02 /* transmit clock shift bit 2 */ +#define SAB_TSAX_XCS1 0x01 /* transmit clock shift bit 1 */ + +/* SAB_TSAR: time-slot assignment register receive (clock mode 5 only) */ +#define SAB_TSAR_TSNR 0xfc /* time-slot number receive */ +#define SAB_TSAR_RCS2 0x02 /* receive clock shift bit 2 */ +#define SAB_TSAR_RCS1 0x01 /* receive clock shift bit 1 */ + +/* SAB_VSTR: version status register */ +#define SAB_VSTR_CD 0x80 /* carrier detect status */ +#define SAB_VSTR_DPLA 0x40 /* dpll asynchronous */ +#define SAB_VSTR_VMASK 0x0f /* chip version mask: */ +#define SAB_VSTR_V_1 0x00 /* version 1 */ +#define SAB_VSTR_V_2 0x01 /* version 2 */ +#define SAB_VSTR_V_32 0x02 /* version 3.2 */ + +/* SAB_GIS: global interrupt status register */ +#define SAB_GIS_PI 0x80 /* universal port interrupt */ +#define SAB_GIS_ISA1 0x08 /* interrupt status a1 */ +#define SAB_GIS_ISA0 0x04 /* interrupt status a0 */ +#define SAB_GIS_ISB1 0x02 /* interrupt status b1 */ +#define SAB_GIS_ISB0 0x01 /* interrupt status b0 */ + +/* SAB_IVA: interrupt vector address */ +#define SAB_IVA_MASK 0xf8 /* interrupt vector address mask */ + +/* SAB_IPC: interrupt port configuration */ +#define SAB_IPC_VIS 0x80 /* masked interrupt bits visible */ +#define SAB_IPC_SLAMASK 0x18 /* slave address mask */ +#define SAB_IPC_CASM 0x04 /* cascading mode */ +#define SAB_IPC_ICMASK 0x03 /* port config mask: */ +#define SAB_IPC_ICOD 0x00 /* open drain output */ +#define SAB_IPC_ICPL 0x01 /* push/pull active low output */ +#define SAB_IPC_ICPH 0x03 /* push/pull active high output */ + +/* SAB_ISR0: interrupt status 0 */ +#define SAB_ISR0_TCD 0x80 /* termination character detected */ +#define SAB_ISR0_TIME 0x40 /* time-out limit exceeded */ +#define SAB_ISR0_PERR 0x20 /* parity error */ +#define SAB_ISR0_FERR 0x10 /* framing error */ +#define SAB_ISR0_PLLA 0x08 /* dpll asynchronous */ +#define SAB_ISR0_CDSC 0x04 /* carrier detect status change */ +#define SAB_ISR0_RFO 0x02 /* rfifo overflow */ +#define SAB_ISR0_RPF 0x01 /* receive pool full */ + +/* SAB_ISR1: interrupt status 1 */ +#define SAB_ISR1_BRK 0x80 /* break detected */ +#define SAB_ISR1_BRKT 0x40 /* break terminated */ +#define SAB_ISR1_ALLS 0x20 /* all sent */ +#define SAB_ISR1_XOFF 0x10 /* xoff detected */ +#define SAB_ISR1_TIN 0x08 /* timer interrupt */ +#define SAB_ISR1_CSC 0x04 /* clear to send status change */ +#define SAB_ISR1_XON 0x02 /* xon detected */ +#define SAB_ISR1_XPR 0x01 /* transmit pool ready */ + +/* SAB_IMR0: interrupt mask 0 */ +#define SAB_IMR0_TCD 0x80 /* termination character detected */ +#define SAB_IMR0_TIME 0x40 /* time-out limit exceeded */ +#define SAB_IMR0_PERR 0x20 /* parity error */ +#define SAB_IMR0_FERR 0x10 /* framing error */ +#define SAB_IMR0_PLLA 0x08 /* dpll asynchronous */ +#define SAB_IMR0_CDSC 0x04 /* carrier detect status change */ +#define SAB_IMR0_RFO 0x02 /* rfifo overflow */ +#define SAB_IMR0_RPF 0x01 /* receive pool full */ + +/* SAB_ISR1: interrupt mask 1 */ +#define SAB_IMR1_BRK 0x80 /* break detected */ +#define SAB_IMR1_BRKT 0x40 /* break terminated */ +#define SAB_IMR1_ALLS 0x20 /* all sent */ +#define SAB_IMR1_XDU 0x10 /* xoff detected */ +#define SAB_IMR1_TIN 0x08 /* timer interrupt */ +#define SAB_IMR1_CSC 0x04 /* clear to send status change */ +#define SAB_IMR1_XMR 0x02 /* xon detected */ +#define SAB_IMR1_XPR 0x01 /* transmit pool ready */ + +/* SAB_PVR: port value register */ +#define SAB_PVR_DSR_A 0x01 /* port A DSR */ +#define SAB_PVR_DTR_A 0x02 /* port A DTR */ +#define SAB_PVR_DTR_B 0x04 /* port B DTR */ +#define SAB_PVR_DSR_B 0x08 /* port B DSR */ +#define SAB_PVR_MAGIC 0x10 /* dunno... */ + +/* SAB_CCR4: channel configuration register 4 */ +#define SAB_CCR4_MCK4 0x80 /* master clock divide by 4 */ +#define SAB_CCR4_EBRG 0x40 /* enhanced baud rate generator mode */ +#define SAB_CCR4_TST1 0x20 /* test pin */ +#define SAB_CCR4_ICD 0x10 /* invert polarity of carrier detect */ + +/* Receive status byte */ +#define SAB_RSTAT_PE 0x80 /* parity error */ +#define SAB_RSTAT_FE 0x40 /* framing error */ +#define SAB_RSTAT_PAR 0x01 /* parity bit */ diff --git a/sys/dev/uart/uart_dev_z8530.c b/sys/dev/uart/uart_dev_z8530.c new file mode 100644 index 0000000..498b3a3 --- /dev/null +++ b/sys/dev/uart/uart_dev_z8530.c @@ -0,0 +1,512 @@ +/* + * Copyright (c) 2003 Marcel Moolenaar + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/conf.h> +#include <machine/bus.h> + +#include <dev/uart/uart.h> +#include <dev/uart/uart_cpu.h> +#include <dev/uart/uart_bus.h> +#include <dev/uart/uart_dev_z8530.h> + +#include "uart_if.h" + +#define DEFAULT_RCLK 307200 + +#define IS_CHANNEL_A(bas) (((bas)->bsh & 7) != 0) +#define IS_CHANNEL_B(bas) (((bas)->bsh & 7) == 0) + +/* Multiplexed I/O. */ +static __inline void +uart_setmreg(struct uart_bas *bas, int reg, int val) +{ + + uart_setreg(bas, REG_CTRL, reg); + uart_barrier(bas); + uart_setreg(bas, REG_CTRL, val); +} + +static __inline uint8_t +uart_getmreg(struct uart_bas *bas, int reg) +{ + + uart_setreg(bas, REG_CTRL, reg); + uart_barrier(bas); + return (uart_getreg(bas, REG_CTRL)); +} + +static int +z8530_divisor(int rclk, int baudrate) +{ + int act_baud, divisor, error; + + if (baudrate == 0) + return (0); + + divisor = (rclk + baudrate) / (baudrate << 1) - 2; + if (divisor >= 65536) + return (0); + act_baud = rclk / 2 / (divisor + 2); + + /* 10 times error in percent: */ + error = ((act_baud - baudrate) * 2000 / baudrate + 1) >> 1; + + /* 3.0% maximum error tolerance: */ + if (error < -30 || error > 30) + return (0); + + return (divisor); +} + +static int +z8530_param(struct uart_bas *bas, int baudrate, int databits, int stopbits, + int parity, uint8_t *tpcp) +{ + int divisor; + uint8_t mpm, rpc, tpc; + + rpc = RPC_RXE; + mpm = MPM_CM16; + tpc = TPC_TXE | (*tpcp & (TPC_DTR | TPC_RTS)); + + if (databits >= 8) { + rpc |= RPC_RB8; + tpc |= TPC_TB8; + } else if (databits == 7) { + rpc |= RPC_RB7; + tpc |= TPC_TB7; + } else if (databits == 6) { + rpc |= RPC_RB6; + tpc |= TPC_TB6; + } else { + rpc |= RPC_RB5; + tpc |= TPC_TB5; + } + mpm |= (stopbits > 1) ? MPM_SB2 : MPM_SB1; + switch (parity) { + case UART_PARITY_EVEN: mpm |= MPM_PE | MPM_EVEN; break; + case UART_PARITY_NONE: break; + case UART_PARITY_ODD: mpm |= MPM_PE; break; + default: return (EINVAL); + } + + /* Set baudrate. */ + if (baudrate > 0) { + divisor = z8530_divisor(bas->rclk, baudrate); + if (divisor == 0) + return (EINVAL); + uart_setmreg(bas, WR_TCL, divisor & 0xff); + uart_barrier(bas); + uart_setmreg(bas, WR_TCH, (divisor >> 8) & 0xff); + uart_barrier(bas); + } + + uart_setmreg(bas, WR_RPC, rpc); + uart_barrier(bas); + uart_setmreg(bas, WR_MPM, mpm); + uart_barrier(bas); + uart_setmreg(bas, WR_TPC, tpc); + uart_barrier(bas); + *tpcp = tpc; + return (0); +} + +static int +z8530_setup(struct uart_bas *bas, int baudrate, int databits, int stopbits, + int parity) +{ + uint8_t tpc; + + if (bas->rclk == 0) + bas->rclk = DEFAULT_RCLK; + + /* Assume we don't need to perform a full hardware reset. */ + uart_setmreg(bas, WR_MIC, ((IS_CHANNEL_A(bas)) ? MIC_CRA : MIC_CRB) | + MIC_MIE | MIC_NV); + uart_barrier(bas); + /* Set clock sources and enable BRG. */ + uart_setmreg(bas, WR_CMC, CMC_RC_BRG | CMC_TC_BRG); + uart_setmreg(bas, WR_MCB2, MCB2_PCLK | MCB2_BRGE); + uart_barrier(bas); + /* Set data encoding. */ + uart_setmreg(bas, WR_MCB1, MCB1_NRZ); + uart_barrier(bas); + + tpc = TPC_DTR | TPC_RTS; + z8530_param(bas, baudrate, databits, stopbits, parity, &tpc); + return (int)tpc; +} + +/* + * Low-level UART interface. + */ +static int z8530_probe(struct uart_bas *bas); +static void z8530_init(struct uart_bas *bas, int, int, int, int); +static void z8530_term(struct uart_bas *bas); +static void z8530_putc(struct uart_bas *bas, int); +static int z8530_poll(struct uart_bas *bas); +static int z8530_getc(struct uart_bas *bas); + +struct uart_ops uart_z8530_ops = { + .probe = z8530_probe, + .init = z8530_init, + .term = z8530_term, + .putc = z8530_putc, + .poll = z8530_poll, + .getc = z8530_getc, +}; + +static int +z8530_probe(struct uart_bas *bas) +{ + + return (0); +} + +static void +z8530_init(struct uart_bas *bas, int baudrate, int databits, int stopbits, + int parity) +{ + + z8530_setup(bas, baudrate, databits, stopbits, parity); +} + +static void +z8530_term(struct uart_bas *bas) +{ +} + +static void +z8530_putc(struct uart_bas *bas, int c) +{ + + while (!(uart_getmreg(bas, RR_BES) & BES_TXE)) + ; + uart_setreg(bas, REG_DATA, c); + uart_barrier(bas); +} + +static int +z8530_poll(struct uart_bas *bas) +{ + + if (!(uart_getmreg(bas, RR_BES) & BES_RXA)) + return (-1); + return (uart_getreg(bas, REG_DATA)); +} + +static int +z8530_getc(struct uart_bas *bas) +{ + + while (!(uart_getmreg(bas, RR_BES) & BES_RXA)) + ; + return (uart_getreg(bas, REG_DATA)); +} + +/* + * High-level UART interface. + */ +struct z8530_softc { + struct uart_softc base; + uint8_t tpc; +}; + +static int z8530_bus_attach(struct uart_softc *); +static int z8530_bus_detach(struct uart_softc *); +static int z8530_bus_flush(struct uart_softc *, int); +static int z8530_bus_getsig(struct uart_softc *); +static int z8530_bus_ioctl(struct uart_softc *, int, intptr_t); +static int z8530_bus_ipend(struct uart_softc *); +static int z8530_bus_param(struct uart_softc *, int, int, int, int); +static int z8530_bus_probe(struct uart_softc *); +static int z8530_bus_receive(struct uart_softc *); +static int z8530_bus_setsig(struct uart_softc *, int); +static int z8530_bus_transmit(struct uart_softc *); + +static kobj_method_t z8530_methods[] = { + KOBJMETHOD(uart_attach, z8530_bus_attach), + KOBJMETHOD(uart_detach, z8530_bus_detach), + KOBJMETHOD(uart_flush, z8530_bus_flush), + KOBJMETHOD(uart_getsig, z8530_bus_getsig), + KOBJMETHOD(uart_ioctl, z8530_bus_ioctl), + KOBJMETHOD(uart_ipend, z8530_bus_ipend), + KOBJMETHOD(uart_param, z8530_bus_param), + KOBJMETHOD(uart_probe, z8530_bus_probe), + KOBJMETHOD(uart_receive, z8530_bus_receive), + KOBJMETHOD(uart_setsig, z8530_bus_setsig), + KOBJMETHOD(uart_transmit, z8530_bus_transmit), + { 0, 0 } +}; + +struct uart_class uart_z8530_class = { + "z8530 class", + z8530_methods, + sizeof(struct z8530_softc), + .uc_range = 2, + .uc_rclk = DEFAULT_RCLK +}; + +#define SIGCHG(c, i, s, d) \ + if (c) { \ + i |= (i & s) ? s : s | d; \ + } else { \ + i = (i & s) ? (i & ~s) | d : i; \ + } + +static int +z8530_bus_attach(struct uart_softc *sc) +{ + struct z8530_softc *z8530 = (struct z8530_softc*)sc; + struct uart_bas *bas; + struct uart_devinfo *di; + + bas = &sc->sc_bas; + if (sc->sc_sysdev != NULL) { + di = sc->sc_sysdev; + z8530->tpc = TPC_DTR|TPC_RTS; + z8530_param(bas, di->baudrate, di->databits, di->stopbits, + di->parity, &z8530->tpc); + } else { + z8530->tpc = z8530_setup(bas, 9600, 8, 1, UART_PARITY_NONE); + z8530->tpc &= ~(TPC_DTR|TPC_RTS); + } + + sc->sc_rxfifosz = 3; + sc->sc_txfifosz = 1; + + (void)z8530_bus_getsig(sc); + + uart_setmreg(bas, WR_IC, IC_BRK | IC_CTS | IC_DCD); + uart_barrier(bas); + uart_setmreg(bas, WR_IDT, IDT_TIE | IDT_RIA); + uart_barrier(bas); + uart_setmreg(bas, WR_IV, 0); + uart_barrier(bas); + uart_setmreg(bas, WR_TPC, z8530->tpc); + uart_barrier(bas); + return (0); +} + +static int +z8530_bus_detach(struct uart_softc *sc) +{ + + return (0); +} + +static int +z8530_bus_flush(struct uart_softc *sc, int what) +{ + + return (0); +} + +static int +z8530_bus_getsig(struct uart_softc *sc) +{ + struct uart_bas *bas; + uint32_t new, old, sig; + uint8_t bes; + + bas = &sc->sc_bas; + do { + old = sc->sc_hwsig; + sig = old; + bes = uart_getmreg(bas, RR_BES); + SIGCHG(bes & BES_CTS, sig, UART_SIG_CTS, UART_SIG_DCTS); + SIGCHG(bes & BES_DCD, sig, UART_SIG_DCD, UART_SIG_DDCD); + new = sig & ~UART_SIGMASK_DELTA; + } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new)); + return (sig); +} + +static int +z8530_bus_ioctl(struct uart_softc *sc, int request, intptr_t data) +{ + struct z8530_softc *z8530 = (struct z8530_softc*)sc; + struct uart_bas *bas; + + bas = &sc->sc_bas; + switch (request) { + case UART_IOCTL_BREAK: + if (data) + z8530->tpc |= TPC_BRK; + else + z8530->tpc &= ~TPC_BRK; + uart_setmreg(bas, WR_TPC, z8530->tpc); + uart_barrier(bas); + break; + default: + return (EINVAL); + } + return (0); +} + +static int +z8530_bus_ipend(struct uart_softc *sc) +{ + struct uart_bas *bas; + int ipend; + uint32_t sig; + uint8_t bes, src; + + bas = &sc->sc_bas; + ipend = 0; + uart_setreg(bas, REG_CTRL, CR_RSTIUS); + uart_barrier(bas); + bes = uart_getmreg(bas, RR_BES); + if (bes & BES_BRK) { + uart_setreg(bas, REG_CTRL, CR_RSTXSI); + ipend |= UART_IPEND_BREAK; + } + if (bes & BES_TXE) { + uart_setreg(bas, REG_CTRL, CR_RSTTXI); + ipend |= UART_IPEND_TXIDLE; + } + if (bes & BES_RXA) + ipend |= UART_IPEND_RXREADY; + sig = sc->sc_hwsig; + SIGCHG(bes & BES_CTS, sig, UART_SIG_CTS, UART_SIG_DCTS); + SIGCHG(bes & BES_DCD, sig, UART_SIG_DCD, UART_SIG_DDCD); + if (sig & UART_SIGMASK_DELTA) + ipend |= UART_IPEND_SIGCHG; + src = uart_getmreg(bas, RR_SRC); + if (src & SRC_OVR) { + uart_setreg(bas, REG_CTRL, CR_RSTERR); + ipend |= UART_IPEND_OVERRUN; + } + return (ipend); +} + +static int +z8530_bus_param(struct uart_softc *sc, int baudrate, int databits, + int stopbits, int parity) +{ + struct z8530_softc *z8530 = (struct z8530_softc*)sc; + int error; + + error = z8530_param(&sc->sc_bas, baudrate, databits, stopbits, parity, + &z8530->tpc); + return (error); +} + +static int +z8530_bus_probe(struct uart_softc *sc) +{ + char buf[80]; + const char *ch; + int error; + + error = z8530_probe(&sc->sc_bas); + if (error) + return (error); + + /* Assume the address range is naturally aligned. */ + ch = IS_CHANNEL_A(&sc->sc_bas) ? "A" : "B"; + + snprintf(buf, sizeof(buf), "z8530, channel %s", ch); + device_set_desc_copy(sc->sc_dev, buf); + return (0); +} + +static int +z8530_bus_receive(struct uart_softc *sc) +{ + struct uart_bas *bas; + int xc; + uint8_t bes, src; + + bas = &sc->sc_bas; + bes = uart_getmreg(bas, RR_BES); + while ((bes & BES_RXA) && !uart_rx_full(sc)) { + src = uart_getmreg(bas, RR_SRC); + xc = uart_getreg(bas, REG_DATA); + if (src & SRC_FE) + xc |= UART_STAT_FRAMERR; + if (src & SRC_PE) + xc |= UART_STAT_PARERR; + uart_rx_put(sc, xc); + if (src & (SRC_FE | SRC_PE)) + uart_setreg(bas, REG_CTRL, CR_RSTERR); + bes = uart_getmreg(bas, RR_BES); + } + return (0); +} + +static int +z8530_bus_setsig(struct uart_softc *sc, int sig) +{ + struct z8530_softc *z8530 = (struct z8530_softc*)sc; + struct uart_bas *bas; + uint32_t new, old; + + bas = &sc->sc_bas; + do { + old = sc->sc_hwsig; + new = old; + if (sig & UART_SIG_DDTR) { + SIGCHG(sig & UART_SIG_DTR, new, UART_SIG_DTR, + UART_SIG_DDTR); + } + if (sig & UART_SIG_DRTS) { + SIGCHG(sig & UART_SIG_RTS, new, UART_SIG_RTS, + UART_SIG_DRTS); + } + } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new)); + + if (new & UART_SIG_DTR) + z8530->tpc |= TPC_DTR; + else + z8530->tpc &= ~TPC_DTR; + if (new & UART_SIG_RTS) + z8530->tpc |= TPC_RTS; + else + z8530->tpc &= ~TPC_RTS; + uart_setmreg(bas, WR_TPC, z8530->tpc); + uart_barrier(bas); + return (0); +} + +static int +z8530_bus_transmit(struct uart_softc *sc) +{ + struct uart_bas *bas; + + bas = &sc->sc_bas; + while (!(uart_getmreg(bas, RR_BES) & BES_TXE)) + ; + uart_setreg(bas, REG_DATA, sc->sc_txbuf[0]); + uart_barrier(bas); + sc->sc_txbusy = 1; + return (0); +} diff --git a/sys/dev/uart/uart_dev_z8530.h b/sys/dev/uart/uart_dev_z8530.h new file mode 100644 index 0000000..458aff7 --- /dev/null +++ b/sys/dev/uart/uart_dev_z8530.h @@ -0,0 +1,252 @@ +/* + * Copyright (c) 2003 Marcel Moolenaar + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _DEV_UART_DEV_Z8530_H_ +#define _DEV_UART_DEV_Z8530_H_ + +/* + * Channel B control: 0 + * Channel B data: 1 + * Channel A control: 2 + * Channel A data: 3 + * + * We expect a seperate subregion for each channel. + */ +#define REG_CTRL 0 +#define REG_DATA 1 + +/* Write registers. */ +#define WR_CR 0 /* Command Register. */ +#define WR_IDT 1 /* Interrupt and Data Transfer Mode. */ +#define WR_IV 2 /* Interrupt Vector (shared). */ +#define WR_RPC 3 /* Receive Parameters and Control. */ +#define WR_MPM 4 /* Miscellaneous Parameters and Modes. */ +#define WR_TPC 5 /* Transmit Parameters and Control. */ +#define WR_SCAF 6 /* Sync Character or (SDLC) Address Field. */ +#define WR_SCF 7 /* Sync Character or (SDCL) Flag. */ +#define WR_EFC 7 /* Extended Feature and FIFO Control. */ +#define WR_TB 8 /* Transmit Buffer. */ +#define WR_MIC 9 /* Master Interrupt Control (shared). */ +#define WR_MCB1 10 /* Miscellaneous Control Bits (part 1 :-). */ +#define WR_CMC 11 /* Clock Mode Control. */ +#define WR_TCL 12 /* BRG Time Constant Low. */ +#define WR_TCH 13 /* BRG Time Constant High. */ +#define WR_MCB2 14 /* Miscellaneous Control Bits (part 2 :-). */ +#define WR_IC 15 /* Interrupt Control. */ + +/* Read registers. */ +#define RR_BES 0 /* Buffer and External Status. */ +#define RR_SRC 1 /* Special Receive Condition. */ +#define RR_IV 2 /* Interrupt Vector. */ +#define RR_IP 3 /* Interrupt Pending (ch A only). */ +#define RR_MPM 4 /* Miscellaneous Parameters and Modes. */ +#define RR_TPC 5 /* Transmit Parameters and Control. */ +#define RR_BCL 6 /* Byte Count Low. */ +#define RR_BCH 7 /* Byte Count High. */ +#define RR_RB 8 /* Receive Buffer. */ +#define RR_RPC 9 /* Receive Parameters and Contro. */ +#define RR_MSB 10 /* Miscellaneous Status Bits. */ +#define RR_MCB1 11 /* Miscellaneous Control Bits (part 1). */ +#define RR_TCL 12 /* BRG Time Constant Low. */ +#define RR_TCH 13 /* BRG Time Constant High. */ +#define RR_EFC 14 /* Extended Feature and FIFO Control. */ +#define RR_IC 15 /* Interrupt Control. */ + +/* Buffer and External Status (RR0). */ +#define BES_BRK 0x80 /* Break (Abort). */ +#define BES_TXU 0x40 /* Tx Underrun (EOM). */ +#define BES_CTS 0x20 /* CTS. */ +#define BES_SYNC 0x10 /* Sync. */ +#define BES_DCD 0x08 /* DCD. */ +#define BES_TXE 0x04 /* Tx Empty. */ +#define BES_ZC 0x02 /* Zero Count. */ +#define BES_RXA 0x01 /* Rx Available. */ + +/* Clock Mode Control (WR11). */ +#define CMC_XTAL 0x80 /* -RTxC connects to quartz crystal. */ +#define CMC_RC_DPLL 0x60 /* Rx Clock from DPLL. */ +#define CMC_RC_BRG 0x40 /* Rx Clock from BRG. */ +#define CMC_RC_TRXC 0x20 /* Rx Clock from -TRxC. */ +#define CMC_RC_RTXC 0x00 /* Rx Clock from -RTxC. */ +#define CMC_TC_DPLL 0x18 /* Tx Clock from DPLL */ +#define CMC_TC_BRG 0x10 /* Tx Clock from BRG */ +#define CMC_TC_TRXC 0x08 /* Tx Clock from -TRxC. */ +#define CMC_TC_RTXC 0x00 /* Tx Clock from -RTxC. */ +#define CMC_TRXC_OUT 0x04 /* -TRxC is output. */ +#define CMC_TRXC_DPLL 0x03 /* -TRxC from DPLL */ +#define CMC_TRXC_BRG 0x02 /* -TRxC from BRG */ +#define CMC_TRXC_XMIT 0x01 /* -TRxC from Tx clock. */ +#define CMC_TRXC_XTAL 0x00 /* -TRxC from XTAL. */ + +/* Command Register (WR0). */ +#define CR_RSTTXU 0xc0 /* Reset Tx. Underrun/EOM. */ +#define CR_RSTTXCRC 0x80 /* Reset Tx. CRC. */ +#define CR_RSTRXCRC 0x40 /* Reset Rx. CRC. */ +#define CR_RSTIUS 0x38 /* Reset Int. Under Service. */ +#define CR_RSTERR 0x30 /* Error Reset. */ +#define CR_RSTTXI 0x28 /* Reset Tx. Int. */ +#define CR_ENARXI 0x20 /* Enable Rx. Int. */ +#define CR_ABORT 0x18 /* Send Abort. */ +#define CR_RSTXSI 0x10 /* Reset Ext/Status Int. */ + +/* Extended Feature and FIFO Control (WR7 prime). */ +#define EFC_ERE 0x40 /* Extended Read Enable. */ +#define EFC_FE 0x20 /* Transmit FIFO Empty. */ +#define EFC_RQT 0x10 /* Request Timing. */ +#define EFC_FHF 0x08 /* Receive FIFO Half Full. */ +#define EFC_RTS 0x04 /* Auto RTS Deactivation. */ +#define EFC_EOM 0x02 /* Auto EOM Reset. */ +#define EFC_FLAG 0x01 /* Auto SDLC Flag on Tx. */ + +/* Interrupt Control (WR15). */ +#define IC_BRK 0x80 /* Break (Abort) IE. */ +#define IC_TXU 0x40 /* Tx Underrun IE. */ +#define IC_CTS 0x20 /* CTS IE. */ +#define IC_SYNC 0x10 /* Sync IE. */ +#define IC_DCD 0x08 /* DCD IE. */ +#define IC_FIFO 0x04 /* SDLC FIFO Enable. */ +#define IC_ZC 0x02 /* Zero Count IE. */ +#define IC_EF 0x01 /* Extended Feature Enable. */ + +/* Interrupt and Data Transfer Mode (WR1). */ +#define IDT_WRE 0x80 /* Wait/DMA Request Enable. */ +#define IDT_REQ 0x40 /* DMA Request. */ +#define IDT_WRR 0x20 /* Wait/DMA Reuest on Receive. */ +#define IDT_RISC 0x18 /* Rx Int. on Special Condition Only. */ +#define IDT_RIA 0x10 /* Rx Int. on All Characters. */ +#define IDT_RIF 0x08 /* Rx Int. on First Character. */ +#define IDT_PSC 0x04 /* Parity is Special Condition. */ +#define IDT_TIE 0x02 /* Tx Int. Enable. */ +#define IDT_XIE 0x01 /* Ext. Int. Enable. */ + +/* Interrupt Pending (RR3). */ +#define IP_RIA 0x20 /* Rx. Int. ch. A. */ +#define IP_TIA 0x10 /* Tx. Int. ch. A. */ +#define IP_SIA 0x08 /* Ext/Status Int. ch. A. */ +#define IP_RIB 0x04 /* Rx. Int. ch. B. */ +#define IP_TIB 0x02 /* Tx. Int. ch. B. */ +#define IP_SIB 0x01 /* Ext/Status Int. ch. B. */ + +/* Interrupt Vector Status Low (RR2). */ +#define IV_SCA 0x0e /* Special Condition ch. A. */ +#define IV_RAA 0x0c /* Receive Available ch. A. */ +#define IV_XSA 0x0a /* External/Status Change ch. A. */ +#define IV_TEA 0x08 /* Transmitter Empty ch. A. */ +#define IV_SCB 0x06 /* Special Condition ch. B. */ +#define IV_RAB 0x04 /* Receive Available ch. B. */ +#define IV_XSB 0x02 /* External/Status Change ch. B. */ +#define IV_TEB 0x00 /* Transmitter Empty ch. B. */ + +/* Miscellaneous Control Bits part 1 (WR10). */ +#define MCB1_CRC1 0x80 /* CRC presets to 1. */ +#define MCB1_FM0 0x60 /* FM0 Encoding. */ +#define MCB1_FM1 0x40 /* FM1 Encoding. */ +#define MCB1_NRZI 0x20 /* NRZI Encoding. */ +#define MCB1_NRZ 0x00 /* NRZ Encoding. */ +#define MCB1_AOP 0x10 /* Active On Poll. */ +#define MCB1_MI 0x08 /* Mark Idle. */ +#define MCB1_AOU 0x04 /* Abort On Underrun. */ +#define MCB1_LM 0x02 /* Loop Mode. */ +#define MCB1_SIX 0x01 /* 6 or 12 bit SYNC. */ + +/* Miscellaneous Control Bits part 2 (WR14). */ +#define MCB2_NRZI 0xe0 /* DPLL - NRZI mode. */ +#define MCB2_FM 0xc0 /* DPLL - FM mode. */ +#define MCB2_RTXC 0xa0 /* DPLL - Clock from -RTxC. */ +#define MCB2_BRG 0x80 /* DPLL - Clock from BRG. */ +#define MCB2_OFF 0x60 /* DPLL - Disable. */ +#define MCB2_RMC 0x40 /* DPLL - Reset Missing Clock. */ +#define MCB2_ESM 0x20 /* DPLL - Enter Search Mode. */ +#define MCB2_LL 0x10 /* Local Loopback. */ +#define MCB2_AE 0x08 /* Auto Echo. */ +#define MCB2_REQ 0x04 /* Request Function. */ +#define MCB2_PCLK 0x02 /* BRG source is PCLK. */ +#define MCB2_BRGE 0x01 /* BRG enable. */ + +/* Master Interrupt Control (WR9). */ +#define MIC_FHR 0xc0 /* Force Hardware Reset. */ +#define MIC_CRA 0x80 /* Channel Reset A. */ +#define MIC_CRB 0x40 /* Channel Reset B. */ +#define MIC_SIE 0x20 /* Software INTACK Enable. */ +#define MIC_SH 0x10 /* Status High. */ +#define MIC_MIE 0x08 /* Master Interrupt Enable. */ +#define MIC_DLC 0x04 /* Disable Lower Chain. */ +#define MIC_NV 0x02 /* No Vector. */ +#define MIC_VIS 0x01 /* Vector Includes Status. */ + +/* Transmit/Receive Miscellaneous Parameters and Modes (WR4). */ +#define MPM_CM64 0xc0 /* X64 Clock Mode. */ +#define MPM_CM32 0x80 /* X32 Clock Mode. */ +#define MPM_CM16 0x40 /* X16 Clock Mode. */ +#define MPM_CM1 0x00 /* X1 Clock Mode. */ +#define MPM_EXT 0x30 /* External Sync Mode. */ +#define MPM_SDLC 0x20 /* SDLC mode. */ +#define MPM_BI 0x10 /* 16-bit Sync (bi-sync). */ +#define MPM_MONO 0x00 /* 8-bit Sync (mono-sync). */ +#define MPM_SB2 0x0c /* Async mode: 2 stopbits. */ +#define MPM_SB15 0x08 /* Async mode: 1.5 stopbits. */ +#define MPM_SB1 0x04 /* Async mode: 1 stopbit. */ +#define MPM_SYNC 0x00 /* Sync Mode Enable. */ +#define MPM_EVEN 0x02 /* Async mode: even parity. */ +#define MPM_PE 0x01 /* Async mode: parity enable. */ + +/* Receive Parameters and Control (WR3). */ +#define RPC_RB8 0xc0 /* 8 databits. */ +#define RPC_RB6 0x80 /* 6 databits. */ +#define RPC_RB7 0x40 /* 7 databits. */ +#define RPC_RB5 0x00 /* 5 databits. */ +#define RPC_AE 0x20 /* Auto Enable. */ +#define RPC_EHM 0x10 /* Enter Hunt Mode. */ +#define RPC_CRC 0x08 /* CRC Enable. */ +#define RPC_ASM 0x04 /* Address Search Mode. */ +#define RPC_LI 0x02 /* SYNC Character Load Inhibit */ +#define RPC_RXE 0x01 /* Receiver Enable */ + +/* Special Receive Condition (RR1). */ +#define SRC_EOF 0x80 /* End Of Frame. */ +#define SRC_FE 0x40 /* Framing Error. */ +#define SRC_OVR 0x20 /* Rx. Overrun. */ +#define SRC_PE 0x10 /* Parity Error. */ +#define SRC_RC0 0x08 /* Residue Code 0. */ +#define SRC_RC1 0x04 /* Residue Code 1. */ +#define SRC_RC2 0x02 /* Residue Code 2. */ +#define SRC_AS 0x01 /* All Sent. */ + +/* Transmit Parameter and Control (WR5). */ +#define TPC_DTR 0x80 /* DTR. */ +#define TPC_TB8 0x60 /* 8 databits. */ +#define TPC_TB6 0x40 /* 6 databits. */ +#define TPC_TB7 0x20 /* 7 databits. */ +#define TPC_TB5 0x00 /* 5 or fewer databits. */ +#define TPC_BRK 0x10 /* Send break. */ +#define TPC_TXE 0x08 /* Transmitter Enable. */ +#define TPC_CRC16 0x04 /* CRC16. */ +#define TPC_RTS 0x02 /* RTS. */ +#define TPC_CRC 0x01 /* CRC Enable. */ + +#endif /* _DEV_UART_DEV_Z8530_H_ */ diff --git a/sys/dev/uart/uart_if.m b/sys/dev/uart/uart_if.m new file mode 100644 index 0000000..772883d --- /dev/null +++ b/sys/dev/uart/uart_if.m @@ -0,0 +1,138 @@ +# Copyright (c) 2003 Marcel Moolenaar +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# $FreeBSD$ + +#include <sys/bus.h> +#include <machine/bus.h> +#include <dev/uart/uart.h> +#include <dev/uart/uart_bus.h> + +# The UART hardware interface. The core UART code is hardware independent. +# The details of the hardware are abstracted by the UART hardware interface. + +INTERFACE uart; + +# attach() - attach hardware. +# This method is called when the device is being attached. All resources +# have been allocated. The transmit and receive buffers exist, but no +# high-level (ie tty) initialization has been done yet. +# The intend of this method is to setup the hardware for normal operation. +METHOD int attach { + struct uart_softc *this; +}; + +# detach() - detach hardware. +# This method is called when a device is being detached from its bus. It +# is the first action performed, so even the high-level (ie tty) interface +# is still operational. +# The intend of this method is to disable the hardware. +METHOD int detach { + struct uart_softc *this; +}; + +# flush() - flush FIFOs. +# This method is called to flush the transmitter and/or the receiver as +# specified by the what argument. Characters are expected to be lost. +METHOD int flush { + struct uart_softc *this; + int what; +}; + +# getsig() - get line and modem signals. +# This method retrieves the DTE and DCE signals and their corresponding +# delta bits. The delta bits include those corresponding to DTE signals +# when they were changed by a call to setsig. The delta bits maintained +# by the hardware driver are cleared as a side-effect. A second call to +# this function will not have any delta bits set, unless there was a +# change in the signals in the mean time. +METHOD int getsig { + struct uart_softc *this; +}; + +# ioctl() - get or set miscellaneous parameters. +# This method is the bitbucket method. It can (and will) be used when there's +# something we need to set or get for which a new method is overkill. It's +# used for example to set HW or SW flow-control. +METHOD int ioctl { + struct uart_softc *this; + int request; + intptr_t data; +}; + +# ipend() - query UART for pending interrupts. +# When an interrupt is signalled, the handler will call this method to find +# out which of the interrupt sources needs attention. The handler will use +# this information to dispatch service routines that deal with each of the +# interrupt sources. An advantage of this approach is that it allows multi- +# port drivers (like puc(4)) to query multiple devices concurrently and +# service them on an interrupt priority basis. If the hardware cannot provide +# the information reliably, it is free to service the interrupt and return 0, +# meaning that no attention is required. +METHOD int ipend { + struct uart_softc *this; +} + +# param() - set communication parameters. +# This method is called to change the communication parameters. +METHOD int param { + struct uart_softc *this; + int baudrate; + int databits; + int stopbits; + int parity; +}; + +# probe() - detect hardware. +# This method is called as part of the bus probe to make sure the +# hardware exists. This function should also set the device description +# to something that represents the hardware. +METHOD int probe { + struct uart_softc *this; +}; + +# receive() - move data from the receive FIFO to the receive buffer. +# This method is called to move received data to the receive buffer and +# additionally should make sure the receive interrupt should be cleared. +METHOD int receive { + struct uart_softc *this; +}; + +# setsig() - set line and modem signals. +# This method allows changing DTE signals. The DTE delta bits indicate which +# signals are to be changed and the DTE bits themselves indicate whether to +# set or clear the signals. A subsequent call to getsig will return with the +# DTE delta bits set of those DTE signals that did change by this method. +METHOD int setsig { + struct uart_softc *this; + int sig; +}; + +# transmit() - move data from the transmit buffer to the transmit FIFO. +# This method is responsible for writing the Tx buffer to the UART and +# additionally should make sure that a transmit interrupt is generated +# when transmission is complete. +METHOD int transmit { + struct uart_softc *this; +}; diff --git a/sys/dev/uart/uart_tty.c b/sys/dev/uart/uart_tty.c new file mode 100644 index 0000000..3841889 --- /dev/null +++ b/sys/dev/uart/uart_tty.c @@ -0,0 +1,569 @@ +/* + * Copyright (c) 2003 Marcel Moolenaar + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/conf.h> +#include <sys/cons.h> +#include <sys/fcntl.h> +#include <sys/interrupt.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/reboot.h> +#include <machine/bus.h> +#include <sys/rman.h> +#include <sys/termios.h> +#include <sys/tty.h> +#include <machine/resource.h> +#include <machine/stdarg.h> + +#include <dev/uart/uart.h> +#include <dev/uart/uart_bus.h> +#include <dev/uart/uart_cpu.h> + +#include "uart_if.h" + +#define UART_MINOR_CALLOUT 0x10000 + +static cn_probe_t uart_cnprobe; +static cn_init_t uart_cninit; +static cn_term_t uart_cnterm; +static cn_getc_t uart_cngetc; +static cn_checkc_t uart_cncheckc; +static cn_putc_t uart_cnputc; + +CONS_DRIVER(uart, uart_cnprobe, uart_cninit, uart_cnterm, uart_cngetc, + uart_cncheckc, uart_cnputc, NULL); + +static d_open_t uart_tty_open; +static d_close_t uart_tty_close; +static d_ioctl_t uart_tty_ioctl; + +static struct cdevsw uart_cdevsw = { + .d_open = uart_tty_open, + .d_close = uart_tty_close, + .d_read = ttyread, + .d_write = ttywrite, + .d_ioctl = uart_tty_ioctl, + .d_poll = ttypoll, + .d_name = uart_driver_name, + .d_maj = MAJOR_AUTO, + .d_flags = D_TTY, + .d_kqfilter = ttykqfilter, +}; + +static struct uart_devinfo uart_console; + +static void +uart_cnprobe(struct consdev *cp) +{ + + cp->cn_dev = NULL; + cp->cn_pri = CN_DEAD; + + KASSERT(uart_console.cookie == NULL, ("foo")); + + if (uart_cpu_getdev(UART_DEV_CONSOLE, &uart_console)) + return; + + if (uart_probe(&uart_console)) + return; + + cp->cn_pri = (boothowto & RB_SERIAL) ? CN_REMOTE : CN_NORMAL; + cp->cn_arg = &uart_console; +} + +static void +uart_cninit(struct consdev *cp) +{ + struct uart_devinfo *di; + + /* + * Yedi trick: we need to be able to define cn_dev before we go + * single- or multi-user. The problem is that we don't know at + * this time what the device will be. Hence, we need to link from + * the uart_devinfo to the consdev that corresponds to it so that + * we can define cn_dev in uart_bus_attach() when we find the + * device during bus enumeration. That's when we'll know what the + * the unit number will be. + */ + di = cp->cn_arg; + KASSERT(di->cookie == NULL, ("foo")); + di->cookie = cp; + di->type = UART_DEV_CONSOLE; + uart_add_sysdev(di); + uart_init(di); +} + +static void +uart_cnterm(struct consdev *cp) +{ + + uart_term(cp->cn_arg); +} + +static void +uart_cnputc(struct consdev *cp, int c) +{ + + uart_putc(cp->cn_arg, c); +} + +static int +uart_cncheckc(struct consdev *cp) +{ + + return (uart_poll(cp->cn_arg)); +} + +static int +uart_cngetc(struct consdev *cp) +{ + + return (uart_getc(cp->cn_arg)); +} + +static void +uart_tty_oproc(struct tty *tp) +{ + struct uart_softc *sc; + + KASSERT(tp->t_dev != NULL, ("foo")); + sc = tp->t_dev->si_drv1; + if (sc == NULL || sc->sc_leaving) + return; + + /* + * Handle input flow control. Note that if we have hardware support, + * we don't do anything here. We continue to receive until our buffer + * is full. At that time we cannot empty the UART itself and it will + * de-assert RTS for us. In that situation we're completely stuffed. + * Without hardware support, we need to toggle RTS ourselves. + */ + if ((tp->t_cflag & CRTS_IFLOW) && !sc->sc_hwiflow) { + if ((tp->t_state & TS_TBLOCK) && + (sc->sc_hwsig & UART_SIG_RTS)) + UART_SETSIG(sc, UART_SIG_DRTS); + else if (!(tp->t_state & TS_TBLOCK) && + !(sc->sc_hwsig & UART_SIG_RTS)) + UART_SETSIG(sc, UART_SIG_DRTS|UART_SIG_RTS); + } + + if (tp->t_state & TS_TTSTOP) + return; + + if ((tp->t_state & TS_BUSY) || sc->sc_txbusy) + return; + + if (tp->t_outq.c_cc == 0) { + ttwwakeup(tp); + return; + } + + sc->sc_txdatasz = q_to_b(&tp->t_outq, sc->sc_txbuf, sc->sc_txfifosz); + tp->t_state |= TS_BUSY; + UART_TRANSMIT(sc); + ttwwakeup(tp); +} + +static int +uart_tty_param(struct tty *tp, struct termios *t) +{ + struct uart_softc *sc; + int databits, parity, stopbits; + + KASSERT(tp->t_dev != NULL, ("foo")); + sc = tp->t_dev->si_drv1; + if (sc == NULL || sc->sc_leaving) + return (ENODEV); + if (t->c_ispeed != t->c_ospeed && t->c_ospeed != 0) + return (EINVAL); + /* Fixate certain parameters for system devices. */ + if (sc->sc_sysdev != NULL) { + t->c_ispeed = t->c_ospeed = sc->sc_sysdev->baudrate; + t->c_cflag |= CLOCAL; + t->c_cflag &= ~HUPCL; + } + if (t->c_ospeed == 0) { + UART_SETSIG(sc, UART_SIG_DDTR | UART_SIG_DRTS); + return (0); + } + switch (t->c_cflag & CSIZE) { + case CS5: databits = 5; break; + case CS6: databits = 6; break; + case CS7: databits = 7; break; + default: databits = 8; break; + } + stopbits = (t->c_cflag & CSTOPB) ? 2 : 1; + if (t->c_cflag & PARENB) + parity = (t->c_cflag & PARODD) ? UART_PARITY_ODD + : UART_PARITY_EVEN; + else + parity = UART_PARITY_NONE; + UART_PARAM(sc, t->c_ospeed, databits, stopbits, parity); + UART_SETSIG(sc, UART_SIG_DDTR | UART_SIG_DTR); + /* Set input flow control state. */ + if (!sc->sc_hwiflow) { + if ((t->c_cflag & CRTS_IFLOW) && (tp->t_state & TS_TBLOCK)) + UART_SETSIG(sc, UART_SIG_DRTS); + else + UART_SETSIG(sc, UART_SIG_DRTS | UART_SIG_RTS); + } else + UART_IOCTL(sc, UART_IOCTL_IFLOW, (t->c_cflag & CRTS_IFLOW)); + /* Set output flow control state. */ + if (sc->sc_hwoflow) + UART_IOCTL(sc, UART_IOCTL_OFLOW, (t->c_cflag & CCTS_OFLOW)); + ttsetwater(tp); + return (0); +} + +static void +uart_tty_stop(struct tty *tp, int rw) +{ + struct uart_softc *sc; + + KASSERT(tp->t_dev != NULL, ("foo")); + sc = tp->t_dev->si_drv1; + if (sc == NULL || sc->sc_leaving) + return; + if (rw & FWRITE) { + if (sc->sc_txbusy) { + sc->sc_txbusy = 0; + UART_FLUSH(sc, UART_FLUSH_TRANSMITTER); + } + tp->t_state &= ~TS_BUSY; + } + if (rw & FREAD) { + UART_FLUSH(sc, UART_FLUSH_RECEIVER); + sc->sc_rxget = sc->sc_rxput = 0; + } +} + +void +uart_tty_intr(void *arg) +{ + struct uart_softc *sc = arg; + struct tty *tp; + int c, pend, sig, xc; + + if (sc->sc_leaving) + return; + + pend = atomic_readandclear_32(&sc->sc_ttypend); + if (!(pend & UART_IPEND_MASK)) + return; + + tp = sc->sc_u.u_tty.tp; + + if (pend & UART_IPEND_RXREADY) { + while (!uart_rx_empty(sc) && !(tp->t_state & TS_TBLOCK)) { + xc = uart_rx_get(sc); + c = xc & 0xff; + if (xc & UART_STAT_FRAMERR) + c |= TTY_FE; + if (xc & UART_STAT_PARERR) + c |= TTY_PE; + (*linesw[tp->t_line].l_rint)(c, tp); + } + } + + if (pend & UART_IPEND_BREAK) { + if (tp != NULL && !(tp->t_iflag & IGNBRK)) + (*linesw[tp->t_line].l_rint)(0, tp); + } + + if (pend & UART_IPEND_SIGCHG) { + sig = pend & UART_IPEND_SIGMASK; + if (sig & UART_SIG_DDCD) + (*linesw[tp->t_line].l_modem)(tp, sig & UART_SIG_DCD); + if ((sig & UART_SIG_DCTS) && (tp->t_cflag & CCTS_OFLOW) && + !sc->sc_hwoflow) { + if (sig & UART_SIG_CTS) { + tp->t_state &= ~TS_TTSTOP; + (*linesw[tp->t_line].l_start)(tp); + } else + tp->t_state |= TS_TTSTOP; + } + } + + if (pend & UART_IPEND_TXIDLE) { + tp->t_state &= ~TS_BUSY; + (*linesw[tp->t_line].l_start)(tp); + } +} + +int +uart_tty_attach(struct uart_softc *sc) +{ + struct tty *tp; + + tp = ttymalloc(NULL); + sc->sc_u.u_tty.tp = tp; + + sc->sc_u.u_tty.si[0] = make_dev(&uart_cdevsw, + device_get_unit(sc->sc_dev), UID_ROOT, GID_WHEEL, 0600, "ttyu%r", + device_get_unit(sc->sc_dev)); + sc->sc_u.u_tty.si[0]->si_drv1 = sc; + sc->sc_u.u_tty.si[0]->si_tty = tp; + sc->sc_u.u_tty.si[1] = make_dev(&uart_cdevsw, + device_get_unit(sc->sc_dev) | UART_MINOR_CALLOUT, UID_UUCP, + GID_DIALER, 0660, "uart%r", device_get_unit(sc->sc_dev)); + sc->sc_u.u_tty.si[1]->si_drv1 = sc; + sc->sc_u.u_tty.si[1]->si_tty = tp; + + tp->t_oproc = uart_tty_oproc; + tp->t_param = uart_tty_param; + tp->t_stop = uart_tty_stop; + + if (sc->sc_sysdev != NULL && sc->sc_sysdev->type == UART_DEV_CONSOLE) { + ((struct consdev *)sc->sc_sysdev->cookie)->cn_dev = + makedev(uart_cdevsw.d_maj, device_get_unit(sc->sc_dev)); + } + + swi_add(&tty_ithd, uart_driver_name, uart_tty_intr, sc, SWI_TTY, + INTR_TYPE_TTY, &sc->sc_softih); + + return (0); +} + +int uart_tty_detach(struct uart_softc *sc) +{ + + ithread_remove_handler(sc->sc_softih); + destroy_dev(sc->sc_u.u_tty.si[0]); + destroy_dev(sc->sc_u.u_tty.si[1]); + /* ttyfree(sc->sc_u.u_tty.tp); */ + + return (0); +} + +static int +uart_tty_open(dev_t dev, int flags, int mode, struct thread *td) +{ + struct uart_softc *sc; + struct tty *tp; + int error; + + sc = dev->si_drv1; + if (sc == NULL || sc->sc_leaving) + return (ENODEV); + + tp = dev->si_tty; + + loop: + if (sc->sc_opened) { + KASSERT(tp->t_state & TS_ISOPEN, ("foo")); + /* + * The device is open, so everything has been initialized. + * Handle conflicts. + */ + if (minor(dev) & UART_MINOR_CALLOUT) { + if (!sc->sc_callout) + return (EBUSY); + } else { + if (sc->sc_callout) { + if (flags & O_NONBLOCK) + return (EBUSY); + error = tsleep(sc, TTIPRI|PCATCH, "uartbi", 0); + if (error) + return (error); + sc = dev->si_drv1; + if (sc == NULL || sc->sc_leaving) + return (ENODEV); + goto loop; + } + } + if (tp->t_state & TS_XCLUDE && suser(td) != 0) + return (EBUSY); + } else { + KASSERT(!(tp->t_state & TS_ISOPEN), ("foo")); + /* + * The device isn't open, so there are no conflicts. + * Initialize it. Initialization is done twice in many + * cases: to preempt sleeping callin opens if we are + * callout, and to complete a callin open after DCD rises. + */ + sc->sc_callout = (minor(dev) & UART_MINOR_CALLOUT) ? 1 : 0; + tp->t_dev = dev; + + tp->t_cflag = TTYDEF_CFLAG; + tp->t_iflag = TTYDEF_IFLAG; + tp->t_lflag = TTYDEF_LFLAG; + tp->t_oflag = TTYDEF_OFLAG; + tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; + ttychars(tp); + error = uart_tty_param(tp, &tp->t_termios); + if (error) + return (error); + /* + * Handle initial DCD. + */ + if ((sc->sc_hwsig & UART_SIG_DCD) || sc->sc_callout) + (*linesw[tp->t_line].l_modem)(tp, 1); + } + /* + * Wait for DCD if necessary. + */ + if (!(tp->t_state & TS_CARR_ON) && !sc->sc_callout && + !(tp->t_cflag & CLOCAL) && !(flags & O_NONBLOCK)) { + error = tsleep(TSA_CARR_ON(tp), TTIPRI|PCATCH, "uartdcd", 0); + if (error) + return (error); + sc = dev->si_drv1; + if (sc == NULL || sc->sc_leaving) + return (ENODEV); + goto loop; + } + error = ttyopen(dev, tp); + if (error) + return (error); + error = (*linesw[tp->t_line].l_open)(dev, tp); + if (error) + return (error); + + KASSERT(tp->t_state & TS_ISOPEN, ("foo")); + sc->sc_opened = 1; + return (0); +} + +static int +uart_tty_close(dev_t dev, int flags, int mode, struct thread *td) +{ + struct uart_softc *sc; + struct tty *tp; + + sc = dev->si_drv1; + if (sc == NULL || sc->sc_leaving) + return (ENODEV); + tp = dev->si_tty; + if (!sc->sc_opened) { + KASSERT(!(tp->t_state & TS_ISOPEN), ("foo")); + return (0); + } + KASSERT(tp->t_state & TS_ISOPEN, ("foo")); + + if (sc->sc_hwiflow) + UART_IOCTL(sc, UART_IOCTL_IFLOW, 0); + if (sc->sc_hwoflow) + UART_IOCTL(sc, UART_IOCTL_OFLOW, 0); + if (sc->sc_sysdev == NULL) + UART_SETSIG(sc, UART_SIG_DDTR | UART_SIG_DRTS); + + (*linesw[tp->t_line].l_close)(tp, flags); + ttyclose(tp); + wakeup(sc); + wakeup(TSA_CARR_ON(tp)); + KASSERT(!(tp->t_state & TS_ISOPEN), ("foo")); + sc->sc_opened = 0; + return (0); +} + +static int +uart_tty_ioctl(dev_t dev, u_long cmd, caddr_t data, int flags, + struct thread *td) +{ + struct uart_softc *sc; + struct tty *tp; + int bits, error, sig; + + sc = dev->si_drv1; + if (sc == NULL || sc->sc_leaving) + return (ENODEV); + + tp = dev->si_tty; + error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flags, td); + if (error != ENOIOCTL) + return (error); + error = ttioctl(tp, cmd, data, flags); + if (error != ENOIOCTL) + return (error); + + switch (cmd) { + case TIOCSBRK: + UART_IOCTL(sc, UART_IOCTL_BREAK, 1); + break; + case TIOCCBRK: + UART_IOCTL(sc, UART_IOCTL_BREAK, 0); + break; + case TIOCSDTR: + UART_SETSIG(sc, UART_SIG_DDTR | UART_SIG_DTR); + break; + case TIOCCDTR: + UART_SETSIG(sc, UART_SIG_DDTR); + break; + case TIOCMSET: + bits = *(int*)data; + sig = UART_SIG_DDTR | UART_SIG_DRTS; + if (bits & TIOCM_DTR) + sig |= UART_SIG_DTR; + if (bits & TIOCM_RTS) + sig |= UART_SIG_RTS; + UART_SETSIG(sc, sig); + break; + case TIOCMBIS: + bits = *(int*)data; + sig = 0; + if (bits & TIOCM_DTR) + sig |= UART_SIG_DDTR | UART_SIG_DTR; + if (bits & TIOCM_RTS) + sig |= UART_SIG_DRTS | UART_SIG_RTS; + UART_SETSIG(sc, sig); + break; + case TIOCMBIC: + bits = *(int*)data; + sig = 0; + if (bits & TIOCM_DTR) + sig |= UART_SIG_DDTR; + if (bits & TIOCM_RTS) + sig |= UART_SIG_DRTS; + UART_SETSIG(sc, sig); + break; + case TIOCMGET: + sig = sc->sc_hwsig; + bits = TIOCM_LE; + if (sig & UART_SIG_DTR) + bits |= TIOCM_DTR; + if (sig & UART_SIG_RTS) + bits |= TIOCM_RTS; + if (sig & UART_SIG_DSR) + bits |= TIOCM_DSR; + if (sig & UART_SIG_CTS) + bits |= TIOCM_CTS; + if (sig & UART_SIG_DCD) + bits |= TIOCM_CD; + if (sig & (UART_SIG_DRI | UART_SIG_RI)) + bits |= TIOCM_RI; + *(int*)data = bits; + break; + default: + return (ENOTTY); + } + return (0); +} |