xref: /freebsd/sys/isa/pnp.c (revision 4249382df0511b51db40ad807c99c41f488778ed)
14249382dSDoug Rabson /*
24249382dSDoug Rabson  * Copyright (c) 1996, Sujal M. Patel
34249382dSDoug Rabson  * All rights reserved.
44249382dSDoug Rabson  *
54249382dSDoug Rabson  * Redistribution and use in source and binary forms, with or without
64249382dSDoug Rabson  * modification, are permitted provided that the following conditions
74249382dSDoug Rabson  * are met:
84249382dSDoug Rabson  * 1. Redistributions of source code must retain the above copyright
94249382dSDoug Rabson  *    notice, this list of conditions and the following disclaimer.
104249382dSDoug Rabson  * 2. Redistributions in binary form must reproduce the above copyright
114249382dSDoug Rabson  *    notice, this list of conditions and the following disclaimer in the
124249382dSDoug Rabson  *    documentation and/or other materials provided with the distribution.
134249382dSDoug Rabson  *
144249382dSDoug Rabson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
154249382dSDoug Rabson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
164249382dSDoug Rabson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
174249382dSDoug Rabson  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
184249382dSDoug Rabson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
194249382dSDoug Rabson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
204249382dSDoug Rabson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
214249382dSDoug Rabson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
224249382dSDoug Rabson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
234249382dSDoug Rabson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
244249382dSDoug Rabson  * SUCH DAMAGE.
254249382dSDoug Rabson  *
264249382dSDoug Rabson  *	$FreeBSD$
274249382dSDoug Rabson  *      from: pnp.c,v 1.11 1999/05/06 22:11:19 peter Exp
284249382dSDoug Rabson  */
294249382dSDoug Rabson 
304249382dSDoug Rabson #include <sys/param.h>
314249382dSDoug Rabson #include <sys/systm.h>
324249382dSDoug Rabson #include <sys/kernel.h>
334249382dSDoug Rabson #include <sys/module.h>
344249382dSDoug Rabson #include <sys/bus.h>
354249382dSDoug Rabson #include <sys/malloc.h>
364249382dSDoug Rabson #include <isa/isavar.h>
374249382dSDoug Rabson #include <isa/pnpreg.h>
384249382dSDoug Rabson #include <isa/pnpvar.h>
394249382dSDoug Rabson #include <machine/resource.h>
404249382dSDoug Rabson #include <machine/clock.h>
414249382dSDoug Rabson 
424249382dSDoug Rabson typedef struct _pnp_id {
434249382dSDoug Rabson 	u_int32_t vendor_id;
444249382dSDoug Rabson 	u_int32_t serial;
454249382dSDoug Rabson 	u_char checksum;
464249382dSDoug Rabson } pnp_id;
474249382dSDoug Rabson 
484249382dSDoug Rabson struct pnp_set_config_arg {
494249382dSDoug Rabson 	int	csn;		/* Card number to configure */
504249382dSDoug Rabson 	int	ldn;		/* Logical device on card */
514249382dSDoug Rabson };
524249382dSDoug Rabson 
534249382dSDoug Rabson struct pnp_quirk {
544249382dSDoug Rabson 	u_int32_t vendor_id;	/* Vendor of the card */
554249382dSDoug Rabson 	u_int32_t logical_id;	/* ID of the device with quirk */
564249382dSDoug Rabson 	int	type;
574249382dSDoug Rabson #define PNP_QUIRK_WRITE_REG	1 /* Need to write a pnp register  */
584249382dSDoug Rabson 	int	arg1;
594249382dSDoug Rabson 	int	arg2;
604249382dSDoug Rabson };
614249382dSDoug Rabson 
624249382dSDoug Rabson struct pnp_quirk pnp_quirks[] = {
634249382dSDoug Rabson 	/*
644249382dSDoug Rabson 	 * The Gravis UltraSound needs register 0xf2 to be set to 0xff
654249382dSDoug Rabson 	 * to enable power.
664249382dSDoug Rabson 	 * XXX need to know the logical device id.
674249382dSDoug Rabson 	 */
684249382dSDoug Rabson 	{ 0x0100561e /* GRV0001 */,	0,
694249382dSDoug Rabson 	  PNP_QUIRK_WRITE_REG,	0xf2,	 0xff },
704249382dSDoug Rabson 
714249382dSDoug Rabson 	{ 0 }
724249382dSDoug Rabson };
734249382dSDoug Rabson 
744249382dSDoug Rabson #if 0
754249382dSDoug Rabson /*
764249382dSDoug Rabson  * these entries are initialized using the autoconfig menu
774249382dSDoug Rabson  * The struct is invalid (and must be initialized) if the first
784249382dSDoug Rabson  * CSN is zero. The init code fills invalid entries with CSN 255
794249382dSDoug Rabson  * which is not a supported value.
804249382dSDoug Rabson  */
814249382dSDoug Rabson 
824249382dSDoug Rabson struct pnp_cinfo pnp_ldn_overrides[MAX_PNP_LDN] = {
834249382dSDoug Rabson     { 0 }
844249382dSDoug Rabson };
854249382dSDoug Rabson #endif
864249382dSDoug Rabson 
874249382dSDoug Rabson /* The READ_DATA port that we are using currently */
884249382dSDoug Rabson static int pnp_rd_port;
894249382dSDoug Rabson 
904249382dSDoug Rabson static void   pnp_send_initiation_key(void);
914249382dSDoug Rabson static int    pnp_get_serial(pnp_id *p);
924249382dSDoug Rabson static int    pnp_isolation_protocol(device_t parent);
934249382dSDoug Rabson 
944249382dSDoug Rabson static void
954249382dSDoug Rabson pnp_write(int d, u_char r)
964249382dSDoug Rabson {
974249382dSDoug Rabson 	outb (_PNP_ADDRESS, d);
984249382dSDoug Rabson 	outb (_PNP_WRITE_DATA, r);
994249382dSDoug Rabson }
1004249382dSDoug Rabson 
1014249382dSDoug Rabson #if 0
1024249382dSDoug Rabson 
1034249382dSDoug Rabson static u_char
1044249382dSDoug Rabson pnp_read(int d)
1054249382dSDoug Rabson {
1064249382dSDoug Rabson 	outb (_PNP_ADDRESS, d);
1074249382dSDoug Rabson 	return (inb(3 | (pnp_rd_port <<2)));
1084249382dSDoug Rabson }
1094249382dSDoug Rabson 
1104249382dSDoug Rabson #endif
1114249382dSDoug Rabson 
1124249382dSDoug Rabson /*
1134249382dSDoug Rabson  * Send Initiation LFSR as described in "Plug and Play ISA Specification",
1144249382dSDoug Rabson  * Intel May 94.
1154249382dSDoug Rabson  */
1164249382dSDoug Rabson static void
1174249382dSDoug Rabson pnp_send_initiation_key()
1184249382dSDoug Rabson {
1194249382dSDoug Rabson 	int cur, i;
1204249382dSDoug Rabson 
1214249382dSDoug Rabson 	/* Reset the LSFR */
1224249382dSDoug Rabson 	outb(_PNP_ADDRESS, 0);
1234249382dSDoug Rabson 	outb(_PNP_ADDRESS, 0); /* yes, we do need it twice! */
1244249382dSDoug Rabson 
1254249382dSDoug Rabson 	cur = 0x6a;
1264249382dSDoug Rabson 	outb(_PNP_ADDRESS, cur);
1274249382dSDoug Rabson 
1284249382dSDoug Rabson 	for (i = 1; i < 32; i++) {
1294249382dSDoug Rabson 		cur = (cur >> 1) | (((cur ^ (cur >> 1)) << 7) & 0xff);
1304249382dSDoug Rabson 		outb(_PNP_ADDRESS, cur);
1314249382dSDoug Rabson 	}
1324249382dSDoug Rabson }
1334249382dSDoug Rabson 
1344249382dSDoug Rabson 
1354249382dSDoug Rabson /*
1364249382dSDoug Rabson  * Get the device's serial number.  Returns 1 if the serial is valid.
1374249382dSDoug Rabson  */
1384249382dSDoug Rabson static int
1394249382dSDoug Rabson pnp_get_serial(pnp_id *p)
1404249382dSDoug Rabson {
1414249382dSDoug Rabson 	int i, bit, valid = 0, sum = 0x6a;
1424249382dSDoug Rabson 	u_char *data = (u_char *)p;
1434249382dSDoug Rabson 
1444249382dSDoug Rabson 	bzero(data, sizeof(char) * 9);
1454249382dSDoug Rabson 	outb(_PNP_ADDRESS, PNP_SERIAL_ISOLATION);
1464249382dSDoug Rabson 	for (i = 0; i < 72; i++) {
1474249382dSDoug Rabson 		bit = inb((pnp_rd_port << 2) | 0x3) == 0x55;
1484249382dSDoug Rabson 		DELAY(250);	/* Delay 250 usec */
1494249382dSDoug Rabson 
1504249382dSDoug Rabson 		/* Can't Short Circuit the next evaluation, so 'and' is last */
1514249382dSDoug Rabson 		bit = (inb((pnp_rd_port << 2) | 0x3) == 0xaa) && bit;
1524249382dSDoug Rabson 		DELAY(250);	/* Delay 250 usec */
1534249382dSDoug Rabson 
1544249382dSDoug Rabson 		valid = valid || bit;
1554249382dSDoug Rabson 
1564249382dSDoug Rabson 		if (i < 64)
1574249382dSDoug Rabson 			sum = (sum >> 1) |
1584249382dSDoug Rabson 				(((sum ^ (sum >> 1) ^ bit) << 7) & 0xff);
1594249382dSDoug Rabson 
1604249382dSDoug Rabson 		data[i / 8] = (data[i / 8] >> 1) | (bit ? 0x80 : 0);
1614249382dSDoug Rabson 	}
1624249382dSDoug Rabson 
1634249382dSDoug Rabson 	valid = valid && (data[8] == sum);
1644249382dSDoug Rabson 
1654249382dSDoug Rabson 	return valid;
1664249382dSDoug Rabson }
1674249382dSDoug Rabson 
1684249382dSDoug Rabson /*
1694249382dSDoug Rabson  * Fill's the buffer with resource info from the device.
1704249382dSDoug Rabson  * Returns 0 if the device fails to report
1714249382dSDoug Rabson  */
1724249382dSDoug Rabson static int
1734249382dSDoug Rabson pnp_get_resource_info(u_char *buffer, int len)
1744249382dSDoug Rabson {
1754249382dSDoug Rabson 	int i, j;
1764249382dSDoug Rabson 	u_char temp;
1774249382dSDoug Rabson 
1784249382dSDoug Rabson 	for (i = 0; i < len; i++) {
1794249382dSDoug Rabson 		outb(_PNP_ADDRESS, PNP_STATUS);
1804249382dSDoug Rabson 		for (j = 0; j < 100; j++) {
1814249382dSDoug Rabson 			if ((inb((pnp_rd_port << 2) | 0x3)) & 0x1)
1824249382dSDoug Rabson 				break;
1834249382dSDoug Rabson 			DELAY(1);
1844249382dSDoug Rabson 		}
1854249382dSDoug Rabson 		if (j == 100) {
1864249382dSDoug Rabson 			printf("PnP device failed to report resource data\n");
1874249382dSDoug Rabson 			return 0;
1884249382dSDoug Rabson 		}
1894249382dSDoug Rabson 		outb(_PNP_ADDRESS, PNP_RESOURCE_DATA);
1904249382dSDoug Rabson 		temp = inb((pnp_rd_port << 2) | 0x3);
1914249382dSDoug Rabson 		if (buffer != NULL)
1924249382dSDoug Rabson 			buffer[i] = temp;
1934249382dSDoug Rabson 	}
1944249382dSDoug Rabson 	return 1;
1954249382dSDoug Rabson }
1964249382dSDoug Rabson 
1974249382dSDoug Rabson #if 0
1984249382dSDoug Rabson /*
1994249382dSDoug Rabson  * write_pnp_parms initializes a logical device with the parms
2004249382dSDoug Rabson  * in d, and then activates the board if the last parameter is 1.
2014249382dSDoug Rabson  */
2024249382dSDoug Rabson 
2034249382dSDoug Rabson static int
2044249382dSDoug Rabson write_pnp_parms(struct pnp_cinfo *d, pnp_id *p, int ldn)
2054249382dSDoug Rabson {
2064249382dSDoug Rabson     int i, empty = -1 ;
2074249382dSDoug Rabson 
2084249382dSDoug Rabson     pnp_write (SET_LDN, ldn );
2094249382dSDoug Rabson     i = pnp_read(SET_LDN) ;
2104249382dSDoug Rabson     if (i != ldn) {
2114249382dSDoug Rabson 	printf("Warning: LDN %d does not exist\n", ldn);
2124249382dSDoug Rabson     }
2134249382dSDoug Rabson     for (i = 0; i < 8; i++) {
2144249382dSDoug Rabson 	pnp_write(IO_CONFIG_BASE + i * 2, d->ic_port[i] >> 8 );
2154249382dSDoug Rabson 	pnp_write(IO_CONFIG_BASE + i * 2 + 1, d->ic_port[i] & 0xff );
2164249382dSDoug Rabson     }
2174249382dSDoug Rabson     for (i = 0; i < 4; i++) {
2184249382dSDoug Rabson 	pnp_write(MEM_CONFIG + i*8, (d->ic_mem[i].base >> 16) & 0xff );
2194249382dSDoug Rabson 	pnp_write(MEM_CONFIG + i*8+1, (d->ic_mem[i].base >> 8) & 0xff );
2204249382dSDoug Rabson 	pnp_write(MEM_CONFIG + i*8+2, d->ic_mem[i].control & 0xff );
2214249382dSDoug Rabson 	pnp_write(MEM_CONFIG + i*8+3, (d->ic_mem[i].range >> 16) & 0xff );
2224249382dSDoug Rabson 	pnp_write(MEM_CONFIG + i*8+4, (d->ic_mem[i].range >> 8) & 0xff );
2234249382dSDoug Rabson     }
2244249382dSDoug Rabson     for (i = 0; i < 2; i++) {
2254249382dSDoug Rabson 	pnp_write(IRQ_CONFIG + i*2    , d->irq[i] );
2264249382dSDoug Rabson 	pnp_write(IRQ_CONFIG + i*2 + 1, d->irq_type[i] );
2274249382dSDoug Rabson 	pnp_write(DRQ_CONFIG + i, d->drq[i] );
2284249382dSDoug Rabson     }
2294249382dSDoug Rabson     /*
2304249382dSDoug Rabson      * store parameters read into the current kernel
2314249382dSDoug Rabson      * so manual editing next time is easier
2324249382dSDoug Rabson      */
2334249382dSDoug Rabson     for (i = 0 ; i < MAX_PNP_LDN; i++) {
2344249382dSDoug Rabson 	if (pnp_ldn_overrides[i].csn == d->csn &&
2354249382dSDoug Rabson 		pnp_ldn_overrides[i].ldn == ldn) {
2364249382dSDoug Rabson 	    d->flags = pnp_ldn_overrides[i].flags ;
2374249382dSDoug Rabson 	    pnp_ldn_overrides[i] = *d ;
2384249382dSDoug Rabson 	    break ;
2394249382dSDoug Rabson 	} else if (pnp_ldn_overrides[i].csn < 1 ||
2404249382dSDoug Rabson 		pnp_ldn_overrides[i].csn == 255)
2414249382dSDoug Rabson 	    empty = i ;
2424249382dSDoug Rabson     }
2434249382dSDoug Rabson     if (i== MAX_PNP_LDN && empty != -1)
2444249382dSDoug Rabson 	pnp_ldn_overrides[empty] = *d;
2454249382dSDoug Rabson 
2464249382dSDoug Rabson     /*
2474249382dSDoug Rabson      * Here should really perform the range check, and
2484249382dSDoug Rabson      * return a failure if not successful.
2494249382dSDoug Rabson      */
2504249382dSDoug Rabson     pnp_write (IO_RANGE_CHECK, 0);
2514249382dSDoug Rabson     DELAY(1000); /* XXX is it really necessary ? */
2524249382dSDoug Rabson     pnp_write (ACTIVATE, d->enable ? 1 : 0);
2534249382dSDoug Rabson     DELAY(1000); /* XXX is it really necessary ? */
2544249382dSDoug Rabson     return 1 ;
2554249382dSDoug Rabson }
2564249382dSDoug Rabson #endif
2574249382dSDoug Rabson 
2584249382dSDoug Rabson /*
2594249382dSDoug Rabson  * This function is called after the bus has assigned resource
2604249382dSDoug Rabson  * locations for a logical device.
2614249382dSDoug Rabson  */
2624249382dSDoug Rabson static void
2634249382dSDoug Rabson pnp_set_config(void *arg, struct isa_config *config, int enable)
2644249382dSDoug Rabson {
2654249382dSDoug Rabson 	int csn = ((struct pnp_set_config_arg *) arg)->csn;
2664249382dSDoug Rabson 	int ldn = ((struct pnp_set_config_arg *) arg)->ldn;
2674249382dSDoug Rabson 	int i;
2684249382dSDoug Rabson 
2694249382dSDoug Rabson 	/*
2704249382dSDoug Rabson 	 * First put all cards into Sleep state with the initiation
2714249382dSDoug Rabson 	 * key, then put our card into Config state.
2724249382dSDoug Rabson 	 */
2734249382dSDoug Rabson 	pnp_send_initiation_key();
2744249382dSDoug Rabson 	pnp_write(PNP_WAKE, csn);
2754249382dSDoug Rabson 
2764249382dSDoug Rabson 	/*
2774249382dSDoug Rabson 	 * Select our logical device so that we can program it.
2784249382dSDoug Rabson 	 */
2794249382dSDoug Rabson 	pnp_write(PNP_SET_LDN, ldn);
2804249382dSDoug Rabson 
2814249382dSDoug Rabson 	/*
2824249382dSDoug Rabson 	 * Now program the resources.
2834249382dSDoug Rabson 	 */
2844249382dSDoug Rabson 	for (i = 0; i < config->ic_nmem; i++) {
2854249382dSDoug Rabson 		u_int32_t start = config->ic_mem[i].ir_start;
2864249382dSDoug Rabson 		u_int32_t size =  config->ic_mem[i].ir_size;
2874249382dSDoug Rabson 		if (start & 0xff)
2884249382dSDoug Rabson 			panic("pnp_set_config: bogus memory assignment");
2894249382dSDoug Rabson 		pnp_write(PNP_MEM_BASE_HIGH(i), (start >> 16) & 0xff);
2904249382dSDoug Rabson 		pnp_write(PNP_MEM_BASE_LOW(i), (start >> 8) & 0xff);
2914249382dSDoug Rabson 		pnp_write(PNP_MEM_RANGE_HIGH(i), (size >> 16) & 0xff);
2924249382dSDoug Rabson 		pnp_write(PNP_MEM_RANGE_LOW(i), (size >> 8) & 0xff);
2934249382dSDoug Rabson 	}
2944249382dSDoug Rabson 	for (; i < ISA_NMEM; i++) {
2954249382dSDoug Rabson 		pnp_write(PNP_MEM_BASE_HIGH(i), 0);
2964249382dSDoug Rabson 		pnp_write(PNP_MEM_BASE_LOW(i), 0);
2974249382dSDoug Rabson 		pnp_write(PNP_MEM_RANGE_HIGH(i), 0);
2984249382dSDoug Rabson 		pnp_write(PNP_MEM_RANGE_LOW(i), 0);
2994249382dSDoug Rabson 	}
3004249382dSDoug Rabson 
3014249382dSDoug Rabson 	for (i = 0; i < config->ic_nport; i++) {
3024249382dSDoug Rabson 		u_int32_t start = config->ic_port[i].ir_start;
3034249382dSDoug Rabson 		pnp_write(PNP_IO_BASE_HIGH(i), (start >> 8) & 0xff);
3044249382dSDoug Rabson 		pnp_write(PNP_IO_BASE_LOW(i), (start >> 0) & 0xff);
3054249382dSDoug Rabson 	}
3064249382dSDoug Rabson 	for (; i < ISA_NPORT; i++) {
3074249382dSDoug Rabson 		pnp_write(PNP_IO_BASE_HIGH(i), 0);
3084249382dSDoug Rabson 		pnp_write(PNP_IO_BASE_LOW(i), 0);
3094249382dSDoug Rabson 	}
3104249382dSDoug Rabson 
3114249382dSDoug Rabson 	for (i = 0; i < config->ic_nirq; i++) {
3124249382dSDoug Rabson 		int irq = ffs(config->ic_irqmask[i]) - 1;
3134249382dSDoug Rabson 		pnp_write(PNP_IRQ_LEVEL(i), irq);
3144249382dSDoug Rabson 		pnp_write(PNP_IRQ_TYPE(i), 2); /* XXX */
3154249382dSDoug Rabson 	}
3164249382dSDoug Rabson 	for (; i < ISA_NIRQ; i++) {
3174249382dSDoug Rabson 		/*
3184249382dSDoug Rabson 		 * IRQ 0 is not a valid interrupt selection and
3194249382dSDoug Rabson 		 * represents no interrupt selection.
3204249382dSDoug Rabson 		 */
3214249382dSDoug Rabson 		pnp_write(PNP_IRQ_LEVEL(i), 0);
3224249382dSDoug Rabson 	}
3234249382dSDoug Rabson 
3244249382dSDoug Rabson 	for (i = 0; i < config->ic_ndrq; i++) {
3254249382dSDoug Rabson 		int drq = ffs(config->ic_drqmask[i]) - 1;
3264249382dSDoug Rabson 		pnp_write(PNP_DMA_CHANNEL(i), drq);
3274249382dSDoug Rabson 	}
3284249382dSDoug Rabson 	for (; i < ISA_NDRQ; i++) {
3294249382dSDoug Rabson 		/*
3304249382dSDoug Rabson 		 * DMA channel 4, the cascade channel is used to
3314249382dSDoug Rabson 		 * indicate no DMA channel is active.
3324249382dSDoug Rabson 		 */
3334249382dSDoug Rabson 		pnp_write(PNP_DMA_CHANNEL(i), 4);
3344249382dSDoug Rabson 	}
3354249382dSDoug Rabson 
3364249382dSDoug Rabson 	pnp_write(PNP_ACTIVATE, enable ? 1 : 0);
3374249382dSDoug Rabson 
3384249382dSDoug Rabson 	/*
3394249382dSDoug Rabson 	 * Wake everyone up again, we are finished.
3404249382dSDoug Rabson 	 */
3414249382dSDoug Rabson 	pnp_write(PNP_CONFIG_CONTROL, PNP_CONFIG_CONTROL_WAIT_FOR_KEY);
3424249382dSDoug Rabson }
3434249382dSDoug Rabson 
3444249382dSDoug Rabson /*
3454249382dSDoug Rabson  * Process quirks for a logical device.. The card must be in Config state.
3464249382dSDoug Rabson  */
3474249382dSDoug Rabson static void
3484249382dSDoug Rabson pnp_check_quirks(u_int32_t vendor_id, u_int32_t logical_id, int ldn)
3494249382dSDoug Rabson {
3504249382dSDoug Rabson 	struct pnp_quirk *qp;
3514249382dSDoug Rabson 
3524249382dSDoug Rabson 	for (qp = &pnp_quirks[0]; qp->vendor_id; qp++) {
3534249382dSDoug Rabson 		if (qp->vendor_id == vendor_id
3544249382dSDoug Rabson 		    && (qp->logical_id == 0
3554249382dSDoug Rabson 			|| qp->logical_id == logical_id)) {
3564249382dSDoug Rabson 			switch (qp->type) {
3574249382dSDoug Rabson 			case PNP_QUIRK_WRITE_REG:
3584249382dSDoug Rabson 				pnp_write(PNP_SET_LDN, ldn);
3594249382dSDoug Rabson 				pnp_write(qp->arg1, qp->arg2);
3604249382dSDoug Rabson 				break;
3614249382dSDoug Rabson 			}
3624249382dSDoug Rabson 		}
3634249382dSDoug Rabson 	}
3644249382dSDoug Rabson }
3654249382dSDoug Rabson 
3664249382dSDoug Rabson /*
3674249382dSDoug Rabson  * Scan Resource Data for Logical Devices.
3684249382dSDoug Rabson  *
3694249382dSDoug Rabson  * This function exits as soon as it gets an error reading *ANY*
3704249382dSDoug Rabson  * Resource Data or ir reaches the end of Resource Data.  In the first
3714249382dSDoug Rabson  * case the return value will be TRUE, FALSE otherwise.
3724249382dSDoug Rabson  */
3734249382dSDoug Rabson static int
3744249382dSDoug Rabson pnp_scan_resdata(device_t parent, pnp_id *p, int csn)
3754249382dSDoug Rabson {
3764249382dSDoug Rabson 	u_char tag, resinfo[16];
3774249382dSDoug Rabson 	int large_len, scanning = 1024, retval = FALSE;
3784249382dSDoug Rabson 	u_int32_t logical_id;
3794249382dSDoug Rabson 	u_int32_t compat_id;
3804249382dSDoug Rabson 	device_t dev = 0;
3814249382dSDoug Rabson 	int ldn = 0;
3824249382dSDoug Rabson 	struct isa_config card, logdev, alt;
3834249382dSDoug Rabson 	struct isa_config *config;
3844249382dSDoug Rabson 	struct pnp_set_config_arg *csnldn;
3854249382dSDoug Rabson 	int priority = 0;
3864249382dSDoug Rabson 	char *desc = 0;
3874249382dSDoug Rabson 
3884249382dSDoug Rabson 	bzero(&card, sizeof card);
3894249382dSDoug Rabson 	bzero(&logdev, sizeof logdev);
3904249382dSDoug Rabson 	bzero(&alt, sizeof alt);
3914249382dSDoug Rabson 	config = &card;
3924249382dSDoug Rabson 	while (scanning-- > 0 && pnp_get_resource_info(&tag, 1)) {
3934249382dSDoug Rabson 		if (PNP_RES_TYPE(tag) == 0) {
3944249382dSDoug Rabson 			/* Small resource */
3954249382dSDoug Rabson 			if (pnp_get_resource_info(resinfo,
3964249382dSDoug Rabson 						  PNP_SRES_LEN(tag)) == 0) {
3974249382dSDoug Rabson 				scanning = 0;
3984249382dSDoug Rabson 				continue;
3994249382dSDoug Rabson 			}
4004249382dSDoug Rabson 
4014249382dSDoug Rabson 			switch (PNP_SRES_NUM(tag)) {
4024249382dSDoug Rabson 			case PNP_TAG_LOGIGAL_DEVICE:
4034249382dSDoug Rabson 				/*
4044249382dSDoug Rabson 				 * A new logical device. Scan
4054249382dSDoug Rabson 				 * resourcea and add device.
4064249382dSDoug Rabson 				 */
4074249382dSDoug Rabson 				bcopy(resinfo, &logical_id, 4);
4084249382dSDoug Rabson 				pnp_check_quirks(p->vendor_id,
4094249382dSDoug Rabson 						 logical_id,
4104249382dSDoug Rabson 						 ldn);
4114249382dSDoug Rabson 				compat_id = 0;
4124249382dSDoug Rabson 				logdev = card;
4134249382dSDoug Rabson 				config = &logdev;
4144249382dSDoug Rabson 				dev = BUS_ADD_CHILD(parent,
4154249382dSDoug Rabson 						    ISA_ORDER_PNP, NULL, -1);
4164249382dSDoug Rabson 				if (desc)
4174249382dSDoug Rabson 					device_set_desc_copy(dev, desc);
4184249382dSDoug Rabson 				isa_set_vendorid(dev, p->vendor_id);
4194249382dSDoug Rabson 				isa_set_serial(dev, p->serial);
4204249382dSDoug Rabson 				isa_set_logicalid(dev, logical_id);
4214249382dSDoug Rabson 				csnldn = malloc(sizeof *csnldn,
4224249382dSDoug Rabson 						M_DEVBUF, M_NOWAIT);
4234249382dSDoug Rabson 				if (!csnldn) {
4244249382dSDoug Rabson 					device_printf(parent,
4254249382dSDoug Rabson 						      "out of memory\n");
4264249382dSDoug Rabson 					scanning = 0;
4274249382dSDoug Rabson 					break;
4284249382dSDoug Rabson 				}
4294249382dSDoug Rabson 				csnldn->csn = csn;
4304249382dSDoug Rabson 				csnldn->ldn = ldn;
4314249382dSDoug Rabson 				ISA_SET_CONFIG_CALLBACK(parent, dev,
4324249382dSDoug Rabson 							pnp_set_config,
4334249382dSDoug Rabson 							csnldn);
4344249382dSDoug Rabson 				ldn++;
4354249382dSDoug Rabson 				break;
4364249382dSDoug Rabson 
4374249382dSDoug Rabson 			case PNP_TAG_COMPAT_DEVICE:
4384249382dSDoug Rabson 				/*
4394249382dSDoug Rabson 				 * Got a compatible device id
4404249382dSDoug Rabson 				 * resource. Should keep a list of
4414249382dSDoug Rabson 				 * compat ids in the device.
4424249382dSDoug Rabson 				 */
4434249382dSDoug Rabson 				bcopy(resinfo, &compat_id, 4);
4444249382dSDoug Rabson 				if (dev)
4454249382dSDoug Rabson 					isa_set_compatid(dev, compat_id);
4464249382dSDoug Rabson 				break;
4474249382dSDoug Rabson 
4484249382dSDoug Rabson 			case PNP_TAG_IRQ_FORMAT:
4494249382dSDoug Rabson 				if (config->ic_nirq == ISA_NIRQ) {
4504249382dSDoug Rabson 					device_printf(parent,
4514249382dSDoug Rabson 						      "CSN %d too many irqs",
4524249382dSDoug Rabson 						      csn);
4534249382dSDoug Rabson 					scanning = 0;
4544249382dSDoug Rabson 					break;
4554249382dSDoug Rabson 				}
4564249382dSDoug Rabson 				config->ic_irqmask[config->ic_nirq] =
4574249382dSDoug Rabson 					resinfo[0] + (resinfo[1]<<8);
4584249382dSDoug Rabson 				config->ic_nirq++;
4594249382dSDoug Rabson 				break;
4604249382dSDoug Rabson 
4614249382dSDoug Rabson 			case PNP_TAG_DMA_FORMAT:
4624249382dSDoug Rabson 				if (config->ic_ndrq == ISA_NDRQ) {
4634249382dSDoug Rabson 					device_printf(parent,
4644249382dSDoug Rabson 						      "CSN %d too many drqs",
4654249382dSDoug Rabson 						      csn);
4664249382dSDoug Rabson 					scanning = 0;
4674249382dSDoug Rabson 					break;
4684249382dSDoug Rabson 				}
4694249382dSDoug Rabson 				config->ic_drqmask[config->ic_ndrq] =
4704249382dSDoug Rabson 					resinfo[0];
4714249382dSDoug Rabson 				config->ic_ndrq++;
4724249382dSDoug Rabson 				break;
4734249382dSDoug Rabson 
4744249382dSDoug Rabson 			case PNP_TAG_START_DEPENDANT:
4754249382dSDoug Rabson 				if (config == &alt) {
4764249382dSDoug Rabson 					ISA_ADD_CONFIG(parent, dev,
4774249382dSDoug Rabson 						       priority, config);
4784249382dSDoug Rabson 				} else if (config != &logdev) {
4794249382dSDoug Rabson 					device_printf(parent,
4804249382dSDoug Rabson 						      "CSN %d malformed\n",
4814249382dSDoug Rabson 						      csn);
4824249382dSDoug Rabson 					scanning = 0;
4834249382dSDoug Rabson 					break;
4844249382dSDoug Rabson 				}
4854249382dSDoug Rabson 				/*
4864249382dSDoug Rabson 				 * If the priority is not specified,
4874249382dSDoug Rabson 				 * then use the default of
4884249382dSDoug Rabson 				 * 'acceptable'
4894249382dSDoug Rabson 				 */
4904249382dSDoug Rabson 				if (PNP_SRES_LEN(tag) > 0)
4914249382dSDoug Rabson 					priority = resinfo[0];
4924249382dSDoug Rabson 				else
4934249382dSDoug Rabson 					priority = 1;
4944249382dSDoug Rabson 				alt = logdev;
4954249382dSDoug Rabson 				config = &alt;
4964249382dSDoug Rabson 				break;
4974249382dSDoug Rabson 
4984249382dSDoug Rabson 			case PNP_TAG_END_DEPENDANT:
4994249382dSDoug Rabson 				ISA_ADD_CONFIG(parent, dev, priority, config);
5004249382dSDoug Rabson 				config = &logdev;
5014249382dSDoug Rabson 				break;
5024249382dSDoug Rabson 
5034249382dSDoug Rabson 			case PNP_TAG_IO_RANGE:
5044249382dSDoug Rabson 				if (config->ic_nport == ISA_NPORT) {
5054249382dSDoug Rabson 					device_printf(parent,
5064249382dSDoug Rabson 						      "CSN %d too many ports",
5074249382dSDoug Rabson 						      csn);
5084249382dSDoug Rabson 					scanning = 0;
5094249382dSDoug Rabson 					break;
5104249382dSDoug Rabson 				}
5114249382dSDoug Rabson 				config->ic_port[config->ic_nport].ir_start =
5124249382dSDoug Rabson 					resinfo[1] + (resinfo[2]<<8);
5134249382dSDoug Rabson 				config->ic_port[config->ic_nport].ir_end =
5144249382dSDoug Rabson 					resinfo[3] + (resinfo[4]<<8)
5154249382dSDoug Rabson 					+ resinfo[6] - 1;
5164249382dSDoug Rabson 				config->ic_port[config->ic_nport].ir_size
5174249382dSDoug Rabson 					=
5184249382dSDoug Rabson 					resinfo[6];
5194249382dSDoug Rabson 				config->ic_port[config->ic_nport].ir_align =
5204249382dSDoug Rabson 					resinfo[5];
5214249382dSDoug Rabson 				config->ic_nport++;
5224249382dSDoug Rabson 				break;
5234249382dSDoug Rabson 
5244249382dSDoug Rabson 			case PNP_TAG_IO_FIXED:
5254249382dSDoug Rabson 				if (config->ic_nport == ISA_NPORT) {
5264249382dSDoug Rabson 					device_printf(parent,
5274249382dSDoug Rabson 						      "CSN %d too many ports",
5284249382dSDoug Rabson 						      csn);
5294249382dSDoug Rabson 					scanning = 0;
5304249382dSDoug Rabson 					break;
5314249382dSDoug Rabson 				}
5324249382dSDoug Rabson 				config->ic_port[config->ic_nport].ir_start =
5334249382dSDoug Rabson 					resinfo[0] + (resinfo[1]<<8);
5344249382dSDoug Rabson 				config->ic_port[config->ic_nport].ir_end =
5354249382dSDoug Rabson 					resinfo[0] + (resinfo[1]<<8)
5364249382dSDoug Rabson 					+ resinfo[2] - 1;
5374249382dSDoug Rabson 				config->ic_port[config->ic_nport].ir_size
5384249382dSDoug Rabson 					= resinfo[2];
5394249382dSDoug Rabson 				config->ic_port[config->ic_nport].ir_align = 1;
5404249382dSDoug Rabson 				config->ic_nport++;
5414249382dSDoug Rabson 				break;
5424249382dSDoug Rabson 
5434249382dSDoug Rabson 			case PNP_TAG_END:
5444249382dSDoug Rabson 				scanning = 0;
5454249382dSDoug Rabson 				break;
5464249382dSDoug Rabson 
5474249382dSDoug Rabson 			default:
5484249382dSDoug Rabson 				/* Skip this resource */
5494249382dSDoug Rabson 				break;
5504249382dSDoug Rabson 			}
5514249382dSDoug Rabson 		} else {
5524249382dSDoug Rabson 			/* Large resource */
5534249382dSDoug Rabson 			if (pnp_get_resource_info(resinfo, 2) == 0) {
5544249382dSDoug Rabson 				scanning = 0;
5554249382dSDoug Rabson 				continue;
5564249382dSDoug Rabson 			}
5574249382dSDoug Rabson 			large_len = resinfo[0] + (resinfo[1] << 8);
5584249382dSDoug Rabson 
5594249382dSDoug Rabson 			if (PNP_LRES_NUM(tag) == PNP_TAG_ID_ANSI) {
5604249382dSDoug Rabson 				if (desc)
5614249382dSDoug Rabson 					free(desc, M_TEMP);
5624249382dSDoug Rabson 				desc = malloc(large_len + 1,
5634249382dSDoug Rabson 					      M_TEMP, M_NOWAIT);
5644249382dSDoug Rabson 				/*
5654249382dSDoug Rabson 				 * Note: if malloc fails, this will
5664249382dSDoug Rabson 				 * skip the resource instead of
5674249382dSDoug Rabson 				 * reading it into desc.
5684249382dSDoug Rabson 				 */
5694249382dSDoug Rabson 				if (pnp_get_resource_info(desc,
5704249382dSDoug Rabson 							  large_len) == 0) {
5714249382dSDoug Rabson 					scanning = 0;
5724249382dSDoug Rabson 				}
5734249382dSDoug Rabson 				if (desc) {
5744249382dSDoug Rabson 					/*
5754249382dSDoug Rabson 					 * Trim trailing spaces.
5764249382dSDoug Rabson 					 */
5774249382dSDoug Rabson 					while (desc[large_len-1] == ' ')
5784249382dSDoug Rabson 						large_len--;
5794249382dSDoug Rabson 					desc[large_len] = '\0';
5804249382dSDoug Rabson 					if (dev)
5814249382dSDoug Rabson 						device_set_desc_copy
5824249382dSDoug Rabson 							(dev, desc);
5834249382dSDoug Rabson 				}
5844249382dSDoug Rabson 				continue;
5854249382dSDoug Rabson 			}
5864249382dSDoug Rabson 
5874249382dSDoug Rabson 			if (PNP_LRES_NUM(tag) != PNP_TAG_MEMORY_RANGE) {
5884249382dSDoug Rabson 				/* skip */
5894249382dSDoug Rabson 				if (pnp_get_resource_info(NULL,
5904249382dSDoug Rabson 							  large_len) == 0) {
5914249382dSDoug Rabson 					scanning = 0;
5924249382dSDoug Rabson 				}
5934249382dSDoug Rabson 				continue;
5944249382dSDoug Rabson 			}
5954249382dSDoug Rabson 
5964249382dSDoug Rabson 			if (pnp_get_resource_info(resinfo, large_len) == 0) {
5974249382dSDoug Rabson 				scanning = 0;
5984249382dSDoug Rabson 				continue;
5994249382dSDoug Rabson 			}
6004249382dSDoug Rabson 
6014249382dSDoug Rabson 			if (config->ic_nmem == ISA_NMEM) {
6024249382dSDoug Rabson 				device_printf(parent,
6034249382dSDoug Rabson 					      "CSN %d too many memory ranges",
6044249382dSDoug Rabson 					      csn);
6054249382dSDoug Rabson 				scanning = 0;
6064249382dSDoug Rabson 				break;
6074249382dSDoug Rabson 			}
6084249382dSDoug Rabson 
6094249382dSDoug Rabson 			config->ic_mem[config->ic_nmem].ir_start =
6104249382dSDoug Rabson 				(resinfo[4]<<8) + (resinfo[5]<<16);
6114249382dSDoug Rabson 			config->ic_mem[config->ic_nmem].ir_end =
6124249382dSDoug Rabson 				(resinfo[6]<<8) + (resinfo[7]<<16);
6134249382dSDoug Rabson 			config->ic_mem[config->ic_nmem].ir_size =
6144249382dSDoug Rabson 				(resinfo[10]<<8) + (resinfo[11]<<16);
6154249382dSDoug Rabson 			config->ic_mem[config->ic_nmem].ir_align =
6164249382dSDoug Rabson 				resinfo[8] + (resinfo[9]<<8);
6174249382dSDoug Rabson 			if (!config->ic_mem[config->ic_nmem].ir_align)
6184249382dSDoug Rabson 				config->ic_mem[config->ic_nmem].ir_align =
6194249382dSDoug Rabson 					0x10000;
6204249382dSDoug Rabson 			config->ic_nmem++;
6214249382dSDoug Rabson 		}
6224249382dSDoug Rabson 	}
6234249382dSDoug Rabson 
6244249382dSDoug Rabson 	if (desc)
6254249382dSDoug Rabson 		free(desc, M_TEMP);
6264249382dSDoug Rabson 
6274249382dSDoug Rabson 	return retval;
6284249382dSDoug Rabson }
6294249382dSDoug Rabson 
6304249382dSDoug Rabson /*
6314249382dSDoug Rabson  * Run the isolation protocol. Use pnp_rd_port as the READ_DATA port
6324249382dSDoug Rabson  * value (caller should try multiple READ_DATA locations before giving
6334249382dSDoug Rabson  * up). Upon exiting, all cards are aware that they should use
6344249382dSDoug Rabson  * pnp_rd_port as the READ_DATA port.
6354249382dSDoug Rabson  *
6364249382dSDoug Rabson  * In the first pass, a csn is assigned to each board and pnp_id's
6374249382dSDoug Rabson  * are saved to an array, pnp_devices. In the second pass, each
6384249382dSDoug Rabson  * card is woken up and the device configuration is called.
6394249382dSDoug Rabson  */
6404249382dSDoug Rabson static int
6414249382dSDoug Rabson pnp_isolation_protocol(device_t parent)
6424249382dSDoug Rabson {
6434249382dSDoug Rabson 	int csn;
6444249382dSDoug Rabson 	pnp_id id;
6454249382dSDoug Rabson 	int found = 0;
6464249382dSDoug Rabson 
6474249382dSDoug Rabson 	/*
6484249382dSDoug Rabson 	 * Put all cards into the Sleep state so that we can clear
6494249382dSDoug Rabson 	 * their CSNs.
6504249382dSDoug Rabson 	 */
6514249382dSDoug Rabson 	pnp_send_initiation_key();
6524249382dSDoug Rabson 
6534249382dSDoug Rabson 	/*
6544249382dSDoug Rabson 	 * Clear the CSN for all cards.
6554249382dSDoug Rabson 	 */
6564249382dSDoug Rabson 	pnp_write(PNP_CONFIG_CONTROL, PNP_CONFIG_CONTROL_RESET_CSN);
6574249382dSDoug Rabson 
6584249382dSDoug Rabson 	/*
6594249382dSDoug Rabson 	 * Move all cards to the Isolation state.
6604249382dSDoug Rabson 	 */
6614249382dSDoug Rabson 	pnp_write(PNP_WAKE, 0);
6624249382dSDoug Rabson 
6634249382dSDoug Rabson 	/*
6644249382dSDoug Rabson 	 * Tell them where the read point is going to be this time.
6654249382dSDoug Rabson 	 */
6664249382dSDoug Rabson 	pnp_write(PNP_SET_RD_DATA, pnp_rd_port);
6674249382dSDoug Rabson 
6684249382dSDoug Rabson 	for (csn = 1; csn < PNP_MAX_CARDS; csn++) {
6694249382dSDoug Rabson 		/*
6704249382dSDoug Rabson 		 * Start the serial isolation protocol.
6714249382dSDoug Rabson 		 */
6724249382dSDoug Rabson 		outb(_PNP_ADDRESS, PNP_SERIAL_ISOLATION);
6734249382dSDoug Rabson 		DELAY(1000);	/* Delay 1 msec */
6744249382dSDoug Rabson 
6754249382dSDoug Rabson 		if (pnp_get_serial(&id)) {
6764249382dSDoug Rabson 			/*
6774249382dSDoug Rabson 			 * We have read the id from a card
6784249382dSDoug Rabson 			 * successfully. The card which won the
6794249382dSDoug Rabson 			 * isolation protocol will be in Isolation
6804249382dSDoug Rabson 			 * mode and all others will be in Sleep.  *
6814249382dSDoug Rabson 			 * Program the CSN of the isolated card
6824249382dSDoug Rabson 			 * (taking it to Config state) and read its
6834249382dSDoug Rabson 			 * resources, creating devices as we find
6844249382dSDoug Rabson 			 * logical devices on the card.
6854249382dSDoug Rabson 			 */
6864249382dSDoug Rabson 			pnp_write(PNP_SET_CSN, csn);
6874249382dSDoug Rabson 			pnp_scan_resdata(parent, &id, csn);
6884249382dSDoug Rabson 			found++;
6894249382dSDoug Rabson 		} else
6904249382dSDoug Rabson 			break;
6914249382dSDoug Rabson 
6924249382dSDoug Rabson 		/*
6934249382dSDoug Rabson 		 * Put this card back to the Sleep state and
6944249382dSDoug Rabson 		 * simultaneously move all cards which don't have a
6954249382dSDoug Rabson 		 * CSN yet to Isolation state.
6964249382dSDoug Rabson 		 */
6974249382dSDoug Rabson 		pnp_write(PNP_WAKE, 0);
6984249382dSDoug Rabson 	}
6994249382dSDoug Rabson 
7004249382dSDoug Rabson 	/*
7014249382dSDoug Rabson 	 * Unless we have chosen the wrong read port, all cards will
7024249382dSDoug Rabson 	 * be in Sleep state. Put them back into WaitForKey for
7034249382dSDoug Rabson 	 * now. Their resources will be programmed later.
7044249382dSDoug Rabson 	 */
7054249382dSDoug Rabson 	pnp_write(PNP_CONFIG_CONTROL, PNP_CONFIG_CONTROL_WAIT_FOR_KEY);
7064249382dSDoug Rabson 
7074249382dSDoug Rabson 	return found;
7084249382dSDoug Rabson }
7094249382dSDoug Rabson 
7104249382dSDoug Rabson 
7114249382dSDoug Rabson /*
7124249382dSDoug Rabson  * pnp_identify()
7134249382dSDoug Rabson  *
7144249382dSDoug Rabson  * autoconfiguration of pnp devices. This routine just runs the
7154249382dSDoug Rabson  * isolation protocol over several ports, until one is successful.
7164249382dSDoug Rabson  *
7174249382dSDoug Rabson  * may be called more than once ?
7184249382dSDoug Rabson  *
7194249382dSDoug Rabson  */
7204249382dSDoug Rabson 
7214249382dSDoug Rabson static void
7224249382dSDoug Rabson pnp_identify(driver_t *driver, device_t parent)
7234249382dSDoug Rabson {
7244249382dSDoug Rabson 	int num_pnp_devs;
7254249382dSDoug Rabson 
7264249382dSDoug Rabson #if 0
7274249382dSDoug Rabson 	if (pnp_ldn_overrides[0].csn == 0) {
7284249382dSDoug Rabson 		if (bootverbose)
7294249382dSDoug Rabson 			printf("Initializing PnP override table\n");
7304249382dSDoug Rabson 		bzero (pnp_ldn_overrides, sizeof(pnp_ldn_overrides));
7314249382dSDoug Rabson 		pnp_ldn_overrides[0].csn = 255 ;
7324249382dSDoug Rabson 	}
7334249382dSDoug Rabson #endif
7344249382dSDoug Rabson 
7354249382dSDoug Rabson 	/* Try various READ_DATA ports from 0x203-0x3ff */
7364249382dSDoug Rabson 	for (pnp_rd_port = 0x80; (pnp_rd_port < 0xff); pnp_rd_port += 0x10) {
7374249382dSDoug Rabson 		if (bootverbose)
7384249382dSDoug Rabson 			printf("Trying Read_Port at %x\n", (pnp_rd_port << 2) | 0x3);
7394249382dSDoug Rabson 
7404249382dSDoug Rabson 		num_pnp_devs = pnp_isolation_protocol(parent);
7414249382dSDoug Rabson 		if (num_pnp_devs)
7424249382dSDoug Rabson 			break;
7434249382dSDoug Rabson 	}
7444249382dSDoug Rabson }
7454249382dSDoug Rabson 
7464249382dSDoug Rabson static device_method_t pnp_methods[] = {
7474249382dSDoug Rabson 	/* Device interface */
7484249382dSDoug Rabson 	DEVMETHOD(device_identify,	pnp_identify),
7494249382dSDoug Rabson 
7504249382dSDoug Rabson 	{ 0, 0 }
7514249382dSDoug Rabson };
7524249382dSDoug Rabson 
7534249382dSDoug Rabson static driver_t pnp_driver = {
7544249382dSDoug Rabson 	"pnp",
7554249382dSDoug Rabson 	pnp_methods,
7564249382dSDoug Rabson 	1,			/* no softc */
7574249382dSDoug Rabson };
7584249382dSDoug Rabson 
7594249382dSDoug Rabson static devclass_t pnp_devclass;
7604249382dSDoug Rabson 
7614249382dSDoug Rabson DRIVER_MODULE(pnp, isa, pnp_driver, pnp_devclass, 0, 0);
762