xref: /openbsd/sys/dev/puc/com_puc.c (revision c4071fd1)
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