1*c4071fd1Smillert /* $OpenBSD: com_puc.c,v 1.5 2002/03/14 01:27:01 millert Exp $ */ 25a7928a9Sdownsj 35a7928a9Sdownsj /* 45a7928a9Sdownsj * Copyright (c) 1997 - 1999, Jason Downs. All rights reserved. 55a7928a9Sdownsj * 65a7928a9Sdownsj * Redistribution and use in source and binary forms, with or without 75a7928a9Sdownsj * modification, are permitted provided that the following conditions 85a7928a9Sdownsj * are met: 95a7928a9Sdownsj * 1. Redistributions of source code must retain the above copyright 105a7928a9Sdownsj * notice, this list of conditions and the following disclaimer. 115a7928a9Sdownsj * 2. Redistributions in binary form must reproduce the above copyright 125a7928a9Sdownsj * notice, this list of conditions and the following disclaimer in the 135a7928a9Sdownsj * documentation and/or other materials provided with the distribution. 145a7928a9Sdownsj * 3. Neither the name(s) of the author(s) nor the name OpenBSD 155a7928a9Sdownsj * may be used to endorse or promote products derived from this software 165a7928a9Sdownsj * without specific prior written permission. 175a7928a9Sdownsj * 185a7928a9Sdownsj * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS 195a7928a9Sdownsj * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 205a7928a9Sdownsj * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 215a7928a9Sdownsj * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, 225a7928a9Sdownsj * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 235a7928a9Sdownsj * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 245a7928a9Sdownsj * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 255a7928a9Sdownsj * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 265a7928a9Sdownsj * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 275a7928a9Sdownsj * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 285a7928a9Sdownsj * SUCH DAMAGE. 295a7928a9Sdownsj */ 305a7928a9Sdownsj 315a7928a9Sdownsj #include <sys/param.h> 325a7928a9Sdownsj #include <sys/systm.h> 335a7928a9Sdownsj #include <sys/ioctl.h> 345a7928a9Sdownsj #include <sys/select.h> 355a7928a9Sdownsj #include <sys/tty.h> 365a7928a9Sdownsj #include <sys/proc.h> 375a7928a9Sdownsj #include <sys/user.h> 385a7928a9Sdownsj #include <sys/conf.h> 395a7928a9Sdownsj #include <sys/file.h> 405a7928a9Sdownsj #include <sys/uio.h> 415a7928a9Sdownsj #include <sys/kernel.h> 425a7928a9Sdownsj #include <sys/syslog.h> 435a7928a9Sdownsj #include <sys/types.h> 445a7928a9Sdownsj #include <sys/device.h> 455a7928a9Sdownsj 465a7928a9Sdownsj #include <machine/intr.h> 475a7928a9Sdownsj #include <machine/bus.h> 485a7928a9Sdownsj 495a7928a9Sdownsj #include <dev/pci/pcivar.h> 505a7928a9Sdownsj #include <dev/pci/pucvar.h> 515a7928a9Sdownsj 525a7928a9Sdownsj #include <dev/isa/isavar.h> /* XXX */ 535a7928a9Sdownsj 545a7928a9Sdownsj #include "com.h" 555a7928a9Sdownsj #ifdef i386 565a7928a9Sdownsj #include "pccom.h" 575a7928a9Sdownsj #endif 585a7928a9Sdownsj 595a7928a9Sdownsj #include <dev/ic/comreg.h> 605a7928a9Sdownsj #if NPCCOM > 0 615a7928a9Sdownsj #include <i386/isa/pccomvar.h> 625a7928a9Sdownsj #endif 635a7928a9Sdownsj #if NCOM > 0 645a7928a9Sdownsj #include <dev/ic/comvar.h> 655a7928a9Sdownsj #endif 665a7928a9Sdownsj #include <dev/ic/ns16550reg.h> 675a7928a9Sdownsj 685a7928a9Sdownsj #define com_lcr com_cfcr 695a7928a9Sdownsj 70*c4071fd1Smillert int com_puc_match(struct device *, void *, void *); 71*c4071fd1Smillert void com_puc_attach(struct device *, struct device *, void *); 725a7928a9Sdownsj 735a7928a9Sdownsj #if NCOM_PUC 745a7928a9Sdownsj struct cfattach com_puc_ca = { 755a7928a9Sdownsj sizeof(struct com_softc), com_puc_match, com_puc_attach 765a7928a9Sdownsj }; 775a7928a9Sdownsj #endif 785a7928a9Sdownsj 795a7928a9Sdownsj #if NPCCOM_PUC 805a7928a9Sdownsj struct cfattach pccom_puc_ca = { 815a7928a9Sdownsj sizeof(struct com_softc), com_puc_match, com_puc_attach 825a7928a9Sdownsj }; 835a7928a9Sdownsj #endif 845a7928a9Sdownsj 85*c4071fd1Smillert void com_puc_attach2(struct com_softc *); 865a7928a9Sdownsj 875a7928a9Sdownsj int 885a7928a9Sdownsj com_puc_match(parent, match, aux) 895a7928a9Sdownsj struct device *parent; 905a7928a9Sdownsj void *match, *aux; 915a7928a9Sdownsj { 925a7928a9Sdownsj struct puc_attach_args *pa = aux; 935a7928a9Sdownsj 945a7928a9Sdownsj if (pa->type == PUC_PORT_TYPE_COM) 955a7928a9Sdownsj return(1); 965a7928a9Sdownsj 975a7928a9Sdownsj return(0); 985a7928a9Sdownsj } 995a7928a9Sdownsj 1005a7928a9Sdownsj void 1015a7928a9Sdownsj com_puc_attach(parent, self, aux) 1025a7928a9Sdownsj struct device *parent, *self; 1035a7928a9Sdownsj void *aux; 1045a7928a9Sdownsj { 1055a7928a9Sdownsj struct com_softc *sc = (void *)self; 1065a7928a9Sdownsj struct puc_attach_args *pa = aux; 1075a7928a9Sdownsj const char *intrstr; 1085a7928a9Sdownsj 1095a7928a9Sdownsj /* Grab a PCI interrupt. */ 1105a7928a9Sdownsj intrstr = pci_intr_string(pa->pc, pa->intrhandle); 1115a7928a9Sdownsj sc->sc_ih = pci_intr_establish(pa->pc, pa->intrhandle, 1125a7928a9Sdownsj IPL_HIGH, comintr, sc, 1135a7928a9Sdownsj sc->sc_dev.dv_xname); 1145a7928a9Sdownsj if (sc->sc_ih == NULL) { 1155a7928a9Sdownsj printf(": couldn't establish interrupt"); 1165a7928a9Sdownsj if (intrstr != NULL) 1175a7928a9Sdownsj printf(" at %s", intrstr); 1185a7928a9Sdownsj printf("\n"); 1195a7928a9Sdownsj return; 1205a7928a9Sdownsj } 1215a7928a9Sdownsj printf(" %s", intrstr); 1225a7928a9Sdownsj 1235a7928a9Sdownsj sc->sc_iot = pa->t; 1245a7928a9Sdownsj sc->sc_ioh = pa->h; 1255a7928a9Sdownsj sc->sc_iobase = pa->a; 12698e273d3Sderaadt sc->sc_frequency = COM_FREQ; 12778a91e64Smickey 12898e273d3Sderaadt if (pa->flags) 12998e273d3Sderaadt sc->sc_frequency = pa->flags & PUC_COM_CLOCKMASK; 1305a7928a9Sdownsj 1315a7928a9Sdownsj com_puc_attach2(sc); 1325a7928a9Sdownsj } 1335a7928a9Sdownsj 1345a7928a9Sdownsj /* 1355a7928a9Sdownsj * XXX This should be handled by a generic attach 1365a7928a9Sdownsj */ 1375a7928a9Sdownsj void 1385a7928a9Sdownsj com_puc_attach2(sc) 1395a7928a9Sdownsj struct com_softc *sc; 1405a7928a9Sdownsj { 1415a7928a9Sdownsj bus_space_tag_t iot = sc->sc_iot; 1425a7928a9Sdownsj bus_space_handle_t ioh = sc->sc_ioh; 1435a7928a9Sdownsj u_int8_t lcr; 1445a7928a9Sdownsj 1455a7928a9Sdownsj sc->sc_hwflags = 0; 1465a7928a9Sdownsj sc->sc_swflags = 0; 1475a7928a9Sdownsj 14878a91e64Smickey timeout_set(&sc->sc_dtr_tmo, com_raisedtr, sc); 14978a91e64Smickey timeout_set(&sc->sc_diag_tmo, comdiag, sc); 15078a91e64Smickey 1515a7928a9Sdownsj /* 1525a7928a9Sdownsj * Probe for all known forms of UART. 1535a7928a9Sdownsj */ 1545a7928a9Sdownsj lcr = bus_space_read_1(iot, ioh, com_lcr); 1555a7928a9Sdownsj 1565a7928a9Sdownsj bus_space_write_1(iot, ioh, com_lcr, LCR_EFR); 1575a7928a9Sdownsj bus_space_write_1(iot, ioh, com_efr, 0); 1585a7928a9Sdownsj bus_space_write_1(iot, ioh, com_lcr, 0); 1595a7928a9Sdownsj 1605a7928a9Sdownsj bus_space_write_1(iot, ioh, com_fifo, FIFO_ENABLE); 1615a7928a9Sdownsj delay(100); 1625a7928a9Sdownsj 1635a7928a9Sdownsj switch(bus_space_read_1(iot, ioh, com_iir) >> 6) { 1645a7928a9Sdownsj case 0: 1655a7928a9Sdownsj sc->sc_uarttype = COM_UART_16450; 1665a7928a9Sdownsj break; 1675a7928a9Sdownsj case 2: 1685a7928a9Sdownsj sc->sc_uarttype = COM_UART_16550; 1695a7928a9Sdownsj break; 1705a7928a9Sdownsj case 3: 1715a7928a9Sdownsj sc->sc_uarttype = COM_UART_16550A; 1725a7928a9Sdownsj break; 1735a7928a9Sdownsj default: 1745a7928a9Sdownsj sc->sc_uarttype = COM_UART_UNKNOWN; 1755a7928a9Sdownsj break; 1765a7928a9Sdownsj } 1775a7928a9Sdownsj 1785a7928a9Sdownsj if (sc->sc_uarttype == COM_UART_16550A) { /* Probe for ST16650s */ 1795a7928a9Sdownsj bus_space_write_1(iot, ioh, com_lcr, lcr | LCR_DLAB); 1805a7928a9Sdownsj if (bus_space_read_1(iot, ioh, com_efr) == 0) { 1815a7928a9Sdownsj sc->sc_uarttype = COM_UART_ST16650; 1825a7928a9Sdownsj } else { 1835a7928a9Sdownsj bus_space_write_1(iot, ioh, com_lcr, LCR_EFR); 1845a7928a9Sdownsj if (bus_space_read_1(iot, ioh, com_efr) == 0) 1855a7928a9Sdownsj sc->sc_uarttype = COM_UART_ST16650V2; 1865a7928a9Sdownsj } 1875a7928a9Sdownsj } 1885a7928a9Sdownsj 1895a7928a9Sdownsj #if NPCCOM > 0 1905a7928a9Sdownsj #ifdef i386 1915a7928a9Sdownsj if (sc->sc_uarttype == COM_UART_ST16650V2) { /* Probe for XR16850s */ 1925a7928a9Sdownsj u_int8_t dlbl, dlbh; 1935a7928a9Sdownsj 1945a7928a9Sdownsj /* Enable latch access and get the current values. */ 1955a7928a9Sdownsj bus_space_write_1(iot, ioh, com_lcr, lcr | LCR_DLAB); 1965a7928a9Sdownsj dlbl = bus_space_read_1(iot, ioh, com_dlbl); 1975a7928a9Sdownsj dlbh = bus_space_read_1(iot, ioh, com_dlbh); 1985a7928a9Sdownsj 1995a7928a9Sdownsj /* Zero out the latch divisors */ 2005a7928a9Sdownsj bus_space_write_1(iot, ioh, com_dlbl, 0); 2015a7928a9Sdownsj bus_space_write_1(iot, ioh, com_dlbh, 0); 2025a7928a9Sdownsj 2035a7928a9Sdownsj if (bus_space_read_1(iot, ioh, com_dlbh) == 0x10) { 2045a7928a9Sdownsj sc->sc_uarttype = COM_UART_XR16850; 2055a7928a9Sdownsj sc->sc_uartrev = bus_space_read_1(iot, ioh, com_dlbl); 2065a7928a9Sdownsj } 2075a7928a9Sdownsj 2085a7928a9Sdownsj /* Reset to original. */ 2095a7928a9Sdownsj bus_space_write_1(iot, ioh, com_dlbl, dlbl); 2105a7928a9Sdownsj bus_space_write_1(iot, ioh, com_dlbh, dlbh); 2115a7928a9Sdownsj } 2125a7928a9Sdownsj #endif 2135a7928a9Sdownsj #endif 2145a7928a9Sdownsj 2155a7928a9Sdownsj /* Reset the LCR (latch access is probably enabled). */ 2165a7928a9Sdownsj bus_space_write_1(iot, ioh, com_lcr, lcr); 2175a7928a9Sdownsj if (sc->sc_uarttype == COM_UART_16450) { /* Probe for 8250 */ 2185a7928a9Sdownsj u_int8_t scr0, scr1, scr2; 2195a7928a9Sdownsj 2205a7928a9Sdownsj scr0 = bus_space_read_1(iot, ioh, com_scratch); 2215a7928a9Sdownsj bus_space_write_1(iot, ioh, com_scratch, 0xa5); 2225a7928a9Sdownsj scr1 = bus_space_read_1(iot, ioh, com_scratch); 2235a7928a9Sdownsj bus_space_write_1(iot, ioh, com_scratch, 0x5a); 2245a7928a9Sdownsj scr2 = bus_space_read_1(iot, ioh, com_scratch); 2255a7928a9Sdownsj bus_space_write_1(iot, ioh, com_scratch, scr0); 2265a7928a9Sdownsj 2275a7928a9Sdownsj if ((scr1 != 0xa5) || (scr2 != 0x5a)) 2285a7928a9Sdownsj sc->sc_uarttype = COM_UART_8250; 2295a7928a9Sdownsj } 2305a7928a9Sdownsj 2315a7928a9Sdownsj /* 2325a7928a9Sdownsj * Print UART type and initialize ourself. 2335a7928a9Sdownsj */ 2345a7928a9Sdownsj sc->sc_fifolen = 1; /* default */ 2355a7928a9Sdownsj switch (sc->sc_uarttype) { 2365a7928a9Sdownsj case COM_UART_UNKNOWN: 2375a7928a9Sdownsj printf(": unknown uart\n"); 2385a7928a9Sdownsj break; 2395a7928a9Sdownsj case COM_UART_8250: 2405a7928a9Sdownsj printf(": ns8250, no fifo\n"); 2415a7928a9Sdownsj break; 2425a7928a9Sdownsj case COM_UART_16450: 2435a7928a9Sdownsj printf(": ns16450, no fifo\n"); 2445a7928a9Sdownsj break; 2455a7928a9Sdownsj case COM_UART_16550: 2465a7928a9Sdownsj printf(": ns16550, no working fifo\n"); 2475a7928a9Sdownsj break; 2485a7928a9Sdownsj case COM_UART_16550A: 2495a7928a9Sdownsj printf(": ns16550a, 16 byte fifo\n"); 2505a7928a9Sdownsj SET(sc->sc_hwflags, COM_HW_FIFO); 2515a7928a9Sdownsj sc->sc_fifolen = 16; 2525a7928a9Sdownsj break; 2535a7928a9Sdownsj case COM_UART_ST16650: 2545a7928a9Sdownsj printf(": st16650, no working fifo\n"); 2555a7928a9Sdownsj break; 2565a7928a9Sdownsj case COM_UART_ST16650V2: 2575a7928a9Sdownsj printf(": st16650, 32 byte fifo\n"); 2585a7928a9Sdownsj SET(sc->sc_hwflags, COM_HW_FIFO); 2595a7928a9Sdownsj sc->sc_fifolen = 32; 2605a7928a9Sdownsj break; 2615a7928a9Sdownsj #if NPCCOM > 0 2625a7928a9Sdownsj #ifdef i386 2635a7928a9Sdownsj case COM_UART_XR16850: 2645a7928a9Sdownsj printf(": xr16850 (rev %d), 128 byte fifo\n", sc->sc_uartrev); 2655a7928a9Sdownsj SET(sc->sc_hwflags, COM_HW_FIFO); 2665a7928a9Sdownsj sc->sc_fifolen = 128; 2675a7928a9Sdownsj break; 2685a7928a9Sdownsj #endif 2695a7928a9Sdownsj #endif 2705a7928a9Sdownsj default: 2715a7928a9Sdownsj panic("comattach: bad fifo type"); 2725a7928a9Sdownsj } 2735a7928a9Sdownsj 2745a7928a9Sdownsj /* clear and disable fifo */ 2755a7928a9Sdownsj bus_space_write_1(iot, ioh, com_fifo, FIFO_RCV_RST | FIFO_XMT_RST); 2765a7928a9Sdownsj (void)bus_space_read_1(iot, ioh, com_data); 2775a7928a9Sdownsj bus_space_write_1(iot, ioh, com_fifo, 0); 2785a7928a9Sdownsj } 279