pnp.c revision 65176
197952Sdougb/*
297952Sdougb * Copyright (c) 1996, Sujal M. Patel
397952Sdougb * All rights reserved.
497952Sdougb *
597952Sdougb * Redistribution and use in source and binary forms, with or without
697952Sdougb * modification, are permitted provided that the following conditions
797952Sdougb * are met:
897952Sdougb * 1. Redistributions of source code must retain the above copyright
997952Sdougb *    notice, this list of conditions and the following disclaimer.
1097952Sdougb * 2. Redistributions in binary form must reproduce the above copyright
1197952Sdougb *    notice, this list of conditions and the following disclaimer in the
1297952Sdougb *    documentation and/or other materials provided with the distribution.
1397952Sdougb *
1497952Sdougb * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1597952Sdougb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1697952Sdougb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1797952Sdougb * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1897952Sdougb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1997952Sdougb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2097952Sdougb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2197952Sdougb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2297952Sdougb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2397952Sdougb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2497952Sdougb * SUCH DAMAGE.
2597952Sdougb *
2697952Sdougb *	$FreeBSD: head/sys/isa/pnp.c 65176 2000-08-28 21:48:13Z dfr $
2797952Sdougb *      from: pnp.c,v 1.11 1999/05/06 22:11:19 peter Exp
2897952Sdougb */
2997952Sdougb
30114924Sdougb#include <sys/param.h>
31101773Sdougb#include <sys/systm.h>
3297952Sdougb#include <sys/kernel.h>
33218535Sdougb#include <sys/module.h>
34216343Sdougb#include <sys/bus.h>
3597952Sdougb#include <sys/malloc.h>
36101773Sdougb#include <isa/isavar.h>
3797952Sdougb#include <isa/pnpreg.h>
38101773Sdougb#include <isa/pnpvar.h>
39101773Sdougb#include <machine/clock.h>
40120836Sdougb#include <machine/bus.h>
41120836Sdougb
42120836Sdougbtypedef struct _pnp_id {
43120836Sdougb	u_int32_t vendor_id;
44120836Sdougb	u_int32_t serial;
45120836Sdougb	u_char checksum;
46120836Sdougb} pnp_id;
47120836Sdougb
48120836Sdougbstruct pnp_set_config_arg {
49120836Sdougb	int	csn;		/* Card number to configure */
50188498Sed	int	ldn;		/* Logical device on card */
51101897Sdougb};
5297952Sdougb
53101773Sdougbstruct pnp_quirk {
54101773Sdougb	u_int32_t vendor_id;	/* Vendor of the card */
5597952Sdougb	u_int32_t logical_id;	/* ID of the device with quirk */
56216196Sdougb	int	type;
57101773Sdougb#define PNP_QUIRK_WRITE_REG	1 /* Need to write a pnp register  */
58101897Sdougb#define PNP_QUIRK_EXTRA_IO	2 /* Has extra io ports  */
59188498Sed	int	arg1;
60101773Sdougb	int	arg2;
6197952Sdougb};
6297952Sdougb
63101773Sdougbstruct pnp_quirk pnp_quirks[] = {
64101773Sdougb	/*
65101773Sdougb	 * The Gravis UltraSound needs register 0xf2 to be set to 0xff
6697952Sdougb	 * to enable power.
67120836Sdougb	 * XXX need to know the logical device id.
68120836Sdougb	 */
69120836Sdougb	{ 0x0100561e /* GRV0001 */,	0,
70120836Sdougb	  PNP_QUIRK_WRITE_REG,	0xf2,	 0xff },
71120836Sdougb	/*
72120836Sdougb	 * An emu8000 does not give us other than the first
73120836Sdougb	 * port.
74120836Sdougb	 */
75120836Sdougb	{ 0x26008c0e /* SB16 */,	0x21008c0e,
76120836Sdougb	  PNP_QUIRK_EXTRA_IO,	0x400,	 0x800 },
77120836Sdougb	{ 0x42008c0e /* SB32(CTL0042) */,	0x21008c0e,
78120836Sdougb	  PNP_QUIRK_EXTRA_IO,	0x400,	 0x800 },
79120836Sdougb	{ 0x44008c0e /* SB32(CTL0044) */,	0x21008c0e,
80120836Sdougb	  PNP_QUIRK_EXTRA_IO,	0x400,	 0x800 },
81120836Sdougb	{ 0x49008c0e /* SB32(CTL0049) */,	0x21008c0e,
82120836Sdougb	  PNP_QUIRK_EXTRA_IO,	0x400,	 0x800 },
83120836Sdougb	{ 0xf1008c0e /* SB32(CTL00f1) */,	0x21008c0e,
84120836Sdougb	  PNP_QUIRK_EXTRA_IO,	0x400,	 0x800 },
85120836Sdougb	{ 0xc1008c0e /* SB64(CTL00c1) */,	0x22008c0e,
86120836Sdougb	  PNP_QUIRK_EXTRA_IO,	0x400,	 0x800 },
87120836Sdougb	{ 0xe4008c0e /* SB64(CTL00e4) */,	0x22008c0e,
88120836Sdougb	  PNP_QUIRK_EXTRA_IO,	0x400,	 0x800 },
89120836Sdougb
90120836Sdougb	{ 0 }
91120836Sdougb};
92120836Sdougb
9397952Sdougb#if 0
94120836Sdougb/*
95120836Sdougb * these entries are initialized using the autoconfig menu
96120836Sdougb * The struct is invalid (and must be initialized) if the first
97120836Sdougb * CSN is zero. The init code fills invalid entries with CSN 255
9897952Sdougb * which is not a supported value.
9997952Sdougb */
10097952Sdougb
10197952Sdougbstruct pnp_cinfo pnp_ldn_overrides[MAX_PNP_LDN] = {
10297952Sdougb    { 0 }
103120836Sdougb};
104120836Sdougb#endif
10597952Sdougb
10697952Sdougb/* The READ_DATA port that we are using currently */
10797952Sdougbstatic int pnp_rd_port;
108188481Smaxim
10997952Sdougbstatic void   pnp_send_initiation_key(void);
11097952Sdougbstatic int    pnp_get_serial(pnp_id *p);
11197952Sdougbstatic int    pnp_isolation_protocol(device_t parent);
11297952Sdougb
11397952Sdougbchar *
11497952Sdougbpnp_eisaformat(u_int32_t id)
11597952Sdougb{
11697952Sdougb	u_int8_t *data = (u_int8_t *) &id;
11797952Sdougb	static char idbuf[8];
11897952Sdougb	const char  hextoascii[] = "0123456789abcdef";
11997952Sdougb
12097952Sdougb	idbuf[0] = '@' + ((data[0] & 0x7c) >> 2);
12197952Sdougb	idbuf[1] = '@' + (((data[0] & 0x3) << 3) + ((data[1] & 0xe0) >> 5));
12297952Sdougb	idbuf[2] = '@' + (data[1] & 0x1f);
12397952Sdougb	idbuf[3] = hextoascii[(data[2] >> 4)];
12497952Sdougb	idbuf[4] = hextoascii[(data[2] & 0xf)];
12597952Sdougb	idbuf[5] = hextoascii[(data[3] >> 4)];
12697952Sdougb	idbuf[6] = hextoascii[(data[3] & 0xf)];
12797952Sdougb	idbuf[7] = 0;
12897952Sdougb	return(idbuf);
12997952Sdougb}
13097952Sdougb
13197952Sdougbstatic void
13297952Sdougbpnp_write(int d, u_char r)
13397952Sdougb{
13497952Sdougb	outb (_PNP_ADDRESS, d);
13597952Sdougb	outb (_PNP_WRITE_DATA, r);
13697952Sdougb}
13797952Sdougb
13897952Sdougb#if 0
13997952Sdougb
14097952Sdougbstatic u_char
14197952Sdougbpnp_read(int d)
14297952Sdougb{
14397952Sdougb	outb (_PNP_ADDRESS, d);
14497952Sdougb	return (inb(3 | (pnp_rd_port <<2)));
14597952Sdougb}
146101773Sdougb
147101773Sdougb#endif
148101773Sdougb
149101773Sdougb/*
150101773Sdougb * Send Initiation LFSR as described in "Plug and Play ISA Specification",
151101773Sdougb * Intel May 94.
152101773Sdougb */
15397952Sdougbstatic void
15497952Sdougbpnp_send_initiation_key()
15597952Sdougb{
15697952Sdougb	int cur, i;
157216203Sdougb
15897952Sdougb	/* Reset the LSFR */
15997952Sdougb	outb(_PNP_ADDRESS, 0);
16097952Sdougb	outb(_PNP_ADDRESS, 0); /* yes, we do need it twice! */
16197952Sdougb
16297952Sdougb	cur = 0x6a;
16397952Sdougb	outb(_PNP_ADDRESS, cur);
16497952Sdougb
16597952Sdougb	for (i = 1; i < 32; i++) {
16697952Sdougb		cur = (cur >> 1) | (((cur ^ (cur >> 1)) << 7) & 0xff);
16797952Sdougb		outb(_PNP_ADDRESS, cur);
168114924Sdougb	}
16997952Sdougb}
17097952Sdougb
17197952Sdougb
17297952Sdougb/*
17397952Sdougb * Get the device's serial number.  Returns 1 if the serial is valid.
17497952Sdougb */
17597952Sdougbstatic int
17697952Sdougbpnp_get_serial(pnp_id *p)
17797952Sdougb{
17897952Sdougb	int i, bit, valid = 0, sum = 0x6a;
179101773Sdougb	u_char *data = (u_char *)p;
18097952Sdougb
181216206Sdougb	bzero(data, sizeof(char) * 9);
18297952Sdougb	outb(_PNP_ADDRESS, PNP_SERIAL_ISOLATION);
18397952Sdougb	for (i = 0; i < 72; i++) {
18497952Sdougb		bit = inb((pnp_rd_port << 2) | 0x3) == 0x55;
18597952Sdougb		DELAY(250);	/* Delay 250 usec */
18697952Sdougb
18797952Sdougb		/* Can't Short Circuit the next evaluation, so 'and' is last */
188207153Sjilles		bit = (inb((pnp_rd_port << 2) | 0x3) == 0xaa) && bit;
189207153Sjilles		DELAY(250);	/* Delay 250 usec */
190207153Sjilles
19197952Sdougb		valid = valid || bit;
192241737Sed
193241737Sed		if (i < 64)
19497952Sdougb			sum = (sum >> 1) |
195101773Sdougb				(((sum ^ (sum >> 1) ^ bit) << 7) & 0xff);
19697952Sdougb
197101773Sdougb		data[i / 8] = (data[i / 8] >> 1) | (bit ? 0x80 : 0);
198101773Sdougb	}
19997952Sdougb
20097952Sdougb	valid = valid && (data[8] == sum);
20197952Sdougb
20297952Sdougb	return valid;
20397952Sdougb}
20497952Sdougb
205101773Sdougb/*
206101773Sdougb * Fill's the buffer with resource info from the device.
207216205Sdougb * Returns the number of characters read.
208188498Sed */
209188498Sedstatic int
21097952Sdougbpnp_get_resource_info(u_char *buffer, int len)
211101773Sdougb{
21297952Sdougb	int i, j, count;
21397952Sdougb	u_char temp;
21497952Sdougb
21597952Sdougb	count = 0;
216101773Sdougb	for (i = 0; i < len; i++) {
217101773Sdougb		outb(_PNP_ADDRESS, PNP_STATUS);
21897952Sdougb		for (j = 0; j < 100; j++) {
21997952Sdougb			if ((inb((pnp_rd_port << 2) | 0x3)) & 0x1)
22097952Sdougb				break;
221101773Sdougb			DELAY(1);
222101773Sdougb		}
223216203Sdougb		if (j == 100) {
224216203Sdougb			printf("PnP device failed to report resource data\n");
225101773Sdougb			return count;
226101773Sdougb		}
227101773Sdougb		outb(_PNP_ADDRESS, PNP_RESOURCE_DATA);
228101773Sdougb		temp = inb((pnp_rd_port << 2) | 0x3);
229101773Sdougb		if (buffer != NULL)
230216343Sdougb			buffer[i] = temp;
231216343Sdougb		count++;
232101773Sdougb	}
233101773Sdougb	return count;
234101773Sdougb}
23597952Sdougb
23697952Sdougb#if 0
23797952Sdougb/*
23897952Sdougb * write_pnp_parms initializes a logical device with the parms
23997952Sdougb * in d, and then activates the board if the last parameter is 1.
24097952Sdougb */
24197952Sdougb
24297952Sdougbstatic int
24397952Sdougbwrite_pnp_parms(struct pnp_cinfo *d, pnp_id *p, int ldn)
24497952Sdougb{
245101773Sdougb    int i, empty = -1 ;
246101773Sdougb
247101773Sdougb    pnp_write (SET_LDN, ldn );
24897952Sdougb    i = pnp_read(SET_LDN) ;
249216203Sdougb    if (i != ldn) {
250216203Sdougb	printf("Warning: LDN %d does not exist\n", ldn);
251216203Sdougb    }
252216203Sdougb    for (i = 0; i < 8; i++) {
25397952Sdougb	pnp_write(IO_CONFIG_BASE + i * 2, d->ic_port[i] >> 8 );
25497952Sdougb	pnp_write(IO_CONFIG_BASE + i * 2 + 1, d->ic_port[i] & 0xff );
25597952Sdougb    }
25697952Sdougb    for (i = 0; i < 4; i++) {
25797952Sdougb	pnp_write(MEM_CONFIG + i*8, (d->ic_mem[i].base >> 16) & 0xff );
25897952Sdougb	pnp_write(MEM_CONFIG + i*8+1, (d->ic_mem[i].base >> 8) & 0xff );
25997952Sdougb	pnp_write(MEM_CONFIG + i*8+2, d->ic_mem[i].control & 0xff );
26097952Sdougb	pnp_write(MEM_CONFIG + i*8+3, (d->ic_mem[i].range >> 16) & 0xff );
26197952Sdougb	pnp_write(MEM_CONFIG + i*8+4, (d->ic_mem[i].range >> 8) & 0xff );
26297952Sdougb    }
26397952Sdougb    for (i = 0; i < 2; i++) {
26497952Sdougb	pnp_write(IRQ_CONFIG + i*2    , d->irq[i] );
26597952Sdougb	pnp_write(IRQ_CONFIG + i*2 + 1, d->irq_type[i] );
26697952Sdougb	pnp_write(DRQ_CONFIG + i, d->drq[i] );
26797952Sdougb    }
268101773Sdougb    /*
26997952Sdougb     * store parameters read into the current kernel
27097952Sdougb     * so manual editing next time is easier
27197952Sdougb     */
27297952Sdougb    for (i = 0 ; i < MAX_PNP_LDN; i++) {
27397952Sdougb	if (pnp_ldn_overrides[i].csn == d->csn &&
27497952Sdougb		pnp_ldn_overrides[i].ldn == ldn) {
27597952Sdougb	    d->flags = pnp_ldn_overrides[i].flags ;
27697952Sdougb	    pnp_ldn_overrides[i] = *d ;
27797952Sdougb	    break ;
27897952Sdougb	} else if (pnp_ldn_overrides[i].csn < 1 ||
27997952Sdougb		pnp_ldn_overrides[i].csn == 255)
28097952Sdougb	    empty = i ;
28197952Sdougb    }
28297952Sdougb    if (i== MAX_PNP_LDN && empty != -1)
28397952Sdougb	pnp_ldn_overrides[empty] = *d;
28497952Sdougb
28597952Sdougb    /*
28697952Sdougb     * Here should really perform the range check, and
28797952Sdougb     * return a failure if not successful.
28897952Sdougb     */
28997952Sdougb    pnp_write (IO_RANGE_CHECK, 0);
29097952Sdougb    DELAY(1000); /* XXX is it really necessary ? */
29197952Sdougb    pnp_write (ACTIVATE, d->enable ? 1 : 0);
29297952Sdougb    DELAY(1000); /* XXX is it really necessary ? */
29397952Sdougb    return 1 ;
29497952Sdougb}
29597952Sdougb#endif
29697952Sdougb
29797952Sdougb/*
29897952Sdougb * This function is called after the bus has assigned resource
29997952Sdougb * locations for a logical device.
30097952Sdougb */
30197952Sdougbstatic void
30297952Sdougbpnp_set_config(void *arg, struct isa_config *config, int enable)
30397952Sdougb{
30497952Sdougb	int csn = ((struct pnp_set_config_arg *) arg)->csn;
30597952Sdougb	int ldn = ((struct pnp_set_config_arg *) arg)->ldn;
306101773Sdougb	int i;
30797952Sdougb
30897952Sdougb	/*
30997952Sdougb	 * First put all cards into Sleep state with the initiation
31097952Sdougb	 * key, then put our card into Config state.
31197952Sdougb	 */
31297952Sdougb	pnp_send_initiation_key();
31397952Sdougb	pnp_write(PNP_WAKE, csn);
31497952Sdougb
315188498Sed	/*
316188498Sed	 * Select our logical device so that we can program it.
317188498Sed	 */
318188498Sed	pnp_write(PNP_SET_LDN, ldn);
319188498Sed
320188498Sed	/*
32197952Sdougb	 * Now program the resources.
322188498Sed	 */
323188498Sed	for (i = 0; i < config->ic_nmem; i++) {
324216196Sdougb		u_int32_t start = config->ic_mem[i].ir_start;
325216196Sdougb		u_int32_t size =  config->ic_mem[i].ir_size;
326216196Sdougb		if (start & 0xff)
327216196Sdougb			panic("pnp_set_config: bogus memory assignment");
328216196Sdougb		pnp_write(PNP_MEM_BASE_HIGH(i), (start >> 16) & 0xff);
329216196Sdougb		pnp_write(PNP_MEM_BASE_LOW(i), (start >> 8) & 0xff);
330216196Sdougb		pnp_write(PNP_MEM_RANGE_HIGH(i), (size >> 16) & 0xff);
331216196Sdougb		pnp_write(PNP_MEM_RANGE_LOW(i), (size >> 8) & 0xff);
332216196Sdougb	}
333216196Sdougb	for (; i < ISA_NMEM; i++) {
334216196Sdougb		pnp_write(PNP_MEM_BASE_HIGH(i), 0);
335188498Sed		pnp_write(PNP_MEM_BASE_LOW(i), 0);
336188498Sed		pnp_write(PNP_MEM_RANGE_HIGH(i), 0);
337188498Sed		pnp_write(PNP_MEM_RANGE_LOW(i), 0);
33897952Sdougb	}
33997952Sdougb
34097952Sdougb	for (i = 0; i < config->ic_nport; i++) {
341101773Sdougb		u_int32_t start = config->ic_port[i].ir_start;
342101773Sdougb		pnp_write(PNP_IO_BASE_HIGH(i), (start >> 8) & 0xff);
343188498Sed		pnp_write(PNP_IO_BASE_LOW(i), (start >> 0) & 0xff);
34497952Sdougb	}
34597952Sdougb	for (; i < ISA_NPORT; i++) {
346216206Sdougb		pnp_write(PNP_IO_BASE_HIGH(i), 0);
34797952Sdougb		pnp_write(PNP_IO_BASE_LOW(i), 0);
34897952Sdougb	}
34997952Sdougb
35097952Sdougb	for (i = 0; i < config->ic_nirq; i++) {
35197952Sdougb		int irq = ffs(config->ic_irqmask[i]) - 1;
35297952Sdougb		pnp_write(PNP_IRQ_LEVEL(i), irq);
353101773Sdougb		pnp_write(PNP_IRQ_TYPE(i), 2); /* XXX */
35497952Sdougb	}
35597952Sdougb	for (; i < ISA_NIRQ; i++) {
356207153Sjilles		/*
357207153Sjilles		 * IRQ 0 is not a valid interrupt selection and
358207153Sjilles		 * represents no interrupt selection.
359207153Sjilles		 */
360207153Sjilles		pnp_write(PNP_IRQ_LEVEL(i), 0);
361207153Sjilles	}
362207153Sjilles
363207153Sjilles	for (i = 0; i < config->ic_ndrq; i++) {
364207153Sjilles		int drq = ffs(config->ic_drqmask[i]) - 1;
365207153Sjilles		pnp_write(PNP_DMA_CHANNEL(i), drq);
366207153Sjilles	}
367207153Sjilles	for (; i < ISA_NDRQ; i++) {
368207153Sjilles		/*
369207153Sjilles		 * DMA channel 4, the cascade channel is used to
370207153Sjilles		 * indicate no DMA channel is active.
371207153Sjilles		 */
372207153Sjilles		pnp_write(PNP_DMA_CHANNEL(i), 4);
373207153Sjilles	}
374207153Sjilles
37597952Sdougb	pnp_write(PNP_ACTIVATE, enable ? 1 : 0);
376101773Sdougb
37797952Sdougb	/*
37897952Sdougb	 * Wake everyone up again, we are finished.
379101773Sdougb	 */
38097952Sdougb	pnp_write(PNP_CONFIG_CONTROL, PNP_CONFIG_CONTROL_WAIT_FOR_KEY);
38197952Sdougb}
38297952Sdougb
38397952Sdougb/*
38497952Sdougb * Process quirks for a logical device.. The card must be in Config state.
38597952Sdougb */
38697952Sdougbvoid
38797952Sdougbpnp_check_quirks(u_int32_t vendor_id, u_int32_t logical_id, int ldn, struct isa_config *config)
388216206Sdougb{
38997952Sdougb	struct pnp_quirk *qp;
39097952Sdougb
391216202Sdougb	for (qp = &pnp_quirks[0]; qp->vendor_id; qp++) {
39297952Sdougb		if (qp->vendor_id == vendor_id
39397952Sdougb		    && (qp->logical_id == 0
39497952Sdougb			|| qp->logical_id == logical_id)) {
395101773Sdougb			switch (qp->type) {
39697952Sdougb			case PNP_QUIRK_WRITE_REG:
39797952Sdougb				pnp_write(PNP_SET_LDN, ldn);
39897952Sdougb				pnp_write(qp->arg1, qp->arg2);
39997952Sdougb				break;
40097952Sdougb			case PNP_QUIRK_EXTRA_IO:
40197952Sdougb				if (config == NULL)
402101773Sdougb					break;
40397952Sdougb				if (qp->arg1 != 0) {
40497952Sdougb					config->ic_nport++;
40597952Sdougb					config->ic_port[config->ic_nport - 1] = config->ic_port[0];
40697952Sdougb					config->ic_port[config->ic_nport - 1].ir_start += qp->arg1;
40797952Sdougb					config->ic_port[config->ic_nport - 1].ir_end += qp->arg1;
40897952Sdougb				}
40997952Sdougb				if (qp->arg2 != 0) {
41097952Sdougb					config->ic_nport++;
41197952Sdougb					config->ic_port[config->ic_nport - 1] = config->ic_port[0];
41297952Sdougb					config->ic_port[config->ic_nport - 1].ir_start += qp->arg2;
41397952Sdougb					config->ic_port[config->ic_nport - 1].ir_end += qp->arg2;
41497952Sdougb				}
41597952Sdougb				break;
41697952Sdougb			}
41797952Sdougb		}
41897952Sdougb	}
419101773Sdougb}
42097952Sdougb
42197952Sdougb/*
42297952Sdougb * Scan Resource Data for Logical Devices.
423101773Sdougb *
42497952Sdougb * This function exits as soon as it gets an error reading *ANY*
42597952Sdougb * Resource Data or it reaches the end of Resource Data.  In the first
42697952Sdougb * case the return value will be TRUE, FALSE otherwise.
427101773Sdougb */
42897952Sdougbstatic int
42997952Sdougbpnp_create_devices(device_t parent, pnp_id *p, int csn,
43097952Sdougb		   u_char *resources, int len)
43197952Sdougb{
43297952Sdougb	u_char tag, *resp, *resinfo, *startres = 0;
43397952Sdougb	int large_len, scanning = len, retval = FALSE;
43497952Sdougb	u_int32_t logical_id;
435101773Sdougb	u_int32_t compat_id;
43697952Sdougb	device_t dev = 0;
43797952Sdougb	int ldn = 0;
43897952Sdougb	struct pnp_set_config_arg *csnldn;
43997952Sdougb	char buf[100];
44097952Sdougb	char *desc = 0;
44197952Sdougb
44297952Sdougb	resp = resources;
44397952Sdougb	while (scanning > 0) {
44497952Sdougb		tag = *resp++;
44597952Sdougb		scanning--;
44697952Sdougb		if (PNP_RES_TYPE(tag) != 0) {
44797952Sdougb			/* Large resource */
44897952Sdougb			if (scanning < 2) {
44997952Sdougb				scanning = 0;
45097952Sdougb				continue;
45197952Sdougb			}
45297952Sdougb			large_len = resp[0] + (resp[1] << 8);
45397952Sdougb			resp += 2;
45497952Sdougb
45597952Sdougb			if (scanning < large_len) {
45697952Sdougb				scanning = 0;
45797952Sdougb				continue;
45897952Sdougb			}
45997952Sdougb			resinfo = resp;
46097952Sdougb			resp += large_len;
46197952Sdougb			scanning -= large_len;
46297952Sdougb
46397952Sdougb			if (PNP_LRES_NUM(tag) == PNP_TAG_ID_ANSI) {
46497952Sdougb				if (large_len > sizeof(buf) - 1)
46597952Sdougb					large_len = sizeof(buf) - 1;
46697952Sdougb				bcopy(resinfo, buf, large_len);
46797952Sdougb
46897952Sdougb				/*
46997952Sdougb				 * Trim trailing spaces.
47097952Sdougb				 */
47197952Sdougb				while (buf[large_len-1] == ' ')
47297952Sdougb					large_len--;
47397952Sdougb				buf[large_len] = '\0';
47497952Sdougb				desc = buf;
47597952Sdougb				if (dev)
47697952Sdougb					device_set_desc_copy(dev, desc);
47797952Sdougb				continue;
47897952Sdougb			}
47997952Sdougb
48097952Sdougb			continue;
48197952Sdougb		}
48297952Sdougb
48397952Sdougb		/* Small resource */
48497952Sdougb		if (scanning < PNP_SRES_LEN(tag)) {
48597952Sdougb			scanning = 0;
48697952Sdougb			continue;
48797952Sdougb		}
48897952Sdougb		resinfo = resp;
48997952Sdougb		resp += PNP_SRES_LEN(tag);
49097952Sdougb		scanning -= PNP_SRES_LEN(tag);;
49197952Sdougb
49297952Sdougb		switch (PNP_SRES_NUM(tag)) {
49397952Sdougb		case PNP_TAG_LOGICAL_DEVICE:
49497952Sdougb			/*
49597952Sdougb			 * Parse the resources for the previous
49697952Sdougb			 * logical device (if any).
49797952Sdougb			 */
49897952Sdougb			if (startres) {
49997952Sdougb				pnp_parse_resources(dev, startres,
50097952Sdougb						    resinfo - startres - 1,
50197952Sdougb						    p->vendor_id, logical_id, ldn);
50297952Sdougb				dev = 0;
50397952Sdougb				startres = 0;
504101773Sdougb			}
505101773Sdougb
50697952Sdougb			/*
507101773Sdougb			 * A new logical device. Scan for end of
508101773Sdougb			 * resources.
509101773Sdougb			 */
510101773Sdougb			bcopy(resinfo, &logical_id, 4);
511101773Sdougb			pnp_check_quirks(p->vendor_id, logical_id, ldn, NULL);
512101773Sdougb			compat_id = 0;
51397952Sdougb			dev = BUS_ADD_CHILD(parent, ISA_ORDER_PNP, NULL, -1);
51497952Sdougb			if (desc)
51597952Sdougb				device_set_desc_copy(dev, desc);
51697952Sdougb			isa_set_vendorid(dev, p->vendor_id);
51797952Sdougb			isa_set_serial(dev, p->serial);
51897952Sdougb			isa_set_logicalid(dev, logical_id);
51997952Sdougb			csnldn = malloc(sizeof *csnldn, M_DEVBUF, M_NOWAIT);
52097952Sdougb			if (!csnldn) {
52197952Sdougb				device_printf(parent,
52297952Sdougb					      "out of memory\n");
52397952Sdougb				scanning = 0;
52497952Sdougb				break;
52597952Sdougb			}
52697952Sdougb			csnldn->csn = csn;
52797952Sdougb			csnldn->ldn = ldn;
528216203Sdougb			ISA_SET_CONFIG_CALLBACK(parent, dev,
52997952Sdougb						pnp_set_config, csnldn);
53097952Sdougb			ldn++;
53197952Sdougb			startres = resp;
53297952Sdougb			break;
53397952Sdougb
53497952Sdougb		case PNP_TAG_END:
53597952Sdougb			if (!startres) {
53697952Sdougb				device_printf(parent,
53797952Sdougb					      "malformed resources\n");
53897952Sdougb				scanning = 0;
539114924Sdougb				break;
54097952Sdougb			}
54197952Sdougb			pnp_parse_resources(dev, startres,
54297952Sdougb					    resinfo - startres - 1,
54397952Sdougb					    p->vendor_id, logical_id, ldn);
54497952Sdougb			dev = 0;
54597952Sdougb			startres = 0;
54697952Sdougb			scanning = 0;
54797952Sdougb			break;
54897952Sdougb
54997952Sdougb		default:
55097952Sdougb			/* Skip this resource */
55197952Sdougb			break;
552101773Sdougb		}
55397952Sdougb	}
55497952Sdougb
55597952Sdougb	return retval;
55697952Sdougb}
55797952Sdougb
558101773Sdougb/*
55997952Sdougb * Read 'amount' bytes of resources from the card, allocating memory
56097952Sdougb * as needed. If a buffer is already available, it should be passed in
561216205Sdougb * '*resourcesp' and its length in '*spacep'. The number of resource
562101773Sdougb * bytes already in the buffer should be passed in '*lenp'. The memory
56397952Sdougb * allocated will be returned in '*resourcesp' with its size and the
56497952Sdougb * number of bytes of resources in '*spacep' and '*lenp' respectively.
56597952Sdougb */
56697952Sdougbstatic int
56797952Sdougbpnp_read_bytes(int amount, u_char **resourcesp, int *spacep, int *lenp)
56897952Sdougb{
56997952Sdougb	u_char *resources = *resourcesp;
57097952Sdougb	u_char *newres;
57197952Sdougb	int space = *spacep;
572101773Sdougb	int len = *lenp;
573101773Sdougb
57497952Sdougb	if (space == 0) {
57597952Sdougb		space = 1024;
57697952Sdougb		resources = malloc(space, M_TEMP, M_NOWAIT);
57797952Sdougb		if (!resources)
57897952Sdougb			return ENOMEM;
57997952Sdougb	}
58097952Sdougb
58197952Sdougb	if (len + amount > space) {
58297952Sdougb		int extra = 1024;
58397952Sdougb		while (len + amount > space + extra)
58497952Sdougb			extra += 1024;
58597952Sdougb		newres = malloc(space + extra, M_TEMP, M_NOWAIT);
58697952Sdougb		if (!newres)
58797952Sdougb			return ENOMEM;
588216205Sdougb		bcopy(resources, newres, len);
589216205Sdougb		free(resources, M_TEMP);
590101773Sdougb		resources = newres;
59197952Sdougb		space += extra;
59297952Sdougb	}
59397952Sdougb
59497952Sdougb	if (pnp_get_resource_info(resources + len, amount) != amount)
59597952Sdougb		return EINVAL;
59697952Sdougb	len += amount;
59797952Sdougb
59897952Sdougb	*resourcesp = resources;
59997952Sdougb	*spacep = space;
60097952Sdougb	*lenp = len;
60197952Sdougb
60297952Sdougb	return 0;
60397952Sdougb}
60497952Sdougb
60597952Sdougb/*
60697952Sdougb * Read all resources from the card, allocating memory as needed. If a
60797952Sdougb * buffer is already available, it should be passed in '*resourcesp'
60897952Sdougb * and its length in '*spacep'. The memory allocated will be returned
60997952Sdougb * in '*resourcesp' with its size and the number of bytes of resources
61097952Sdougb * in '*spacep' and '*lenp' respectively.
611120836Sdougb */
61297952Sdougbstatic int
61397952Sdougbpnp_read_resources(u_char **resourcesp, int *spacep, int *lenp)
61497952Sdougb{
61597952Sdougb	u_char *resources = *resourcesp;
61697952Sdougb	int space = *spacep;
61797952Sdougb	int len = 0;
61897952Sdougb	int error, done;
61997952Sdougb	u_char tag;
620120836Sdougb
62197952Sdougb	error = 0;
62297952Sdougb	done = 0;
62397952Sdougb	while (!done) {
62497952Sdougb		error = pnp_read_bytes(1, &resources, &space, &len);
62597952Sdougb		if (error)
62697952Sdougb			goto out;
62797952Sdougb		tag = resources[len-1];
62897952Sdougb		if (PNP_RES_TYPE(tag) == 0) {
629101773Sdougb			/*
630120836Sdougb			 * Small resource, read contents.
631101773Sdougb			 */
632120836Sdougb			error = pnp_read_bytes(PNP_SRES_LEN(tag),
633120836Sdougb					       &resources, &space, &len);
634120836Sdougb			if (error)
63597952Sdougb				goto out;
636101773Sdougb			if (PNP_SRES_NUM(tag) == PNP_TAG_END)
63797952Sdougb				done = 1;
63897952Sdougb		} else {
63997952Sdougb			/*
64097952Sdougb			 * Large resource, read length and contents.
64197952Sdougb			 */
642101773Sdougb			error = pnp_read_bytes(2, &resources, &space, &len);
64397952Sdougb			if (error)
644101773Sdougb				goto out;
64597952Sdougb			error = pnp_read_bytes(resources[len-2]
64697952Sdougb					       + (resources[len-1] << 8),
64797952Sdougb					       &resources, &space, &len);
64897952Sdougb			if (error)
64997952Sdougb				goto out;
650216205Sdougb		}
651216205Sdougb	}
652216205Sdougb
653216205Sdougb out:
65497952Sdougb	*resourcesp = resources;
65597952Sdougb	*spacep = space;
656216205Sdougb	*lenp = len;
657216205Sdougb	return error;
65897952Sdougb}
65997952Sdougb
66097952Sdougb/*
66197952Sdougb * Run the isolation protocol. Use pnp_rd_port as the READ_DATA port
662216205Sdougb * value (caller should try multiple READ_DATA locations before giving
663216205Sdougb * up). Upon exiting, all cards are aware that they should use
66497952Sdougb * pnp_rd_port as the READ_DATA port.
66597952Sdougb *
66697952Sdougb * In the first pass, a csn is assigned to each board and pnp_id's
66797952Sdougb * are saved to an array, pnp_devices. In the second pass, each
668216205Sdougb * card is woken up and the device configuration is called.
669216205Sdougb */
67097952Sdougbstatic int
67197952Sdougbpnp_isolation_protocol(device_t parent)
672216205Sdougb{
673101773Sdougb	int csn;
674101773Sdougb	pnp_id id;
67597952Sdougb	int found = 0, len;
676101773Sdougb	u_char *resources = 0;
67797952Sdougb	int space = 0;
67897952Sdougb	int error;
67997952Sdougb
68097952Sdougb	/*
68197952Sdougb	 * Put all cards into the Sleep state so that we can clear
682101773Sdougb	 * their CSNs.
68397952Sdougb	 */
684101773Sdougb	pnp_send_initiation_key();
68597952Sdougb
68697952Sdougb	/*
68797952Sdougb	 * Clear the CSN for all cards.
68897952Sdougb	 */
68997952Sdougb	pnp_write(PNP_CONFIG_CONTROL, PNP_CONFIG_CONTROL_RESET_CSN);
69097952Sdougb
69197952Sdougb	/*
69297952Sdougb	 * Move all cards to the Isolation state.
69397952Sdougb	 */
69497952Sdougb	pnp_write(PNP_WAKE, 0);
695101773Sdougb
696101773Sdougb	/*
69797952Sdougb	 * Tell them where the read point is going to be this time.
698101773Sdougb	 */
69997952Sdougb	pnp_write(PNP_SET_RD_DATA, pnp_rd_port);
70097952Sdougb
70197952Sdougb	for (csn = 1; csn < PNP_MAX_CARDS; csn++) {
70297952Sdougb		/*
70397952Sdougb		 * Start the serial isolation protocol.
70497952Sdougb		 */
70597952Sdougb		outb(_PNP_ADDRESS, PNP_SERIAL_ISOLATION);
70697952Sdougb		DELAY(1000);	/* Delay 1 msec */
70797952Sdougb
70897952Sdougb		if (pnp_get_serial(&id)) {
709101773Sdougb			/*
710101773Sdougb			 * We have read the id from a card
71197952Sdougb			 * successfully. The card which won the
712101773Sdougb			 * isolation protocol will be in Isolation
71397952Sdougb			 * mode and all others will be in Sleep.
71497952Sdougb			 * Program the CSN of the isolated card
71597952Sdougb			 * (taking it to Config state) and read its
71697952Sdougb			 * resources, creating devices as we find
71797952Sdougb			 * logical devices on the card.
71897952Sdougb			 */
71997952Sdougb			pnp_write(PNP_SET_CSN, csn);
72097952Sdougb			error = pnp_read_resources(&resources,
72197952Sdougb						   &space,
72297952Sdougb						   &len);
72397952Sdougb			if (error)
724114924Sdougb				break;
725120836Sdougb			pnp_create_devices(parent, &id, csn,
726114924Sdougb					   resources, len);
727114924Sdougb			found++;
728114924Sdougb		} else
729120836Sdougb			break;
73097952Sdougb
73197952Sdougb		/*
73297952Sdougb		 * Put this card back to the Sleep state and
73397952Sdougb		 * simultaneously move all cards which don't have a
734218535Sdougb		 * CSN yet to Isolation state.
735218535Sdougb		 */
736218535Sdougb		pnp_write(PNP_WAKE, 0);
737218535Sdougb	}
73897952Sdougb
73997952Sdougb	/*
740101773Sdougb	 * Unless we have chosen the wrong read port, all cards will
741101773Sdougb	 * be in Sleep state. Put them back into WaitForKey for
74297952Sdougb	 * now. Their resources will be programmed later.
743101773Sdougb	 */
74497952Sdougb	pnp_write(PNP_CONFIG_CONTROL, PNP_CONFIG_CONTROL_WAIT_FOR_KEY);
74597952Sdougb
74697952Sdougb	/*
74797952Sdougb	 * Cleanup.
74897952Sdougb	 */
749101773Sdougb	if (resources)
75097952Sdougb		free(resources, M_TEMP);
751101773Sdougb
75297952Sdougb	return found;
75397952Sdougb}
75497952Sdougb
75597952Sdougb
75697952Sdougb/*
757101773Sdougb * pnp_identify()
75897952Sdougb *
759101773Sdougb * autoconfiguration of pnp devices. This routine just runs the
76097952Sdougb * isolation protocol over several ports, until one is successful.
76197952Sdougb *
76297952Sdougb * may be called more than once ?
76397952Sdougb *
76497952Sdougb */
765101773Sdougb
76697952Sdougbstatic void
767101773Sdougbpnp_identify(driver_t *driver, device_t parent)
76897952Sdougb{
769120836Sdougb	int num_pnp_devs;
77097952Sdougb
77197952Sdougb#if 0
77297952Sdougb	if (pnp_ldn_overrides[0].csn == 0) {
773207153Sjilles		if (bootverbose)
774207153Sjilles			printf("Initializing PnP override table\n");
775207153Sjilles		bzero (pnp_ldn_overrides, sizeof(pnp_ldn_overrides));
776207153Sjilles		pnp_ldn_overrides[0].csn = 255 ;
777207153Sjilles	}
77897952Sdougb#endif
779101773Sdougb
78097952Sdougb	/* Try various READ_DATA ports from 0x203-0x3ff */
781120836Sdougb	for (pnp_rd_port = 0x80; (pnp_rd_port < 0xff); pnp_rd_port += 0x10) {
782120836Sdougb		if (bootverbose)
78397952Sdougb			printf("Trying Read_Port at %x\n", (pnp_rd_port << 2) | 0x3);
78497952Sdougb
78597952Sdougb		num_pnp_devs = pnp_isolation_protocol(parent);
78697952Sdougb		if (num_pnp_devs)
787101773Sdougb			break;
78897952Sdougb	}
789101773Sdougb}
79097952Sdougb
791120836Sdougbstatic device_method_t pnp_methods[] = {
792216203Sdougb	/* Device interface */
793216203Sdougb	DEVMETHOD(device_identify,	pnp_identify),
794216203Sdougb
795216203Sdougb	{ 0, 0 }
796216512Sdougb};
797216203Sdougb
798216203Sdougbstatic driver_t pnp_driver = {
799216203Sdougb	"pnp",
800216203Sdougb	pnp_methods,
801216203Sdougb	1,			/* no softc */
802216203Sdougb};
803216203Sdougb
804216203Sdougbstatic devclass_t pnp_devclass;
805216203Sdougb
806216203SdougbDRIVER_MODULE(pnp, isa, pnp_driver, pnp_devclass, 0, 0);
807216203Sdougb