/* $OpenBSD: com_puc.c,v 1.7 2004/08/19 21:47:54 miod Exp $ */ /* * 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. * 3. Neither the name(s) of the author(s) nor the name OpenBSD * may be used to endorse or promote products derived from this software * without specific prior written permission. * * 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef i386 #include /* XXX */ #endif #include "com.h" #ifdef i386 #include "pccom.h" #endif #include #if NPCCOM > 0 #include #endif #if NCOM > 0 #include #endif #include #define com_lcr com_cfcr int com_puc_match(struct device *, void *, void *); void com_puc_attach(struct device *, struct device *, void *); #if NCOM_PUC struct cfattach com_puc_ca = { sizeof(struct com_softc), com_puc_match, com_puc_attach }; #endif #if NPCCOM_PUC struct cfattach pccom_puc_ca = { sizeof(struct com_softc), com_puc_match, com_puc_attach }; #endif void com_puc_attach2(struct com_softc *); int com_puc_match(parent, match, aux) struct device *parent; void *match, *aux; { struct puc_attach_args *pa = aux; if (pa->type == PUC_PORT_TYPE_COM) return(1); return(0); } void com_puc_attach(parent, self, aux) struct device *parent, *self; void *aux; { struct com_softc *sc = (void *)self; struct puc_attach_args *pa = aux; const char *intrstr; /* Grab a PCI interrupt. */ intrstr = pci_intr_string(pa->pc, pa->intrhandle); sc->sc_ih = pci_intr_establish(pa->pc, pa->intrhandle, IPL_HIGH, comintr, sc, sc->sc_dev.dv_xname); if (sc->sc_ih == NULL) { printf(": couldn't establish interrupt"); if (intrstr != NULL) printf(" at %s", intrstr); printf("\n"); return; } printf(" %s", intrstr); sc->sc_iot = pa->t; sc->sc_ioh = pa->h; sc->sc_iobase = pa->a; sc->sc_frequency = COM_FREQ; if (pa->flags) sc->sc_frequency = pa->flags & PUC_COM_CLOCKMASK; com_puc_attach2(sc); } /* * XXX This should be handled by a generic attach */ void com_puc_attach2(sc) struct com_softc *sc; { bus_space_tag_t iot = sc->sc_iot; bus_space_handle_t ioh = sc->sc_ioh; u_int8_t lcr; sc->sc_hwflags = 0; sc->sc_swflags = 0; /* * Probe for all known forms of UART. */ lcr = bus_space_read_1(iot, ioh, com_lcr); 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); 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; } if (sc->sc_uarttype == COM_UART_16550A) { /* Probe for ST16650s */ bus_space_write_1(iot, ioh, com_lcr, 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 NPCCOM > 0 #ifdef i386 if (sc->sc_uarttype == COM_UART_ST16650V2) { /* Probe for XR16850s */ u_int8_t dlbl, dlbh; /* Enable latch access and get the current values. */ bus_space_write_1(iot, ioh, com_lcr, lcr | LCR_DLAB); dlbl = bus_space_read_1(iot, ioh, com_dlbl); dlbh = bus_space_read_1(iot, ioh, com_dlbh); /* Zero out the latch divisors */ bus_space_write_1(iot, ioh, com_dlbl, 0); bus_space_write_1(iot, ioh, com_dlbh, 0); if (bus_space_read_1(iot, ioh, com_dlbh) == 0x10) { sc->sc_uarttype = COM_UART_XR16850; sc->sc_uartrev = bus_space_read_1(iot, ioh, com_dlbl); } /* Reset to original. */ bus_space_write_1(iot, ioh, com_dlbl, dlbl); bus_space_write_1(iot, ioh, com_dlbh, dlbh); } #endif #endif /* Reset the LCR (latch access is probably enabled). */ bus_space_write_1(iot, ioh, com_lcr, 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 = 1; /* default */ 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: printf(": ns16550a, 16 byte fifo\n"); SET(sc->sc_hwflags, COM_HW_FIFO); sc->sc_fifolen = 16; break; case COM_UART_ST16650: printf(": st16650, no working fifo\n"); break; case COM_UART_ST16650V2: printf(": st16650, 32 byte fifo\n"); SET(sc->sc_hwflags, COM_HW_FIFO); sc->sc_fifolen = 32; break; case COM_UART_TI16750: printf(": ti16750, 64 byte fifo\n"); SET(sc->sc_hwflags, COM_HW_FIFO); sc->sc_fifolen = 64; break; #if NPCCOM > 0 #ifdef i386 case COM_UART_XR16850: printf(": xr16850 (rev %d), 128 byte fifo\n", sc->sc_uartrev); SET(sc->sc_hwflags, COM_HW_FIFO); sc->sc_fifolen = 128; break; #endif #endif default: panic("com_puc_attach2: bad fifo type"); } /* 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_mcr = 0; bus_space_write_1(iot, ioh, com_mcr, sc->sc_mcr); timeout_set(&sc->sc_diag_tmo, comdiag, sc); timeout_set(&sc->sc_dtr_tmo, com_raisedtr, sc); #if NCOM > 0 #ifdef __HAVE_GENERIC_SOFT_INTERRUPTS sc->sc_si = softintr_establish(IPL_TTY, comsoft, sc); if (sc->sc_si == NULL) panic("%s: can't establish soft interrupt.", sc->sc_dev.dv_xname); #else timeout_set(&sc->sc_comsoft_tmo, comsoft, sc); #endif #endif /* * If there are no enable/disable functions, assume the device * is always enabled. */ if (!sc->enable) sc->enabled = 1; }