128221Smsmith/*-
293021Snsouch * Copyright (c) 1997-2000 Nicolas Souchu
371622Snsouch * Copyright (c) 2001 Alcove - Nicolas Souchu
428221Smsmith * All rights reserved.
528221Smsmith *
628221Smsmith * Redistribution and use in source and binary forms, with or without
728221Smsmith * modification, are permitted provided that the following conditions
828221Smsmith * are met:
928221Smsmith * 1. Redistributions of source code must retain the above copyright
1028221Smsmith *    notice, this list of conditions and the following disclaimer.
1128221Smsmith * 2. Redistributions in binary form must reproduce the above copyright
1228221Smsmith *    notice, this list of conditions and the following disclaimer in the
1328221Smsmith *    documentation and/or other materials provided with the distribution.
1428221Smsmith *
1528221Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1628221Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1728221Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1828221Smsmith * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1928221Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2028221Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2128221Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2228221Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2328221Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2428221Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2528221Smsmith * SUCH DAMAGE.
2628221Smsmith */
2728221Smsmith
28116181Sobrien#include <sys/cdefs.h>
29116181Sobrien__FBSDID("$FreeBSD$");
30116181Sobrien
3155939Snsouch#include "opt_ppc.h"
3255939Snsouch
3328221Smsmith#include <sys/param.h>
3428221Smsmith#include <sys/systm.h>
35183053Sjhb#include <sys/bus.h>
3655939Snsouch#include <sys/kernel.h>
37187576Sjhb#include <sys/lock.h>
38183053Sjhb#include <sys/interrupt.h>
39129879Sphk#include <sys/module.h>
4028221Smsmith#include <sys/malloc.h>
41187576Sjhb#include <sys/mutex.h>
42183053Sjhb#include <sys/proc.h>
43185003Sjhb
4455939Snsouch#include <machine/bus.h>
4555939Snsouch#include <machine/resource.h>
4655939Snsouch#include <sys/rman.h>
4728221Smsmith
48158005Smarcel#ifdef __i386__
49158005Smarcel#include <vm/vm.h>
50158005Smarcel#include <vm/pmap.h>
51158005Smarcel#include <machine/vmparam.h>
52158005Smarcel#endif
5328221Smsmith
5428221Smsmith#include <dev/ppbus/ppbconf.h>
5538061Smsmith#include <dev/ppbus/ppb_msq.h>
5638061Smsmith
57118292Sambrisko#include <dev/ppc/ppcvar.h>
58118292Sambrisko#include <dev/ppc/ppcreg.h>
5928221Smsmith
6055939Snsouch#include "ppbus_if.h"
6142475Snsouch
62118292Sambriskostatic void ppcintr(void *arg);
63118292Sambrisko
64158005Smarcel#define	IO_LPTSIZE_EXTENDED	8	/* "Extended" LPT controllers */
65158005Smarcel#define	IO_LPTSIZE_NORMAL	4	/* "Normal" LPT controllers */
66158005Smarcel
6742475Snsouch#define LOG_PPC(function, ppc, string) \
6842475Snsouch		if (bootverbose) printf("%s: %s\n", function, string)
6942475Snsouch
70158005Smarcel#if defined(__i386__) && defined(PC98)
71158005Smarcel#define	PC98_IEEE_1284_DISABLE	0x100
72158005Smarcel#define	PC98_IEEE_1284_PORT	0x140
73158005Smarcel#endif
7428221Smsmith
7555939Snsouch#define DEVTOSOFTC(dev) ((struct ppc_data *)device_get_softc(dev))
76118292Sambrisko
77247066Simp/*
78247094Sglebius * We use critical enter/exit for the simple config locking needed to
79247066Simp * detect the devices. We just want to make sure that both of our writes
80247066Simp * happen without someone else also writing to those config registers. Since
81247066Simp * we just do this at startup, Giant keeps multiple threads from executing,
82247066Simp * and critical_enter() then is all that's needed to keep us from being preempted
83247066Simp * during the critical sequences with the hardware.
84247066Simp *
85247066Simp * Note: this doesn't prevent multiple threads from putting the chips into
86247066Simp * config mode, but since we only do that to detect the type at startup the
87247066Simp * extra overhead isn't needed since Giant protects us from multiple entry
88247066Simp * and no other code changes these registers.
89247066Simp */
90247066Simp#define PPC_CONFIG_LOCK(ppc)		critical_enter()
91247094Sglebius#define PPC_CONFIG_UNLOCK(ppc)		critical_exit()
92247066Simp
93118292Sambriskodevclass_t ppc_devclass;
94166933Sjhbconst char ppc_driver_name[] = "ppc";
9555939Snsouch
9655939Snsouchstatic char *ppc_models[] = {
9738061Smsmith	"SMC-like", "SMC FDC37C665GT", "SMC FDC37C666GT", "PC87332", "PC87306",
9863403Sdfr	"82091AA", "Generic", "W83877F", "W83877AF", "Winbond", "PC87334",
9963624Sdfr	"SMC FDC37C935", "PC87303", 0
10028221Smsmith};
10128221Smsmith
10238061Smsmith/* list of available modes */
10338061Smsmithstatic char *ppc_avms[] = {
10438061Smsmith	"COMPATIBLE", "NIBBLE-only", "PS2-only", "PS2/NIBBLE", "EPP-only",
10538061Smsmith	"EPP/NIBBLE", "EPP/PS2", "EPP/PS2/NIBBLE", "ECP-only",
10638061Smsmith	"ECP/NIBBLE", "ECP/PS2", "ECP/PS2/NIBBLE", "ECP/EPP",
10738061Smsmith	"ECP/EPP/NIBBLE", "ECP/EPP/PS2", "ECP/EPP/PS2/NIBBLE", 0
10838061Smsmith};
10938061Smsmith
11038061Smsmith/* list of current executing modes
11138061Smsmith * Note that few modes do not actually exist.
11238061Smsmith */
11328221Smsmithstatic char *ppc_modes[] = {
11438061Smsmith	"COMPATIBLE", "NIBBLE", "PS/2", "PS/2", "EPP",
11538061Smsmith	"EPP", "EPP", "EPP", "ECP",
11638061Smsmith	"ECP", "ECP+PS2", "ECP+PS2", "ECP+EPP",
11738061Smsmith	"ECP+EPP", "ECP+EPP", "ECP+EPP", 0
11828221Smsmith};
11928221Smsmith
12028221Smsmithstatic char *ppc_epp_protocol[] = { " (EPP 1.9)", " (EPP 1.7)", 0 };
12128221Smsmith
12256617Sdfr#ifdef __i386__
12328221Smsmith/*
12428221Smsmith * BIOS printer list - used by BIOS probe.
12528221Smsmith */
12628221Smsmith#define	BIOS_PPC_PORTS	0x408
12728221Smsmith#define	BIOS_PORTS	(short *)(KERNBASE+BIOS_PPC_PORTS)
12828221Smsmith#define	BIOS_MAX_PPC	4
12956617Sdfr#endif
13028221Smsmith
13128221Smsmith/*
13228221Smsmith * ppc_ecp_sync()		XXX
13328221Smsmith */
134188173Simpint
135185003Sjhbppc_ecp_sync(device_t dev)
136185003Sjhb{
13728221Smsmith	int i, r;
13855939Snsouch	struct ppc_data *ppc = DEVTOSOFTC(dev);
13928221Smsmith
140187576Sjhb	PPC_ASSERT_LOCKED(ppc);
14171622Snsouch	if (!(ppc->ppc_avm & PPB_ECP) && !(ppc->ppc_dtm & PPB_ECP))
142188173Simp		return 0;
14342475Snsouch
14428221Smsmith	r = r_ecr(ppc);
14542475Snsouch	if ((r & 0xe0) != PPC_ECR_EPP)
146188173Simp		return 0;
14728221Smsmith
14828221Smsmith	for (i = 0; i < 100; i++) {
14928221Smsmith		r = r_ecr(ppc);
15028221Smsmith		if (r & 0x1)
151188173Simp			return 0;
15228221Smsmith		DELAY(100);
15328221Smsmith	}
15428221Smsmith
155184130Sjhb	device_printf(dev, "ECP sync failed as data still present in FIFO.\n");
15628221Smsmith
157188173Simp	return 0;
15828221Smsmith}
15928221Smsmith
16042475Snsouch/*
16142475Snsouch * ppc_detect_fifo()
16242475Snsouch *
16342475Snsouch * Detect parallel port FIFO
16442475Snsouch */
16542475Snsouchstatic int
16642475Snsouchppc_detect_fifo(struct ppc_data *ppc)
16728221Smsmith{
16842475Snsouch	char ecr_sav;
16942475Snsouch	char ctr_sav, ctr, cc;
17042475Snsouch	short i;
171185003Sjhb
17242475Snsouch	/* save registers */
17342475Snsouch	ecr_sav = r_ecr(ppc);
17442475Snsouch	ctr_sav = r_ctr(ppc);
17528221Smsmith
17642475Snsouch	/* enter ECP configuration mode, no interrupt, no DMA */
17742475Snsouch	w_ecr(ppc, 0xf4);
17842475Snsouch
17942475Snsouch	/* read PWord size - transfers in FIFO mode must be PWord aligned */
18042475Snsouch	ppc->ppc_pword = (r_cnfgA(ppc) & PPC_PWORD_MASK);
18142475Snsouch
18242475Snsouch	/* XXX 16 and 32 bits implementations not supported */
18342475Snsouch	if (ppc->ppc_pword != PPC_PWORD_8) {
18487599Sobrien		LOG_PPC(__func__, ppc, "PWord not supported");
18542475Snsouch		goto error;
18642475Snsouch	}
18742475Snsouch
18842475Snsouch	w_ecr(ppc, 0x34);		/* byte mode, no interrupt, no DMA */
18942475Snsouch	ctr = r_ctr(ppc);
19042475Snsouch	w_ctr(ppc, ctr | PCD);		/* set direction to 1 */
19142475Snsouch
19242475Snsouch	/* enter ECP test mode, no interrupt, no DMA */
19342475Snsouch	w_ecr(ppc, 0xd4);
19442475Snsouch
19542475Snsouch	/* flush the FIFO */
19642475Snsouch	for (i=0; i<1024; i++) {
19742475Snsouch		if (r_ecr(ppc) & PPC_FIFO_EMPTY)
19842475Snsouch			break;
19942475Snsouch		cc = r_fifo(ppc);
20042475Snsouch	}
20142475Snsouch
20242475Snsouch	if (i >= 1024) {
20387599Sobrien		LOG_PPC(__func__, ppc, "can't flush FIFO");
20442475Snsouch		goto error;
20542475Snsouch	}
20642475Snsouch
20742475Snsouch	/* enable interrupts, no DMA */
20842475Snsouch	w_ecr(ppc, 0xd0);
20942475Snsouch
21042475Snsouch	/* determine readIntrThreshold
21142475Snsouch	 * fill the FIFO until serviceIntr is set
21242475Snsouch	 */
21342475Snsouch	for (i=0; i<1024; i++) {
21442475Snsouch		w_fifo(ppc, (char)i);
21542475Snsouch		if (!ppc->ppc_rthr && (r_ecr(ppc) & PPC_SERVICE_INTR)) {
21642475Snsouch			/* readThreshold reached */
21742475Snsouch			ppc->ppc_rthr = i+1;
21842475Snsouch		}
21942475Snsouch		if (r_ecr(ppc) & PPC_FIFO_FULL) {
22042475Snsouch			ppc->ppc_fifo = i+1;
22142475Snsouch			break;
22242475Snsouch		}
22342475Snsouch	}
22442475Snsouch
22542475Snsouch	if (i >= 1024) {
22687599Sobrien		LOG_PPC(__func__, ppc, "can't fill FIFO");
22742475Snsouch		goto error;
22842475Snsouch	}
22942475Snsouch
23042475Snsouch	w_ecr(ppc, 0xd4);		/* test mode, no interrupt, no DMA */
23142475Snsouch	w_ctr(ppc, ctr & ~PCD);		/* set direction to 0 */
23242475Snsouch	w_ecr(ppc, 0xd0);		/* enable interrupts */
23342475Snsouch
23442475Snsouch	/* determine writeIntrThreshold
23542475Snsouch	 * empty the FIFO until serviceIntr is set
23642475Snsouch	 */
23742475Snsouch	for (i=ppc->ppc_fifo; i>0; i--) {
23842475Snsouch		if (r_fifo(ppc) != (char)(ppc->ppc_fifo-i)) {
23987599Sobrien			LOG_PPC(__func__, ppc, "invalid data in FIFO");
24042475Snsouch			goto error;
24142475Snsouch		}
24242475Snsouch		if (r_ecr(ppc) & PPC_SERVICE_INTR) {
24342475Snsouch			/* writeIntrThreshold reached */
24442475Snsouch			ppc->ppc_wthr = ppc->ppc_fifo - i+1;
24542475Snsouch		}
24642475Snsouch		/* if FIFO empty before the last byte, error */
24742475Snsouch		if (i>1 && (r_ecr(ppc) & PPC_FIFO_EMPTY)) {
24887599Sobrien			LOG_PPC(__func__, ppc, "data lost in FIFO");
24942475Snsouch			goto error;
25042475Snsouch		}
25142475Snsouch	}
25242475Snsouch
25342475Snsouch	/* FIFO must be empty after the last byte */
25442475Snsouch	if (!(r_ecr(ppc) & PPC_FIFO_EMPTY)) {
25587599Sobrien		LOG_PPC(__func__, ppc, "can't empty the FIFO");
25642475Snsouch		goto error;
25742475Snsouch	}
258185003Sjhb
25942475Snsouch	w_ctr(ppc, ctr_sav);
26042475Snsouch	w_ecr(ppc, ecr_sav);
26142475Snsouch
26242475Snsouch	return (0);
26342475Snsouch
26442475Snsoucherror:
26542475Snsouch	w_ctr(ppc, ctr_sav);
26642475Snsouch	w_ecr(ppc, ecr_sav);
26742475Snsouch
26842475Snsouch	return (EINVAL);
26928221Smsmith}
27028221Smsmith
27138061Smsmithstatic int
27238061Smsmithppc_detect_port(struct ppc_data *ppc)
27338061Smsmith{
27438061Smsmith
27538061Smsmith	w_ctr(ppc, 0x0c);	/* To avoid missing PS2 ports */
27638061Smsmith	w_dtr(ppc, 0xaa);
27742482Snsouch	if (r_dtr(ppc) != 0xaa)
27838061Smsmith		return (0);
27938061Smsmith
28038061Smsmith	return (1);
28138061Smsmith}
28238061Smsmith
28328221Smsmith/*
28455939Snsouch * EPP timeout, according to the PC87332 manual
28555939Snsouch * Semantics of clearing EPP timeout bit.
28655939Snsouch * PC87332	- reading SPP_STR does it...
28755939Snsouch * SMC		- write 1 to EPP timeout bit			XXX
28855939Snsouch * Others	- (?) write 0 to EPP timeout bit
28955939Snsouch */
29055939Snsouchstatic void
29155939Snsouchppc_reset_epp_timeout(struct ppc_data *ppc)
29255939Snsouch{
29355939Snsouch	register char r;
29455939Snsouch
29555939Snsouch	r = r_str(ppc);
29655939Snsouch	w_str(ppc, r | 0x1);
29755939Snsouch	w_str(ppc, r & 0xfe);
29855939Snsouch
29955939Snsouch	return;
30055939Snsouch}
30155939Snsouch
30255939Snsouchstatic int
30355939Snsouchppc_check_epp_timeout(struct ppc_data *ppc)
30455939Snsouch{
30555939Snsouch	ppc_reset_epp_timeout(ppc);
30655939Snsouch
30755939Snsouch	return (!(r_str(ppc) & TIMEOUT));
30855939Snsouch}
30955939Snsouch
31055939Snsouch/*
31155939Snsouch * Configure current operating mode
31255939Snsouch */
31355939Snsouchstatic int
31455939Snsouchppc_generic_setmode(struct ppc_data *ppc, int mode)
31555939Snsouch{
31655939Snsouch	u_char ecr = 0;
31755939Snsouch
31855939Snsouch	/* check if mode is available */
31955939Snsouch	if (mode && !(ppc->ppc_avm & mode))
32055939Snsouch		return (EINVAL);
32155939Snsouch
32255939Snsouch	/* if ECP mode, configure ecr register */
32371622Snsouch	if ((ppc->ppc_avm & PPB_ECP) || (ppc->ppc_dtm & PPB_ECP)) {
32455939Snsouch		/* return to byte mode (keeping direction bit),
32555939Snsouch		 * no interrupt, no DMA to be able to change to
32655939Snsouch		 * ECP
32755939Snsouch		 */
32855939Snsouch		w_ecr(ppc, PPC_ECR_RESET);
32955939Snsouch		ecr = PPC_DISABLE_INTR;
33055939Snsouch
33155939Snsouch		if (mode & PPB_EPP)
33255939Snsouch			return (EINVAL);
33355939Snsouch		else if (mode & PPB_ECP)
33455939Snsouch			/* select ECP mode */
33555939Snsouch			ecr |= PPC_ECR_ECP;
33655939Snsouch		else if (mode & PPB_PS2)
33755939Snsouch			/* select PS2 mode with ECP */
33855939Snsouch			ecr |= PPC_ECR_PS2;
33955939Snsouch		else
34055939Snsouch			/* select COMPATIBLE/NIBBLE mode */
34155939Snsouch			ecr |= PPC_ECR_STD;
34255939Snsouch
34355939Snsouch		w_ecr(ppc, ecr);
34455939Snsouch	}
34555939Snsouch
34655939Snsouch	ppc->ppc_mode = mode;
34755939Snsouch
34855939Snsouch	return (0);
34955939Snsouch}
35055939Snsouch
35155939Snsouch/*
35255939Snsouch * The ppc driver is free to choose options like FIFO or DMA
35355939Snsouch * if ECP mode is available.
35455939Snsouch *
35555939Snsouch * The 'RAW' option allows the upper drivers to force the ppc mode
35655939Snsouch * even with FIFO, DMA available.
35755939Snsouch */
35855939Snsouchstatic int
35955939Snsouchppc_smclike_setmode(struct ppc_data *ppc, int mode)
36055939Snsouch{
36155939Snsouch	u_char ecr = 0;
36255939Snsouch
36355939Snsouch	/* check if mode is available */
36455939Snsouch	if (mode && !(ppc->ppc_avm & mode))
36555939Snsouch		return (EINVAL);
36655939Snsouch
36755939Snsouch	/* if ECP mode, configure ecr register */
36871622Snsouch	if ((ppc->ppc_avm & PPB_ECP) || (ppc->ppc_dtm & PPB_ECP)) {
36955939Snsouch		/* return to byte mode (keeping direction bit),
37055939Snsouch		 * no interrupt, no DMA to be able to change to
37155939Snsouch		 * ECP or EPP mode
37255939Snsouch		 */
37355939Snsouch		w_ecr(ppc, PPC_ECR_RESET);
37455939Snsouch		ecr = PPC_DISABLE_INTR;
37555939Snsouch
37655939Snsouch		if (mode & PPB_EPP)
37755939Snsouch			/* select EPP mode */
37855939Snsouch			ecr |= PPC_ECR_EPP;
37955939Snsouch		else if (mode & PPB_ECP)
38055939Snsouch			/* select ECP mode */
38155939Snsouch			ecr |= PPC_ECR_ECP;
38255939Snsouch		else if (mode & PPB_PS2)
38355939Snsouch			/* select PS2 mode with ECP */
38455939Snsouch			ecr |= PPC_ECR_PS2;
38555939Snsouch		else
38655939Snsouch			/* select COMPATIBLE/NIBBLE mode */
38755939Snsouch			ecr |= PPC_ECR_STD;
38855939Snsouch
38955939Snsouch		w_ecr(ppc, ecr);
39055939Snsouch	}
39155939Snsouch
39255939Snsouch	ppc->ppc_mode = mode;
39355939Snsouch
39455939Snsouch	return (0);
39555939Snsouch}
39655939Snsouch
39755939Snsouch#ifdef PPC_PROBE_CHIPSET
39855939Snsouch/*
39928221Smsmith * ppc_pc873xx_detect
40028221Smsmith *
40128221Smsmith * Probe for a Natsemi PC873xx-family part.
40228221Smsmith *
40328221Smsmith * References in this function are to the National Semiconductor
40428221Smsmith * PC87332 datasheet TL/C/11930, May 1995 revision.
40528221Smsmith */
40628221Smsmithstatic int pc873xx_basetab[] = {0x0398, 0x026e, 0x015c, 0x002e, 0};
40728221Smsmithstatic int pc873xx_porttab[] = {0x0378, 0x03bc, 0x0278, 0};
40840784Snsouchstatic int pc873xx_irqtab[] = {5, 7, 5, 0};
40928221Smsmith
41040784Snsouchstatic int pc873xx_regstab[] = {
41140784Snsouch	PC873_FER, PC873_FAR, PC873_PTR,
41240784Snsouch	PC873_FCR, PC873_PCR, PC873_PMC,
41340784Snsouch	PC873_TUP, PC873_SID, PC873_PNP0,
41440784Snsouch	PC873_PNP1, PC873_LPTBA, -1
41540784Snsouch};
41640784Snsouch
41740784Snsouchstatic char *pc873xx_rnametab[] = {
41840784Snsouch	"FER", "FAR", "PTR", "FCR", "PCR",
41940784Snsouch	"PMC", "TUP", "SID", "PNP0", "PNP1",
42040784Snsouch	"LPTBA", NULL
42140784Snsouch};
42240784Snsouch
42328221Smsmithstatic int
42438061Smsmithppc_pc873xx_detect(struct ppc_data *ppc, int chipset_mode)	/* XXX mode never forced */
42528221Smsmith{
42628221Smsmith    static int	index = 0;
42741591Sarchie    int		idport, irq;
42840784Snsouch    int		ptr, pcr, val, i;
429185003Sjhb
43028221Smsmith    while ((idport = pc873xx_basetab[index++])) {
431185003Sjhb
43228221Smsmith	/* XXX should check first to see if this location is already claimed */
43328221Smsmith
43428221Smsmith	/*
43540784Snsouch	 * Pull the 873xx through the power-on ID cycle (2.2,1.).
43640784Snsouch	 * We can't use this to locate the chip as it may already have
43740784Snsouch	 * been used by the BIOS.
43828221Smsmith	 */
43940784Snsouch	(void)inb(idport); (void)inb(idport);
44040784Snsouch	(void)inb(idport); (void)inb(idport);
44128221Smsmith
44228221Smsmith	/*
44328221Smsmith	 * Read the SID byte.  Possible values are :
44428221Smsmith	 *
44540784Snsouch	 * 01010xxx	PC87334
44628221Smsmith	 * 0001xxxx	PC87332
44728221Smsmith	 * 01110xxx	PC87306
44863624Sdfr	 * 00110xxx	PC87303
44928221Smsmith	 */
45028221Smsmith	outb(idport, PC873_SID);
45128221Smsmith	val = inb(idport + 1);
45228221Smsmith	if ((val & 0xf0) == 0x10) {
45355939Snsouch	    ppc->ppc_model = NS_PC87332;
45428221Smsmith	} else if ((val & 0xf8) == 0x70) {
45555939Snsouch	    ppc->ppc_model = NS_PC87306;
45640784Snsouch	} else if ((val & 0xf8) == 0x50) {
45755939Snsouch	    ppc->ppc_model = NS_PC87334;
45863624Sdfr	} else if ((val & 0xf8) == 0x40) { /* Should be 0x30 by the
45963624Sdfr					      documentation, but probing
46063624Sdfr					      yielded 0x40... */
46163624Sdfr	    ppc->ppc_model = NS_PC87303;
46228221Smsmith	} else {
46328221Smsmith	    if (bootverbose && (val != 0xff))
46428221Smsmith		printf("PC873xx probe at 0x%x got unknown ID 0x%x\n", idport, val);
46528221Smsmith	    continue ;		/* not recognised */
46628221Smsmith	}
46740784Snsouch
46840784Snsouch	/* print registers */
46940784Snsouch	if (bootverbose) {
47040784Snsouch		printf("PC873xx");
47140784Snsouch		for (i=0; pc873xx_regstab[i] != -1; i++) {
47240784Snsouch			outb(idport, pc873xx_regstab[i]);
47340784Snsouch			printf(" %s=0x%x", pc873xx_rnametab[i],
47440784Snsouch						inb(idport + 1) & 0xff);
47540784Snsouch		}
47640784Snsouch		printf("\n");
47740784Snsouch	}
478185003Sjhb
47928221Smsmith	/*
48040784Snsouch	 * We think we have one.  Is it enabled and where we want it to be?
48128221Smsmith	 */
48228221Smsmith	outb(idport, PC873_FER);
48328221Smsmith	val = inb(idport + 1);
48428221Smsmith	if (!(val & PC873_PPENABLE)) {
48528221Smsmith	    if (bootverbose)
48628221Smsmith		printf("PC873xx parallel port disabled\n");
48728221Smsmith	    continue;
48828221Smsmith	}
48928221Smsmith	outb(idport, PC873_FAR);
49063624Sdfr	val = inb(idport + 1);
49128221Smsmith	/* XXX we should create a driver instance for every port found */
49263624Sdfr	if (pc873xx_porttab[val & 0x3] != ppc->ppc_base) {
49363624Sdfr
49463624Sdfr	    /* First try to change the port address to that requested... */
49563624Sdfr
496187576Sjhb	    switch (ppc->ppc_base) {
49763624Sdfr		case 0x378:
49863624Sdfr		val &= 0xfc;
49963624Sdfr		break;
50063624Sdfr
50163624Sdfr		case 0x3bc:
50263624Sdfr		val &= 0xfd;
50363624Sdfr		break;
50463624Sdfr
50563624Sdfr		case 0x278:
50663624Sdfr		val &= 0xfe;
50763624Sdfr		break;
50863624Sdfr
50963624Sdfr		default:
51063624Sdfr		val &= 0xfd;
51163624Sdfr		break;
51263624Sdfr	    }
51363624Sdfr
51463624Sdfr	    outb(idport, PC873_FAR);
51563624Sdfr	    outb(idport + 1, val);
51663624Sdfr	    outb(idport + 1, val);
51763624Sdfr
51863624Sdfr	    /* Check for success by reading back the value we supposedly
51963624Sdfr	       wrote and comparing...*/
52063624Sdfr
52163624Sdfr	    outb(idport, PC873_FAR);
52263624Sdfr	    val = inb(idport + 1) & 0x3;
52363624Sdfr
52463624Sdfr	    /* If we fail, report the failure... */
52563624Sdfr
52663624Sdfr	    if (pc873xx_porttab[val] != ppc->ppc_base) {
52763624Sdfr 		if (bootverbose)
52863624Sdfr	  	    printf("PC873xx at 0x%x not for driver at port 0x%x\n",
52963624Sdfr			   pc873xx_porttab[val], ppc->ppc_base);
53063624Sdfr	    }
53128221Smsmith	    continue;
53228221Smsmith	}
53340784Snsouch
53440784Snsouch	outb(idport, PC873_PTR);
535185003Sjhb	ptr = inb(idport + 1);
53640784Snsouch
53740784Snsouch	/* get irq settings */
53840784Snsouch	if (ppc->ppc_base == 0x378)
53940784Snsouch		irq = (ptr & PC873_LPTBIRQ7) ? 7 : 5;
54040784Snsouch	else
54140784Snsouch		irq = pc873xx_irqtab[val];
54240784Snsouch
54340784Snsouch	if (bootverbose)
54440784Snsouch		printf("PC873xx irq %d at 0x%x\n", irq, ppc->ppc_base);
545185003Sjhb
54640784Snsouch	/*
54740784Snsouch	 * Check if irq settings are correct
54828221Smsmith	 */
54940784Snsouch	if (irq != ppc->ppc_irq) {
55040784Snsouch		/*
55140784Snsouch		 * If the chipset is not locked and base address is 0x378,
55240784Snsouch		 * we have another chance
55340784Snsouch		 */
55440784Snsouch		if (ppc->ppc_base == 0x378 && !(ptr & PC873_CFGLOCK)) {
55540784Snsouch			if (ppc->ppc_irq == 7) {
55640784Snsouch				outb(idport + 1, (ptr | PC873_LPTBIRQ7));
55740784Snsouch				outb(idport + 1, (ptr | PC873_LPTBIRQ7));
55840784Snsouch			} else {
55940784Snsouch				outb(idport + 1, (ptr & ~PC873_LPTBIRQ7));
56040784Snsouch				outb(idport + 1, (ptr & ~PC873_LPTBIRQ7));
56140784Snsouch			}
56240784Snsouch			if (bootverbose)
56340784Snsouch			   printf("PC873xx irq set to %d\n", ppc->ppc_irq);
56440784Snsouch		} else {
56540784Snsouch			if (bootverbose)
56640784Snsouch			   printf("PC873xx sorry, can't change irq setting\n");
56728221Smsmith		}
56828221Smsmith	} else {
56940784Snsouch		if (bootverbose)
57040784Snsouch			printf("PC873xx irq settings are correct\n");
57140784Snsouch	}
57240784Snsouch
57340784Snsouch	outb(idport, PC873_PCR);
57440784Snsouch	pcr = inb(idport + 1);
575185003Sjhb
57640784Snsouch	if ((ptr & PC873_CFGLOCK) || !chipset_mode) {
57728221Smsmith	    if (bootverbose)
57840784Snsouch		printf("PC873xx %s", (ptr & PC873_CFGLOCK)?"locked":"unlocked");
57928221Smsmith
58040784Snsouch	    ppc->ppc_avm |= PPB_NIBBLE;
58140784Snsouch	    if (bootverbose)
58240784Snsouch		printf(", NIBBLE");
58340784Snsouch
58440784Snsouch	    if (pcr & PC873_EPPEN) {
585185003Sjhb		ppc->ppc_avm |= PPB_EPP;
58640784Snsouch
58728221Smsmith		if (bootverbose)
58840784Snsouch			printf(", EPP");
58928221Smsmith
59040784Snsouch		if (pcr & PC873_EPP19)
59140784Snsouch			ppc->ppc_epp = EPP_1_9;
59240784Snsouch		else
59340784Snsouch			ppc->ppc_epp = EPP_1_7;
59440784Snsouch
59555939Snsouch		if ((ppc->ppc_model == NS_PC87332) && bootverbose) {
59640784Snsouch			outb(idport, PC873_PTR);
59740784Snsouch			ptr = inb(idport + 1);
59840784Snsouch			if (ptr & PC873_EPPRDIR)
59940784Snsouch				printf(", Regular mode");
60040784Snsouch			else
60140784Snsouch				printf(", Automatic mode");
60240784Snsouch		}
60340784Snsouch	    } else if (pcr & PC873_ECPEN) {
60440784Snsouch		ppc->ppc_avm |= PPB_ECP;
60528221Smsmith		if (bootverbose)
60640784Snsouch			printf(", ECP");
60728221Smsmith
60840784Snsouch		if (pcr & PC873_ECPCLK)	{		/* XXX */
60940784Snsouch			ppc->ppc_avm |= PPB_PS2;
61040784Snsouch			if (bootverbose)
61140784Snsouch				printf(", PS/2");
61240784Snsouch		}
61340784Snsouch	    } else {
61428221Smsmith		outb(idport, PC873_PTR);
61540784Snsouch		ptr = inb(idport + 1);
61640784Snsouch		if (ptr & PC873_EXTENDED) {
61740784Snsouch			ppc->ppc_avm |= PPB_SPP;
618185003Sjhb			if (bootverbose)
619185003Sjhb				printf(", SPP");
62040784Snsouch		}
62140784Snsouch	    }
62240784Snsouch	} else {
62328221Smsmith		if (bootverbose)
62440784Snsouch			printf("PC873xx unlocked");
62528221Smsmith
62640784Snsouch		if (chipset_mode & PPB_ECP) {
62740784Snsouch			if ((chipset_mode & PPB_EPP) && bootverbose)
62840784Snsouch				printf(", ECP+EPP not supported");
62940784Snsouch
63040784Snsouch			pcr &= ~PC873_EPPEN;
63140784Snsouch			pcr |= (PC873_ECPEN | PC873_ECPCLK);	/* XXX */
63240784Snsouch			outb(idport + 1, pcr);
63340784Snsouch			outb(idport + 1, pcr);
63440784Snsouch
63540784Snsouch			if (bootverbose)
63640784Snsouch				printf(", ECP");
63740784Snsouch
63840784Snsouch		} else if (chipset_mode & PPB_EPP) {
63940784Snsouch			pcr &= ~(PC873_ECPEN | PC873_ECPCLK);
64040784Snsouch			pcr |= (PC873_EPPEN | PC873_EPP19);
64140784Snsouch			outb(idport + 1, pcr);
64240784Snsouch			outb(idport + 1, pcr);
64340784Snsouch
64440784Snsouch			ppc->ppc_epp = EPP_1_9;			/* XXX */
64540784Snsouch
64640784Snsouch			if (bootverbose)
64740784Snsouch				printf(", EPP1.9");
64840784Snsouch
64940784Snsouch			/* enable automatic direction turnover */
65055939Snsouch			if (ppc->ppc_model == NS_PC87332) {
65140784Snsouch				outb(idport, PC873_PTR);
65240784Snsouch				ptr = inb(idport + 1);
65340784Snsouch				ptr &= ~PC873_EPPRDIR;
65440784Snsouch				outb(idport + 1, ptr);
65540784Snsouch				outb(idport + 1, ptr);
65640784Snsouch
65740784Snsouch				if (bootverbose)
65840784Snsouch					printf(", Automatic mode");
65940784Snsouch			}
66040784Snsouch		} else {
66140784Snsouch			pcr &= ~(PC873_ECPEN | PC873_ECPCLK | PC873_EPPEN);
66240784Snsouch			outb(idport + 1, pcr);
66340784Snsouch			outb(idport + 1, pcr);
66440784Snsouch
66540784Snsouch			/* configure extended bit in PTR */
66640784Snsouch			outb(idport, PC873_PTR);
66740784Snsouch			ptr = inb(idport + 1);
66840784Snsouch
66940784Snsouch			if (chipset_mode & PPB_PS2) {
67040784Snsouch				ptr |= PC873_EXTENDED;
67140784Snsouch
67240784Snsouch				if (bootverbose)
67340784Snsouch					printf(", PS/2");
674185003Sjhb
67540784Snsouch			} else {
67640784Snsouch				/* default to NIBBLE mode */
67740784Snsouch				ptr &= ~PC873_EXTENDED;
67840784Snsouch
67940784Snsouch				if (bootverbose)
68040784Snsouch					printf(", NIBBLE");
68140784Snsouch			}
68240784Snsouch			outb(idport + 1, ptr);
68340784Snsouch			outb(idport + 1, ptr);
68440784Snsouch		}
68540784Snsouch
68640784Snsouch		ppc->ppc_avm = chipset_mode;
68728221Smsmith	}
68838061Smsmith
68940784Snsouch	if (bootverbose)
69040784Snsouch		printf("\n");
69140784Snsouch
69255939Snsouch	ppc->ppc_type = PPC_TYPE_GENERIC;
69355939Snsouch	ppc_generic_setmode(ppc, chipset_mode);
69440784Snsouch
69538061Smsmith	return(chipset_mode);
69628221Smsmith    }
69738061Smsmith    return(-1);
69828221Smsmith}
69928221Smsmith
70028221Smsmith/*
70128221Smsmith * ppc_smc37c66xgt_detect
70228221Smsmith *
70328221Smsmith * SMC FDC37C66xGT configuration.
70428221Smsmith */
70528221Smsmithstatic int
70638061Smsmithppc_smc37c66xgt_detect(struct ppc_data *ppc, int chipset_mode)
70728221Smsmith{
708247066Simp	int i;
70943460Snsouch	u_char r;
71028221Smsmith	int type = -1;
71128221Smsmith	int csr = SMC66x_CSR;	/* initial value is 0x3F0 */
71228221Smsmith
71328221Smsmith	int port_address[] = { -1 /* disabled */ , 0x3bc, 0x378, 0x278 };
71428221Smsmith
71528221Smsmith
71628221Smsmith#define cio csr+1	/* config IO port is either 0x3F1 or 0x371 */
71728221Smsmith
71828221Smsmith	/*
71928221Smsmith	 * Detection: enter configuration mode and read CRD register.
72028221Smsmith	 */
721247066Simp	PPC_CONFIG_LOCK(ppc);
72228221Smsmith	outb(csr, SMC665_iCODE);
72328221Smsmith	outb(csr, SMC665_iCODE);
724247066Simp	PPC_CONFIG_UNLOCK(ppc);
72528221Smsmith
72628221Smsmith	outb(csr, 0xd);
72728221Smsmith	if (inb(cio) == 0x65) {
72828221Smsmith		type = SMC_37C665GT;
72928221Smsmith		goto config;
73028221Smsmith	}
73128221Smsmith
73228221Smsmith	for (i = 0; i < 2; i++) {
733247066Simp		PPC_CONFIG_LOCK(ppc);
73428221Smsmith		outb(csr, SMC666_iCODE);
73528221Smsmith		outb(csr, SMC666_iCODE);
736247066Simp		PPC_CONFIG_UNLOCK(ppc);
73728221Smsmith
73828221Smsmith		outb(csr, 0xd);
73928221Smsmith		if (inb(cio) == 0x66) {
74028221Smsmith			type = SMC_37C666GT;
74128221Smsmith			break;
74228221Smsmith		}
74328221Smsmith
74428221Smsmith		/* Another chance, CSR may be hard-configured to be at 0x370 */
74528221Smsmith		csr = SMC666_CSR;
74628221Smsmith	}
74728221Smsmith
74828221Smsmithconfig:
74928221Smsmith	/*
75028221Smsmith	 * If chipset not found, do not continue.
75128221Smsmith	 */
752247066Simp	if (type == -1) {
753247066Simp		outb(csr, 0xaa);	/* end config mode */
75438061Smsmith		return (-1);
755247066Simp	}
75628221Smsmith
75728221Smsmith	/* select CR1 */
75828221Smsmith	outb(csr, 0x1);
75928221Smsmith
76028221Smsmith	/* read the port's address: bits 0 and 1 of CR1 */
76128221Smsmith	r = inb(cio) & SMC_CR1_ADDR;
762247066Simp	if (port_address[(int)r] != ppc->ppc_base) {
763247066Simp		outb(csr, 0xaa);	/* end config mode */
76438061Smsmith		return (-1);
765247066Simp	}
76628221Smsmith
76755939Snsouch	ppc->ppc_model = type;
76828221Smsmith
76928221Smsmith	/*
77028221Smsmith	 * CR1 and CR4 registers bits 3 and 0/1 for mode configuration
77138061Smsmith	 * If SPP mode is detected, try to set ECP+EPP mode
77228221Smsmith	 */
77328221Smsmith
77438061Smsmith	if (bootverbose) {
77538061Smsmith		outb(csr, 0x1);
776184176Sjhb		device_printf(ppc->ppc_dev, "SMC registers CR1=0x%x",
777184176Sjhb		    inb(cio) & 0xff);
77838061Smsmith
77938061Smsmith		outb(csr, 0x4);
78038061Smsmith		printf(" CR4=0x%x", inb(cio) & 0xff);
78138061Smsmith	}
78238061Smsmith
78338061Smsmith	/* select CR1 */
78428221Smsmith	outb(csr, 0x1);
78528221Smsmith
78638061Smsmith	if (!chipset_mode) {
78728221Smsmith		/* autodetect mode */
78828221Smsmith
78938061Smsmith		/* 666GT is ~certainly~ hardwired to an extended ECP+EPP mode */
79038061Smsmith		if (type == SMC_37C666GT) {
79138061Smsmith			ppc->ppc_avm |= PPB_ECP | PPB_EPP | PPB_SPP;
79238761Snsouch			if (bootverbose)
79338761Snsouch				printf(" configuration hardwired, supposing " \
79438761Snsouch					"ECP+EPP SPP");
79528221Smsmith
79638061Smsmith		} else
79738061Smsmith		   if ((inb(cio) & SMC_CR1_MODE) == 0) {
79828221Smsmith			/* already in extended parallel port mode, read CR4 */
79928221Smsmith			outb(csr, 0x4);
80028221Smsmith			r = (inb(cio) & SMC_CR4_EMODE);
80128221Smsmith
80228221Smsmith			switch (r) {
80328221Smsmith			case SMC_SPP:
80438061Smsmith				ppc->ppc_avm |= PPB_SPP;
80538761Snsouch				if (bootverbose)
80638761Snsouch					printf(" SPP");
80728221Smsmith				break;
80828221Smsmith
80928221Smsmith			case SMC_EPPSPP:
81038061Smsmith				ppc->ppc_avm |= PPB_EPP | PPB_SPP;
81138761Snsouch				if (bootverbose)
81238761Snsouch					printf(" EPP SPP");
81328221Smsmith				break;
81428221Smsmith
81528221Smsmith			case SMC_ECP:
81638061Smsmith				ppc->ppc_avm |= PPB_ECP | PPB_SPP;
81738761Snsouch				if (bootverbose)
81838761Snsouch					printf(" ECP SPP");
81928221Smsmith				break;
82028221Smsmith
82128221Smsmith			case SMC_ECPEPP:
82238061Smsmith				ppc->ppc_avm |= PPB_ECP | PPB_EPP | PPB_SPP;
82338761Snsouch				if (bootverbose)
82438761Snsouch					printf(" ECP+EPP SPP");
82528221Smsmith				break;
82628221Smsmith			}
82738061Smsmith		   } else {
82838061Smsmith			/* not an extended port mode */
82938061Smsmith			ppc->ppc_avm |= PPB_SPP;
83038761Snsouch			if (bootverbose)
83138761Snsouch				printf(" SPP");
83238061Smsmith		   }
83338061Smsmith
83428221Smsmith	} else {
83528221Smsmith		/* mode forced */
83639135Snsouch		ppc->ppc_avm = chipset_mode;
83728221Smsmith
83838061Smsmith		/* 666GT is ~certainly~ hardwired to an extended ECP+EPP mode */
83928221Smsmith		if (type == SMC_37C666GT)
84028221Smsmith			goto end_detect;
84128221Smsmith
84228221Smsmith		r = inb(cio);
84338061Smsmith		if ((chipset_mode & (PPB_ECP | PPB_EPP)) == 0) {
84438061Smsmith			/* do not use ECP when the mode is not forced to */
84528221Smsmith			outb(cio, r | SMC_CR1_MODE);
84638761Snsouch			if (bootverbose)
84738761Snsouch				printf(" SPP");
84828221Smsmith		} else {
84928221Smsmith			/* an extended mode is selected */
85028221Smsmith			outb(cio, r & ~SMC_CR1_MODE);
85128221Smsmith
85228221Smsmith			/* read CR4 register and reset mode field */
85328221Smsmith			outb(csr, 0x4);
85428221Smsmith			r = inb(cio) & ~SMC_CR4_EMODE;
85528221Smsmith
85638061Smsmith			if (chipset_mode & PPB_ECP) {
85738061Smsmith				if (chipset_mode & PPB_EPP) {
85838061Smsmith					outb(cio, r | SMC_ECPEPP);
85938761Snsouch					if (bootverbose)
86038761Snsouch						printf(" ECP+EPP");
86138061Smsmith				} else {
86238061Smsmith					outb(cio, r | SMC_ECP);
86338761Snsouch					if (bootverbose)
86438761Snsouch						printf(" ECP");
86538061Smsmith				}
86638061Smsmith			} else {
86738061Smsmith				/* PPB_EPP is set */
86828221Smsmith				outb(cio, r | SMC_EPPSPP);
86938761Snsouch				if (bootverbose)
87038761Snsouch					printf(" EPP SPP");
87128221Smsmith			}
87228221Smsmith		}
87338061Smsmith		ppc->ppc_avm = chipset_mode;
87428221Smsmith	}
87528221Smsmith
87642475Snsouch	/* set FIFO threshold to 16 */
87742475Snsouch	if (ppc->ppc_avm & PPB_ECP) {
87842475Snsouch		/* select CRA */
87942475Snsouch		outb(csr, 0xa);
88042475Snsouch		outb(cio, 16);
88142475Snsouch	}
88242475Snsouch
88328221Smsmithend_detect:
88438061Smsmith
88538061Smsmith	if (bootverbose)
88638061Smsmith		printf ("\n");
88738061Smsmith
88839135Snsouch	if (ppc->ppc_avm & PPB_EPP) {
88928221Smsmith		/* select CR4 */
89028221Smsmith		outb(csr, 0x4);
89128221Smsmith		r = inb(cio);
89228221Smsmith
89328221Smsmith		/*
89428221Smsmith		 * Set the EPP protocol...
89528221Smsmith		 * Low=EPP 1.9 (1284 standard) and High=EPP 1.7
89628221Smsmith		 */
89728221Smsmith		if (ppc->ppc_epp == EPP_1_9)
89828221Smsmith			outb(cio, (r & ~SMC_CR4_EPPTYPE));
89928221Smsmith		else
90028221Smsmith			outb(cio, (r | SMC_CR4_EPPTYPE));
90128221Smsmith	}
90228221Smsmith
903247066Simp	outb(csr, 0xaa);	/* end config mode */
90428221Smsmith
90555939Snsouch	ppc->ppc_type = PPC_TYPE_SMCLIKE;
90655939Snsouch	ppc_smclike_setmode(ppc, chipset_mode);
90728221Smsmith
90838061Smsmith	return (chipset_mode);
90938061Smsmith}
91028221Smsmith
91138061Smsmith/*
91263403Sdfr * SMC FDC37C935 configuration
91363403Sdfr * Found on many Alpha machines
91463403Sdfr */
91563403Sdfrstatic int
91663403Sdfrppc_smc37c935_detect(struct ppc_data *ppc, int chipset_mode)
91763403Sdfr{
91863403Sdfr	int type = -1;
91963403Sdfr
920247066Simp	PPC_CONFIG_LOCK(ppc);
92163403Sdfr	outb(SMC935_CFG, 0x55); /* enter config mode */
92263403Sdfr	outb(SMC935_CFG, 0x55);
923247066Simp	PPC_CONFIG_UNLOCK(ppc);
92463403Sdfr
92563403Sdfr	outb(SMC935_IND, SMC935_ID); /* check device id */
92663403Sdfr	if (inb(SMC935_DAT) == 0x2)
92763403Sdfr		type = SMC_37C935;
92863403Sdfr
92963403Sdfr	if (type == -1) {
93063403Sdfr		outb(SMC935_CFG, 0xaa); /* exit config mode */
93163403Sdfr		return (-1);
93263403Sdfr	}
93363403Sdfr
93463403Sdfr	ppc->ppc_model = type;
93563403Sdfr
93663403Sdfr	outb(SMC935_IND, SMC935_LOGDEV); /* select parallel port, */
937185003Sjhb	outb(SMC935_DAT, 3);	     /* which is logical device 3 */
93863403Sdfr
93963403Sdfr	/* set io port base */
94063403Sdfr	outb(SMC935_IND, SMC935_PORTHI);
94163403Sdfr	outb(SMC935_DAT, (u_char)((ppc->ppc_base & 0xff00) >> 8));
94263403Sdfr	outb(SMC935_IND, SMC935_PORTLO);
94363403Sdfr	outb(SMC935_DAT, (u_char)(ppc->ppc_base & 0xff));
94463403Sdfr
94563403Sdfr	if (!chipset_mode)
94663403Sdfr		ppc->ppc_avm = PPB_COMPATIBLE; /* default mode */
94763403Sdfr	else {
94863403Sdfr		ppc->ppc_avm = chipset_mode;
94963403Sdfr		outb(SMC935_IND, SMC935_PPMODE);
95063403Sdfr		outb(SMC935_DAT, SMC935_CENT); /* start in compatible mode */
95163403Sdfr
95263403Sdfr		/* SPP + EPP or just plain SPP */
95363403Sdfr		if (chipset_mode & (PPB_SPP)) {
95463403Sdfr			if (chipset_mode & PPB_EPP) {
95563403Sdfr				if (ppc->ppc_epp == EPP_1_9) {
95663403Sdfr					outb(SMC935_IND, SMC935_PPMODE);
95763403Sdfr					outb(SMC935_DAT, SMC935_EPP19SPP);
95863403Sdfr				}
95963403Sdfr				if (ppc->ppc_epp == EPP_1_7) {
96063403Sdfr					outb(SMC935_IND, SMC935_PPMODE);
96163403Sdfr					outb(SMC935_DAT, SMC935_EPP17SPP);
96263403Sdfr				}
96363403Sdfr			} else {
96463403Sdfr				outb(SMC935_IND, SMC935_PPMODE);
96563403Sdfr				outb(SMC935_DAT, SMC935_SPP);
96663403Sdfr			}
96763403Sdfr		}
96863403Sdfr
96963403Sdfr		/* ECP + EPP or just plain ECP */
97063403Sdfr		if (chipset_mode & PPB_ECP) {
97163403Sdfr			if (chipset_mode & PPB_EPP) {
97263403Sdfr				if (ppc->ppc_epp == EPP_1_9) {
97363403Sdfr					outb(SMC935_IND, SMC935_PPMODE);
97463403Sdfr					outb(SMC935_DAT, SMC935_ECPEPP19);
97563403Sdfr				}
97663403Sdfr				if (ppc->ppc_epp == EPP_1_7) {
97763403Sdfr					outb(SMC935_IND, SMC935_PPMODE);
97863403Sdfr					outb(SMC935_DAT, SMC935_ECPEPP17);
97963403Sdfr				}
98063403Sdfr			} else {
98163403Sdfr				outb(SMC935_IND, SMC935_PPMODE);
98263403Sdfr				outb(SMC935_DAT, SMC935_ECP);
98363403Sdfr			}
98463403Sdfr		}
98563403Sdfr	}
98663403Sdfr
98763403Sdfr	outb(SMC935_CFG, 0xaa); /* exit config mode */
98863403Sdfr
98963403Sdfr	ppc->ppc_type = PPC_TYPE_SMCLIKE;
99063403Sdfr	ppc_smclike_setmode(ppc, chipset_mode);
99163403Sdfr
99263403Sdfr	return (chipset_mode);
99363403Sdfr}
99463403Sdfr
99563403Sdfr/*
99638061Smsmith * Winbond W83877F stuff
99738061Smsmith *
99838061Smsmith * EFER: extended function enable register
99938061Smsmith * EFIR: extended function index register
100038061Smsmith * EFDR: extended function data register
100138061Smsmith */
100238061Smsmith#define efir ((efer == 0x250) ? 0x251 : 0x3f0)
100338061Smsmith#define efdr ((efer == 0x250) ? 0x252 : 0x3f1)
100438061Smsmith
100538061Smsmithstatic int w83877f_efers[] = { 0x250, 0x3f0, 0x3f0, 0x250 };
1006185003Sjhbstatic int w83877f_keys[] = { 0x89, 0x86, 0x87, 0x88 };
100738061Smsmithstatic int w83877f_keyiter[] = { 1, 2, 2, 1 };
100838061Smsmithstatic int w83877f_hefs[] = { WINB_HEFERE, WINB_HEFRAS, WINB_HEFERE | WINB_HEFRAS, 0 };
100938061Smsmith
101038061Smsmithstatic int
101138061Smsmithppc_w83877f_detect(struct ppc_data *ppc, int chipset_mode)
101238061Smsmith{
101341591Sarchie	int i, j, efer;
101438061Smsmith	unsigned char r, hefere, hefras;
101538061Smsmith
101638061Smsmith	for (i = 0; i < 4; i ++) {
101738061Smsmith		/* first try to enable configuration registers */
101838061Smsmith		efer = w83877f_efers[i];
101938061Smsmith
102038061Smsmith		/* write the key to the EFER */
102138061Smsmith		for (j = 0; j < w83877f_keyiter[i]; j ++)
102238061Smsmith			outb (efer, w83877f_keys[i]);
102338061Smsmith
102438061Smsmith		/* then check HEFERE and HEFRAS bits */
102538061Smsmith		outb (efir, 0x0c);
102638061Smsmith		hefere = inb(efdr) & WINB_HEFERE;
102738061Smsmith
102838061Smsmith		outb (efir, 0x16);
102938061Smsmith		hefras = inb(efdr) & WINB_HEFRAS;
103038061Smsmith
103128221Smsmith		/*
103238061Smsmith		 * HEFRAS	HEFERE
103338061Smsmith		 *   0		   1	write 89h to 250h (power-on default)
103438061Smsmith		 *   1		   0	write 86h twice to 3f0h
103538061Smsmith		 *   1		   1	write 87h twice to 3f0h
103638061Smsmith		 *   0		   0	write 88h to 250h
103728221Smsmith		 */
103838061Smsmith		if ((hefere | hefras) == w83877f_hefs[i])
103938061Smsmith			goto found;
104038061Smsmith	}
104128221Smsmith
104238061Smsmith	return (-1);	/* failed */
104338061Smsmith
104438061Smsmithfound:
104538061Smsmith	/* check base port address - read from CR23 */
104638061Smsmith	outb(efir, 0x23);
104738061Smsmith	if (ppc->ppc_base != inb(efdr) * 4)		/* 4 bytes boundaries */
104838061Smsmith		return (-1);
104938061Smsmith
105038061Smsmith	/* read CHIP ID from CR9/bits0-3 */
105138061Smsmith	outb(efir, 0x9);
105238061Smsmith
105338061Smsmith	switch (inb(efdr) & WINB_CHIPID) {
105438061Smsmith		case WINB_W83877F_ID:
105555939Snsouch			ppc->ppc_model = WINB_W83877F;
105638061Smsmith			break;
105738061Smsmith
105838061Smsmith		case WINB_W83877AF_ID:
105955939Snsouch			ppc->ppc_model = WINB_W83877AF;
106038061Smsmith			break;
106138061Smsmith
106238061Smsmith		default:
106355939Snsouch			ppc->ppc_model = WINB_UNKNOWN;
106428221Smsmith	}
106528221Smsmith
106638061Smsmith	if (bootverbose) {
106738061Smsmith		/* dump of registers */
1068184176Sjhb		device_printf(ppc->ppc_dev, "0x%x - ", w83877f_keys[i]);
106938061Smsmith		for (i = 0; i <= 0xd; i ++) {
107038061Smsmith			outb(efir, i);
107138061Smsmith			printf("0x%x ", inb(efdr));
107238061Smsmith		}
107338061Smsmith		for (i = 0x10; i <= 0x17; i ++) {
107438061Smsmith			outb(efir, i);
107538061Smsmith			printf("0x%x ", inb(efdr));
107638061Smsmith		}
107738061Smsmith		outb(efir, 0x1e);
107838061Smsmith		printf("0x%x ", inb(efdr));
107938061Smsmith		for (i = 0x20; i <= 0x29; i ++) {
108038061Smsmith			outb(efir, i);
108138061Smsmith			printf("0x%x ", inb(efdr));
108238061Smsmith		}
108338061Smsmith		printf("\n");
108438061Smsmith	}
108528221Smsmith
108655939Snsouch	ppc->ppc_type = PPC_TYPE_GENERIC;
108738761Snsouch
108838061Smsmith	if (!chipset_mode) {
108938061Smsmith		/* autodetect mode */
109028221Smsmith
109138061Smsmith		/* select CR0 */
109238061Smsmith		outb(efir, 0x0);
109338061Smsmith		r = inb(efdr) & (WINB_PRTMODS0 | WINB_PRTMODS1);
109428221Smsmith
109538061Smsmith		/* select CR9 */
109638061Smsmith		outb(efir, 0x9);
109738061Smsmith		r |= (inb(efdr) & WINB_PRTMODS2);
109838061Smsmith
109938061Smsmith		switch (r) {
110038061Smsmith		case WINB_W83757:
110138061Smsmith			if (bootverbose)
1102184176Sjhb				device_printf(ppc->ppc_dev,
1103184176Sjhb				    "W83757 compatible mode\n");
110438061Smsmith			return (-1);	/* generic or SMC-like */
110538061Smsmith
110638061Smsmith		case WINB_EXTFDC:
110738061Smsmith		case WINB_EXTADP:
110838061Smsmith		case WINB_EXT2FDD:
110938061Smsmith		case WINB_JOYSTICK:
111038061Smsmith			if (bootverbose)
1111184176Sjhb				device_printf(ppc->ppc_dev,
1112184130Sjhb				    "not in parallel port mode\n");
111338061Smsmith			return (-1);
111438061Smsmith
111538061Smsmith		case (WINB_PARALLEL | WINB_EPP_SPP):
111638061Smsmith			ppc->ppc_avm |= PPB_EPP | PPB_SPP;
111738761Snsouch			if (bootverbose)
1118184176Sjhb				device_printf(ppc->ppc_dev, "EPP SPP\n");
111938061Smsmith			break;
112038061Smsmith
112138061Smsmith		case (WINB_PARALLEL | WINB_ECP):
112238061Smsmith			ppc->ppc_avm |= PPB_ECP | PPB_SPP;
112338761Snsouch			if (bootverbose)
1124184176Sjhb				device_printf(ppc->ppc_dev, "ECP SPP\n");
112538061Smsmith			break;
112638061Smsmith
112738061Smsmith		case (WINB_PARALLEL | WINB_ECP_EPP):
112838061Smsmith			ppc->ppc_avm |= PPB_ECP | PPB_EPP | PPB_SPP;
112955939Snsouch			ppc->ppc_type = PPC_TYPE_SMCLIKE;
113038761Snsouch
113138761Snsouch			if (bootverbose)
1132184176Sjhb				device_printf(ppc->ppc_dev, "ECP+EPP SPP\n");
113338061Smsmith			break;
113438061Smsmith		default:
113587599Sobrien			printf("%s: unknown case (0x%x)!\n", __func__, r);
113638061Smsmith		}
113738061Smsmith
113838061Smsmith	} else {
113938061Smsmith		/* mode forced */
114038061Smsmith
114138061Smsmith		/* select CR9 and set PRTMODS2 bit */
114238061Smsmith		outb(efir, 0x9);
114338061Smsmith		outb(efdr, inb(efdr) & ~WINB_PRTMODS2);
114438061Smsmith
114538061Smsmith		/* select CR0 and reset PRTMODSx bits */
114638061Smsmith		outb(efir, 0x0);
114738061Smsmith		outb(efdr, inb(efdr) & ~(WINB_PRTMODS0 | WINB_PRTMODS1));
114838061Smsmith
114938061Smsmith		if (chipset_mode & PPB_ECP) {
115038761Snsouch			if (chipset_mode & PPB_EPP) {
115138061Smsmith				outb(efdr, inb(efdr) | WINB_ECP_EPP);
115238761Snsouch				if (bootverbose)
1153184176Sjhb					device_printf(ppc->ppc_dev,
1154184176Sjhb					    "ECP+EPP\n");
115538761Snsouch
115655939Snsouch				ppc->ppc_type = PPC_TYPE_SMCLIKE;
115738761Snsouch
115838761Snsouch			} else {
115938061Smsmith				outb(efdr, inb(efdr) | WINB_ECP);
116038761Snsouch				if (bootverbose)
1161184176Sjhb					device_printf(ppc->ppc_dev, "ECP\n");
116238761Snsouch			}
116338061Smsmith		} else {
116438061Smsmith			/* select EPP_SPP otherwise */
116538061Smsmith			outb(efdr, inb(efdr) | WINB_EPP_SPP);
116638761Snsouch			if (bootverbose)
1167184176Sjhb				device_printf(ppc->ppc_dev, "EPP SPP\n");
116838061Smsmith		}
116938061Smsmith		ppc->ppc_avm = chipset_mode;
117028221Smsmith	}
117138761Snsouch
117238061Smsmith	/* exit configuration mode */
117338061Smsmith	outb(efer, 0xaa);
117428221Smsmith
117555939Snsouch	switch (ppc->ppc_type) {
117655939Snsouch	case PPC_TYPE_SMCLIKE:
117755939Snsouch		ppc_smclike_setmode(ppc, chipset_mode);
117855939Snsouch		break;
117955939Snsouch	default:
118055939Snsouch		ppc_generic_setmode(ppc, chipset_mode);
118155939Snsouch		break;
118255939Snsouch	}
118328221Smsmith
118438061Smsmith	return (chipset_mode);
118528221Smsmith}
118655939Snsouch#endif
118728221Smsmith
118828221Smsmith/*
118928221Smsmith * ppc_generic_detect
119028221Smsmith */
119128221Smsmithstatic int
119238061Smsmithppc_generic_detect(struct ppc_data *ppc, int chipset_mode)
119328221Smsmith{
119438761Snsouch	/* default to generic */
119555939Snsouch	ppc->ppc_type = PPC_TYPE_GENERIC;
119638761Snsouch
119738761Snsouch	if (bootverbose)
1198184130Sjhb		device_printf(ppc->ppc_dev, "SPP");
119938761Snsouch
120071622Snsouch	/* first, check for ECP */
120171622Snsouch	w_ecr(ppc, PPC_ECR_PS2);
120271622Snsouch	if ((r_ecr(ppc) & 0xe0) == PPC_ECR_PS2) {
120371622Snsouch		ppc->ppc_dtm |= PPB_ECP | PPB_SPP;
120471622Snsouch		if (bootverbose)
1205184130Sjhb			printf(" ECP ");
120628221Smsmith
120771622Snsouch		/* search for SMC style ECP+EPP mode */
120871622Snsouch		w_ecr(ppc, PPC_ECR_EPP);
120971622Snsouch	}
121028221Smsmith
121171622Snsouch	/* try to reset EPP timeout bit */
121271622Snsouch	if (ppc_check_epp_timeout(ppc)) {
121371622Snsouch		ppc->ppc_dtm |= PPB_EPP;
121428221Smsmith
121571622Snsouch		if (ppc->ppc_dtm & PPB_ECP) {
121671622Snsouch			/* SMC like chipset found */
121771622Snsouch			ppc->ppc_model = SMC_LIKE;
121871622Snsouch			ppc->ppc_type = PPC_TYPE_SMCLIKE;
121938761Snsouch
122071622Snsouch			if (bootverbose)
122171622Snsouch				printf(" ECP+EPP");
122238761Snsouch		} else {
122371622Snsouch			if (bootverbose)
122471622Snsouch				printf(" EPP");
122538061Smsmith		}
122671622Snsouch	} else {
122771622Snsouch		/* restore to standard mode */
122871622Snsouch		w_ecr(ppc, PPC_ECR_STD);
122971622Snsouch	}
123028221Smsmith
123171622Snsouch	/* XXX try to detect NIBBLE and PS2 modes */
123271622Snsouch	ppc->ppc_dtm |= PPB_NIBBLE;
123328221Smsmith
123471622Snsouch	if (chipset_mode)
123538061Smsmith		ppc->ppc_avm = chipset_mode;
123671622Snsouch	else
123771622Snsouch		ppc->ppc_avm = ppc->ppc_dtm;
123838061Smsmith
123938761Snsouch	if (bootverbose)
124038761Snsouch		printf("\n");
124138061Smsmith
124255939Snsouch	switch (ppc->ppc_type) {
124355939Snsouch	case PPC_TYPE_SMCLIKE:
124455939Snsouch		ppc_smclike_setmode(ppc, chipset_mode);
124555939Snsouch		break;
124655939Snsouch	default:
124755939Snsouch		ppc_generic_setmode(ppc, chipset_mode);
124855939Snsouch		break;
124955939Snsouch	}
125038761Snsouch
125138061Smsmith	return (chipset_mode);
125228221Smsmith}
125328221Smsmith
125428221Smsmith/*
125528221Smsmith * ppc_detect()
125628221Smsmith *
125728221Smsmith * mode is the mode suggested at boot
125828221Smsmith */
125928221Smsmithstatic int
126038061Smsmithppc_detect(struct ppc_data *ppc, int chipset_mode) {
126128221Smsmith
126255939Snsouch#ifdef PPC_PROBE_CHIPSET
126338061Smsmith	int i, mode;
126428221Smsmith
126538061Smsmith	/* list of supported chipsets */
126638061Smsmith	int (*chipset_detect[])(struct ppc_data *, int) = {
126738061Smsmith		ppc_pc873xx_detect,
126838061Smsmith		ppc_smc37c66xgt_detect,
126938061Smsmith		ppc_w83877f_detect,
127063403Sdfr		ppc_smc37c935_detect,
127138061Smsmith		ppc_generic_detect,
127238061Smsmith		NULL
127338061Smsmith	};
127455939Snsouch#endif
127528221Smsmith
127638061Smsmith	/* if can't find the port and mode not forced return error */
127738061Smsmith	if (!ppc_detect_port(ppc) && chipset_mode == 0)
127838061Smsmith		return (EIO);			/* failed, port not present */
127928221Smsmith
128038061Smsmith	/* assume centronics compatible mode is supported */
128138061Smsmith	ppc->ppc_avm = PPB_COMPATIBLE;
128228221Smsmith
128355939Snsouch#ifdef PPC_PROBE_CHIPSET
128438061Smsmith	/* we have to differenciate available chipset modes,
128538061Smsmith	 * chipset running modes and IEEE-1284 operating modes
128638061Smsmith	 *
128738061Smsmith	 * after detection, the port must support running in compatible mode
128838061Smsmith	 */
128940784Snsouch	if (ppc->ppc_flags & 0x40) {
129040784Snsouch		if (bootverbose)
129140784Snsouch			printf("ppc: chipset forced to generic\n");
129255939Snsouch#endif
129340784Snsouch
129440784Snsouch		ppc->ppc_mode = ppc_generic_detect(ppc, chipset_mode);
129540784Snsouch
129655939Snsouch#ifdef PPC_PROBE_CHIPSET
129740784Snsouch	} else {
129840784Snsouch		for (i=0; chipset_detect[i] != NULL; i++) {
129940784Snsouch			if ((mode = chipset_detect[i](ppc, chipset_mode)) != -1) {
130040784Snsouch				ppc->ppc_mode = mode;
130140784Snsouch				break;
130240784Snsouch			}
130338061Smsmith		}
130438061Smsmith	}
130555939Snsouch#endif
130628221Smsmith
130742475Snsouch	/* configure/detect ECP FIFO */
130842475Snsouch	if ((ppc->ppc_avm & PPB_ECP) && !(ppc->ppc_flags & 0x80))
130942475Snsouch		ppc_detect_fifo(ppc);
131042475Snsouch
131128221Smsmith	return (0);
131228221Smsmith}
131328221Smsmith
131428221Smsmith/*
131538061Smsmith * ppc_exec_microseq()
131638061Smsmith *
131738061Smsmith * Execute a microsequence.
131838061Smsmith * Microsequence mechanism is supposed to handle fast I/O operations.
131938061Smsmith */
1320118292Sambriskoint
132155939Snsouchppc_exec_microseq(device_t dev, struct ppb_microseq **p_msq)
132238061Smsmith{
132355939Snsouch	struct ppc_data *ppc = DEVTOSOFTC(dev);
132439521Snsouch	struct ppb_microseq *mi;
132538061Smsmith	char cc, *p;
132639135Snsouch	int i, iter, len;
132738061Smsmith	int error;
132838061Smsmith
132939135Snsouch	register int reg;
133039135Snsouch	register char mask;
133139135Snsouch	register int accum = 0;
133239135Snsouch	register char *ptr = 0;
133338061Smsmith
133439521Snsouch	struct ppb_microseq *stack = 0;
133538061Smsmith
133638061Smsmith/* microsequence registers are equivalent to PC-like port registers */
133738061Smsmith
1338182016Sjhb#define r_reg(reg,ppc) (bus_read_1((ppc)->res_ioport, reg))
1339182016Sjhb#define w_reg(reg, ppc, byte) (bus_write_1((ppc)->res_ioport, reg, byte))
134078646Snsouch
134139521Snsouch#define INCR_PC (mi ++)		/* increment program counter */
134238061Smsmith
1343187576Sjhb	PPC_ASSERT_LOCKED(ppc);
134439521Snsouch	mi = *p_msq;
134538061Smsmith	for (;;) {
1346185003Sjhb		switch (mi->opcode) {
134738061Smsmith		case MS_OP_RSET:
134838061Smsmith			cc = r_reg(mi->arg[0].i, ppc);
134939135Snsouch			cc &= (char)mi->arg[2].i;	/* clear mask */
135039135Snsouch			cc |= (char)mi->arg[1].i;	/* assert mask */
1351185003Sjhb			w_reg(mi->arg[0].i, ppc, cc);
135238061Smsmith			INCR_PC;
1353185003Sjhb			break;
135438061Smsmith
135538061Smsmith		case MS_OP_RASSERT_P:
135639135Snsouch			reg = mi->arg[1].i;
135739135Snsouch			ptr = ppc->ppc_ptr;
135839135Snsouch
135939135Snsouch			if ((len = mi->arg[0].i) == MS_ACCUM) {
136039135Snsouch				accum = ppc->ppc_accum;
136139135Snsouch				for (; accum; accum--)
136239135Snsouch					w_reg(reg, ppc, *ptr++);
136339135Snsouch				ppc->ppc_accum = accum;
136439135Snsouch			} else
136539135Snsouch				for (i=0; i<len; i++)
136639135Snsouch					w_reg(reg, ppc, *ptr++);
136739135Snsouch			ppc->ppc_ptr = ptr;
136839135Snsouch
136938061Smsmith			INCR_PC;
137038061Smsmith			break;
137138061Smsmith
1372185003Sjhb		case MS_OP_RFETCH_P:
137339135Snsouch			reg = mi->arg[1].i;
137439135Snsouch			mask = (char)mi->arg[2].i;
137539135Snsouch			ptr = ppc->ppc_ptr;
137639135Snsouch
137739135Snsouch			if ((len = mi->arg[0].i) == MS_ACCUM) {
137839135Snsouch				accum = ppc->ppc_accum;
137939135Snsouch				for (; accum; accum--)
138039135Snsouch					*ptr++ = r_reg(reg, ppc) & mask;
138139135Snsouch				ppc->ppc_accum = accum;
138239135Snsouch			} else
138339135Snsouch				for (i=0; i<len; i++)
138439135Snsouch					*ptr++ = r_reg(reg, ppc) & mask;
138539135Snsouch			ppc->ppc_ptr = ptr;
138639135Snsouch
138738061Smsmith			INCR_PC;
1388185003Sjhb			break;
138938061Smsmith
1390185003Sjhb		case MS_OP_RFETCH:
139138061Smsmith			*((char *) mi->arg[2].p) = r_reg(mi->arg[0].i, ppc) &
139239135Snsouch							(char)mi->arg[1].i;
139338061Smsmith			INCR_PC;
1394185003Sjhb			break;
139538061Smsmith
139638061Smsmith		case MS_OP_RASSERT:
1397185003Sjhb		case MS_OP_DELAY:
1398185003Sjhb
139938061Smsmith		/* let's suppose the next instr. is the same */
140038061Smsmith		prefetch:
140138061Smsmith			for (;mi->opcode == MS_OP_RASSERT; INCR_PC)
140239135Snsouch				w_reg(mi->arg[0].i, ppc, (char)mi->arg[1].i);
140338061Smsmith
140438061Smsmith			if (mi->opcode == MS_OP_DELAY) {
140538061Smsmith				DELAY(mi->arg[0].i);
140638061Smsmith				INCR_PC;
140738061Smsmith				goto prefetch;
140838061Smsmith			}
140938061Smsmith			break;
141038061Smsmith
141139135Snsouch		case MS_OP_ADELAY:
1412187576Sjhb			if (mi->arg[0].i) {
1413187576Sjhb				PPC_UNLOCK(ppc);
1414166909Sjhb				pause("ppbdelay", mi->arg[0].i * (hz/1000));
1415187576Sjhb				PPC_LOCK(ppc);
1416187576Sjhb			}
141738061Smsmith			INCR_PC;
141839135Snsouch			break;
141938061Smsmith
142038061Smsmith		case MS_OP_TRIG:
142138061Smsmith			reg = mi->arg[0].i;
142238061Smsmith			iter = mi->arg[1].i;
142338061Smsmith			p = (char *)mi->arg[2].p;
142438061Smsmith
142539135Snsouch			/* XXX delay limited to 255 us */
142638061Smsmith			for (i=0; i<iter; i++) {
142738061Smsmith				w_reg(reg, ppc, *p++);
142838061Smsmith				DELAY((unsigned char)*p++);
142938061Smsmith			}
143038061Smsmith			INCR_PC;
143138061Smsmith			break;
143238061Smsmith
1433185003Sjhb		case MS_OP_SET:
1434185003Sjhb			ppc->ppc_accum = mi->arg[0].i;
143538061Smsmith			INCR_PC;
1436185003Sjhb			break;
143738061Smsmith
1438185003Sjhb		case MS_OP_DBRA:
1439185003Sjhb			if (--ppc->ppc_accum > 0)
1440185003Sjhb				mi += mi->arg[0].i;
144143433Snsouch			INCR_PC;
1442185003Sjhb			break;
144338061Smsmith
1444185003Sjhb		case MS_OP_BRSET:
1445185003Sjhb			cc = r_str(ppc);
1446185003Sjhb			if ((cc & (char)mi->arg[0].i) == (char)mi->arg[0].i)
1447185003Sjhb				mi += mi->arg[1].i;
144843433Snsouch			INCR_PC;
1449185003Sjhb			break;
145038061Smsmith
1451185003Sjhb		case MS_OP_BRCLEAR:
1452185003Sjhb			cc = r_str(ppc);
1453185003Sjhb			if ((cc & (char)mi->arg[0].i) == 0)
1454185003Sjhb				mi += mi->arg[1].i;
145543433Snsouch			INCR_PC;
1456185003Sjhb			break;
145738061Smsmith
145839135Snsouch		case MS_OP_BRSTAT:
145939135Snsouch			cc = r_str(ppc);
146039135Snsouch			if ((cc & ((char)mi->arg[0].i | (char)mi->arg[1].i)) ==
146139135Snsouch							(char)mi->arg[0].i)
146239521Snsouch				mi += mi->arg[2].i;
146343433Snsouch			INCR_PC;
146439135Snsouch			break;
146539135Snsouch
146638061Smsmith		case MS_OP_C_CALL:
146738061Smsmith			/*
146838061Smsmith			 * If the C call returns !0 then end the microseq.
146938061Smsmith			 * The current state of ptr is passed to the C function
147038061Smsmith			 */
147139135Snsouch			if ((error = mi->arg[0].f(mi->arg[1].p, ppc->ppc_ptr)))
147238061Smsmith				return (error);
147338061Smsmith
147438061Smsmith			INCR_PC;
147538061Smsmith			break;
147638061Smsmith
147738061Smsmith		case MS_OP_PTR:
147839135Snsouch			ppc->ppc_ptr = (char *)mi->arg[0].p;
147938061Smsmith			INCR_PC;
148038061Smsmith			break;
148138061Smsmith
148238061Smsmith		case MS_OP_CALL:
148339521Snsouch			if (stack)
148487599Sobrien				panic("%s: too much calls", __func__);
148538061Smsmith
148638061Smsmith			if (mi->arg[0].p) {
148738061Smsmith				/* store the state of the actual
148838061Smsmith				 * microsequence
148938061Smsmith				 */
149039521Snsouch				stack = mi;
149138061Smsmith
149238061Smsmith				/* jump to the new microsequence */
149339521Snsouch				mi = (struct ppb_microseq *)mi->arg[0].p;
149438061Smsmith			} else
149538061Smsmith				INCR_PC;
149638061Smsmith
149738061Smsmith			break;
149838061Smsmith
149938061Smsmith		case MS_OP_SUBRET:
150038061Smsmith			/* retrieve microseq and pc state before the call */
150139521Snsouch			mi = stack;
150238061Smsmith
150338061Smsmith			/* reset the stack */
150439521Snsouch			stack = 0;
150538061Smsmith
150638061Smsmith			/* XXX return code */
150738061Smsmith
150838061Smsmith			INCR_PC;
150938061Smsmith			break;
151038061Smsmith
1511185003Sjhb		case MS_OP_PUT:
1512185003Sjhb		case MS_OP_GET:
1513185003Sjhb		case MS_OP_RET:
151438061Smsmith			/* can't return to ppb level during the execution
151538061Smsmith			 * of a submicrosequence */
151639521Snsouch			if (stack)
151738061Smsmith				panic("%s: can't return to ppb level",
151887599Sobrien								__func__);
151938061Smsmith
152038061Smsmith			/* update pc for ppb level of execution */
152139521Snsouch			*p_msq = mi;
152238061Smsmith
152338061Smsmith			/* return to ppb level of execution */
152438061Smsmith			return (0);
152538061Smsmith
1526185003Sjhb		default:
1527185003Sjhb			panic("%s: unknown microsequence opcode 0x%x",
1528185003Sjhb			    __func__, mi->opcode);
1529185003Sjhb		}
153038061Smsmith	}
153138061Smsmith
153238061Smsmith	/* unreached */
153338061Smsmith}
153438061Smsmith
153542475Snsouchstatic void
153655939Snsouchppcintr(void *arg)
153742475Snsouch{
1538183053Sjhb	struct ppc_data *ppc = arg;
153943990Snsouch	u_char ctr, ecr, str;
154042475Snsouch
1541183053Sjhb	/*
1542183053Sjhb	 * If we have any child interrupt handlers registered, let
1543183053Sjhb	 * them handle this interrupt.
1544183053Sjhb	 *
1545183053Sjhb	 * XXX: If DMA is in progress should we just complete that w/o
1546183053Sjhb	 * doing this?
1547183053Sjhb	 */
1548187576Sjhb	PPC_LOCK(ppc);
1549187576Sjhb	if (ppc->ppc_intr_hook != NULL &&
1550187576Sjhb	    ppc->ppc_intr_hook(ppc->ppc_intr_arg) == 0) {
1551187576Sjhb		PPC_UNLOCK(ppc);
1552183053Sjhb		return;
1553183053Sjhb	}
1554183053Sjhb
155543990Snsouch	str = r_str(ppc);
155642475Snsouch	ctr = r_ctr(ppc);
155742475Snsouch	ecr = r_ecr(ppc);
155842475Snsouch
1559153072Sru#if defined(PPC_DEBUG) && PPC_DEBUG > 1
156043990Snsouch		printf("![%x/%x/%x]", ctr, ecr, str);
156142475Snsouch#endif
156242475Snsouch
156342475Snsouch	/* don't use ecp mode with IRQENABLE set */
156442475Snsouch	if (ctr & IRQENABLE) {
1565187576Sjhb		PPC_UNLOCK(ppc);
156642475Snsouch		return;
156742475Snsouch	}
156842475Snsouch
156943990Snsouch	/* interrupts are generated by nFault signal
157043990Snsouch	 * only in ECP mode */
157143990Snsouch	if ((str & nFAULT) && (ppc->ppc_mode & PPB_ECP)) {
157243990Snsouch		/* check if ppc driver has programmed the
157343990Snsouch		 * nFault interrupt */
157442475Snsouch		if  (ppc->ppc_irqstat & PPC_IRQ_nFAULT) {
157542475Snsouch
157642475Snsouch			w_ecr(ppc, ecr | PPC_nFAULT_INTR);
157742475Snsouch			ppc->ppc_irqstat &= ~PPC_IRQ_nFAULT;
157842475Snsouch		} else {
157955939Snsouch			/* shall be handled by underlying layers XXX */
1580187576Sjhb			PPC_UNLOCK(ppc);
158142475Snsouch			return;
158242475Snsouch		}
158342475Snsouch	}
158442475Snsouch
158542475Snsouch	if (ppc->ppc_irqstat & PPC_IRQ_DMA) {
158642475Snsouch		/* disable interrupts (should be done by hardware though) */
158742475Snsouch		w_ecr(ppc, ecr | PPC_SERVICE_INTR);
158842475Snsouch		ppc->ppc_irqstat &= ~PPC_IRQ_DMA;
158942475Snsouch		ecr = r_ecr(ppc);
159042475Snsouch
159142475Snsouch		/* check if DMA completed */
159242475Snsouch		if ((ppc->ppc_avm & PPB_ECP) && (ecr & PPC_ENABLE_DMA)) {
159342475Snsouch#ifdef PPC_DEBUG
159442475Snsouch			printf("a");
159542475Snsouch#endif
159642475Snsouch			/* stop DMA */
159742475Snsouch			w_ecr(ppc, ecr & ~PPC_ENABLE_DMA);
159842475Snsouch			ecr = r_ecr(ppc);
159942475Snsouch
160042475Snsouch			if (ppc->ppc_dmastat == PPC_DMA_STARTED) {
160142475Snsouch#ifdef PPC_DEBUG
160242475Snsouch				printf("d");
160342475Snsouch#endif
1604158005Smarcel				ppc->ppc_dmadone(ppc);
160542475Snsouch				ppc->ppc_dmastat = PPC_DMA_COMPLETE;
160642475Snsouch
160742475Snsouch				/* wakeup the waiting process */
1608111748Sdes				wakeup(ppc);
160942475Snsouch			}
161042475Snsouch		}
161142475Snsouch	} else if (ppc->ppc_irqstat & PPC_IRQ_FIFO) {
161242475Snsouch
161342475Snsouch		/* classic interrupt I/O */
161442475Snsouch		ppc->ppc_irqstat &= ~PPC_IRQ_FIFO;
161542475Snsouch	}
1616187576Sjhb	PPC_UNLOCK(ppc);
161742475Snsouch
161842475Snsouch	return;
161942475Snsouch}
162042475Snsouch
1621118292Sambriskoint
162255939Snsouchppc_read(device_t dev, char *buf, int len, int mode)
162342475Snsouch{
162442475Snsouch	return (EINVAL);
162542475Snsouch}
162642475Snsouch
1627118292Sambriskoint
162855939Snsouchppc_write(device_t dev, char *buf, int len, int how)
162942475Snsouch{
1630158005Smarcel	return (EINVAL);
163142475Snsouch}
163242475Snsouch
1633188173Simpint
163455939Snsouchppc_reset_epp(device_t dev)
163538061Smsmith{
163655939Snsouch	struct ppc_data *ppc = DEVTOSOFTC(dev);
1637185003Sjhb
1638187576Sjhb	PPC_ASSERT_LOCKED(ppc);
163955939Snsouch	ppc_reset_epp_timeout(ppc);
164038061Smsmith
1641188173Simp	return 0;
164255939Snsouch}
164338061Smsmith
1644118292Sambriskoint
164555939Snsouchppc_setmode(device_t dev, int mode)
164655939Snsouch{
164755939Snsouch	struct ppc_data *ppc = DEVTOSOFTC(dev);
164838061Smsmith
1649187576Sjhb	PPC_ASSERT_LOCKED(ppc);
165055939Snsouch	switch (ppc->ppc_type) {
165155939Snsouch	case PPC_TYPE_SMCLIKE:
165255939Snsouch		return (ppc_smclike_setmode(ppc, mode));
165355939Snsouch		break;
165442475Snsouch
165555939Snsouch	case PPC_TYPE_GENERIC:
165655939Snsouch	default:
165755939Snsouch		return (ppc_generic_setmode(ppc, mode));
165855939Snsouch		break;
165938761Snsouch	}
166038761Snsouch
166155939Snsouch	/* not reached */
166255939Snsouch	return (ENXIO);
166338061Smsmith}
166438061Smsmith
1665118292Sambriskoint
1666158005Smarcelppc_probe(device_t dev, int rid)
1667118292Sambrisko{
1668118292Sambrisko#ifdef __i386__
1669118292Sambrisko	static short next_bios_ppc = 0;
1670158005Smarcel#ifdef PC98
1671158005Smarcel	unsigned int pc98_ieee_mode = 0x00;
1672158005Smarcel	unsigned int tmp;
1673118292Sambrisko#endif
1674158005Smarcel#endif
1675118292Sambrisko	struct ppc_data *ppc;
1676118292Sambrisko	int error;
1677118292Sambrisko	u_long port;
1678118292Sambrisko
167955939Snsouch	/*
168055939Snsouch	 * Allocate the ppc_data structure.
168155939Snsouch	 */
168255939Snsouch	ppc = DEVTOSOFTC(dev);
168355939Snsouch	bzero(ppc, sizeof(struct ppc_data));
168438761Snsouch
1685158005Smarcel	ppc->rid_ioport = rid;
168638761Snsouch
168755939Snsouch	/* retrieve ISA parameters */
1688158005Smarcel	error = bus_get_resource(dev, SYS_RES_IOPORT, rid, &port, NULL);
168938761Snsouch
169056617Sdfr#ifdef __i386__
169128221Smsmith	/*
169228221Smsmith	 * If port not specified, use bios list.
169328221Smsmith	 */
169456617Sdfr	if (error) {
1695158005Smarcel#ifdef PC98
1696158005Smarcel		if (next_bios_ppc == 0) {
1697158005Smarcel			/* Use default IEEE-1284 port of NEC PC-98x1 */
1698158005Smarcel			port = PC98_IEEE_1284_PORT;
1699158005Smarcel			next_bios_ppc += 1;
1700158005Smarcel			if (bootverbose)
1701158005Smarcel				device_printf(dev,
1702185003Sjhb				    "parallel port found at 0x%lx\n", port);
1703158005Smarcel		}
1704158005Smarcel#else
1705185003Sjhb		if ((next_bios_ppc < BIOS_MAX_PPC) &&
1706185003Sjhb		    (*(BIOS_PORTS + next_bios_ppc) != 0)) {
1707185003Sjhb			port = *(BIOS_PORTS + next_bios_ppc++);
170842475Snsouch			if (bootverbose)
1709185003Sjhb				device_printf(dev,
1710185003Sjhb				    "parallel port found at 0x%lx\n", port);
171155939Snsouch		} else {
171255939Snsouch			device_printf(dev, "parallel port not found.\n");
1713185003Sjhb			return (ENXIO);
171455939Snsouch		}
1715158005Smarcel#endif	/* PC98 */
1716158005Smarcel		bus_set_resource(dev, SYS_RES_IOPORT, rid, port,
171763403Sdfr				 IO_LPTSIZE_EXTENDED);
171828221Smsmith	}
171956617Sdfr#endif
172028221Smsmith
172155939Snsouch	/* IO port is mandatory */
172263403Sdfr
172363403Sdfr	/* Try "extended" IO port range...*/
172455939Snsouch	ppc->res_ioport = bus_alloc_resource(dev, SYS_RES_IOPORT,
172556617Sdfr					     &ppc->rid_ioport, 0, ~0,
172663403Sdfr					     IO_LPTSIZE_EXTENDED, RF_ACTIVE);
172728221Smsmith
172863403Sdfr	if (ppc->res_ioport != 0) {
172962061Sdfr		if (bootverbose)
173063403Sdfr			device_printf(dev, "using extended I/O port range\n");
173163403Sdfr	} else {
173263403Sdfr		/* Failed? If so, then try the "normal" IO port range... */
173363403Sdfr		 ppc->res_ioport = bus_alloc_resource(dev, SYS_RES_IOPORT,
173463403Sdfr						      &ppc->rid_ioport, 0, ~0,
173563403Sdfr						      IO_LPTSIZE_NORMAL,
173663403Sdfr						      RF_ACTIVE);
173763403Sdfr		if (ppc->res_ioport != 0) {
173863403Sdfr			if (bootverbose)
173963403Sdfr				device_printf(dev, "using normal I/O port range\n");
174063403Sdfr		} else {
174163403Sdfr			device_printf(dev, "cannot reserve I/O port range\n");
174263403Sdfr			goto error;
174363403Sdfr		}
174462061Sdfr	}
174560544Sdfr
174660544Sdfr 	ppc->ppc_base = rman_get_start(ppc->res_ioport);
174760544Sdfr
174855939Snsouch	ppc->ppc_flags = device_get_flags(dev);
174928221Smsmith
175055939Snsouch	if (!(ppc->ppc_flags & 0x20)) {
1751127135Snjl		ppc->res_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ,
1752127135Snjl						      &ppc->rid_irq,
1753127135Snjl						      RF_SHAREABLE);
1754127135Snjl		ppc->res_drq = bus_alloc_resource_any(dev, SYS_RES_DRQ,
1755127135Snjl						      &ppc->rid_drq,
1756127135Snjl						      RF_ACTIVE);
175755939Snsouch	}
175840784Snsouch
175955939Snsouch	if (ppc->res_irq)
176056617Sdfr		ppc->ppc_irq = rman_get_start(ppc->res_irq);
176155939Snsouch	if (ppc->res_drq)
176256617Sdfr		ppc->ppc_dmachan = rman_get_start(ppc->res_drq);
176328221Smsmith
1764184130Sjhb	ppc->ppc_dev = dev;
176555939Snsouch	ppc->ppc_model = GENERIC;
176628221Smsmith
176755939Snsouch	ppc->ppc_mode = PPB_COMPATIBLE;
176855939Snsouch	ppc->ppc_epp = (ppc->ppc_flags & 0x10) >> 4;
176942475Snsouch
177055939Snsouch	ppc->ppc_type = PPC_TYPE_GENERIC;
177128221Smsmith
1772158005Smarcel#if defined(__i386__) && defined(PC98)
177328221Smsmith	/*
1774158005Smarcel	 * IEEE STD 1284 Function Check and Enable
1775158005Smarcel	 * for default IEEE-1284 port of NEC PC-98x1
1776158005Smarcel	 */
1777158005Smarcel	if (ppc->ppc_base == PC98_IEEE_1284_PORT &&
1778158005Smarcel	    !(ppc->ppc_flags & PC98_IEEE_1284_DISABLE)) {
1779158005Smarcel		tmp = inb(ppc->ppc_base + PPC_1284_ENABLE);
1780158005Smarcel		pc98_ieee_mode = tmp;
1781158005Smarcel		if ((tmp & 0x10) == 0x10) {
1782158005Smarcel			outb(ppc->ppc_base + PPC_1284_ENABLE, tmp & ~0x10);
1783158005Smarcel			tmp = inb(ppc->ppc_base + PPC_1284_ENABLE);
1784158005Smarcel			if ((tmp & 0x10) == 0x10)
1785158005Smarcel				goto error;
1786158005Smarcel		} else {
1787158005Smarcel			outb(ppc->ppc_base + PPC_1284_ENABLE, tmp | 0x10);
1788158005Smarcel			tmp = inb(ppc->ppc_base + PPC_1284_ENABLE);
1789158005Smarcel			if ((tmp & 0x10) != 0x10)
1790158005Smarcel				goto error;
1791158005Smarcel		}
1792158005Smarcel		outb(ppc->ppc_base + PPC_1284_ENABLE, pc98_ieee_mode | 0x10);
1793158005Smarcel	}
1794158005Smarcel#endif
1795158005Smarcel
1796158005Smarcel	/*
179735256Sdes	 * Try to detect the chipset and its mode.
179828221Smsmith	 */
179955939Snsouch	if (ppc_detect(ppc, ppc->ppc_flags & 0xf))
180028221Smsmith		goto error;
180128221Smsmith
180255939Snsouch	return (0);
180328221Smsmith
180428221Smsmitherror:
1805158005Smarcel#if defined(__i386__) && defined(PC98)
1806158005Smarcel	if (ppc->ppc_base == PC98_IEEE_1284_PORT &&
1807158005Smarcel	    !(ppc->ppc_flags & PC98_IEEE_1284_DISABLE)) {
1808158005Smarcel		outb(ppc->ppc_base + PPC_1284_ENABLE, pc98_ieee_mode);
1809158005Smarcel	}
1810158005Smarcel#endif
181155939Snsouch	if (ppc->res_irq != 0) {
181255939Snsouch		bus_release_resource(dev, SYS_RES_IRQ, ppc->rid_irq,
181355939Snsouch				     ppc->res_irq);
181455939Snsouch	}
181555939Snsouch	if (ppc->res_ioport != 0) {
181655939Snsouch		bus_release_resource(dev, SYS_RES_IOPORT, ppc->rid_ioport,
181755939Snsouch				     ppc->res_ioport);
181855939Snsouch	}
181955939Snsouch	if (ppc->res_drq != 0) {
182055939Snsouch		bus_release_resource(dev, SYS_RES_DRQ, ppc->rid_drq,
182155939Snsouch				     ppc->res_drq);
182255939Snsouch	}
182355939Snsouch	return (ENXIO);
182428221Smsmith}
182528221Smsmith
1826118292Sambriskoint
182755939Snsouchppc_attach(device_t dev)
182828221Smsmith{
182955939Snsouch	struct ppc_data *ppc = DEVTOSOFTC(dev);
1830183053Sjhb	int error;
183128221Smsmith
1832187576Sjhb	mtx_init(&ppc->ppc_lock, device_get_nameunit(dev), "ppc", MTX_DEF);
1833187576Sjhb
183455939Snsouch	device_printf(dev, "%s chipset (%s) in %s mode%s\n",
183555939Snsouch		      ppc_models[ppc->ppc_model], ppc_avms[ppc->ppc_avm],
183655939Snsouch		      ppc_modes[ppc->ppc_mode], (PPB_IS_EPP(ppc->ppc_mode)) ?
183755939Snsouch		      ppc_epp_protocol[ppc->ppc_epp] : "");
1838185003Sjhb
183942475Snsouch	if (ppc->ppc_fifo)
184055939Snsouch		device_printf(dev, "FIFO with %d/%d/%d bytes threshold\n",
184155939Snsouch			      ppc->ppc_fifo, ppc->ppc_wthr, ppc->ppc_rthr);
184242475Snsouch
1843183053Sjhb	if (ppc->res_irq) {
1844183053Sjhb		/* default to the tty mask for registration */	/* XXX */
1845187576Sjhb		error = bus_setup_intr(dev, ppc->res_irq, INTR_TYPE_TTY |
1846187576Sjhb		    INTR_MPSAFE, NULL, ppcintr, ppc, &ppc->intr_cookie);
1847183053Sjhb		if (error) {
1848183053Sjhb			device_printf(dev,
1849183053Sjhb			    "failed to register interrupt handler: %d\n",
1850183053Sjhb			    error);
1851187576Sjhb			mtx_destroy(&ppc->ppc_lock);
1852183053Sjhb			return (error);
1853183053Sjhb		}
1854183053Sjhb	}
1855183053Sjhb
185655939Snsouch	/* add ppbus as a child of this isa to parallel bridge */
1857187576Sjhb	ppc->ppbus = device_add_child(dev, "ppbus", -1);
185855939Snsouch
185928221Smsmith	/*
186055939Snsouch	 * Probe the ppbus and attach devices found.
186128221Smsmith	 */
1862187576Sjhb	device_probe_and_attach(ppc->ppbus);
186328221Smsmith
186455939Snsouch	return (0);
186555939Snsouch}
186642475Snsouch
1867157774Siwasakiint
1868157774Siwasakippc_detach(device_t dev)
1869157774Siwasaki{
1870157774Siwasaki	struct ppc_data *ppc = DEVTOSOFTC(dev);
1871157774Siwasaki
1872157774Siwasaki	if (ppc->res_irq == 0) {
1873157774Siwasaki		return (ENXIO);
1874157774Siwasaki	}
1875157774Siwasaki
1876157774Siwasaki	/* detach & delete all children */
1877227849Shselasky	device_delete_children(dev);
1878157774Siwasaki
1879157774Siwasaki	if (ppc->res_irq != 0) {
1880157774Siwasaki		bus_teardown_intr(dev, ppc->res_irq, ppc->intr_cookie);
1881157774Siwasaki		bus_release_resource(dev, SYS_RES_IRQ, ppc->rid_irq,
1882157774Siwasaki				     ppc->res_irq);
1883157774Siwasaki	}
1884157774Siwasaki	if (ppc->res_ioport != 0) {
1885157774Siwasaki		bus_release_resource(dev, SYS_RES_IOPORT, ppc->rid_ioport,
1886157774Siwasaki				     ppc->res_ioport);
1887157774Siwasaki	}
1888157774Siwasaki	if (ppc->res_drq != 0) {
1889157774Siwasaki		bus_release_resource(dev, SYS_RES_DRQ, ppc->rid_drq,
1890157774Siwasaki				     ppc->res_drq);
1891157774Siwasaki	}
1892157774Siwasaki
1893187576Sjhb	mtx_destroy(&ppc->ppc_lock);
1894187576Sjhb
1895157774Siwasaki	return (0);
1896157774Siwasaki}
1897157774Siwasaki
1898118292Sambriskou_char
189955939Snsouchppc_io(device_t ppcdev, int iop, u_char *addr, int cnt, u_char byte)
190055939Snsouch{
190155939Snsouch	struct ppc_data *ppc = DEVTOSOFTC(ppcdev);
1902185003Sjhb
1903187576Sjhb	PPC_ASSERT_LOCKED(ppc);
190455939Snsouch	switch (iop) {
190555939Snsouch	case PPB_OUTSB_EPP:
1906182016Sjhb	    bus_write_multi_1(ppc->res_ioport, PPC_EPP_DATA, addr, cnt);
190755939Snsouch		break;
190855939Snsouch	case PPB_OUTSW_EPP:
1909182016Sjhb	    bus_write_multi_2(ppc->res_ioport, PPC_EPP_DATA, (u_int16_t *)addr, cnt);
191055939Snsouch		break;
191155939Snsouch	case PPB_OUTSL_EPP:
1912182016Sjhb	    bus_write_multi_4(ppc->res_ioport, PPC_EPP_DATA, (u_int32_t *)addr, cnt);
191355939Snsouch		break;
191455939Snsouch	case PPB_INSB_EPP:
1915182016Sjhb	    bus_read_multi_1(ppc->res_ioport, PPC_EPP_DATA, addr, cnt);
191655939Snsouch		break;
191755939Snsouch	case PPB_INSW_EPP:
1918182016Sjhb	    bus_read_multi_2(ppc->res_ioport, PPC_EPP_DATA, (u_int16_t *)addr, cnt);
191955939Snsouch		break;
192055939Snsouch	case PPB_INSL_EPP:
1921182016Sjhb	    bus_read_multi_4(ppc->res_ioport, PPC_EPP_DATA, (u_int32_t *)addr, cnt);
192255939Snsouch		break;
192355939Snsouch	case PPB_RDTR:
192455939Snsouch		return (r_dtr(ppc));
192555939Snsouch	case PPB_RSTR:
192655939Snsouch		return (r_str(ppc));
192755939Snsouch	case PPB_RCTR:
192855939Snsouch		return (r_ctr(ppc));
192955939Snsouch	case PPB_REPP_A:
193055939Snsouch		return (r_epp_A(ppc));
193155939Snsouch	case PPB_REPP_D:
193255939Snsouch		return (r_epp_D(ppc));
193355939Snsouch	case PPB_RECR:
193455939Snsouch		return (r_ecr(ppc));
193555939Snsouch	case PPB_RFIFO:
193655939Snsouch		return (r_fifo(ppc));
193755939Snsouch	case PPB_WDTR:
193855939Snsouch		w_dtr(ppc, byte);
193955939Snsouch		break;
194055939Snsouch	case PPB_WSTR:
194155939Snsouch		w_str(ppc, byte);
194255939Snsouch		break;
194355939Snsouch	case PPB_WCTR:
194455939Snsouch		w_ctr(ppc, byte);
194555939Snsouch		break;
194655939Snsouch	case PPB_WEPP_A:
194755939Snsouch		w_epp_A(ppc, byte);
194855939Snsouch		break;
194955939Snsouch	case PPB_WEPP_D:
195055939Snsouch		w_epp_D(ppc, byte);
195155939Snsouch		break;
195255939Snsouch	case PPB_WECR:
195355939Snsouch		w_ecr(ppc, byte);
195455939Snsouch		break;
195555939Snsouch	case PPB_WFIFO:
195655939Snsouch		w_fifo(ppc, byte);
195755939Snsouch		break;
195855939Snsouch	default:
195987599Sobrien		panic("%s: unknown I/O operation", __func__);
196055939Snsouch		break;
196142475Snsouch	}
196242475Snsouch
196355939Snsouch	return (0);	/* not significative */
196455939Snsouch}
196528221Smsmith
1966118292Sambriskoint
196755939Snsouchppc_read_ivar(device_t bus, device_t dev, int index, uintptr_t *val)
196855939Snsouch{
196955939Snsouch	struct ppc_data *ppc = (struct ppc_data *)device_get_softc(bus);
197055939Snsouch
197155939Snsouch	switch (index) {
197255939Snsouch	case PPC_IVAR_EPP_PROTO:
1973187576Sjhb		PPC_ASSERT_LOCKED(ppc);
197455939Snsouch		*val = (u_long)ppc->ppc_epp;
197555939Snsouch		break;
1976187576Sjhb	case PPC_IVAR_LOCK:
1977187576Sjhb		*val = (uintptr_t)&ppc->ppc_lock;
1978187576Sjhb		break;
197955939Snsouch	default:
198055939Snsouch		return (ENOENT);
198155939Snsouch	}
198255939Snsouch
198355939Snsouch	return (0);
198428221Smsmith}
198555939Snsouch
1986187576Sjhbint
1987187576Sjhbppc_write_ivar(device_t bus, device_t dev, int index, uintptr_t val)
1988187576Sjhb{
1989187576Sjhb	struct ppc_data *ppc = (struct ppc_data *)device_get_softc(bus);
1990187576Sjhb
1991187576Sjhb	switch (index) {
1992187576Sjhb	case PPC_IVAR_INTR_HANDLER:
1993187576Sjhb		PPC_ASSERT_LOCKED(ppc);
1994187576Sjhb		if (dev != ppc->ppbus)
1995187576Sjhb			return (EINVAL);
1996187576Sjhb		if (val == 0) {
1997187576Sjhb			ppc->ppc_intr_hook = NULL;
1998187576Sjhb			break;
1999187576Sjhb		}
2000187576Sjhb		if (ppc->ppc_intr_hook != NULL)
2001187576Sjhb			return (EBUSY);
2002187576Sjhb		ppc->ppc_intr_hook = (void *)val;
2003187576Sjhb		ppc->ppc_intr_arg = device_get_softc(dev);
2004187576Sjhb		break;
2005187576Sjhb	default:
2006187576Sjhb		return (ENOENT);
2007187576Sjhb	}
2008187576Sjhb
2009187576Sjhb	return (0);
2010187576Sjhb}
2011187576Sjhb
201255939Snsouch/*
2013183053Sjhb * We allow child devices to allocate an IRQ resource at rid 0 for their
2014183053Sjhb * interrupt handlers.
201555939Snsouch */
2016183053Sjhbstruct resource *
2017183053Sjhbppc_alloc_resource(device_t bus, device_t child, int type, int *rid,
2018183053Sjhb    u_long start, u_long end, u_long count, u_int flags)
201955939Snsouch{
202055939Snsouch	struct ppc_data *ppc = DEVTOSOFTC(bus);
202155939Snsouch
2022183053Sjhb	switch (type) {
2023183053Sjhb	case SYS_RES_IRQ:
2024183053Sjhb		if (*rid == 0)
2025183053Sjhb			return (ppc->res_irq);
2026183053Sjhb		break;
2027183053Sjhb	}
2028183053Sjhb	return (NULL);
2029183053Sjhb}
203055939Snsouch
2031183053Sjhbint
2032183053Sjhbppc_release_resource(device_t bus, device_t child, int type, int rid,
2033183053Sjhb    struct resource *r)
2034183053Sjhb{
2035183053Sjhb#ifdef INVARIANTS
2036183053Sjhb	struct ppc_data *ppc = DEVTOSOFTC(bus);
2037183053Sjhb#endif
203855939Snsouch
2039183053Sjhb	switch (type) {
2040183053Sjhb	case SYS_RES_IRQ:
2041183053Sjhb		if (rid == 0) {
2042183053Sjhb			KASSERT(r == ppc->res_irq,
2043183053Sjhb			    ("ppc child IRQ resource mismatch"));
2044183053Sjhb			return (0);
2045183053Sjhb		}
2046183053Sjhb		break;
204755939Snsouch	}
2048183053Sjhb	return (EINVAL);
204955939Snsouch}
205055939Snsouch
2051153610SruMODULE_DEPEND(ppc, ppbus, 1, 1, 1);
2052