/* * Copyright (c) 2009 Hypertriton, Inc. * 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 COPYRIGHT HOLDERS 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. */ /* * Copyright (c) 1997 - 1999, Jason Downs. 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(S) ``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(S) 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. */ /*- * Copyright (c) 1993, 1994, 1995, 1996 * Charles M. Hannum. All rights reserved. * Copyright (c) 1991 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. * * @(#)com.c 7.5 (Berkeley) 5/16/91 */ /* * 16450/16550 UART driver modified for text-based LCD output and polled-mode * operation. This allows a standard serial link to be used in interfacing * with common serial LCD control ICs such as the EDE700 or EDE702. * * Polled-mode operation is useful for debugging, and is the cnc(4) subsystem's * only way of displaying information during program execution in real time. */ #include #include #include #include #include #include #include #include #include #define com_lcr com_cfcr #include #include #include struct cfdriver cnclcd_cd = { NULL, "cnclcd", DV_DULL }; void cnclcd_attach_fifo(struct cnclcd_softc *); void cnclcd_attach(struct cnclcd_softc *sc) { bus_space_tag_t iot = sc->sc_iot; bus_space_handle_t ioh = sc->sc_ioh; /* Generic CNC device initialization. */ if (cnc_device_attach(sc, CNC_DEVICE_LCD) == -1) return; sc->sc_open = 0; sc->sc_flags = 0; /* Disable interrupts */ bus_space_write_1(iot, ioh, com_ier, 0); /* * Probe for all known forms of UART. */ bus_space_write_1(iot, ioh, com_lcr, LCR_EFR); bus_space_write_1(iot, ioh, com_efr, 0); bus_space_write_1(iot, ioh, com_lcr, 0); bus_space_write_1(iot, ioh, com_fifo, FIFO_ENABLE); delay(100); /* * Skip specific probes if attachment code knows it already. */ switch (bus_space_read_1(iot, ioh, com_iir) >> 6) { case 0: sc->sc_uarttype = COM_UART_16450; break; case 2: sc->sc_uarttype = COM_UART_16550; break; case 3: sc->sc_uarttype = COM_UART_16550A; break; default: sc->sc_uarttype = COM_UART_UNKNOWN; break; } /* Read the LCR */ sc->sc_lcr = bus_space_read_1(iot, ioh, com_lcr); if (sc->sc_uarttype == COM_UART_16550A) { /* Probe for ST16650s */ bus_space_write_1(iot, ioh, com_lcr, sc->sc_lcr|LCR_DLAB); if (bus_space_read_1(iot, ioh, com_efr) == 0) { sc->sc_uarttype = COM_UART_ST16650; } else { bus_space_write_1(iot, ioh, com_lcr, LCR_EFR); if (bus_space_read_1(iot, ioh, com_efr) == 0) sc->sc_uarttype = COM_UART_ST16650V2; } } if (sc->sc_uarttype == COM_UART_16550A) { /* Probe for TI16750s */ bus_space_write_1(iot, ioh, com_lcr, sc->sc_lcr|LCR_DLAB); bus_space_write_1(iot, ioh, com_fifo, FIFO_ENABLE | FIFO_ENABLE_64BYTE); if ((bus_space_read_1(iot, ioh, com_iir) >> 5) == 7) { #if 0 bus_space_write_1(iot, ioh, com_lcr, 0); if ((bus_space_read_1(iot, ioh, com_iir) >> 5) == 6) #endif sc->sc_uarttype = COM_UART_TI16750; } bus_space_write_1(iot, ioh, com_fifo, FIFO_ENABLE); } /* Reset the LCR (latch access is probably enabled). */ bus_space_write_1(iot, ioh, com_lcr, sc->sc_lcr); if (sc->sc_uarttype == COM_UART_16450) { /* Probe for 8250 */ u_int8_t scr0, scr1, scr2; scr0 = bus_space_read_1(iot, ioh, com_scratch); bus_space_write_1(iot, ioh, com_scratch, 0xa5); scr1 = bus_space_read_1(iot, ioh, com_scratch); bus_space_write_1(iot, ioh, com_scratch, 0x5a); scr2 = bus_space_read_1(iot, ioh, com_scratch); bus_space_write_1(iot, ioh, com_scratch, scr0); if ((scr1 != 0xa5) || (scr2 != 0x5a)) sc->sc_uarttype = COM_UART_8250; } /* * Print UART type and initialize ourself. */ sc->sc_fifolen = 0; switch (sc->sc_uarttype) { case COM_UART_UNKNOWN: printf(": unknown uart\n"); break; case COM_UART_8250: printf(": ns8250, no fifo\n"); break; case COM_UART_16450: printf(": ns16450, no fifo\n"); break; case COM_UART_16550: printf(": ns16550, no working fifo\n"); break; case COM_UART_16550A: sc->sc_fifolen = 16; printf(": ns16550a, %d byte fifo\n", sc->sc_fifolen); SET(sc->sc_hwflags, COM_HW_FIFO); break; case COM_UART_ST16650: printf(": st16650, no working fifo\n"); break; case COM_UART_ST16650V2: sc->sc_fifolen = 32; printf(": st16650, %d byte fifo\n", sc->sc_fifolen); SET(sc->sc_hwflags, COM_HW_FIFO); break; case COM_UART_ST16C654: printf(": st16c654, 64 byte fifo\n"); SET(sc->sc_hwflags, COM_HW_FIFO); sc->sc_fifolen = 64; break; case COM_UART_TI16750: printf(": ti16750, 64 byte fifo\n"); SET(sc->sc_hwflags, COM_HW_FIFO); sc->sc_fifolen = 64; break; default: panic("cnclcd_attach: bad fifo type"); } if (ISSET(sc->sc_hwflags, COM_HW_FIFO)) { cnclcd_attach_fifo(sc); } if (sc->sc_fifolen == 0) { CLR(sc->sc_hwflags, COM_HW_FIFO); sc->sc_fifolen = 1; } /* clear and disable fifo */ bus_space_write_1(iot, ioh, com_fifo, FIFO_RCV_RST | FIFO_XMT_RST); (void)bus_space_read_1(iot, ioh, com_data); bus_space_write_1(iot, ioh, com_fifo, 0); sc->sc_msr = 0; sc->sc_mcr = bus_space_read_1(iot, ioh, com_mcr); } void cnclcd_attach_fifo(struct cnclcd_softc *sc) { bus_space_handle_t ioh = sc->sc_ioh; bus_space_tag_t iot = sc->sc_iot; u_int8_t fifo; int timo, len; bus_space_write_1(iot, ioh, com_ier, 0); bus_space_write_1(iot, ioh, com_lcr, LCR_DLAB); bus_space_write_1(iot, ioh, com_dlbl, 3); bus_space_write_1(iot, ioh, com_dlbh, 0); bus_space_write_1(iot, ioh, com_lcr, LCR_PNONE | LCR_8BITS); bus_space_write_1(iot, ioh, com_mcr, MCR_LOOPBACK); fifo = FIFO_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST; if (sc->sc_uarttype == COM_UART_TI16750) fifo |= FIFO_ENABLE_64BYTE; bus_space_write_1(iot, ioh, com_fifo, fifo); for (len = 0; len < 256; len++) { bus_space_write_1(iot, ioh, com_data, (len + 1)); timo = 2000; while (!ISSET(bus_space_read_1(iot, ioh, com_lsr), LSR_TXRDY) && --timo) delay(1); if (!timo) break; } delay(100); for (len = 0; len < 256; len++) { timo = 2000; while (!ISSET(bus_space_read_1(iot,ioh,com_lsr),LSR_RXRDY) && --timo) { delay(1); } if (!timo || bus_space_read_1(iot, ioh, com_data) != (len + 1)) break; } /* For safety, always use the smaller value. */ if (sc->sc_fifolen > len) { sc->sc_fifolen = len; } printf("%s: probed fifo depth: %d bytes\n", ((struct device *)sc)->dv_xname, len); } int cnclcd_speed(long freq, long speed) { #define divrnd(n, q) (((n)*2/(q)+1)/2) /* divide and round off */ int x, err; if (speed == 0) { return (0); } if (speed < 0) { return (-1); } x = divrnd((freq / 16), speed); if (x <= 0) { return (-1); } err = divrnd((quad_t)freq * 1000 / 16, speed * x) - 1000; if (err < 0) { err = -err; } if (err > COM_TOLERANCE) { return (-1); } return (x); #undef divrnd } int cnclcd_open(struct cnclcd_softc *sc, int speed, int bits, enum cnclcd_parity parity, int stopbits) { bus_space_tag_t iot = sc->sc_iot; bus_space_handle_t ioh = sc->sc_ioh; int s, rate; if (sc->sc_open) return (-1); s = spltty(); sc->sc_speed = speed; /* Clear DTR */ CLR(sc->sc_mcr, MCR_DTR); bus_space_write_1(iot, ioh, com_mcr, sc->sc_mcr); /* Configure bits */ CLR(sc->sc_lcr, LCR_5BITS); CLR(sc->sc_lcr, LCR_6BITS); CLR(sc->sc_lcr, LCR_7BITS); CLR(sc->sc_lcr, LCR_8BITS); switch (bits) { case 5: SET(sc->sc_lcr, LCR_5BITS); break; case 6: SET(sc->sc_lcr, LCR_6BITS); break; case 7: SET(sc->sc_lcr, LCR_7BITS); break; case 8: SET(sc->sc_lcr, LCR_8BITS); break; default: break; } /* Configure parity and stop bits */ if (parity != CNCLCD_PNONE) { SET(sc->sc_lcr, LCR_PENAB); switch (parity) { case CNCLCD_PZERO: SET(sc->sc_lcr, LCR_PZERO); break; case CNCLCD_PONE: SET(sc->sc_lcr, LCR_PONE); break; case CNCLCD_PEVEN: SET(sc->sc_lcr, LCR_PEVEN); break; case CNCLCD_PODD: SET(sc->sc_lcr, LCR_PODD); break; default: panic("cnclcd_open: parity"); } } else { CLR(sc->sc_lcr, LCR_PENAB); CLR(sc->sc_lcr, LCR_PZERO); CLR(sc->sc_lcr, LCR_PONE); CLR(sc->sc_lcr, LCR_PEVEN); CLR(sc->sc_lcr, LCR_PODD); } if (stopbits == 2) { SET(sc->sc_lcr, LCR_STOPB); } else { CLR(sc->sc_lcr, LCR_STOPB); } bus_space_write_1(iot, ioh, com_lcr, sc->sc_lcr); /* Configure speed */ bus_space_write_1(iot, ioh, com_lcr, sc->sc_lcr|LCR_DLAB); rate = cnclcd_speed(COM_FREQ, sc->sc_speed); bus_space_write_1(iot, ioh, com_dlbl, rate); bus_space_write_1(iot, ioh, com_dlbh, rate >> 8); bus_space_write_1(iot, ioh, com_lcr, sc->sc_lcr); /* Set the FIFO threshold based on the speed. */ if (ISSET(sc->sc_hwflags, COM_HW_FIFO)) { if (sc->sc_uarttype == COM_UART_TI16750) { bus_space_write_1(iot, ioh, com_lcr, sc->sc_lcr|LCR_DLAB); bus_space_write_1(iot, ioh, com_fifo, FIFO_ENABLE | FIFO_ENABLE_64BYTE | (sc->sc_speed <= 1200 ? FIFO_TRIGGER_1 : FIFO_TRIGGER_8)); bus_space_write_1(iot, ioh, com_lcr, sc->sc_lcr); } else { bus_space_write_1(iot, ioh, com_fifo, FIFO_ENABLE | (sc->sc_speed <= 1200 ? FIFO_TRIGGER_1 : FIFO_TRIGGER_8)); } } else { bus_space_write_1(iot, ioh, com_lcr, sc->sc_lcr); } /* * Wake up the sleepy heads. */ switch (sc->sc_uarttype) { case COM_UART_ST16650: case COM_UART_ST16650V2: bus_space_write_1(iot, ioh, com_lcr, LCR_EFR); bus_space_write_1(iot, ioh, com_efr, EFR_ECB); bus_space_write_1(iot, ioh, com_ier, 0); bus_space_write_1(iot, ioh, com_efr, 0); bus_space_write_1(iot, ioh, com_lcr, 0); break; case COM_UART_TI16750: bus_space_write_1(iot, ioh, com_ier, 0); break; } if (ISSET(sc->sc_hwflags, COM_HW_FIFO)) { u_int8_t fifo = FIFO_ENABLE|FIFO_RCV_RST|FIFO_XMT_RST; if (sc->sc_speed <= 1200) { fifo |= FIFO_TRIGGER_1; } else { fifo |= FIFO_TRIGGER_8; } if (sc->sc_uarttype == COM_UART_TI16750) { fifo |= FIFO_ENABLE_64BYTE; bus_space_write_1(iot, ioh, com_lcr, sc->sc_lcr|LCR_DLAB); } /* * (Re)enable and drain FIFOs. * * Certain SMC chips cause problems if the FIFOs are * enabled while input is ready. Turn off the FIFO * if necessary to clear the input. Test the input * ready bit after enabling the FIFOs to handle races * between enabling and fresh input. * * Set the FIFO threshold based on the receive speed. */ for (;;) { bus_space_write_1(iot, ioh, com_fifo, 0); delay(100); (void)bus_space_read_1(iot, ioh, com_data); bus_space_write_1(iot, ioh, com_fifo, fifo | FIFO_RCV_RST | FIFO_XMT_RST); delay(100); if (!ISSET(bus_space_read_1(iot,ioh,com_lsr),LSR_RXRDY)) break; } if (sc->sc_uarttype == COM_UART_TI16750) bus_space_write_1(iot, ioh, com_lcr, sc->sc_lcr); } /* Flush any pending I/O. */ while (ISSET(bus_space_read_1(iot, ioh, com_lsr), LSR_RXRDY)) (void)bus_space_read_1(iot, ioh, com_data); /* Set DTR|RTS */ SET(sc->sc_mcr, MCR_DTR|MCR_RTS); bus_space_write_1(sc->sc_iot, sc->sc_ioh, com_mcr, sc->sc_mcr); /* Read initial MSR */ sc->sc_msr = bus_space_read_1(iot, ioh, com_msr); sc->sc_open = 1; splx(s); return (0); } int cnclcd_close(struct cnclcd_softc *sc) { int s; s = spltty(); if (sc->sc_open) { CLR(sc->sc_mcr, MCR_DTR|MCR_RTS); bus_space_write_1(sc->sc_iot, sc->sc_ioh, com_mcr, sc->sc_mcr); sc->sc_open = 0; } splx(s); return (0); } void cnclcd_puts(struct cnclcd_softc *sc, const char *s) { const char *c; for (c = &s[0]; *c != '\0'; c++) cnclcd_putc(sc, *c); } void cnclcd_putc(struct cnclcd_softc *sc, int c) { bus_space_tag_t iot = sc->sc_iot; bus_space_handle_t ioh = sc->sc_ioh; int timo; /* Wait for any pending transmission to finish. */ timo = 2000; while (!ISSET(bus_space_read_1(iot,ioh,com_lsr),LSR_TXRDY) && --timo) delay(1); bus_space_write_1(iot, ioh, com_data, (u_int8_t)(c & 0xff)); bus_space_barrier(iot, ioh, 0, COM_NPORTS, (BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE)); /* Wait for this transmission to complete. */ timo = 2000; while (!ISSET(bus_space_read_1(iot,ioh,com_lsr),LSR_TXRDY) && --timo) delay(1); } #if 0 void cnclcd_init(bus_space_tag_t iot, bus_space_handle_t ioh, int rate, int frequency) { int s; s = splhigh(); bus_space_write_1(iot, ioh, com_lcr, LCR_DLAB); rate = cnclcd_speed(frequency, rate); /* XXX not cnclcd_default_rate? */ bus_space_write_1(iot, ioh, com_dlbl, rate); bus_space_write_1(iot, ioh, com_dlbh, rate >> 8); bus_space_write_1(iot, ioh, com_lcr, LCR_8BITS); bus_space_write_1(iot, ioh, com_mcr, MCR_DTR | MCR_RTS); bus_space_write_1(iot, ioh, com_ier, 0); /* Make sure they are off */ bus_space_write_1(iot, ioh, com_fifo, FIFO_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST | FIFO_TRIGGER_1); splx(s); } #endif