Index: head/sys/arm/amlogic/aml8726/uart_dev_aml8726.c =================================================================== --- head/sys/arm/amlogic/aml8726/uart_dev_aml8726.c (revision 281437) +++ head/sys/arm/amlogic/aml8726/uart_dev_aml8726.c (revision 281438) @@ -1,734 +1,735 @@ /*- * Copyright 2013-2015 John Wehle * 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 AND CONTRIBUTORS ``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. */ /* * Amlogic aml8726 UART driver. * * The current implementation only targets features common to all * uarts. For example ... though UART A as a 128 byte FIFO, the * others only have a 64 byte FIFO. * * Also, it's assumed that register 5 (the new baud rate register * present on the aml8726-m6) has not been activated. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "uart_if.h" #undef uart_getreg #undef uart_setreg #define uart_getreg(bas, reg) \ bus_space_read_4((bas)->bst, (bas)->bsh, reg) #define uart_setreg(bas, reg, value) \ bus_space_write_4((bas)->bst, (bas)->bsh, reg, value) #define SIGCHG(c, i, s, d) \ do { \ if (c) { \ i |= (i & s) ? s : s | d; \ } else { \ i = (i & s) ? (i & ~s) | d : i; \ } \ } while (0) static int aml8726_uart_divisor(int rclk, int baudrate) { int actual_baud, divisor; int error; if (baudrate == 0) return (0); /* integer version of (rclk / baudrate + .5) */ divisor = ((rclk << 1) + baudrate) / (baudrate << 1); if (divisor == 0 || divisor >= 65536) return (0); actual_baud = rclk / divisor; /* 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 aml8726_uart_param(struct uart_bas *bas, int baudrate, int databits, int stopbits, int parity) { uint32_t cr; uint32_t mr; int divisor; cr = uart_getreg(bas, AML_UART_CONTROL_REG); cr &= ~(AML_UART_CONTROL_DB_MASK | AML_UART_CONTROL_SB_MASK | AML_UART_CONTROL_P_MASK); switch (databits) { case 5: cr |= AML_UART_CONTROL_5_DB; break; case 6: cr |= AML_UART_CONTROL_6_DB; break; case 7: cr |= AML_UART_CONTROL_7_DB; break; case 8: cr |= AML_UART_CONTROL_8_DB; break; default: return (EINVAL); } switch (stopbits) { case 1: cr |= AML_UART_CONTROL_1_SB; break; case 2: cr |= AML_UART_CONTROL_2_SB; break; default: return (EINVAL); } switch (parity) { case UART_PARITY_EVEN: cr |= AML_UART_CONTROL_P_EVEN; cr |= AML_UART_CONTROL_P_EN; break; case UART_PARITY_ODD: cr |= AML_UART_CONTROL_P_ODD; cr |= AML_UART_CONTROL_P_EN; break; case UART_PARITY_NONE: break; default: return (EINVAL); } /* Set baudrate. */ if (baudrate > 0 && bas->rclk != 0) { divisor = aml8726_uart_divisor(bas->rclk / 4, baudrate) - 1; if (divisor > 0xffff) return (EINVAL); cr &= ~AML_UART_CONTROL_BAUD_MASK; cr |= (divisor & AML_UART_CONTROL_BAUD_MASK); divisor >>= AML_UART_CONTROL_BAUD_WIDTH; mr = uart_getreg(bas, AML_UART_MISC_REG); mr &= ~(AML_UART_MISC_OLD_RX_BAUD | AML_UART_MISC_BAUD_EXT_MASK); mr |= ((divisor << AML_UART_MISC_BAUD_EXT_SHIFT) & AML_UART_MISC_BAUD_EXT_MASK); uart_setreg(bas, AML_UART_MISC_REG, mr); } uart_setreg(bas, AML_UART_CONTROL_REG, cr); uart_barrier(bas); return (0); } /* * Low-level UART interface. */ static int aml8726_uart_probe(struct uart_bas *bas) { return (0); } static void aml8726_uart_init(struct uart_bas *bas, int baudrate, int databits, int stopbits, int parity) { uint32_t cr; uint32_t mr; aml8726_uart_param(bas, baudrate, databits, stopbits, parity); cr = uart_getreg(bas, AML_UART_CONTROL_REG); /* Disable all interrupt sources. */ cr &= ~(AML_UART_CONTROL_TX_INT_EN | AML_UART_CONTROL_RX_INT_EN); /* Reset the transmitter and receiver. */ cr |= (AML_UART_CONTROL_TX_RST | AML_UART_CONTROL_RX_RST); /* Enable the transmitter and receiver. */ cr |= (AML_UART_CONTROL_TX_EN | AML_UART_CONTROL_RX_EN); uart_setreg(bas, AML_UART_CONTROL_REG, cr); uart_barrier(bas); /* Clear RX FIFO level for generating interrupts. */ mr = uart_getreg(bas, AML_UART_MISC_REG); mr &= ~AML_UART_MISC_RECV_IRQ_CNT_MASK; uart_setreg(bas, AML_UART_MISC_REG, mr); uart_barrier(bas); /* Ensure the reset bits are clear. */ cr &= ~(AML_UART_CONTROL_TX_RST | AML_UART_CONTROL_RX_RST); uart_setreg(bas, AML_UART_CONTROL_REG, cr); uart_barrier(bas); } static void aml8726_uart_term(struct uart_bas *bas) { } static void aml8726_uart_putc(struct uart_bas *bas, int c) { while ((uart_getreg(bas, AML_UART_STATUS_REG) & AML_UART_STATUS_TX_FIFO_FULL) != 0) cpu_spinwait(); uart_setreg(bas, AML_UART_WFIFO_REG, c); uart_barrier(bas); } static int aml8726_uart_rxready(struct uart_bas *bas) { return ((uart_getreg(bas, AML_UART_STATUS_REG) & AML_UART_STATUS_RX_FIFO_EMPTY) == 0 ? 1 : 0); } static int aml8726_uart_getc(struct uart_bas *bas, struct mtx *hwmtx) { int c; uart_lock(hwmtx); while ((uart_getreg(bas, AML_UART_STATUS_REG) & AML_UART_STATUS_RX_FIFO_EMPTY) != 0) { uart_unlock(hwmtx); DELAY(4); uart_lock(hwmtx); } c = uart_getreg(bas, AML_UART_RFIFO_REG) & 0xff; uart_unlock(hwmtx); return (c); } struct uart_ops aml8726_uart_ops = { .probe = aml8726_uart_probe, .init = aml8726_uart_init, .term = aml8726_uart_term, .putc = aml8726_uart_putc, .rxready = aml8726_uart_rxready, .getc = aml8726_uart_getc, }; static unsigned int aml8726_uart_bus_clk(phandle_t node) { pcell_t prop; ssize_t len; phandle_t clk_node; len = OF_getencprop(node, "clocks", &prop, sizeof(prop)); if ((len / sizeof(prop)) != 1 || prop == 0 || (clk_node = OF_node_from_xref(prop)) == 0) return (0); len = OF_getencprop(clk_node, "clock-frequency", &prop, sizeof(prop)); if ((len / sizeof(prop)) != 1 || prop == 0) return (0); return ((unsigned int)prop); } static int aml8726_uart_bus_probe(struct uart_softc *sc) { int error; error = aml8726_uart_probe(&sc->sc_bas); if (error) return (error); sc->sc_rxfifosz = 64; sc->sc_txfifosz = 64; sc->sc_hwiflow = 1; sc->sc_hwoflow = 1; device_set_desc(sc->sc_dev, "Amlogic aml8726 UART"); return (0); } static int aml8726_uart_bus_getsig(struct uart_softc *sc) { uint32_t new, old, sig; /* * Treat DSR, DCD, and CTS as always on. */ do { old = sc->sc_hwsig; sig = old; SIGCHG(1, sig, SER_DSR, SER_DDSR); SIGCHG(1, sig, SER_DCD, SER_DDCD); SIGCHG(1, sig, SER_CTS, SER_DCTS); new = sig & ~SER_MASK_DELTA; } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new)); return (sig); } static int aml8726_uart_bus_setsig(struct uart_softc *sc, int sig) { uint32_t new, old; do { old = sc->sc_hwsig; new = old; if (sig & SER_DDTR) { SIGCHG(sig & SER_DTR, new, SER_DTR, SER_DDTR); } if (sig & SER_DRTS) { SIGCHG(sig & SER_RTS, new, SER_RTS, SER_DRTS); } } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new)); return (0); } static int aml8726_uart_bus_attach(struct uart_softc *sc) { struct uart_bas *bas; uint32_t cr; uint32_t mr; bas = &sc->sc_bas; bas->rclk = aml8726_uart_bus_clk(ofw_bus_get_node(sc->sc_dev)); if (bas->rclk == 0) { device_printf(sc->sc_dev, "missing clocks attribute in FDT\n"); return (ENXIO); } cr = uart_getreg(bas, AML_UART_CONTROL_REG); /* Disable all interrupt sources. */ cr &= ~(AML_UART_CONTROL_TX_INT_EN | AML_UART_CONTROL_RX_INT_EN); /* Ensure the reset bits are clear. */ cr &= ~(AML_UART_CONTROL_TX_RST | AML_UART_CONTROL_RX_RST); /* * Reset the transmitter and receiver only if not acting as a * console, otherwise it means that: * * 1) aml8726_uart_init was already called which did the reset * * 2) there may be console bytes sitting in the transmit fifo */ if (sc->sc_sysdev != NULL && sc->sc_sysdev->type == UART_DEV_CONSOLE) ; else cr |= (AML_UART_CONTROL_TX_RST | AML_UART_CONTROL_RX_RST); /* Default to two wire mode. */ cr |= AML_UART_CONTROL_TWO_WIRE_EN; /* Enable the transmitter and receiver. */ cr |= (AML_UART_CONTROL_TX_EN | AML_UART_CONTROL_RX_EN); /* Reset error bits. */ cr |= AML_UART_CONTROL_CLR_ERR; uart_setreg(bas, AML_UART_CONTROL_REG, cr); uart_barrier(bas); /* Set FIFO levels for generating interrupts. */ mr = uart_getreg(bas, AML_UART_MISC_REG); mr &= ~AML_UART_MISC_XMIT_IRQ_CNT_MASK; mr |= (0 << AML_UART_MISC_XMIT_IRQ_CNT_SHIFT); mr &= ~AML_UART_MISC_RECV_IRQ_CNT_MASK; mr |= (1 << AML_UART_MISC_RECV_IRQ_CNT_SHIFT); uart_setreg(bas, AML_UART_MISC_REG, mr); uart_barrier(bas); aml8726_uart_bus_getsig(sc); /* Ensure the reset bits are clear. */ cr &= ~(AML_UART_CONTROL_TX_RST | AML_UART_CONTROL_RX_RST); cr &= ~AML_UART_CONTROL_CLR_ERR; /* Enable the receive interrupt. */ cr |= AML_UART_CONTROL_RX_INT_EN; uart_setreg(bas, AML_UART_CONTROL_REG, cr); uart_barrier(bas); return (0); } static int aml8726_uart_bus_detach(struct uart_softc *sc) { struct uart_bas *bas; uint32_t cr; uint32_t mr; bas = &sc->sc_bas; /* Disable all interrupt sources. */ cr = uart_getreg(bas, AML_UART_CONTROL_REG); cr &= ~(AML_UART_CONTROL_TX_INT_EN | AML_UART_CONTROL_RX_INT_EN); uart_setreg(bas, AML_UART_CONTROL_REG, cr); uart_barrier(bas); /* Clear RX FIFO level for generating interrupts. */ mr = uart_getreg(bas, AML_UART_MISC_REG); mr &= ~AML_UART_MISC_RECV_IRQ_CNT_MASK; uart_setreg(bas, AML_UART_MISC_REG, mr); uart_barrier(bas); return (0); } static int aml8726_uart_bus_flush(struct uart_softc *sc, int what) { struct uart_bas *bas; uint32_t cr; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); cr = uart_getreg(bas, AML_UART_CONTROL_REG); if (what & UART_FLUSH_TRANSMITTER) cr |= AML_UART_CONTROL_TX_RST; if (what & UART_FLUSH_RECEIVER) cr |= AML_UART_CONTROL_RX_RST; uart_setreg(bas, AML_UART_CONTROL_REG, cr); uart_barrier(bas); /* Ensure the reset bits are clear. */ cr &= ~(AML_UART_CONTROL_TX_RST | AML_UART_CONTROL_RX_RST); uart_setreg(bas, AML_UART_CONTROL_REG, cr); uart_barrier(bas); uart_unlock(sc->sc_hwmtx); return (0); } static int aml8726_uart_bus_ioctl(struct uart_softc *sc, int request, intptr_t data) { struct uart_bas *bas; int baudrate, divisor, error; uint32_t cr, mr; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); error = 0; switch (request) { case UART_IOCTL_BAUD: cr = uart_getreg(bas, AML_UART_CONTROL_REG); cr &= AML_UART_CONTROL_BAUD_MASK; mr = uart_getreg(bas, AML_UART_MISC_REG); mr &= AML_UART_MISC_BAUD_EXT_MASK; divisor = ((mr >> AML_UART_MISC_BAUD_EXT_SHIFT) << AML_UART_CONTROL_BAUD_WIDTH) | cr; baudrate = bas->rclk / 4 / (divisor + 1); if (baudrate > 0) *(int*)data = baudrate; else error = ENXIO; break; case UART_IOCTL_IFLOW: case UART_IOCTL_OFLOW: cr = uart_getreg(bas, AML_UART_CONTROL_REG); if (data) cr &= ~AML_UART_CONTROL_TWO_WIRE_EN; else cr |= AML_UART_CONTROL_TWO_WIRE_EN; uart_setreg(bas, AML_UART_CONTROL_REG, cr); break; default: error = EINVAL; break; } uart_unlock(sc->sc_hwmtx); return (error); } static int aml8726_uart_bus_ipend(struct uart_softc *sc) { struct uart_bas *bas; int ipend; uint32_t sr; uint32_t cr; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); ipend = 0; sr = uart_getreg(bas, AML_UART_STATUS_REG); cr = uart_getreg(bas, AML_UART_CONTROL_REG); if ((sr & AML_UART_STATUS_RX_FIFO_OVERFLOW) != 0) ipend |= SER_INT_OVERRUN; if ((sr & AML_UART_STATUS_TX_FIFO_EMPTY) != 0 && (cr & AML_UART_CONTROL_TX_INT_EN) != 0) { ipend |= SER_INT_TXIDLE; cr &= ~AML_UART_CONTROL_TX_INT_EN; uart_setreg(bas, AML_UART_CONTROL_REG, cr); uart_barrier(bas); } if ((sr & AML_UART_STATUS_RX_FIFO_EMPTY) == 0) ipend |= SER_INT_RXREADY; uart_unlock(sc->sc_hwmtx); return (ipend); } static int aml8726_uart_bus_param(struct uart_softc *sc, int baudrate, int databits, int stopbits, int parity) { struct uart_bas *bas; int error; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); error = aml8726_uart_param(bas, baudrate, databits, stopbits, parity); uart_unlock(sc->sc_hwmtx); return (error); } static int aml8726_uart_bus_receive(struct uart_softc *sc) { struct uart_bas *bas; int xc; uint32_t sr; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); sr = uart_getreg(bas, AML_UART_STATUS_REG); while ((sr & AML_UART_STATUS_RX_FIFO_EMPTY) == 0) { if (uart_rx_full(sc)) { sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN; break; } xc = uart_getreg(bas, AML_UART_RFIFO_REG) & 0xff; if (sr & AML_UART_STATUS_FRAME_ERR) xc |= UART_STAT_FRAMERR; if (sr & AML_UART_STATUS_PARITY_ERR) xc |= UART_STAT_PARERR; uart_rx_put(sc, xc); sr = uart_getreg(bas, AML_UART_STATUS_REG); } /* Discard everything left in the RX FIFO. */ while ((sr & AML_UART_STATUS_RX_FIFO_EMPTY) == 0) { (void)uart_getreg(bas, AML_UART_RFIFO_REG); sr = uart_getreg(bas, AML_UART_STATUS_REG); } /* Reset error bits */ if ((sr & (AML_UART_STATUS_FRAME_ERR | AML_UART_STATUS_PARITY_ERR)) != 0) { uart_setreg(bas, AML_UART_CONTROL_REG, (uart_getreg(bas, AML_UART_CONTROL_REG) | AML_UART_CONTROL_CLR_ERR)); uart_barrier(bas); uart_setreg(bas, AML_UART_CONTROL_REG, (uart_getreg(bas, AML_UART_CONTROL_REG) & ~AML_UART_CONTROL_CLR_ERR)); uart_barrier(bas); } uart_unlock(sc->sc_hwmtx); return (0); } static int aml8726_uart_bus_transmit(struct uart_softc *sc) { struct uart_bas *bas; int i; uint32_t cr; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); /* * Wait for sufficient space since aml8726_uart_putc * may have been called after SER_INT_TXIDLE occurred. */ while ((uart_getreg(bas, AML_UART_STATUS_REG) & AML_UART_STATUS_TX_FIFO_EMPTY) == 0) cpu_spinwait(); for (i = 0; i < sc->sc_txdatasz; i++) { uart_setreg(bas, AML_UART_WFIFO_REG, sc->sc_txbuf[i]); uart_barrier(bas); } sc->sc_txbusy = 1; cr = uart_getreg(bas, AML_UART_CONTROL_REG); cr |= AML_UART_CONTROL_TX_INT_EN; uart_setreg(bas, AML_UART_CONTROL_REG, cr); uart_barrier(bas); uart_unlock(sc->sc_hwmtx); return (0); } static void aml8726_uart_bus_grab(struct uart_softc *sc) { struct uart_bas *bas; uint32_t cr; /* * Disable the receive interrupt to avoid a race between * aml8726_uart_getc and aml8726_uart_bus_receive which * can trigger: * * panic: bad stray interrupt * * due to the RX FIFO receiving a character causing an * interrupt which gets serviced after aml8726_uart_getc * has been called (meaning the RX FIFO is now empty). */ bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); cr = uart_getreg(bas, AML_UART_CONTROL_REG); cr &= ~AML_UART_CONTROL_RX_INT_EN; uart_setreg(bas, AML_UART_CONTROL_REG, cr); uart_barrier(bas); uart_unlock(sc->sc_hwmtx); } static void aml8726_uart_bus_ungrab(struct uart_softc *sc) { struct uart_bas *bas; uint32_t cr; uint32_t mr; /* * The RX FIFO level being set indicates that the device * is currently attached meaning the receive interrupt * should be enabled. */ bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); mr = uart_getreg(bas, AML_UART_MISC_REG); mr &= AML_UART_MISC_RECV_IRQ_CNT_MASK; if (mr != 0) { cr = uart_getreg(bas, AML_UART_CONTROL_REG); cr |= AML_UART_CONTROL_RX_INT_EN; uart_setreg(bas, AML_UART_CONTROL_REG, cr); uart_barrier(bas); } uart_unlock(sc->sc_hwmtx); } static kobj_method_t aml8726_uart_methods[] = { KOBJMETHOD(uart_probe, aml8726_uart_bus_probe), KOBJMETHOD(uart_attach, aml8726_uart_bus_attach), KOBJMETHOD(uart_detach, aml8726_uart_bus_detach), KOBJMETHOD(uart_flush, aml8726_uart_bus_flush), KOBJMETHOD(uart_getsig, aml8726_uart_bus_getsig), KOBJMETHOD(uart_setsig, aml8726_uart_bus_setsig), KOBJMETHOD(uart_ioctl, aml8726_uart_bus_ioctl), KOBJMETHOD(uart_ipend, aml8726_uart_bus_ipend), KOBJMETHOD(uart_param, aml8726_uart_bus_param), KOBJMETHOD(uart_receive, aml8726_uart_bus_receive), KOBJMETHOD(uart_transmit, aml8726_uart_bus_transmit), KOBJMETHOD(uart_grab, aml8726_uart_bus_grab), KOBJMETHOD(uart_ungrab, aml8726_uart_bus_ungrab), { 0, 0 } }; struct uart_class uart_aml8726_class = { "uart", aml8726_uart_methods, sizeof(struct uart_softc), .uc_ops = &aml8726_uart_ops, .uc_range = 24, - .uc_rclk = 0 + .uc_rclk = 0, + .uc_rshift = 0 }; static struct ofw_compat_data compat_data[] = { { "amlogic,meson-uart", (uintptr_t)&uart_aml8726_class }, { NULL, (uintptr_t)NULL } }; UART_FDT_CLASS_AND_DEVICE(compat_data); Index: head/sys/arm/freescale/vybrid/vf_uart.c =================================================================== --- head/sys/arm/freescale/vybrid/vf_uart.c (revision 281437) +++ head/sys/arm/freescale/vybrid/vf_uart.c (revision 281438) @@ -1,515 +1,516 @@ /*- * Copyright (c) 2013 Ruslan Bukin * 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 AND CONTRIBUTORS ``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. */ /* * Vybrid Family Universal Asynchronous Receiver/Transmitter * Chapter 49, Vybrid Reference Manual, Rev. 5, 07/2013 */ #include __FBSDID("$FreeBSD$"); #include "opt_ddb.h" #include #include #include #include #include #include #include #include #include #include #include "uart_if.h" #define UART_BDH 0x00 /* Baud Rate Registers: High */ #define UART_BDL 0x01 /* Baud Rate Registers: Low */ #define UART_C1 0x02 /* Control Register 1 */ #define UART_C2 0x03 /* Control Register 2 */ #define UART_S1 0x04 /* Status Register 1 */ #define UART_S2 0x05 /* Status Register 2 */ #define UART_C3 0x06 /* Control Register 3 */ #define UART_D 0x07 /* Data Register */ #define UART_MA1 0x08 /* Match Address Registers 1 */ #define UART_MA2 0x09 /* Match Address Registers 2 */ #define UART_C4 0x0A /* Control Register 4 */ #define UART_C5 0x0B /* Control Register 5 */ #define UART_ED 0x0C /* Extended Data Register */ #define UART_MODEM 0x0D /* Modem Register */ #define UART_IR 0x0E /* Infrared Register */ #define UART_PFIFO 0x10 /* FIFO Parameters */ #define UART_CFIFO 0x11 /* FIFO Control Register */ #define UART_SFIFO 0x12 /* FIFO Status Register */ #define UART_TWFIFO 0x13 /* FIFO Transmit Watermark */ #define UART_TCFIFO 0x14 /* FIFO Transmit Count */ #define UART_RWFIFO 0x15 /* FIFO Receive Watermark */ #define UART_RCFIFO 0x16 /* FIFO Receive Count */ #define UART_C7816 0x18 /* 7816 Control Register */ #define UART_IE7816 0x19 /* 7816 Interrupt Enable Register */ #define UART_IS7816 0x1A /* 7816 Interrupt Status Register */ #define UART_WP7816T0 0x1B /* 7816 Wait Parameter Register */ #define UART_WP7816T1 0x1B /* 7816 Wait Parameter Register */ #define UART_WN7816 0x1C /* 7816 Wait N Register */ #define UART_WF7816 0x1D /* 7816 Wait FD Register */ #define UART_ET7816 0x1E /* 7816 Error Threshold Register */ #define UART_TL7816 0x1F /* 7816 Transmit Length Register */ #define UART_C6 0x21 /* CEA709.1-B Control Register 6 */ #define UART_PCTH 0x22 /* CEA709.1-B Packet Cycle Time Counter High */ #define UART_PCTL 0x23 /* CEA709.1-B Packet Cycle Time Counter Low */ #define UART_B1T 0x24 /* CEA709.1-B Beta1 Timer */ #define UART_SDTH 0x25 /* CEA709.1-B Secondary Delay Timer High */ #define UART_SDTL 0x26 /* CEA709.1-B Secondary Delay Timer Low */ #define UART_PRE 0x27 /* CEA709.1-B Preamble */ #define UART_TPL 0x28 /* CEA709.1-B Transmit Packet Length */ #define UART_IE 0x29 /* CEA709.1-B Interrupt Enable Register */ #define UART_WB 0x2A /* CEA709.1-B WBASE */ #define UART_S3 0x2B /* CEA709.1-B Status Register */ #define UART_S4 0x2C /* CEA709.1-B Status Register */ #define UART_RPL 0x2D /* CEA709.1-B Received Packet Length */ #define UART_RPREL 0x2E /* CEA709.1-B Received Preamble Length */ #define UART_CPW 0x2F /* CEA709.1-B Collision Pulse Width */ #define UART_RIDT 0x30 /* CEA709.1-B Receive Indeterminate Time */ #define UART_TIDT 0x31 /* CEA709.1-B Transmit Indeterminate Time */ #define UART_C2_TE (1 << 3) /* Transmitter Enable */ #define UART_C2_TIE (1 << 7) /* Transmitter Interrupt Enable */ #define UART_C2_RE (1 << 2) /* Receiver Enable */ #define UART_C2_RIE (1 << 5) /* Receiver Interrupt Enable */ #define UART_S1_TDRE (1 << 7) /* Transmit Data Register Empty Flag */ #define UART_S1_RDRF (1 << 5) /* Receive Data Register Full Flag */ #define UART_S2_LBKDIF (1 << 7) /* LIN Break Detect Interrupt Flag */ #define UART_C4_BRFA 0x1f /* Baud Rate Fine Adjust */ #define UART_BDH_SBR 0x1f /* UART Baud Rate Bits */ /* * Low-level UART interface. */ static int vf_uart_probe(struct uart_bas *bas); static void vf_uart_init(struct uart_bas *bas, int, int, int, int); static void vf_uart_term(struct uart_bas *bas); static void vf_uart_putc(struct uart_bas *bas, int); static int vf_uart_rxready(struct uart_bas *bas); static int vf_uart_getc(struct uart_bas *bas, struct mtx *); void uart_reinit(struct uart_softc *,int,int); static struct uart_ops uart_vybrid_ops = { .probe = vf_uart_probe, .init = vf_uart_init, .term = vf_uart_term, .putc = vf_uart_putc, .rxready = vf_uart_rxready, .getc = vf_uart_getc, }; static int vf_uart_probe(struct uart_bas *bas) { return (0); } static void vf_uart_init(struct uart_bas *bas, int baudrate, int databits, int stopbits, int parity) { } static void vf_uart_term(struct uart_bas *bas) { } static void vf_uart_putc(struct uart_bas *bas, int c) { while (!(uart_getreg(bas, UART_S1) & UART_S1_TDRE)) ; uart_setreg(bas, UART_D, c); } static int vf_uart_rxready(struct uart_bas *bas) { int usr1; usr1 = uart_getreg(bas, UART_S1); if (usr1 & UART_S1_RDRF) { return (1); } return (0); } static int vf_uart_getc(struct uart_bas *bas, struct mtx *hwmtx) { int c; uart_lock(hwmtx); while (!(uart_getreg(bas, UART_S1) & UART_S1_RDRF)) ; c = uart_getreg(bas, UART_D); uart_unlock(hwmtx); return (c & 0xff); } /* * High-level UART interface. */ struct vf_uart_softc { struct uart_softc base; }; void uart_reinit(struct uart_softc *sc, int clkspeed, int baud) { struct uart_bas *bas; int sbr; int brfa; int reg; bas = &sc->sc_bas; if (!bas) { printf("Error: cant reconfigure bas\n"); return; } uart_setreg(bas, UART_MODEM, 0x00); /* * Disable transmitter and receiver * for a while. */ reg = uart_getreg(bas, UART_C2); reg &= ~(UART_C2_RE | UART_C2_TE); uart_setreg(bas, UART_C2, 0x00); uart_setreg(bas, UART_C1, 0x00); sbr = (uint16_t) (clkspeed / (baud * 16)); brfa = (clkspeed / baud) - (sbr * 16); reg = uart_getreg(bas, UART_BDH); reg &= ~UART_BDH_SBR; reg |= ((sbr & 0x1f00) >> 8); uart_setreg(bas, UART_BDH, reg); reg = sbr & 0x00ff; uart_setreg(bas, UART_BDL, reg); reg = uart_getreg(bas, UART_C4); reg &= ~UART_C4_BRFA; reg |= (brfa & UART_C4_BRFA); uart_setreg(bas, UART_C4, reg); reg = uart_getreg(bas, UART_C2); reg |= (UART_C2_RE | UART_C2_TE); uart_setreg(bas, UART_C2, reg); } static int vf_uart_bus_attach(struct uart_softc *); static int vf_uart_bus_detach(struct uart_softc *); static int vf_uart_bus_flush(struct uart_softc *, int); static int vf_uart_bus_getsig(struct uart_softc *); static int vf_uart_bus_ioctl(struct uart_softc *, int, intptr_t); static int vf_uart_bus_ipend(struct uart_softc *); static int vf_uart_bus_param(struct uart_softc *, int, int, int, int); static int vf_uart_bus_probe(struct uart_softc *); static int vf_uart_bus_receive(struct uart_softc *); static int vf_uart_bus_setsig(struct uart_softc *, int); static int vf_uart_bus_transmit(struct uart_softc *); static kobj_method_t vf_uart_methods[] = { KOBJMETHOD(uart_attach, vf_uart_bus_attach), KOBJMETHOD(uart_detach, vf_uart_bus_detach), KOBJMETHOD(uart_flush, vf_uart_bus_flush), KOBJMETHOD(uart_getsig, vf_uart_bus_getsig), KOBJMETHOD(uart_ioctl, vf_uart_bus_ioctl), KOBJMETHOD(uart_ipend, vf_uart_bus_ipend), KOBJMETHOD(uart_param, vf_uart_bus_param), KOBJMETHOD(uart_probe, vf_uart_bus_probe), KOBJMETHOD(uart_receive, vf_uart_bus_receive), KOBJMETHOD(uart_setsig, vf_uart_bus_setsig), KOBJMETHOD(uart_transmit, vf_uart_bus_transmit), { 0, 0 } }; static struct uart_class uart_vybrid_class = { "vybrid", vf_uart_methods, sizeof(struct vf_uart_softc), .uc_ops = &uart_vybrid_ops, .uc_range = 0x100, - .uc_rclk = 24000000 /* TODO: get value from CCM */ + .uc_rclk = 24000000, /* TODO: get value from CCM */ + .uc_rshift = 0 }; static struct ofw_compat_data compat_data[] = { {"fsl,mvf600-uart", (uintptr_t)&uart_vybrid_class}, {NULL, (uintptr_t)NULL}, }; UART_FDT_CLASS_AND_DEVICE(compat_data); static int vf_uart_bus_attach(struct uart_softc *sc) { struct uart_bas *bas; int reg; bas = &sc->sc_bas; sc->sc_hwiflow = 0; sc->sc_hwoflow = 0; uart_reinit(sc, 66000000, 115200); reg = uart_getreg(bas, UART_C2); if (sc->sc_sysdev != NULL && sc->sc_sysdev->type == UART_DEV_CONSOLE) { reg &= ~UART_C2_RIE; } else { reg |= UART_C2_RIE; } uart_setreg(bas, UART_C2, reg); return (0); } static int vf_uart_bus_detach(struct uart_softc *sc) { /* TODO */ return (0); } static int vf_uart_bus_flush(struct uart_softc *sc, int what) { /* TODO */ return (0); } static int vf_uart_bus_getsig(struct uart_softc *sc) { /* TODO */ return (0); } static int vf_uart_bus_ioctl(struct uart_softc *sc, int request, intptr_t data) { struct uart_bas *bas; int error; bas = &sc->sc_bas; error = 0; uart_lock(sc->sc_hwmtx); switch (request) { case UART_IOCTL_BREAK: /* TODO */ break; case UART_IOCTL_BAUD: /* TODO */ *(int*)data = 115200; break; default: error = EINVAL; break; } uart_unlock(sc->sc_hwmtx); return (error); } static int vf_uart_bus_ipend(struct uart_softc *sc) { struct uart_bas *bas; int ipend; uint32_t usr1, usr2; int reg; int sfifo; bas = &sc->sc_bas; ipend = 0; uart_lock(sc->sc_hwmtx); usr1 = uart_getreg(bas, UART_S1); usr2 = uart_getreg(bas, UART_S2); sfifo = uart_getreg(bas, UART_SFIFO); /* ack usr2 */ uart_setreg(bas, UART_S2, usr2); if (usr1 & UART_S1_TDRE) { reg = uart_getreg(bas, UART_C2); reg &= ~(UART_C2_TIE); uart_setreg(bas, UART_C2, reg); if (sc->sc_txbusy != 0) { ipend |= SER_INT_TXIDLE; } } if (usr1 & UART_S1_RDRF) { reg = uart_getreg(bas, UART_C2); reg &= ~(UART_C2_RIE); uart_setreg(bas, UART_C2, reg); ipend |= SER_INT_RXREADY; } if (usr2 & UART_S2_LBKDIF) { ipend |= SER_INT_BREAK; } uart_unlock(sc->sc_hwmtx); return (ipend); } static int vf_uart_bus_param(struct uart_softc *sc, int baudrate, int databits, int stopbits, int parity) { uart_lock(sc->sc_hwmtx); vf_uart_init(&sc->sc_bas, baudrate, databits, stopbits, parity); uart_unlock(sc->sc_hwmtx); return (0); } static int vf_uart_bus_probe(struct uart_softc *sc) { int error; error = vf_uart_probe(&sc->sc_bas); if (error) return (error); sc->sc_rxfifosz = 1; sc->sc_txfifosz = 1; device_set_desc(sc->sc_dev, "Vybrid Family UART"); return (0); } static int vf_uart_bus_receive(struct uart_softc *sc) { struct uart_bas *bas; int reg; int c; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); /* Read FIFO */ while (uart_getreg(bas, UART_S1) & UART_S1_RDRF) { if (uart_rx_full(sc)) { /* No space left in input buffer */ sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN; break; } c = uart_getreg(bas, UART_D); uart_rx_put(sc, c); } /* Reenable Data Ready interrupt */ reg = uart_getreg(bas, UART_C2); reg |= (UART_C2_RIE); uart_setreg(bas, UART_C2, reg); uart_unlock(sc->sc_hwmtx); return (0); } static int vf_uart_bus_setsig(struct uart_softc *sc, int sig) { struct uart_bas *bas; int reg; /* TODO: implement (?) */ /* XXX workaround to have working console on mount prompt */ /* Enable RX interrupt */ bas = &sc->sc_bas; if (sc->sc_sysdev != NULL && sc->sc_sysdev->type == UART_DEV_CONSOLE) { reg = uart_getreg(bas, UART_C2); reg |= (UART_C2_RIE); uart_setreg(bas, UART_C2, reg); } return (0); } static int vf_uart_bus_transmit(struct uart_softc *sc) { struct uart_bas *bas = &sc->sc_bas; int i; int reg; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); /* Fill TX FIFO */ for (i = 0; i < sc->sc_txdatasz; i++) { uart_setreg(bas, UART_D, sc->sc_txbuf[i] & 0xff); uart_barrier(&sc->sc_bas); } sc->sc_txbusy = 1; /* Call me when ready */ reg = uart_getreg(bas, UART_C2); reg |= (UART_C2_TIE); uart_setreg(bas, UART_C2, reg); uart_unlock(sc->sc_hwmtx); return (0); } Index: head/sys/arm/samsung/exynos/exynos_uart.c =================================================================== --- head/sys/arm/samsung/exynos/exynos_uart.c (revision 281437) +++ head/sys/arm/samsung/exynos/exynos_uart.c (revision 281438) @@ -1,389 +1,390 @@ /* * Copyright (c) 2003 Marcel Moolenaar * Copyright (c) 2007-2009 Andrew Turner * Copyright (c) 2013 Ruslan Bukin * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "uart_if.h" #define DEF_CLK 100000000 static int sscomspeed(long, long); static int exynos4210_uart_param(struct uart_bas *, int, int, int, int); /* * Low-level UART interface. */ static int exynos4210_probe(struct uart_bas *bas); static void exynos4210_init(struct uart_bas *bas, int, int, int, int); static void exynos4210_term(struct uart_bas *bas); static void exynos4210_putc(struct uart_bas *bas, int); static int exynos4210_rxready(struct uart_bas *bas); static int exynos4210_getc(struct uart_bas *bas, struct mtx *mtx); extern SLIST_HEAD(uart_devinfo_list, uart_devinfo) uart_sysdevs; static int sscomspeed(long speed, long frequency) { int x; if (speed <= 0 || frequency <= 0) return (-1); x = (frequency / 16) / speed; return (x-1); } static int exynos4210_uart_param(struct uart_bas *bas, int baudrate, int databits, int stopbits, int parity) { int brd, ulcon; ulcon = 0; switch(databits) { case 5: ulcon |= ULCON_LENGTH_5; break; case 6: ulcon |= ULCON_LENGTH_6; break; case 7: ulcon |= ULCON_LENGTH_7; break; case 8: ulcon |= ULCON_LENGTH_8; break; default: return (EINVAL); } switch (parity) { case UART_PARITY_NONE: ulcon |= ULCON_PARITY_NONE; break; case UART_PARITY_ODD: ulcon |= ULCON_PARITY_ODD; break; case UART_PARITY_EVEN: ulcon |= ULCON_PARITY_EVEN; break; case UART_PARITY_MARK: case UART_PARITY_SPACE: default: return (EINVAL); } if (stopbits == 2) ulcon |= ULCON_STOP; uart_setreg(bas, SSCOM_ULCON, ulcon); brd = sscomspeed(baudrate, bas->rclk); uart_setreg(bas, SSCOM_UBRDIV, brd); return (0); } struct uart_ops uart_exynos4210_ops = { .probe = exynos4210_probe, .init = exynos4210_init, .term = exynos4210_term, .putc = exynos4210_putc, .rxready = exynos4210_rxready, .getc = exynos4210_getc, }; static int exynos4210_probe(struct uart_bas *bas) { return (0); } static void exynos4210_init(struct uart_bas *bas, int baudrate, int databits, int stopbits, int parity) { if (bas->rclk == 0) bas->rclk = DEF_CLK; KASSERT(bas->rclk != 0, ("exynos4210_init: Invalid rclk")); uart_setreg(bas, SSCOM_UCON, 0); uart_setreg(bas, SSCOM_UFCON, UFCON_TXTRIGGER_8 | UFCON_RXTRIGGER_8 | UFCON_TXFIFO_RESET | UFCON_RXFIFO_RESET | UFCON_FIFO_ENABLE); exynos4210_uart_param(bas, baudrate, databits, stopbits, parity); /* Enable UART. */ uart_setreg(bas, SSCOM_UCON, UCON_TXMODE_INT | UCON_RXMODE_INT | UCON_TOINT); uart_setreg(bas, SSCOM_UMCON, UMCON_RTS); } static void exynos4210_term(struct uart_bas *bas) { /* XXX */ } static void exynos4210_putc(struct uart_bas *bas, int c) { while ((bus_space_read_4(bas->bst, bas->bsh, SSCOM_UFSTAT) & UFSTAT_TXFULL) == UFSTAT_TXFULL) continue; uart_setreg(bas, SSCOM_UTXH, c); } static int exynos4210_rxready(struct uart_bas *bas) { return ((uart_getreg(bas, SSCOM_UTRSTAT) & UTRSTAT_RXREADY) == UTRSTAT_RXREADY); } static int exynos4210_getc(struct uart_bas *bas, struct mtx *mtx) { int utrstat; utrstat = bus_space_read_1(bas->bst, bas->bsh, SSCOM_UTRSTAT); while (!(utrstat & UTRSTAT_RXREADY)) { utrstat = bus_space_read_1(bas->bst, bas->bsh, SSCOM_UTRSTAT); continue; } return (bus_space_read_1(bas->bst, bas->bsh, SSCOM_URXH)); } static int exynos4210_bus_probe(struct uart_softc *sc); static int exynos4210_bus_attach(struct uart_softc *sc); static int exynos4210_bus_flush(struct uart_softc *, int); static int exynos4210_bus_getsig(struct uart_softc *); static int exynos4210_bus_ioctl(struct uart_softc *, int, intptr_t); static int exynos4210_bus_ipend(struct uart_softc *); static int exynos4210_bus_param(struct uart_softc *, int, int, int, int); static int exynos4210_bus_receive(struct uart_softc *); static int exynos4210_bus_setsig(struct uart_softc *, int); static int exynos4210_bus_transmit(struct uart_softc *); static kobj_method_t exynos4210_methods[] = { KOBJMETHOD(uart_probe, exynos4210_bus_probe), KOBJMETHOD(uart_attach, exynos4210_bus_attach), KOBJMETHOD(uart_flush, exynos4210_bus_flush), KOBJMETHOD(uart_getsig, exynos4210_bus_getsig), KOBJMETHOD(uart_ioctl, exynos4210_bus_ioctl), KOBJMETHOD(uart_ipend, exynos4210_bus_ipend), KOBJMETHOD(uart_param, exynos4210_bus_param), KOBJMETHOD(uart_receive, exynos4210_bus_receive), KOBJMETHOD(uart_setsig, exynos4210_bus_setsig), KOBJMETHOD(uart_transmit, exynos4210_bus_transmit), {0, 0 } }; int exynos4210_bus_probe(struct uart_softc *sc) { sc->sc_txfifosz = 16; sc->sc_rxfifosz = 16; return (0); } static int exynos4210_bus_attach(struct uart_softc *sc) { sc->sc_hwiflow = 0; sc->sc_hwoflow = 0; return (0); } static int exynos4210_bus_transmit(struct uart_softc *sc) { int i; int reg; uart_lock(sc->sc_hwmtx); for (i = 0; i < sc->sc_txdatasz; i++) { exynos4210_putc(&sc->sc_bas, sc->sc_txbuf[i]); uart_barrier(&sc->sc_bas); } sc->sc_txbusy = 1; uart_unlock(sc->sc_hwmtx); /* unmask TX interrupt */ reg = bus_space_read_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTM); reg &= ~(1 << 2); bus_space_write_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTM, reg); return (0); } static int exynos4210_bus_setsig(struct uart_softc *sc, int sig) { return (0); } static int exynos4210_bus_receive(struct uart_softc *sc) { struct uart_bas *bas; bas = &sc->sc_bas; while (bus_space_read_4(bas->bst, bas->bsh, SSCOM_UFSTAT) & UFSTAT_RXCOUNT) uart_rx_put(sc, uart_getreg(&sc->sc_bas, SSCOM_URXH)); return (0); } static int exynos4210_bus_param(struct uart_softc *sc, int baudrate, int databits, int stopbits, int parity) { int error; if (sc->sc_bas.rclk == 0) sc->sc_bas.rclk = DEF_CLK; KASSERT(sc->sc_bas.rclk != 0, ("exynos4210_init: Invalid rclk")); uart_lock(sc->sc_hwmtx); error = exynos4210_uart_param(&sc->sc_bas, baudrate, databits, stopbits, parity); uart_unlock(sc->sc_hwmtx); return (error); } static int exynos4210_bus_ipend(struct uart_softc *sc) { uint32_t ints; uint32_t txempty, rxready; int reg; int ipend; uart_lock(sc->sc_hwmtx); ints = bus_space_read_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTP); bus_space_write_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTP, ints); txempty = (1 << 2); rxready = (1 << 0); ipend = 0; if ((ints & txempty) > 0) { if (sc->sc_txbusy != 0) ipend |= SER_INT_TXIDLE; /* mask TX interrupt */ reg = bus_space_read_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTM); reg |= (1 << 2); bus_space_write_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTM, reg); } if ((ints & rxready) > 0) { ipend |= SER_INT_RXREADY; } uart_unlock(sc->sc_hwmtx); return (ipend); } static int exynos4210_bus_flush(struct uart_softc *sc, int what) { return (0); } static int exynos4210_bus_getsig(struct uart_softc *sc) { return (0); } static int exynos4210_bus_ioctl(struct uart_softc *sc, int request, intptr_t data) { return (EINVAL); } static struct uart_class uart_exynos4210_class = { "exynos4210 class", exynos4210_methods, 1, .uc_ops = &uart_exynos4210_ops, .uc_range = 8, .uc_rclk = 0, + .uc_rshift = 0 }; static struct ofw_compat_data compat_data[] = { {"exynos", (uintptr_t)&uart_exynos4210_class}, {NULL, (uintptr_t)NULL}, }; UART_FDT_CLASS_AND_DEVICE(compat_data); Index: head/sys/arm/samsung/s3c2xx0/uart_dev_s3c2410.c =================================================================== --- head/sys/arm/samsung/s3c2xx0/uart_dev_s3c2410.c (revision 281437) +++ head/sys/arm/samsung/s3c2xx0/uart_dev_s3c2410.c (revision 281438) @@ -1,405 +1,406 @@ /* * Copyright (c) 2003 Marcel Moolenaar * Copyright (c) 2007-2009 Andrew Turner * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "uart_if.h" /* Finds the subirq from the parent */ #define get_sub_irq(parent, offset) \ ((parent == S3C24X0_INT_UART0) ? S3C24X0_SUBIRQ_MIN + offset : \ ((parent == S3C24X0_INT_UART1) ? S3C24X0_SUBIRQ_MIN + 3 + offset : \ S3C24X0_SUBIRQ_MIN + 6 + offset)) #define RX_OFF 0 #define TX_OFF 1 #define ERR_OFF 2 extern unsigned int s3c2410_pclk; static int sscomspeed(long, long); static int s3c24x0_uart_param(struct uart_bas *, int, int, int, int); /* * Low-level UART interface. */ static int s3c2410_probe(struct uart_bas *bas); static void s3c2410_init(struct uart_bas *bas, int, int, int, int); static void s3c2410_term(struct uart_bas *bas); static void s3c2410_putc(struct uart_bas *bas, int); static int s3c2410_rxready(struct uart_bas *bas); static int s3c2410_getc(struct uart_bas *bas, struct mtx *mtx); extern SLIST_HEAD(uart_devinfo_list, uart_devinfo) uart_sysdevs; static int sscomspeed(long speed, long frequency) { int x; if (speed <= 0 || frequency <= 0) return -1; x = (frequency / 16) / speed; return x-1; } static int s3c24x0_uart_param(struct uart_bas *bas, int baudrate, int databits, int stopbits, int parity) { int brd, ulcon; ulcon = 0; switch(databits) { case 5: ulcon |= ULCON_LENGTH_5; break; case 6: ulcon |= ULCON_LENGTH_6; break; case 7: ulcon |= ULCON_LENGTH_7; break; case 8: ulcon |= ULCON_LENGTH_8; break; default: return (EINVAL); } switch (parity) { case UART_PARITY_NONE: ulcon |= ULCON_PARITY_NONE; break; case UART_PARITY_ODD: ulcon |= ULCON_PARITY_ODD; break; case UART_PARITY_EVEN: ulcon |= ULCON_PARITY_EVEN; break; case UART_PARITY_MARK: case UART_PARITY_SPACE: default: return (EINVAL); } if (stopbits == 2) ulcon |= ULCON_STOP; uart_setreg(bas, SSCOM_ULCON, ulcon); brd = sscomspeed(baudrate, bas->rclk); uart_setreg(bas, SSCOM_UBRDIV, brd); return (0); } struct uart_ops uart_s3c2410_ops = { .probe = s3c2410_probe, .init = s3c2410_init, .term = s3c2410_term, .putc = s3c2410_putc, .rxready = s3c2410_rxready, .getc = s3c2410_getc, }; static int s3c2410_probe(struct uart_bas *bas) { return (0); } static void s3c2410_init(struct uart_bas *bas, int baudrate, int databits, int stopbits, int parity) { if (bas->rclk == 0) bas->rclk = s3c2410_pclk; KASSERT(bas->rclk != 0, ("s3c2410_init: Invalid rclk")); uart_setreg(bas, SSCOM_UCON, 0); uart_setreg(bas, SSCOM_UFCON, UFCON_TXTRIGGER_8 | UFCON_RXTRIGGER_8 | UFCON_TXFIFO_RESET | UFCON_RXFIFO_RESET | UFCON_FIFO_ENABLE); s3c24x0_uart_param(bas, baudrate, databits, stopbits, parity); /* Enable UART. */ uart_setreg(bas, SSCOM_UCON, UCON_TXMODE_INT | UCON_RXMODE_INT | UCON_TOINT); uart_setreg(bas, SSCOM_UMCON, UMCON_RTS); } static void s3c2410_term(struct uart_bas *bas) { /* XXX */ } static void s3c2410_putc(struct uart_bas *bas, int c) { while ((bus_space_read_4(bas->bst, bas->bsh, SSCOM_UFSTAT) & UFSTAT_TXFULL) == UFSTAT_TXFULL) continue; uart_setreg(bas, SSCOM_UTXH, c); } static int s3c2410_rxready(struct uart_bas *bas) { return ((uart_getreg(bas, SSCOM_UTRSTAT) & UTRSTAT_RXREADY) == UTRSTAT_RXREADY); } static int s3c2410_getc(struct uart_bas *bas, struct mtx *mtx) { while (!sscom_rxrdy(bas->bst, bas->bsh)) continue; return sscom_getc(bas->bst, bas->bsh); } static int s3c2410_bus_probe(struct uart_softc *sc); static int s3c2410_bus_attach(struct uart_softc *sc); static int s3c2410_bus_flush(struct uart_softc *, int); static int s3c2410_bus_getsig(struct uart_softc *); static int s3c2410_bus_ioctl(struct uart_softc *, int, intptr_t); static int s3c2410_bus_ipend(struct uart_softc *); static int s3c2410_bus_param(struct uart_softc *, int, int, int, int); static int s3c2410_bus_receive(struct uart_softc *); static int s3c2410_bus_setsig(struct uart_softc *, int); static int s3c2410_bus_transmit(struct uart_softc *); static void s3c2410_bus_grab(struct uart_softc *); static void s3c2410_bus_ungrab(struct uart_softc *); static kobj_method_t s3c2410_methods[] = { KOBJMETHOD(uart_probe, s3c2410_bus_probe), KOBJMETHOD(uart_attach, s3c2410_bus_attach), KOBJMETHOD(uart_flush, s3c2410_bus_flush), KOBJMETHOD(uart_getsig, s3c2410_bus_getsig), KOBJMETHOD(uart_ioctl, s3c2410_bus_ioctl), KOBJMETHOD(uart_ipend, s3c2410_bus_ipend), KOBJMETHOD(uart_param, s3c2410_bus_param), KOBJMETHOD(uart_receive, s3c2410_bus_receive), KOBJMETHOD(uart_setsig, s3c2410_bus_setsig), KOBJMETHOD(uart_transmit, s3c2410_bus_transmit), KOBJMETHOD(uart_grab, s3c2410_bus_grab), KOBJMETHOD(uart_ungrab, s3c2410_bus_ungrab), {0, 0 } }; int s3c2410_bus_probe(struct uart_softc *sc) { switch(s3c2xx0_softc->sc_cpu) { case CPU_S3C2410: sc->sc_txfifosz = 16; sc->sc_rxfifosz = 16; break; case CPU_S3C2440: sc->sc_txfifosz = 64; sc->sc_rxfifosz = 64; break; default: return (ENXIO); } return (0); } static int s3c2410_bus_attach(struct uart_softc *sc) { uintptr_t irq; sc->sc_hwiflow = 0; sc->sc_hwoflow = 0; irq = rman_get_start(sc->sc_ires); arm_unmask_irq(irq); arm_unmask_irq(get_sub_irq(irq, RX_OFF)); arm_unmask_irq(get_sub_irq(irq, TX_OFF)); arm_unmask_irq(get_sub_irq(irq, ERR_OFF)); return (0); } static int s3c2410_bus_transmit(struct uart_softc *sc) { uintptr_t irq; uart_lock(sc->sc_hwmtx); for (int i = 0; i < sc->sc_txdatasz; i++) { s3c2410_putc(&sc->sc_bas, sc->sc_txbuf[i]); uart_barrier(&sc->sc_bas); } sc->sc_txbusy = 1; uart_unlock(sc->sc_hwmtx); irq = rman_get_start(sc->sc_ires); arm_unmask_irq(get_sub_irq(irq, TX_OFF)); return (0); } static int s3c2410_bus_setsig(struct uart_softc *sc, int sig) { return (0); } static int s3c2410_bus_receive(struct uart_softc *sc) { uart_rx_put(sc, uart_getreg(&sc->sc_bas, SSCOM_URXH)); return (0); } static int s3c2410_bus_param(struct uart_softc *sc, int baudrate, int databits, int stopbits, int parity) { int error; if (sc->sc_bas.rclk == 0) sc->sc_bas.rclk = s3c2410_pclk; KASSERT(sc->sc_bas.rclk != 0, ("s3c2410_init: Invalid rclk")); uart_lock(sc->sc_hwmtx); error = s3c24x0_uart_param(&sc->sc_bas, baudrate, databits, stopbits, parity); uart_unlock(sc->sc_hwmtx); return (error); } static int s3c2410_bus_ipend(struct uart_softc *sc) { uint32_t ufstat, txmask, rxmask; uintptr_t irq; int ipend = 0; uart_lock(sc->sc_hwmtx); ufstat = bus_space_read_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UFSTAT); uart_unlock(sc->sc_hwmtx); txmask = rxmask = 0; switch (s3c2xx0_softc->sc_cpu) { case CPU_S3C2410: txmask = UFSTAT_TXCOUNT; rxmask = UFSTAT_RXCOUNT; break; case CPU_S3C2440: txmask = S3C2440_UFSTAT_TXCOUNT; rxmask = S3C2440_UFSTAT_RXCOUNT; break; } if ((ufstat & txmask) == 0) { if (sc->sc_txbusy != 0) ipend |= SER_INT_TXIDLE; irq = rman_get_start(sc->sc_ires); arm_mask_irq(get_sub_irq(irq, TX_OFF)); } if ((ufstat & rxmask) > 0) { ipend |= SER_INT_RXREADY; } return (ipend); } static int s3c2410_bus_flush(struct uart_softc *sc, int what) { return (0); } static int s3c2410_bus_getsig(struct uart_softc *sc) { return (0); } static int s3c2410_bus_ioctl(struct uart_softc *sc, int request, intptr_t data) { return (EINVAL); } static void s3c2410_bus_grab(struct uart_softc *sc) { uintptr_t irq; irq = rman_get_start(sc->sc_ires); arm_mask_irq(get_sub_irq(irq, RX_OFF)); } static void s3c2410_bus_ungrab(struct uart_softc *sc) { uintptr_t irq; irq = rman_get_start(sc->sc_ires); arm_unmask_irq(get_sub_irq(irq, RX_OFF)); } struct uart_class uart_s3c2410_class = { "s3c2410 class", s3c2410_methods, 1, .uc_ops = &uart_s3c2410_ops, .uc_range = 8, .uc_rclk = 0, + .uc_rshift = 0 }; Index: head/sys/dev/uart/uart_bus.h =================================================================== --- head/sys/dev/uart/uart_bus.h (revision 281437) +++ head/sys/dev/uart/uart_bus.h (revision 281438) @@ -1,217 +1,218 @@ /*- * 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_ #ifndef KLD_MODULE #include "opt_uart.h" #endif #include #include /* 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 /* Received character status bits. */ #define UART_STAT_BREAK 0x0100 #define UART_STAT_FRAMERR 0x0200 #define UART_STAT_OVERRUN 0x0400 #define UART_STAT_PARERR 0x0800 #ifdef UART_PPS_ON_CTS #define UART_SIG_DPPS SER_DCTS #define UART_SIG_PPS SER_CTS #else #define UART_SIG_DPPS SER_DDCD #define UART_SIG_PPS SER_DCD #endif /* UART_IOCTL() requests */ #define UART_IOCTL_BREAK 1 #define UART_IOCTL_IFLOW 2 #define UART_IOCTL_OFLOW 3 #define UART_IOCTL_BAUD 4 /* * UART class & instance (=softc) */ struct uart_class { KOBJ_CLASS_FIELDS; struct uart_ops *uc_ops; /* Low-level console operations. */ u_int uc_range; /* Bus space address range. */ u_int uc_rclk; /* Default rclk for this device. */ + u_int uc_rshift; /* Default regshift for this device. */ }; struct uart_softc { KOBJ_FIELDS; struct uart_class *sc_class; struct uart_bas sc_bas; device_t sc_dev; struct mtx sc_hwmtx_s; /* Spinlock protecting hardware. */ struct mtx *sc_hwmtx; 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; struct callout sc_timer; int sc_callout:1; /* This UART is opened for callout. */ int sc_fastintr:1; /* This UART uses fast interrupts. */ 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. */ int sc_isquelch:1; /* This UART has input squelched. */ 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. */ /* Pulse capturing support (PPS). */ struct pps_state sc_pps; /* Upper layer data. */ void *sc_softih; uint32_t sc_ttypend; union { /* TTY specific data. */ struct { 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_resume(device_t dev); serdev_intr_t *uart_bus_ihand(device_t dev, int ipend); int uart_bus_ipend(device_t dev); int uart_bus_probe(device_t dev, int regshft, int rclk, int rid, int chan); int uart_bus_sysdev(device_t dev); void uart_sched_softih(struct uart_softc *, uint32_t); 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_next(struct uart_softc *sc) { int ptr; ptr = sc->sc_rxget; if (ptr == sc->sc_rxput) return (-1); ptr += 1; sc->sc_rxget = (ptr < sc->sc_rxbufsz) ? ptr : 0; return (0); } static __inline int uart_rx_peek(struct uart_softc *sc) { int ptr; ptr = sc->sc_rxget; return ((ptr == sc->sc_rxput) ? -1 : sc->sc_rxbuf[ptr]); } 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_ */ Index: head/sys/dev/uart/uart_bus_fdt.c =================================================================== --- head/sys/dev/uart/uart_bus_fdt.c (revision 281437) +++ head/sys/dev/uart/uart_bus_fdt.c (revision 281438) @@ -1,139 +1,134 @@ /*- * Copyright (c) 2009-2010 The FreeBSD Foundation * All rights reserved. * * This software was developed by Semihalf under sponsorship from * the FreeBSD Foundation. * * 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 AND CONTRIBUTORS ``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 __FBSDID("$FreeBSD$"); #include "opt_platform.h" #include #include #include #include #include #include #include #include #include #include #include #include static int uart_fdt_probe(device_t); static device_method_t uart_fdt_methods[] = { /* Device interface */ DEVMETHOD(device_probe, uart_fdt_probe), DEVMETHOD(device_attach, uart_bus_attach), DEVMETHOD(device_detach, uart_bus_detach), { 0, 0 } }; static driver_t uart_fdt_driver = { uart_driver_name, uart_fdt_methods, sizeof(struct uart_softc), }; int uart_fdt_get_clock(phandle_t node, pcell_t *cell) { /* clock-frequency is a FreeBSD-only extention. */ if ((OF_getencprop(node, "clock-frequency", cell, sizeof(*cell))) <= 0) { /* Try to retrieve parent 'bus-frequency' */ /* XXX this should go to simple-bus fixup or so */ if ((OF_getencprop(OF_parent(node), "bus-frequency", cell, sizeof(*cell))) <= 0) *cell = 0; } return (0); } int uart_fdt_get_shift(phandle_t node, pcell_t *cell) { -#ifdef __aarch64__ -#define DEFAULT_SHIFT 2 -#else -#define DEFAULT_SHIFT 0 -#endif if ((OF_getencprop(node, "reg-shift", cell, sizeof(*cell))) <= 0) - *cell = DEFAULT_SHIFT; + return (-1); return (0); -#undef DEFAULT_SHIFT } static uintptr_t uart_fdt_find_device(device_t dev) { struct ofw_compat_data **cd; const struct ofw_compat_data *ocd; SET_FOREACH(cd, uart_fdt_class_and_device_set) { ocd = ofw_bus_search_compatible(dev, *cd); if (ocd->ocd_data != 0) return (ocd->ocd_data); } return (0); } static int uart_fdt_probe(device_t dev) { struct uart_softc *sc; phandle_t node; pcell_t clock, shift; int err; sc = device_get_softc(dev); if (!ofw_bus_status_okay(dev)) return (ENXIO); sc->sc_class = (struct uart_class *)uart_fdt_find_device(dev); if (sc->sc_class == NULL) return (ENXIO); node = ofw_bus_get_node(dev); if ((err = uart_fdt_get_clock(node, &clock)) != 0) return (err); - uart_fdt_get_shift(node, &shift); + if (uart_fdt_get_shift(node, &shift) != 0) + shift = uart_getregshift(sc->sc_class); return (uart_bus_probe(dev, (int)shift, (int)clock, 0, 0)); } DRIVER_MODULE(uart, simplebus, uart_fdt_driver, uart_devclass, 0, 0); DRIVER_MODULE(uart, ofwbus, uart_fdt_driver, uart_devclass, 0, 0); Index: head/sys/dev/uart/uart_core.c =================================================================== --- head/sys/dev/uart/uart_core.c (revision 281437) +++ head/sys/dev/uart/uart_core.c (revision 281438) @@ -1,642 +1,648 @@ /*- * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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); static MALLOC_DEFINE(M_UART, "UART", "UART driver"); #ifndef UART_POLL_FREQ #define UART_POLL_FREQ 50 #endif static int uart_poll_freq = UART_POLL_FREQ; TUNABLE_INT("debug.uart_poll_freq", &uart_poll_freq); void uart_add_sysdev(struct uart_devinfo *di) { SLIST_INSERT_HEAD(&uart_sysdevs, di, next); } const char * uart_getname(struct uart_class *uc) { return ((uc != NULL) ? uc->name : NULL); } struct uart_ops * uart_getops(struct uart_class *uc) { return ((uc != NULL) ? uc->uc_ops : NULL); } int uart_getrange(struct uart_class *uc) { return ((uc != NULL) ? uc->uc_range : 0); } +u_int +uart_getregshift(struct uart_class *uc) +{ + return ((uc != NULL) ? uc->uc_rshift : 0); +} + /* * Schedule a soft interrupt. We do this on the 0 to !0 transition * of the TTY pending interrupt status. */ void uart_sched_softih(struct uart_softc *sc, uint32_t ipend) { uint32_t new, old; do { old = sc->sc_ttypend; new = old | ipend; } while (!atomic_cmpset_32(&sc->sc_ttypend, old, new)); if ((old & SER_INT_MASK) == 0) swi_sched(sc->sc_softih, 0); } /* * 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 __inline int uart_intr_break(void *arg) { struct uart_softc *sc = arg; #if defined(KDB) if (sc->sc_sysdev != NULL && sc->sc_sysdev->type == UART_DEV_CONSOLE) { if (kdb_break()) return (0); } #endif if (sc->sc_opened) uart_sched_softih(sc, SER_INT_BREAK); return (0); } /* * 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 losing 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 __inline int uart_intr_overrun(void *arg) { struct uart_softc *sc = arg; if (sc->sc_opened) { UART_RECEIVE(sc); if (uart_rx_put(sc, UART_STAT_OVERRUN)) sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN; uart_sched_softih(sc, SER_INT_RXREADY); } UART_FLUSH(sc, UART_FLUSH_RECEIVER); return (0); } /* * Received data ready. */ static __inline int uart_intr_rxready(void *arg) { struct uart_softc *sc = arg; int rxp; rxp = sc->sc_rxput; UART_RECEIVE(sc); #if defined(KDB) if (sc->sc_sysdev != NULL && sc->sc_sysdev->type == UART_DEV_CONSOLE) { while (rxp != sc->sc_rxput) { kdb_alt_break(sc->sc_rxbuf[rxp++], &sc->sc_altbrk); if (rxp == sc->sc_rxbufsz) rxp = 0; } } #endif if (sc->sc_opened) uart_sched_softih(sc, SER_INT_RXREADY); else sc->sc_rxput = sc->sc_rxget; /* Ignore received data. */ return (1); } /* * 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 losing state transitions due to having more * than 1 hardware interrupt between software interrupts. */ static __inline int uart_intr_sigchg(void *arg) { struct uart_softc *sc = arg; int new, old, sig; sig = UART_GETSIG(sc); if (sc->sc_pps.ppsparam.mode & PPS_CAPTUREBOTH) { if (sig & UART_SIG_DPPS) { pps_capture(&sc->sc_pps); pps_event(&sc->sc_pps, (sig & UART_SIG_PPS) ? PPS_CAPTUREASSERT : PPS_CAPTURECLEAR); } } /* * Keep track of signal changes, even when the device is not * opened. This allows us to inform upper layers about a * possible loss of DCD and thus the existence of a (possibly) * different connection when we have DCD back, during the time * that the device was closed. */ do { old = sc->sc_ttypend; new = old & ~SER_MASK_STATE; new |= sig & SER_INT_SIGMASK; } while (!atomic_cmpset_32(&sc->sc_ttypend, old, new)); if (sc->sc_opened) uart_sched_softih(sc, SER_INT_SIGCHG); return (1); } /* * The transmitter can accept more data. */ static __inline int uart_intr_txidle(void *arg) { struct uart_softc *sc = arg; if (sc->sc_txbusy) { sc->sc_txbusy = 0; uart_sched_softih(sc, SER_INT_TXIDLE); } return (0); } static int uart_intr(void *arg) { struct uart_softc *sc = arg; int cnt, ipend; if (sc->sc_leaving) return (FILTER_STRAY); cnt = 0; while (cnt < 20 && (ipend = UART_IPEND(sc)) != 0) { cnt++; if (ipend & SER_INT_OVERRUN) uart_intr_overrun(sc); if (ipend & SER_INT_BREAK) uart_intr_break(sc); if (ipend & SER_INT_RXREADY) uart_intr_rxready(sc); if (ipend & SER_INT_SIGCHG) uart_intr_sigchg(sc); if (ipend & SER_INT_TXIDLE) uart_intr_txidle(sc); } if (sc->sc_polled) { callout_reset(&sc->sc_timer, hz / uart_poll_freq, (timeout_t *)uart_intr, sc); } return ((cnt == 0) ? FILTER_STRAY : ((cnt == 20) ? FILTER_SCHEDULE_THREAD : FILTER_HANDLED)); } serdev_intr_t * uart_bus_ihand(device_t dev, int ipend) { switch (ipend) { case SER_INT_BREAK: return (uart_intr_break); case SER_INT_OVERRUN: return (uart_intr_overrun); case SER_INT_RXREADY: return (uart_intr_rxready); case SER_INT_SIGCHG: return (uart_intr_sigchg); case SER_INT_TXIDLE: return (uart_intr_txidle); } return (NULL); } int uart_bus_ipend(device_t dev) { struct uart_softc *sc; sc = device_get_softc(dev); return (UART_IPEND(sc)); } int uart_bus_sysdev(device_t dev) { struct uart_softc *sc; sc = device_get_softc(dev); return ((sc->sc_sysdev != NULL) ? 1 : 0); } int uart_bus_probe(device_t dev, int regshft, int rclk, int rid, int chan) { struct uart_softc *sc; struct uart_devinfo *sysdev; int error; sc = device_get_softc(dev); /* * All uart_class references are weak. Check that the needed * class has been compiled-in. Fail if not. */ if (sc->sc_class == NULL) return (ENXIO); /* * 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. */ 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, uart_getname(sc->sc_class)); /* * 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, uart_getrange(sc->sc_class), 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, uart_getrange(sc->sc_class), 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.chan = chan; sc->sc_bas.regshft = regshft; sc->sc_bas.rclk = (rclk == 0) ? sc->sc_class->uc_rclk : rclk; SLIST_FOREACH(sysdev, &uart_sysdevs, next) { if (chan == sysdev->bas.chan && uart_cpu_eqres(&sc->sc_bas, &sysdev->bas)) { /* XXX check if ops matches class. */ sc->sc_sysdev = sysdev; sysdev->bas.rclk = sc->sc_bas.rclk; } } error = UART_PROBE(sc); bus_release_resource(dev, sc->sc_rtype, sc->sc_rrid, sc->sc_rres); return ((error) ? error : BUS_PROBE_DEFAULT); } int uart_bus_attach(device_t dev) { struct uart_softc *sc, *sc0; const char *sep; int error, filt; /* * 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; /* * Now that we know the softc for this device, connect the back * pointer from the sysdev for this device, if any */ if (sc->sc_sysdev != NULL) sc->sc_sysdev->sc = sc; /* * 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; mtx_init(&sc->sc_hwmtx_s, "uart_hwmtx", NULL, MTX_SPIN); if (sc->sc_hwmtx == NULL) sc->sc_hwmtx = &sc->sc_hwmtx_s; /* * 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, uart_getrange(sc->sc_class), RF_ACTIVE); if (sc->sc_rres == NULL) { mtx_destroy(&sc->sc_hwmtx_s); return (ENXIO); } sc->sc_bas.bsh = rman_get_bushandle(sc->sc_rres); sc->sc_bas.bst = rman_get_bustag(sc->sc_rres); /* * Ensure there is room for at least three full FIFOs of data in the * receive buffer (handles the case of low-level drivers with huge * FIFOs), and also ensure that there is no less than the historical * size of 384 bytes (handles the typical small-FIFO case). */ sc->sc_rxbufsz = MAX(384, sc->sc_rxfifosz * 3); 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 (sc->sc_sysdev != NULL) { if (sc->sc_sysdev->baudrate == 0) { if (UART_IOCTL(sc, UART_IOCTL_BAUD, (intptr_t)&sc->sc_sysdev->baudrate) != 0) sc->sc_sysdev->baudrate = -1; } 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); } sc->sc_pps.ppscap = PPS_CAPTUREBOTH; pps_init(&sc->sc_pps); sc->sc_leaving = 0; filt = uart_intr(sc); /* * Don't use interrupts if we couldn't clear any pending interrupt * conditions. We may have broken H/W and polling is probably the * safest thing to do. */ if (filt != FILTER_SCHEDULE_THREAD) { sc->sc_irid = 0; sc->sc_ires = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->sc_irid, RF_ACTIVE | RF_SHAREABLE); } if (sc->sc_ires != NULL) { error = bus_setup_intr(dev, sc->sc_ires, INTR_TYPE_TTY, uart_intr, NULL, sc, &sc->sc_icookie); sc->sc_fastintr = (error == 0) ? 1 : 0; if (!sc->sc_fastintr) error = bus_setup_intr(dev, sc->sc_ires, INTR_TYPE_TTY | INTR_MPSAFE, NULL, (driver_intr_t *)uart_intr, sc, &sc->sc_icookie); 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) { /* No interrupt resource. Force polled mode. */ sc->sc_polled = 1; callout_init(&sc->sc_timer, 1); } 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 (%dHz)", sep, uart_poll_freq); sep = ", "; } printf("\n"); } error = (sc->sc_sysdev != NULL && sc->sc_sysdev->attach != NULL) ? (*sc->sc_sysdev->attach)(sc) : uart_tty_attach(sc); if (error) goto fail; if (sc->sc_sysdev != NULL) sc->sc_sysdev->hwmtx = sc->sc_hwmtx; 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); mtx_destroy(&sc->sc_hwmtx_s); return (error); } int uart_bus_detach(device_t dev) { struct uart_softc *sc; sc = device_get_softc(dev); sc->sc_leaving = 1; if (sc->sc_sysdev != NULL) sc->sc_sysdev->hwmtx = NULL; 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); mtx_destroy(&sc->sc_hwmtx_s); if (sc->sc_class->size > sizeof(*sc)) { device_set_softc(dev, NULL); free(sc, M_UART); } else device_set_softc(dev, NULL); return (0); } int uart_bus_resume(device_t dev) { struct uart_softc *sc; sc = device_get_softc(dev); return (UART_ATTACH(sc)); } void uart_grab(struct uart_devinfo *di) { if (di->sc) UART_GRAB(di->sc); } void uart_ungrab(struct uart_devinfo *di) { if (di->sc) UART_UNGRAB(di->sc); } Index: head/sys/dev/uart/uart_cpu.h =================================================================== --- head/sys/dev/uart/uart_cpu.h (revision 281437) +++ head/sys/dev/uart/uart_cpu.h (revision 281438) @@ -1,175 +1,176 @@ /*- * Copyright (c) 2003, 2004 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_ #include #include #include struct uart_softc; /* * 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 (*rxready)(struct uart_bas *); int (*getc)(struct uart_bas *, struct mtx *); }; extern bus_space_tag_t uart_bus_space_io; extern bus_space_tag_t uart_bus_space_mem; /* * Console and debug port device info. */ 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. */ struct mtx *hwmtx; struct uart_softc *sc; /* valid only from start of attach */ }; int uart_cpu_eqres(struct uart_bas *, struct uart_bas *); int uart_cpu_getdev(int, struct uart_devinfo *); int uart_getenv(int, struct uart_devinfo *, struct uart_class *); const char *uart_getname(struct uart_class *); struct uart_ops *uart_getops(struct uart_class *); int uart_getrange(struct uart_class *); +u_int uart_getregshift(struct uart_class *); 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 void uart_lock(struct mtx *hwmtx) { if (!kdb_active && hwmtx != NULL) mtx_lock_spin(hwmtx); } static __inline void uart_unlock(struct mtx *hwmtx) { if (!kdb_active && hwmtx != NULL) mtx_unlock_spin(hwmtx); } static __inline int uart_probe(struct uart_devinfo *di) { int res; uart_lock(di->hwmtx); res = di->ops->probe(&di->bas); uart_unlock(di->hwmtx); return (res); } static __inline void uart_init(struct uart_devinfo *di) { uart_lock(di->hwmtx); di->ops->init(&di->bas, di->baudrate, di->databits, di->stopbits, di->parity); uart_unlock(di->hwmtx); } static __inline void uart_term(struct uart_devinfo *di) { uart_lock(di->hwmtx); di->ops->term(&di->bas); uart_unlock(di->hwmtx); } static __inline void uart_putc(struct uart_devinfo *di, int c) { uart_lock(di->hwmtx); di->ops->putc(&di->bas, c); uart_unlock(di->hwmtx); } static __inline int uart_rxready(struct uart_devinfo *di) { int res; uart_lock(di->hwmtx); res = di->ops->rxready(&di->bas); uart_unlock(di->hwmtx); return (res); } static __inline int uart_poll(struct uart_devinfo *di) { int res; uart_lock(di->hwmtx); if (di->ops->rxready(&di->bas)) res = di->ops->getc(&di->bas, NULL); else res = -1; uart_unlock(di->hwmtx); return (res); } static __inline int uart_getc(struct uart_devinfo *di) { return (di->ops->getc(&di->bas, di->hwmtx)); } void uart_grab(struct uart_devinfo *di); void uart_ungrab(struct uart_devinfo *di); #endif /* _DEV_UART_CPU_H_ */ Index: head/sys/dev/uart/uart_cpu_fdt.c =================================================================== --- head/sys/dev/uart/uart_cpu_fdt.c (revision 281437) +++ head/sys/dev/uart/uart_cpu_fdt.c (revision 281438) @@ -1,217 +1,219 @@ /*- * Copyright (c) 2009-2010 The FreeBSD Foundation * All rights reserved. * * This software was developed by Semihalf under sponsorship from * the FreeBSD Foundation. * * 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 AND CONTRIBUTORS ``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 __FBSDID("$FreeBSD$"); #include "opt_platform.h" #include #include #include #include #include #include #include #include #ifndef __aarch64__ #include #endif #include #include #include #include #include #include #include #ifdef __aarch64__ extern bus_space_tag_t fdtbus_bs_tag; #endif /* * UART console routines. */ bus_space_tag_t uart_bus_space_io; bus_space_tag_t uart_bus_space_mem; int uart_cpu_eqres(struct uart_bas *b1, struct uart_bas *b2) { if (b1->bst != b2->bst) return (0); if (pmap_kextract(b1->bsh) == 0) return (0); if (pmap_kextract(b2->bsh) == 0) return (0); return ((pmap_kextract(b1->bsh) == pmap_kextract(b2->bsh)) ? 1 : 0); } static int phandle_chosen_propdev(phandle_t chosen, const char *name, phandle_t *node) { char buf[64]; if (OF_getprop(chosen, name, buf, sizeof(buf)) <= 0) return (ENXIO); if ((*node = OF_finddevice(buf)) == -1) return (ENXIO); return (0); } static const struct ofw_compat_data * uart_fdt_find_compatible(phandle_t node, const struct ofw_compat_data *cd) { const struct ofw_compat_data *ocd; for (ocd = cd; ocd->ocd_str != NULL; ocd++) { if (fdt_is_compatible(node, ocd->ocd_str)) return (ocd); } return (NULL); } static uintptr_t uart_fdt_find_by_node(phandle_t node, int class_list) { struct ofw_compat_data **cd; const struct ofw_compat_data *ocd; if (class_list) { SET_FOREACH(cd, uart_fdt_class_set) { ocd = uart_fdt_find_compatible(node, *cd); if ((ocd != NULL) && (ocd->ocd_data != 0)) return (ocd->ocd_data); } } else { SET_FOREACH(cd, uart_fdt_class_and_device_set) { ocd = uart_fdt_find_compatible(node, *cd); if ((ocd != NULL) && (ocd->ocd_data != 0)) return (ocd->ocd_data); } } return (0); } int uart_cpu_getdev(int devtype, struct uart_devinfo *di) { const char *propnames[] = {"stdout-path", "linux,stdout-path", "stdout", "stdin-path", "stdin", NULL}; const char **name; struct uart_class *class; phandle_t node, chosen; pcell_t shift, br, rclk; u_long start, size, pbase, psize; int err; uart_bus_space_mem = fdtbus_bs_tag; uart_bus_space_io = NULL; /* Allow overriding the FDT using the environment. */ class = &uart_ns8250_class; err = uart_getenv(devtype, di, class); if (!err) return (0); if (devtype != UART_DEV_CONSOLE) return (ENXIO); /* * Retrieve /chosen/std{in,out}. */ node = -1; if ((chosen = OF_finddevice("/chosen")) != -1) { for (name = propnames; *name != NULL; name++) { if (phandle_chosen_propdev(chosen, *name, &node) == 0) break; } } if (chosen == -1 || *name == NULL) node = OF_finddevice("serial0"); /* Last ditch */ if (node == -1) /* Can't find anything */ return (ENXIO); /* - * Retrieve serial attributes. - */ - uart_fdt_get_shift(node, &shift); - if (OF_getprop(node, "current-speed", &br, sizeof(br)) <= 0) - br = 0; - else - br = fdt32_to_cpu(br); - - /* * Check old style of UART definition first. Unfortunately, the common * FDT processing is not possible if we have clock, power domains and * pinmux stuff. */ class = (struct uart_class *)uart_fdt_find_by_node(node, 0); if (class != NULL) { if ((err = uart_fdt_get_clock(node, &rclk)) != 0) return (err); } else { /* Check class only linker set */ class = (struct uart_class *)uart_fdt_find_by_node(node, 1); if (class == NULL) return (ENXIO); rclk = 0; } + + /* + * Retrieve serial attributes. + */ + if (uart_fdt_get_shift(node, &shift) != 0) + shift = uart_getregshift(class); + + if (OF_getprop(node, "current-speed", &br, sizeof(br)) <= 0) + br = 0; + else + br = fdt32_to_cpu(br); /* * Finalize configuration. */ di->bas.chan = 0; di->bas.regshft = (u_int)shift; di->baudrate = br; di->bas.rclk = (u_int)rclk; di->ops = uart_getops(class); di->databits = 8; di->stopbits = 1; di->parity = UART_PARITY_NONE; di->bas.bst = uart_bus_space_mem; err = fdt_regsize(node, &start, &size); if (err) return (ENXIO); err = fdt_get_range(OF_parent(node), 0, &pbase, &psize); if (err) pbase = 0; start += pbase; return (bus_space_map(di->bas.bst, start, size, 0, &di->bas.bsh)); } Index: head/sys/dev/uart/uart_dev_imx.c =================================================================== --- head/sys/dev/uart/uart_dev_imx.c (revision 281437) +++ head/sys/dev/uart/uart_dev_imx.c (revision 281438) @@ -1,617 +1,618 @@ /*- * Copyright (c) 2012 The FreeBSD Foundation * All rights reserved. * * This software was developed by Oleksandr Rybalko under sponsorship * from the FreeBSD Foundation. * * 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 AND CONTRIBUTORS ``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 __FBSDID("$FreeBSD$"); #include "opt_ddb.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "uart_if.h" #include /* * The hardare FIFOs are 32 bytes. We want an interrupt when there are 24 bytes * available to read or space for 24 more bytes to write. While 8 bytes of * slack before over/underrun might seem excessive, the hardware can run at * 5mbps, which means 2uS per char, so at full speed 8 bytes provides only 16uS * to get into the interrupt handler and service the fifo. */ #define IMX_FIFOSZ 32 #define IMX_RXFIFO_LEVEL 24 #define IMX_TXFIFO_LEVEL 24 /* * Low-level UART interface. */ static int imx_uart_probe(struct uart_bas *bas); static void imx_uart_init(struct uart_bas *bas, int, int, int, int); static void imx_uart_term(struct uart_bas *bas); static void imx_uart_putc(struct uart_bas *bas, int); static int imx_uart_rxready(struct uart_bas *bas); static int imx_uart_getc(struct uart_bas *bas, struct mtx *); static struct uart_ops uart_imx_uart_ops = { .probe = imx_uart_probe, .init = imx_uart_init, .term = imx_uart_term, .putc = imx_uart_putc, .rxready = imx_uart_rxready, .getc = imx_uart_getc, }; #if 0 /* Handy when debugging. */ static void dumpregs(struct uart_bas *bas, const char * msg) { if (!bootverbose) return; printf("%s bsh 0x%08lx UCR1 0x%08x UCR2 0x%08x " "UCR3 0x%08x UCR4 0x%08x USR1 0x%08x USR2 0x%08x\n", msg, bas->bsh, GETREG(bas, REG(UCR1)), GETREG(bas, REG(UCR2)), GETREG(bas, REG(UCR3)), GETREG(bas, REG(UCR4)), GETREG(bas, REG(USR1)), GETREG(bas, REG(USR2))); } #endif static int imx_uart_probe(struct uart_bas *bas) { return (0); } static u_int imx_uart_getbaud(struct uart_bas *bas) { uint32_t rate, ubir, ubmr; u_int baud, blo, bhi, i; static const u_int predivs[] = {6, 5, 4, 3, 2, 1, 7, 1}; static const u_int std_rates[] = { 9600, 14400, 19200, 38400, 57600, 115200, 230400, 460800, 921600 }; /* * Get the baud rate the hardware is programmed for, then search the * table of standard baud rates for a number that's within 3% of the * actual rate the hardware is programmed for. It's more comforting to * see that your console is running at 115200 than 114942. Note that * here we cannot make a simplifying assumption that the predivider and * numerator are 1 (like we do when setting the baud rate), because we * don't know what u-boot might have set up. */ i = (GETREG(bas, REG(UFCR)) & IMXUART_UFCR_RFDIV_MASK) >> IMXUART_UFCR_RFDIV_SHIFT; rate = imx_ccm_uart_hz() / predivs[i]; ubir = GETREG(bas, REG(UBIR)) + 1; ubmr = GETREG(bas, REG(UBMR)) + 1; baud = ((rate / 16 ) * ubir) / ubmr; blo = (baud * 100) / 103; bhi = (baud * 100) / 97; for (i = 0; i < nitems(std_rates); i++) { rate = std_rates[i]; if (rate >= blo && rate <= bhi) { baud = rate; break; } } return (baud); } static void imx_uart_init(struct uart_bas *bas, int baudrate, int databits, int stopbits, int parity) { uint32_t baseclk, reg; /* Enable the device and the RX/TX channels. */ SET(bas, REG(UCR1), FLD(UCR1, UARTEN)); SET(bas, REG(UCR2), FLD(UCR2, RXEN) | FLD(UCR2, TXEN)); if (databits == 7) DIS(bas, UCR2, WS); else ENA(bas, UCR2, WS); if (stopbits == 2) ENA(bas, UCR2, STPB); else DIS(bas, UCR2, STPB); switch (parity) { case UART_PARITY_ODD: DIS(bas, UCR2, PROE); ENA(bas, UCR2, PREN); break; case UART_PARITY_EVEN: ENA(bas, UCR2, PROE); ENA(bas, UCR2, PREN); break; case UART_PARITY_MARK: case UART_PARITY_SPACE: /* FALLTHROUGH: Hardware doesn't support mark/space. */ case UART_PARITY_NONE: default: DIS(bas, UCR2, PREN); break; } /* * The hardware has an extremely flexible baud clock: it allows setting * both the numerator and denominator of the divider, as well as a * separate pre-divider. We simplify the problem of coming up with a * workable pair of numbers by assuming a pre-divider and numerator of * one because our base clock is so fast we can reach virtually any * reasonable speed with a simple divisor. The numerator value actually * includes the 16x over-sampling (so a value of 16 means divide by 1); * the register value is the numerator-1, so we have a hard-coded 15. * Note that a quirk of the hardware requires that both UBIR and UBMR be * set back to back in order for the change to take effect. */ if (baudrate > 0) { baseclk = imx_ccm_uart_hz(); reg = GETREG(bas, REG(UFCR)); reg = (reg & ~IMXUART_UFCR_RFDIV_MASK) | IMXUART_UFCR_RFDIV_DIV1; SETREG(bas, REG(UFCR), reg); SETREG(bas, REG(UBIR), 15); SETREG(bas, REG(UBMR), (baseclk / baudrate) - 1); } /* * Program the tx lowater and rx hiwater levels at which fifo-service * interrupts are signaled. The tx value is interpetted as "when there * are only this many bytes remaining" (not "this many free"). */ reg = GETREG(bas, REG(UFCR)); reg &= ~(IMXUART_UFCR_TXTL_MASK | IMXUART_UFCR_RXTL_MASK); reg |= (IMX_FIFOSZ - IMX_TXFIFO_LEVEL) << IMXUART_UFCR_TXTL_SHIFT; reg |= IMX_RXFIFO_LEVEL << IMXUART_UFCR_RXTL_SHIFT; SETREG(bas, REG(UFCR), reg); } static void imx_uart_term(struct uart_bas *bas) { } static void imx_uart_putc(struct uart_bas *bas, int c) { while (!(IS(bas, USR1, TRDY))) ; SETREG(bas, REG(UTXD), c); } static int imx_uart_rxready(struct uart_bas *bas) { return ((IS(bas, USR2, RDR)) ? 1 : 0); } static int imx_uart_getc(struct uart_bas *bas, struct mtx *hwmtx) { int c; uart_lock(hwmtx); while (!(IS(bas, USR2, RDR))) ; c = GETREG(bas, REG(URXD)); uart_unlock(hwmtx); #if defined(KDB) if (c & FLD(URXD, BRK)) { if (kdb_break()) return (0); } #endif return (c & 0xff); } /* * High-level UART interface. */ struct imx_uart_softc { struct uart_softc base; }; static int imx_uart_bus_attach(struct uart_softc *); static int imx_uart_bus_detach(struct uart_softc *); static int imx_uart_bus_flush(struct uart_softc *, int); static int imx_uart_bus_getsig(struct uart_softc *); static int imx_uart_bus_ioctl(struct uart_softc *, int, intptr_t); static int imx_uart_bus_ipend(struct uart_softc *); static int imx_uart_bus_param(struct uart_softc *, int, int, int, int); static int imx_uart_bus_probe(struct uart_softc *); static int imx_uart_bus_receive(struct uart_softc *); static int imx_uart_bus_setsig(struct uart_softc *, int); static int imx_uart_bus_transmit(struct uart_softc *); static void imx_uart_bus_grab(struct uart_softc *); static void imx_uart_bus_ungrab(struct uart_softc *); static kobj_method_t imx_uart_methods[] = { KOBJMETHOD(uart_attach, imx_uart_bus_attach), KOBJMETHOD(uart_detach, imx_uart_bus_detach), KOBJMETHOD(uart_flush, imx_uart_bus_flush), KOBJMETHOD(uart_getsig, imx_uart_bus_getsig), KOBJMETHOD(uart_ioctl, imx_uart_bus_ioctl), KOBJMETHOD(uart_ipend, imx_uart_bus_ipend), KOBJMETHOD(uart_param, imx_uart_bus_param), KOBJMETHOD(uart_probe, imx_uart_bus_probe), KOBJMETHOD(uart_receive, imx_uart_bus_receive), KOBJMETHOD(uart_setsig, imx_uart_bus_setsig), KOBJMETHOD(uart_transmit, imx_uart_bus_transmit), KOBJMETHOD(uart_grab, imx_uart_bus_grab), KOBJMETHOD(uart_ungrab, imx_uart_bus_ungrab), { 0, 0 } }; static struct uart_class uart_imx_class = { "imx", imx_uart_methods, sizeof(struct imx_uart_softc), .uc_ops = &uart_imx_uart_ops, .uc_range = 0x100, - .uc_rclk = 24000000 /* TODO: get value from CCM */ + .uc_rclk = 24000000, /* TODO: get value from CCM */ + .uc_rshift = 0 }; static struct ofw_compat_data compat_data[] = { {"fsl,imx6q-uart", (uintptr_t)&uart_imx_class}, {"fsl,imx53-uart", (uintptr_t)&uart_imx_class}, {"fsl,imx51-uart", (uintptr_t)&uart_imx_class}, {"fsl,imx31-uart", (uintptr_t)&uart_imx_class}, {"fsl,imx27-uart", (uintptr_t)&uart_imx_class}, {"fsl,imx25-uart", (uintptr_t)&uart_imx_class}, {"fsl,imx21-uart", (uintptr_t)&uart_imx_class}, {NULL, (uintptr_t)NULL}, }; UART_FDT_CLASS_AND_DEVICE(compat_data); #define SIGCHG(c, i, s, d) \ if (c) { \ i |= (i & s) ? s : s | d; \ } else { \ i = (i & s) ? (i & ~s) | d : i; \ } static int imx_uart_bus_attach(struct uart_softc *sc) { struct uart_bas *bas; struct uart_devinfo *di; bas = &sc->sc_bas; if (sc->sc_sysdev != NULL) { di = sc->sc_sysdev; imx_uart_init(bas, di->baudrate, di->databits, di->stopbits, di->parity); } else { imx_uart_init(bas, 115200, 8, 1, 0); } (void)imx_uart_bus_getsig(sc); /* Clear all pending interrupts. */ SETREG(bas, REG(USR1), 0xffff); SETREG(bas, REG(USR2), 0xffff); DIS(bas, UCR4, DREN); ENA(bas, UCR1, RRDYEN); DIS(bas, UCR1, IDEN); DIS(bas, UCR3, RXDSEN); ENA(bas, UCR2, ATEN); DIS(bas, UCR1, TXMPTYEN); DIS(bas, UCR1, TRDYEN); DIS(bas, UCR4, TCEN); DIS(bas, UCR4, OREN); ENA(bas, UCR4, BKEN); DIS(bas, UCR4, WKEN); DIS(bas, UCR1, ADEN); DIS(bas, UCR3, ACIEN); DIS(bas, UCR2, ESCI); DIS(bas, UCR4, ENIRI); DIS(bas, UCR3, AIRINTEN); DIS(bas, UCR3, AWAKEN); DIS(bas, UCR3, FRAERREN); DIS(bas, UCR3, PARERREN); DIS(bas, UCR1, RTSDEN); DIS(bas, UCR2, RTSEN); DIS(bas, UCR3, DTREN); DIS(bas, UCR3, RI); DIS(bas, UCR3, DCD); DIS(bas, UCR3, DTRDEN); ENA(bas, UCR2, IRTS); ENA(bas, UCR3, RXDMUXSEL); return (0); } static int imx_uart_bus_detach(struct uart_softc *sc) { SETREG(&sc->sc_bas, REG(UCR4), 0); return (0); } static int imx_uart_bus_flush(struct uart_softc *sc, int what) { /* TODO */ return (0); } static int imx_uart_bus_getsig(struct uart_softc *sc) { uint32_t new, old, sig; uint8_t bes; do { old = sc->sc_hwsig; sig = old; uart_lock(sc->sc_hwmtx); bes = GETREG(&sc->sc_bas, REG(USR2)); uart_unlock(sc->sc_hwmtx); /* XXX: chip can show delta */ SIGCHG(bes & FLD(USR2, DCDIN), sig, SER_DCD, SER_DDCD); new = sig & ~SER_MASK_DELTA; } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new)); return (sig); } static int imx_uart_bus_ioctl(struct uart_softc *sc, int request, intptr_t data) { struct uart_bas *bas; int error; bas = &sc->sc_bas; error = 0; uart_lock(sc->sc_hwmtx); switch (request) { case UART_IOCTL_BREAK: /* TODO */ break; case UART_IOCTL_BAUD: *(u_int*)data = imx_uart_getbaud(bas); break; default: error = EINVAL; break; } uart_unlock(sc->sc_hwmtx); return (error); } static int imx_uart_bus_ipend(struct uart_softc *sc) { struct uart_bas *bas; int ipend; uint32_t usr1, usr2; uint32_t ucr1, ucr2, ucr4; bas = &sc->sc_bas; ipend = 0; uart_lock(sc->sc_hwmtx); /* Read pending interrupts */ usr1 = GETREG(bas, REG(USR1)); usr2 = GETREG(bas, REG(USR2)); /* ACK interrupts */ SETREG(bas, REG(USR1), usr1); SETREG(bas, REG(USR2), usr2); ucr1 = GETREG(bas, REG(UCR1)); ucr2 = GETREG(bas, REG(UCR2)); ucr4 = GETREG(bas, REG(UCR4)); /* If we have reached tx low-water, we can tx some more now. */ if ((usr1 & FLD(USR1, TRDY)) && (ucr1 & FLD(UCR1, TRDYEN))) { DIS(bas, UCR1, TRDYEN); ipend |= SER_INT_TXIDLE; } /* * If we have reached the rx high-water, or if there are bytes in the rx * fifo and no new data has arrived for 8 character periods (aging * timer), we have input data to process. */ if (((usr1 & FLD(USR1, RRDY)) && (ucr1 & FLD(UCR1, RRDYEN))) || ((usr1 & FLD(USR1, AGTIM)) && (ucr2 & FLD(UCR2, ATEN)))) { DIS(bas, UCR1, RRDYEN); DIS(bas, UCR2, ATEN); ipend |= SER_INT_RXREADY; } /* A break can come in at any time, it never gets disabled. */ if ((usr2 & FLD(USR2, BRCD)) && (ucr4 & FLD(UCR4, BKEN))) ipend |= SER_INT_BREAK; uart_unlock(sc->sc_hwmtx); return (ipend); } static int imx_uart_bus_param(struct uart_softc *sc, int baudrate, int databits, int stopbits, int parity) { uart_lock(sc->sc_hwmtx); imx_uart_init(&sc->sc_bas, baudrate, databits, stopbits, parity); uart_unlock(sc->sc_hwmtx); return (0); } static int imx_uart_bus_probe(struct uart_softc *sc) { int error; error = imx_uart_probe(&sc->sc_bas); if (error) return (error); /* * On input we can read up to the full fifo size at once. On output, we * want to write only as much as the programmed tx low water level, * because that's all we can be certain we have room for in the fifo * when we get a tx-ready interrupt. */ sc->sc_rxfifosz = IMX_FIFOSZ; sc->sc_txfifosz = IMX_TXFIFO_LEVEL; device_set_desc(sc->sc_dev, "Freescale i.MX UART"); return (0); } static int imx_uart_bus_receive(struct uart_softc *sc) { struct uart_bas *bas; int xc, out; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); /* * Empty the rx fifo. We get the RRDY interrupt when IMX_RXFIFO_LEVEL * (the rx high-water level) is reached, but we set sc_rxfifosz to the * full hardware fifo size, so we can safely process however much is * there, not just the highwater size. */ while (IS(bas, USR2, RDR)) { if (uart_rx_full(sc)) { /* No space left in input buffer */ sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN; break; } xc = GETREG(bas, REG(URXD)); out = xc & 0x000000ff; if (xc & FLD(URXD, FRMERR)) out |= UART_STAT_FRAMERR; if (xc & FLD(URXD, PRERR)) out |= UART_STAT_PARERR; if (xc & FLD(URXD, OVRRUN)) out |= UART_STAT_OVERRUN; if (xc & FLD(URXD, BRK)) out |= UART_STAT_BREAK; uart_rx_put(sc, out); } ENA(bas, UCR1, RRDYEN); ENA(bas, UCR2, ATEN); uart_unlock(sc->sc_hwmtx); return (0); } static int imx_uart_bus_setsig(struct uart_softc *sc, int sig) { return (0); } static int imx_uart_bus_transmit(struct uart_softc *sc) { struct uart_bas *bas = &sc->sc_bas; int i; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); /* * Fill the tx fifo. The uart core puts at most IMX_TXFIFO_LEVEL bytes * into the txbuf (because that's what sc_txfifosz is set to), and * because we got the TRDY (low-water reached) interrupt we know at * least that much space is available in the fifo. */ for (i = 0; i < sc->sc_txdatasz; i++) { SETREG(bas, REG(UTXD), sc->sc_txbuf[i] & 0xff); } sc->sc_txbusy = 1; ENA(bas, UCR1, TRDYEN); uart_unlock(sc->sc_hwmtx); return (0); } static void imx_uart_bus_grab(struct uart_softc *sc) { struct uart_bas *bas = &sc->sc_bas; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); DIS(bas, UCR1, RRDYEN); DIS(bas, UCR2, ATEN); uart_unlock(sc->sc_hwmtx); } static void imx_uart_bus_ungrab(struct uart_softc *sc) { struct uart_bas *bas = &sc->sc_bas; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); ENA(bas, UCR1, RRDYEN); ENA(bas, UCR2, ATEN); uart_unlock(sc->sc_hwmtx); } Index: head/sys/dev/uart/uart_dev_lpc.c =================================================================== --- head/sys/dev/uart/uart_dev_lpc.c (revision 281437) +++ head/sys/dev/uart/uart_dev_lpc.c (revision 281438) @@ -1,934 +1,935 @@ /*- * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include "uart_if.h" #define DEFAULT_RCLK (13 * 1000 * 1000) static bus_space_handle_t bsh_clkpwr; #define lpc_ns8250_get_clkreg(_bas, _reg) \ bus_space_read_4(fdtbus_bs_tag, bsh_clkpwr, (_reg)) #define lpc_ns8250_set_clkreg(_bas, _reg, _val) \ bus_space_write_4(fdtbus_bs_tag, bsh_clkpwr, (_reg), (_val)) /* * Clear pending interrupts. THRE is cleared by reading IIR. Data * that may have been received gets lost here. */ static void lpc_ns8250_clrint(struct uart_bas *bas) { uint8_t iir, lsr; iir = uart_getreg(bas, REG_IIR); while ((iir & IIR_NOPEND) == 0) { iir &= IIR_IMASK; if (iir == IIR_RLS) { lsr = uart_getreg(bas, REG_LSR); if (lsr & (LSR_BI|LSR_FE|LSR_PE)) (void)uart_getreg(bas, REG_DATA); } 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 lpc_ns8250_delay(struct uart_bas *bas) { uint32_t uclk; int x, y; uclk = lpc_ns8250_get_clkreg(bas, LPC_CLKPWR_UART_U5CLK); x = (uclk >> 8) & 0xff; y = uclk & 0xff; return (16000000 / (bas->rclk * x / y)); } static void lpc_ns8250_divisor(int rclk, int baudrate, int *x, int *y) { switch (baudrate) { case 2400: *x = 1; *y = 255; return; case 4800: *x = 1; *y = 169; return; case 9600: *x = 3; *y = 254; return; case 19200: *x = 3; *y = 127; return; case 38400: *x = 6; *y = 127; return; case 57600: *x = 9; *y = 127; return; default: case 115200: *x = 19; *y = 134; return; case 230400: *x = 19; *y = 67; return; case 460800: *x = 38; *y = 67; return; } } static int lpc_ns8250_drain(struct uart_bas *bas, int what) { int delay, limit; delay = lpc_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("lpc_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("lpc_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 lpc_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 lpc_ns8250_param(struct uart_bas *bas, int baudrate, int databits, int stopbits, int parity) { int xdiv, ydiv; 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); uart_setreg(bas, REG_DLL, 0x00); uart_setreg(bas, REG_DLH, 0x00); uart_barrier(bas); lpc_ns8250_divisor(bas->rclk, baudrate, &xdiv, &ydiv); lpc_ns8250_set_clkreg(bas, LPC_CLKPWR_UART_U5CLK, LPC_CLKPWR_UART_UCLK_X(xdiv) | LPC_CLKPWR_UART_UCLK_Y(ydiv)); } /* Set LCR and clear DLAB. */ uart_setreg(bas, REG_LCR, lcr); uart_barrier(bas); return (0); } /* * Low-level UART interface. */ static int lpc_ns8250_probe(struct uart_bas *bas); static void lpc_ns8250_init(struct uart_bas *bas, int, int, int, int); static void lpc_ns8250_term(struct uart_bas *bas); static void lpc_ns8250_putc(struct uart_bas *bas, int); static int lpc_ns8250_rxready(struct uart_bas *bas); static int lpc_ns8250_getc(struct uart_bas *bas, struct mtx *); static struct uart_ops uart_lpc_ns8250_ops = { .probe = lpc_ns8250_probe, .init = lpc_ns8250_init, .term = lpc_ns8250_term, .putc = lpc_ns8250_putc, .rxready = lpc_ns8250_rxready, .getc = lpc_ns8250_getc, }; static int lpc_ns8250_probe(struct uart_bas *bas) { #if 0 u_char val; /* Check known 0 bits that don't depend on DLAB. */ val = uart_getreg(bas, REG_IIR); if (val & 0x30) return (ENXIO); /* * Bit 6 of the MCR (= 0x40) appears to be 1 for the Sun1699 * chip, but otherwise doesn't seem to have a function. In * other words, uart(4) works regardless. Ignore that bit so * the probe succeeds. */ val = uart_getreg(bas, REG_MCR); if (val & 0xa0) return (ENXIO); #endif return (0); } static void lpc_ns8250_init(struct uart_bas *bas, int baudrate, int databits, int stopbits, int parity) { u_char ier; u_long clkmode; /* Enable UART clock */ bus_space_map(fdtbus_bs_tag, LPC_CLKPWR_PHYS_BASE, LPC_CLKPWR_SIZE, 0, &bsh_clkpwr); clkmode = lpc_ns8250_get_clkreg(bas, LPC_UART_CLKMODE); lpc_ns8250_set_clkreg(bas, LPC_UART_CLKMODE, clkmode | LPC_UART_CLKMODE_UART5(1)); #if 0 /* Work around H/W bug */ uart_setreg(bas, REG_DATA, 0x00); #endif if (bas->rclk == 0) bas->rclk = DEFAULT_RCLK; lpc_ns8250_param(bas, baudrate, databits, stopbits, parity); /* Disable all interrupt sources. */ /* * We use 0xe0 instead of 0xf0 as the mask because the XScale PXA * UARTs split the receive time-out interrupt bit out separately as * 0x10. This gets handled by ier_mask and ier_rxbits below. */ ier = uart_getreg(bas, REG_IER) & 0xe0; uart_setreg(bas, REG_IER, ier); 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); lpc_ns8250_clrint(bas); } static void lpc_ns8250_term(struct uart_bas *bas) { /* Clear RTS & DTR. */ uart_setreg(bas, REG_MCR, MCR_IE); uart_barrier(bas); } static void lpc_ns8250_putc(struct uart_bas *bas, int c) { int limit; limit = 250000; while ((uart_getreg(bas, REG_LSR) & LSR_THRE) == 0 && --limit) DELAY(4); uart_setreg(bas, REG_DATA, c); uart_barrier(bas); limit = 250000; while ((uart_getreg(bas, REG_LSR) & LSR_TEMT) == 0 && --limit) DELAY(4); } static int lpc_ns8250_rxready(struct uart_bas *bas) { return ((uart_getreg(bas, REG_LSR) & LSR_RXRDY) != 0 ? 1 : 0); } static int lpc_ns8250_getc(struct uart_bas *bas, struct mtx *hwmtx) { int c; uart_lock(hwmtx); while ((uart_getreg(bas, REG_LSR) & LSR_RXRDY) == 0) { uart_unlock(hwmtx); DELAY(4); uart_lock(hwmtx); } c = uart_getreg(bas, REG_DATA); uart_unlock(hwmtx); return (c); } /* * High-level UART interface. */ struct lpc_ns8250_softc { struct uart_softc base; uint8_t fcr; uint8_t ier; uint8_t mcr; uint8_t ier_mask; uint8_t ier_rxbits; }; static int lpc_ns8250_bus_attach(struct uart_softc *); static int lpc_ns8250_bus_detach(struct uart_softc *); static int lpc_ns8250_bus_flush(struct uart_softc *, int); static int lpc_ns8250_bus_getsig(struct uart_softc *); static int lpc_ns8250_bus_ioctl(struct uart_softc *, int, intptr_t); static int lpc_ns8250_bus_ipend(struct uart_softc *); static int lpc_ns8250_bus_param(struct uart_softc *, int, int, int, int); static int lpc_ns8250_bus_probe(struct uart_softc *); static int lpc_ns8250_bus_receive(struct uart_softc *); static int lpc_ns8250_bus_setsig(struct uart_softc *, int); static int lpc_ns8250_bus_transmit(struct uart_softc *); static void lpc_ns8250_bus_grab(struct uart_softc *); static void lpc_ns8250_bus_ungrab(struct uart_softc *); static kobj_method_t lpc_ns8250_methods[] = { KOBJMETHOD(uart_attach, lpc_ns8250_bus_attach), KOBJMETHOD(uart_detach, lpc_ns8250_bus_detach), KOBJMETHOD(uart_flush, lpc_ns8250_bus_flush), KOBJMETHOD(uart_getsig, lpc_ns8250_bus_getsig), KOBJMETHOD(uart_ioctl, lpc_ns8250_bus_ioctl), KOBJMETHOD(uart_ipend, lpc_ns8250_bus_ipend), KOBJMETHOD(uart_param, lpc_ns8250_bus_param), KOBJMETHOD(uart_probe, lpc_ns8250_bus_probe), KOBJMETHOD(uart_receive, lpc_ns8250_bus_receive), KOBJMETHOD(uart_setsig, lpc_ns8250_bus_setsig), KOBJMETHOD(uart_transmit, lpc_ns8250_bus_transmit), KOBJMETHOD(uart_grab, lpc_ns8250_bus_grab), KOBJMETHOD(uart_ungrab, lpc_ns8250_bus_ungrab), { 0, 0 } }; static struct uart_class uart_lpc_class = { "lpc_ns8250", lpc_ns8250_methods, sizeof(struct lpc_ns8250_softc), .uc_ops = &uart_lpc_ns8250_ops, .uc_range = 8, - .uc_rclk = DEFAULT_RCLK + .uc_rclk = DEFAULT_RCLK, + .uc_rshift = 0 }; static struct ofw_compat_data compat_data[] = { {"lpc,uart", (uintptr_t)&uart_lpc_class}, {NULL, (uintptr_t)NULL}, }; UART_FDT_CLASS_AND_DEVICE(compat_data); #define SIGCHG(c, i, s, d) \ if (c) { \ i |= (i & s) ? s : s | d; \ } else { \ i = (i & s) ? (i & ~s) | d : i; \ } static int lpc_ns8250_bus_attach(struct uart_softc *sc) { struct lpc_ns8250_softc *lpc_ns8250 = (struct lpc_ns8250_softc*)sc; struct uart_bas *bas; unsigned int ivar; bas = &sc->sc_bas; lpc_ns8250->mcr = uart_getreg(bas, REG_MCR); lpc_ns8250->fcr = FCR_ENABLE | FCR_DMA; if (!resource_int_value("uart", device_get_unit(sc->sc_dev), "flags", &ivar)) { if (UART_FLAGS_FCR_RX_LOW(ivar)) lpc_ns8250->fcr |= FCR_RX_LOW; else if (UART_FLAGS_FCR_RX_MEDL(ivar)) lpc_ns8250->fcr |= FCR_RX_MEDL; else if (UART_FLAGS_FCR_RX_HIGH(ivar)) lpc_ns8250->fcr |= FCR_RX_HIGH; else lpc_ns8250->fcr |= FCR_RX_MEDH; } else lpc_ns8250->fcr |= FCR_RX_HIGH; /* Get IER mask */ ivar = 0xf0; resource_int_value("uart", device_get_unit(sc->sc_dev), "ier_mask", &ivar); lpc_ns8250->ier_mask = (uint8_t)(ivar & 0xff); /* Get IER RX interrupt bits */ ivar = IER_EMSC | IER_ERLS | IER_ERXRDY; resource_int_value("uart", device_get_unit(sc->sc_dev), "ier_rxbits", &ivar); lpc_ns8250->ier_rxbits = (uint8_t)(ivar & 0xff); uart_setreg(bas, REG_FCR, lpc_ns8250->fcr); uart_barrier(bas); lpc_ns8250_bus_flush(sc, UART_FLUSH_RECEIVER|UART_FLUSH_TRANSMITTER); if (lpc_ns8250->mcr & MCR_DTR) sc->sc_hwsig |= SER_DTR; if (lpc_ns8250->mcr & MCR_RTS) sc->sc_hwsig |= SER_RTS; lpc_ns8250_bus_getsig(sc); lpc_ns8250_clrint(bas); lpc_ns8250->ier = uart_getreg(bas, REG_IER) & lpc_ns8250->ier_mask; lpc_ns8250->ier |= lpc_ns8250->ier_rxbits; uart_setreg(bas, REG_IER, lpc_ns8250->ier); uart_barrier(bas); return (0); } static int lpc_ns8250_bus_detach(struct uart_softc *sc) { struct lpc_ns8250_softc *lpc_ns8250; struct uart_bas *bas; u_char ier; lpc_ns8250 = (struct lpc_ns8250_softc *)sc; bas = &sc->sc_bas; ier = uart_getreg(bas, REG_IER) & lpc_ns8250->ier_mask; uart_setreg(bas, REG_IER, ier); uart_barrier(bas); lpc_ns8250_clrint(bas); return (0); } static int lpc_ns8250_bus_flush(struct uart_softc *sc, int what) { struct lpc_ns8250_softc *lpc_ns8250 = (struct lpc_ns8250_softc*)sc; struct uart_bas *bas; int error; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); if (sc->sc_rxfifosz > 1) { lpc_ns8250_flush(bas, what); uart_setreg(bas, REG_FCR, lpc_ns8250->fcr); uart_barrier(bas); error = 0; } else error = lpc_ns8250_drain(bas, what); uart_unlock(sc->sc_hwmtx); return (error); } static int lpc_ns8250_bus_getsig(struct uart_softc *sc) { uint32_t new, old, sig; uint8_t msr; do { old = sc->sc_hwsig; sig = old; uart_lock(sc->sc_hwmtx); msr = uart_getreg(&sc->sc_bas, REG_MSR); uart_unlock(sc->sc_hwmtx); SIGCHG(msr & MSR_DSR, sig, SER_DSR, SER_DDSR); SIGCHG(msr & MSR_CTS, sig, SER_CTS, SER_DCTS); SIGCHG(msr & MSR_DCD, sig, SER_DCD, SER_DDCD); SIGCHG(msr & MSR_RI, sig, SER_RI, SER_DRI); new = sig & ~SER_MASK_DELTA; } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new)); return (sig); } static int lpc_ns8250_bus_ioctl(struct uart_softc *sc, int request, intptr_t data) { struct uart_bas *bas; int baudrate, divisor, error; uint8_t efr, lcr; bas = &sc->sc_bas; error = 0; uart_lock(sc->sc_hwmtx); 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; case UART_IOCTL_IFLOW: lcr = uart_getreg(bas, REG_LCR); uart_barrier(bas); uart_setreg(bas, REG_LCR, 0xbf); uart_barrier(bas); efr = uart_getreg(bas, REG_EFR); if (data) efr |= EFR_RTS; else efr &= ~EFR_RTS; uart_setreg(bas, REG_EFR, efr); uart_barrier(bas); uart_setreg(bas, REG_LCR, lcr); uart_barrier(bas); break; case UART_IOCTL_OFLOW: lcr = uart_getreg(bas, REG_LCR); uart_barrier(bas); uart_setreg(bas, REG_LCR, 0xbf); uart_barrier(bas); efr = uart_getreg(bas, REG_EFR); if (data) efr |= EFR_CTS; else efr &= ~EFR_CTS; uart_setreg(bas, REG_EFR, efr); uart_barrier(bas); uart_setreg(bas, REG_LCR, lcr); uart_barrier(bas); break; case UART_IOCTL_BAUD: lcr = uart_getreg(bas, REG_LCR); uart_setreg(bas, REG_LCR, lcr | LCR_DLAB); uart_barrier(bas); divisor = uart_getreg(bas, REG_DLL) | (uart_getreg(bas, REG_DLH) << 8); uart_barrier(bas); uart_setreg(bas, REG_LCR, lcr); uart_barrier(bas); baudrate = (divisor > 0) ? bas->rclk / divisor / 16 : 0; if (baudrate > 0) *(int*)data = baudrate; else error = ENXIO; break; default: error = EINVAL; break; } uart_unlock(sc->sc_hwmtx); return (error); } static int lpc_ns8250_bus_ipend(struct uart_softc *sc) { struct uart_bas *bas; struct lpc_ns8250_softc *lpc_ns8250; int ipend; uint8_t iir, lsr; lpc_ns8250 = (struct lpc_ns8250_softc *)sc; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); iir = uart_getreg(bas, REG_IIR); if (iir & IIR_NOPEND) { uart_unlock(sc->sc_hwmtx); return (0); } ipend = 0; if (iir & IIR_RXRDY) { lsr = uart_getreg(bas, REG_LSR); if (lsr & LSR_OE) ipend |= SER_INT_OVERRUN; if (lsr & LSR_BI) ipend |= SER_INT_BREAK; if (lsr & LSR_RXRDY) ipend |= SER_INT_RXREADY; } else { if (iir & IIR_TXRDY) { ipend |= SER_INT_TXIDLE; uart_setreg(bas, REG_IER, lpc_ns8250->ier); } else ipend |= SER_INT_SIGCHG; } if (ipend == 0) lpc_ns8250_clrint(bas); uart_unlock(sc->sc_hwmtx); return (ipend); } static int lpc_ns8250_bus_param(struct uart_softc *sc, int baudrate, int databits, int stopbits, int parity) { struct uart_bas *bas; int error; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); error = lpc_ns8250_param(bas, baudrate, databits, stopbits, parity); uart_unlock(sc->sc_hwmtx); return (error); } static int lpc_ns8250_bus_probe(struct uart_softc *sc) { struct lpc_ns8250_softc *lpc_ns8250; struct uart_bas *bas; int count, delay, error, limit; uint8_t lsr, mcr, ier; lpc_ns8250 = (struct lpc_ns8250_softc *)sc; bas = &sc->sc_bas; error = lpc_ns8250_probe(bas); if (error) return (error); mcr = MCR_IE; if (sc->sc_sysdev == NULL) { /* By using lpc_ns8250_init() we also set DTR and RTS. */ lpc_ns8250_init(bas, 115200, 8, 1, UART_PARITY_NONE); } else mcr |= MCR_DTR | MCR_RTS; error = lpc_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. */ uart_setreg(bas, REG_MCR, MCR_LOOPBACK | MCR_IE | MCR_DTR | MCR_RTS); uart_barrier(bas); /* * Enable FIFOs. And check that the UART has them. If not, we're * done. Since this is the first time we enable the FIFOs, we reset * them. */ uart_setreg(bas, REG_FCR, FCR_ENABLE); uart_barrier(bas); if (!(uart_getreg(bas, REG_IIR) & IIR_FIFO_MASK)) { /* * 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); sc->sc_rxfifosz = sc->sc_txfifosz = 1; device_set_desc(sc->sc_dev, "8250 or 16450 or compatible"); return (0); } uart_setreg(bas, REG_FCR, FCR_ENABLE | FCR_XMT_RST | FCR_RCV_RST); uart_barrier(bas); count = 0; delay = lpc_ns8250_delay(bas); /* We have FIFOs. Drain the transmitter and receiver. */ error = lpc_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 done; } /* * 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 overflow bit in the LSR register to be * asserted, counting the characters as we send them. Based on * that count we know the FIFO size. */ do { uart_setreg(bas, REG_DATA, 0); uart_barrier(bas); count++; limit = 30; lsr = 0; /* * LSR bits are cleared upon read, so we must accumulate * them to be able to test LSR_OE below. */ while (((lsr |= uart_getreg(bas, REG_LSR)) & LSR_TEMT) == 0 && --limit) DELAY(delay); if (limit == 0) { ier = uart_getreg(bas, REG_IER) & lpc_ns8250->ier_mask; uart_setreg(bas, REG_IER, ier); uart_setreg(bas, REG_MCR, mcr); uart_setreg(bas, REG_FCR, 0); uart_barrier(bas); count = 0; goto done; } } while ((lsr & LSR_OE) == 0 && count < 130); count--; uart_setreg(bas, REG_MCR, mcr); /* Reset FIFOs. */ lpc_ns8250_flush(bas, UART_FLUSH_RECEIVER|UART_FLUSH_TRANSMITTER); done: sc->sc_rxfifosz = 64; device_set_desc(sc->sc_dev, "LPC32x0 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; #if 0 /* * XXX there are some issues related to hardware flow control and * it's likely that uart(4) is the cause. This basicly needs more * investigation, but we avoid using for hardware flow control * until then. */ /* 16650s or higher have automatic flow control. */ if (sc->sc_rxfifosz > 16) { sc->sc_hwiflow = 1; sc->sc_hwoflow = 1; } #endif return (0); } static int lpc_ns8250_bus_receive(struct uart_softc *sc) { struct uart_bas *bas; int xc; uint8_t lsr; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); lsr = uart_getreg(bas, REG_LSR); while (lsr & LSR_RXRDY) { if (uart_rx_full(sc)) { sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN; 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); lsr = uart_getreg(bas, REG_LSR); } /* Discard everything left in the Rx FIFO. */ while (lsr & LSR_RXRDY) { (void)uart_getreg(bas, REG_DATA); uart_barrier(bas); lsr = uart_getreg(bas, REG_LSR); } uart_unlock(sc->sc_hwmtx); return (0); } static int lpc_ns8250_bus_setsig(struct uart_softc *sc, int sig) { struct lpc_ns8250_softc *lpc_ns8250 = (struct lpc_ns8250_softc*)sc; struct uart_bas *bas; uint32_t new, old; bas = &sc->sc_bas; do { old = sc->sc_hwsig; new = old; if (sig & SER_DDTR) { SIGCHG(sig & SER_DTR, new, SER_DTR, SER_DDTR); } if (sig & SER_DRTS) { SIGCHG(sig & SER_RTS, new, SER_RTS, SER_DRTS); } } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new)); uart_lock(sc->sc_hwmtx); lpc_ns8250->mcr &= ~(MCR_DTR|MCR_RTS); if (new & SER_DTR) lpc_ns8250->mcr |= MCR_DTR; if (new & SER_RTS) lpc_ns8250->mcr |= MCR_RTS; uart_setreg(bas, REG_MCR, lpc_ns8250->mcr); uart_barrier(bas); uart_unlock(sc->sc_hwmtx); return (0); } static int lpc_ns8250_bus_transmit(struct uart_softc *sc) { struct lpc_ns8250_softc *lpc_ns8250 = (struct lpc_ns8250_softc*)sc; struct uart_bas *bas; int i; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); while ((uart_getreg(bas, REG_LSR) & LSR_THRE) == 0) ; uart_setreg(bas, REG_IER, lpc_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; uart_unlock(sc->sc_hwmtx); return (0); } void lpc_ns8250_bus_grab(struct uart_softc *sc) { struct uart_bas *bas = &sc->sc_bas; /* * turn off all interrupts to enter polling mode. Leave the * saved mask alone. We'll restore whatever it was in ungrab. * All pending interupt signals are reset when IER is set to 0. */ uart_lock(sc->sc_hwmtx); uart_setreg(bas, REG_IER, 0); uart_barrier(bas); uart_unlock(sc->sc_hwmtx); } void lpc_ns8250_bus_ungrab(struct uart_softc *sc) { struct lpc_ns8250_softc *lpc_ns8250 = (struct lpc_ns8250_softc*)sc; struct uart_bas *bas = &sc->sc_bas; /* * Restore previous interrupt mask */ uart_lock(sc->sc_hwmtx); uart_setreg(bas, REG_IER, lpc_ns8250->ier); uart_barrier(bas); uart_unlock(sc->sc_hwmtx); } Index: head/sys/dev/uart/uart_dev_msm.c =================================================================== --- head/sys/dev/uart/uart_dev_msm.c (revision 281437) +++ head/sys/dev/uart/uart_dev_msm.c (revision 281438) @@ -1,575 +1,576 @@ /*- * Copyright (c) 2014 Ganbold Tsagaankhuu * 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. */ /* Qualcomm MSM7K/8K uart driver */ #include __FBSDID("$FreeBSD$"); #include "opt_ddb.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "uart_if.h" #define DEF_CLK 7372800 #define GETREG(bas, reg) \ bus_space_read_4((bas)->bst, (bas)->bsh, (reg)) #define SETREG(bas, reg, value) \ bus_space_write_4((bas)->bst, (bas)->bsh, (reg), (value)) static int msm_uart_param(struct uart_bas *, int, int, int, int); /* * Low-level UART interface. */ static int msm_probe(struct uart_bas *bas); static void msm_init(struct uart_bas *bas, int, int, int, int); static void msm_term(struct uart_bas *bas); static void msm_putc(struct uart_bas *bas, int); static int msm_rxready(struct uart_bas *bas); static int msm_getc(struct uart_bas *bas, struct mtx *mtx); extern SLIST_HEAD(uart_devinfo_list, uart_devinfo) uart_sysdevs; static int msm_uart_param(struct uart_bas *bas, int baudrate, int databits, int stopbits, int parity) { int ulcon; ulcon = 0; switch (databits) { case 5: ulcon |= (UART_DM_5_BPS << 4); break; case 6: ulcon |= (UART_DM_6_BPS << 4); break; case 7: ulcon |= (UART_DM_7_BPS << 4); break; case 8: ulcon |= (UART_DM_8_BPS << 4); break; default: return (EINVAL); } switch (parity) { case UART_PARITY_NONE: ulcon |= UART_DM_NO_PARITY; break; case UART_PARITY_ODD: ulcon |= UART_DM_ODD_PARITY; break; case UART_PARITY_EVEN: ulcon |= UART_DM_EVEN_PARITY; break; case UART_PARITY_SPACE: ulcon |= UART_DM_SPACE_PARITY; break; case UART_PARITY_MARK: default: return (EINVAL); } switch (stopbits) { case 1: ulcon |= (UART_DM_SBL_1 << 2); break; case 2: ulcon |= (UART_DM_SBL_2 << 2); break; default: return (EINVAL); } uart_setreg(bas, UART_DM_MR2, ulcon); /* Set 115200 for both TX and RX. */; uart_setreg(bas, UART_DM_CSR, UART_DM_CSR_115200); uart_barrier(bas); return (0); } struct uart_ops uart_msm_ops = { .probe = msm_probe, .init = msm_init, .term = msm_term, .putc = msm_putc, .rxready = msm_rxready, .getc = msm_getc, }; static int msm_probe(struct uart_bas *bas) { return (0); } static void msm_init(struct uart_bas *bas, int baudrate, int databits, int stopbits, int parity) { if (bas->rclk == 0) bas->rclk = DEF_CLK; KASSERT(bas->rclk != 0, ("msm_init: Invalid rclk")); /* Set default parameters */ msm_uart_param(bas, baudrate, databits, stopbits, parity); /* * Configure UART mode registers MR1 and MR2. * Hardware flow control isn't supported. */ uart_setreg(bas, UART_DM_MR1, 0x0); /* Reset interrupt mask register. */ uart_setreg(bas, UART_DM_IMR, 0); /* * Configure Tx and Rx watermarks configuration registers. * TX watermark value is set to 0 - interrupt is generated when * FIFO level is less than or equal to 0. */ uart_setreg(bas, UART_DM_TFWR, UART_DM_TFW_VALUE); /* Set RX watermark value */ uart_setreg(bas, UART_DM_RFWR, UART_DM_RFW_VALUE); /* * Configure Interrupt Programming Register. * Set initial Stale timeout value. */ uart_setreg(bas, UART_DM_IPR, UART_DM_STALE_TIMEOUT_LSB); /* Disable IRDA mode */ uart_setreg(bas, UART_DM_IRDA, 0x0); /* * Configure and enable sim interface if required. * Configure hunt character value in HCR register. * Keep it in reset state. */ uart_setreg(bas, UART_DM_HCR, 0x0); /* Issue soft reset command */ SETREG(bas, UART_DM_CR, UART_DM_RESET_TX); SETREG(bas, UART_DM_CR, UART_DM_RESET_RX); SETREG(bas, UART_DM_CR, UART_DM_RESET_ERROR_STATUS); SETREG(bas, UART_DM_CR, UART_DM_RESET_BREAK_INT); SETREG(bas, UART_DM_CR, UART_DM_RESET_STALE_INT); /* Enable/Disable Rx/Tx DM interfaces */ /* Disable Data Mover for now. */ uart_setreg(bas, UART_DM_DMEN, 0x0); /* Enable transmitter and receiver */ uart_setreg(bas, UART_DM_CR, UART_DM_CR_RX_ENABLE); uart_setreg(bas, UART_DM_CR, UART_DM_CR_TX_ENABLE); uart_barrier(bas); } static void msm_term(struct uart_bas *bas) { /* XXX */ } static void msm_putc(struct uart_bas *bas, int c) { int limit; /* * Write to NO_CHARS_FOR_TX register the number of characters * to be transmitted. However, before writing TX_FIFO must * be empty as indicated by TX_READY interrupt in IMR register */ /* * Check if transmit FIFO is empty. * If not wait for TX_READY interrupt. */ limit = 1000; if (!(uart_getreg(bas, UART_DM_SR) & UART_DM_SR_TXEMT)) { while ((uart_getreg(bas, UART_DM_ISR) & UART_DM_TX_READY) == 0 && --limit) DELAY(4); } /* FIFO is ready, write number of characters to be written */ uart_setreg(bas, UART_DM_NO_CHARS_FOR_TX, 1); /* Wait till TX FIFO has space */ while ((uart_getreg(bas, UART_DM_SR) & UART_DM_SR_TXRDY) == 0) DELAY(4); /* TX FIFO has space. Write char */ SETREG(bas, UART_DM_TF(0), (c & 0xff)); } static int msm_rxready(struct uart_bas *bas) { /* Wait for a character to come ready */ return ((uart_getreg(bas, UART_DM_SR) & UART_DM_SR_RXRDY) == UART_DM_SR_RXRDY); } static int msm_getc(struct uart_bas *bas, struct mtx *mtx) { int c; uart_lock(mtx); /* Wait for a character to come ready */ while ((uart_getreg(bas, UART_DM_SR) & UART_DM_SR_RXRDY) != UART_DM_SR_RXRDY) DELAY(4); /* Check for Overrun error. If so reset Error Status */ if (uart_getreg(bas, UART_DM_SR) & UART_DM_SR_UART_OVERRUN) uart_setreg(bas, UART_DM_CR, UART_DM_RESET_ERROR_STATUS); /* Read char */ c = uart_getreg(bas, UART_DM_RF(0)); uart_unlock(mtx); return (c); } /* * High-level UART interface. */ struct msm_uart_softc { struct uart_softc base; uint32_t ier; }; static int msm_bus_probe(struct uart_softc *sc); static int msm_bus_attach(struct uart_softc *sc); static int msm_bus_flush(struct uart_softc *, int); static int msm_bus_getsig(struct uart_softc *); static int msm_bus_ioctl(struct uart_softc *, int, intptr_t); static int msm_bus_ipend(struct uart_softc *); static int msm_bus_param(struct uart_softc *, int, int, int, int); static int msm_bus_receive(struct uart_softc *); static int msm_bus_setsig(struct uart_softc *, int); static int msm_bus_transmit(struct uart_softc *); static void msm_bus_grab(struct uart_softc *); static void msm_bus_ungrab(struct uart_softc *); static kobj_method_t msm_methods[] = { KOBJMETHOD(uart_probe, msm_bus_probe), KOBJMETHOD(uart_attach, msm_bus_attach), KOBJMETHOD(uart_flush, msm_bus_flush), KOBJMETHOD(uart_getsig, msm_bus_getsig), KOBJMETHOD(uart_ioctl, msm_bus_ioctl), KOBJMETHOD(uart_ipend, msm_bus_ipend), KOBJMETHOD(uart_param, msm_bus_param), KOBJMETHOD(uart_receive, msm_bus_receive), KOBJMETHOD(uart_setsig, msm_bus_setsig), KOBJMETHOD(uart_transmit, msm_bus_transmit), KOBJMETHOD(uart_grab, msm_bus_grab), KOBJMETHOD(uart_ungrab, msm_bus_ungrab), {0, 0 } }; int msm_bus_probe(struct uart_softc *sc) { sc->sc_txfifosz = 64; sc->sc_rxfifosz = 64; device_set_desc(sc->sc_dev, "Qualcomm HSUART"); return (0); } static int msm_bus_attach(struct uart_softc *sc) { struct msm_uart_softc *u = (struct msm_uart_softc *)sc; struct uart_bas *bas = &sc->sc_bas; sc->sc_hwiflow = 0; sc->sc_hwoflow = 0; /* Set TX_READY, TXLEV, RXLEV, RXSTALE */ u->ier = UART_DM_IMR_ENABLED; /* Configure Interrupt Mask register IMR */ uart_setreg(bas, UART_DM_IMR, u->ier); return (0); } /* * Write the current transmit buffer to the TX FIFO. */ static int msm_bus_transmit(struct uart_softc *sc) { struct msm_uart_softc *u = (struct msm_uart_softc *)sc; struct uart_bas *bas = &sc->sc_bas; int i; uart_lock(sc->sc_hwmtx); /* Write some data */ for (i = 0; i < sc->sc_txdatasz; i++) { /* Write TX data */ msm_putc(bas, sc->sc_txbuf[i]); uart_barrier(bas); } /* TX FIFO is empty now, enable TX_READY interrupt */ u->ier |= UART_DM_TX_READY; SETREG(bas, UART_DM_IMR, u->ier); uart_barrier(bas); /* * Inform upper layer that it is transmitting data to hardware, * this will be cleared when TXIDLE interrupt occurs. */ sc->sc_txbusy = 1; uart_unlock(sc->sc_hwmtx); return (0); } static int msm_bus_setsig(struct uart_softc *sc, int sig) { return (0); } static int msm_bus_receive(struct uart_softc *sc) { struct msm_uart_softc *u = (struct msm_uart_softc *)sc; struct uart_bas *bas; int c; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); /* Initialize Receive Path and interrupt */ SETREG(bas, UART_DM_CR, UART_DM_RESET_STALE_INT); SETREG(bas, UART_DM_CR, UART_DM_STALE_EVENT_ENABLE); u->ier |= UART_DM_RXLEV; SETREG(bas, UART_DM_IMR, u->ier); /* Loop over until we are full, or no data is available */ while (uart_getreg(bas, UART_DM_SR) & UART_DM_SR_RXRDY) { if (uart_rx_full(sc)) { /* No space left in input buffer */ sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN; break; } /* Read RX FIFO */ c = uart_getreg(bas, UART_DM_RF(0)); uart_barrier(bas); uart_rx_put(sc, c); } uart_unlock(sc->sc_hwmtx); return (0); } static int msm_bus_param(struct uart_softc *sc, int baudrate, int databits, int stopbits, int parity) { int error; if (sc->sc_bas.rclk == 0) sc->sc_bas.rclk = DEF_CLK; KASSERT(sc->sc_bas.rclk != 0, ("msm_init: Invalid rclk")); uart_lock(sc->sc_hwmtx); error = msm_uart_param(&sc->sc_bas, baudrate, databits, stopbits, parity); uart_unlock(sc->sc_hwmtx); return (error); } static int msm_bus_ipend(struct uart_softc *sc) { struct msm_uart_softc *u = (struct msm_uart_softc *)sc; struct uart_bas *bas = &sc->sc_bas; uint32_t isr; int ipend; uart_lock(sc->sc_hwmtx); /* Get ISR status */ isr = GETREG(bas, UART_DM_MISR); ipend = 0; /* Uart RX starting, notify upper layer */ if (isr & UART_DM_RXLEV) { u->ier &= ~UART_DM_RXLEV; SETREG(bas, UART_DM_IMR, u->ier); uart_barrier(bas); ipend |= SER_INT_RXREADY; } /* Stale RX interrupt */ if (isr & UART_DM_RXSTALE) { /* Disable and reset it */ SETREG(bas, UART_DM_CR, UART_DM_STALE_EVENT_DISABLE); SETREG(bas, UART_DM_CR, UART_DM_RESET_STALE_INT); uart_barrier(bas); ipend |= SER_INT_RXREADY; } /* TX READY interrupt */ if (isr & UART_DM_TX_READY) { /* Clear TX Ready */ SETREG(bas, UART_DM_CR, UART_DM_CLEAR_TX_READY); /* Disable TX_READY */ u->ier &= ~UART_DM_TX_READY; SETREG(bas, UART_DM_IMR, u->ier); uart_barrier(bas); if (sc->sc_txbusy != 0) ipend |= SER_INT_TXIDLE; } if (isr & UART_DM_TXLEV) { /* TX FIFO is empty */ u->ier &= ~UART_DM_TXLEV; SETREG(bas, UART_DM_IMR, u->ier); uart_barrier(bas); if (sc->sc_txbusy != 0) ipend |= SER_INT_TXIDLE; } uart_unlock(sc->sc_hwmtx); return (ipend); } static int msm_bus_flush(struct uart_softc *sc, int what) { return (0); } static int msm_bus_getsig(struct uart_softc *sc) { return (0); } static int msm_bus_ioctl(struct uart_softc *sc, int request, intptr_t data) { return (EINVAL); } static void msm_bus_grab(struct uart_softc *sc) { struct uart_bas *bas = &sc->sc_bas; /* * XXX: Turn off all interrupts to enter polling mode. Leave the * saved mask alone. We'll restore whatever it was in ungrab. */ uart_lock(sc->sc_hwmtx); SETREG(bas, UART_DM_CR, UART_DM_RESET_STALE_INT); SETREG(bas, UART_DM_IMR, 0); uart_barrier(bas); uart_unlock(sc->sc_hwmtx); } static void msm_bus_ungrab(struct uart_softc *sc) { struct msm_uart_softc *u = (struct msm_uart_softc *)sc; struct uart_bas *bas = &sc->sc_bas; /* * Restore previous interrupt mask */ uart_lock(sc->sc_hwmtx); SETREG(bas, UART_DM_IMR, u->ier); uart_barrier(bas); uart_unlock(sc->sc_hwmtx); } static struct uart_class uart_msm_class = { "msm", msm_methods, sizeof(struct msm_uart_softc), .uc_ops = &uart_msm_ops, .uc_range = 8, .uc_rclk = DEF_CLK, + .uc_rshift = 0 }; static struct ofw_compat_data compat_data[] = { {"qcom,msm-uartdm", (uintptr_t)&uart_msm_class}, {NULL, (uintptr_t)NULL}, }; UART_FDT_CLASS_AND_DEVICE(compat_data); Index: head/sys/dev/uart/uart_dev_ns8250.c =================================================================== --- head/sys/dev/uart/uart_dev_ns8250.c (revision 281437) +++ head/sys/dev/uart/uart_dev_ns8250.c (revision 281438) @@ -1,970 +1,971 @@ /*- * 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 "opt_platform.h" #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #ifdef FDT #include #include #include #endif #include #include #ifdef FDT #include #endif #include #include #include #include "uart_if.h" #define DEFAULT_RCLK 1843200 static int broken_txfifo = 0; SYSCTL_INT(_hw, OID_AUTO, broken_txfifo, CTLFLAG_RWTUN, &broken_txfifo, 0, "UART FIFO has QEMU emulation bug"); /* * 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, lsr; iir = uart_getreg(bas, REG_IIR); while ((iir & IIR_NOPEND) == 0) { iir &= IIR_IMASK; if (iir == IIR_RLS) { lsr = uart_getreg(bas, REG_LSR); if (lsr & (LSR_BI|LSR_FE|LSR_PE)) (void)uart_getreg(bas, REG_DATA); } 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_getreg(bas, REG_DLL) | (uart_getreg(bas, REG_DLH) << 8); uart_barrier(bas); uart_setreg(bas, REG_LCR, lcr); uart_barrier(bas); /* 1/10th the time to transmit 1 character (estimate). */ if (divisor <= 134) return (16000000 * divisor / bas->rclk); return (16000 * divisor / (bas->rclk / 1000)); } 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) { divisor = ns8250_divisor(bas->rclk, baudrate); if (divisor == 0) return (EINVAL); uart_setreg(bas, REG_LCR, lcr | LCR_DLAB); uart_barrier(bas); uart_setreg(bas, REG_DLL, divisor & 0xff); uart_setreg(bas, REG_DLH, (divisor >> 8) & 0xff); 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_rxready(struct uart_bas *bas); static int ns8250_getc(struct uart_bas *bas, struct mtx *); struct uart_ops uart_ns8250_ops = { .probe = ns8250_probe, .init = ns8250_init, .term = ns8250_term, .putc = ns8250_putc, .rxready = ns8250_rxready, .getc = ns8250_getc, }; static int ns8250_probe(struct uart_bas *bas) { u_char val; /* Check known 0 bits that don't depend on DLAB. */ val = uart_getreg(bas, REG_IIR); if (val & 0x30) return (ENXIO); /* * Bit 6 of the MCR (= 0x40) appears to be 1 for the Sun1699 * chip, but otherwise doesn't seem to have a function. In * other words, uart(4) works regardless. Ignore that bit so * the probe succeeds. */ val = uart_getreg(bas, REG_MCR); if (val & 0xa0) return (ENXIO); return (0); } static void ns8250_init(struct uart_bas *bas, int baudrate, int databits, int stopbits, int parity) { u_char ier; if (bas->rclk == 0) bas->rclk = DEFAULT_RCLK; ns8250_param(bas, baudrate, databits, stopbits, parity); /* Disable all interrupt sources. */ /* * We use 0xe0 instead of 0xf0 as the mask because the XScale PXA * UARTs split the receive time-out interrupt bit out separately as * 0x10. This gets handled by ier_mask and ier_rxbits below. */ ier = uart_getreg(bas, REG_IER) & 0xe0; uart_setreg(bas, REG_IER, ier); 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 limit; limit = 250000; while ((uart_getreg(bas, REG_LSR) & LSR_THRE) == 0 && --limit) DELAY(4); uart_setreg(bas, REG_DATA, c); uart_barrier(bas); limit = 250000; while ((uart_getreg(bas, REG_LSR) & LSR_TEMT) == 0 && --limit) DELAY(4); } static int ns8250_rxready(struct uart_bas *bas) { return ((uart_getreg(bas, REG_LSR) & LSR_RXRDY) != 0 ? 1 : 0); } static int ns8250_getc(struct uart_bas *bas, struct mtx *hwmtx) { int c; uart_lock(hwmtx); while ((uart_getreg(bas, REG_LSR) & LSR_RXRDY) == 0) { uart_unlock(hwmtx); DELAY(4); uart_lock(hwmtx); } c = uart_getreg(bas, REG_DATA); uart_unlock(hwmtx); return (c); } 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), KOBJMETHOD(uart_grab, ns8250_bus_grab), KOBJMETHOD(uart_ungrab, ns8250_bus_ungrab), { 0, 0 } }; struct uart_class uart_ns8250_class = { "ns8250", ns8250_methods, sizeof(struct ns8250_softc), .uc_ops = &uart_ns8250_ops, .uc_range = 8, - .uc_rclk = DEFAULT_RCLK + .uc_rclk = DEFAULT_RCLK, + .uc_rshift = 0 }; #ifdef FDT static struct ofw_compat_data compat_data[] = { {"ns16550", (uintptr_t)&uart_ns8250_class}, {NULL, (uintptr_t)NULL}, }; UART_FDT_CLASS_AND_DEVICE(compat_data); #endif #define SIGCHG(c, i, s, d) \ if (c) { \ i |= (i & s) ? s : s | d; \ } else { \ i = (i & s) ? (i & ~s) | d : i; \ } int ns8250_bus_attach(struct uart_softc *sc) { struct ns8250_softc *ns8250 = (struct ns8250_softc*)sc; struct uart_bas *bas; unsigned int ivar; #ifdef FDT phandle_t node; pcell_t cell; #endif ns8250->busy_detect = 0; #ifdef FDT /* * Check whether uart requires to read USR reg when IIR_BUSY and * has broken txfifo. */ node = ofw_bus_get_node(sc->sc_dev); if ((OF_getprop(node, "busy-detect", &cell, sizeof(cell))) > 0) ns8250->busy_detect = 1; if ((OF_getprop(node, "broken-txfifo", &cell, sizeof(cell))) > 0) broken_txfifo = 1; #endif bas = &sc->sc_bas; ns8250->mcr = uart_getreg(bas, REG_MCR); ns8250->fcr = FCR_ENABLE; if (!resource_int_value("uart", device_get_unit(sc->sc_dev), "flags", &ivar)) { if (UART_FLAGS_FCR_RX_LOW(ivar)) ns8250->fcr |= FCR_RX_LOW; else if (UART_FLAGS_FCR_RX_MEDL(ivar)) ns8250->fcr |= FCR_RX_MEDL; else if (UART_FLAGS_FCR_RX_HIGH(ivar)) ns8250->fcr |= FCR_RX_HIGH; else ns8250->fcr |= FCR_RX_MEDH; } else ns8250->fcr |= FCR_RX_MEDH; /* Get IER mask */ ivar = 0xf0; resource_int_value("uart", device_get_unit(sc->sc_dev), "ier_mask", &ivar); ns8250->ier_mask = (uint8_t)(ivar & 0xff); /* Get IER RX interrupt bits */ ivar = IER_EMSC | IER_ERLS | IER_ERXRDY; resource_int_value("uart", device_get_unit(sc->sc_dev), "ier_rxbits", &ivar); ns8250->ier_rxbits = (uint8_t)(ivar & 0xff); 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 |= SER_DTR; if (ns8250->mcr & MCR_RTS) sc->sc_hwsig |= SER_RTS; ns8250_bus_getsig(sc); ns8250_clrint(bas); ns8250->ier = uart_getreg(bas, REG_IER) & ns8250->ier_mask; ns8250->ier |= ns8250->ier_rxbits; uart_setreg(bas, REG_IER, ns8250->ier); uart_barrier(bas); /* * Timing of the H/W access was changed with r253161 of uart_core.c * It has been observed that an ITE IT8513E would signal a break * condition with pretty much every character it received, unless * it had enough time to settle between ns8250_bus_attach() and * ns8250_bus_ipend() -- which it accidentally had before r253161. * It's not understood why the UART chip behaves this way and it * could very well be that the DELAY make the H/W work in the same * accidental manner as before. More analysis is warranted, but * at least now we fixed a known regression. */ DELAY(200); return (0); } int ns8250_bus_detach(struct uart_softc *sc) { struct ns8250_softc *ns8250; struct uart_bas *bas; u_char ier; ns8250 = (struct ns8250_softc *)sc; bas = &sc->sc_bas; ier = uart_getreg(bas, REG_IER) & ns8250->ier_mask; uart_setreg(bas, REG_IER, ier); uart_barrier(bas); ns8250_clrint(bas); return (0); } int ns8250_bus_flush(struct uart_softc *sc, int what) { struct ns8250_softc *ns8250 = (struct ns8250_softc*)sc; struct uart_bas *bas; int error; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); if (sc->sc_rxfifosz > 1) { ns8250_flush(bas, what); uart_setreg(bas, REG_FCR, ns8250->fcr); uart_barrier(bas); error = 0; } else error = ns8250_drain(bas, what); uart_unlock(sc->sc_hwmtx); return (error); } int ns8250_bus_getsig(struct uart_softc *sc) { uint32_t new, old, sig; uint8_t msr; do { old = sc->sc_hwsig; sig = old; uart_lock(sc->sc_hwmtx); msr = uart_getreg(&sc->sc_bas, REG_MSR); uart_unlock(sc->sc_hwmtx); SIGCHG(msr & MSR_DSR, sig, SER_DSR, SER_DDSR); SIGCHG(msr & MSR_CTS, sig, SER_CTS, SER_DCTS); SIGCHG(msr & MSR_DCD, sig, SER_DCD, SER_DDCD); SIGCHG(msr & MSR_RI, sig, SER_RI, SER_DRI); new = sig & ~SER_MASK_DELTA; } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new)); return (sig); } int ns8250_bus_ioctl(struct uart_softc *sc, int request, intptr_t data) { struct uart_bas *bas; int baudrate, divisor, error; uint8_t efr, lcr; bas = &sc->sc_bas; error = 0; uart_lock(sc->sc_hwmtx); 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; case UART_IOCTL_IFLOW: lcr = uart_getreg(bas, REG_LCR); uart_barrier(bas); uart_setreg(bas, REG_LCR, 0xbf); uart_barrier(bas); efr = uart_getreg(bas, REG_EFR); if (data) efr |= EFR_RTS; else efr &= ~EFR_RTS; uart_setreg(bas, REG_EFR, efr); uart_barrier(bas); uart_setreg(bas, REG_LCR, lcr); uart_barrier(bas); break; case UART_IOCTL_OFLOW: lcr = uart_getreg(bas, REG_LCR); uart_barrier(bas); uart_setreg(bas, REG_LCR, 0xbf); uart_barrier(bas); efr = uart_getreg(bas, REG_EFR); if (data) efr |= EFR_CTS; else efr &= ~EFR_CTS; uart_setreg(bas, REG_EFR, efr); uart_barrier(bas); uart_setreg(bas, REG_LCR, lcr); uart_barrier(bas); break; case UART_IOCTL_BAUD: lcr = uart_getreg(bas, REG_LCR); uart_setreg(bas, REG_LCR, lcr | LCR_DLAB); uart_barrier(bas); divisor = uart_getreg(bas, REG_DLL) | (uart_getreg(bas, REG_DLH) << 8); uart_barrier(bas); uart_setreg(bas, REG_LCR, lcr); uart_barrier(bas); baudrate = (divisor > 0) ? bas->rclk / divisor / 16 : 0; if (baudrate > 0) *(int*)data = baudrate; else error = ENXIO; break; default: error = EINVAL; break; } uart_unlock(sc->sc_hwmtx); return (error); } int ns8250_bus_ipend(struct uart_softc *sc) { struct uart_bas *bas; struct ns8250_softc *ns8250; int ipend; uint8_t iir, lsr; ns8250 = (struct ns8250_softc *)sc; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); iir = uart_getreg(bas, REG_IIR); if (ns8250->busy_detect && (iir & IIR_BUSY) == IIR_BUSY) { (void)uart_getreg(bas, DW_REG_USR); uart_unlock(sc->sc_hwmtx); return (0); } if (iir & IIR_NOPEND) { uart_unlock(sc->sc_hwmtx); return (0); } ipend = 0; if (iir & IIR_RXRDY) { lsr = uart_getreg(bas, REG_LSR); if (lsr & LSR_OE) ipend |= SER_INT_OVERRUN; if (lsr & LSR_BI) ipend |= SER_INT_BREAK; if (lsr & LSR_RXRDY) ipend |= SER_INT_RXREADY; } else { if (iir & IIR_TXRDY) { ipend |= SER_INT_TXIDLE; uart_setreg(bas, REG_IER, ns8250->ier); } else ipend |= SER_INT_SIGCHG; } if (ipend == 0) ns8250_clrint(bas); uart_unlock(sc->sc_hwmtx); return (ipend); } int ns8250_bus_param(struct uart_softc *sc, int baudrate, int databits, int stopbits, int parity) { struct ns8250_softc *ns8250; struct uart_bas *bas; int error, limit; ns8250 = (struct ns8250_softc*)sc; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); /* * When using DW UART with BUSY detection it is necessary to wait * until all serial transfers are finished before manipulating the * line control. LCR will not be affected when UART is busy. */ if (ns8250->busy_detect != 0) { /* * Pick an arbitrary high limit to avoid getting stuck in * an infinite loop in case when the hardware is broken. */ limit = 10 * 1024; while (((uart_getreg(bas, DW_REG_USR) & USR_BUSY) != 0) && --limit) DELAY(4); if (limit <= 0) { /* UART appears to be stuck */ uart_unlock(sc->sc_hwmtx); return (EIO); } } error = ns8250_param(bas, baudrate, databits, stopbits, parity); uart_unlock(sc->sc_hwmtx); return (error); } int ns8250_bus_probe(struct uart_softc *sc) { struct ns8250_softc *ns8250; struct uart_bas *bas; int count, delay, error, limit; uint8_t lsr, mcr, ier; ns8250 = (struct ns8250_softc *)sc; 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, 115200, 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. */ uart_setreg(bas, REG_MCR, MCR_LOOPBACK | MCR_IE | MCR_DTR | MCR_RTS); uart_barrier(bas); /* * Enable FIFOs. And check that the UART has them. If not, we're * done. Since this is the first time we enable the FIFOs, we reset * them. */ uart_setreg(bas, REG_FCR, FCR_ENABLE); uart_barrier(bas); if (!(uart_getreg(bas, REG_IIR) & IIR_FIFO_MASK)) { /* * 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); sc->sc_rxfifosz = sc->sc_txfifosz = 1; device_set_desc(sc->sc_dev, "8250 or 16450 or compatible"); return (0); } uart_setreg(bas, REG_FCR, FCR_ENABLE | 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; } /* * 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 overflow bit in the LSR register to be * asserted, counting the characters as we send them. Based on * that count we know the FIFO size. */ do { uart_setreg(bas, REG_DATA, 0); uart_barrier(bas); count++; limit = 30; lsr = 0; /* * LSR bits are cleared upon read, so we must accumulate * them to be able to test LSR_OE below. */ while (((lsr |= uart_getreg(bas, REG_LSR)) & LSR_TEMT) == 0 && --limit) DELAY(delay); if (limit == 0) { ier = uart_getreg(bas, REG_IER) & ns8250->ier_mask; uart_setreg(bas, REG_IER, ier); uart_setreg(bas, REG_MCR, mcr); uart_setreg(bas, REG_FCR, 0); uart_barrier(bas); count = 0; goto describe; } } while ((lsr & LSR_OE) == 0 && count < 130); count--; 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 = 16; 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; #if 0 /* * XXX there are some issues related to hardware flow control and * it's likely that uart(4) is the cause. This basicly needs more * investigation, but we avoid using for hardware flow control * until then. */ /* 16650s or higher have automatic flow control. */ if (sc->sc_rxfifosz > 16) { sc->sc_hwiflow = 1; sc->sc_hwoflow = 1; } #endif return (0); } int ns8250_bus_receive(struct uart_softc *sc) { struct uart_bas *bas; int xc; uint8_t lsr; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); lsr = uart_getreg(bas, REG_LSR); while (lsr & LSR_RXRDY) { if (uart_rx_full(sc)) { sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN; 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); lsr = uart_getreg(bas, REG_LSR); } /* Discard everything left in the Rx FIFO. */ while (lsr & LSR_RXRDY) { (void)uart_getreg(bas, REG_DATA); uart_barrier(bas); lsr = uart_getreg(bas, REG_LSR); } uart_unlock(sc->sc_hwmtx); return (0); } 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 & SER_DDTR) { SIGCHG(sig & SER_DTR, new, SER_DTR, SER_DDTR); } if (sig & SER_DRTS) { SIGCHG(sig & SER_RTS, new, SER_RTS, SER_DRTS); } } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new)); uart_lock(sc->sc_hwmtx); ns8250->mcr &= ~(MCR_DTR|MCR_RTS); if (new & SER_DTR) ns8250->mcr |= MCR_DTR; if (new & SER_RTS) ns8250->mcr |= MCR_RTS; uart_setreg(bas, REG_MCR, ns8250->mcr); uart_barrier(bas); uart_unlock(sc->sc_hwmtx); return (0); } 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; uart_lock(sc->sc_hwmtx); 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); } if (broken_txfifo) ns8250_drain(bas, UART_DRAIN_TRANSMITTER); else sc->sc_txbusy = 1; uart_unlock(sc->sc_hwmtx); if (broken_txfifo) uart_sched_softih(sc, SER_INT_TXIDLE); return (0); } void ns8250_bus_grab(struct uart_softc *sc) { struct uart_bas *bas = &sc->sc_bas; struct ns8250_softc *ns8250 = (struct ns8250_softc*)sc; u_char ier; /* * turn off all interrupts to enter polling mode. Leave the * saved mask alone. We'll restore whatever it was in ungrab. * All pending interupt signals are reset when IER is set to 0. */ uart_lock(sc->sc_hwmtx); ier = uart_getreg(bas, REG_IER); uart_setreg(bas, REG_IER, ier & ns8250->ier_mask); uart_barrier(bas); uart_unlock(sc->sc_hwmtx); } void ns8250_bus_ungrab(struct uart_softc *sc) { struct ns8250_softc *ns8250 = (struct ns8250_softc*)sc; struct uart_bas *bas = &sc->sc_bas; /* * Restore previous interrupt mask */ uart_lock(sc->sc_hwmtx); uart_setreg(bas, REG_IER, ns8250->ier); uart_barrier(bas); uart_unlock(sc->sc_hwmtx); } Index: head/sys/dev/uart/uart_dev_pl011.c =================================================================== --- head/sys/dev/uart/uart_dev_pl011.c (revision 281437) +++ head/sys/dev/uart/uart_dev_pl011.c (revision 281438) @@ -1,504 +1,505 @@ /*- * Copyright (c) 2012 Semihalf. * 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 AND CONTRIBUTORS ``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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include "uart_if.h" #include /* PL011 UART registers and masks*/ #define UART_DR 0x00 /* Data register */ #define DR_FE (1 << 8) /* Framing error */ #define DR_PE (1 << 9) /* Parity error */ #define DR_BE (1 << 10) /* Break error */ #define DR_OE (1 << 11) /* Overrun error */ #define UART_FR 0x06 /* Flag register */ #define FR_TXFF (1 << 5) /* Transmit FIFO/reg full */ #define FR_RXFF (1 << 6) /* Receive FIFO/reg full */ #define FR_TXFE (1 << 7) /* Transmit FIFO/reg empty */ #define UART_IBRD 0x09 /* Integer baud rate register */ #define IBRD_BDIVINT 0xffff /* Significant part of int. divisor value */ #define UART_FBRD 0x0a /* Fractional baud rate register */ #define FBRD_BDIVFRAC 0x3f /* Significant part of frac. divisor value */ #define UART_LCR_H 0x0b /* Line control register */ #define LCR_H_WLEN8 (0x3 << 5) #define LCR_H_WLEN7 (0x2 << 5) #define LCR_H_WLEN6 (0x1 << 5) #define LCR_H_FEN (1 << 4) /* FIFO mode enable */ #define LCR_H_STP2 (1 << 3) /* 2 stop frames at the end */ #define LCR_H_EPS (1 << 2) /* Even parity select */ #define LCR_H_PEN (1 << 1) /* Parity enable */ #define UART_CR 0x0c /* Control register */ #define CR_RXE (1 << 9) /* Receive enable */ #define CR_TXE (1 << 8) /* Transmit enable */ #define CR_UARTEN (1 << 0) /* UART enable */ #define UART_IMSC 0x0e /* Interrupt mask set/clear register */ #define IMSC_MASK_ALL 0x7ff /* Mask all interrupts */ #define UART_RIS 0x0f /* Raw interrupt status register */ #define UART_RXREADY (1 << 4) /* RX buffer full */ #define UART_TXEMPTY (1 << 5) /* TX buffer empty */ #define RIS_RTIM (1 << 6) /* Receive timeout */ #define RIS_FE (1 << 7) /* Framing error interrupt status */ #define RIS_PE (1 << 8) /* Parity error interrupt status */ #define RIS_BE (1 << 9) /* Break error interrupt status */ #define RIS_OE (1 << 10) /* Overrun interrupt status */ #define UART_MIS 0x10 /* Masked interrupt status register */ #define UART_ICR 0x11 /* Interrupt clear register */ /* * FIXME: actual register size is SoC-dependent, we need to handle it */ #define __uart_getreg(bas, reg) \ bus_space_read_4((bas)->bst, (bas)->bsh, uart_regofs(bas, reg)) #define __uart_setreg(bas, reg, value) \ bus_space_write_4((bas)->bst, (bas)->bsh, uart_regofs(bas, reg), value) /* * Low-level UART interface. */ static int uart_pl011_probe(struct uart_bas *bas); static void uart_pl011_init(struct uart_bas *bas, int, int, int, int); static void uart_pl011_term(struct uart_bas *bas); static void uart_pl011_putc(struct uart_bas *bas, int); static int uart_pl011_rxready(struct uart_bas *bas); static int uart_pl011_getc(struct uart_bas *bas, struct mtx *); static struct uart_ops uart_pl011_ops = { .probe = uart_pl011_probe, .init = uart_pl011_init, .term = uart_pl011_term, .putc = uart_pl011_putc, .rxready = uart_pl011_rxready, .getc = uart_pl011_getc, }; static int uart_pl011_probe(struct uart_bas *bas) { return (0); } static void uart_pl011_param(struct uart_bas *bas, int baudrate, int databits, int stopbits, int parity) { uint32_t ctrl, line; uint32_t baud; /* * Zero all settings to make sure * UART is disabled and not configured */ ctrl = line = 0x0; __uart_setreg(bas, UART_CR, ctrl); /* As we know UART is disabled we may setup the line */ switch (databits) { case 7: line |= LCR_H_WLEN7; break; case 6: line |= LCR_H_WLEN6; break; case 8: default: line |= LCR_H_WLEN8; break; } if (stopbits == 2) line |= LCR_H_STP2; else line &= ~LCR_H_STP2; if (parity) line |= LCR_H_PEN; else line &= ~LCR_H_PEN; /* Configure the rest */ line &= ~LCR_H_FEN; ctrl |= (CR_RXE | CR_TXE | CR_UARTEN); if (bas->rclk != 0 && baudrate != 0) { baud = bas->rclk * 4 / baudrate; __uart_setreg(bas, UART_IBRD, ((uint32_t)(baud >> 6)) & IBRD_BDIVINT); __uart_setreg(bas, UART_FBRD, (uint32_t)(baud & 0x3F) & FBRD_BDIVFRAC); } /* Add config. to line before reenabling UART */ __uart_setreg(bas, UART_LCR_H, (__uart_getreg(bas, UART_LCR_H) & ~0xff) | line); __uart_setreg(bas, UART_CR, ctrl); } static void uart_pl011_init(struct uart_bas *bas, int baudrate, int databits, int stopbits, int parity) { /* Mask all interrupts */ __uart_setreg(bas, UART_IMSC, __uart_getreg(bas, UART_IMSC) & ~IMSC_MASK_ALL); uart_pl011_param(bas, baudrate, databits, stopbits, parity); } static void uart_pl011_term(struct uart_bas *bas) { } static void uart_pl011_putc(struct uart_bas *bas, int c) { /* Wait when TX FIFO full. Push character otherwise. */ while (__uart_getreg(bas, UART_FR) & FR_TXFF) ; __uart_setreg(bas, UART_DR, c & 0xff); } static int uart_pl011_rxready(struct uart_bas *bas) { return (__uart_getreg(bas, UART_FR) & FR_RXFF); } static int uart_pl011_getc(struct uart_bas *bas, struct mtx *hwmtx) { int c; while (!uart_pl011_rxready(bas)) ; c = __uart_getreg(bas, UART_DR) & 0xff; return (c); } /* * High-level UART interface. */ struct uart_pl011_softc { struct uart_softc base; uint8_t fcr; uint8_t ier; uint8_t mcr; uint8_t ier_mask; uint8_t ier_rxbits; }; static int uart_pl011_bus_attach(struct uart_softc *); static int uart_pl011_bus_detach(struct uart_softc *); static int uart_pl011_bus_flush(struct uart_softc *, int); static int uart_pl011_bus_getsig(struct uart_softc *); static int uart_pl011_bus_ioctl(struct uart_softc *, int, intptr_t); static int uart_pl011_bus_ipend(struct uart_softc *); static int uart_pl011_bus_param(struct uart_softc *, int, int, int, int); static int uart_pl011_bus_probe(struct uart_softc *); static int uart_pl011_bus_receive(struct uart_softc *); static int uart_pl011_bus_setsig(struct uart_softc *, int); static int uart_pl011_bus_transmit(struct uart_softc *); static void uart_pl011_bus_grab(struct uart_softc *); static void uart_pl011_bus_ungrab(struct uart_softc *); static kobj_method_t uart_pl011_methods[] = { KOBJMETHOD(uart_attach, uart_pl011_bus_attach), KOBJMETHOD(uart_detach, uart_pl011_bus_detach), KOBJMETHOD(uart_flush, uart_pl011_bus_flush), KOBJMETHOD(uart_getsig, uart_pl011_bus_getsig), KOBJMETHOD(uart_ioctl, uart_pl011_bus_ioctl), KOBJMETHOD(uart_ipend, uart_pl011_bus_ipend), KOBJMETHOD(uart_param, uart_pl011_bus_param), KOBJMETHOD(uart_probe, uart_pl011_bus_probe), KOBJMETHOD(uart_receive, uart_pl011_bus_receive), KOBJMETHOD(uart_setsig, uart_pl011_bus_setsig), KOBJMETHOD(uart_transmit, uart_pl011_bus_transmit), KOBJMETHOD(uart_grab, uart_pl011_bus_grab), KOBJMETHOD(uart_ungrab, uart_pl011_bus_ungrab), { 0, 0 } }; static struct uart_class uart_pl011_class = { "uart_pl011", uart_pl011_methods, sizeof(struct uart_pl011_softc), .uc_ops = &uart_pl011_ops, .uc_range = 0x48, - .uc_rclk = 0 + .uc_rclk = 0, + .uc_rshift = 2 }; static struct ofw_compat_data compat_data[] = { {"arm,pl011", (uintptr_t)&uart_pl011_class}, {NULL, (uintptr_t)NULL}, }; UART_FDT_CLASS_AND_DEVICE(compat_data); static int uart_pl011_bus_attach(struct uart_softc *sc) { struct uart_bas *bas; int reg; bas = &sc->sc_bas; /* Enable interrupts */ reg = (UART_RXREADY | RIS_RTIM | UART_TXEMPTY); __uart_setreg(bas, UART_IMSC, reg); /* Clear interrupts */ __uart_setreg(bas, UART_ICR, IMSC_MASK_ALL); return (0); } static int uart_pl011_bus_detach(struct uart_softc *sc) { return (0); } static int uart_pl011_bus_flush(struct uart_softc *sc, int what) { return (0); } static int uart_pl011_bus_getsig(struct uart_softc *sc) { return (0); } static int uart_pl011_bus_ioctl(struct uart_softc *sc, int request, intptr_t data) { struct uart_bas *bas; int error; bas = &sc->sc_bas; error = 0; uart_lock(sc->sc_hwmtx); switch (request) { case UART_IOCTL_BREAK: break; case UART_IOCTL_BAUD: *(int*)data = 115200; break; default: error = EINVAL; break; } uart_unlock(sc->sc_hwmtx); return (error); } static int uart_pl011_bus_ipend(struct uart_softc *sc) { struct uart_bas *bas; uint32_t ints; int ipend; int reg; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); ints = __uart_getreg(bas, UART_MIS); ipend = 0; if (ints & (UART_RXREADY | RIS_RTIM)) ipend |= SER_INT_RXREADY; if (ints & RIS_BE) ipend |= SER_INT_BREAK; if (ints & RIS_OE) ipend |= SER_INT_OVERRUN; if (ints & UART_TXEMPTY) { if (sc->sc_txbusy) ipend |= SER_INT_TXIDLE; /* Disable TX interrupt */ reg = __uart_getreg(bas, UART_IMSC); reg &= ~(UART_TXEMPTY); __uart_setreg(bas, UART_IMSC, reg); } uart_unlock(sc->sc_hwmtx); return (ipend); } static int uart_pl011_bus_param(struct uart_softc *sc, int baudrate, int databits, int stopbits, int parity) { uart_lock(sc->sc_hwmtx); uart_pl011_param(&sc->sc_bas, baudrate, databits, stopbits, parity); uart_unlock(sc->sc_hwmtx); return (0); } static int uart_pl011_bus_probe(struct uart_softc *sc) { device_set_desc(sc->sc_dev, "PrimeCell UART (PL011)"); sc->sc_rxfifosz = 1; sc->sc_txfifosz = 1; return (0); } static int uart_pl011_bus_receive(struct uart_softc *sc) { struct uart_bas *bas; uint32_t ints, xc; int rx; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); ints = __uart_getreg(bas, UART_MIS); while (ints & (UART_RXREADY | RIS_RTIM)) { if (uart_rx_full(sc)) { sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN; break; } xc = __uart_getreg(bas, UART_DR); rx = xc & 0xff; if (xc & DR_FE) rx |= UART_STAT_FRAMERR; if (xc & DR_PE) rx |= UART_STAT_PARERR; __uart_setreg(bas, UART_ICR, (UART_RXREADY | RIS_RTIM)); uart_rx_put(sc, rx); ints = __uart_getreg(bas, UART_MIS); } uart_unlock(sc->sc_hwmtx); return (0); } static int uart_pl011_bus_setsig(struct uart_softc *sc, int sig) { return (0); } static int uart_pl011_bus_transmit(struct uart_softc *sc) { struct uart_bas *bas; int reg; int i; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); for (i = 0; i < sc->sc_txdatasz; i++) { __uart_setreg(bas, UART_DR, sc->sc_txbuf[i]); uart_barrier(bas); } /* If not empty wait until it is */ if ((__uart_getreg(bas, UART_FR) & FR_TXFE) != FR_TXFE) { sc->sc_txbusy = 1; /* Enable TX interrupt */ reg = __uart_getreg(bas, UART_IMSC); reg |= (UART_TXEMPTY); __uart_setreg(bas, UART_IMSC, reg); } uart_unlock(sc->sc_hwmtx); /* No interrupt expected, schedule the next fifo write */ if (!sc->sc_txbusy) uart_sched_softih(sc, SER_INT_TXIDLE); return (0); } static void uart_pl011_bus_grab(struct uart_softc *sc) { struct uart_bas *bas; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); __uart_setreg(bas, UART_IMSC, /* Switch to RX polling while grabbed */ ~UART_RXREADY & __uart_getreg(bas, UART_IMSC)); uart_unlock(sc->sc_hwmtx); } static void uart_pl011_bus_ungrab(struct uart_softc *sc) { struct uart_bas *bas; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); __uart_setreg(bas, UART_IMSC, /* Switch to RX interrupts while not grabbed */ UART_RXREADY | __uart_getreg(bas, UART_IMSC)); uart_unlock(sc->sc_hwmtx); } Index: head/sys/dev/uart/uart_dev_quicc.c =================================================================== --- head/sys/dev/uart/uart_dev_quicc.c (revision 281437) +++ head/sys/dev/uart/uart_dev_quicc.c (revision 281438) @@ -1,522 +1,523 @@ /*- * Copyright (c) 2006 Juniper Networks * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include "uart_if.h" #define DEFAULT_RCLK ((266000000 * 2) / 16) #define quicc_read2(bas, reg) \ bus_space_read_2((bas)->bst, (bas)->bsh, reg) #define quicc_read4(bas, reg) \ bus_space_read_4((bas)->bst, (bas)->bsh, reg) #define quicc_write2(bas, reg, val) \ bus_space_write_2((bas)->bst, (bas)->bsh, reg, val) #define quicc_write4(bas, reg, val) \ bus_space_write_4((bas)->bst, (bas)->bsh, reg, val) static int quicc_divisor(int rclk, int baudrate) { int act_baud, divisor, error; if (baudrate == 0) return (-1); divisor = rclk / baudrate / 16; if (divisor > 4096) divisor = ((divisor >> 3) - 2) | 1; else if (divisor >= 0) divisor = (divisor - 1) << 1; if (divisor < 0 || divisor >= 8192) return (-1); act_baud = rclk / (((divisor >> 1) + 1) << ((divisor & 1) ? 8 : 4)); /* 10 times error in percent: */ error = ((act_baud - baudrate) * 2000 / baudrate + 1) >> 1; /* 3.0% maximum error tolerance: */ if (error < -30 || error > 30) return (-1); return (divisor); } static int quicc_param(struct uart_bas *bas, int baudrate, int databits, int stopbits, int parity) { int divisor; uint16_t psmr; if (baudrate > 0) { divisor = quicc_divisor(bas->rclk, baudrate); if (divisor == -1) return (EINVAL); quicc_write4(bas, QUICC_REG_BRG(bas->chan - 1), divisor | 0x10000); } psmr = 0; switch (databits) { case 5: psmr |= 0x0000; break; case 6: psmr |= 0x1000; break; case 7: psmr |= 0x2000; break; case 8: psmr |= 0x3000; break; default: return (EINVAL); } switch (stopbits) { case 1: psmr |= 0x0000; break; case 2: psmr |= 0x4000; break; default: return (EINVAL); } switch (parity) { case UART_PARITY_EVEN: psmr |= 0x1a; break; case UART_PARITY_MARK: psmr |= 0x1f; break; case UART_PARITY_NONE: psmr |= 0x00; break; case UART_PARITY_ODD: psmr |= 0x10; break; case UART_PARITY_SPACE: psmr |= 0x15; break; default: return (EINVAL); } quicc_write2(bas, QUICC_REG_SCC_PSMR(bas->chan - 1), psmr); return (0); } static void quicc_setup(struct uart_bas *bas, int baudrate, int databits, int stopbits, int parity) { if (bas->rclk == 0) bas->rclk = DEFAULT_RCLK; /* * GSMR_L = 0x00028034 * GSMR_H = 0x00000020 */ quicc_param(bas, baudrate, databits, stopbits, parity); quicc_write2(bas, QUICC_REG_SCC_SCCE(bas->chan - 1), ~0); quicc_write2(bas, QUICC_REG_SCC_SCCM(bas->chan - 1), 0x0027); } /* * Low-level UART interface. */ static int quicc_probe(struct uart_bas *bas); static void quicc_init(struct uart_bas *bas, int, int, int, int); static void quicc_term(struct uart_bas *bas); static void quicc_putc(struct uart_bas *bas, int); static int quicc_rxready(struct uart_bas *bas); static int quicc_getc(struct uart_bas *bas, struct mtx *); static struct uart_ops uart_quicc_ops = { .probe = quicc_probe, .init = quicc_init, .term = quicc_term, .putc = quicc_putc, .rxready = quicc_rxready, .getc = quicc_getc, }; static int quicc_probe(struct uart_bas *bas) { return (0); } static void quicc_init(struct uart_bas *bas, int baudrate, int databits, int stopbits, int parity) { quicc_setup(bas, baudrate, databits, stopbits, parity); } static void quicc_term(struct uart_bas *bas) { } static void quicc_putc(struct uart_bas *bas, int c) { int unit; uint16_t toseq; unit = bas->chan - 1; while (quicc_read2(bas, QUICC_PRAM_SCC_UART_TOSEQ(unit)) & 0x2000) DELAY(10); toseq = 0x2000 | (c & 0xff); quicc_write2(bas, QUICC_PRAM_SCC_UART_TOSEQ(unit), toseq); } static int quicc_rxready(struct uart_bas *bas) { uint16_t rb; rb = quicc_read2(bas, QUICC_PRAM_SCC_RBASE(bas->chan - 1)); return ((quicc_read2(bas, rb) & 0x8000) ? 0 : 1); } static int quicc_getc(struct uart_bas *bas, struct mtx *hwmtx) { volatile char *buf; int c; uint16_t rb, sc; uart_lock(hwmtx); rb = quicc_read2(bas, QUICC_PRAM_SCC_RBASE(bas->chan - 1)); while ((sc = quicc_read2(bas, rb)) & 0x8000) { uart_unlock(hwmtx); DELAY(4); uart_lock(hwmtx); } buf = (void *)(uintptr_t)quicc_read4(bas, rb + 4); c = *buf; quicc_write2(bas, rb, sc | 0x8000); uart_unlock(hwmtx); return (c); } /* * High-level UART interface. */ struct quicc_softc { struct uart_softc base; }; static int quicc_bus_attach(struct uart_softc *); static int quicc_bus_detach(struct uart_softc *); static int quicc_bus_flush(struct uart_softc *, int); static int quicc_bus_getsig(struct uart_softc *); static int quicc_bus_ioctl(struct uart_softc *, int, intptr_t); static int quicc_bus_ipend(struct uart_softc *); static int quicc_bus_param(struct uart_softc *, int, int, int, int); static int quicc_bus_probe(struct uart_softc *); static int quicc_bus_receive(struct uart_softc *); static int quicc_bus_setsig(struct uart_softc *, int); static int quicc_bus_transmit(struct uart_softc *); static void quicc_bus_grab(struct uart_softc *); static void quicc_bus_ungrab(struct uart_softc *); static kobj_method_t quicc_methods[] = { KOBJMETHOD(uart_attach, quicc_bus_attach), KOBJMETHOD(uart_detach, quicc_bus_detach), KOBJMETHOD(uart_flush, quicc_bus_flush), KOBJMETHOD(uart_getsig, quicc_bus_getsig), KOBJMETHOD(uart_ioctl, quicc_bus_ioctl), KOBJMETHOD(uart_ipend, quicc_bus_ipend), KOBJMETHOD(uart_param, quicc_bus_param), KOBJMETHOD(uart_probe, quicc_bus_probe), KOBJMETHOD(uart_receive, quicc_bus_receive), KOBJMETHOD(uart_setsig, quicc_bus_setsig), KOBJMETHOD(uart_transmit, quicc_bus_transmit), KOBJMETHOD(uart_grab, quicc_bus_grab), KOBJMETHOD(uart_ungrab, quicc_bus_ungrab), { 0, 0 } }; struct uart_class uart_quicc_class = { "quicc", quicc_methods, sizeof(struct quicc_softc), .uc_ops = &uart_quicc_ops, .uc_range = 2, - .uc_rclk = DEFAULT_RCLK + .uc_rclk = DEFAULT_RCLK, + .uc_rshift = 0 }; #define SIGCHG(c, i, s, d) \ if (c) { \ i |= (i & s) ? s : s | d; \ } else { \ i = (i & s) ? (i & ~s) | d : i; \ } static int quicc_bus_attach(struct uart_softc *sc) { struct uart_bas *bas; struct uart_devinfo *di; uint16_t st, rb; bas = &sc->sc_bas; if (sc->sc_sysdev != NULL) { di = sc->sc_sysdev; quicc_param(bas, di->baudrate, di->databits, di->stopbits, di->parity); } else { quicc_setup(bas, 9600, 8, 1, UART_PARITY_NONE); } /* Enable interrupts on the receive buffer. */ rb = quicc_read2(bas, QUICC_PRAM_SCC_RBASE(bas->chan - 1)); st = quicc_read2(bas, rb); quicc_write2(bas, rb, st | 0x9000); (void)quicc_bus_getsig(sc); return (0); } static int quicc_bus_detach(struct uart_softc *sc) { return (0); } static int quicc_bus_flush(struct uart_softc *sc, int what) { return (0); } static int quicc_bus_getsig(struct uart_softc *sc) { uint32_t new, old, sig; uint32_t dummy; do { old = sc->sc_hwsig; sig = old; uart_lock(sc->sc_hwmtx); /* XXX SIGNALS */ dummy = 0; uart_unlock(sc->sc_hwmtx); SIGCHG(dummy, sig, SER_CTS, SER_DCTS); SIGCHG(dummy, sig, SER_DCD, SER_DDCD); SIGCHG(dummy, sig, SER_DSR, SER_DDSR); new = sig & ~SER_MASK_DELTA; } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new)); return (sig); } static int quicc_bus_ioctl(struct uart_softc *sc, int request, intptr_t data) { struct uart_bas *bas; uint32_t brg; int baudrate, error; bas = &sc->sc_bas; error = 0; uart_lock(sc->sc_hwmtx); switch (request) { case UART_IOCTL_BREAK: break; case UART_IOCTL_BAUD: brg = quicc_read4(bas, QUICC_REG_BRG(bas->chan - 1)) & 0x1fff; brg = (brg & 1) ? (brg + 1) << 3 : (brg + 2) >> 1; baudrate = bas->rclk / (brg * 16); *(int*)data = baudrate; break; default: error = EINVAL; break; } uart_unlock(sc->sc_hwmtx); return (error); } static int quicc_bus_ipend(struct uart_softc *sc) { struct uart_bas *bas; int ipend; uint16_t scce; bas = &sc->sc_bas; ipend = 0; uart_lock(sc->sc_hwmtx); scce = quicc_read2(bas, QUICC_REG_SCC_SCCE(bas->chan - 1)); quicc_write2(bas, QUICC_REG_SCC_SCCE(bas->chan - 1), ~0); uart_unlock(sc->sc_hwmtx); if (scce & 0x0001) ipend |= SER_INT_RXREADY; if (scce & 0x0002) ipend |= SER_INT_TXIDLE; if (scce & 0x0004) ipend |= SER_INT_OVERRUN; if (scce & 0x0020) ipend |= SER_INT_BREAK; /* XXX SIGNALS */ return (ipend); } static int quicc_bus_param(struct uart_softc *sc, int baudrate, int databits, int stopbits, int parity) { int error; uart_lock(sc->sc_hwmtx); error = quicc_param(&sc->sc_bas, baudrate, databits, stopbits, parity); uart_unlock(sc->sc_hwmtx); return (error); } static int quicc_bus_probe(struct uart_softc *sc) { char buf[80]; int error; error = quicc_probe(&sc->sc_bas); if (error) return (error); sc->sc_rxfifosz = 1; sc->sc_txfifosz = 1; snprintf(buf, sizeof(buf), "quicc, channel %d", sc->sc_bas.chan); device_set_desc_copy(sc->sc_dev, buf); return (0); } static int quicc_bus_receive(struct uart_softc *sc) { struct uart_bas *bas; volatile char *buf; uint16_t st, rb; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); rb = quicc_read2(bas, QUICC_PRAM_SCC_RBASE(bas->chan - 1)); st = quicc_read2(bas, rb); buf = (void *)(uintptr_t)quicc_read4(bas, rb + 4); uart_rx_put(sc, *buf); quicc_write2(bas, rb, st | 0x9000); uart_unlock(sc->sc_hwmtx); return (0); } static int quicc_bus_setsig(struct uart_softc *sc, int sig) { struct uart_bas *bas; uint32_t new, old; bas = &sc->sc_bas; do { old = sc->sc_hwsig; new = old; if (sig & SER_DDTR) { SIGCHG(sig & SER_DTR, new, SER_DTR, SER_DDTR); } if (sig & SER_DRTS) { SIGCHG(sig & SER_RTS, new, SER_RTS, SER_DRTS); } } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new)); uart_lock(sc->sc_hwmtx); /* XXX SIGNALS */ uart_unlock(sc->sc_hwmtx); return (0); } static int quicc_bus_transmit(struct uart_softc *sc) { volatile char *buf; struct uart_bas *bas; uint16_t st, tb; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); tb = quicc_read2(bas, QUICC_PRAM_SCC_TBASE(bas->chan - 1)); st = quicc_read2(bas, tb); buf = (void *)(uintptr_t)quicc_read4(bas, tb + 4); *buf = sc->sc_txbuf[0]; quicc_write2(bas, tb + 2, 1); quicc_write2(bas, tb, st | 0x9000); sc->sc_txbusy = 1; uart_unlock(sc->sc_hwmtx); return (0); } static void quicc_bus_grab(struct uart_softc *sc) { struct uart_bas *bas; uint16_t st, rb; /* Disable interrupts on the receive buffer. */ bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); rb = quicc_read2(bas, QUICC_PRAM_SCC_RBASE(bas->chan - 1)); st = quicc_read2(bas, rb); quicc_write2(bas, rb, st & ~0x9000); uart_unlock(sc->sc_hwmtx); } static void quicc_bus_ungrab(struct uart_softc *sc) { struct uart_bas *bas; uint16_t st, rb; /* Enable interrupts on the receive buffer. */ bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); rb = quicc_read2(bas, QUICC_PRAM_SCC_RBASE(bas->chan - 1)); st = quicc_read2(bas, rb); quicc_write2(bas, rb, st | 0x9000); uart_unlock(sc->sc_hwmtx); } Index: head/sys/dev/uart/uart_dev_sab82532.c =================================================================== --- head/sys/dev/uart/uart_dev_sab82532.c (revision 281437) +++ head/sys/dev/uart/uart_dev_sab82532.c (revision 281438) @@ -1,759 +1,760 @@ /*- * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include "uart_if.h" #define DEFAULT_RCLK 29491200 /* * 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<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< 63) continue; act_div = n * (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_rxready(struct uart_bas *bas); static int sab82532_getc(struct uart_bas *bas, struct mtx *); static struct uart_ops uart_sab82532_ops = { .probe = sab82532_probe, .init = sab82532_init, .term = sab82532_term, .putc = sab82532_putc, .rxready = sab82532_rxready, .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); switch (bas->chan) { case 1: pvr &= ~SAB_PVR_DTR_A; break; case 2: pvr &= ~SAB_PVR_DTR_B; break; } 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, (unsigned char)~SAB_IMR0_TCD); 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); switch (bas->chan) { case 1: pvr |= SAB_PVR_DTR_A; break; case 2: pvr |= SAB_PVR_DTR_B; break; } 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_rxready(struct uart_bas *bas) { return ((uart_getreg(bas, SAB_STAR) & SAB_STAR_RFNE) != 0 ? 1 : 0); } static int sab82532_getc(struct uart_bas *bas, struct mtx *hwmtx) { int c, delay; uart_lock(hwmtx); /* 1/10th the time to transmit 1 character (estimate). */ delay = sab82532_delay(bas); while (!(uart_getreg(bas, SAB_STAR) & SAB_STAR_RFNE)) { uart_unlock(hwmtx); DELAY(delay); uart_lock(hwmtx); } 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); uart_unlock(hwmtx); 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 void sab82532_bus_grab(struct uart_softc *); static void sab82532_bus_ungrab(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), KOBJMETHOD(uart_grab, sab82532_bus_grab), KOBJMETHOD(uart_ungrab, sab82532_bus_ungrab), { 0, 0 } }; struct uart_class uart_sab82532_class = { "sab82532", sab82532_methods, sizeof(struct sab82532_softc), .uc_ops = &uart_sab82532_ops, .uc_range = 64, - .uc_rclk = DEFAULT_RCLK + .uc_rclk = DEFAULT_RCLK, + .uc_rshift = 0 }; #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); 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, SER_DDTR|SER_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) { uart_lock(sc->sc_hwmtx); sab82532_flush(&sc->sc_bas, what); uart_unlock(sc->sc_hwmtx); 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; uart_lock(sc->sc_hwmtx); star = uart_getreg(bas, SAB_STAR); SIGCHG(star & SAB_STAR_CTS, sig, SER_CTS, SER_DCTS); vstr = uart_getreg(bas, SAB_VSTR); SIGCHG(vstr & SAB_VSTR_CD, sig, SER_DCD, SER_DDCD); pvr = ~uart_getreg(bas, SAB_PVR); switch (bas->chan) { case 1: pvr &= SAB_PVR_DSR_A; break; case 2: pvr &= SAB_PVR_DSR_B; break; } SIGCHG(pvr, sig, SER_DSR, SER_DDSR); uart_unlock(sc->sc_hwmtx); new = sig & ~SER_MASK_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; int error; bas = &sc->sc_bas; error = 0; uart_lock(sc->sc_hwmtx); 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: error = EINVAL; break; } uart_unlock(sc->sc_hwmtx); return (error); } static int sab82532_bus_ipend(struct uart_softc *sc) { struct uart_bas *bas; int ipend; uint8_t isr0, isr1; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); 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); } uart_unlock(sc->sc_hwmtx); ipend = 0; if (isr1 & SAB_ISR1_BRKT) ipend |= SER_INT_BREAK; if (isr0 & SAB_ISR0_RFO) ipend |= SER_INT_OVERRUN; if (isr0 & (SAB_ISR0_TCD|SAB_ISR0_RPF)) ipend |= SER_INT_RXREADY; if ((isr0 & SAB_ISR0_CDSC) || (isr1 & SAB_ISR1_CSC)) ipend |= SER_INT_SIGCHG; if (isr1 & SAB_ISR1_ALLS) ipend |= SER_INT_TXIDLE; return (ipend); } static int sab82532_bus_param(struct uart_softc *sc, int baudrate, int databits, int stopbits, int parity) { struct uart_bas *bas; int error; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); error = sab82532_param(bas, baudrate, databits, stopbits, parity); uart_unlock(sc->sc_hwmtx); return (error); } static int sab82532_bus_probe(struct uart_softc *sc) { char buf[80]; const char *vstr; int error; char ch; error = sab82532_probe(&sc->sc_bas); if (error) return (error); sc->sc_rxfifosz = 32; sc->sc_txfifosz = 32; ch = sc->sc_bas.chan - 1 + 'A'; 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 %c", 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; uart_lock(sc->sc_hwmtx); 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); uart_unlock(sc->sc_hwmtx); 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 & SER_DDTR) { SIGCHG(sig & SER_DTR, new, SER_DTR, SER_DDTR); } if (sig & SER_DRTS) { SIGCHG(sig & SER_RTS, new, SER_RTS, SER_DRTS); } } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new)); uart_lock(sc->sc_hwmtx); /* Set DTR pin. */ pvr = uart_getreg(bas, SAB_PVR); switch (bas->chan) { case 1: if (new & SER_DTR) pvr &= ~SAB_PVR_DTR_A; else pvr |= SAB_PVR_DTR_A; break; case 2: if (new & SER_DTR) pvr &= ~SAB_PVR_DTR_B; else pvr |= SAB_PVR_DTR_B; break; } uart_setreg(bas, SAB_PVR, pvr); /* Set RTS pin. */ mode = uart_getreg(bas, SAB_MODE); if (new & SER_RTS) mode &= ~SAB_MODE_FRTS; else mode |= SAB_MODE_FRTS; uart_setreg(bas, SAB_MODE, mode); uart_barrier(bas); uart_unlock(sc->sc_hwmtx); return (0); } static int sab82532_bus_transmit(struct uart_softc *sc) { struct uart_bas *bas; int i; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); 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; uart_unlock(sc->sc_hwmtx); return (0); } static void sab82532_bus_grab(struct uart_softc *sc) { struct uart_bas *bas; uint8_t imr0; bas = &sc->sc_bas; imr0 = SAB_IMR0_TIME|SAB_IMR0_CDSC|SAB_IMR0_RFO; /* No TCD or RPF */ uart_lock(sc->sc_hwmtx); uart_setreg(bas, SAB_IMR0, 0xff & ~imr0); uart_barrier(bas); uart_unlock(sc->sc_hwmtx); } static void sab82532_bus_ungrab(struct uart_softc *sc) { struct uart_bas *bas; uint8_t imr0; bas = &sc->sc_bas; imr0 = SAB_IMR0_TCD|SAB_IMR0_TIME|SAB_IMR0_CDSC|SAB_IMR0_RFO| SAB_IMR0_RPF; uart_lock(sc->sc_hwmtx); uart_setreg(bas, SAB_IMR0, 0xff & ~imr0); uart_barrier(bas); uart_unlock(sc->sc_hwmtx); } Index: head/sys/dev/uart/uart_dev_ti8250.c =================================================================== --- head/sys/dev/uart/uart_dev_ti8250.c (revision 281437) +++ head/sys/dev/uart/uart_dev_ti8250.c (revision 281438) @@ -1,146 +1,147 @@ /*- * Copyright (c) 2013 Ian Lepore * 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 "opt_platform.h" #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "uart_if.h" /* * High-level UART interface. */ struct ti8250_softc { struct ns8250_softc ns8250_base; /*uint32_t mystuff;*/ }; #define MDR1_REG 8 #define MDR1_MODE_UART 0 #define MDR1_MODE_DISABLE 7 #define SYSCC_REG 15 #define SYSCC_SOFTRESET (1 << 1) #define SYSS_REG 16 #define SYSS_STATUS_RESETDONE (1 << 0) static int ti8250_bus_probe(struct uart_softc *sc) { int status; int devid; clk_ident_t clkid; pcell_t prop; phandle_t node; /* * Get the device id from FDT. If it's not there we can't turn on the * right clocks, so bail, unless we're doing unit 0. We assume that's * the serial console, whose clock isn't controllable anyway, and we * sure don't want to break the console because of a config error. */ node = ofw_bus_get_node(sc->sc_dev); if ((OF_getprop(node, "uart-device-id", &prop, sizeof(prop))) <= 0) { device_printf(sc->sc_dev, "missing uart-device-id attribute in FDT\n"); if (device_get_unit(sc->sc_dev) != 0) return (ENXIO); devid = 0; } else devid = fdt32_to_cpu(prop); /* Enable clocks for this device. We can't continue if that fails. */ clkid = UART0_CLK + devid; if ((status = ti_prcm_clk_enable(clkid)) != 0) return (status); /* * Set the hardware to disabled mode, do a full device reset, then set * it to uart mode. Most devices will be reset-and-disabled already, * but you never know what a bootloader might have done. */ uart_setreg(&sc->sc_bas, MDR1_REG, MDR1_MODE_DISABLE); uart_setreg(&sc->sc_bas, SYSCC_REG, SYSCC_SOFTRESET); while (uart_getreg(&sc->sc_bas, SYSS_REG) & SYSS_STATUS_RESETDONE) continue; uart_setreg(&sc->sc_bas, MDR1_REG, MDR1_MODE_UART); status = ns8250_bus_probe(sc); if (status == 0) device_set_desc(sc->sc_dev, "TI UART (16550 compatible)"); return (status); } static kobj_method_t ti8250_methods[] = { KOBJMETHOD(uart_probe, ti8250_bus_probe), 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_receive, ns8250_bus_receive), KOBJMETHOD(uart_setsig, ns8250_bus_setsig), KOBJMETHOD(uart_transmit, ns8250_bus_transmit), KOBJMETHOD_END }; static struct uart_class uart_ti8250_class = { "ti8250", ti8250_methods, sizeof(struct ti8250_softc), .uc_ops = &uart_ns8250_ops, .uc_range = 0x88, - .uc_rclk = 48000000 + .uc_rclk = 48000000, + .uc_rshift = 0 }; static struct ofw_compat_data compat_data[] = { {"ti,ns16550", (uintptr_t)&uart_ti8250_class}, {NULL, (uintptr_t)NULL}, }; UART_FDT_CLASS_AND_DEVICE(compat_data); Index: head/sys/dev/uart/uart_dev_z8530.c =================================================================== --- head/sys/dev/uart/uart_dev_z8530.c (revision 281437) +++ head/sys/dev/uart/uart_dev_z8530.c (revision 281438) @@ -1,651 +1,652 @@ /*- * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include "uart_if.h" #define DEFAULT_RCLK 307200 /* Hack! */ #ifdef __powerpc__ #define UART_PCLK 0 #else #define UART_PCLK MCB2_PCLK #endif /* 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 (-1); divisor = (rclk + baudrate) / (baudrate << 1) - 2; if (divisor < 0 || divisor >= 65536) return (-1); 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 (-1); 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); } if (baudrate > 0) { divisor = z8530_divisor(bas->rclk, baudrate); if (divisor == -1) return (EINVAL); } else divisor = -1; uart_setmreg(bas, WR_MCB2, UART_PCLK); uart_barrier(bas); if (divisor >= 0) { 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); uart_setmreg(bas, WR_MCB2, UART_PCLK | MCB2_BRGE); 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. */ switch (bas->chan) { case 1: uart_setmreg(bas, WR_MIC, MIC_NV | MIC_CRA); break; case 2: uart_setmreg(bas, WR_MIC, MIC_NV | MIC_CRB); break; } uart_barrier(bas); /* Set clock sources. */ uart_setmreg(bas, WR_CMC, CMC_RC_BRG | CMC_TC_BRG); uart_setmreg(bas, WR_MCB2, UART_PCLK); 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_rxready(struct uart_bas *bas); static int z8530_getc(struct uart_bas *bas, struct mtx *); static struct uart_ops uart_z8530_ops = { .probe = z8530_probe, .init = z8530_init, .term = z8530_term, .putc = z8530_putc, .rxready = z8530_rxready, .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_getreg(bas, REG_CTRL) & BES_TXE)) ; uart_setreg(bas, REG_DATA, c); uart_barrier(bas); } static int z8530_rxready(struct uart_bas *bas) { return ((uart_getreg(bas, REG_CTRL) & BES_RXA) != 0 ? 1 : 0); } static int z8530_getc(struct uart_bas *bas, struct mtx *hwmtx) { int c; uart_lock(hwmtx); while (!(uart_getreg(bas, REG_CTRL) & BES_RXA)) { uart_unlock(hwmtx); DELAY(10); uart_lock(hwmtx); } c = uart_getreg(bas, REG_DATA); uart_unlock(hwmtx); return (c); } /* * High-level UART interface. */ struct z8530_softc { struct uart_softc base; uint8_t tpc; uint8_t txidle; }; 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 void z8530_bus_grab(struct uart_softc *); static void z8530_bus_ungrab(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), KOBJMETHOD(uart_grab, z8530_bus_grab), KOBJMETHOD(uart_ungrab, z8530_bus_ungrab), { 0, 0 } }; struct uart_class uart_z8530_class = { "z8530", z8530_methods, sizeof(struct z8530_softc), .uc_ops = &uart_z8530_ops, .uc_range = 2, - .uc_rclk = DEFAULT_RCLK + .uc_rclk = DEFAULT_RCLK, + .uc_rshift = 0 }; #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); } z8530->txidle = 1; /* Report SER_INT_TXIDLE. */ (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_XIE | 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); uart_setmreg(bas, WR_MIC, MIC_NV | MIC_MIE); 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) { uint32_t new, old, sig; uint8_t bes; do { old = sc->sc_hwsig; sig = old; uart_lock(sc->sc_hwmtx); bes = uart_getmreg(&sc->sc_bas, RR_BES); uart_unlock(sc->sc_hwmtx); SIGCHG(bes & BES_CTS, sig, SER_CTS, SER_DCTS); SIGCHG(bes & BES_DCD, sig, SER_DCD, SER_DDCD); SIGCHG(bes & BES_SYNC, sig, SER_DSR, SER_DDSR); new = sig & ~SER_MASK_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; int baudrate, divisor, error; bas = &sc->sc_bas; error = 0; uart_lock(sc->sc_hwmtx); 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; case UART_IOCTL_BAUD: divisor = uart_getmreg(bas, RR_TCH); divisor = (divisor << 8) | uart_getmreg(bas, RR_TCL); baudrate = bas->rclk / 2 / (divisor + 2); *(int*)data = baudrate; break; default: error = EINVAL; break; } uart_unlock(sc->sc_hwmtx); return (error); } static int z8530_bus_ipend(struct uart_softc *sc) { struct z8530_softc *z8530 = (struct z8530_softc*)sc; struct uart_bas *bas; int ipend; uint32_t sig; uint8_t bes, ip, iv, src; bas = &sc->sc_bas; ipend = 0; uart_lock(sc->sc_hwmtx); switch (bas->chan) { case 1: ip = uart_getmreg(bas, RR_IP); break; case 2: /* XXX hack!!! */ iv = uart_getmreg(bas, RR_IV) & 0x0E; switch (iv) { case IV_TEB: ip = IP_TIA; break; case IV_XSB: ip = IP_SIA; break; case IV_RAB: ip = IP_RIA; break; default: ip = 0; break; } break; default: ip = 0; break; } if (ip & IP_RIA) ipend |= SER_INT_RXREADY; if (ip & IP_TIA) { uart_setreg(bas, REG_CTRL, CR_RSTTXI); uart_barrier(bas); if (z8530->txidle) { ipend |= SER_INT_TXIDLE; z8530->txidle = 0; /* Mask SER_INT_TXIDLE. */ } } if (ip & IP_SIA) { uart_setreg(bas, REG_CTRL, CR_RSTXSI); uart_barrier(bas); bes = uart_getmreg(bas, RR_BES); if (bes & BES_BRK) ipend |= SER_INT_BREAK; sig = sc->sc_hwsig; SIGCHG(bes & BES_CTS, sig, SER_CTS, SER_DCTS); SIGCHG(bes & BES_DCD, sig, SER_DCD, SER_DDCD); SIGCHG(bes & BES_SYNC, sig, SER_DSR, SER_DDSR); if (sig & SER_MASK_DELTA) ipend |= SER_INT_SIGCHG; src = uart_getmreg(bas, RR_SRC); if (src & SRC_OVR) { uart_setreg(bas, REG_CTRL, CR_RSTERR); uart_barrier(bas); ipend |= SER_INT_OVERRUN; } } if (ipend) { uart_setreg(bas, REG_CTRL, CR_RSTIUS); uart_barrier(bas); } uart_unlock(sc->sc_hwmtx); 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; uart_lock(sc->sc_hwmtx); error = z8530_param(&sc->sc_bas, baudrate, databits, stopbits, parity, &z8530->tpc); uart_unlock(sc->sc_hwmtx); return (error); } static int z8530_bus_probe(struct uart_softc *sc) { char buf[80]; int error; char ch; error = z8530_probe(&sc->sc_bas); if (error) return (error); sc->sc_rxfifosz = 3; sc->sc_txfifosz = 1; ch = sc->sc_bas.chan - 1 + 'A'; snprintf(buf, sizeof(buf), "z8530, channel %c", 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; uart_lock(sc->sc_hwmtx); bes = uart_getmreg(bas, RR_BES); while (bes & BES_RXA) { if (uart_rx_full(sc)) { sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN; break; } xc = uart_getreg(bas, REG_DATA); uart_barrier(bas); src = uart_getmreg(bas, RR_SRC); if (src & SRC_FE) xc |= UART_STAT_FRAMERR; if (src & SRC_PE) xc |= UART_STAT_PARERR; if (src & SRC_OVR) xc |= UART_STAT_OVERRUN; uart_rx_put(sc, xc); if (src & (SRC_FE | SRC_PE | SRC_OVR)) { uart_setreg(bas, REG_CTRL, CR_RSTERR); uart_barrier(bas); } bes = uart_getmreg(bas, RR_BES); } /* Discard everything left in the Rx FIFO. */ while (bes & BES_RXA) { (void)uart_getreg(bas, REG_DATA); uart_barrier(bas); src = uart_getmreg(bas, RR_SRC); if (src & (SRC_FE | SRC_PE | SRC_OVR)) { uart_setreg(bas, REG_CTRL, CR_RSTERR); uart_barrier(bas); } bes = uart_getmreg(bas, RR_BES); } uart_unlock(sc->sc_hwmtx); 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 & SER_DDTR) { SIGCHG(sig & SER_DTR, new, SER_DTR, SER_DDTR); } if (sig & SER_DRTS) { SIGCHG(sig & SER_RTS, new, SER_RTS, SER_DRTS); } } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new)); uart_lock(sc->sc_hwmtx); if (new & SER_DTR) z8530->tpc |= TPC_DTR; else z8530->tpc &= ~TPC_DTR; if (new & SER_RTS) z8530->tpc |= TPC_RTS; else z8530->tpc &= ~TPC_RTS; uart_setmreg(bas, WR_TPC, z8530->tpc); uart_barrier(bas); uart_unlock(sc->sc_hwmtx); return (0); } static int z8530_bus_transmit(struct uart_softc *sc) { struct z8530_softc *z8530 = (struct z8530_softc*)sc; struct uart_bas *bas; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); while (!(uart_getmreg(bas, RR_BES) & BES_TXE)) ; uart_setreg(bas, REG_DATA, sc->sc_txbuf[0]); uart_barrier(bas); sc->sc_txbusy = 1; z8530->txidle = 1; /* Report SER_INT_TXIDLE again. */ uart_unlock(sc->sc_hwmtx); return (0); } static void z8530_bus_grab(struct uart_softc *sc) { struct uart_bas *bas; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); uart_setmreg(bas, WR_IDT, IDT_XIE | IDT_TIE); uart_barrier(bas); uart_unlock(sc->sc_hwmtx); } static void z8530_bus_ungrab(struct uart_softc *sc) { struct uart_bas *bas; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); uart_setmreg(bas, WR_IDT, IDT_XIE | IDT_TIE | IDT_RIA); uart_barrier(bas); uart_unlock(sc->sc_hwmtx); } Index: head/sys/mips/adm5120/uart_dev_adm5120.c =================================================================== --- head/sys/mips/adm5120/uart_dev_adm5120.c (revision 281437) +++ head/sys/mips/adm5120/uart_dev_adm5120.c (revision 281438) @@ -1,479 +1,480 @@ /* $NetBSD: uart.c,v 1.2 2007/03/23 20:05:47 dogcow Exp $ */ /*- * Copyright (c) 2007 Ruslan Ermilov and Vsevolod Lobko. * Copyright (c) 2007 Oleksandr Tymoshenko. * 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. The names of the authors may not be used to endorse or promote * products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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 AUTHORS * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include "uart_if.h" /* * Low-level UART interface. */ static int adm5120_uart_probe(struct uart_bas *bas); static void adm5120_uart_init(struct uart_bas *bas, int, int, int, int); static void adm5120_uart_term(struct uart_bas *bas); static void adm5120_uart_putc(struct uart_bas *bas, int); static int adm5120_uart_rxready(struct uart_bas *bas); static int adm5120_uart_getc(struct uart_bas *bas, struct mtx *); static struct uart_ops uart_adm5120_uart_ops = { .probe = adm5120_uart_probe, .init = adm5120_uart_init, .term = adm5120_uart_term, .putc = adm5120_uart_putc, .rxready = adm5120_uart_rxready, .getc = adm5120_uart_getc, }; static int adm5120_uart_probe(struct uart_bas *bas) { return (0); } static void adm5120_uart_init(struct uart_bas *bas, int baudrate, int databits, int stopbits, int parity) { /* TODO: Set parameters for uart, meanwhile stick with 115200N1 */ } static void adm5120_uart_term(struct uart_bas *bas) { } static void adm5120_uart_putc(struct uart_bas *bas, int c) { char chr; chr = c; while (uart_getreg(bas, UART_FR_REG) & UART_FR_TX_FIFO_FULL) ; uart_setreg(bas, UART_DR_REG, c); while (uart_getreg(bas, UART_FR_REG) & UART_FR_BUSY) ; uart_barrier(bas); } static int adm5120_uart_rxready(struct uart_bas *bas) { if (uart_getreg(bas, UART_FR_REG) & UART_FR_RX_FIFO_EMPTY) return (0); return (1); } static int adm5120_uart_getc(struct uart_bas *bas, struct mtx *hwmtx) { int c; uart_lock(hwmtx); while (uart_getreg(bas, UART_FR_REG) & UART_FR_RX_FIFO_EMPTY) { uart_unlock(hwmtx); DELAY(10); uart_lock(hwmtx); } c = uart_getreg(bas, UART_DR_REG); uart_unlock(hwmtx); return (c); } /* * High-level UART interface. */ struct adm5120_uart_softc { struct uart_softc base; }; static int adm5120_uart_bus_attach(struct uart_softc *); static int adm5120_uart_bus_detach(struct uart_softc *); static int adm5120_uart_bus_flush(struct uart_softc *, int); static int adm5120_uart_bus_getsig(struct uart_softc *); static int adm5120_uart_bus_ioctl(struct uart_softc *, int, intptr_t); static int adm5120_uart_bus_ipend(struct uart_softc *); static int adm5120_uart_bus_param(struct uart_softc *, int, int, int, int); static int adm5120_uart_bus_probe(struct uart_softc *); static int adm5120_uart_bus_receive(struct uart_softc *); static int adm5120_uart_bus_setsig(struct uart_softc *, int); static int adm5120_uart_bus_transmit(struct uart_softc *); static void adm5120_uart_bus_grab(struct uart_softc *); static void adm5120_uart_bus_ungrab(struct uart_softc *); static kobj_method_t adm5120_uart_methods[] = { KOBJMETHOD(uart_attach, adm5120_uart_bus_attach), KOBJMETHOD(uart_detach, adm5120_uart_bus_detach), KOBJMETHOD(uart_flush, adm5120_uart_bus_flush), KOBJMETHOD(uart_getsig, adm5120_uart_bus_getsig), KOBJMETHOD(uart_ioctl, adm5120_uart_bus_ioctl), KOBJMETHOD(uart_ipend, adm5120_uart_bus_ipend), KOBJMETHOD(uart_param, adm5120_uart_bus_param), KOBJMETHOD(uart_probe, adm5120_uart_bus_probe), KOBJMETHOD(uart_receive, adm5120_uart_bus_receive), KOBJMETHOD(uart_setsig, adm5120_uart_bus_setsig), KOBJMETHOD(uart_transmit, adm5120_uart_bus_transmit), KOBJMETHOD(uart_grab, adm5120_uart_bus_grab), KOBJMETHOD(uart_ungrab, adm5120_uart_bus_ungrab), { 0, 0 } }; struct uart_class uart_adm5120_uart_class = { "adm5120", adm5120_uart_methods, sizeof(struct adm5120_uart_softc), .uc_ops = &uart_adm5120_uart_ops, .uc_range = 1, /* use hinted range */ - .uc_rclk = 62500000 + .uc_rclk = 62500000, + .uc_rshift = 0 }; #define SIGCHG(c, i, s, d) \ if (c) { \ i |= (i & s) ? s : s | d; \ } else { \ i = (i & s) ? (i & ~s) | d : i; \ } /* * Disable TX interrupt. uart should be locked */ static __inline void adm5120_uart_disable_txintr(struct uart_softc *sc) { uint8_t cr; cr = uart_getreg(&sc->sc_bas, UART_CR_REG); cr &= ~UART_CR_TX_INT_EN; uart_setreg(&sc->sc_bas, UART_CR_REG, cr); } /* * Enable TX interrupt. uart should be locked */ static __inline void adm5120_uart_enable_txintr(struct uart_softc *sc) { uint8_t cr; cr = uart_getreg(&sc->sc_bas, UART_CR_REG); cr |= UART_CR_TX_INT_EN; uart_setreg(&sc->sc_bas, UART_CR_REG, cr); } static int adm5120_uart_bus_attach(struct uart_softc *sc) { struct uart_bas *bas; struct uart_devinfo *di; bas = &sc->sc_bas; if (sc->sc_sysdev != NULL) { di = sc->sc_sysdev; /* TODO: set parameters from di */ } else { /* TODO: set parameters 115200, 8N1 */ } (void)adm5120_uart_bus_getsig(sc); #if 1 /* Enable FIFO */ uart_setreg(bas, UART_LCR_H_REG, uart_getreg(bas, UART_LCR_H_REG) | UART_LCR_H_FEN); #endif /* Enable interrupts */ uart_setreg(bas, UART_CR_REG, UART_CR_PORT_EN|UART_CR_RX_INT_EN|UART_CR_RX_TIMEOUT_INT_EN| UART_CR_MODEM_STATUS_INT_EN); return (0); } static int adm5120_uart_bus_detach(struct uart_softc *sc) { return (0); } static int adm5120_uart_bus_flush(struct uart_softc *sc, int what) { return (0); } static int adm5120_uart_bus_getsig(struct uart_softc *sc) { uint32_t new, old, sig; uint8_t bes; do { old = sc->sc_hwsig; sig = old; uart_lock(sc->sc_hwmtx); bes = uart_getreg(&sc->sc_bas, UART_FR_REG); uart_unlock(sc->sc_hwmtx); SIGCHG(bes & UART_FR_CTS, sig, SER_CTS, SER_DCTS); SIGCHG(bes & UART_FR_DCD, sig, SER_DCD, SER_DDCD); SIGCHG(bes & UART_FR_DSR, sig, SER_DSR, SER_DDSR); new = sig & ~SER_MASK_DELTA; } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new)); return (sig); } static int adm5120_uart_bus_ioctl(struct uart_softc *sc, int request, intptr_t data) { struct uart_bas *bas; int baudrate, divisor, error; bas = &sc->sc_bas; error = 0; uart_lock(sc->sc_hwmtx); switch (request) { case UART_IOCTL_BREAK: /* TODO: Send BREAK */ break; case UART_IOCTL_BAUD: divisor = uart_getreg(bas, UART_LCR_M_REG); divisor = (divisor << 8) | uart_getreg(bas, UART_LCR_L_REG); baudrate = bas->rclk / 2 / (divisor + 2); *(int*)data = baudrate; break; default: error = EINVAL; break; } uart_unlock(sc->sc_hwmtx); return (error); } static int adm5120_uart_bus_ipend(struct uart_softc *sc) { struct uart_bas *bas; int ipend; uint8_t ir, fr, rsr; bas = &sc->sc_bas; ipend = 0; uart_lock(sc->sc_hwmtx); ir = uart_getreg(&sc->sc_bas, UART_IR_REG); fr = uart_getreg(&sc->sc_bas, UART_FR_REG); rsr = uart_getreg(&sc->sc_bas, UART_RSR_REG); if (ir & UART_IR_RX_INT) ipend |= SER_INT_RXREADY; if (ir & UART_IR_RX_TIMEOUT_INT) ipend |= SER_INT_RXREADY; if (ir & UART_IR_MODEM_STATUS_INT) ipend |= SER_INT_SIGCHG; if (rsr & UART_RSR_BE) ipend |= SER_INT_BREAK; if (rsr & UART_RSR_OE) ipend |= SER_INT_OVERRUN; if (fr & UART_FR_TX_FIFO_EMPTY) { if (ir & UART_IR_TX_INT) { adm5120_uart_disable_txintr(sc); ipend |= SER_INT_TXIDLE; } } if (ipend) uart_setreg(bas, UART_IR_REG, ir | UART_IR_UICR); uart_unlock(sc->sc_hwmtx); return (ipend); } static int adm5120_uart_bus_param(struct uart_softc *sc, int baudrate, int databits, int stopbits, int parity) { /* TODO: Set parameters for uart, meanwhile stick with 115200 8N1 */ return (0); } static int adm5120_uart_bus_probe(struct uart_softc *sc) { char buf[80]; int error; char ch; error = adm5120_uart_probe(&sc->sc_bas); if (error) return (error); sc->sc_rxfifosz = 16; sc->sc_txfifosz = 16; ch = sc->sc_bas.chan + 'A'; snprintf(buf, sizeof(buf), "adm5120_uart, channel %c", ch); device_set_desc_copy(sc->sc_dev, buf); return (0); } static int adm5120_uart_bus_receive(struct uart_softc *sc) { struct uart_bas *bas; int xc; uint8_t fr, rsr; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); fr = uart_getreg(bas, UART_FR_REG); while (!(fr & UART_FR_RX_FIFO_EMPTY)) { if (uart_rx_full(sc)) { sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN; break; } xc = 0; rsr = uart_getreg(bas, UART_RSR_REG); if (rsr & UART_RSR_FE) xc |= UART_STAT_FRAMERR; if (rsr & UART_RSR_PE) xc |= UART_STAT_PARERR; if (rsr & UART_RSR_OE) xc |= UART_STAT_OVERRUN; xc |= uart_getreg(bas, UART_DR_REG); uart_barrier(bas); uart_rx_put(sc, xc); if (rsr & (UART_RSR_FE | UART_RSR_PE | UART_RSR_OE)) { uart_setreg(bas, UART_ECR_REG, UART_ECR_RSR); uart_barrier(bas); } fr = uart_getreg(bas, UART_FR_REG); } /* Discard everything left in the Rx FIFO. */ while (!(fr & UART_FR_RX_FIFO_EMPTY)) { ( void)uart_getreg(bas, UART_DR_REG); uart_barrier(bas); rsr = uart_getreg(bas, UART_RSR_REG); if (rsr & (UART_RSR_FE | UART_RSR_PE | UART_RSR_OE)) { uart_setreg(bas, UART_ECR_REG, UART_ECR_RSR); uart_barrier(bas); } fr = uart_getreg(bas, UART_FR_REG); } uart_unlock(sc->sc_hwmtx); return (0); } static int adm5120_uart_bus_setsig(struct uart_softc *sc, int sig) { /* TODO: implement (?) */ return (0); } static int adm5120_uart_bus_transmit(struct uart_softc *sc) { struct uart_bas *bas; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); sc->sc_txbusy = 1; for (int i = 0; i < sc->sc_txdatasz; i++) { if (uart_getreg(bas, UART_FR_REG) & UART_FR_TX_FIFO_FULL) break; uart_setreg(bas, UART_DR_REG, sc->sc_txbuf[i]); } /* Enable TX interrupt */ adm5120_uart_enable_txintr(sc); uart_unlock(sc->sc_hwmtx); return (0); } static void adm5120_uart_bus_grab(struct uart_softc *sc) { /* Enable interrupts - no RX_INT or RX_TIMEOUT */ uart_lock(sc->sc_hwmtx); uart_setreg(&sc->sc_bas, UART_CR_REG, UART_CR_PORT_EN | UART_CR_MODEM_STATUS_INT_EN); uart_unlock(sc->sc_hwmtx); } static void adm5120_uart_bus_ungrab(struct uart_softc *sc) { /* Enable interrupts */ uart_lock(sc->sc_hwmtx); uart_setreg(&sc->sc_bas, UART_CR_REG, UART_CR_PORT_EN|UART_CR_RX_INT_EN|UART_CR_RX_TIMEOUT_INT_EN| UART_CR_MODEM_STATUS_INT_EN); uart_unlock(sc->sc_hwmtx); } Index: head/sys/mips/atheros/uart_dev_ar933x.c =================================================================== --- head/sys/mips/atheros/uart_dev_ar933x.c (revision 281437) +++ head/sys/mips/atheros/uart_dev_ar933x.c (revision 281438) @@ -1,737 +1,738 @@ /*- * Copyright (c) 2013 Adrian Chadd * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include "uart_if.h" /* * Default system clock is 25MHz; see ar933x_chip.c for how * the startup process determines whether it's 25MHz or 40MHz. */ #define DEFAULT_RCLK (25 * 1000 * 1000) #define ar933x_getreg(bas, reg) \ bus_space_read_4((bas)->bst, (bas)->bsh, reg) #define ar933x_setreg(bas, reg, value) \ bus_space_write_4((bas)->bst, (bas)->bsh, reg, value) static int ar933x_drain(struct uart_bas *bas, int what) { int limit; if (what & UART_DRAIN_TRANSMITTER) { limit = 10*1024; /* Loop over until the TX FIFO shows entirely clear */ while (--limit) { if ((ar933x_getreg(bas, AR933X_UART_CS_REG) & AR933X_UART_CS_TX_BUSY) == 0) break; } if (limit == 0) { return (EIO); } } if (what & UART_DRAIN_RECEIVER) { limit=10*4096; while (--limit) { /* XXX duplicated from ar933x_getc() */ /* XXX TODO: refactor! */ /* If there's nothing to read, stop! */ if ((ar933x_getreg(bas, AR933X_UART_DATA_REG) & AR933X_UART_DATA_RX_CSR) == 0) { break; } /* Read the top of the RX FIFO */ (void) ar933x_getreg(bas, AR933X_UART_DATA_REG); /* Remove that entry from said RX FIFO */ ar933x_setreg(bas, AR933X_UART_DATA_REG, AR933X_UART_DATA_RX_CSR); uart_barrier(bas); DELAY(2); } if (limit == 0) { return (EIO); } } return (0); } /* * Calculate the baud from the given chip configuration parameters. */ static unsigned long ar933x_uart_get_baud(unsigned int clk, unsigned int scale, unsigned int step) { uint64_t t; uint32_t div; div = (2 << 16) * (scale + 1); t = clk; t *= step; t += (div / 2); t = t / div; return (t); } /* * Calculate the scale/step with the lowest possible deviation from * the target baudrate. */ static void ar933x_uart_get_scale_step(struct uart_bas *bas, unsigned int baud, unsigned int *scale, unsigned int *step) { unsigned int tscale; uint32_t clk; long min_diff; clk = bas->rclk; *scale = 0; *step = 0; min_diff = baud; for (tscale = 0; tscale < AR933X_UART_MAX_SCALE; tscale++) { uint64_t tstep; int diff; tstep = baud * (tscale + 1); tstep *= (2 << 16); tstep = tstep / clk; if (tstep > AR933X_UART_MAX_STEP) break; diff = abs(ar933x_uart_get_baud(clk, tscale, tstep) - baud); if (diff < min_diff) { min_diff = diff; *scale = tscale; *step = tstep; } } } static int ar933x_param(struct uart_bas *bas, int baudrate, int databits, int stopbits, int parity) { /* UART always 8 bits */ /* UART always 1 stop bit */ /* UART parity is controllable by bits 0:1, ignore for now */ /* Set baudrate if required. */ if (baudrate > 0) { uint32_t clock_scale, clock_step; /* Find the best fit for the given baud rate */ ar933x_uart_get_scale_step(bas, baudrate, &clock_scale, &clock_step); /* * Program the clock register in its entirety - no need * for Read-Modify-Write. */ ar933x_setreg(bas, AR933X_UART_CLOCK_REG, ((clock_scale & AR933X_UART_CLOCK_SCALE_M) << AR933X_UART_CLOCK_SCALE_S) | (clock_step & AR933X_UART_CLOCK_STEP_M)); } uart_barrier(bas); return (0); } /* * Low-level UART interface. */ static int ar933x_probe(struct uart_bas *bas); static void ar933x_init(struct uart_bas *bas, int, int, int, int); static void ar933x_term(struct uart_bas *bas); static void ar933x_putc(struct uart_bas *bas, int); static int ar933x_rxready(struct uart_bas *bas); static int ar933x_getc(struct uart_bas *bas, struct mtx *); static struct uart_ops uart_ar933x_ops = { .probe = ar933x_probe, .init = ar933x_init, .term = ar933x_term, .putc = ar933x_putc, .rxready = ar933x_rxready, .getc = ar933x_getc, }; static int ar933x_probe(struct uart_bas *bas) { /* We always know this will be here */ return (0); } static void ar933x_init(struct uart_bas *bas, int baudrate, int databits, int stopbits, int parity) { uint32_t reg; /* Setup default parameters */ ar933x_param(bas, baudrate, databits, stopbits, parity); /* XXX Force enable UART in case it was disabled */ /* Disable all interrupts */ ar933x_setreg(bas, AR933X_UART_INT_EN_REG, 0x00000000); /* Disable the host interrupt */ reg = ar933x_getreg(bas, AR933X_UART_CS_REG); reg &= ~AR933X_UART_CS_HOST_INT_EN; ar933x_setreg(bas, AR933X_UART_CS_REG, reg); uart_barrier(bas); /* XXX Set RTS/DTR? */ } /* * Detach from console. */ static void ar933x_term(struct uart_bas *bas) { /* XXX TODO */ } static void ar933x_putc(struct uart_bas *bas, int c) { int limit; limit = 250000; /* Wait for space in the TX FIFO */ while ( ((ar933x_getreg(bas, AR933X_UART_DATA_REG) & AR933X_UART_DATA_TX_CSR) == 0) && --limit) DELAY(4); /* Write the actual byte */ ar933x_setreg(bas, AR933X_UART_DATA_REG, (c & 0xff) | AR933X_UART_DATA_TX_CSR); } static int ar933x_rxready(struct uart_bas *bas) { /* Wait for a character to come ready */ return (!!(ar933x_getreg(bas, AR933X_UART_DATA_REG) & AR933X_UART_DATA_RX_CSR)); } static int ar933x_getc(struct uart_bas *bas, struct mtx *hwmtx) { int c; uart_lock(hwmtx); /* Wait for a character to come ready */ while ((ar933x_getreg(bas, AR933X_UART_DATA_REG) & AR933X_UART_DATA_RX_CSR) == 0) { uart_unlock(hwmtx); DELAY(4); uart_lock(hwmtx); } /* Read the top of the RX FIFO */ c = ar933x_getreg(bas, AR933X_UART_DATA_REG) & 0xff; /* Remove that entry from said RX FIFO */ ar933x_setreg(bas, AR933X_UART_DATA_REG, AR933X_UART_DATA_RX_CSR); uart_unlock(hwmtx); return (c); } /* * High-level UART interface. */ struct ar933x_softc { struct uart_softc base; uint32_t u_ier; }; static int ar933x_bus_attach(struct uart_softc *); static int ar933x_bus_detach(struct uart_softc *); static int ar933x_bus_flush(struct uart_softc *, int); static int ar933x_bus_getsig(struct uart_softc *); static int ar933x_bus_ioctl(struct uart_softc *, int, intptr_t); static int ar933x_bus_ipend(struct uart_softc *); static int ar933x_bus_param(struct uart_softc *, int, int, int, int); static int ar933x_bus_probe(struct uart_softc *); static int ar933x_bus_receive(struct uart_softc *); static int ar933x_bus_setsig(struct uart_softc *, int); static int ar933x_bus_transmit(struct uart_softc *); static void ar933x_bus_grab(struct uart_softc *); static void ar933x_bus_ungrab(struct uart_softc *); static kobj_method_t ar933x_methods[] = { KOBJMETHOD(uart_attach, ar933x_bus_attach), KOBJMETHOD(uart_detach, ar933x_bus_detach), KOBJMETHOD(uart_flush, ar933x_bus_flush), KOBJMETHOD(uart_getsig, ar933x_bus_getsig), KOBJMETHOD(uart_ioctl, ar933x_bus_ioctl), KOBJMETHOD(uart_ipend, ar933x_bus_ipend), KOBJMETHOD(uart_param, ar933x_bus_param), KOBJMETHOD(uart_probe, ar933x_bus_probe), KOBJMETHOD(uart_receive, ar933x_bus_receive), KOBJMETHOD(uart_setsig, ar933x_bus_setsig), KOBJMETHOD(uart_transmit, ar933x_bus_transmit), KOBJMETHOD(uart_grab, ar933x_bus_grab), KOBJMETHOD(uart_ungrab, ar933x_bus_ungrab), { 0, 0 } }; struct uart_class uart_ar933x_class = { "ar933x", ar933x_methods, sizeof(struct ar933x_softc), .uc_ops = &uart_ar933x_ops, .uc_range = 8, - .uc_rclk = DEFAULT_RCLK + .uc_rclk = DEFAULT_RCLK, + .uc_rshift = 0 }; #define SIGCHG(c, i, s, d) \ if (c) { \ i |= (i & s) ? s : s | d; \ } else { \ i = (i & s) ? (i & ~s) | d : i; \ } static int ar933x_bus_attach(struct uart_softc *sc) { struct ar933x_softc *u = (struct ar933x_softc *)sc; struct uart_bas *bas = &sc->sc_bas; uint32_t reg; /* XXX TODO: flush transmitter */ /* * Setup initial interrupt notifications. * * XXX for now, just RX FIFO valid. * Later on (when they're handled), also handle * RX errors/overflow. */ u->u_ier = AR933X_UART_INT_RX_VALID; /* Enable RX interrupts to kick-start things */ ar933x_setreg(bas, AR933X_UART_INT_EN_REG, u->u_ier); /* Enable the host interrupt now */ reg = ar933x_getreg(bas, AR933X_UART_CS_REG); reg |= AR933X_UART_CS_HOST_INT_EN; ar933x_setreg(bas, AR933X_UART_CS_REG, reg); return (0); } static int ar933x_bus_detach(struct uart_softc *sc) { struct uart_bas *bas = &sc->sc_bas; uint32_t reg; /* Disable all interrupts */ ar933x_setreg(bas, AR933X_UART_INT_EN_REG, 0x00000000); /* Disable the host interrupt */ reg = ar933x_getreg(bas, AR933X_UART_CS_REG); reg &= ~AR933X_UART_CS_HOST_INT_EN; ar933x_setreg(bas, AR933X_UART_CS_REG, reg); uart_barrier(bas); return (0); } static int ar933x_bus_flush(struct uart_softc *sc, int what) { struct uart_bas *bas; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); ar933x_drain(bas, what); uart_unlock(sc->sc_hwmtx); return (0); } static int ar933x_bus_getsig(struct uart_softc *sc) { uint32_t sig = sc->sc_hwsig; /* * For now, let's just return that DSR/DCD/CTS is asserted. */ SIGCHG(1, sig, SER_DSR, SER_DDSR); SIGCHG(1, sig, SER_CTS, SER_DCTS); SIGCHG(1, sig, SER_DCD, SER_DDCD); SIGCHG(1, sig, SER_RI, SER_DRI); sc->sc_hwsig = sig & ~SER_MASK_DELTA; return (sig); } /* * XXX TODO: actually implement the rest of this! */ static int ar933x_bus_ioctl(struct uart_softc *sc, int request, intptr_t data) { int error = 0; /* XXX lock */ switch (request) { case UART_IOCTL_BREAK: case UART_IOCTL_IFLOW: case UART_IOCTL_OFLOW: break; case UART_IOCTL_BAUD: *(int*)data = 115200; break; default: error = EINVAL; break; } /* XXX unlock */ return (error); } /* * Bus interrupt handler. * * For now, system interrupts are disabled. * So this is just called from a callout in uart_core.c * to poll various state. */ static int ar933x_bus_ipend(struct uart_softc *sc) { struct ar933x_softc *u = (struct ar933x_softc *)sc; struct uart_bas *bas = &sc->sc_bas; int ipend = 0; uint32_t isr; uart_lock(sc->sc_hwmtx); /* * Fetch/ACK the ISR status. */ isr = ar933x_getreg(bas, AR933X_UART_INT_REG); ar933x_setreg(bas, AR933X_UART_INT_REG, isr); uart_barrier(bas); /* * RX ready - notify upper layer. */ if (isr & AR933X_UART_INT_RX_VALID) { ipend |= SER_INT_RXREADY; } /* * If we get this interrupt, we should disable * it from the interrupt mask and inform the uart * driver appropriately. * * We can't keep setting SER_INT_TXIDLE or SER_INT_SIGCHG * all the time or IO stops working. So we will always * clear this interrupt if we get it, then we only signal * the upper layer if we were doing active TX in the * first place. * * Also, the name is misleading. This actually means * "the FIFO is almost empty." So if we just write some * more data to the FIFO without checking whether it can * take said data, we'll overflow the thing. * * Unfortunately the FreeBSD uart device has no concept of * partial UART writes - it expects that the whole buffer * is written to the hardware. Thus for now, ar933x_bus_transmit() * will wait for the FIFO to finish draining before it pushes * more frames into it. */ if (isr & AR933X_UART_INT_TX_EMPTY) { /* * Update u_ier to disable TX notifications; update hardware */ u->u_ier &= ~AR933X_UART_INT_TX_EMPTY; ar933x_setreg(bas, AR933X_UART_INT_EN_REG, u->u_ier); uart_barrier(bas); } /* * Only signal TX idle if we're not busy transmitting. * * XXX I never get _out_ of txbusy? Debug that! */ if (sc->sc_txbusy) { if (isr & AR933X_UART_INT_TX_EMPTY) { ipend |= SER_INT_TXIDLE; } else { ipend |= SER_INT_SIGCHG; } } uart_unlock(sc->sc_hwmtx); return (ipend); } static int ar933x_bus_param(struct uart_softc *sc, int baudrate, int databits, int stopbits, int parity) { struct uart_bas *bas; int error; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); error = ar933x_param(bas, baudrate, databits, stopbits, parity); uart_unlock(sc->sc_hwmtx); return (error); } static int ar933x_bus_probe(struct uart_softc *sc) { struct uart_bas *bas; int error; bas = &sc->sc_bas; error = ar933x_probe(bas); if (error) return (error); /* Reset FIFOs. */ ar933x_drain(bas, UART_FLUSH_RECEIVER|UART_FLUSH_TRANSMITTER); /* XXX TODO: actually find out what the FIFO depth is! */ sc->sc_rxfifosz = 16; sc->sc_txfifosz = 16; return (0); } static int ar933x_bus_receive(struct uart_softc *sc) { struct uart_bas *bas = &sc->sc_bas; int xc; uart_lock(sc->sc_hwmtx); /* Loop over until we are full, or no data is available */ while (ar933x_rxready(bas)) { if (uart_rx_full(sc)) { sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN; break; } /* Read the top of the RX FIFO */ xc = ar933x_getreg(bas, AR933X_UART_DATA_REG) & 0xff; /* Remove that entry from said RX FIFO */ ar933x_setreg(bas, AR933X_UART_DATA_REG, AR933X_UART_DATA_RX_CSR); uart_barrier(bas); /* XXX frame, parity error */ uart_rx_put(sc, xc); } /* * XXX TODO: Discard everything left in the Rx FIFO? * XXX only if we've hit an overrun condition? */ uart_unlock(sc->sc_hwmtx); return (0); } static int ar933x_bus_setsig(struct uart_softc *sc, int sig) { #if 0 struct ar933x_softc *ns8250 = (struct ar933x_softc*)sc; struct uart_bas *bas; uint32_t new, old; bas = &sc->sc_bas; do { old = sc->sc_hwsig; new = old; if (sig & SER_DDTR) { SIGCHG(sig & SER_DTR, new, SER_DTR, SER_DDTR); } if (sig & SER_DRTS) { SIGCHG(sig & SER_RTS, new, SER_RTS, SER_DRTS); } } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new)); uart_lock(sc->sc_hwmtx); ns8250->mcr &= ~(MCR_DTR|MCR_RTS); if (new & SER_DTR) ns8250->mcr |= MCR_DTR; if (new & SER_RTS) ns8250->mcr |= MCR_RTS; uart_setreg(bas, REG_MCR, ns8250->mcr); uart_barrier(bas); uart_unlock(sc->sc_hwmtx); #endif return (0); } /* * Write the current transmit buffer to the TX FIFO. * * Unfortunately the FreeBSD uart device has no concept of * partial UART writes - it expects that the whole buffer * is written to the hardware. Thus for now, this will wait for * the FIFO to finish draining before it pushes more frames into it. * * If non-blocking operation is truely needed here, either * the FreeBSD uart device will need to handle partial writes * in xxx_bus_transmit(), or we'll need to do TX FIFO buffering * of our own here. */ static int ar933x_bus_transmit(struct uart_softc *sc) { struct uart_bas *bas = &sc->sc_bas; struct ar933x_softc *u = (struct ar933x_softc *)sc; int i; uart_lock(sc->sc_hwmtx); /* Wait for the FIFO to be clear - see above */ while (ar933x_getreg(bas, AR933X_UART_CS_REG) & AR933X_UART_CS_TX_BUSY) ; /* * Write some data! */ for (i = 0; i < sc->sc_txdatasz; i++) { /* Write the TX data */ ar933x_setreg(bas, AR933X_UART_DATA_REG, (sc->sc_txbuf[i] & 0xff) | AR933X_UART_DATA_TX_CSR); uart_barrier(bas); } /* * Now that we're transmitting, get interrupt notification * when the FIFO is (almost) empty - see above. */ u->u_ier |= AR933X_UART_INT_TX_EMPTY; ar933x_setreg(bas, AR933X_UART_INT_EN_REG, u->u_ier); uart_barrier(bas); /* * Inform the upper layer that we are presently transmitting * data to the hardware; this will be cleared when the * TXIDLE interrupt occurs. */ sc->sc_txbusy = 1; uart_unlock(sc->sc_hwmtx); return (0); } static void ar933x_bus_grab(struct uart_softc *sc) { struct uart_bas *bas = &sc->sc_bas; uint32_t reg; /* Disable the host interrupt now */ uart_lock(sc->sc_hwmtx); reg = ar933x_getreg(bas, AR933X_UART_CS_REG); reg &= ~AR933X_UART_CS_HOST_INT_EN; ar933x_setreg(bas, AR933X_UART_CS_REG, reg); uart_unlock(sc->sc_hwmtx); } static void ar933x_bus_ungrab(struct uart_softc *sc) { struct uart_bas *bas = &sc->sc_bas; uint32_t reg; /* Enable the host interrupt now */ uart_lock(sc->sc_hwmtx); reg = ar933x_getreg(bas, AR933X_UART_CS_REG); reg |= AR933X_UART_CS_HOST_INT_EN; ar933x_setreg(bas, AR933X_UART_CS_REG, reg); uart_unlock(sc->sc_hwmtx); } Index: head/sys/mips/cavium/uart_dev_oct16550.c =================================================================== --- head/sys/mips/cavium/uart_dev_oct16550.c (revision 281437) +++ head/sys/mips/cavium/uart_dev_oct16550.c (revision 281438) @@ -1,847 +1,848 @@ /*- * 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. */ /* * uart_dev_oct16550.c * * Derived from uart_dev_ns8250.c * * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include "uart_if.h" /* * Clear pending interrupts. THRE is cleared by reading IIR. Data * that may have been received gets lost here. */ static void oct16550_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); else if (iir == IIR_BUSY) (void) uart_getreg(bas, REG_USR); uart_barrier(bas); iir = uart_getreg(bas, REG_IIR); } } static int delay_changed = 1; static int oct16550_delay (struct uart_bas *bas) { int divisor; u_char lcr; static int delay = 0; if (!delay_changed) return delay; delay_changed = 0; lcr = uart_getreg(bas, REG_LCR); uart_setreg(bas, REG_LCR, lcr | LCR_DLAB); uart_barrier(bas); divisor = uart_getreg(bas, REG_DLL) | (uart_getreg(bas, REG_DLH) << 8); uart_barrier(bas); uart_setreg(bas, REG_LCR, lcr); uart_barrier(bas); if(!bas->rclk) return 10; /* return an approx delay value */ /* 1/10th the time to transmit 1 character (estimate). */ if (divisor <= 134) return (16000000 * divisor / bas->rclk); return (16000 * divisor / (bas->rclk / 1000)); } static int oct16550_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 oct16550_drain (struct uart_bas *bas, int what) { int delay, limit; delay = oct16550_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*10*10*1024; while ((uart_getreg(bas, REG_LSR) & LSR_TEMT) == 0 && --limit) DELAY(delay); if (limit == 0) { /* printf("oct16550: transmitter appears stuck... "); */ return (0); } } 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("oct16550: 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 oct16550_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 oct16550_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) { divisor = oct16550_divisor(bas->rclk, baudrate); if (divisor == 0) return (EINVAL); uart_setreg(bas, REG_LCR, lcr | LCR_DLAB); uart_barrier(bas); uart_setreg(bas, REG_DLL, divisor & 0xff); uart_setreg(bas, REG_DLH, (divisor >> 8) & 0xff); uart_barrier(bas); delay_changed = 1; } /* Set LCR and clear DLAB. */ uart_setreg(bas, REG_LCR, lcr); uart_barrier(bas); return (0); } /* * Low-level UART interface. */ static int oct16550_probe(struct uart_bas *bas); static void oct16550_init(struct uart_bas *bas, int, int, int, int); static void oct16550_term(struct uart_bas *bas); static void oct16550_putc(struct uart_bas *bas, int); static int oct16550_rxready(struct uart_bas *bas); static int oct16550_getc(struct uart_bas *bas, struct mtx *); struct uart_ops uart_oct16550_ops = { .probe = oct16550_probe, .init = oct16550_init, .term = oct16550_term, .putc = oct16550_putc, .rxready = oct16550_rxready, .getc = oct16550_getc, }; static int oct16550_probe (struct uart_bas *bas) { u_char 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 & 0xc0) return (ENXIO); val = uart_getreg(bas, REG_USR); if (val & 0xe0) return (ENXIO); return (0); } static void oct16550_init (struct uart_bas *bas, int baudrate, int databits, int stopbits, int parity) { u_char ier; oct16550_param(bas, baudrate, databits, stopbits, parity); /* Disable all interrupt sources. */ ier = uart_getreg(bas, REG_IER) & 0x0; uart_setreg(bas, REG_IER, ier); 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_RTS | MCR_DTR); uart_barrier(bas); oct16550_clrint(bas); } static void oct16550_term (struct uart_bas *bas) { /* Clear RTS & DTR. */ uart_setreg(bas, REG_MCR, 0); uart_barrier(bas); } static inline void oct16550_wait_txhr_empty (struct uart_bas *bas, int limit, int delay) { while (((uart_getreg(bas, REG_LSR) & LSR_THRE) == 0) && ((uart_getreg(bas, REG_USR) & USR_TXFIFO_NOTFULL) == 0)) DELAY(delay); } static void oct16550_putc (struct uart_bas *bas, int c) { int delay; /* 1/10th the time to transmit 1 character (estimate). */ delay = oct16550_delay(bas); oct16550_wait_txhr_empty(bas, 100, delay); uart_setreg(bas, REG_DATA, c); uart_barrier(bas); oct16550_wait_txhr_empty(bas, 100, delay); } static int oct16550_rxready (struct uart_bas *bas) { return ((uart_getreg(bas, REG_LSR) & LSR_RXRDY) != 0 ? 1 : 0); } static int oct16550_getc (struct uart_bas *bas, struct mtx *hwmtx) { int c, delay; uart_lock(hwmtx); /* 1/10th the time to transmit 1 character (estimate). */ delay = oct16550_delay(bas); while ((uart_getreg(bas, REG_LSR) & LSR_RXRDY) == 0) { uart_unlock(hwmtx); DELAY(delay); uart_lock(hwmtx); } c = uart_getreg(bas, REG_DATA); uart_unlock(hwmtx); return (c); } /* * High-level UART interface. */ struct oct16550_softc { struct uart_softc base; uint8_t fcr; uint8_t ier; uint8_t mcr; }; static int oct16550_bus_attach(struct uart_softc *); static int oct16550_bus_detach(struct uart_softc *); static int oct16550_bus_flush(struct uart_softc *, int); static int oct16550_bus_getsig(struct uart_softc *); static int oct16550_bus_ioctl(struct uart_softc *, int, intptr_t); static int oct16550_bus_ipend(struct uart_softc *); static int oct16550_bus_param(struct uart_softc *, int, int, int, int); static int oct16550_bus_probe(struct uart_softc *); static int oct16550_bus_receive(struct uart_softc *); static int oct16550_bus_setsig(struct uart_softc *, int); static int oct16550_bus_transmit(struct uart_softc *); static void oct16550_bus_grab(struct uart_softc *); static void oct16550_bus_ungrab(struct uart_softc *); static kobj_method_t oct16550_methods[] = { KOBJMETHOD(uart_attach, oct16550_bus_attach), KOBJMETHOD(uart_detach, oct16550_bus_detach), KOBJMETHOD(uart_flush, oct16550_bus_flush), KOBJMETHOD(uart_getsig, oct16550_bus_getsig), KOBJMETHOD(uart_ioctl, oct16550_bus_ioctl), KOBJMETHOD(uart_ipend, oct16550_bus_ipend), KOBJMETHOD(uart_param, oct16550_bus_param), KOBJMETHOD(uart_probe, oct16550_bus_probe), KOBJMETHOD(uart_receive, oct16550_bus_receive), KOBJMETHOD(uart_setsig, oct16550_bus_setsig), KOBJMETHOD(uart_transmit, oct16550_bus_transmit), KOBJMETHOD(uart_grab, oct16550_bus_grab), KOBJMETHOD(uart_ungrab, oct16550_bus_ungrab), { 0, 0 } }; struct uart_class uart_oct16550_class = { "oct16550 class", oct16550_methods, sizeof(struct oct16550_softc), .uc_ops = &uart_oct16550_ops, .uc_range = 8 << 3, - .uc_rclk = 0 + .uc_rclk = 0, + .uc_rshift = 0 }; #define SIGCHG(c, i, s, d) \ if (c) { \ i |= (i & s) ? s : s | d; \ } else { \ i = (i & s) ? (i & ~s) | d : i; \ } static int oct16550_bus_attach (struct uart_softc *sc) { struct oct16550_softc *oct16550 = (struct oct16550_softc*)sc; struct uart_bas *bas; int unit; unit = device_get_unit(sc->sc_dev); bas = &sc->sc_bas; oct16550_drain(bas, UART_DRAIN_TRANSMITTER); oct16550->mcr = uart_getreg(bas, REG_MCR); oct16550->fcr = FCR_ENABLE | FCR_RX_HIGH; uart_setreg(bas, REG_FCR, oct16550->fcr); uart_barrier(bas); oct16550_bus_flush(sc, UART_FLUSH_RECEIVER|UART_FLUSH_TRANSMITTER); if (oct16550->mcr & MCR_DTR) sc->sc_hwsig |= SER_DTR; if (oct16550->mcr & MCR_RTS) sc->sc_hwsig |= SER_RTS; oct16550_bus_getsig(sc); oct16550_clrint(bas); oct16550->ier = uart_getreg(bas, REG_IER) & 0xf0; oct16550->ier |= IER_EMSC | IER_ERLS | IER_ERXRDY; uart_setreg(bas, REG_IER, oct16550->ier); uart_barrier(bas); return (0); } static int oct16550_bus_detach (struct uart_softc *sc) { struct uart_bas *bas; u_char ier; bas = &sc->sc_bas; ier = uart_getreg(bas, REG_IER) & 0xf0; uart_setreg(bas, REG_IER, ier); uart_barrier(bas); oct16550_clrint(bas); return (0); } static int oct16550_bus_flush (struct uart_softc *sc, int what) { struct oct16550_softc *oct16550 = (struct oct16550_softc*)sc; struct uart_bas *bas; int error; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); if (sc->sc_rxfifosz > 1) { oct16550_flush(bas, what); uart_setreg(bas, REG_FCR, oct16550->fcr); uart_barrier(bas); error = 0; } else error = oct16550_drain(bas, what); uart_unlock(sc->sc_hwmtx); return (error); } static int oct16550_bus_getsig (struct uart_softc *sc) { uint32_t new, old, sig; uint8_t msr; do { old = sc->sc_hwsig; sig = old; uart_lock(sc->sc_hwmtx); msr = uart_getreg(&sc->sc_bas, REG_MSR); uart_unlock(sc->sc_hwmtx); SIGCHG(msr & MSR_DSR, sig, SER_DSR, SER_DDSR); SIGCHG(msr & MSR_CTS, sig, SER_CTS, SER_DCTS); SIGCHG(msr & MSR_DCD, sig, SER_DCD, SER_DDCD); SIGCHG(msr & MSR_RI, sig, SER_RI, SER_DRI); new = sig & ~SER_MASK_DELTA; } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new)); return (sig); } static int oct16550_bus_ioctl (struct uart_softc *sc, int request, intptr_t data) { struct uart_bas *bas; int baudrate, divisor, error; uint8_t efr, lcr; bas = &sc->sc_bas; error = 0; uart_lock(sc->sc_hwmtx); 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; case UART_IOCTL_IFLOW: lcr = uart_getreg(bas, REG_LCR); uart_barrier(bas); uart_setreg(bas, REG_LCR, 0xbf); uart_barrier(bas); efr = uart_getreg(bas, REG_EFR); if (data) efr |= EFR_RTS; else efr &= ~EFR_RTS; uart_setreg(bas, REG_EFR, efr); uart_barrier(bas); uart_setreg(bas, REG_LCR, lcr); uart_barrier(bas); break; case UART_IOCTL_OFLOW: lcr = uart_getreg(bas, REG_LCR); uart_barrier(bas); uart_setreg(bas, REG_LCR, 0xbf); uart_barrier(bas); efr = uart_getreg(bas, REG_EFR); if (data) efr |= EFR_CTS; else efr &= ~EFR_CTS; uart_setreg(bas, REG_EFR, efr); uart_barrier(bas); uart_setreg(bas, REG_LCR, lcr); uart_barrier(bas); break; case UART_IOCTL_BAUD: lcr = uart_getreg(bas, REG_LCR); uart_setreg(bas, REG_LCR, lcr | LCR_DLAB); uart_barrier(bas); divisor = uart_getreg(bas, REG_DLL) | (uart_getreg(bas, REG_DLH) << 8); uart_barrier(bas); uart_setreg(bas, REG_LCR, lcr); uart_barrier(bas); baudrate = (divisor > 0) ? bas->rclk / divisor / 16 : 0; delay_changed = 1; if (baudrate > 0) *(int*)data = baudrate; else error = ENXIO; break; default: error = EINVAL; break; } uart_unlock(sc->sc_hwmtx); return (error); } static int oct16550_bus_ipend(struct uart_softc *sc) { struct uart_bas *bas; int ipend = 0; uint8_t iir, lsr; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); iir = uart_getreg(bas, REG_IIR) & IIR_IMASK; if (iir != IIR_NOPEND) { if (iir == IIR_RLS) { lsr = uart_getreg(bas, REG_LSR); if (lsr & LSR_OE) ipend |= SER_INT_OVERRUN; if (lsr & LSR_BI) ipend |= SER_INT_BREAK; if (lsr & LSR_RXRDY) ipend |= SER_INT_RXREADY; } else if (iir == IIR_RXRDY) { ipend |= SER_INT_RXREADY; } else if (iir == IIR_RXTOUT) { ipend |= SER_INT_RXREADY; } else if (iir == IIR_TXRDY) { ipend |= SER_INT_TXIDLE; } else if (iir == IIR_MLSC) { ipend |= SER_INT_SIGCHG; } else if (iir == IIR_BUSY) { (void) uart_getreg(bas, REG_USR); } } uart_unlock(sc->sc_hwmtx); return (ipend); } static int oct16550_bus_param (struct uart_softc *sc, int baudrate, int databits, int stopbits, int parity) { struct uart_bas *bas; int error; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); error = oct16550_param(bas, baudrate, databits, stopbits, parity); uart_unlock(sc->sc_hwmtx); return (error); } static int oct16550_bus_probe (struct uart_softc *sc) { struct uart_bas *bas; int error; bas = &sc->sc_bas; bas->rclk = uart_oct16550_class.uc_rclk = cvmx_clock_get_rate(CVMX_CLOCK_SCLK); error = oct16550_probe(bas); if (error) { return (error); } uart_setreg(bas, REG_MCR, (MCR_DTR | MCR_RTS)); /* * Enable FIFOs. And check that the UART has them. If not, we're * done. Since this is the first time we enable the FIFOs, we reset * them. */ oct16550_drain(bas, UART_DRAIN_TRANSMITTER); #define ENABLE_OCTEON_FIFO 1 #ifdef ENABLE_OCTEON_FIFO uart_setreg(bas, REG_FCR, FCR_ENABLE | FCR_XMT_RST | FCR_RCV_RST); #endif uart_barrier(bas); oct16550_flush(bas, UART_FLUSH_RECEIVER|UART_FLUSH_TRANSMITTER); if (device_get_unit(sc->sc_dev)) { device_set_desc(sc->sc_dev, "Octeon-16550 channel 1"); } else { device_set_desc(sc->sc_dev, "Octeon-16550 channel 0"); } #ifdef ENABLE_OCTEON_FIFO sc->sc_rxfifosz = 64; sc->sc_txfifosz = 64; #else sc->sc_rxfifosz = 1; sc->sc_txfifosz = 1; #endif #if 0 /* * XXX there are some issues related to hardware flow control and * it's likely that uart(4) is the cause. This basicly needs more * investigation, but we avoid using for hardware flow control * until then. */ /* 16650s or higher have automatic flow control. */ if (sc->sc_rxfifosz > 16) { sc->sc_hwiflow = 1; sc->sc_hwoflow = 1; } #endif return (0); } static int oct16550_bus_receive (struct uart_softc *sc) { struct uart_bas *bas; int xc; uint8_t lsr; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); lsr = uart_getreg(bas, REG_LSR); while (lsr & LSR_RXRDY) { if (uart_rx_full(sc)) { sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN; 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); lsr = uart_getreg(bas, REG_LSR); } /* Discard everything left in the Rx FIFO. */ /* * First do a dummy read/discard anyway, in case the UART was lying to us. * This problem was seen on board, when IIR said RBR, but LSR said no RXRDY * Results in a stuck ipend loop. */ (void)uart_getreg(bas, REG_DATA); while (lsr & LSR_RXRDY) { (void)uart_getreg(bas, REG_DATA); uart_barrier(bas); lsr = uart_getreg(bas, REG_LSR); } uart_unlock(sc->sc_hwmtx); return (0); } static int oct16550_bus_setsig (struct uart_softc *sc, int sig) { struct oct16550_softc *oct16550 = (struct oct16550_softc*)sc; struct uart_bas *bas; uint32_t new, old; bas = &sc->sc_bas; do { old = sc->sc_hwsig; new = old; if (sig & SER_DDTR) { SIGCHG(sig & SER_DTR, new, SER_DTR, SER_DDTR); } if (sig & SER_DRTS) { SIGCHG(sig & SER_RTS, new, SER_RTS, SER_DRTS); } } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new)); uart_lock(sc->sc_hwmtx); oct16550->mcr &= ~(MCR_DTR|MCR_RTS); if (new & SER_DTR) oct16550->mcr |= MCR_DTR; if (new & SER_RTS) oct16550->mcr |= MCR_RTS; uart_setreg(bas, REG_MCR, oct16550->mcr); uart_barrier(bas); uart_unlock(sc->sc_hwmtx); return (0); } static int oct16550_bus_transmit (struct uart_softc *sc) { struct oct16550_softc *oct16550 = (struct oct16550_softc*)sc; struct uart_bas *bas; int i; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); #ifdef NO_UART_INTERRUPTS for (i = 0; i < sc->sc_txdatasz; i++) { oct16550_putc(bas, sc->sc_txbuf[i]); } #else oct16550_wait_txhr_empty(bas, 100, oct16550_delay(bas)); uart_setreg(bas, REG_IER, oct16550->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; #endif uart_unlock(sc->sc_hwmtx); return (0); } static void oct16550_bus_grab(struct uart_softc *sc) { struct uart_bas *bas = &sc->sc_bas; /* * turn off all interrupts to enter polling mode. Leave the * saved mask alone. We'll restore whatever it was in ungrab. * All pending interupt signals are reset when IER is set to 0. */ uart_lock(sc->sc_hwmtx); uart_setreg(bas, REG_IER, 0); uart_barrier(bas); uart_unlock(sc->sc_hwmtx); } static void oct16550_bus_ungrab(struct uart_softc *sc) { struct oct16550_softc *oct16550 = (struct oct16550_softc*)sc; struct uart_bas *bas = &sc->sc_bas; /* * Restore previous interrupt mask */ uart_lock(sc->sc_hwmtx); uart_setreg(bas, REG_IER, oct16550->ier); uart_barrier(bas); uart_unlock(sc->sc_hwmtx); } Index: head/sys/mips/rt305x/uart_dev_rt305x.c =================================================================== --- head/sys/mips/rt305x/uart_dev_rt305x.c (revision 281437) +++ head/sys/mips/rt305x/uart_dev_rt305x.c (revision 281438) @@ -1,532 +1,533 @@ /* $NetBSD: uart.c,v 1.2 2007/03/23 20:05:47 dogcow Exp $ */ /*- * Copyright (c) 2010 Aleksandr Rybalko. * Copyright (c) 2007 Ruslan Ermilov and Vsevolod Lobko. * Copyright (c) 2007 Oleksandr Tymoshenko. * 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 AUTHORS ``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 AUTHORS * 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 __FBSDID("$FreeBSD$"); #include "opt_ddb.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "uart_if.h" /* * Low-level UART interface. */ static int rt305x_uart_probe(struct uart_bas *bas); static void rt305x_uart_init(struct uart_bas *bas, int, int, int, int); static void rt305x_uart_term(struct uart_bas *bas); static void rt305x_uart_putc(struct uart_bas *bas, int); static int rt305x_uart_rxready(struct uart_bas *bas); static int rt305x_uart_getc(struct uart_bas *bas, struct mtx *); static struct uart_ops uart_rt305x_uart_ops = { .probe = rt305x_uart_probe, .init = rt305x_uart_init, .term = rt305x_uart_term, .putc = rt305x_uart_putc, .rxready = rt305x_uart_rxready, .getc = rt305x_uart_getc, }; static int uart_output = 1; SYSCTL_INT(_kern, OID_AUTO, uart_output, CTLFLAG_RWTUN, &uart_output, 0, "UART output enabled."); static int rt305x_uart_probe(struct uart_bas *bas) { return (0); } static void rt305x_uart_init(struct uart_bas *bas, int baudrate, int databits, int stopbits, int parity) { #ifdef notyet /* CLKDIV = 384000000/ 3/ 16/ br */ /* for 384MHz CLKDIV = 8000000 / baudrate; */ switch (databits) { case 5: databits = UART_LCR_5B; break; case 6: databits = UART_LCR_6B; break; case 7: databits = UART_LCR_7B; break; case 8: databits = UART_LCR_8B; break; default: /* Unsupported */ return; } switch (parity) { case UART_PARITY_EVEN: parity = (UART_LCR_PEN|UART_LCR_EVEN); break; case UART_PARITY_NONE: parity = (UART_LCR_PEN); break; case UART_PARITY_ODD: parity = 0; break; /* Unsupported */ default: return; } uart_setreg(bas, UART_CDDL_REG, 8000000/baudrate); uart_barrier(bas); uart_setreg(bas, UART_LCR_REG, databits | (stopbits==1?0:4) | parity); uart_barrier(bas); #endif } static void rt305x_uart_term(struct uart_bas *bas) { uart_setreg(bas, UART_MCR_REG, 0); uart_barrier(bas); } static void rt305x_uart_putc(struct uart_bas *bas, int c) { char chr; if (!uart_output) return; chr = c; while (!(uart_getreg(bas, UART_LSR_REG) & UART_LSR_THRE)); uart_setreg(bas, UART_TX_REG, c); uart_barrier(bas); while (!(uart_getreg(bas, UART_LSR_REG) & UART_LSR_THRE)); } static int rt305x_uart_rxready(struct uart_bas *bas) { #ifdef notyet if (uart_getreg(bas, UART_LSR_REG) & UART_LSR_DR) return (1); return (0); #else return (1); #endif } static int rt305x_uart_getc(struct uart_bas *bas, struct mtx *hwmtx) { int c; uart_lock(hwmtx); while (!(uart_getreg(bas, UART_LSR_REG) & UART_LSR_DR)) { uart_unlock(hwmtx); DELAY(10); uart_lock(hwmtx); } c = uart_getreg(bas, UART_RX_REG); uart_unlock(hwmtx); return (c); } /* * High-level UART interface. */ struct rt305x_uart_softc { struct uart_softc base; }; static int rt305x_uart_bus_attach(struct uart_softc *); static int rt305x_uart_bus_detach(struct uart_softc *); static int rt305x_uart_bus_flush(struct uart_softc *, int); static int rt305x_uart_bus_getsig(struct uart_softc *); static int rt305x_uart_bus_ioctl(struct uart_softc *, int, intptr_t); static int rt305x_uart_bus_ipend(struct uart_softc *); static int rt305x_uart_bus_param(struct uart_softc *, int, int, int, int); static int rt305x_uart_bus_probe(struct uart_softc *); static int rt305x_uart_bus_receive(struct uart_softc *); static int rt305x_uart_bus_setsig(struct uart_softc *, int); static int rt305x_uart_bus_transmit(struct uart_softc *); static void rt305x_uart_bus_grab(struct uart_softc *); static void rt305x_uart_bus_ungrab(struct uart_softc *); static kobj_method_t rt305x_uart_methods[] = { KOBJMETHOD(uart_attach, rt305x_uart_bus_attach), KOBJMETHOD(uart_detach, rt305x_uart_bus_detach), KOBJMETHOD(uart_flush, rt305x_uart_bus_flush), KOBJMETHOD(uart_getsig, rt305x_uart_bus_getsig), KOBJMETHOD(uart_ioctl, rt305x_uart_bus_ioctl), KOBJMETHOD(uart_ipend, rt305x_uart_bus_ipend), KOBJMETHOD(uart_param, rt305x_uart_bus_param), KOBJMETHOD(uart_probe, rt305x_uart_bus_probe), KOBJMETHOD(uart_receive, rt305x_uart_bus_receive), KOBJMETHOD(uart_setsig, rt305x_uart_bus_setsig), KOBJMETHOD(uart_transmit, rt305x_uart_bus_transmit), KOBJMETHOD(uart_grab, rt305x_uart_bus_grab), KOBJMETHOD(uart_ungrab, rt305x_uart_bus_ungrab), { 0, 0 } }; struct uart_class uart_rt305x_uart_class = { "rt305x", rt305x_uart_methods, sizeof(struct rt305x_uart_softc), .uc_ops = &uart_rt305x_uart_ops, .uc_range = 1, /* use hinted range */ - .uc_rclk = SYSTEM_CLOCK + .uc_rclk = SYSTEM_CLOCK, + .uc_rshift = 0 }; #define SIGCHG(c, i, s, d) \ if (c) { \ i |= (i & s) ? s : s | d; \ } else { \ i = (i & s) ? (i & ~s) | d : i; \ } /* * Disable TX interrupt. uart should be locked */ static __inline void rt305x_uart_disable_txintr(struct uart_softc *sc) { struct uart_bas *bas = &sc->sc_bas; uint8_t cr; cr = uart_getreg(bas, UART_IER_REG); cr &= ~UART_IER_ETBEI; uart_setreg(bas, UART_IER_REG, cr); uart_barrier(bas); } /* * Enable TX interrupt. uart should be locked */ static __inline void rt305x_uart_enable_txintr(struct uart_softc *sc) { struct uart_bas *bas = &sc->sc_bas; uint8_t cr; cr = uart_getreg(bas, UART_IER_REG); cr |= UART_IER_ETBEI; uart_setreg(bas, UART_IER_REG, cr); uart_barrier(bas); } static int rt305x_uart_bus_attach(struct uart_softc *sc) { struct uart_bas *bas; struct uart_devinfo *di; bas = &sc->sc_bas; if (sc->sc_sysdev != NULL) { di = sc->sc_sysdev; rt305x_uart_init(bas, di->baudrate, di->databits, di->stopbits, di->parity); } else { rt305x_uart_init(bas, 115200, 8, 1, 0); } (void)rt305x_uart_bus_getsig(sc); /* Enable FIFO */ uart_setreg(bas, UART_FCR_REG, uart_getreg(bas, UART_FCR_REG) | UART_FCR_FIFOEN | UART_FCR_TXTGR_1 | UART_FCR_RXTGR_1); uart_barrier(bas); /* Enable interrupts */ uart_setreg(bas, UART_IER_REG, UART_IER_EDSSI | UART_IER_ELSI | UART_IER_ERBFI); uart_barrier(bas); return (0); } static int rt305x_uart_bus_detach(struct uart_softc *sc) { return (0); } static int rt305x_uart_bus_flush(struct uart_softc *sc, int what) { struct uart_bas *bas = &sc->sc_bas; uint32_t fcr = uart_getreg(bas, UART_FCR_REG); if (what & UART_FLUSH_TRANSMITTER) { uart_setreg(bas, UART_FCR_REG, fcr|UART_FCR_TXRST); uart_barrier(bas); } if (what & UART_FLUSH_RECEIVER) { uart_setreg(bas, UART_FCR_REG, fcr|UART_FCR_RXRST); uart_barrier(bas); } uart_setreg(bas, UART_FCR_REG, fcr); uart_barrier(bas); return (0); } static int rt305x_uart_bus_getsig(struct uart_softc *sc) { uint32_t new, old, sig; uint8_t bes; do { old = sc->sc_hwsig; sig = old; uart_lock(sc->sc_hwmtx); bes = uart_getreg(&sc->sc_bas, UART_MSR_REG); uart_unlock(sc->sc_hwmtx); /* XXX: chip can show delta */ SIGCHG(bes & UART_MSR_CTS, sig, SER_CTS, SER_DCTS); SIGCHG(bes & UART_MSR_DCD, sig, SER_DCD, SER_DDCD); SIGCHG(bes & UART_MSR_DSR, sig, SER_DSR, SER_DDSR); new = sig & ~SER_MASK_DELTA; } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new)); return (sig); } static int rt305x_uart_bus_ioctl(struct uart_softc *sc, int request, intptr_t data) { struct uart_bas *bas; int baudrate, divisor, error; bas = &sc->sc_bas; error = 0; uart_lock(sc->sc_hwmtx); switch (request) { case UART_IOCTL_BREAK: /* TODO: Send BREAK */ break; case UART_IOCTL_BAUD: divisor = uart_getreg(bas, UART_CDDL_REG); baudrate = bas->rclk / (divisor * 16); *(int*)data = baudrate; break; default: error = EINVAL; break; } uart_unlock(sc->sc_hwmtx); return (error); } static int rt305x_uart_bus_ipend(struct uart_softc *sc) { struct uart_bas *bas; int ipend; uint8_t iir, lsr, msr; bas = &sc->sc_bas; ipend = 0; uart_lock(sc->sc_hwmtx); iir = uart_getreg(&sc->sc_bas, UART_IIR_REG); lsr = uart_getreg(&sc->sc_bas, UART_LSR_REG); uart_setreg(&sc->sc_bas, UART_LSR_REG, lsr); msr = uart_getreg(&sc->sc_bas, UART_MSR_REG); uart_setreg(&sc->sc_bas, UART_MSR_REG, msr); if (iir & UART_IIR_INTP) { uart_unlock(sc->sc_hwmtx); return (0); } switch ((iir >> 1) & 0x07) { case UART_IIR_ID_THRE: ipend |= SER_INT_TXIDLE; break; case UART_IIR_ID_DR2: rt305x_uart_bus_flush(sc, UART_FLUSH_RECEIVER); /* passthrough */ case UART_IIR_ID_DR: ipend |= SER_INT_RXREADY; break; case UART_IIR_ID_MST: case UART_IIR_ID_LINESTATUS: ipend |= SER_INT_SIGCHG; if (lsr & UART_LSR_BI) { ipend |= SER_INT_BREAK; #ifdef KDB breakpoint(); #endif } if (lsr & UART_LSR_OE) ipend |= SER_INT_OVERRUN; break; default: /* XXX: maybe return error here */ break; } uart_unlock(sc->sc_hwmtx); return (ipend); } static int rt305x_uart_bus_param(struct uart_softc *sc, int baudrate, int databits, int stopbits, int parity) { uart_lock(sc->sc_hwmtx); rt305x_uart_init(&sc->sc_bas, baudrate, databits, stopbits, parity); uart_unlock(sc->sc_hwmtx); return (0); } static int rt305x_uart_bus_probe(struct uart_softc *sc) { char buf[80]; int error; error = rt305x_uart_probe(&sc->sc_bas); if (error) return (error); sc->sc_rxfifosz = 16; sc->sc_txfifosz = 16; snprintf(buf, sizeof(buf), "rt305x_uart"); device_set_desc_copy(sc->sc_dev, buf); return (0); } static int rt305x_uart_bus_receive(struct uart_softc *sc) { struct uart_bas *bas; int xc; uint8_t lsr; bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); lsr = uart_getreg(bas, UART_LSR_REG); while ((lsr & UART_LSR_DR)) { if (uart_rx_full(sc)) { sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN; break; } xc = 0; xc = uart_getreg(bas, UART_RX_REG); if (lsr & UART_LSR_FE) xc |= UART_STAT_FRAMERR; if (lsr & UART_LSR_PE) xc |= UART_STAT_PARERR; if (lsr & UART_LSR_OE) xc |= UART_STAT_OVERRUN; uart_barrier(bas); uart_rx_put(sc, xc); lsr = uart_getreg(bas, UART_LSR_REG); } uart_unlock(sc->sc_hwmtx); return (0); } static int rt305x_uart_bus_setsig(struct uart_softc *sc, int sig) { /* TODO: implement (?) */ return (0); } static int rt305x_uart_bus_transmit(struct uart_softc *sc) { struct uart_bas *bas = &sc->sc_bas; int i; if (!uart_output) return (0); bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); while ((uart_getreg(bas, UART_LSR_REG) & UART_LSR_THRE) == 0) ; rt305x_uart_enable_txintr(sc); for (i = 0; i < sc->sc_txdatasz; i++) { uart_setreg(bas, UART_TX_REG, sc->sc_txbuf[i]); uart_barrier(bas); } sc->sc_txbusy = 1; uart_unlock(sc->sc_hwmtx); return (0); } static void rt305x_uart_bus_grab(struct uart_softc *sc) { struct uart_bas *bas = &sc->sc_bas; /* disable interrupts -- XXX not sure which one is RX, so kill them all */ uart_lock(sc->sc_hwmtx); uart_setreg(bas, UART_IER_REG, 0); uart_barrier(bas); uart_unlock(sc->sc_hwmtx); } static void rt305x_uart_bus_ungrab(struct uart_softc *sc) { struct uart_bas *bas = &sc->sc_bas; /* Enable interrupts */ uart_lock(sc->sc_hwmtx); uart_setreg(bas, UART_IER_REG, UART_IER_EDSSI | UART_IER_ELSI | UART_IER_ERBFI); uart_barrier(bas); uart_unlock(sc->sc_hwmtx); } Index: head/sys/sparc64/pci/sbbc.c =================================================================== --- head/sys/sparc64/pci/sbbc.c (revision 281437) +++ head/sys/sparc64/pci/sbbc.c (revision 281438) @@ -1,1110 +1,1111 @@ /* $OpenBSD: sbbc.c,v 1.7 2009/11/09 17:53:39 nicm Exp $ */ /*- * Copyright (c) 2008 Mark Kettenis * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /*- * Copyright (c) 2010 Marius Strobl * 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 AND CONTRIBUTORS ``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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "clock_if.h" #include "uart_if.h" #define SBBC_PCI_BAR PCIR_BAR(0) #define SBBC_PCI_VENDOR 0x108e #define SBBC_PCI_PRODUCT 0xc416 #define SBBC_REGS_OFFSET 0x800000 #define SBBC_REGS_SIZE 0x6230 #define SBBC_EPLD_OFFSET 0x8e0000 #define SBBC_EPLD_SIZE 0x20 #define SBBC_SRAM_OFFSET 0x900000 #define SBBC_SRAM_SIZE 0x20000 /* 128KB SRAM */ #define SBBC_PCI_INT_STATUS 0x2320 #define SBBC_PCI_INT_ENABLE 0x2330 #define SBBC_PCI_ENABLE_INT_A 0x11 #define SBBC_EPLD_INTERRUPT 0x13 #define SBBC_EPLD_INTERRUPT_ON 0x01 #define SBBC_SRAM_CONS_IN 0x00000001 #define SBBC_SRAM_CONS_OUT 0x00000002 #define SBBC_SRAM_CONS_BRK 0x00000004 #define SBBC_SRAM_CONS_SPACE_IN 0x00000008 #define SBBC_SRAM_CONS_SPACE_OUT 0x00000010 #define SBBC_TAG_KEY_SIZE 8 #define SBBC_TAG_KEY_SCSOLIE "SCSOLIE" /* SC -> OS int. enable */ #define SBBC_TAG_KEY_SCSOLIR "SCSOLIR" /* SC -> OS int. reason */ #define SBBC_TAG_KEY_SOLCONS "SOLCONS" /* OS console buffer */ #define SBBC_TAG_KEY_SOLSCIE "SOLSCIE" /* OS -> SC int. enable */ #define SBBC_TAG_KEY_SOLSCIR "SOLSCIR" /* OS -> SC int. reason */ #define SBBC_TAG_KEY_TODDATA "TODDATA" /* OS TOD struct */ #define SBBC_TAG_OFF(x) offsetof(struct sbbc_sram_tag, x) struct sbbc_sram_tag { char tag_key[SBBC_TAG_KEY_SIZE]; uint32_t tag_size; uint32_t tag_offset; } __packed; #define SBBC_TOC_MAGIC "TOCSRAM" #define SBBC_TOC_MAGIC_SIZE 8 #define SBBC_TOC_TAGS_MAX 32 #define SBBC_TOC_OFF(x) offsetof(struct sbbc_sram_toc, x) struct sbbc_sram_toc { char toc_magic[SBBC_TOC_MAGIC_SIZE]; uint8_t toc_reserved; uint8_t toc_type; uint16_t toc_version; uint32_t toc_ntags; struct sbbc_sram_tag toc_tag[SBBC_TOC_TAGS_MAX]; } __packed; #define SBBC_TOD_MAGIC 0x54443100 /* "TD1" */ #define SBBC_TOD_VERSION 1 #define SBBC_TOD_OFF(x) offsetof(struct sbbc_sram_tod, x) struct sbbc_sram_tod { uint32_t tod_magic; uint32_t tod_version; uint64_t tod_time; uint64_t tod_skew; uint32_t tod_reserved; uint32_t tod_heartbeat; uint32_t tod_timeout; } __packed; #define SBBC_CONS_MAGIC 0x434f4e00 /* "CON" */ #define SBBC_CONS_VERSION 1 #define SBBC_CONS_OFF(x) offsetof(struct sbbc_sram_cons, x) struct sbbc_sram_cons { uint32_t cons_magic; uint32_t cons_version; uint32_t cons_size; uint32_t cons_in_begin; uint32_t cons_in_end; uint32_t cons_in_rdptr; uint32_t cons_in_wrptr; uint32_t cons_out_begin; uint32_t cons_out_end; uint32_t cons_out_rdptr; uint32_t cons_out_wrptr; } __packed; struct sbbc_softc { struct resource *sc_res; }; #define SBBC_READ_N(wdth, offs) \ bus_space_read_ ## wdth((bst), (bsh), (offs)) #define SBBC_WRITE_N(wdth, offs, val) \ bus_space_write_ ## wdth((bst), (bsh), (offs), (val)) #define SBBC_READ_1(offs) \ SBBC_READ_N(1, (offs)) #define SBBC_READ_2(offs) \ bswap16(SBBC_READ_N(2, (offs))) #define SBBC_READ_4(offs) \ bswap32(SBBC_READ_N(4, (offs))) #define SBBC_READ_8(offs) \ bswap64(SBBC_READ_N(8, (offs))) #define SBBC_WRITE_1(offs, val) \ SBBC_WRITE_N(1, (offs), (val)) #define SBBC_WRITE_2(offs, val) \ SBBC_WRITE_N(2, (offs), bswap16(val)) #define SBBC_WRITE_4(offs, val) \ SBBC_WRITE_N(4, (offs), bswap32(val)) #define SBBC_WRITE_8(offs, val) \ SBBC_WRITE_N(8, (offs), bswap64(val)) #define SBBC_REGS_READ_1(offs) \ SBBC_READ_1((offs) + SBBC_REGS_OFFSET) #define SBBC_REGS_READ_2(offs) \ SBBC_READ_2((offs) + SBBC_REGS_OFFSET) #define SBBC_REGS_READ_4(offs) \ SBBC_READ_4((offs) + SBBC_REGS_OFFSET) #define SBBC_REGS_READ_8(offs) \ SBBC_READ_8((offs) + SBBC_REGS_OFFSET) #define SBBC_REGS_WRITE_1(offs, val) \ SBBC_WRITE_1((offs) + SBBC_REGS_OFFSET, (val)) #define SBBC_REGS_WRITE_2(offs, val) \ SBBC_WRITE_2((offs) + SBBC_REGS_OFFSET, (val)) #define SBBC_REGS_WRITE_4(offs, val) \ SBBC_WRITE_4((offs) + SBBC_REGS_OFFSET, (val)) #define SBBC_REGS_WRITE_8(offs, val) \ SBBC_WRITE_8((offs) + SBBC_REGS_OFFSET, (val)) #define SBBC_EPLD_READ_1(offs) \ SBBC_READ_1((offs) + SBBC_EPLD_OFFSET) #define SBBC_EPLD_READ_2(offs) \ SBBC_READ_2((offs) + SBBC_EPLD_OFFSET) #define SBBC_EPLD_READ_4(offs) \ SBBC_READ_4((offs) + SBBC_EPLD_OFFSET) #define SBBC_EPLD_READ_8(offs) \ SBBC_READ_8((offs) + SBBC_EPLD_OFFSET) #define SBBC_EPLD_WRITE_1(offs, val) \ SBBC_WRITE_1((offs) + SBBC_EPLD_OFFSET, (val)) #define SBBC_EPLD_WRITE_2(offs, val) \ SBBC_WRITE_2((offs) + SBBC_EPLD_OFFSET, (val)) #define SBBC_EPLD_WRITE_4(offs, val) \ SBBC_WRITE_4((offs) + SBBC_EPLD_OFFSET, (val)) #define SBBC_EPLD_WRITE_8(offs, val) \ SBBC_WRITE_8((offs) + SBBC_EPLD_OFFSET, (val)) #define SBBC_SRAM_READ_1(offs) \ SBBC_READ_1((offs) + SBBC_SRAM_OFFSET) #define SBBC_SRAM_READ_2(offs) \ SBBC_READ_2((offs) + SBBC_SRAM_OFFSET) #define SBBC_SRAM_READ_4(offs) \ SBBC_READ_4((offs) + SBBC_SRAM_OFFSET) #define SBBC_SRAM_READ_8(offs) \ SBBC_READ_8((offs) + SBBC_SRAM_OFFSET) #define SBBC_SRAM_WRITE_1(offs, val) \ SBBC_WRITE_1((offs) + SBBC_SRAM_OFFSET, (val)) #define SBBC_SRAM_WRITE_2(offs, val) \ SBBC_WRITE_2((offs) + SBBC_SRAM_OFFSET, (val)) #define SBBC_SRAM_WRITE_4(offs, val) \ SBBC_WRITE_4((offs) + SBBC_SRAM_OFFSET, (val)) #define SBBC_SRAM_WRITE_8(offs, val) \ SBBC_WRITE_8((offs) + SBBC_SRAM_OFFSET, (val)) #define SUNW_SETCONSINPUT "SUNW,set-console-input" #define SUNW_SETCONSINPUT_CLNT "CON_CLNT" #define SUNW_SETCONSINPUT_OBP "CON_OBP" static u_int sbbc_console; static uint32_t sbbc_scsolie; static uint32_t sbbc_scsolir; static uint32_t sbbc_solcons; static uint32_t sbbc_solscie; static uint32_t sbbc_solscir; static uint32_t sbbc_toddata; /* * internal helpers */ static int sbbc_parse_toc(bus_space_tag_t bst, bus_space_handle_t bsh); static inline void sbbc_send_intr(bus_space_tag_t bst, bus_space_handle_t bsh); static const char *sbbc_serengeti_set_console_input(char *new); /* * SBBC PCI interface */ static bus_activate_resource_t sbbc_bus_activate_resource; static bus_adjust_resource_t sbbc_bus_adjust_resource; static bus_deactivate_resource_t sbbc_bus_deactivate_resource; static bus_alloc_resource_t sbbc_bus_alloc_resource; static bus_release_resource_t sbbc_bus_release_resource; static bus_get_resource_list_t sbbc_bus_get_resource_list; static bus_setup_intr_t sbbc_bus_setup_intr; static bus_teardown_intr_t sbbc_bus_teardown_intr; static device_attach_t sbbc_pci_attach; static device_probe_t sbbc_pci_probe; static clock_gettime_t sbbc_tod_gettime; static clock_settime_t sbbc_tod_settime; static device_method_t sbbc_pci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, sbbc_pci_probe), DEVMETHOD(device_attach, sbbc_pci_attach), DEVMETHOD(bus_alloc_resource, sbbc_bus_alloc_resource), DEVMETHOD(bus_activate_resource,sbbc_bus_activate_resource), DEVMETHOD(bus_deactivate_resource,sbbc_bus_deactivate_resource), DEVMETHOD(bus_adjust_resource, sbbc_bus_adjust_resource), DEVMETHOD(bus_release_resource, sbbc_bus_release_resource), DEVMETHOD(bus_setup_intr, sbbc_bus_setup_intr), DEVMETHOD(bus_teardown_intr, sbbc_bus_teardown_intr), DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), DEVMETHOD(bus_get_resource_list, sbbc_bus_get_resource_list), /* clock interface */ DEVMETHOD(clock_gettime, sbbc_tod_gettime), DEVMETHOD(clock_settime, sbbc_tod_settime), DEVMETHOD_END }; static devclass_t sbbc_devclass; DEFINE_CLASS_0(sbbc, sbbc_driver, sbbc_pci_methods, sizeof(struct sbbc_softc)); DRIVER_MODULE(sbbc, pci, sbbc_driver, sbbc_devclass, NULL, NULL); static int sbbc_pci_probe(device_t dev) { if (pci_get_vendor(dev) == SBBC_PCI_VENDOR && pci_get_device(dev) == SBBC_PCI_PRODUCT) { device_set_desc(dev, "Sun BootBus controller"); return (BUS_PROBE_DEFAULT); } return (ENXIO); } static int sbbc_pci_attach(device_t dev) { struct sbbc_softc *sc; struct timespec ts; device_t child; bus_space_tag_t bst; bus_space_handle_t bsh; phandle_t node; int error, rid; uint32_t val; /* Nothing to to if we're not the chosen one. */ if ((node = OF_finddevice("/chosen")) == -1) { device_printf(dev, "failed to find /chosen\n"); return (ENXIO); } if (OF_getprop(node, "iosram", &node, sizeof(node)) == -1) { device_printf(dev, "failed to get iosram\n"); return (ENXIO); } if (node != ofw_bus_get_node(dev)) return (0); sc = device_get_softc(dev); rid = SBBC_PCI_BAR; sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->sc_res == NULL) { device_printf(dev, "failed to allocate resources\n"); return (ENXIO); } bst = rman_get_bustag(sc->sc_res); bsh = rman_get_bushandle(sc->sc_res); if (sbbc_console != 0) { /* Once again the interrupt pin isn't set. */ if (pci_get_intpin(dev) == 0) pci_set_intpin(dev, 1); child = device_add_child(dev, NULL, -1); if (child == NULL) device_printf(dev, "failed to add UART device\n"); error = bus_generic_attach(dev); if (error != 0) device_printf(dev, "failed to attach UART device\n"); } else { error = sbbc_parse_toc(bst, bsh); if (error != 0) { device_printf(dev, "failed to parse TOC\n"); if (sbbc_console != 0) { bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->sc_res); return (error); } } } if (sbbc_toddata != 0) { if ((val = SBBC_SRAM_READ_4(sbbc_toddata + SBBC_TOD_OFF(tod_magic))) != SBBC_TOD_MAGIC) device_printf(dev, "invalid TOD magic %#x\n", val); else if ((val = SBBC_SRAM_READ_4(sbbc_toddata + SBBC_TOD_OFF(tod_version))) < SBBC_TOD_VERSION) device_printf(dev, "invalid TOD version %#x\n", val); else { clock_register(dev, 1000000); /* 1 sec. resolution */ if (bootverbose) { sbbc_tod_gettime(dev, &ts); device_printf(dev, "current time: %ld.%09ld\n", (long)ts.tv_sec, ts.tv_nsec); } } } return (0); } /* * Note that the bus methods don't pass-through the uart(4) requests but act * as if they would come from sbbc(4) in order to avoid complications with * pci(4) (actually, uart(4) isn't a real child but rather a function of * sbbc(4) anyway). */ static struct resource * sbbc_bus_alloc_resource(device_t dev, device_t child __unused, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { struct sbbc_softc *sc; sc = device_get_softc(dev); switch (type) { case SYS_RES_IRQ: return (bus_generic_alloc_resource(dev, dev, type, rid, start, end, count, flags)); case SYS_RES_MEMORY: return (sc->sc_res); default: return (NULL); } } static int sbbc_bus_activate_resource(device_t bus, device_t child, int type, int rid, struct resource *res) { if (type == SYS_RES_MEMORY) return (0); return (bus_generic_activate_resource(bus, child, type, rid, res)); } static int sbbc_bus_deactivate_resource(device_t bus, device_t child, int type, int rid, struct resource *res) { if (type == SYS_RES_MEMORY) return (0); return (bus_generic_deactivate_resource(bus, child, type, rid, res)); } static int sbbc_bus_adjust_resource(device_t bus __unused, device_t child __unused, int type __unused, struct resource *res __unused, u_long start __unused, u_long end __unused) { return (ENXIO); } static int sbbc_bus_release_resource(device_t dev, device_t child __unused, int type, int rid, struct resource *res) { if (type == SYS_RES_IRQ) return (bus_generic_release_resource(dev, dev, type, rid, res)); return (0); } static struct resource_list * sbbc_bus_get_resource_list(device_t dev, device_t child __unused) { return (bus_generic_get_resource_list(dev, dev)); } static int sbbc_bus_setup_intr(device_t dev, device_t child __unused, struct resource *res, int flags, driver_filter_t *filt, driver_intr_t *intr, void *arg, void **cookiep) { return (bus_generic_setup_intr(dev, dev, res, flags, filt, intr, arg, cookiep)); } static int sbbc_bus_teardown_intr(device_t dev, device_t child __unused, struct resource *res, void *cookie) { return (bus_generic_teardown_intr(dev, dev, res, cookie)); } /* * internal helpers */ static int sbbc_parse_toc(bus_space_tag_t bst, bus_space_handle_t bsh) { char buf[MAX(SBBC_TAG_KEY_SIZE, SBBC_TOC_MAGIC_SIZE)]; bus_size_t tag; phandle_t node; uint32_t off, sram_toc; u_int i, tags; if ((node = OF_finddevice("/chosen")) == -1) return (ENXIO); /* SRAM TOC offset defaults to 0. */ if (OF_getprop(node, "iosram-toc", &sram_toc, sizeof(sram_toc)) <= 0) sram_toc = 0; bus_space_read_region_1(bst, bsh, SBBC_SRAM_OFFSET + sram_toc + SBBC_TOC_OFF(toc_magic), buf, SBBC_TOC_MAGIC_SIZE); buf[SBBC_TOC_MAGIC_SIZE - 1] = '\0'; if (strcmp(buf, SBBC_TOC_MAGIC) != 0) return (ENXIO); tags = SBBC_SRAM_READ_4(sram_toc + SBBC_TOC_OFF(toc_ntags)); for (i = 0; i < tags; i++) { tag = sram_toc + SBBC_TOC_OFF(toc_tag) + i * sizeof(struct sbbc_sram_tag); bus_space_read_region_1(bst, bsh, SBBC_SRAM_OFFSET + tag + SBBC_TAG_OFF(tag_key), buf, SBBC_TAG_KEY_SIZE); buf[SBBC_TAG_KEY_SIZE - 1] = '\0'; off = SBBC_SRAM_READ_4(tag + SBBC_TAG_OFF(tag_offset)); if (strcmp(buf, SBBC_TAG_KEY_SCSOLIE) == 0) sbbc_scsolie = off; else if (strcmp(buf, SBBC_TAG_KEY_SCSOLIR) == 0) sbbc_scsolir = off; else if (strcmp(buf, SBBC_TAG_KEY_SOLCONS) == 0) sbbc_solcons = off; else if (strcmp(buf, SBBC_TAG_KEY_SOLSCIE) == 0) sbbc_solscie = off; else if (strcmp(buf, SBBC_TAG_KEY_SOLSCIR) == 0) sbbc_solscir = off; else if (strcmp(buf, SBBC_TAG_KEY_TODDATA) == 0) sbbc_toddata = off; } return (0); } static const char * sbbc_serengeti_set_console_input(char *new) { struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t new; cell_t old; } args = { (cell_t)SUNW_SETCONSINPUT, 1, 1, }; args.new = (cell_t)new; if (ofw_entry(&args) == -1) return (NULL); return ((const char *)args.old); } static inline void sbbc_send_intr(bus_space_tag_t bst, bus_space_handle_t bsh) { SBBC_EPLD_WRITE_1(SBBC_EPLD_INTERRUPT, SBBC_EPLD_INTERRUPT_ON); bus_space_barrier(bst, bsh, SBBC_EPLD_OFFSET + SBBC_EPLD_INTERRUPT, 1, BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); } /* * TOD interface */ static int sbbc_tod_gettime(device_t dev, struct timespec *ts) { struct sbbc_softc *sc; bus_space_tag_t bst; bus_space_handle_t bsh; sc = device_get_softc(dev); bst = rman_get_bustag(sc->sc_res); bsh = rman_get_bushandle(sc->sc_res); ts->tv_sec = SBBC_SRAM_READ_8(sbbc_toddata + SBBC_TOD_OFF(tod_time)) + SBBC_SRAM_READ_8(sbbc_toddata + SBBC_TOD_OFF(tod_skew)); ts->tv_nsec = 0; return (0); } static int sbbc_tod_settime(device_t dev, struct timespec *ts) { struct sbbc_softc *sc; bus_space_tag_t bst; bus_space_handle_t bsh; sc = device_get_softc(dev); bst = rman_get_bustag(sc->sc_res); bsh = rman_get_bushandle(sc->sc_res); SBBC_SRAM_WRITE_8(sbbc_toddata + SBBC_TOD_OFF(tod_skew), ts->tv_sec - SBBC_SRAM_READ_8(sbbc_toddata + SBBC_TOD_OFF(tod_time))); return (0); } /* * UART bus front-end */ static device_probe_t sbbc_uart_sbbc_probe; static device_method_t sbbc_uart_sbbc_methods[] = { /* Device interface */ DEVMETHOD(device_probe, sbbc_uart_sbbc_probe), DEVMETHOD(device_attach, uart_bus_attach), DEVMETHOD(device_detach, uart_bus_detach), DEVMETHOD_END }; DEFINE_CLASS_0(uart, sbbc_uart_driver, sbbc_uart_sbbc_methods, sizeof(struct uart_softc)); DRIVER_MODULE(uart, sbbc, sbbc_uart_driver, uart_devclass, NULL, NULL); static int sbbc_uart_sbbc_probe(device_t dev) { struct uart_softc *sc; sc = device_get_softc(dev); sc->sc_class = &uart_sbbc_class; device_set_desc(dev, "Serengeti console"); return (uart_bus_probe(dev, 0, 0, SBBC_PCI_BAR, 0)); } /* * Low-level UART interface */ static int sbbc_uart_probe(struct uart_bas *bas); static void sbbc_uart_init(struct uart_bas *bas, int baudrate, int databits, int stopbits, int parity); static void sbbc_uart_term(struct uart_bas *bas); static void sbbc_uart_putc(struct uart_bas *bas, int c); static int sbbc_uart_rxready(struct uart_bas *bas); static int sbbc_uart_getc(struct uart_bas *bas, struct mtx *hwmtx); static struct uart_ops sbbc_uart_ops = { .probe = sbbc_uart_probe, .init = sbbc_uart_init, .term = sbbc_uart_term, .putc = sbbc_uart_putc, .rxready = sbbc_uart_rxready, .getc = sbbc_uart_getc, }; static int sbbc_uart_probe(struct uart_bas *bas) { bus_space_tag_t bst; bus_space_handle_t bsh; int error; sbbc_console = 1; bst = bas->bst; bsh = bas->bsh; error = sbbc_parse_toc(bst, bsh); if (error != 0) return (error); if (sbbc_scsolie == 0 || sbbc_scsolir == 0 || sbbc_solcons == 0 || sbbc_solscie == 0 || sbbc_solscir == 0) return (ENXIO); if (SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_magic)) != SBBC_CONS_MAGIC || SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_version)) < SBBC_CONS_VERSION) return (ENXIO); return (0); } static void sbbc_uart_init(struct uart_bas *bas, int baudrate __unused, int databits __unused, int stopbits __unused, int parity __unused) { bus_space_tag_t bst; bus_space_handle_t bsh; bst = bas->bst; bsh = bas->bsh; /* Enable output to and space in from the SC interrupts. */ SBBC_SRAM_WRITE_4(sbbc_solscie, SBBC_SRAM_READ_4(sbbc_solscie) | SBBC_SRAM_CONS_OUT | SBBC_SRAM_CONS_SPACE_IN); uart_barrier(bas); /* Take over the console input. */ sbbc_serengeti_set_console_input(SUNW_SETCONSINPUT_CLNT); } static void sbbc_uart_term(struct uart_bas *bas __unused) { /* Give back the console input. */ sbbc_serengeti_set_console_input(SUNW_SETCONSINPUT_OBP); } static void sbbc_uart_putc(struct uart_bas *bas, int c) { bus_space_tag_t bst; bus_space_handle_t bsh; uint32_t wrptr; bst = bas->bst; bsh = bas->bsh; wrptr = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_wrptr)); SBBC_SRAM_WRITE_1(sbbc_solcons + wrptr, c); uart_barrier(bas); if (++wrptr == SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_end))) wrptr = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_begin)); SBBC_SRAM_WRITE_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_wrptr), wrptr); uart_barrier(bas); SBBC_SRAM_WRITE_4(sbbc_solscir, SBBC_SRAM_READ_4(sbbc_solscir) | SBBC_SRAM_CONS_OUT); uart_barrier(bas); sbbc_send_intr(bst, bsh); } static int sbbc_uart_rxready(struct uart_bas *bas) { bus_space_tag_t bst; bus_space_handle_t bsh; bst = bas->bst; bsh = bas->bsh; if (SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr)) == SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_wrptr))) return (0); return (1); } static int sbbc_uart_getc(struct uart_bas *bas, struct mtx *hwmtx) { bus_space_tag_t bst; bus_space_handle_t bsh; int c; uint32_t rdptr; bst = bas->bst; bsh = bas->bsh; uart_lock(hwmtx); while (sbbc_uart_rxready(bas) == 0) { uart_unlock(hwmtx); DELAY(4); uart_lock(hwmtx); } rdptr = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr)); c = SBBC_SRAM_READ_1(sbbc_solcons + rdptr); uart_barrier(bas); if (++rdptr == SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_end))) rdptr = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_begin)); SBBC_SRAM_WRITE_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr), rdptr); uart_barrier(bas); SBBC_SRAM_WRITE_4(sbbc_solscir, SBBC_SRAM_READ_4(sbbc_solscir) | SBBC_SRAM_CONS_SPACE_IN); uart_barrier(bas); sbbc_send_intr(bst, bsh); uart_unlock(hwmtx); return (c); } /* * High-level UART interface */ static int sbbc_uart_bus_attach(struct uart_softc *sc); static int sbbc_uart_bus_detach(struct uart_softc *sc); static int sbbc_uart_bus_flush(struct uart_softc *sc, int what); static int sbbc_uart_bus_getsig(struct uart_softc *sc); static int sbbc_uart_bus_ioctl(struct uart_softc *sc, int request, intptr_t data); static int sbbc_uart_bus_ipend(struct uart_softc *sc); static int sbbc_uart_bus_param(struct uart_softc *sc, int baudrate, int databits, int stopbits, int parity); static int sbbc_uart_bus_probe(struct uart_softc *sc); static int sbbc_uart_bus_receive(struct uart_softc *sc); static int sbbc_uart_bus_setsig(struct uart_softc *sc, int sig); static int sbbc_uart_bus_transmit(struct uart_softc *sc); static kobj_method_t sbbc_uart_methods[] = { KOBJMETHOD(uart_attach, sbbc_uart_bus_attach), KOBJMETHOD(uart_detach, sbbc_uart_bus_detach), KOBJMETHOD(uart_flush, sbbc_uart_bus_flush), KOBJMETHOD(uart_getsig, sbbc_uart_bus_getsig), KOBJMETHOD(uart_ioctl, sbbc_uart_bus_ioctl), KOBJMETHOD(uart_ipend, sbbc_uart_bus_ipend), KOBJMETHOD(uart_param, sbbc_uart_bus_param), KOBJMETHOD(uart_probe, sbbc_uart_bus_probe), KOBJMETHOD(uart_receive, sbbc_uart_bus_receive), KOBJMETHOD(uart_setsig, sbbc_uart_bus_setsig), KOBJMETHOD(uart_transmit, sbbc_uart_bus_transmit), DEVMETHOD_END }; struct uart_class uart_sbbc_class = { "sbbc", sbbc_uart_methods, sizeof(struct uart_softc), .uc_ops = &sbbc_uart_ops, .uc_range = 1, - .uc_rclk = 0x5bbc /* arbitrary */ + .uc_rclk = 0x5bbc, /* arbitrary */ + .uc_rshift = 0 }; #define SIGCHG(c, i, s, d) \ if ((c) != 0) { \ i |= (((i) & (s)) != 0) ? (s) : (s) | (d); \ } else { \ i = (((i) & (s)) != 0) ? ((i) & ~(s)) | (d) : (i); \ } static int sbbc_uart_bus_attach(struct uart_softc *sc) { struct uart_bas *bas; bus_space_tag_t bst; bus_space_handle_t bsh; uint32_t wrptr; bas = &sc->sc_bas; bst = bas->bst; bsh = bas->bsh; uart_lock(sc->sc_hwmtx); /* * Let the current output drain before enabling interrupts. Not * doing so tends to cause lost output when turning them on. */ wrptr = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_wrptr)); while (SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_rdptr)) != wrptr); cpu_spinwait(); /* Clear and acknowledge possibly outstanding interrupts. */ SBBC_SRAM_WRITE_4(sbbc_scsolir, 0); uart_barrier(bas); SBBC_REGS_WRITE_4(SBBC_PCI_INT_STATUS, SBBC_SRAM_READ_4(sbbc_scsolir)); uart_barrier(bas); /* Enable PCI interrupts. */ SBBC_REGS_WRITE_4(SBBC_PCI_INT_ENABLE, SBBC_PCI_ENABLE_INT_A); uart_barrier(bas); /* Enable input from and output to SC as well as break interrupts. */ SBBC_SRAM_WRITE_4(sbbc_scsolie, SBBC_SRAM_READ_4(sbbc_scsolie) | SBBC_SRAM_CONS_IN | SBBC_SRAM_CONS_BRK | SBBC_SRAM_CONS_SPACE_OUT); uart_barrier(bas); uart_unlock(sc->sc_hwmtx); return (0); } static int sbbc_uart_bus_detach(struct uart_softc *sc) { /* Give back the console input. */ sbbc_serengeti_set_console_input(SUNW_SETCONSINPUT_OBP); return (0); } static int sbbc_uart_bus_flush(struct uart_softc *sc, int what) { struct uart_bas *bas; bus_space_tag_t bst; bus_space_handle_t bsh; bas = &sc->sc_bas; bst = bas->bst; bsh = bas->bsh; if ((what & UART_FLUSH_TRANSMITTER) != 0) return (ENODEV); if ((what & UART_FLUSH_RECEIVER) != 0) { SBBC_SRAM_WRITE_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr), SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_wrptr))); uart_barrier(bas); } return (0); } static int sbbc_uart_bus_getsig(struct uart_softc *sc) { uint32_t dummy, new, old, sig; do { old = sc->sc_hwsig; sig = old; dummy = 0; SIGCHG(dummy, sig, SER_CTS, SER_DCTS); SIGCHG(dummy, sig, SER_DCD, SER_DDCD); SIGCHG(dummy, sig, SER_DSR, SER_DDSR); new = sig & ~SER_MASK_DELTA; } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new)); return (sig); } static int sbbc_uart_bus_ioctl(struct uart_softc *sc, int request, intptr_t data) { int error; error = 0; uart_lock(sc->sc_hwmtx); switch (request) { case UART_IOCTL_BAUD: *(int*)data = 9600; /* arbitrary */ break; default: error = EINVAL; break; } uart_unlock(sc->sc_hwmtx); return (error); } static int sbbc_uart_bus_ipend(struct uart_softc *sc) { struct uart_bas *bas; bus_space_tag_t bst; bus_space_handle_t bsh; int ipend; uint32_t reason, status; bas = &sc->sc_bas; bst = bas->bst; bsh = bas->bsh; uart_lock(sc->sc_hwmtx); status = SBBC_REGS_READ_4(SBBC_PCI_INT_STATUS); if (status == 0) { uart_unlock(sc->sc_hwmtx); return (0); } /* * Unfortunately, we can't use compare and swap for non-cachable * memory. */ reason = SBBC_SRAM_READ_4(sbbc_scsolir); SBBC_SRAM_WRITE_4(sbbc_scsolir, 0); uart_barrier(bas); /* Acknowledge the interrupt. */ SBBC_REGS_WRITE_4(SBBC_PCI_INT_STATUS, status); uart_barrier(bas); uart_unlock(sc->sc_hwmtx); ipend = 0; if ((reason & SBBC_SRAM_CONS_IN) != 0) ipend |= SER_INT_RXREADY; if ((reason & SBBC_SRAM_CONS_BRK) != 0) ipend |= SER_INT_BREAK; if ((reason & SBBC_SRAM_CONS_SPACE_OUT) != 0 && SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_rdptr)) == SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_wrptr))) ipend |= SER_INT_TXIDLE; return (ipend); } static int sbbc_uart_bus_param(struct uart_softc *sc __unused, int baudrate __unused, int databits __unused, int stopbits __unused, int parity __unused) { return (0); } static int sbbc_uart_bus_probe(struct uart_softc *sc) { struct uart_bas *bas; bus_space_tag_t bst; bus_space_handle_t bsh; if (sbbc_console != 0) { bas = &sc->sc_bas; bst = bas->bst; bsh = bas->bsh; sc->sc_rxfifosz = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_end)) - SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_begin)) - 1; sc->sc_txfifosz = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_end)) - SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_begin)) - 1; return (0); } return (ENXIO); } static int sbbc_uart_bus_receive(struct uart_softc *sc) { struct uart_bas *bas; bus_space_tag_t bst; bus_space_handle_t bsh; int c; uint32_t end, rdptr, wrptr; bas = &sc->sc_bas; bst = bas->bst; bsh = bas->bsh; uart_lock(sc->sc_hwmtx); end = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_end)); rdptr = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr)); wrptr = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_wrptr)); while (rdptr != wrptr) { if (uart_rx_full(sc) != 0) { sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN; break; } c = SBBC_SRAM_READ_1(sbbc_solcons + rdptr); uart_rx_put(sc, c); if (++rdptr == end) rdptr = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_begin)); } uart_barrier(bas); SBBC_SRAM_WRITE_4(sbbc_solcons + SBBC_CONS_OFF(cons_in_rdptr), rdptr); uart_barrier(bas); SBBC_SRAM_WRITE_4(sbbc_solscir, SBBC_SRAM_READ_4(sbbc_solscir) | SBBC_SRAM_CONS_SPACE_IN); uart_barrier(bas); sbbc_send_intr(bst, bsh); uart_unlock(sc->sc_hwmtx); return (0); } static int sbbc_uart_bus_setsig(struct uart_softc *sc, int sig) { struct uart_bas *bas; uint32_t new, old; bas = &sc->sc_bas; do { old = sc->sc_hwsig; new = old; if ((sig & SER_DDTR) != 0) { SIGCHG(sig & SER_DTR, new, SER_DTR, SER_DDTR); } if ((sig & SER_DRTS) != 0) { SIGCHG(sig & SER_RTS, new, SER_RTS, SER_DRTS); } } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new)); return (0); } static int sbbc_uart_bus_transmit(struct uart_softc *sc) { struct uart_bas *bas; bus_space_tag_t bst; bus_space_handle_t bsh; int i; uint32_t end, wrptr; bas = &sc->sc_bas; bst = bas->bst; bsh = bas->bsh; uart_lock(sc->sc_hwmtx); end = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_end)); wrptr = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_wrptr)); for (i = 0; i < sc->sc_txdatasz; i++) { SBBC_SRAM_WRITE_1(sbbc_solcons + wrptr, sc->sc_txbuf[i]); if (++wrptr == end) wrptr = SBBC_SRAM_READ_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_begin)); } uart_barrier(bas); SBBC_SRAM_WRITE_4(sbbc_solcons + SBBC_CONS_OFF(cons_out_wrptr), wrptr); uart_barrier(bas); SBBC_SRAM_WRITE_4(sbbc_solscir, SBBC_SRAM_READ_4(sbbc_solscir) | SBBC_SRAM_CONS_OUT); uart_barrier(bas); sbbc_send_intr(bst, bsh); sc->sc_txbusy = 1; uart_unlock(sc->sc_hwmtx); return (0); }