/* $OpenBSD: cn30xxuart.c,v 1.11 2017/09/21 12:50:20 visa Exp $ */ /* * Copyright (c) 2001-2004 Opsycon AB (www.opsycon.se / www.opsycon.com) * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define OCTEON_UART_FIFO_SIZE 64 int cn30xxuart_probe(struct device *, void *, void *); void cn30xxuart_attach(struct device *, struct device *, void *); int cn30xxuart_intr(void *); struct cfattach cn30xxuart_ca = { sizeof(struct com_softc), cn30xxuart_probe, cn30xxuart_attach }; extern struct cfdriver com_cd; cons_decl(cn30xxuart); #define USR_TXFIFO_NOTFULL 2 /* XXX: What is this used for? Removed from stand/boot/uart.c -r1.2 */ static int delay_changed = 1; int cn30xxuart_delay(void); void cn30xxuart_wait_txhr_empty(int); uint8_t uartbus_read_1(bus_space_tag_t, bus_space_handle_t, bus_size_t); void uartbus_write_1(bus_space_tag_t, bus_space_handle_t, bus_size_t, uint8_t); bus_space_t uartbus_tag = { .bus_base = PHYS_TO_XKPHYS(0, CCA_NC), .bus_private = NULL, ._space_read_1 = uartbus_read_1, ._space_write_1 = uartbus_write_1, ._space_map = iobus_space_map, ._space_unmap = iobus_space_unmap }; void com_fdt_init_cons(void) { comconsiot = &uartbus_tag; comconsaddr = OCTEON_UART0_BASE; comconsfreq = octeon_ioclock_speed(); comconsrate = B115200; comconscflag = (TTYDEF_CFLAG & ~(CSIZE | PARENB)) | CS8; } int cn30xxuart_probe(struct device *parent, void *match, void *aux) { struct fdt_attach_args *faa = aux; return OF_is_compatible(faa->fa_node, "cavium,octeon-3860-uart"); } void cn30xxuart_attach(struct device *parent, struct device *self, void *aux) { struct fdt_attach_args *faa = aux; struct com_softc *sc = (void *)self; int console = 0; if (faa->fa_nreg != 1) return; if (comconsiot == &uartbus_tag && comconsaddr == faa->fa_reg[0].addr) console = 1; sc->sc_hwflags = 0; sc->sc_swflags = 0; sc->sc_frequency = octeon_ioclock_speed(); sc->sc_uarttype = COM_UART_16550A; sc->sc_fifolen = OCTEON_UART_FIFO_SIZE; if (!console || comconsattached) { sc->sc_iot = &uartbus_tag; sc->sc_iobase = faa->fa_reg[0].addr; if (bus_space_map(sc->sc_iot, sc->sc_iobase, COM_NPORTS, 0, &sc->sc_ioh)) { printf(": could not map UART registers\n"); return; } } else { /* Reuse the early console settings. */ sc->sc_iot = comconsiot; sc->sc_iobase = comconsaddr; if (comcnattach(sc->sc_iot, sc->sc_iobase, comconsrate, sc->sc_frequency, comconscflag)) panic("could not set up serial console"); sc->sc_ioh = comconsioh; } com_attach_subr(sc); octeon_intr_establish_fdt(faa->fa_node, IPL_TTY, cn30xxuart_intr, sc, sc->sc_dev.dv_xname); } int cn30xxuart_intr(void *arg) { comintr(arg); /* * Always return non-zero to prevent console clutter about spurious * interrupts. comstart() enables the transmitter holding register * empty interrupt before adding data to the FIFO, which can trigger * a premature interrupt on the primary CPU in a multiprocessor system. */ return 1; } /* * Early console routines. */ int cn30xxuart_delay(void) { int divisor; u_char lcr; static int d = 0; if (!delay_changed) return d; delay_changed = 0; lcr = octeon_xkphys_read_8(MIO_UART0_LCR); octeon_xkphys_write_8(MIO_UART0_LCR, lcr | LCR_DLAB); divisor = octeon_xkphys_read_8(MIO_UART0_DLL) | octeon_xkphys_read_8(MIO_UART0_DLH) << 8; octeon_xkphys_write_8(MIO_UART0_LCR, lcr); return 10; /* return an approx delay value */ } void cn30xxuart_wait_txhr_empty(int d) { while (((octeon_xkphys_read_8(MIO_UART0_LSR) & LSR_TXRDY) == 0) && ((octeon_xkphys_read_8(MIO_UART0_USR) & USR_TXFIFO_NOTFULL) == 0)) delay(d); } void cn30xxuartcninit(struct consdev *consdev) { } void cn30xxuartcnprobe(struct consdev *consdev) { } void cn30xxuartcnpollc(dev_t dev, int c) { } void cn30xxuartcnputc(dev_t dev, int c) { int d; /* 1/10th the time to transmit 1 character (estimate). */ d = cn30xxuart_delay(); cn30xxuart_wait_txhr_empty(d); octeon_xkphys_write_8(MIO_UART0_RBR, (uint8_t)c); cn30xxuart_wait_txhr_empty(d); } int cn30xxuartcngetc(dev_t dev) { int c, d; /* 1/10th the time to transmit 1 character (estimate). */ d = cn30xxuart_delay(); while ((octeon_xkphys_read_8(MIO_UART0_LSR) & LSR_RXRDY) == 0) delay(d); c = (uint8_t)octeon_xkphys_read_8(MIO_UART0_RBR); return (c); } /* * Bus access routines. These let com(4) work with the 64-bit registers. */ uint8_t uartbus_read_1(bus_space_tag_t tag, bus_space_handle_t handle, bus_size_t off) { return *(volatile uint64_t *)(handle + (off << 3)); } void uartbus_write_1(bus_space_tag_t tag, bus_space_handle_t handle, bus_size_t off, uint8_t value) { volatile uint64_t *reg = (uint64_t *)(handle + (off << 3)); *reg = value; (void)*reg; }