pnp.c revision 139271
1193323Sed/*
2193323Sed * Copyright (c) 1996, Sujal M. Patel
3193323Sed * All rights reserved.
4193323Sed *
5193323Sed * Redistribution and use in source and binary forms, with or without
6193323Sed * modification, are permitted provided that the following conditions
7193323Sed * are met:
8193323Sed * 1. Redistributions of source code must retain the above copyright
9193323Sed *    notice, this list of conditions and the following disclaimer.
10193323Sed * 2. Redistributions in binary form must reproduce the above copyright
11193323Sed *    notice, this list of conditions and the following disclaimer in the
12193323Sed *    documentation and/or other materials provided with the distribution.
13193323Sed *
14193323Sed * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15193323Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16193323Sed * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17193323Sed * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18193323Sed * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19193323Sed * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20193323Sed * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21193323Sed * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22193323Sed * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23193323Sed * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24193323Sed * SUCH DAMAGE.
25193323Sed *
26193323Sed *      from: pnp.c,v 1.11 1999/05/06 22:11:19 peter Exp
27193323Sed */
28193323Sed
29193323Sed#include <sys/cdefs.h>
30195340Sed__FBSDID("$FreeBSD: head/sys/isa/pnp.c 139271 2004-12-24 22:18:19Z imp $");
31193323Sed
32193323Sed#include <sys/param.h>
33193323Sed#include <sys/systm.h>
34193323Sed#include <sys/kernel.h>
35193323Sed#include <sys/module.h>
36193323Sed#include <sys/bus.h>
37198090Srdivacky#include <sys/malloc.h>
38193323Sed#include <isa/isavar.h>
39193323Sed#include <isa/pnpreg.h>
40193323Sed#include <isa/pnpvar.h>
41198090Srdivacky#include <machine/bus.h>
42193323Sed
43193323Sedtypedef struct _pnp_id {
44193323Sed	uint32_t vendor_id;
45193323Sed	uint32_t serial;
46193323Sed	u_char checksum;
47193323Sed} pnp_id;
48193323Sed
49193323Sedstruct pnp_set_config_arg {
50193323Sed	int	csn;		/* Card number to configure */
51193323Sed	int	ldn;		/* Logical device on card */
52198090Srdivacky};
53193323Sed
54193323Sedstruct pnp_quirk {
55193323Sed	uint32_t vendor_id;	/* Vendor of the card */
56193323Sed	uint32_t logical_id;	/* ID of the device with quirk */
57193323Sed	int	type;
58193323Sed#define PNP_QUIRK_WRITE_REG	1 /* Need to write a pnp register  */
59193323Sed#define PNP_QUIRK_EXTRA_IO	2 /* Has extra io ports  */
60193323Sed	int	arg1;
61193323Sed	int	arg2;
62193323Sed};
63193323Sed
64193323Sedstruct pnp_quirk pnp_quirks[] = {
65193323Sed	/*
66193323Sed	 * The Gravis UltraSound needs register 0xf2 to be set to 0xff
67193323Sed	 * to enable power.
68193323Sed	 * XXX need to know the logical device id.
69193323Sed	 */
70193323Sed	{ 0x0100561e /* GRV0001 */,	0,
71193323Sed	  PNP_QUIRK_WRITE_REG,	0xf2,	 0xff },
72193323Sed	/*
73193323Sed	 * An emu8000 does not give us other than the first
74193323Sed	 * port.
75193323Sed	 */
76193323Sed	{ 0x26008c0e /* SB16 */,	0x21008c0e,
77201360Srdivacky	  PNP_QUIRK_EXTRA_IO,	0x400,	 0x800 },
78201360Srdivacky	{ 0x42008c0e /* SB32(CTL0042) */,	0x21008c0e,
79201360Srdivacky	  PNP_QUIRK_EXTRA_IO,	0x400,	 0x800 },
80201360Srdivacky	{ 0x44008c0e /* SB32(CTL0044) */,	0x21008c0e,
81193323Sed	  PNP_QUIRK_EXTRA_IO,	0x400,	 0x800 },
82193323Sed	{ 0x49008c0e /* SB32(CTL0049) */,	0x21008c0e,
83193323Sed	  PNP_QUIRK_EXTRA_IO,	0x400,	 0x800 },
84193323Sed	{ 0xf1008c0e /* SB32(CTL00f1) */,	0x21008c0e,
85193323Sed	  PNP_QUIRK_EXTRA_IO,	0x400,	 0x800 },
86193323Sed	{ 0xc1008c0e /* SB64(CTL00c1) */,	0x22008c0e,
87193323Sed	  PNP_QUIRK_EXTRA_IO,	0x400,	 0x800 },
88193323Sed	{ 0xc5008c0e /* SB64(CTL00c5) */,	0x22008c0e,
89193323Sed	  PNP_QUIRK_EXTRA_IO,	0x400,	 0x800 },
90193323Sed	{ 0xe4008c0e /* SB64(CTL00e4) */,	0x22008c0e,
91193323Sed	  PNP_QUIRK_EXTRA_IO,	0x400,	 0x800 },
92193323Sed
93193323Sed	{ 0 }
94193323Sed};
95193323Sed
96193323Sed#ifdef PC98
97193323Sed/* Some NEC PnP cards have 9 bytes serial code. */
98193323Sedstatic pnp_id necids[] = {
99193323Sed	{0x4180a3b8, 0xffffffff, 0x00},	/* PC-9801CB-B04 (NEC8041) */
100193323Sed	{0x5181a3b8, 0xffffffff, 0x46},	/* PC-9821CB2-B04(NEC8151) */
101193323Sed	{0x5182a3b8, 0xffffffff, 0xb8},	/* PC-9801-XX    (NEC8251) */
102193323Sed	{0x9181a3b8, 0xffffffff, 0x00},	/* PC-9801-120   (NEC8191) */
103193323Sed	{0, 0, 0}
104193323Sed};
105193323Sed#endif
106193323Sed
107198892Srdivacky/* The READ_DATA port that we are using currently */
108193323Sedstatic int pnp_rd_port;
109201360Srdivacky
110201360Srdivackystatic void   pnp_send_initiation_key(void);
111201360Srdivackystatic int    pnp_get_serial(pnp_id *p);
112201360Srdivackystatic int    pnp_isolation_protocol(device_t parent);
113201360Srdivacky
114201360Srdivackychar *
115201360Srdivackypnp_eisaformat(uint32_t id)
116201360Srdivacky{
117201360Srdivacky	uint8_t *data = (uint8_t *) &id;
118193323Sed	static char idbuf[8];
119198892Srdivacky	const char  hextoascii[] = "0123456789abcdef";
120198892Srdivacky
121201360Srdivacky	idbuf[0] = '@' + ((data[0] & 0x7c) >> 2);
122201360Srdivacky	idbuf[1] = '@' + (((data[0] & 0x3) << 3) + ((data[1] & 0xe0) >> 5));
123198892Srdivacky	idbuf[2] = '@' + (data[1] & 0x1f);
124193323Sed	idbuf[3] = hextoascii[(data[2] >> 4)];
125201360Srdivacky	idbuf[4] = hextoascii[(data[2] & 0xf)];
126201360Srdivacky	idbuf[5] = hextoascii[(data[3] >> 4)];
127201360Srdivacky	idbuf[6] = hextoascii[(data[3] & 0xf)];
128201360Srdivacky	idbuf[7] = 0;
129201360Srdivacky	return(idbuf);
130201360Srdivacky}
131201360Srdivacky
132198892Srdivackystatic void
133193323Sedpnp_write(int d, u_char r)
134198892Srdivacky{
135193323Sed	outb (_PNP_ADDRESS, d);
136198892Srdivacky	outb (_PNP_WRITE_DATA, r);
137193323Sed}
138193323Sed
139193323Sed/*
140193323Sed * Send Initiation LFSR as described in "Plug and Play ISA Specification",
141193323Sed * Intel May 94.
142193323Sed */
143193323Sedstatic void
144193323Sedpnp_send_initiation_key()
145193323Sed{
146198892Srdivacky	int cur, i;
147193323Sed
148193323Sed	/* Reset the LSFR */
149193323Sed	outb(_PNP_ADDRESS, 0);
150193323Sed	outb(_PNP_ADDRESS, 0); /* yes, we do need it twice! */
151193323Sed
152193323Sed	cur = 0x6a;
153193323Sed	outb(_PNP_ADDRESS, cur);
154193323Sed
155193323Sed	for (i = 1; i < 32; i++) {
156193323Sed		cur = (cur >> 1) | (((cur ^ (cur >> 1)) << 7) & 0xff);
157193323Sed		outb(_PNP_ADDRESS, cur);
158193323Sed	}
159193323Sed}
160198090Srdivacky
161198090Srdivacky
162193323Sed/*
163198090Srdivacky * Get the device's serial number.  Returns 1 if the serial is valid.
164198090Srdivacky */
165198090Srdivackystatic int
166198090Srdivackypnp_get_serial(pnp_id *p)
167198090Srdivacky{
168198090Srdivacky	int i, bit, valid = 0, sum = 0x6a;
169198090Srdivacky	u_char *data = (u_char *)p;
170193323Sed
171193323Sed	bzero(data, sizeof(char) * 9);
172193323Sed	outb(_PNP_ADDRESS, PNP_SERIAL_ISOLATION);
173193323Sed	for (i = 0; i < 72; i++) {
174193323Sed		bit = inb((pnp_rd_port << 2) | 0x3) == 0x55;
175193323Sed		DELAY(250);	/* Delay 250 usec */
176193323Sed
177193323Sed		/* Can't Short Circuit the next evaluation, so 'and' is last */
178193323Sed		bit = (inb((pnp_rd_port << 2) | 0x3) == 0xaa) && bit;
179193323Sed		DELAY(250);	/* Delay 250 usec */
180193323Sed
181193323Sed		valid = valid || bit;
182193323Sed		if (i < 64)
183193323Sed			sum = (sum >> 1) |
184193323Sed			  (((sum ^ (sum >> 1) ^ bit) << 7) & 0xff);
185193323Sed		data[i / 8] = (data[i / 8] >> 1) | (bit ? 0x80 : 0);
186193323Sed	}
187193323Sed
188193323Sed	valid = valid && (data[8] == sum);
189193323Sed
190193323Sed	return (valid);
191193323Sed}
192193323Sed
193193323Sed/*
194193323Sed * Fill's the buffer with resource info from the device.
195193323Sed * Returns the number of characters read.
196193323Sed */
197193323Sedstatic int
198193323Sedpnp_get_resource_info(u_char *buffer, int len)
199193323Sed{
200193323Sed	int i, j, count;
201193323Sed	u_char temp;
202193323Sed
203199989Srdivacky	count = 0;
204193323Sed	for (i = 0; i < len; i++) {
205193323Sed		outb(_PNP_ADDRESS, PNP_STATUS);
206193323Sed		for (j = 0; j < 100; j++) {
207193323Sed			if ((inb((pnp_rd_port << 2) | 0x3)) & 0x1)
208193323Sed				break;
209193323Sed			DELAY(1);
210193323Sed		}
211193323Sed		if (j == 100) {
212193323Sed			printf("PnP device failed to report resource data\n");
213193323Sed			return (count);
214193323Sed		}
215193323Sed		outb(_PNP_ADDRESS, PNP_RESOURCE_DATA);
216193323Sed		temp = inb((pnp_rd_port << 2) | 0x3);
217193323Sed		if (buffer != NULL)
218193323Sed			buffer[i] = temp;
219193323Sed		count++;
220193323Sed	}
221193323Sed	return (count);
222193323Sed}
223193323Sed
224198892Srdivacky/*
225193323Sed * This function is called after the bus has assigned resource
226193323Sed * locations for a logical device.
227193323Sed */
228193323Sedstatic void
229198892Srdivackypnp_set_config(void *arg, struct isa_config *config, int enable)
230193323Sed{
231193323Sed	int csn = ((struct pnp_set_config_arg *) arg)->csn;
232193323Sed	int ldn = ((struct pnp_set_config_arg *) arg)->ldn;
233193323Sed	int i;
234193323Sed
235198892Srdivacky	/*
236193323Sed	 * First put all cards into Sleep state with the initiation
237193323Sed	 * key, then put our card into Config state.
238193323Sed	 */
239193323Sed	pnp_send_initiation_key();
240193323Sed	pnp_write(PNP_WAKE, csn);
241193323Sed
242193323Sed	/*
243193323Sed	 * Select our logical device so that we can program it.
244193323Sed	 */
245193323Sed	pnp_write(PNP_SET_LDN, ldn);
246193323Sed
247193323Sed	/*
248193323Sed	 * Constrain the number of resources we will try to program
249193323Sed	 */
250193323Sed	if (config->ic_nmem > ISA_PNP_NMEM) {
251193323Sed		printf("too many ISA memory ranges (%d > %d)\n",
252193323Sed		    config->ic_nmem, ISA_PNP_NMEM);
253193323Sed		config->ic_nmem = ISA_PNP_NMEM;
254193323Sed	}
255202375Srdivacky	if (config->ic_nport > ISA_PNP_NPORT) {
256202375Srdivacky		printf("too many ISA I/O ranges (%d > %d)\n", config->ic_nport,
257193323Sed		    ISA_PNP_NPORT);
258198090Srdivacky		config->ic_nport = ISA_PNP_NPORT;
259193323Sed	}
260193323Sed	if (config->ic_nirq > ISA_PNP_NIRQ) {
261193323Sed		printf("too many ISA IRQs (%d > %d)\n", config->ic_nirq,
262193323Sed		    ISA_PNP_NIRQ);
263193323Sed		config->ic_nirq = ISA_PNP_NIRQ;
264193323Sed	}
265193323Sed	if (config->ic_ndrq > ISA_PNP_NDRQ) {
266193323Sed		printf("too many ISA DRQs (%d > %d)\n", config->ic_ndrq,
267193323Sed		    ISA_PNP_NDRQ);
268193323Sed		config->ic_ndrq = ISA_PNP_NDRQ;
269193323Sed	}
270193323Sed
271193323Sed	/*
272198090Srdivacky	 * Now program the resources.
273198090Srdivacky	 */
274198090Srdivacky	for (i = 0; i < config->ic_nmem; i++) {
275193323Sed		uint32_t start;
276193323Sed		uint32_t size;
277198090Srdivacky
278193323Sed		/* XXX: should handle memory control register, 32 bit memory */
279193323Sed		if (config->ic_mem[i].ir_size == 0) {
280193323Sed			pnp_write(PNP_MEM_BASE_HIGH(i), 0);
281193323Sed			pnp_write(PNP_MEM_BASE_LOW(i), 0);
282193323Sed			pnp_write(PNP_MEM_RANGE_HIGH(i), 0);
283193323Sed			pnp_write(PNP_MEM_RANGE_LOW(i), 0);
284193323Sed		} else {
285198090Srdivacky			start = config->ic_mem[i].ir_start;
286193323Sed			size =  config->ic_mem[i].ir_size;
287193323Sed			if (start & 0xff)
288193323Sed				panic("pnp_set_config: bogus memory assignment");
289193323Sed			pnp_write(PNP_MEM_BASE_HIGH(i), (start >> 16) & 0xff);
290193323Sed			pnp_write(PNP_MEM_BASE_LOW(i), (start >> 8) & 0xff);
291193323Sed			pnp_write(PNP_MEM_RANGE_HIGH(i), (size >> 16) & 0xff);
292193323Sed			pnp_write(PNP_MEM_RANGE_LOW(i), (size >> 8) & 0xff);
293193323Sed		}
294193323Sed	}
295193323Sed	for (; i < ISA_PNP_NMEM; i++) {
296193323Sed		pnp_write(PNP_MEM_BASE_HIGH(i), 0);
297193323Sed		pnp_write(PNP_MEM_BASE_LOW(i), 0);
298193323Sed		pnp_write(PNP_MEM_RANGE_HIGH(i), 0);
299193323Sed		pnp_write(PNP_MEM_RANGE_LOW(i), 0);
300193323Sed	}
301193323Sed
302193323Sed	for (i = 0; i < config->ic_nport; i++) {
303193323Sed		uint32_t start;
304193323Sed
305193323Sed		if (config->ic_port[i].ir_size == 0) {
306193323Sed			pnp_write(PNP_IO_BASE_HIGH(i), 0);
307193323Sed			pnp_write(PNP_IO_BASE_LOW(i), 0);
308193323Sed		} else {
309193323Sed			start = config->ic_port[i].ir_start;
310193323Sed			pnp_write(PNP_IO_BASE_HIGH(i), (start >> 8) & 0xff);
311193323Sed			pnp_write(PNP_IO_BASE_LOW(i), (start >> 0) & 0xff);
312193323Sed		}
313193323Sed	}
314193323Sed	for (; i < ISA_PNP_NPORT; i++) {
315193323Sed		pnp_write(PNP_IO_BASE_HIGH(i), 0);
316193323Sed		pnp_write(PNP_IO_BASE_LOW(i), 0);
317202375Srdivacky	}
318198090Srdivacky
319193323Sed	for (i = 0; i < config->ic_nirq; i++) {
320193323Sed		int irq;
321198090Srdivacky
322193323Sed		/* XXX: interrupt type */
323193323Sed		if (config->ic_irqmask[i] == 0) {
324202375Srdivacky			pnp_write(PNP_IRQ_LEVEL(i), 0);
325193323Sed			pnp_write(PNP_IRQ_TYPE(i), 2);
326193323Sed		} else {
327198090Srdivacky			irq = ffs(config->ic_irqmask[i]) - 1;
328193323Sed			pnp_write(PNP_IRQ_LEVEL(i), irq);
329193323Sed			pnp_write(PNP_IRQ_TYPE(i), 2); /* XXX */
330193323Sed		}
331193323Sed	}
332193323Sed	for (; i < ISA_PNP_NIRQ; i++) {
333193323Sed		/*
334193323Sed		 * IRQ 0 is not a valid interrupt selection and
335193323Sed		 * represents no interrupt selection.
336193323Sed		 */
337193323Sed		pnp_write(PNP_IRQ_LEVEL(i), 0);
338193323Sed		pnp_write(PNP_IRQ_TYPE(i), 2);
339193323Sed	}
340193323Sed
341193323Sed	for (i = 0; i < config->ic_ndrq; i++) {
342193323Sed		int drq;
343193323Sed
344193323Sed		if (config->ic_drqmask[i] == 0) {
345193323Sed			pnp_write(PNP_DMA_CHANNEL(i), 4);
346198892Srdivacky		} else {
347198892Srdivacky			drq = ffs(config->ic_drqmask[i]) - 1;
348202375Srdivacky			pnp_write(PNP_DMA_CHANNEL(i), drq);
349193323Sed		}
350193323Sed	}
351193323Sed	for (; i < ISA_PNP_NDRQ; i++) {
352193323Sed		/*
353193323Sed		 * DMA channel 4, the cascade channel is used to
354193323Sed		 * indicate no DMA channel is active.
355198090Srdivacky		 */
356193323Sed		pnp_write(PNP_DMA_CHANNEL(i), 4);
357193323Sed	}
358193323Sed
359193323Sed	pnp_write(PNP_ACTIVATE, enable ? 1 : 0);
360193323Sed
361193323Sed	/*
362193323Sed	 * Wake everyone up again, we are finished.
363193323Sed	 */
364193323Sed	pnp_write(PNP_CONFIG_CONTROL, PNP_CONFIG_CONTROL_WAIT_FOR_KEY);
365198090Srdivacky}
366193323Sed
367193323Sed/*
368193323Sed * Process quirks for a logical device.. The card must be in Config state.
369193323Sed */
370193323Sedvoid
371201360Srdivackypnp_check_quirks(uint32_t vendor_id, uint32_t logical_id, int ldn,
372201360Srdivacky    struct isa_config *config)
373201360Srdivacky{
374193323Sed	struct pnp_quirk *qp;
375201360Srdivacky
376201360Srdivacky	for (qp = &pnp_quirks[0]; qp->vendor_id; qp++) {
377201360Srdivacky		if (qp->vendor_id == vendor_id
378193323Sed		    && (qp->logical_id == 0 || qp->logical_id == logical_id)) {
379201360Srdivacky			switch (qp->type) {
380201360Srdivacky			case PNP_QUIRK_WRITE_REG:
381193323Sed				pnp_write(PNP_SET_LDN, ldn);
382201360Srdivacky				pnp_write(qp->arg1, qp->arg2);
383201360Srdivacky				break;
384201360Srdivacky			case PNP_QUIRK_EXTRA_IO:
385201360Srdivacky				if (config == NULL)
386201360Srdivacky					break;
387193323Sed				if (qp->arg1 != 0) {
388201360Srdivacky					config->ic_nport++;
389201360Srdivacky					config->ic_port[config->ic_nport - 1] = config->ic_port[0];
390201360Srdivacky					config->ic_port[config->ic_nport - 1].ir_start += qp->arg1;
391201360Srdivacky					config->ic_port[config->ic_nport - 1].ir_end += qp->arg1;
392201360Srdivacky				}
393201360Srdivacky				if (qp->arg2 != 0) {
394201360Srdivacky					config->ic_nport++;
395201360Srdivacky					config->ic_port[config->ic_nport - 1] = config->ic_port[0];
396201360Srdivacky					config->ic_port[config->ic_nport - 1].ir_start += qp->arg2;
397201360Srdivacky					config->ic_port[config->ic_nport - 1].ir_end += qp->arg2;
398201360Srdivacky				}
399193323Sed				break;
400193323Sed			}
401201360Srdivacky		}
402201360Srdivacky	}
403201360Srdivacky}
404201360Srdivacky
405201360Srdivacky/*
406201360Srdivacky * Scan Resource Data for Logical Devices.
407201360Srdivacky *
408201360Srdivacky * This function exits as soon as it gets an error reading *ANY*
409201360Srdivacky * Resource Data or it reaches the end of Resource Data.  In the first
410193323Sed * case the return value will be TRUE, FALSE otherwise.
411201360Srdivacky */
412201360Srdivackystatic int
413201360Srdivackypnp_create_devices(device_t parent, pnp_id *p, int csn,
414201360Srdivacky    u_char *resources, int len)
415201360Srdivacky{
416201360Srdivacky	u_char tag, *resp, *resinfo, *startres = 0;
417201360Srdivacky	int large_len, scanning = len, retval = FALSE;
418201360Srdivacky	uint32_t logical_id;
419201360Srdivacky	device_t dev = 0;
420201360Srdivacky	int ldn = 0;
421201360Srdivacky	struct pnp_set_config_arg *csnldn;
422201360Srdivacky	char buf[100];
423201360Srdivacky	char *desc = 0;
424201360Srdivacky
425201360Srdivacky	resp = resources;
426201360Srdivacky	while (scanning > 0) {
427201360Srdivacky		tag = *resp++;
428201360Srdivacky		scanning--;
429201360Srdivacky		if (PNP_RES_TYPE(tag) != 0) {
430201360Srdivacky			/* Large resource */
431201360Srdivacky			if (scanning < 2) {
432201360Srdivacky				scanning = 0;
433193323Sed				continue;
434201360Srdivacky			}
435201360Srdivacky			large_len = resp[0] + (resp[1] << 8);
436201360Srdivacky			resp += 2;
437201360Srdivacky
438201360Srdivacky			if (scanning < large_len) {
439201360Srdivacky				scanning = 0;
440201360Srdivacky				continue;
441201360Srdivacky			}
442201360Srdivacky			resinfo = resp;
443201360Srdivacky			resp += large_len;
444201360Srdivacky			scanning -= large_len;
445201360Srdivacky
446193323Sed			if (PNP_LRES_NUM(tag) == PNP_TAG_ID_ANSI) {
447201360Srdivacky				if (dev) {
448201360Srdivacky					/*
449198090Srdivacky					 * This is an optional device
450201360Srdivacky					 * indentifier string. Skipt it
451193323Sed					 * for now.
452201360Srdivacky					 */
453193323Sed					continue;
454193323Sed				}
455193323Sed				/* else mandately card identifier string */
456201360Srdivacky				if (large_len > sizeof(buf) - 1)
457201360Srdivacky					large_len = sizeof(buf) - 1;
458201360Srdivacky				bcopy(resinfo, buf, large_len);
459201360Srdivacky
460201360Srdivacky				/*
461201360Srdivacky				 * Trim trailing spaces.
462201360Srdivacky				 */
463201360Srdivacky				while (buf[large_len-1] == ' ')
464201360Srdivacky					large_len--;
465201360Srdivacky				buf[large_len] = '\0';
466193323Sed				desc = buf;
467201360Srdivacky				continue;
468201360Srdivacky			}
469201360Srdivacky
470201360Srdivacky			continue;
471201360Srdivacky		}
472201360Srdivacky
473193323Sed		/* Small resource */
474201360Srdivacky		if (scanning < PNP_SRES_LEN(tag)) {
475201360Srdivacky			scanning = 0;
476201360Srdivacky			continue;
477193323Sed		}
478193323Sed		resinfo = resp;
479201360Srdivacky		resp += PNP_SRES_LEN(tag);
480201360Srdivacky		scanning -= PNP_SRES_LEN(tag);;
481201360Srdivacky
482201360Srdivacky		switch (PNP_SRES_NUM(tag)) {
483201360Srdivacky		case PNP_TAG_LOGICAL_DEVICE:
484201360Srdivacky			/*
485201360Srdivacky			 * Parse the resources for the previous
486201360Srdivacky			 * logical device (if any).
487193323Sed			 */
488201360Srdivacky			if (startres) {
489201360Srdivacky				pnp_parse_resources(dev, startres,
490201360Srdivacky				    resinfo - startres - 1, ldn);
491201360Srdivacky				dev = 0;
492201360Srdivacky				startres = 0;
493201360Srdivacky			}
494201360Srdivacky
495201360Srdivacky			/*
496201360Srdivacky			 * A new logical device. Scan for end of
497201360Srdivacky			 * resources.
498201360Srdivacky			 */
499201360Srdivacky			bcopy(resinfo, &logical_id, 4);
500201360Srdivacky			pnp_check_quirks(p->vendor_id, logical_id, ldn, NULL);
501201360Srdivacky			dev = BUS_ADD_CHILD(parent, ISA_ORDER_PNP, NULL, -1);
502201360Srdivacky			if (desc)
503201360Srdivacky				device_set_desc_copy(dev, desc);
504201360Srdivacky			else
505201360Srdivacky				device_set_desc_copy(dev,
506201360Srdivacky				    pnp_eisaformat(logical_id));
507201360Srdivacky			isa_set_vendorid(dev, p->vendor_id);
508193323Sed			isa_set_serial(dev, p->serial);
509201360Srdivacky			isa_set_logicalid(dev, logical_id);
510193323Sed			isa_set_configattr(dev,
511193323Sed			    ISACFGATTR_CANDISABLE | ISACFGATTR_DYNAMIC);
512201360Srdivacky			csnldn = malloc(sizeof *csnldn, M_DEVBUF, M_NOWAIT);
513201360Srdivacky			if (!csnldn) {
514201360Srdivacky				device_printf(parent, "out of memory\n");
515201360Srdivacky				scanning = 0;
516193323Sed				break;
517201360Srdivacky			}
518193323Sed			csnldn->csn = csn;
519193323Sed			csnldn->ldn = ldn;
520201360Srdivacky			ISA_SET_CONFIG_CALLBACK(parent, dev, pnp_set_config,
521201360Srdivacky			    csnldn);
522201360Srdivacky			ldn++;
523201360Srdivacky			startres = resp;
524201360Srdivacky			break;
525201360Srdivacky
526201360Srdivacky		case PNP_TAG_END:
527201360Srdivacky			if (!startres) {
528201360Srdivacky				device_printf(parent, "malformed resources\n");
529201360Srdivacky				scanning = 0;
530201360Srdivacky				break;
531201360Srdivacky			}
532201360Srdivacky			pnp_parse_resources(dev, startres,
533201360Srdivacky			    resinfo - startres - 1, ldn);
534201360Srdivacky			dev = 0;
535201360Srdivacky			startres = 0;
536201360Srdivacky			scanning = 0;
537201360Srdivacky			break;
538201360Srdivacky
539193323Sed		default:
540201360Srdivacky			/* Skip this resource */
541201360Srdivacky			break;
542201360Srdivacky		}
543201360Srdivacky	}
544201360Srdivacky
545201360Srdivacky	return (retval);
546193323Sed}
547193323Sed
548201360Srdivacky/*
549201360Srdivacky * Read 'amount' bytes of resources from the card, allocating memory
550201360Srdivacky * as needed. If a buffer is already available, it should be passed in
551201360Srdivacky * '*resourcesp' and its length in '*spacep'. The number of resource
552201360Srdivacky * bytes already in the buffer should be passed in '*lenp'. The memory
553201360Srdivacky * allocated will be returned in '*resourcesp' with its size and the
554201360Srdivacky * number of bytes of resources in '*spacep' and '*lenp' respectively.
555201360Srdivacky *
556193323Sed * XXX: Multiple problems here, we forget to free() stuff in one
557201360Srdivacky * XXX: error return, and in another case we free (*resourcesp) but
558201360Srdivacky * XXX: don't tell the caller.
559201360Srdivacky */
560201360Srdivackystatic int
561201360Srdivackypnp_read_bytes(int amount, u_char **resourcesp, int *spacep, int *lenp)
562201360Srdivacky{
563201360Srdivacky	u_char *resources = *resourcesp;
564201360Srdivacky	u_char *newres;
565201360Srdivacky	int space = *spacep;
566201360Srdivacky	int len = *lenp;
567201360Srdivacky
568201360Srdivacky	if (space == 0) {
569201360Srdivacky		space = 1024;
570201360Srdivacky		resources = malloc(space, M_TEMP, M_NOWAIT);
571201360Srdivacky		if (!resources)
572201360Srdivacky			return (ENOMEM);
573201360Srdivacky	}
574201360Srdivacky
575201360Srdivacky	if (len + amount > space) {
576201360Srdivacky		int extra = 1024;
577201360Srdivacky		while (len + amount > space + extra)
578201360Srdivacky			extra += 1024;
579201360Srdivacky		newres = malloc(space + extra, M_TEMP, M_NOWAIT);
580201360Srdivacky		if (!newres) {
581201360Srdivacky			/* XXX: free resources */
582201360Srdivacky			return (ENOMEM);
583201360Srdivacky		}
584201360Srdivacky		bcopy(resources, newres, len);
585201360Srdivacky		free(resources, M_TEMP);
586201360Srdivacky		resources = newres;
587201360Srdivacky		space += extra;
588201360Srdivacky	}
589201360Srdivacky
590201360Srdivacky	if (pnp_get_resource_info(resources + len, amount) != amount)
591201360Srdivacky		return (EINVAL);
592193323Sed	len += amount;
593201360Srdivacky
594201360Srdivacky	*resourcesp = resources;
595201360Srdivacky	*spacep = space;
596201360Srdivacky	*lenp = len;
597201360Srdivacky
598201360Srdivacky	return (0);
599201360Srdivacky}
600201360Srdivacky
601201360Srdivacky/*
602201360Srdivacky * Read all resources from the card, allocating memory as needed. If a
603201360Srdivacky * buffer is already available, it should be passed in '*resourcesp'
604201360Srdivacky * and its length in '*spacep'. The memory allocated will be returned
605201360Srdivacky * in '*resourcesp' with its size and the number of bytes of resources
606201360Srdivacky * in '*spacep' and '*lenp' respectively.
607201360Srdivacky */
608201360Srdivackystatic int
609201360Srdivackypnp_read_resources(u_char **resourcesp, int *spacep, int *lenp)
610201360Srdivacky{
611201360Srdivacky	u_char *resources = *resourcesp;
612201360Srdivacky	int space = *spacep;
613201360Srdivacky	int len = 0;
614201360Srdivacky	int error, done;
615193323Sed	u_char tag;
616193323Sed
617193323Sed	error = 0;
618193323Sed	done = 0;
619193323Sed	while (!done) {
620201360Srdivacky		error = pnp_read_bytes(1, &resources, &space, &len);
621201360Srdivacky		if (error)
622201360Srdivacky			goto out;
623201360Srdivacky		tag = resources[len-1];
624201360Srdivacky		if (PNP_RES_TYPE(tag) == 0) {
625201360Srdivacky			/*
626201360Srdivacky			 * Small resource, read contents.
627193323Sed			 */
628201360Srdivacky			error = pnp_read_bytes(PNP_SRES_LEN(tag),
629201360Srdivacky			    &resources, &space, &len);
630201360Srdivacky			if (error)
631201360Srdivacky				goto out;
632201360Srdivacky			if (PNP_SRES_NUM(tag) == PNP_TAG_END)
633201360Srdivacky				done = 1;
634201360Srdivacky		} else {
635201360Srdivacky			/*
636201360Srdivacky			 * Large resource, read length and contents.
637201360Srdivacky			 */
638193323Sed			error = pnp_read_bytes(2, &resources, &space, &len);
639201360Srdivacky			if (error)
640201360Srdivacky				goto out;
641201360Srdivacky			error = pnp_read_bytes(resources[len-2]
642201360Srdivacky			    + (resources[len-1] << 8), &resources, &space,
643201360Srdivacky			    &len);
644201360Srdivacky			if (error)
645201360Srdivacky				goto out;
646201360Srdivacky		}
647201360Srdivacky	}
648201360Srdivacky
649201360Srdivacky out:
650201360Srdivacky	*resourcesp = resources;
651201360Srdivacky	*spacep = space;
652201360Srdivacky	*lenp = len;
653201360Srdivacky	return (error);
654193323Sed}
655201360Srdivacky
656201360Srdivacky/*
657201360Srdivacky * Run the isolation protocol. Use pnp_rd_port as the READ_DATA port
658201360Srdivacky * value (caller should try multiple READ_DATA locations before giving
659201360Srdivacky * up). Upon exiting, all cards are aware that they should use
660201360Srdivacky * pnp_rd_port as the READ_DATA port.
661201360Srdivacky *
662193323Sed * In the first pass, a csn is assigned to each board and pnp_id's
663193323Sed * are saved to an array, pnp_devices. In the second pass, each
664201360Srdivacky * card is woken up and the device configuration is called.
665201360Srdivacky */
666201360Srdivackystatic int
667201360Srdivackypnp_isolation_protocol(device_t parent)
668201360Srdivacky{
669201360Srdivacky	int csn;
670201360Srdivacky	pnp_id id;
671201360Srdivacky	int found = 0, len;
672201360Srdivacky	u_char *resources = 0;
673201360Srdivacky	int space = 0;
674201360Srdivacky	int error;
675201360Srdivacky#ifdef PC98
676201360Srdivacky	int n, necpnp;
677201360Srdivacky	u_char buffer[10];
678201360Srdivacky#endif
679201360Srdivacky
680201360Srdivacky	/*
681201360Srdivacky	 * Put all cards into the Sleep state so that we can clear
682201360Srdivacky	 * their CSNs.
683201360Srdivacky	 */
684201360Srdivacky	pnp_send_initiation_key();
685201360Srdivacky
686201360Srdivacky	/*
687201360Srdivacky	 * Clear the CSN for all cards.
688201360Srdivacky	 */
689201360Srdivacky	pnp_write(PNP_CONFIG_CONTROL, PNP_CONFIG_CONTROL_RESET_CSN);
690201360Srdivacky
691201360Srdivacky	/*
692201360Srdivacky	 * Move all cards to the Isolation state.
693201360Srdivacky	 */
694201360Srdivacky	pnp_write(PNP_WAKE, 0);
695201360Srdivacky
696201360Srdivacky	/*
697201360Srdivacky	 * Tell them where the read point is going to be this time.
698201360Srdivacky	 */
699201360Srdivacky	pnp_write(PNP_SET_RD_DATA, pnp_rd_port);
700201360Srdivacky
701201360Srdivacky	for (csn = 1; csn < PNP_MAX_CARDS; csn++) {
702201360Srdivacky		/*
703201360Srdivacky		 * Start the serial isolation protocol.
704201360Srdivacky		 */
705201360Srdivacky		outb(_PNP_ADDRESS, PNP_SERIAL_ISOLATION);
706201360Srdivacky		DELAY(1000);	/* Delay 1 msec */
707201360Srdivacky
708201360Srdivacky		if (pnp_get_serial(&id)) {
709201360Srdivacky			/*
710193323Sed			 * We have read the id from a card
711193323Sed			 * successfully. The card which won the
712201360Srdivacky			 * isolation protocol will be in Isolation
713198892Srdivacky			 * mode and all others will be in Sleep.
714193323Sed			 * Program the CSN of the isolated card
715193323Sed			 * (taking it to Config state) and read its
716193323Sed			 * resources, creating devices as we find
717193323Sed			 * logical devices on the card.
718193323Sed			 */
719193323Sed			pnp_write(PNP_SET_CSN, csn);
720198090Srdivacky#ifdef PC98
721193323Sed			if (bootverbose)
722193323Sed				printf("PnP Vendor ID = %x\n", id.vendor_id);
723201360Srdivacky			/* Check for NEC PnP (9 bytes serial). */
724193323Sed			for (n = necpnp = 0; necids[n].vendor_id; n++) {
725193323Sed				if (id.vendor_id == necids[n].vendor_id) {
726201360Srdivacky					necpnp = 1;
727193323Sed					break;
728193323Sed				}
729193323Sed			}
730200581Srdivacky			if (necpnp) {
731193323Sed				if (bootverbose)
732193323Sed					printf("An NEC-PnP card (%s).\n",
733193323Sed					    pnp_eisaformat(id.vendor_id));
734201360Srdivacky				/*  Read dummy 9 bytes serial area. */
735201360Srdivacky				pnp_get_resource_info(buffer, 9);
736201360Srdivacky			} else {
737201360Srdivacky				if (bootverbose)
738201360Srdivacky					printf("A Normal-ISA-PnP card (%s).\n",
739201360Srdivacky					    pnp_eisaformat(id.vendor_id));
740201360Srdivacky			}
741201360Srdivacky			if (bootverbose)
742201360Srdivacky				printf("Reading PnP configuration for %s.\n",
743201360Srdivacky				    pnp_eisaformat(id.vendor_id));
744201360Srdivacky#endif
745201360Srdivacky			error = pnp_read_resources(&resources, &space, &len);
746201360Srdivacky			if (error)
747201360Srdivacky				break;
748201360Srdivacky			pnp_create_devices(parent, &id, csn, resources, len);
749201360Srdivacky			found++;
750201360Srdivacky		} else
751201360Srdivacky			break;
752201360Srdivacky
753201360Srdivacky		/*
754201360Srdivacky		 * Put this card back to the Sleep state and
755201360Srdivacky		 * simultaneously move all cards which don't have a
756201360Srdivacky		 * CSN yet to Isolation state.
757201360Srdivacky		 */
758193323Sed		pnp_write(PNP_WAKE, 0);
759193323Sed	}
760193323Sed
761193323Sed	/*
762193323Sed	 * Unless we have chosen the wrong read port, all cards will
763193323Sed	 * be in Sleep state. Put them back into WaitForKey for
764193323Sed	 * now. Their resources will be programmed later.
765193323Sed	 */
766193323Sed	pnp_write(PNP_CONFIG_CONTROL, PNP_CONFIG_CONTROL_WAIT_FOR_KEY);
767193323Sed
768193323Sed	/*
769193323Sed	 * Cleanup.
770193323Sed	 */
771193323Sed	if (resources)
772193323Sed		free(resources, M_TEMP);
773201360Srdivacky
774193323Sed	return (found);
775198090Srdivacky}
776193323Sed
777193323Sed
778193323Sed/*
779193323Sed * pnp_identify()
780193323Sed *
781193323Sed * autoconfiguration of pnp devices. This routine just runs the
782201360Srdivacky * isolation protocol over several ports, until one is successful.
783201360Srdivacky *
784201360Srdivacky * may be called more than once ?
785201360Srdivacky *
786198090Srdivacky */
787198090Srdivacky
788201360Srdivackystatic void
789198090Srdivackypnp_identify(driver_t *driver, device_t parent)
790201360Srdivacky{
791193323Sed	int num_pnp_devs;
792193323Sed
793193323Sed	/* Try various READ_DATA ports from 0x203-0x3ff */
794193323Sed	for (pnp_rd_port = 0x80; (pnp_rd_port < 0xff); pnp_rd_port += 0x10) {
795193323Sed		if (bootverbose)
796193323Sed			printf("pnp_identify: Trying Read_Port at %x\n",
797193323Sed			    (pnp_rd_port << 2) | 0x3);
798193323Sed
799193323Sed		num_pnp_devs = pnp_isolation_protocol(parent);
800193323Sed		if (num_pnp_devs)
801193323Sed			break;
802193323Sed	}
803193323Sed	if (bootverbose)
804193323Sed		printf("PNP Identify complete\n");
805193323Sed}
806193323Sed
807193323Sedstatic device_method_t pnp_methods[] = {
808193323Sed	/* Device interface */
809193323Sed	DEVMETHOD(device_identify,	pnp_identify),
810193323Sed
811193323Sed	{ 0, 0 }
812193323Sed};
813193323Sed
814193323Sedstatic driver_t pnp_driver = {
815193323Sed	"pnp",
816193323Sed	pnp_methods,
817193323Sed	1,			/* no softc */
818193323Sed};
819193323Sed
820193323Sedstatic devclass_t pnp_devclass;
821193323Sed
822193323SedDRIVER_MODULE(pnp, isa, pnp_driver, pnp_devclass, 0, 0);
823193323Sed