Index: stable/10/sys/arm/at91/uart_bus_at91usart.c =================================================================== --- stable/10/sys/arm/at91/uart_bus_at91usart.c (revision 283326) +++ stable/10/sys/arm/at91/uart_bus_at91usart.c (revision 283327) @@ -1,110 +1,111 @@ /*- * Copyright (c) 2005 Olivier Houchard. 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 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 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 "opt_uart.h" #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include "uart_if.h" +extern struct uart_class at91_usart_class; static int usart_at91_probe(device_t dev); static device_method_t usart_at91_methods[] = { /* Device interface */ DEVMETHOD(device_probe, usart_at91_probe), DEVMETHOD(device_attach, uart_bus_attach), DEVMETHOD(device_detach, uart_bus_detach), { 0, 0 } }; static driver_t usart_at91_driver = { uart_driver_name, usart_at91_methods, sizeof(struct uart_softc), }; extern SLIST_HEAD(uart_devinfo_list, uart_devinfo) uart_sysdevs; static int usart_at91_probe(device_t dev) { struct uart_softc *sc; sc = device_get_softc(dev); switch (device_get_unit(dev)) { case 0: device_set_desc(dev, "DBGU"); /* * Setting sc_sysdev makes this device a 'system device' and * indirectly makes it the system console. */ sc->sc_sysdev = SLIST_FIRST(&uart_sysdevs); bcopy(&sc->sc_sysdev->bas, &sc->sc_bas, sizeof(sc->sc_bas)); break; case 1: device_set_desc(dev, "USART0"); break; case 2: device_set_desc(dev, "USART1"); break; case 3: device_set_desc(dev, "USART2"); break; case 4: device_set_desc(dev, "USART3"); break; case 5: device_set_desc(dev, "USART4"); break; case 6: device_set_desc(dev, "USART5"); break; } sc->sc_class = &at91_usart_class; if (sc->sc_class->uc_rclk == 0) sc->sc_class->uc_rclk = at91_master_clock; return (uart_bus_probe(dev, 0, 0, 0, device_get_unit(dev))); } DRIVER_MODULE(uart, atmelarm, usart_at91_driver, uart_devclass, 0, 0); Index: stable/10/sys/arm/at91/uart_cpu_at91usart.c =================================================================== --- stable/10/sys/arm/at91/uart_cpu_at91usart.c (revision 283326) +++ stable/10/sys/arm/at91/uart_cpu_at91usart.c (revision 283327) @@ -1,90 +1,91 @@ /*- * Copyright (c) 2003 Marcel Moolenaar * Copyright (c) 2006 M. Warner Losh * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY 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 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 "opt_platform.h" #include "opt_uart.h" #ifndef FDT #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include bus_space_tag_t uart_bus_space_io; bus_space_tag_t uart_bus_space_mem; extern struct bus_space at91_bs_tag; +extern struct uart_class at91_usart_class; int uart_cpu_eqres(struct uart_bas *b1, struct uart_bas *b2) { return ((b1->bsh == b2->bsh && b1->bst == b2->bst) ? 1 : 0); } int uart_cpu_getdev(int devtype, struct uart_devinfo *di) { struct uart_class *class; class = &at91_usart_class; if (class->uc_rclk == 0 && at91_master_clock != 0) class->uc_rclk = at91_master_clock; di->ops = uart_getops(class); di->bas.chan = 0; di->bas.bst = &at91_bs_tag; /* * XXX: Not pretty, but will work because we map the needed addresses * early. At least we probed this so that the console will work on * all flavors of Atmel we can detect. */ di->bas.bsh = soc_info.dbgu_base; di->baudrate = 115200; di->bas.regshft = 0; di->bas.rclk = 0; di->databits = 8; di->stopbits = 1; di->parity = UART_PARITY_NONE; uart_bus_space_io = &at91_bs_tag; uart_bus_space_mem = NULL; /* Check the environment for overrides */ uart_getenv(devtype, di, class); return (0); } #endif Index: stable/10/sys/arm/at91/uart_dev_at91usart.c =================================================================== --- stable/10/sys/arm/at91/uart_dev_at91usart.c (revision 283326) +++ stable/10/sys/arm/at91/uart_dev_at91usart.c (revision 283327) @@ -1,868 +1,880 @@ /*- * Copyright (c) 2005 M. Warner Losh * Copyright (c) 2005 Olivier Houchard * Copyright (c) 2012 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 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 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 +#ifdef FDT +#include +#endif #include #include #include #include #include #include #include #include "uart_if.h" #define DEFAULT_RCLK at91_master_clock #define USART_DEFAULT_FIFO_BYTES 128 #define USART_DCE_CHANGE_BITS (USART_CSR_CTSIC | USART_CSR_DCDIC | \ USART_CSR_DSRIC | USART_CSR_RIIC) /* * High-level UART interface. */ struct at91_usart_rx { bus_addr_t pa; uint8_t *buffer; bus_dmamap_t map; }; struct at91_usart_softc { struct uart_softc base; bus_dma_tag_t tx_tag; bus_dmamap_t tx_map; uint32_t flags; #define HAS_TIMEOUT 0x1 #define USE_RTS0_WORKAROUND 0x2 bus_dma_tag_t rx_tag; struct at91_usart_rx ping_pong[2]; struct at91_usart_rx *ping; struct at91_usart_rx *pong; }; #define RD4(bas, reg) \ bus_space_read_4((bas)->bst, (bas)->bsh, uart_regofs(bas, reg)) #define WR4(bas, reg, value) \ bus_space_write_4((bas)->bst, (bas)->bsh, uart_regofs(bas, 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); #define BAUD2DIVISOR(b) \ ((((DEFAULT_RCLK * 10) / ((b) * 16)) + 5) / 10) /* * Low-level UART interface. */ static int at91_usart_probe(struct uart_bas *bas); static void at91_usart_init(struct uart_bas *bas, int, int, int, int); static void at91_usart_term(struct uart_bas *bas); static void at91_usart_putc(struct uart_bas *bas, int); static int at91_usart_rxready(struct uart_bas *bas); static int at91_usart_getc(struct uart_bas *bas, struct mtx *hwmtx); extern SLIST_HEAD(uart_devinfo_list, uart_devinfo) uart_sysdevs; static int at91_usart_param(struct uart_bas *bas, int baudrate, int databits, int stopbits, int parity) { uint32_t mr; /* * Assume 3-wire RS-232 configuration. * XXX Not sure how uart will present the other modes to us, so * XXX they are unimplemented. maybe ioctl? */ mr = USART_MR_MODE_NORMAL; mr |= USART_MR_USCLKS_MCK; /* Assume MCK */ /* * Or in the databits requested */ if (databits < 9) mr &= ~USART_MR_MODE9; switch (databits) { case 5: mr |= USART_MR_CHRL_5BITS; break; case 6: mr |= USART_MR_CHRL_6BITS; break; case 7: mr |= USART_MR_CHRL_7BITS; break; case 8: mr |= USART_MR_CHRL_8BITS; break; case 9: mr |= USART_MR_CHRL_8BITS | USART_MR_MODE9; break; default: return (EINVAL); } /* * Or in the parity */ switch (parity) { case UART_PARITY_NONE: mr |= USART_MR_PAR_NONE; break; case UART_PARITY_ODD: mr |= USART_MR_PAR_ODD; break; case UART_PARITY_EVEN: mr |= USART_MR_PAR_EVEN; break; case UART_PARITY_MARK: mr |= USART_MR_PAR_MARK; break; case UART_PARITY_SPACE: mr |= USART_MR_PAR_SPACE; break; default: return (EINVAL); } /* * Or in the stop bits. Note: The hardware supports 1.5 stop * bits in async mode, but there's no way to specify that * AFAICT. Instead, rely on the convention documented at * http://www.lammertbies.nl/comm/info/RS-232_specs.html which * states that 1.5 stop bits are used for 5 bit bytes and * 2 stop bits only for longer bytes. */ if (stopbits == 1) mr |= USART_MR_NBSTOP_1; else if (databits > 5) mr |= USART_MR_NBSTOP_2; else mr |= USART_MR_NBSTOP_1_5; /* * We want normal plumbing mode too, none of this fancy * loopback or echo mode. */ mr |= USART_MR_CHMODE_NORMAL; mr &= ~USART_MR_MSBF; /* lsb first */ mr &= ~USART_MR_CKLO_SCK; /* Don't drive SCK */ WR4(bas, USART_MR, mr); /* * Set the baud rate (only if we know our master clock rate) */ if (DEFAULT_RCLK != 0) WR4(bas, USART_BRGR, BAUD2DIVISOR(baudrate)); /* * Set the receive timeout based on the baud rate. The idea is to * compromise between being responsive on an interactive connection and * giving a bulk data sender a bit of time to queue up a new buffer * without mistaking it for a stopping point in the transmission. For * 19.2kbps and below, use 20 * bit time (2 characters). For faster * connections use 500 microseconds worth of bits. */ if (baudrate <= 19200) WR4(bas, USART_RTOR, 20); else WR4(bas, USART_RTOR, baudrate / 2000); WR4(bas, USART_CR, USART_CR_STTTO); /* XXX Need to take possible synchronous mode into account */ return (0); } static struct uart_ops at91_usart_ops = { .probe = at91_usart_probe, .init = at91_usart_init, .term = at91_usart_term, .putc = at91_usart_putc, .rxready = at91_usart_rxready, .getc = at91_usart_getc, }; #ifdef EARLY_PRINTF /* * Early printf support. This assumes that we have the SoC "system" devices * mapped into AT91_BASE. To use this before we adjust the boostrap tables, * you'll need to define SOCDEV_VA to be 0xdc000000 and SOCDEV_PA to be * 0xfc000000 in your config file where you define EARLY_PRINTF */ volatile uint32_t *at91_dbgu = (volatile uint32_t *)(AT91_BASE + AT91_DBGU0); static void eputc(int c) { while (!(at91_dbgu[USART_CSR / 4] & USART_CSR_TXRDY)) continue; at91_dbgu[USART_THR / 4] = c; } early_putc_t * early_putc = eputc; #endif static int at91_usart_probe(struct uart_bas *bas) { /* We know that this is always here */ return (0); } /* * Initialize this device for use as a console. */ static void at91_usart_init(struct uart_bas *bas, int baudrate, int databits, int stopbits, int parity) { #ifdef EARLY_PRINTF if (early_putc != NULL) { printf("Early printf yielding control to the real console.\n"); early_putc = NULL; } #endif /* * This routine is called multiple times, sometimes right after writing * some output, and the last byte is still shifting out. If that's the * case delay briefly before resetting, but don't loop on TXRDY because * we don't want to hang here forever if the hardware is in a bad state. */ if (!(RD4(bas, USART_CSR) & USART_CSR_TXRDY)) DELAY(10000); at91_usart_param(bas, baudrate, databits, stopbits, parity); /* Reset the rx and tx buffers and turn on rx and tx */ WR4(bas, USART_CR, USART_CR_RSTSTA | USART_CR_RSTRX | USART_CR_RSTTX); WR4(bas, USART_CR, USART_CR_RXEN | USART_CR_TXEN); WR4(bas, USART_IDR, 0xffffffff); } /* * Free resources now that we're no longer the console. This appears to * be never called, and I'm unsure quite what to do if I am called. */ static void at91_usart_term(struct uart_bas *bas) { /* XXX */ } /* * Put a character of console output (so we do it here polling rather than * interrupt driven). */ static void at91_usart_putc(struct uart_bas *bas, int c) { while (!(RD4(bas, USART_CSR) & USART_CSR_TXRDY)) continue; WR4(bas, USART_THR, c); } /* * Check for a character available. */ static int at91_usart_rxready(struct uart_bas *bas) { return ((RD4(bas, USART_CSR) & USART_CSR_RXRDY) != 0 ? 1 : 0); } /* * Block waiting for a character. */ static int at91_usart_getc(struct uart_bas *bas, struct mtx *hwmtx) { int c; uart_lock(hwmtx); while (!(RD4(bas, USART_CSR) & USART_CSR_RXRDY)) { uart_unlock(hwmtx); DELAY(4); uart_lock(hwmtx); } c = RD4(bas, USART_RHR) & 0xff; uart_unlock(hwmtx); return (c); } static int at91_usart_bus_probe(struct uart_softc *sc); static int at91_usart_bus_attach(struct uart_softc *sc); static int at91_usart_bus_flush(struct uart_softc *, int); static int at91_usart_bus_getsig(struct uart_softc *); static int at91_usart_bus_ioctl(struct uart_softc *, int, intptr_t); static int at91_usart_bus_ipend(struct uart_softc *); static int at91_usart_bus_param(struct uart_softc *, int, int, int, int); static int at91_usart_bus_receive(struct uart_softc *); static int at91_usart_bus_setsig(struct uart_softc *, int); static int at91_usart_bus_transmit(struct uart_softc *); static void at91_usart_bus_grab(struct uart_softc *); static void at91_usart_bus_ungrab(struct uart_softc *); static kobj_method_t at91_usart_methods[] = { KOBJMETHOD(uart_probe, at91_usart_bus_probe), KOBJMETHOD(uart_attach, at91_usart_bus_attach), KOBJMETHOD(uart_flush, at91_usart_bus_flush), KOBJMETHOD(uart_getsig, at91_usart_bus_getsig), KOBJMETHOD(uart_ioctl, at91_usart_bus_ioctl), KOBJMETHOD(uart_ipend, at91_usart_bus_ipend), KOBJMETHOD(uart_param, at91_usart_bus_param), KOBJMETHOD(uart_receive, at91_usart_bus_receive), KOBJMETHOD(uart_setsig, at91_usart_bus_setsig), KOBJMETHOD(uart_transmit, at91_usart_bus_transmit), KOBJMETHOD(uart_grab, at91_usart_bus_grab), KOBJMETHOD(uart_ungrab, at91_usart_bus_ungrab), KOBJMETHOD_END }; int at91_usart_bus_probe(struct uart_softc *sc) { int value; value = USART_DEFAULT_FIFO_BYTES; resource_int_value(device_get_name(sc->sc_dev), device_get_unit(sc->sc_dev), "fifo_bytes", &value); value = roundup2(value, arm_dcache_align); sc->sc_txfifosz = value; sc->sc_rxfifosz = value; sc->sc_hwiflow = 0; return (0); } static void at91_getaddr(void *arg, bus_dma_segment_t *segs, int nsegs, int error) { if (error != 0) return; *(bus_addr_t *)arg = segs[0].ds_addr; } static int at91_usart_requires_rts0_workaround(struct uart_softc *sc) { int value; int unit; unit = device_get_unit(sc->sc_dev); /* * On the rm9200 chips, the PA21/RTS0 pin is not correctly wired to the * usart device interally (so-called 'erratum 39', but it's 41.14 in rev * I of the manual). This prevents use of the hardware flow control * feature in the usart itself. It also means that if we are to * implement RTS/CTS flow via the tty layer logic, we must use pin PA21 * as a gpio and manually manipulate it in at91_usart_bus_setsig(). We * can only safely do so if we've been given permission via a hint, * otherwise we might manipulate a pin that's attached to who-knows-what * and Bad Things could happen. */ if (at91_is_rm92() && unit == 1) { value = 0; resource_int_value(device_get_name(sc->sc_dev), unit, "use_rts0_workaround", &value); if (value != 0) { at91_pio_use_gpio(AT91RM92_PIOA_BASE, AT91C_PIO_PA21); at91_pio_gpio_output(AT91RM92_PIOA_BASE, AT91C_PIO_PA21, 1); at91_pio_use_periph_a(AT91RM92_PIOA_BASE, AT91C_PIO_PA20, 0); return (1); } } return (0); } static int at91_usart_bus_attach(struct uart_softc *sc) { int err; int i; uint32_t cr; struct at91_usart_softc *atsc; atsc = (struct at91_usart_softc *)sc; if (at91_usart_requires_rts0_workaround(sc)) atsc->flags |= USE_RTS0_WORKAROUND; /* * See if we have a TIMEOUT bit. We disable all interrupts as * a side effect. Boot loaders may have enabled them. Since * a TIMEOUT interrupt can't happen without other setup, the * apparent race here can't actually happen. */ WR4(&sc->sc_bas, USART_IDR, 0xffffffff); WR4(&sc->sc_bas, USART_IER, USART_CSR_TIMEOUT); if (RD4(&sc->sc_bas, USART_IMR) & USART_CSR_TIMEOUT) atsc->flags |= HAS_TIMEOUT; WR4(&sc->sc_bas, USART_IDR, 0xffffffff); /* * Allocate transmit DMA tag and map. We allow a transmit buffer * to be any size, but it must map to a single contiguous physical * extent. */ err = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, BUS_SPACE_MAXSIZE_32BIT, 1, BUS_SPACE_MAXSIZE_32BIT, 0, NULL, NULL, &atsc->tx_tag); if (err != 0) goto errout; err = bus_dmamap_create(atsc->tx_tag, 0, &atsc->tx_map); if (err != 0) goto errout; if (atsc->flags & HAS_TIMEOUT) { /* * Allocate receive DMA tags, maps, and buffers. * The receive buffers should be aligned to arm_dcache_align, * otherwise partial cache line flushes on every receive * interrupt are pretty much guaranteed. */ err = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), arm_dcache_align, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, sc->sc_rxfifosz, 1, sc->sc_rxfifosz, BUS_DMA_ALLOCNOW, NULL, NULL, &atsc->rx_tag); if (err != 0) goto errout; for (i = 0; i < 2; i++) { err = bus_dmamem_alloc(atsc->rx_tag, (void **)&atsc->ping_pong[i].buffer, BUS_DMA_NOWAIT, &atsc->ping_pong[i].map); if (err != 0) goto errout; err = bus_dmamap_load(atsc->rx_tag, atsc->ping_pong[i].map, atsc->ping_pong[i].buffer, sc->sc_rxfifosz, at91_getaddr, &atsc->ping_pong[i].pa, 0); if (err != 0) goto errout; bus_dmamap_sync(atsc->rx_tag, atsc->ping_pong[i].map, BUS_DMASYNC_PREREAD); } atsc->ping = &atsc->ping_pong[0]; atsc->pong = &atsc->ping_pong[1]; } /* Turn on rx and tx */ cr = USART_CR_RSTSTA | USART_CR_RSTRX | USART_CR_RSTTX; WR4(&sc->sc_bas, USART_CR, cr); WR4(&sc->sc_bas, USART_CR, USART_CR_RXEN | USART_CR_TXEN); /* * Setup the PDC to receive data. We use the ping-pong buffers * so that we can more easily bounce between the two and so that * we get an interrupt 1/2 way through the software 'fifo' we have * to avoid overruns. */ if (atsc->flags & HAS_TIMEOUT) { WR4(&sc->sc_bas, PDC_RPR, atsc->ping->pa); WR4(&sc->sc_bas, PDC_RCR, sc->sc_rxfifosz); WR4(&sc->sc_bas, PDC_RNPR, atsc->pong->pa); WR4(&sc->sc_bas, PDC_RNCR, sc->sc_rxfifosz); WR4(&sc->sc_bas, PDC_PTCR, PDC_PTCR_RXTEN); /* * Set the receive timeout to be 1.5 character times * assuming 8N1. */ WR4(&sc->sc_bas, USART_RTOR, 15); WR4(&sc->sc_bas, USART_CR, USART_CR_STTTO); WR4(&sc->sc_bas, USART_IER, USART_CSR_TIMEOUT | USART_CSR_RXBUFF | USART_CSR_ENDRX); } else { WR4(&sc->sc_bas, USART_IER, USART_CSR_RXRDY); } WR4(&sc->sc_bas, USART_IER, USART_CSR_RXBRK | USART_DCE_CHANGE_BITS); /* Prime sc->hwsig with the initial hw line states. */ at91_usart_bus_getsig(sc); errout: return (err); } static int at91_usart_bus_transmit(struct uart_softc *sc) { bus_addr_t addr; struct at91_usart_softc *atsc; int err; err = 0; atsc = (struct at91_usart_softc *)sc; uart_lock(sc->sc_hwmtx); if (bus_dmamap_load(atsc->tx_tag, atsc->tx_map, sc->sc_txbuf, sc->sc_txdatasz, at91_getaddr, &addr, 0) != 0) { err = EAGAIN; goto errout; } bus_dmamap_sync(atsc->tx_tag, atsc->tx_map, BUS_DMASYNC_PREWRITE); sc->sc_txbusy = 1; /* * Setup the PDC to transfer the data and interrupt us when it * is done. We've already requested the interrupt. */ WR4(&sc->sc_bas, PDC_TPR, addr); WR4(&sc->sc_bas, PDC_TCR, sc->sc_txdatasz); WR4(&sc->sc_bas, PDC_PTCR, PDC_PTCR_TXTEN); WR4(&sc->sc_bas, USART_IER, USART_CSR_ENDTX); errout: uart_unlock(sc->sc_hwmtx); return (err); } static int at91_usart_bus_setsig(struct uart_softc *sc, int sig) { uint32_t new, old, cr; struct at91_usart_softc *atsc; atsc = (struct at91_usart_softc *)sc; 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)); cr = 0; if (new & SER_DTR) cr |= USART_CR_DTREN; else cr |= USART_CR_DTRDIS; if (new & SER_RTS) cr |= USART_CR_RTSEN; else cr |= USART_CR_RTSDIS; uart_lock(sc->sc_hwmtx); WR4(&sc->sc_bas, USART_CR, cr); if (atsc->flags & USE_RTS0_WORKAROUND) { /* Signal is active-low. */ if (new & SER_RTS) at91_pio_gpio_clear(AT91RM92_PIOA_BASE, AT91C_PIO_PA21); else at91_pio_gpio_set(AT91RM92_PIOA_BASE,AT91C_PIO_PA21); } uart_unlock(sc->sc_hwmtx); return (0); } static int at91_usart_bus_receive(struct uart_softc *sc) { return (0); } static int at91_usart_bus_param(struct uart_softc *sc, int baudrate, int databits, int stopbits, int parity) { return (at91_usart_param(&sc->sc_bas, baudrate, databits, stopbits, parity)); } static __inline void at91_rx_put(struct uart_softc *sc, int key) { #if defined(KDB) if (sc->sc_sysdev != NULL && sc->sc_sysdev->type == UART_DEV_CONSOLE) kdb_alt_break(key, &sc->sc_altbrk); #endif uart_rx_put(sc, key); } static int at91_usart_bus_ipend(struct uart_softc *sc) { struct at91_usart_softc *atsc; struct at91_usart_rx *p; int i, ipend, len; uint32_t csr; ipend = 0; atsc = (struct at91_usart_softc *)sc; uart_lock(sc->sc_hwmtx); csr = RD4(&sc->sc_bas, USART_CSR); if (csr & USART_CSR_OVRE) { WR4(&sc->sc_bas, USART_CR, USART_CR_RSTSTA); ipend |= SER_INT_OVERRUN; } if (csr & USART_DCE_CHANGE_BITS) ipend |= SER_INT_SIGCHG; if (csr & USART_CSR_ENDTX) { bus_dmamap_sync(atsc->tx_tag, atsc->tx_map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(atsc->tx_tag, atsc->tx_map); } if (csr & (USART_CSR_TXRDY | USART_CSR_ENDTX)) { if (sc->sc_txbusy) ipend |= SER_INT_TXIDLE; WR4(&sc->sc_bas, USART_IDR, csr & (USART_CSR_TXRDY | USART_CSR_ENDTX)); } /* * Due to the contraints of the DMA engine present in the * atmel chip, I can't just say I have a rx interrupt pending * and do all the work elsewhere. I need to look at the CSR * bits right now and do things based on them to avoid races. */ if (atsc->flags & HAS_TIMEOUT) { if (csr & USART_CSR_RXBUFF) { /* * We have a buffer overflow. Consume data from ping * and give it back to the hardware before worrying * about pong, to minimze data loss. Insert an overrun * marker after the contents of the pong buffer. */ WR4(&sc->sc_bas, PDC_PTCR, PDC_PTCR_RXTDIS); bus_dmamap_sync(atsc->rx_tag, atsc->ping->map, BUS_DMASYNC_POSTREAD); for (i = 0; i < sc->sc_rxfifosz; i++) at91_rx_put(sc, atsc->ping->buffer[i]); bus_dmamap_sync(atsc->rx_tag, atsc->ping->map, BUS_DMASYNC_PREREAD); WR4(&sc->sc_bas, PDC_RPR, atsc->ping->pa); WR4(&sc->sc_bas, PDC_RCR, sc->sc_rxfifosz); WR4(&sc->sc_bas, PDC_PTCR, PDC_PTCR_RXTEN); bus_dmamap_sync(atsc->rx_tag, atsc->pong->map, BUS_DMASYNC_POSTREAD); for (i = 0; i < sc->sc_rxfifosz; i++) at91_rx_put(sc, atsc->pong->buffer[i]); uart_rx_put(sc, UART_STAT_OVERRUN); bus_dmamap_sync(atsc->rx_tag, atsc->pong->map, BUS_DMASYNC_PREREAD); WR4(&sc->sc_bas, PDC_RNPR, atsc->pong->pa); WR4(&sc->sc_bas, PDC_RNCR, sc->sc_rxfifosz); ipend |= SER_INT_RXREADY; } else if (csr & USART_CSR_ENDRX) { /* * Consume data from ping of ping pong buffer, but leave * current pong in place, as it has become the new ping. * We need to copy data and setup the old ping as the * new pong when we're done. */ bus_dmamap_sync(atsc->rx_tag, atsc->ping->map, BUS_DMASYNC_POSTREAD); for (i = 0; i < sc->sc_rxfifosz; i++) at91_rx_put(sc, atsc->ping->buffer[i]); p = atsc->ping; atsc->ping = atsc->pong; atsc->pong = p; bus_dmamap_sync(atsc->rx_tag, atsc->pong->map, BUS_DMASYNC_PREREAD); WR4(&sc->sc_bas, PDC_RNPR, atsc->pong->pa); WR4(&sc->sc_bas, PDC_RNCR, sc->sc_rxfifosz); ipend |= SER_INT_RXREADY; } else if (csr & USART_CSR_TIMEOUT) { /* * On a timeout, one of the following applies: * 1. Two empty buffers. The last received byte exactly * filled a buffer, causing an ENDTX that got * processed earlier; no new bytes have arrived. * 2. Ping buffer contains some data and pong is empty. * This should be the most common timeout condition. * 3. Ping buffer is full and pong is now being filled. * This is exceedingly rare; it can happen only if * the ping buffer is almost full when a timeout is * signaled, and then dataflow resumes and the ping * buffer filled up between the time we read the * status register above and the point where the * RXTDIS takes effect here. Yes, it can happen. * Because dataflow can resume at any time following a * timeout (it may have already resumed before we get * here), it's important to minimize the time the PDC is * disabled -- just long enough to take the ping buffer * out of service (so we can consume it) and install the * pong buffer as the active one. Note that in case 3 * the hardware has already done the ping-pong swap. */ WR4(&sc->sc_bas, PDC_PTCR, PDC_PTCR_RXTDIS); if (RD4(&sc->sc_bas, PDC_RNCR) == 0) { len = sc->sc_rxfifosz; } else { len = sc->sc_rxfifosz - RD4(&sc->sc_bas, PDC_RCR); WR4(&sc->sc_bas, PDC_RPR, atsc->pong->pa); WR4(&sc->sc_bas, PDC_RCR, sc->sc_rxfifosz); WR4(&sc->sc_bas, PDC_RNCR, 0); } WR4(&sc->sc_bas, USART_CR, USART_CR_STTTO); WR4(&sc->sc_bas, PDC_PTCR, PDC_PTCR_RXTEN); bus_dmamap_sync(atsc->rx_tag, atsc->ping->map, BUS_DMASYNC_POSTREAD); for (i = 0; i < len; i++) at91_rx_put(sc, atsc->ping->buffer[i]); bus_dmamap_sync(atsc->rx_tag, atsc->ping->map, BUS_DMASYNC_PREREAD); p = atsc->ping; atsc->ping = atsc->pong; atsc->pong = p; WR4(&sc->sc_bas, PDC_RNPR, atsc->pong->pa); WR4(&sc->sc_bas, PDC_RNCR, sc->sc_rxfifosz); ipend |= SER_INT_RXREADY; } } else if (csr & USART_CSR_RXRDY) { /* * We have another charater in a device that doesn't support * timeouts, so we do it one character at a time. */ at91_rx_put(sc, RD4(&sc->sc_bas, USART_RHR) & 0xff); ipend |= SER_INT_RXREADY; } if (csr & USART_CSR_RXBRK) { ipend |= SER_INT_BREAK; WR4(&sc->sc_bas, USART_CR, USART_CR_RSTSTA); } uart_unlock(sc->sc_hwmtx); return (ipend); } static int at91_usart_bus_flush(struct uart_softc *sc, int what) { return (0); } static int at91_usart_bus_getsig(struct uart_softc *sc) { uint32_t csr, new, old, sig; /* * Note that the atmel channel status register DCE status bits reflect * the electrical state of the lines, not the logical state. Since they * are logically active-low signals, we invert the tests here. */ do { old = sc->sc_hwsig; sig = old; csr = RD4(&sc->sc_bas, USART_CSR); SIGCHG(!(csr & USART_CSR_DSR), sig, SER_DSR, SER_DDSR); SIGCHG(!(csr & USART_CSR_CTS), sig, SER_CTS, SER_DCTS); SIGCHG(!(csr & USART_CSR_DCD), sig, SER_DCD, SER_DDCD); SIGCHG(!(csr & USART_CSR_RI), sig, SER_RI, SER_DRI); new = sig & ~SER_MASK_DELTA; } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new)); return (sig); } static int at91_usart_bus_ioctl(struct uart_softc *sc, int request, intptr_t data) { switch (request) { case UART_IOCTL_BREAK: case UART_IOCTL_IFLOW: case UART_IOCTL_OFLOW: break; case UART_IOCTL_BAUD: /* only if we know our master clock rate */ if (DEFAULT_RCLK != 0) WR4(&sc->sc_bas, USART_BRGR, BAUD2DIVISOR(*(int *)data)); return (0); } return (EINVAL); } static void at91_usart_bus_grab(struct uart_softc *sc) { uart_lock(sc->sc_hwmtx); WR4(&sc->sc_bas, USART_IDR, USART_CSR_RXRDY); uart_unlock(sc->sc_hwmtx); } static void at91_usart_bus_ungrab(struct uart_softc *sc) { uart_lock(sc->sc_hwmtx); WR4(&sc->sc_bas, USART_IER, USART_CSR_RXRDY); uart_unlock(sc->sc_hwmtx); } struct uart_class at91_usart_class = { "at91_usart", at91_usart_methods, sizeof(struct at91_usart_softc), .uc_ops = &at91_usart_ops, .uc_range = 8 }; + +#ifdef FDT +static struct ofw_compat_data compat_data[] = { + {"atmel,at91rm9200-usart",(uintptr_t)&at91_usart_class}, + {"atmel,at91sam9260-usart",(uintptr_t)&at91_usart_class}, + {NULL, (uintptr_t)NULL}, +}; +UART_FDT_CLASS_AND_DEVICE(compat_data); +#endif Index: stable/10/sys/arm/freescale/vybrid/vf_uart.c =================================================================== --- stable/10/sys/arm/freescale/vybrid/vf_uart.c (revision 283326) +++ stable/10/sys/arm/freescale/vybrid/vf_uart.c (revision 283327) @@ -1,509 +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 #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 } }; -struct uart_class uart_vybrid_class = { +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 */ }; + +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: stable/10/sys/arm/samsung/exynos/exynos_uart.c =================================================================== --- stable/10/sys/arm/samsung/exynos/exynos_uart.c (revision 283326) +++ stable/10/sys/arm/samsung/exynos/exynos_uart.c (revision 283327) @@ -1,382 +1,389 @@ /* * 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); } -struct uart_class uart_exynos4210_class = { +static struct uart_class uart_exynos4210_class = { "exynos4210 class", exynos4210_methods, 1, .uc_ops = &uart_exynos4210_ops, .uc_range = 8, .uc_rclk = 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: stable/10/sys/arm/samsung/s3c2xx0/uart_bus_s3c2410.c =================================================================== --- stable/10/sys/arm/samsung/s3c2xx0/uart_bus_s3c2410.c (revision 283326) +++ stable/10/sys/arm/samsung/s3c2xx0/uart_bus_s3c2410.c (revision 283327) @@ -1,58 +1,60 @@ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include "uart_if.h" +extern struct uart_class uart_s3c2410_class; + static int uart_s3c2410_probe(device_t dev); static device_method_t uart_s3c2410_methods[] = { /* Device interface */ DEVMETHOD(device_probe, uart_s3c2410_probe), DEVMETHOD(device_attach, uart_bus_attach), DEVMETHOD(device_detach, uart_bus_detach), { 0, 0 } }; static driver_t uart_s3c2410_driver = { uart_driver_name, uart_s3c2410_methods, sizeof(struct uart_softc), }; extern SLIST_HEAD(uart_devinfo_list, uart_devinfo) uart_sysdevs; static int uart_s3c2410_probe(device_t dev) { struct uart_devinfo *sysdev; struct uart_softc *sc; int unit; sc = device_get_softc(dev); sc->sc_class = &uart_s3c2410_class; unit = device_get_unit(dev); sysdev = SLIST_FIRST(&uart_sysdevs); if (S3C24X0_UART_BASE(unit) == sysdev->bas.bsh) { sc->sc_sysdev = sysdev; bcopy(&sc->sc_sysdev->bas, &sc->sc_bas, sizeof(sc->sc_bas)); } return(uart_bus_probe(dev, 0, 0, 0, unit)); } DRIVER_MODULE(uart, s3c24x0, uart_s3c2410_driver, uart_devclass, 0, 0); Index: stable/10/sys/arm/samsung/s3c2xx0/uart_cpu_s3c2410.c =================================================================== --- stable/10/sys/arm/samsung/s3c2xx0/uart_cpu_s3c2410.c (revision 283326) +++ stable/10/sys/arm/samsung/s3c2xx0/uart_cpu_s3c2410.c (revision 283327) @@ -1,76 +1,76 @@ /* * Copyright (c) 2003 Marcel Moolenaar * Copyright (c) 2007 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 +extern struct uart_class uart_s3c2410_class; + bus_space_tag_t uart_bus_space_io; bus_space_tag_t uart_bus_space_mem; - -extern struct uart_ops uart_s3c2410_ops; vm_offset_t s3c2410_uart_vaddr; unsigned int s3c2410_pclk; int uart_cpu_eqres(struct uart_bas *b1, struct uart_bas *b2) { return ((b1->bsh == b2->bsh && b1->bst == b2->bst) ? 1 : 0); } int uart_cpu_getdev(int devtype, struct uart_devinfo *di) { if (devtype != UART_DEV_CONSOLE) return (ENXIO); di->ops = uart_getops(&uart_s3c2410_class); di->bas.chan = 0; di->bas.bst = s3c2xx0_bs_tag; di->bas.bsh = s3c2410_uart_vaddr; di->bas.regshft = 0; di->bas.rclk = s3c2410_pclk; di->baudrate = 115200; di->databits = 8; di->stopbits = 1; di->parity = UART_PARITY_NONE; uart_bus_space_io = s3c2xx0_bs_tag; uart_bus_space_mem = NULL; return (0); } Index: stable/10/sys/arm/xilinx/uart_dev_cdnc.c =================================================================== --- stable/10/sys/arm/xilinx/uart_dev_cdnc.c (revision 283326) +++ stable/10/sys/arm/xilinx/uart_dev_cdnc.c (revision 283327) @@ -1,709 +1,716 @@ /*- * Copyright (c) 2005 M. Warner Losh * Copyright (c) 2005 Olivier Houchard * Copyright (c) 2012 Thomas Skibo * 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 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 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. */ /* A driver for the Cadence AMBA UART as used by the Xilinx Zynq-7000. * * Reference: Zynq-7000 All Programmable SoC Technical Reference Manual. * (v1.4) November 16, 2012. Xilinx doc UG585. UART is covered in Ch. 19 * and register definitions are in appendix B.33. */ #include __FBSDID("$FreeBSD$"); #include "opt_global.h" #include #include #include #include #include #include #include #include #include +#include #include #include "uart_if.h" #define UART_FIFO_SIZE 64 #define RD4(bas, reg) \ bus_space_read_4((bas)->bst, (bas)->bsh, uart_regofs((bas), (reg))) #define WR4(bas, reg, value) \ bus_space_write_4((bas)->bst, (bas)->bsh, uart_regofs((bas), (reg)), \ (value)) /* Register definitions for Cadence UART Controller. */ #define CDNC_UART_CTRL_REG 0x00 /* Control Register. */ #define CDNC_UART_CTRL_REG_STOPBRK (1<<8) #define CDNC_UART_CTRL_REG_STARTBRK (1<<7) #define CDNC_UART_CTRL_REG_TORST (1<<6) #define CDNC_UART_CTRL_REG_TX_DIS (1<<5) #define CDNC_UART_CTRL_REG_TX_EN (1<<4) #define CDNC_UART_CTRL_REG_RX_DIS (1<<3) #define CDNC_UART_CTRL_REG_RX_EN (1<<2) #define CDNC_UART_CTRL_REG_TXRST (1<<1) #define CDNC_UART_CTRL_REG_RXRST (1<<0) #define CDNC_UART_MODE_REG 0x04 /* Mode Register. */ #define CDNC_UART_MODE_REG_CHMOD_R_LOOP (3<<8) /* [9:8] - channel mode */ #define CDNC_UART_MODE_REG_CHMOD_L_LOOP (2<<8) #define CDNC_UART_MODE_REG_CHMOD_AUTECHO (1<<8) #define CDNC_UART_MODE_REG_STOP2 (2<<6) /* [7:6] - stop bits */ #define CDNC_UART_MODE_REG_PAR_NONE (4<<3) /* [5:3] - parity type */ #define CDNC_UART_MODE_REG_PAR_MARK (3<<3) #define CDNC_UART_MODE_REG_PAR_SPACE (2<<3) #define CDNC_UART_MODE_REG_PAR_ODD (1<<3) #define CDNC_UART_MODE_REG_PAR_EVEN (0<<3) #define CDNC_UART_MODE_REG_6BIT (3<<1) /* [2:1] - character len */ #define CDNC_UART_MODE_REG_7BIT (2<<1) #define CDNC_UART_MODE_REG_8BIT (0<<1) #define CDNC_UART_MODE_REG_CLKSEL (1<<0) #define CDNC_UART_IEN_REG 0x08 /* Interrupt registers. */ #define CDNC_UART_IDIS_REG 0x0C #define CDNC_UART_IMASK_REG 0x10 #define CDNC_UART_ISTAT_REG 0x14 #define CDNC_UART_INT_TXOVR (1<<12) #define CDNC_UART_INT_TXNRLYFUL (1<<11) /* tx "nearly" full */ #define CDNC_UART_INT_TXTRIG (1<<10) #define CDNC_UART_INT_DMSI (1<<9) /* delta modem status */ #define CDNC_UART_INT_RXTMOUT (1<<8) #define CDNC_UART_INT_PARITY (1<<7) #define CDNC_UART_INT_FRAMING (1<<6) #define CDNC_UART_INT_RXOVR (1<<5) #define CDNC_UART_INT_TXFULL (1<<4) #define CDNC_UART_INT_TXEMPTY (1<<3) #define CDNC_UART_INT_RXFULL (1<<2) #define CDNC_UART_INT_RXEMPTY (1<<1) #define CDNC_UART_INT_RXTRIG (1<<0) #define CDNC_UART_INT_ALL 0x1FFF #define CDNC_UART_BAUDGEN_REG 0x18 #define CDNC_UART_RX_TIMEO_REG 0x1C #define CDNC_UART_RX_WATER_REG 0x20 #define CDNC_UART_MODEM_CTRL_REG 0x24 #define CDNC_UART_MODEM_CTRL_REG_FCM (1<<5) /* automatic flow control */ #define CDNC_UART_MODEM_CTRL_REG_RTS (1<<1) #define CDNC_UART_MODEM_CTRL_REG_DTR (1<<0) #define CDNC_UART_MODEM_STAT_REG 0x28 #define CDNC_UART_MODEM_STAT_REG_FCMS (1<<8) /* flow control mode (rw) */ #define CDNC_UART_MODEM_STAT_REG_DCD (1<<7) #define CDNC_UART_MODEM_STAT_REG_RI (1<<6) #define CDNC_UART_MODEM_STAT_REG_DSR (1<<5) #define CDNC_UART_MODEM_STAT_REG_CTS (1<<4) #define CDNC_UART_MODEM_STAT_REG_DDCD (1<<3) /* change in DCD (w1tc) */ #define CDNC_UART_MODEM_STAT_REG_TERI (1<<2) /* trail edge ring (w1tc) */ #define CDNC_UART_MODEM_STAT_REG_DDSR (1<<1) /* change in DSR (w1tc) */ #define CDNC_UART_MODEM_STAT_REG_DCTS (1<<0) /* change in CTS (w1tc) */ #define CDNC_UART_CHAN_STAT_REG 0x2C /* Channel status register. */ #define CDNC_UART_CHAN_STAT_REG_TXNRLYFUL (1<<14) /* tx "nearly" full */ #define CDNC_UART_CHAN_STAT_REG_TXTRIG (1<<13) #define CDNC_UART_CHAN_STAT_REG_FDELT (1<<12) #define CDNC_UART_CHAN_STAT_REG_TXACTIVE (1<<11) #define CDNC_UART_CHAN_STAT_REG_RXACTIVE (1<<10) #define CDNC_UART_CHAN_STAT_REG_TXFULL (1<<4) #define CDNC_UART_CHAN_STAT_REG_TXEMPTY (1<<3) #define CDNC_UART_CHAN_STAT_REG_RXEMPTY (1<<1) #define CDNC_UART_CHAN_STAT_REG_RXTRIG (1<<0) #define CDNC_UART_FIFO 0x30 /* Data FIFO (tx and rx) */ #define CDNC_UART_BAUDDIV_REG 0x34 #define CDNC_UART_FLOWDEL_REG 0x38 #define CDNC_UART_TX_WATER_REG 0x44 /* * Low-level UART interface. */ static int cdnc_uart_probe(struct uart_bas *bas); static void cdnc_uart_init(struct uart_bas *bas, int, int, int, int); static void cdnc_uart_term(struct uart_bas *bas); static void cdnc_uart_putc(struct uart_bas *bas, int); static int cdnc_uart_rxready(struct uart_bas *bas); static int cdnc_uart_getc(struct uart_bas *bas, struct mtx *mtx); extern SLIST_HEAD(uart_devinfo_list, uart_devinfo) uart_sysdevs; static struct uart_ops cdnc_uart_ops = { .probe = cdnc_uart_probe, .init = cdnc_uart_init, .term = cdnc_uart_term, .putc = cdnc_uart_putc, .rxready = cdnc_uart_rxready, .getc = cdnc_uart_getc, }; #define SIGCHG(c, i, s, d) \ if (c) { \ i |= (i & s) ? s : s | d; \ } else { \ i = (i & s) ? (i & ~s) | d : i; \ } static int cdnc_uart_probe(struct uart_bas *bas) { return (0); } static int cdnc_uart_set_baud(struct uart_bas *bas, int baudrate) { uint32_t baudgen, bauddiv; uint32_t best_bauddiv, best_baudgen, best_error; uint32_t baud_out, err; best_bauddiv = 0; best_baudgen = 0; best_error = ~0; /* Try all possible bauddiv values and pick best match. */ for (bauddiv = 4; bauddiv <= 255; bauddiv++) { baudgen = (bas->rclk + (baudrate * (bauddiv + 1)) / 2) / (baudrate * (bauddiv + 1)); if (baudgen < 1 || baudgen > 0xffff) continue; baud_out = bas->rclk / (baudgen * (bauddiv + 1)); err = baud_out > baudrate ? baud_out - baudrate : baudrate - baud_out; if (err < best_error) { best_error = err; best_bauddiv = bauddiv; best_baudgen = baudgen; } } if (best_bauddiv > 0) { WR4(bas, CDNC_UART_BAUDDIV_REG, best_bauddiv); WR4(bas, CDNC_UART_BAUDGEN_REG, best_baudgen); return (0); } else return (-1); /* out of range */ } static int cdnc_uart_set_params(struct uart_bas *bas, int baudrate, int databits, int stopbits, int parity) { uint32_t mode_reg_value = 0; switch (databits) { case 6: mode_reg_value |= CDNC_UART_MODE_REG_6BIT; break; case 7: mode_reg_value |= CDNC_UART_MODE_REG_7BIT; break; case 8: default: mode_reg_value |= CDNC_UART_MODE_REG_8BIT; break; } if (stopbits == 2) mode_reg_value |= CDNC_UART_MODE_REG_STOP2; switch (parity) { case UART_PARITY_MARK: mode_reg_value |= CDNC_UART_MODE_REG_PAR_MARK; break; case UART_PARITY_SPACE: mode_reg_value |= CDNC_UART_MODE_REG_PAR_SPACE; break; case UART_PARITY_ODD: mode_reg_value |= CDNC_UART_MODE_REG_PAR_ODD; break; case UART_PARITY_EVEN: mode_reg_value |= CDNC_UART_MODE_REG_PAR_EVEN; break; case UART_PARITY_NONE: default: mode_reg_value |= CDNC_UART_MODE_REG_PAR_NONE; break; } WR4(bas, CDNC_UART_MODE_REG, mode_reg_value); if (baudrate > 0 && cdnc_uart_set_baud(bas, baudrate) < 0) return (EINVAL); return(0); } static void cdnc_uart_hw_init(struct uart_bas *bas) { /* Reset RX and TX. */ WR4(bas, CDNC_UART_CTRL_REG, CDNC_UART_CTRL_REG_RXRST | CDNC_UART_CTRL_REG_TXRST); /* Interrupts all off. */ WR4(bas, CDNC_UART_IDIS_REG, CDNC_UART_INT_ALL); WR4(bas, CDNC_UART_ISTAT_REG, CDNC_UART_INT_ALL); /* Clear delta bits. */ WR4(bas, CDNC_UART_MODEM_STAT_REG, CDNC_UART_MODEM_STAT_REG_DDCD | CDNC_UART_MODEM_STAT_REG_TERI | CDNC_UART_MODEM_STAT_REG_DDSR | CDNC_UART_MODEM_STAT_REG_DCTS); /* RX FIFO water level, stale timeout */ WR4(bas, CDNC_UART_RX_WATER_REG, UART_FIFO_SIZE/2); WR4(bas, CDNC_UART_RX_TIMEO_REG, 10); /* TX FIFO water level (not used.) */ WR4(bas, CDNC_UART_TX_WATER_REG, UART_FIFO_SIZE/2); /* Bring RX and TX online. */ WR4(bas, CDNC_UART_CTRL_REG, CDNC_UART_CTRL_REG_RX_EN | CDNC_UART_CTRL_REG_TX_EN | CDNC_UART_CTRL_REG_TORST | CDNC_UART_CTRL_REG_STOPBRK); /* Set DTR and RTS. */ WR4(bas, CDNC_UART_MODEM_CTRL_REG, CDNC_UART_MODEM_CTRL_REG_DTR | CDNC_UART_MODEM_CTRL_REG_RTS); } /* * Initialize this device for use as a console. */ static void cdnc_uart_init(struct uart_bas *bas, int baudrate, int databits, int stopbits, int parity) { /* Initialize hardware. */ cdnc_uart_hw_init(bas); /* Set baudrate, parameters. */ (void)cdnc_uart_set_params(bas, baudrate, databits, stopbits, parity); } /* * Free resources now that we're no longer the console. This appears to * be never called, and I'm unsure quite what to do if I am called. */ static void cdnc_uart_term(struct uart_bas *bas) { /* XXX */ } /* * Put a character of console output (so we do it here polling rather than * interrutp driven). */ static void cdnc_uart_putc(struct uart_bas *bas, int c) { /* Wait for room. */ while ((RD4(bas,CDNC_UART_CHAN_STAT_REG) & CDNC_UART_CHAN_STAT_REG_TXFULL) != 0) ; WR4(bas, CDNC_UART_FIFO, c); while ((RD4(bas,CDNC_UART_CHAN_STAT_REG) & CDNC_UART_CHAN_STAT_REG_TXEMPTY) == 0) ; } /* * Check for a character available. */ static int cdnc_uart_rxready(struct uart_bas *bas) { return ((RD4(bas, CDNC_UART_CHAN_STAT_REG) & CDNC_UART_CHAN_STAT_REG_RXEMPTY) == 0); } /* * Block waiting for a character. */ static int cdnc_uart_getc(struct uart_bas *bas, struct mtx *mtx) { int c; uart_lock(mtx); while ((RD4(bas, CDNC_UART_CHAN_STAT_REG) & CDNC_UART_CHAN_STAT_REG_RXEMPTY) != 0) { uart_unlock(mtx); DELAY(4); uart_lock(mtx); } c = RD4(bas, CDNC_UART_FIFO); uart_unlock(mtx); c &= 0xff; return (c); } /*****************************************************************************/ /* * High-level UART interface. */ static int cdnc_uart_bus_probe(struct uart_softc *sc); static int cdnc_uart_bus_attach(struct uart_softc *sc); static int cdnc_uart_bus_flush(struct uart_softc *, int); static int cdnc_uart_bus_getsig(struct uart_softc *); static int cdnc_uart_bus_ioctl(struct uart_softc *, int, intptr_t); static int cdnc_uart_bus_ipend(struct uart_softc *); static int cdnc_uart_bus_param(struct uart_softc *, int, int, int, int); static int cdnc_uart_bus_receive(struct uart_softc *); static int cdnc_uart_bus_setsig(struct uart_softc *, int); static int cdnc_uart_bus_transmit(struct uart_softc *); static void cdnc_uart_bus_grab(struct uart_softc *); static void cdnc_uart_bus_ungrab(struct uart_softc *); static kobj_method_t cdnc_uart_bus_methods[] = { KOBJMETHOD(uart_probe, cdnc_uart_bus_probe), KOBJMETHOD(uart_attach, cdnc_uart_bus_attach), KOBJMETHOD(uart_flush, cdnc_uart_bus_flush), KOBJMETHOD(uart_getsig, cdnc_uart_bus_getsig), KOBJMETHOD(uart_ioctl, cdnc_uart_bus_ioctl), KOBJMETHOD(uart_ipend, cdnc_uart_bus_ipend), KOBJMETHOD(uart_param, cdnc_uart_bus_param), KOBJMETHOD(uart_receive, cdnc_uart_bus_receive), KOBJMETHOD(uart_setsig, cdnc_uart_bus_setsig), KOBJMETHOD(uart_transmit, cdnc_uart_bus_transmit), KOBJMETHOD(uart_grab, cdnc_uart_bus_grab), KOBJMETHOD(uart_ungrab, cdnc_uart_bus_ungrab), KOBJMETHOD_END }; int cdnc_uart_bus_probe(struct uart_softc *sc) { sc->sc_txfifosz = UART_FIFO_SIZE; sc->sc_rxfifosz = UART_FIFO_SIZE; sc->sc_hwiflow = 0; sc->sc_hwoflow = 0; device_set_desc(sc->sc_dev, "Cadence UART"); return (0); } static int cdnc_uart_bus_attach(struct uart_softc *sc) { struct uart_bas *bas = &sc->sc_bas; struct uart_devinfo *di; if (sc->sc_sysdev != NULL) { di = sc->sc_sysdev; (void)cdnc_uart_set_params(bas, di->baudrate, di->databits, di->stopbits, di->parity); } else cdnc_uart_hw_init(bas); (void)cdnc_uart_bus_getsig(sc); /* Enable interrupts. */ WR4(bas, CDNC_UART_IEN_REG, CDNC_UART_INT_RXTRIG | CDNC_UART_INT_RXTMOUT | CDNC_UART_INT_TXOVR | CDNC_UART_INT_RXOVR | CDNC_UART_INT_DMSI); return (0); } static int cdnc_uart_bus_transmit(struct uart_softc *sc) { int i; struct uart_bas *bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); /* Clear sticky TXEMPTY status bit. */ WR4(bas, CDNC_UART_ISTAT_REG, CDNC_UART_INT_TXEMPTY); for (i = 0; i < sc->sc_txdatasz; i++) WR4(bas, CDNC_UART_FIFO, sc->sc_txbuf[i]); /* Enable TX empty interrupt. */ WR4(bas, CDNC_UART_IEN_REG, CDNC_UART_INT_TXEMPTY); sc->sc_txbusy = 1; uart_unlock(sc->sc_hwmtx); return (0); } static int cdnc_uart_bus_setsig(struct uart_softc *sc, int sig) { struct uart_bas *bas = &sc->sc_bas; uint32_t new, old, modem_ctrl; 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); modem_ctrl = RD4(bas, CDNC_UART_MODEM_CTRL_REG) & ~(CDNC_UART_MODEM_CTRL_REG_DTR | CDNC_UART_MODEM_CTRL_REG_RTS); if ((new & SER_DTR) != 0) modem_ctrl |= CDNC_UART_MODEM_CTRL_REG_DTR; if ((new & SER_RTS) != 0) modem_ctrl |= CDNC_UART_MODEM_CTRL_REG_RTS; WR4(bas, CDNC_UART_MODEM_CTRL_REG, modem_ctrl); uart_unlock(sc->sc_hwmtx); return (0); } static int cdnc_uart_bus_receive(struct uart_softc *sc) { struct uart_bas *bas = &sc->sc_bas; uint32_t status; int c, c_status = 0; uart_lock(sc->sc_hwmtx); /* Check for parity or framing errors and clear the status bits. */ status = RD4(bas, CDNC_UART_ISTAT_REG); if ((status & (CDNC_UART_INT_FRAMING | CDNC_UART_INT_PARITY)) != 0) { WR4(bas, CDNC_UART_ISTAT_REG, status & (CDNC_UART_INT_FRAMING | CDNC_UART_INT_PARITY)); if ((status & CDNC_UART_INT_PARITY) != 0) c_status |= UART_STAT_PARERR; if ((status & CDNC_UART_INT_FRAMING) != 0) c_status |= UART_STAT_FRAMERR; } while ((RD4(bas, CDNC_UART_CHAN_STAT_REG) & CDNC_UART_CHAN_STAT_REG_RXEMPTY) == 0) { c = RD4(bas, CDNC_UART_FIFO) & 0xff; #ifdef KDB /* Detect break and drop into debugger. */ if (c == 0 && (c_status & UART_STAT_FRAMERR) != 0 && sc->sc_sysdev != NULL && sc->sc_sysdev->type == UART_DEV_CONSOLE) { kdb_break(); WR4(bas, CDNC_UART_ISTAT_REG, CDNC_UART_INT_FRAMING); } #endif uart_rx_put(sc, c | c_status); } uart_unlock(sc->sc_hwmtx); return (0); } static int cdnc_uart_bus_param(struct uart_softc *sc, int baudrate, int databits, int stopbits, int parity) { return (cdnc_uart_set_params(&sc->sc_bas, baudrate, databits, stopbits, parity)); } static int cdnc_uart_bus_ipend(struct uart_softc *sc) { int ipend = 0; struct uart_bas *bas = &sc->sc_bas; uint32_t istatus; uart_lock(sc->sc_hwmtx); istatus = RD4(bas, CDNC_UART_ISTAT_REG); /* Clear interrupt bits. */ WR4(bas, CDNC_UART_ISTAT_REG, istatus & (CDNC_UART_INT_RXTRIG | CDNC_UART_INT_RXTMOUT | CDNC_UART_INT_TXOVR | CDNC_UART_INT_RXOVR | CDNC_UART_INT_TXEMPTY | CDNC_UART_INT_DMSI)); /* Receive data. */ if ((istatus & (CDNC_UART_INT_RXTRIG | CDNC_UART_INT_RXTMOUT)) != 0) ipend |= SER_INT_RXREADY; /* Transmit fifo empty. */ if (sc->sc_txbusy && (istatus & CDNC_UART_INT_TXEMPTY) != 0) { /* disable txempty interrupt. */ WR4(bas, CDNC_UART_IDIS_REG, CDNC_UART_INT_TXEMPTY); ipend |= SER_INT_TXIDLE; } /* TX Overflow. */ if ((istatus & CDNC_UART_INT_TXOVR) != 0) ipend |= SER_INT_OVERRUN; /* RX Overflow. */ if ((istatus & CDNC_UART_INT_RXOVR) != 0) ipend |= SER_INT_OVERRUN; /* Modem signal change. */ if ((istatus & CDNC_UART_INT_DMSI) != 0) { WR4(bas, CDNC_UART_MODEM_STAT_REG, CDNC_UART_MODEM_STAT_REG_DDCD | CDNC_UART_MODEM_STAT_REG_TERI | CDNC_UART_MODEM_STAT_REG_DDSR | CDNC_UART_MODEM_STAT_REG_DCTS); ipend |= SER_INT_SIGCHG; } uart_unlock(sc->sc_hwmtx); return (ipend); } static int cdnc_uart_bus_flush(struct uart_softc *sc, int what) { return (0); } static int cdnc_uart_bus_getsig(struct uart_softc *sc) { struct uart_bas *bas = &sc->sc_bas; uint32_t new, old, sig; uint8_t modem_status; do { old = sc->sc_hwsig; sig = old; uart_lock(sc->sc_hwmtx); modem_status = RD4(bas, CDNC_UART_MODEM_STAT_REG); uart_unlock(sc->sc_hwmtx); SIGCHG(modem_status & CDNC_UART_MODEM_STAT_REG_DSR, sig, SER_DSR, SER_DDSR); SIGCHG(modem_status & CDNC_UART_MODEM_STAT_REG_CTS, sig, SER_CTS, SER_DCTS); SIGCHG(modem_status & CDNC_UART_MODEM_STAT_REG_DCD, sig, SER_DCD, SER_DDCD); SIGCHG(modem_status & CDNC_UART_MODEM_STAT_REG_RI, sig, SER_RI, SER_DRI); new = sig & ~SER_MASK_DELTA; } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new)); return (sig); } static int cdnc_uart_bus_ioctl(struct uart_softc *sc, int request, intptr_t data) { struct uart_bas *bas = &sc->sc_bas; uint32_t uart_ctrl, modem_ctrl; int error = 0; uart_lock(sc->sc_hwmtx); switch (request) { case UART_IOCTL_BREAK: uart_ctrl = RD4(bas, CDNC_UART_CTRL_REG); if (data) { uart_ctrl |= CDNC_UART_CTRL_REG_STARTBRK; uart_ctrl &= ~CDNC_UART_CTRL_REG_STOPBRK; } else { uart_ctrl |= CDNC_UART_CTRL_REG_STOPBRK; uart_ctrl &= ~CDNC_UART_CTRL_REG_STARTBRK; } WR4(bas, CDNC_UART_CTRL_REG, uart_ctrl); break; case UART_IOCTL_IFLOW: modem_ctrl = RD4(bas, CDNC_UART_MODEM_CTRL_REG); if (data) modem_ctrl |= CDNC_UART_MODEM_CTRL_REG_RTS; else modem_ctrl &= ~CDNC_UART_MODEM_CTRL_REG_RTS; WR4(bas, CDNC_UART_MODEM_CTRL_REG, modem_ctrl); break; default: error = EINVAL; break; } uart_unlock(sc->sc_hwmtx); return (error); } static void cdnc_uart_bus_grab(struct uart_softc *sc) { /* Enable interrupts. */ WR4(&sc->sc_bas, CDNC_UART_IEN_REG, CDNC_UART_INT_TXOVR | CDNC_UART_INT_RXOVR | CDNC_UART_INT_DMSI); } static void cdnc_uart_bus_ungrab(struct uart_softc *sc) { /* Enable interrupts. */ WR4(&sc->sc_bas, CDNC_UART_IEN_REG, CDNC_UART_INT_RXTRIG | CDNC_UART_INT_RXTMOUT | CDNC_UART_INT_TXOVR | CDNC_UART_INT_RXOVR | CDNC_UART_INT_DMSI); } -struct uart_class uart_cdnc_class = { +static struct uart_class uart_cdnc_class = { "cdnc_uart", cdnc_uart_bus_methods, sizeof(struct uart_softc), .uc_ops = &cdnc_uart_ops, .uc_range = 8 }; + +static struct ofw_compat_data compat_data[] = { + {"cadence,uart", (uintptr_t)&uart_cdnc_class}, + {NULL, (uintptr_t)NULL}, +}; +UART_FDT_CLASS_AND_DEVICE(compat_data); Index: stable/10/sys/dev/uart/uart.h =================================================================== --- stable/10/sys/dev/uart/uart.h (revision 283326) +++ stable/10/sys/dev/uart/uart.h (revision 283327) @@ -1,111 +1,97 @@ /*- * Copyright (c) 2003 Marcel Moolenaar * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _DEV_UART_H_ #define _DEV_UART_H_ /* * Bus access structure. This structure holds the minimum information needed * to access the UART. The rclk field, although not important to actually * access the UART, is important for baudrate programming, delay loops and * other timing related computations. */ struct uart_bas { bus_space_tag_t bst; bus_space_handle_t bsh; u_int chan; u_int rclk; u_int regshft; }; #define uart_regofs(bas, reg) ((reg) << (bas)->regshft) #define uart_getreg(bas, reg) \ bus_space_read_1((bas)->bst, (bas)->bsh, uart_regofs(bas, reg)) #define uart_setreg(bas, reg, value) \ bus_space_write_1((bas)->bst, (bas)->bsh, uart_regofs(bas, reg), value) /* * XXX we don't know the length of the bus space address range in use by * the UART. Since barriers don't use the length field currently, we put * a zero there for now. */ #define uart_barrier(bas) \ bus_space_barrier((bas)->bst, (bas)->bsh, 0, 0, \ BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE) /* * UART device classes. */ struct uart_class; -extern struct uart_class uart_imx_class __attribute__((weak)); -extern struct uart_class uart_msm_class __attribute__((weak)); extern struct uart_class uart_ns8250_class __attribute__((weak)); extern struct uart_class uart_quicc_class __attribute__((weak)); extern struct uart_class uart_s3c2410_class __attribute__((weak)); extern struct uart_class uart_sab82532_class __attribute__((weak)); extern struct uart_class uart_sbbc_class __attribute__((weak)); extern struct uart_class uart_z8530_class __attribute__((weak)); -extern struct uart_class uart_lpc_class __attribute__((weak)); -extern struct uart_class uart_pl011_class __attribute__((weak)); -extern struct uart_class uart_cdnc_class __attribute__((weak)); -extern struct uart_class uart_ti8250_class __attribute__((weak)); -extern struct uart_class uart_vybrid_class __attribute__((weak)); -extern struct uart_class at91_usart_class __attribute__((weak)); -extern struct uart_class uart_exynos4210_class __attribute__((weak)); - -#ifdef FDT -struct ofw_compat_data; -extern const struct ofw_compat_data *uart_fdt_compat_data; -#endif #ifdef PC98 struct uart_class *uart_pc98_getdev(u_long port); #endif /* * Device flags. */ #define UART_FLAGS_CONSOLE(f) ((f) & 0x10) #define UART_FLAGS_DBGPORT(f) ((f) & 0x80) #define UART_FLAGS_FCR_RX_LOW(f) ((f) & 0x100) #define UART_FLAGS_FCR_RX_MEDL(f) ((f) & 0x200) #define UART_FLAGS_FCR_RX_MEDH(f) ((f) & 0x400) #define UART_FLAGS_FCR_RX_HIGH(f) ((f) & 0x800) /* * Data parity values (magical numbers related to ns8250). */ #define UART_PARITY_NONE 0 #define UART_PARITY_ODD 1 #define UART_PARITY_EVEN 3 #define UART_PARITY_MARK 5 #define UART_PARITY_SPACE 7 #endif /* _DEV_UART_H_ */ Index: stable/10/sys/dev/uart/uart_bus_fdt.c =================================================================== --- stable/10/sys/dev/uart/uart_bus_fdt.c (revision 283326) +++ stable/10/sys/dev/uart/uart_bus_fdt.c (revision 283327) @@ -1,160 +1,141 @@ /*- * 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), }; -/* - * Compatible devices. Keep this sorted in most- to least-specific order first, - * alphabetical second. That is, "zwie,ns16550" should appear before "ns16550" - * on the theory that the zwie driver knows how to make better use of the - * hardware than the generic driver. Likewise with chips within a family, the - * highest-numbers / most recent models should probably appear earlier. - */ -static struct ofw_compat_data compat_data[] = { - {"arm,pl011", (uintptr_t)&uart_pl011_class}, - {"atmel,at91rm9200-usart",(uintptr_t)&at91_usart_class}, - {"atmel,at91sam9260-usart",(uintptr_t)&at91_usart_class}, - {"cadence,uart", (uintptr_t)&uart_cdnc_class}, - {"exynos", (uintptr_t)&uart_exynos4210_class}, - {"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}, - {"fsl,mvf600-uart", (uintptr_t)&uart_vybrid_class}, - {"lpc,uart", (uintptr_t)&uart_lpc_class}, - {"qcom,msm-uartdm", (uintptr_t)&uart_msm_class}, - {"ti,ns16550", (uintptr_t)&uart_ti8250_class}, - {"ns16550", (uintptr_t)&uart_ns8250_class}, - {NULL, (uintptr_t)NULL}, -}; - -/* Export the compat_data table for use by the uart_cpu_fdt.c probe routine. */ -const struct ofw_compat_data *uart_fdt_compat_data = compat_data; - static int uart_fdt_get_clock(phandle_t node, pcell_t *cell) { pcell_t clock; /* * clock-frequency is a FreeBSD-specific hack. Make its presence optional. */ if ((OF_getprop(node, "clock-frequency", &clock, sizeof(clock))) <= 0) clock = 0; if (clock == 0) /* Try to retrieve parent 'bus-frequency' */ /* XXX this should go to simple-bus fixup or so */ if ((OF_getprop(OF_parent(node), "bus-frequency", &clock, sizeof(clock))) <= 0) clock = 0; *cell = fdt32_to_cpu(clock); return (0); } static int uart_fdt_get_shift(phandle_t node, pcell_t *cell) { pcell_t shift; if ((OF_getprop(node, "reg-shift", &shift, sizeof(shift))) <= 0) shift = 0; *cell = fdt32_to_cpu(shift); return (0); } +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; - const struct ofw_compat_data * cd; sc = device_get_softc(dev); if (!ofw_bus_status_okay(dev)) return (ENXIO); - cd = ofw_bus_search_compatible(dev, compat_data); - if (cd->ocd_data == (uintptr_t)NULL) + sc->sc_class = (struct uart_class *)uart_fdt_find_device(dev); + if (sc->sc_class == NULL) return (ENXIO); - - sc->sc_class = (struct uart_class *)cd->ocd_data; node = ofw_bus_get_node(dev); if ((err = uart_fdt_get_clock(node, &clock)) != 0) return (err); uart_fdt_get_shift(node, &shift); 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: stable/10/sys/dev/uart/uart_cpu_fdt.c =================================================================== --- stable/10/sys/dev/uart/uart_cpu_fdt.c (revision 283326) +++ stable/10/sys/dev/uart/uart_cpu_fdt.c (revision 283327) @@ -1,201 +1,243 @@ /*- * 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 #include #include #include +#include /* * UART console routines. */ bus_space_tag_t uart_bus_space_io; bus_space_tag_t uart_bus_space_mem; static int uart_fdt_get_clock(phandle_t node, pcell_t *cell) { pcell_t clock; /* clock-frequency is a FreeBSD-only extention. */ if ((OF_getprop(node, "clock-frequency", &clock, sizeof(clock))) <= 0) clock = 0; if (clock == 0) /* Try to retrieve parent 'bus-frequency' */ /* XXX this should go to simple-bus fixup or so */ if ((OF_getprop(OF_parent(node), "bus-frequency", &clock, sizeof(clock))) <= 0) clock = 0; *cell = fdt32_to_cpu(clock); return (0); } static int uart_fdt_get_shift(phandle_t node, pcell_t *cell) { pcell_t shift; if ((OF_getprop(node, "reg-shift", &shift, sizeof(shift))) <= 0) shift = 0; *cell = fdt32_to_cpu(shift); return (0); } 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; - const struct ofw_compat_data *cd; 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; - br = fdt32_to_cpu(br); + else + br = fdt32_to_cpu(br); - if ((err = uart_fdt_get_clock(node, &rclk)) != 0) - return (err); /* - * Finalize configuration. + * Check old style of UART definition first. Unfortunately, the common + * FDT processing is not possible if we have clock, power domains and + * pinmux stuff. */ - for (cd = uart_fdt_compat_data; cd->ocd_str != NULL; ++cd) { - if (fdt_is_compatible(node, cd->ocd_str)) - break; + 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; } - if (cd->ocd_str == NULL) - return (ENXIO); - class = (struct uart_class *)cd->ocd_data; + /* + * 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: stable/10/sys/dev/uart/uart_cpu_fdt.h =================================================================== --- stable/10/sys/dev/uart/uart_cpu_fdt.h (nonexistent) +++ stable/10/sys/dev/uart/uart_cpu_fdt.h (revision 283327) @@ -0,0 +1,54 @@ +/*- + * Copyright 2015 Michal Meloun + * 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. + * + * $FreeBSD$ + */ + +#ifndef _DEV_UART_CPU_FDT_H_ +#define _DEV_UART_CPU_FDT_H_ + +#include + +#include + +/* + * If your UART driver implements only uart_class and uses uart_cpu_fdt.c + * for device instantiation, then use UART_FDT_CLASS_AND_DEVICE for its + * declaration + */ +SET_DECLARE(uart_fdt_class_and_device_set, struct ofw_compat_data ); +#define UART_FDT_CLASS_AND_DEVICE(data) \ + DATA_SET(uart_fdt_class_and_device_set, data) + +/* + * If your UART driver implements uart_class and custom device layer, + * then use UART_FDT_CLASS for its declaration + */ +SET_DECLARE(uart_fdt_class_set, struct ofw_compat_data ); +#define UART_FDT_CLASS(data) \ + DATA_SET(uart_fdt_class_set, data) + + +#endif /* _DEV_UART_CPU_FDT_H_ */ Property changes on: stable/10/sys/dev/uart/uart_cpu_fdt.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: stable/10/sys/dev/uart/uart_dev_imx.c =================================================================== --- stable/10/sys/dev/uart/uart_dev_imx.c (revision 283326) +++ stable/10/sys/dev/uart/uart_dev_imx.c (revision 283327) @@ -1,604 +1,617 @@ /*- * 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 } }; -struct uart_class uart_imx_class = { +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 */ }; + +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: stable/10/sys/dev/uart/uart_dev_lpc.c =================================================================== --- stable/10/sys/dev/uart/uart_dev_lpc.c (revision 283326) +++ stable/10/sys/dev/uart/uart_dev_lpc.c (revision 283327) @@ -1,927 +1,934 @@ /*- * 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 } }; -struct uart_class uart_lpc_class = { +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 }; + +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: stable/10/sys/dev/uart/uart_dev_msm.c =================================================================== --- stable/10/sys/dev/uart/uart_dev_msm.c (revision 283326) +++ stable/10/sys/dev/uart/uart_dev_msm.c (revision 283327) @@ -1,568 +1,575 @@ /*- * 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); } -struct uart_class uart_msm_class = { +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, }; + +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: stable/10/sys/dev/uart/uart_dev_ns8250.c =================================================================== --- stable/10/sys/dev/uart/uart_dev_ns8250.c (revision 283326) +++ stable/10/sys/dev/uart/uart_dev_ns8250.c (revision 283327) @@ -1,957 +1,968 @@ /*- * 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_RW | CTLFLAG_TUN, &broken_txfifo, 0, "UART FIFO has QEMU emulation bug"); TUNABLE_INT("hw.broken_txfifo", &broken_txfifo); /* * 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 }; + +#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; /* * 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 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: stable/10/sys/dev/uart/uart_dev_pl011.c =================================================================== --- stable/10/sys/dev/uart/uart_dev_pl011.c (revision 283326) +++ stable/10/sys/dev/uart/uart_dev_pl011.c (revision 283327) @@ -1,489 +1,496 @@ /*- * 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 } }; -struct uart_class uart_pl011_class = { +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 }; + +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); } 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); 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: stable/10/sys/dev/uart/uart_dev_ti8250.c =================================================================== --- stable/10/sys/dev/uart/uart_dev_ti8250.c (revision 283326) +++ stable/10/sys/dev/uart/uart_dev_ti8250.c (revision 283327) @@ -1,141 +1,146 @@ /*- * 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 }; -struct uart_class uart_ti8250_class = { +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 }; - +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: stable/10/sys/dev/uart/uart_subr.c =================================================================== --- stable/10/sys/dev/uart/uart_subr.c (revision 283326) +++ stable/10/sys/dev/uart/uart_subr.c (revision 283327) @@ -1,310 +1,309 @@ /*- * Copyright (c) 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #define UART_TAG_BR 0 #define UART_TAG_CH 1 #define UART_TAG_DB 2 #define UART_TAG_DT 3 #define UART_TAG_IO 4 #define UART_TAG_MM 5 #define UART_TAG_PA 6 #define UART_TAG_RS 7 #define UART_TAG_SB 8 #define UART_TAG_XO 9 static struct uart_class *uart_classes[] = { &uart_ns8250_class, &uart_sab82532_class, &uart_z8530_class, #if defined(__arm__) - &uart_lpc_class, &uart_s3c2410_class, #endif }; static size_t uart_nclasses = sizeof(uart_classes) / sizeof(uart_classes[0]); static bus_addr_t uart_parse_addr(const char **p) { return (strtoul(*p, (char**)(uintptr_t)p, 0)); } static struct uart_class * uart_parse_class(struct uart_class *class, const char **p) { struct uart_class *uc; const char *nm; size_t len; u_int i; for (i = 0; i < uart_nclasses; i++) { uc = uart_classes[i]; nm = uart_getname(uc); if (nm == NULL || *nm == '\0') continue; len = strlen(nm); if (strncmp(nm, *p, len) == 0) { *p += len; return (uc); } } return (class); } static long uart_parse_long(const char **p) { return (strtol(*p, (char**)(uintptr_t)p, 0)); } static int uart_parse_parity(const char **p) { if (!strncmp(*p, "even", 4)) { *p += 4; return UART_PARITY_EVEN; } if (!strncmp(*p, "mark", 4)) { *p += 4; return UART_PARITY_MARK; } if (!strncmp(*p, "none", 4)) { *p += 4; return UART_PARITY_NONE; } if (!strncmp(*p, "odd", 3)) { *p += 3; return UART_PARITY_ODD; } if (!strncmp(*p, "space", 5)) { *p += 5; return UART_PARITY_SPACE; } return (-1); } static int uart_parse_tag(const char **p) { int tag; if ((*p)[0] == 'b' && (*p)[1] == 'r') { tag = UART_TAG_BR; goto out; } if ((*p)[0] == 'c' && (*p)[1] == 'h') { tag = UART_TAG_CH; goto out; } if ((*p)[0] == 'd' && (*p)[1] == 'b') { tag = UART_TAG_DB; goto out; } if ((*p)[0] == 'd' && (*p)[1] == 't') { tag = UART_TAG_DT; goto out; } if ((*p)[0] == 'i' && (*p)[1] == 'o') { tag = UART_TAG_IO; goto out; } if ((*p)[0] == 'm' && (*p)[1] == 'm') { tag = UART_TAG_MM; goto out; } if ((*p)[0] == 'p' && (*p)[1] == 'a') { tag = UART_TAG_PA; goto out; } if ((*p)[0] == 'r' && (*p)[1] == 's') { tag = UART_TAG_RS; goto out; } if ((*p)[0] == 's' && (*p)[1] == 'b') { tag = UART_TAG_SB; goto out; } if ((*p)[0] == 'x' && (*p)[1] == 'o') { tag = UART_TAG_XO; goto out; } return (-1); out: *p += 2; if ((*p)[0] != ':') return (-1); (*p)++; return (tag); } /* * Parse a device specification. The specification is a list of attributes * separated by commas. Each attribute is a tag-value pair with the tag and * value separated by a colon. Supported tags are: * * br = Baudrate * ch = Channel * db = Data bits * dt = Device type * io = I/O port address * mm = Memory mapped I/O address * pa = Parity * rs = Register shift * sb = Stopbits * xo = Device clock (xtal oscillator) * * The io and mm tags are mutually exclusive. */ int uart_getenv(int devtype, struct uart_devinfo *di, struct uart_class *class) { const char *spec; bus_addr_t addr = ~0U; int error; /* * All uart_class references are weak. Make sure the default * device class has been compiled-in. */ if (class == NULL) return (ENXIO); /* * Check the environment variables "hw.uart.console" and * "hw.uart.dbgport". These variables, when present, specify * which UART port is to be used as serial console or debug * port (resp). */ if (devtype == UART_DEV_CONSOLE) spec = getenv("hw.uart.console"); else if (devtype == UART_DEV_DBGPORT) spec = getenv("hw.uart.dbgport"); else spec = NULL; if (spec == NULL) return (ENXIO); /* Set defaults. */ di->bas.chan = 0; di->bas.regshft = 0; di->bas.rclk = 0; di->baudrate = 0; di->databits = 8; di->stopbits = 1; di->parity = UART_PARITY_NONE; /* Parse the attributes. */ while (1) { switch (uart_parse_tag(&spec)) { case UART_TAG_BR: di->baudrate = uart_parse_long(&spec); break; case UART_TAG_CH: di->bas.chan = uart_parse_long(&spec); break; case UART_TAG_DB: di->databits = uart_parse_long(&spec); break; case UART_TAG_DT: class = uart_parse_class(class, &spec); break; case UART_TAG_IO: di->bas.bst = uart_bus_space_io; addr = uart_parse_addr(&spec); break; case UART_TAG_MM: di->bas.bst = uart_bus_space_mem; addr = uart_parse_addr(&spec); break; case UART_TAG_PA: di->parity = uart_parse_parity(&spec); break; case UART_TAG_RS: di->bas.regshft = uart_parse_long(&spec); break; case UART_TAG_SB: di->stopbits = uart_parse_long(&spec); break; case UART_TAG_XO: di->bas.rclk = uart_parse_long(&spec); break; default: return (EINVAL); } if (*spec == '\0') break; if (*spec != ',') return (EINVAL); spec++; } /* * If we still have an invalid address, the specification must be * missing an I/O port or memory address. We don't like that. */ if (addr == ~0U) return (EINVAL); /* * Accept only the well-known baudrates. Any invalid baudrate * is silently replaced with a 0-valued baudrate. The 0 baudrate * has special meaning. It means that we're not supposed to * program the baudrate and simply communicate with whatever * speed the hardware is currently programmed for. */ if (di->baudrate >= 19200) { if (di->baudrate % 19200) di->baudrate = 0; } else if (di->baudrate >= 1200) { if (di->baudrate % 1200) di->baudrate = 0; } else if (di->baudrate > 0) { if (di->baudrate % 75) di->baudrate = 0; } else di->baudrate = 0; /* Set the ops and create a bus space handle. */ di->ops = uart_getops(class); error = bus_space_map(di->bas.bst, addr, uart_getrange(class), 0, &di->bas.bsh); return (error); } Index: stable/10 =================================================================== --- stable/10 (revision 283326) +++ stable/10 (revision 283327) Property changes on: stable/10 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r279723-279724