xref: /openbsd/sys/dev/puc/com_puc.c (revision 78a91e64)
1*78a91e64Smickey /*	$OpenBSD: com_puc.c,v 1.3 2001/08/08 15:10:03 mickey 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 #define	SET(t, f)	(t) |= (f)
705a7928a9Sdownsj 
715a7928a9Sdownsj int com_puc_match __P((struct device *, void *, void *));
725a7928a9Sdownsj void com_puc_attach __P((struct device *, struct device *, void *));
735a7928a9Sdownsj 
745a7928a9Sdownsj #if NCOM_PUC
755a7928a9Sdownsj struct cfattach com_puc_ca = {
765a7928a9Sdownsj 	sizeof(struct com_softc), com_puc_match, com_puc_attach
775a7928a9Sdownsj };
785a7928a9Sdownsj #endif
795a7928a9Sdownsj 
805a7928a9Sdownsj #if NPCCOM_PUC
815a7928a9Sdownsj struct cfattach pccom_puc_ca = {
825a7928a9Sdownsj 	sizeof(struct com_softc), com_puc_match, com_puc_attach
835a7928a9Sdownsj };
845a7928a9Sdownsj #endif
855a7928a9Sdownsj 
865a7928a9Sdownsj void com_puc_attach2 __P((struct com_softc *));
875a7928a9Sdownsj 
885a7928a9Sdownsj int
895a7928a9Sdownsj com_puc_match(parent, match, aux)
905a7928a9Sdownsj 	struct device *parent;
915a7928a9Sdownsj 	void *match, *aux;
925a7928a9Sdownsj {
935a7928a9Sdownsj 	struct puc_attach_args *pa = aux;
945a7928a9Sdownsj 
955a7928a9Sdownsj 	if (pa->type == PUC_PORT_TYPE_COM)
965a7928a9Sdownsj 		return(1);
975a7928a9Sdownsj 
985a7928a9Sdownsj 	return(0);
995a7928a9Sdownsj }
1005a7928a9Sdownsj 
1015a7928a9Sdownsj void
1025a7928a9Sdownsj com_puc_attach(parent, self, aux)
1035a7928a9Sdownsj 	struct device *parent, *self;
1045a7928a9Sdownsj 	void *aux;
1055a7928a9Sdownsj {
1065a7928a9Sdownsj 	struct com_softc *sc = (void *)self;
1075a7928a9Sdownsj 	struct puc_attach_args *pa = aux;
1085a7928a9Sdownsj 	const char *intrstr;
1095a7928a9Sdownsj 
1105a7928a9Sdownsj 	/* Grab a PCI interrupt. */
1115a7928a9Sdownsj 	intrstr = pci_intr_string(pa->pc, pa->intrhandle);
1125a7928a9Sdownsj 	sc->sc_ih = pci_intr_establish(pa->pc, pa->intrhandle,
1135a7928a9Sdownsj 			IPL_HIGH, comintr, sc,
1145a7928a9Sdownsj 			sc->sc_dev.dv_xname);
1155a7928a9Sdownsj 	if (sc->sc_ih == NULL) {
1165a7928a9Sdownsj 		printf(": couldn't establish interrupt");
1175a7928a9Sdownsj 		if (intrstr != NULL)
1185a7928a9Sdownsj 			printf(" at %s", intrstr);
1195a7928a9Sdownsj 		printf("\n");
1205a7928a9Sdownsj 		return;
1215a7928a9Sdownsj 	}
1225a7928a9Sdownsj 	printf(" %s", intrstr);
1235a7928a9Sdownsj 
1245a7928a9Sdownsj 	sc->sc_iot = pa->t;
1255a7928a9Sdownsj 	sc->sc_ioh = pa->h;
1265a7928a9Sdownsj 	sc->sc_iobase = pa->a;
12798e273d3Sderaadt 	sc->sc_frequency = COM_FREQ;
128*78a91e64Smickey 
12998e273d3Sderaadt 	if (pa->flags)
13098e273d3Sderaadt 		sc->sc_frequency = pa->flags & PUC_COM_CLOCKMASK;
1315a7928a9Sdownsj 
1325a7928a9Sdownsj 	com_puc_attach2(sc);
1335a7928a9Sdownsj }
1345a7928a9Sdownsj 
1355a7928a9Sdownsj /*
1365a7928a9Sdownsj  * XXX This should be handled by a generic attach
1375a7928a9Sdownsj  */
1385a7928a9Sdownsj void
1395a7928a9Sdownsj com_puc_attach2(sc)
1405a7928a9Sdownsj 	struct com_softc *sc;
1415a7928a9Sdownsj {
1425a7928a9Sdownsj 	bus_space_tag_t iot = sc->sc_iot;
1435a7928a9Sdownsj 	bus_space_handle_t ioh = sc->sc_ioh;
1445a7928a9Sdownsj 	u_int8_t lcr;
1455a7928a9Sdownsj 
1465a7928a9Sdownsj 	sc->sc_hwflags = 0;
1475a7928a9Sdownsj 	sc->sc_swflags = 0;
1485a7928a9Sdownsj 
149*78a91e64Smickey 	timeout_set(&sc->sc_dtr_tmo, com_raisedtr, sc);
150*78a91e64Smickey 	timeout_set(&sc->sc_diag_tmo, comdiag, sc);
151*78a91e64Smickey 
1525a7928a9Sdownsj 	/*
1535a7928a9Sdownsj 	 * Probe for all known forms of UART.
1545a7928a9Sdownsj 	 */
1555a7928a9Sdownsj 	lcr = bus_space_read_1(iot, ioh, com_lcr);
1565a7928a9Sdownsj 
1575a7928a9Sdownsj 	bus_space_write_1(iot, ioh, com_lcr, LCR_EFR);
1585a7928a9Sdownsj 	bus_space_write_1(iot, ioh, com_efr, 0);
1595a7928a9Sdownsj 	bus_space_write_1(iot, ioh, com_lcr, 0);
1605a7928a9Sdownsj 
1615a7928a9Sdownsj 	bus_space_write_1(iot, ioh, com_fifo, FIFO_ENABLE);
1625a7928a9Sdownsj 	delay(100);
1635a7928a9Sdownsj 
1645a7928a9Sdownsj 	switch(bus_space_read_1(iot, ioh, com_iir) >> 6) {
1655a7928a9Sdownsj 	case 0:
1665a7928a9Sdownsj 		sc->sc_uarttype = COM_UART_16450;
1675a7928a9Sdownsj 		break;
1685a7928a9Sdownsj 	case 2:
1695a7928a9Sdownsj 		sc->sc_uarttype = COM_UART_16550;
1705a7928a9Sdownsj 		break;
1715a7928a9Sdownsj 	case 3:
1725a7928a9Sdownsj 		sc->sc_uarttype = COM_UART_16550A;
1735a7928a9Sdownsj 		break;
1745a7928a9Sdownsj 	default:
1755a7928a9Sdownsj 		sc->sc_uarttype = COM_UART_UNKNOWN;
1765a7928a9Sdownsj 		break;
1775a7928a9Sdownsj 	}
1785a7928a9Sdownsj 
1795a7928a9Sdownsj 	if (sc->sc_uarttype == COM_UART_16550A) { /* Probe for ST16650s */
1805a7928a9Sdownsj 		bus_space_write_1(iot, ioh, com_lcr, lcr | LCR_DLAB);
1815a7928a9Sdownsj 		if (bus_space_read_1(iot, ioh, com_efr) == 0) {
1825a7928a9Sdownsj 			sc->sc_uarttype = COM_UART_ST16650;
1835a7928a9Sdownsj 		} else {
1845a7928a9Sdownsj 			bus_space_write_1(iot, ioh, com_lcr, LCR_EFR);
1855a7928a9Sdownsj 			if (bus_space_read_1(iot, ioh, com_efr) == 0)
1865a7928a9Sdownsj 				sc->sc_uarttype = COM_UART_ST16650V2;
1875a7928a9Sdownsj 		}
1885a7928a9Sdownsj 	}
1895a7928a9Sdownsj 
1905a7928a9Sdownsj #if NPCCOM > 0
1915a7928a9Sdownsj #ifdef i386
1925a7928a9Sdownsj 	if (sc->sc_uarttype == COM_UART_ST16650V2) {	/* Probe for XR16850s */
1935a7928a9Sdownsj 		u_int8_t dlbl, dlbh;
1945a7928a9Sdownsj 
1955a7928a9Sdownsj 		/* Enable latch access and get the current values. */
1965a7928a9Sdownsj 		bus_space_write_1(iot, ioh, com_lcr, lcr | LCR_DLAB);
1975a7928a9Sdownsj 		dlbl = bus_space_read_1(iot, ioh, com_dlbl);
1985a7928a9Sdownsj 		dlbh = bus_space_read_1(iot, ioh, com_dlbh);
1995a7928a9Sdownsj 
2005a7928a9Sdownsj 		/* Zero out the latch divisors */
2015a7928a9Sdownsj 		bus_space_write_1(iot, ioh, com_dlbl, 0);
2025a7928a9Sdownsj 		bus_space_write_1(iot, ioh, com_dlbh, 0);
2035a7928a9Sdownsj 
2045a7928a9Sdownsj 		if (bus_space_read_1(iot, ioh, com_dlbh) == 0x10) {
2055a7928a9Sdownsj 			sc->sc_uarttype = COM_UART_XR16850;
2065a7928a9Sdownsj 			sc->sc_uartrev = bus_space_read_1(iot, ioh, com_dlbl);
2075a7928a9Sdownsj 		}
2085a7928a9Sdownsj 
2095a7928a9Sdownsj 		/* Reset to original. */
2105a7928a9Sdownsj 		bus_space_write_1(iot, ioh, com_dlbl, dlbl);
2115a7928a9Sdownsj 		bus_space_write_1(iot, ioh, com_dlbh, dlbh);
2125a7928a9Sdownsj 	}
2135a7928a9Sdownsj #endif
2145a7928a9Sdownsj #endif
2155a7928a9Sdownsj 
2165a7928a9Sdownsj 	/* Reset the LCR (latch access is probably enabled). */
2175a7928a9Sdownsj 	bus_space_write_1(iot, ioh, com_lcr, lcr);
2185a7928a9Sdownsj 	if (sc->sc_uarttype == COM_UART_16450) { /* Probe for 8250 */
2195a7928a9Sdownsj 		u_int8_t scr0, scr1, scr2;
2205a7928a9Sdownsj 
2215a7928a9Sdownsj 		scr0 = bus_space_read_1(iot, ioh, com_scratch);
2225a7928a9Sdownsj 		bus_space_write_1(iot, ioh, com_scratch, 0xa5);
2235a7928a9Sdownsj 		scr1 = bus_space_read_1(iot, ioh, com_scratch);
2245a7928a9Sdownsj 		bus_space_write_1(iot, ioh, com_scratch, 0x5a);
2255a7928a9Sdownsj 		scr2 = bus_space_read_1(iot, ioh, com_scratch);
2265a7928a9Sdownsj 		bus_space_write_1(iot, ioh, com_scratch, scr0);
2275a7928a9Sdownsj 
2285a7928a9Sdownsj 		if ((scr1 != 0xa5) || (scr2 != 0x5a))
2295a7928a9Sdownsj 			sc->sc_uarttype = COM_UART_8250;
2305a7928a9Sdownsj 	}
2315a7928a9Sdownsj 
2325a7928a9Sdownsj 	/*
2335a7928a9Sdownsj 	 * Print UART type and initialize ourself.
2345a7928a9Sdownsj 	 */
2355a7928a9Sdownsj 	sc->sc_fifolen = 1;	/* default */
2365a7928a9Sdownsj 	switch (sc->sc_uarttype) {
2375a7928a9Sdownsj 	case COM_UART_UNKNOWN:
2385a7928a9Sdownsj 		printf(": unknown uart\n");
2395a7928a9Sdownsj 		break;
2405a7928a9Sdownsj 	case COM_UART_8250:
2415a7928a9Sdownsj 		printf(": ns8250, no fifo\n");
2425a7928a9Sdownsj 		break;
2435a7928a9Sdownsj 	case COM_UART_16450:
2445a7928a9Sdownsj 		printf(": ns16450, no fifo\n");
2455a7928a9Sdownsj 		break;
2465a7928a9Sdownsj 	case COM_UART_16550:
2475a7928a9Sdownsj 		printf(": ns16550, no working fifo\n");
2485a7928a9Sdownsj 		break;
2495a7928a9Sdownsj 	case COM_UART_16550A:
2505a7928a9Sdownsj 		printf(": ns16550a, 16 byte fifo\n");
2515a7928a9Sdownsj 		SET(sc->sc_hwflags, COM_HW_FIFO);
2525a7928a9Sdownsj 		sc->sc_fifolen = 16;
2535a7928a9Sdownsj 		break;
2545a7928a9Sdownsj 	case COM_UART_ST16650:
2555a7928a9Sdownsj 		printf(": st16650, no working fifo\n");
2565a7928a9Sdownsj 		break;
2575a7928a9Sdownsj 	case COM_UART_ST16650V2:
2585a7928a9Sdownsj 		printf(": st16650, 32 byte fifo\n");
2595a7928a9Sdownsj 		SET(sc->sc_hwflags, COM_HW_FIFO);
2605a7928a9Sdownsj 		sc->sc_fifolen = 32;
2615a7928a9Sdownsj 		break;
2625a7928a9Sdownsj #if NPCCOM > 0
2635a7928a9Sdownsj #ifdef i386
2645a7928a9Sdownsj 	case COM_UART_XR16850:
2655a7928a9Sdownsj 		printf(": xr16850 (rev %d), 128 byte fifo\n", sc->sc_uartrev);
2665a7928a9Sdownsj 		SET(sc->sc_hwflags, COM_HW_FIFO);
2675a7928a9Sdownsj 		sc->sc_fifolen = 128;
2685a7928a9Sdownsj 		break;
2695a7928a9Sdownsj #endif
2705a7928a9Sdownsj #endif
2715a7928a9Sdownsj 	default:
2725a7928a9Sdownsj 		panic("comattach: bad fifo type");
2735a7928a9Sdownsj 	}
2745a7928a9Sdownsj 
2755a7928a9Sdownsj 	/* clear and disable fifo */
2765a7928a9Sdownsj 	bus_space_write_1(iot, ioh, com_fifo, FIFO_RCV_RST | FIFO_XMT_RST);
2775a7928a9Sdownsj 	(void)bus_space_read_1(iot, ioh, com_data);
2785a7928a9Sdownsj 	bus_space_write_1(iot, ioh, com_fifo, 0);
2795a7928a9Sdownsj }
280