pnp.c revision 115543
150769Sdfr/*
250769Sdfr * Copyright (c) 1996, Sujal M. Patel
350769Sdfr * All rights reserved.
450769Sdfr *
550769Sdfr * Redistribution and use in source and binary forms, with or without
650769Sdfr * modification, are permitted provided that the following conditions
750769Sdfr * are met:
850769Sdfr * 1. Redistributions of source code must retain the above copyright
950769Sdfr *    notice, this list of conditions and the following disclaimer.
1050769Sdfr * 2. Redistributions in binary form must reproduce the above copyright
1150769Sdfr *    notice, this list of conditions and the following disclaimer in the
1250769Sdfr *    documentation and/or other materials provided with the distribution.
1350769Sdfr *
1450769Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1550769Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1650769Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1750769Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1850769Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1950769Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2050769Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2150769Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2250769Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2350769Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2450769Sdfr * SUCH DAMAGE.
2550769Sdfr *
2650769Sdfr *	$FreeBSD: head/sys/isa/pnp.c 115543 2003-05-31 20:21:53Z phk $
2750769Sdfr *      from: pnp.c,v 1.11 1999/05/06 22:11:19 peter Exp
2850769Sdfr */
2950769Sdfr
3050769Sdfr#include <sys/param.h>
3150769Sdfr#include <sys/systm.h>
3250769Sdfr#include <sys/kernel.h>
3350769Sdfr#include <sys/module.h>
3450769Sdfr#include <sys/bus.h>
3550769Sdfr#include <sys/malloc.h>
3650769Sdfr#include <isa/isavar.h>
3750769Sdfr#include <isa/pnpreg.h>
3850769Sdfr#include <isa/pnpvar.h>
3965176Sdfr#include <machine/bus.h>
4050769Sdfr
4150769Sdfrtypedef struct _pnp_id {
4250769Sdfr	u_int32_t vendor_id;
4350769Sdfr	u_int32_t serial;
4450769Sdfr	u_char checksum;
4550769Sdfr} pnp_id;
4650769Sdfr
4750769Sdfrstruct pnp_set_config_arg {
4850769Sdfr	int	csn;		/* Card number to configure */
4950769Sdfr	int	ldn;		/* Logical device on card */
5050769Sdfr};
5150769Sdfr
5250769Sdfrstruct pnp_quirk {
5350769Sdfr	u_int32_t vendor_id;	/* Vendor of the card */
5450769Sdfr	u_int32_t logical_id;	/* ID of the device with quirk */
5550769Sdfr	int	type;
5650769Sdfr#define PNP_QUIRK_WRITE_REG	1 /* Need to write a pnp register  */
5762947Stanimura#define PNP_QUIRK_EXTRA_IO	2 /* Has extra io ports  */
5850769Sdfr	int	arg1;
5950769Sdfr	int	arg2;
6050769Sdfr};
6150769Sdfr
6250769Sdfrstruct pnp_quirk pnp_quirks[] = {
6350769Sdfr	/*
6450769Sdfr	 * The Gravis UltraSound needs register 0xf2 to be set to 0xff
6550769Sdfr	 * to enable power.
6650769Sdfr	 * XXX need to know the logical device id.
6750769Sdfr	 */
6850769Sdfr	{ 0x0100561e /* GRV0001 */,	0,
6950769Sdfr	  PNP_QUIRK_WRITE_REG,	0xf2,	 0xff },
7062947Stanimura	/*
7162947Stanimura	 * An emu8000 does not give us other than the first
7262947Stanimura	 * port.
7362947Stanimura	 */
7462947Stanimura	{ 0x26008c0e /* SB16 */,	0x21008c0e,
7562947Stanimura	  PNP_QUIRK_EXTRA_IO,	0x400,	 0x800 },
7662947Stanimura	{ 0x42008c0e /* SB32(CTL0042) */,	0x21008c0e,
7762947Stanimura	  PNP_QUIRK_EXTRA_IO,	0x400,	 0x800 },
7862947Stanimura	{ 0x44008c0e /* SB32(CTL0044) */,	0x21008c0e,
7962947Stanimura	  PNP_QUIRK_EXTRA_IO,	0x400,	 0x800 },
8062947Stanimura	{ 0x49008c0e /* SB32(CTL0049) */,	0x21008c0e,
8162947Stanimura	  PNP_QUIRK_EXTRA_IO,	0x400,	 0x800 },
8262947Stanimura	{ 0xf1008c0e /* SB32(CTL00f1) */,	0x21008c0e,
8362947Stanimura	  PNP_QUIRK_EXTRA_IO,	0x400,	 0x800 },
8462947Stanimura	{ 0xc1008c0e /* SB64(CTL00c1) */,	0x22008c0e,
8562947Stanimura	  PNP_QUIRK_EXTRA_IO,	0x400,	 0x800 },
8690234Stanimura	{ 0xc5008c0e /* SB64(CTL00c5) */,	0x22008c0e,
8790234Stanimura	  PNP_QUIRK_EXTRA_IO,	0x400,	 0x800 },
8862947Stanimura	{ 0xe4008c0e /* SB64(CTL00e4) */,	0x22008c0e,
8962947Stanimura	  PNP_QUIRK_EXTRA_IO,	0x400,	 0x800 },
9050769Sdfr
9150769Sdfr	{ 0 }
9250769Sdfr};
9350769Sdfr
94104142Snyan#ifdef PC98
95104142Snyan/* Some NEC PnP cards have 9 bytes serial code. */
96104142Snyanstatic pnp_id necids[] = {
97104142Snyan	{0x4180a3b8, 0xffffffff, 0x00},	/* PC-9801CB-B04 (NEC8041) */
98104142Snyan	{0x5181a3b8, 0xffffffff, 0x46},	/* PC-9821CB2-B04(NEC8151) */
99104142Snyan	{0x5182a3b8, 0xffffffff, 0xb8},	/* PC-9801-XX    (NEC8251) */
100104142Snyan	{0x9181a3b8, 0xffffffff, 0x00},	/* PC-9801-120   (NEC8191) */
101104142Snyan	{0, 0, 0}
102104142Snyan};
103104142Snyan#endif
104104142Snyan
10550769Sdfr#if 0
10650769Sdfr/*
10750769Sdfr * these entries are initialized using the autoconfig menu
10850769Sdfr * The struct is invalid (and must be initialized) if the first
10950769Sdfr * CSN is zero. The init code fills invalid entries with CSN 255
11050769Sdfr * which is not a supported value.
11150769Sdfr */
11250769Sdfr
11350769Sdfrstruct pnp_cinfo pnp_ldn_overrides[MAX_PNP_LDN] = {
11450769Sdfr    { 0 }
11550769Sdfr};
11650769Sdfr#endif
11750769Sdfr
11850769Sdfr/* The READ_DATA port that we are using currently */
11950769Sdfrstatic int pnp_rd_port;
12050769Sdfr
12150769Sdfrstatic void   pnp_send_initiation_key(void);
12250769Sdfrstatic int    pnp_get_serial(pnp_id *p);
12350769Sdfrstatic int    pnp_isolation_protocol(device_t parent);
12450769Sdfr
12552241Sdfrchar *
12652241Sdfrpnp_eisaformat(u_int32_t id)
12752241Sdfr{
12852241Sdfr	u_int8_t *data = (u_int8_t *) &id;
12952241Sdfr	static char idbuf[8];
13052241Sdfr	const char  hextoascii[] = "0123456789abcdef";
13152241Sdfr
13252241Sdfr	idbuf[0] = '@' + ((data[0] & 0x7c) >> 2);
13352241Sdfr	idbuf[1] = '@' + (((data[0] & 0x3) << 3) + ((data[1] & 0xe0) >> 5));
13452241Sdfr	idbuf[2] = '@' + (data[1] & 0x1f);
13552241Sdfr	idbuf[3] = hextoascii[(data[2] >> 4)];
13652241Sdfr	idbuf[4] = hextoascii[(data[2] & 0xf)];
13752241Sdfr	idbuf[5] = hextoascii[(data[3] >> 4)];
13852241Sdfr	idbuf[6] = hextoascii[(data[3] & 0xf)];
13952241Sdfr	idbuf[7] = 0;
14052241Sdfr	return(idbuf);
14152241Sdfr}
14252241Sdfr
14350769Sdfrstatic void
14450769Sdfrpnp_write(int d, u_char r)
14550769Sdfr{
14650769Sdfr	outb (_PNP_ADDRESS, d);
14750769Sdfr	outb (_PNP_WRITE_DATA, r);
14850769Sdfr}
14950769Sdfr
15050769Sdfr#if 0
15150769Sdfr
15250769Sdfrstatic u_char
15350769Sdfrpnp_read(int d)
15450769Sdfr{
15550769Sdfr	outb (_PNP_ADDRESS, d);
15650769Sdfr	return (inb(3 | (pnp_rd_port <<2)));
15750769Sdfr}
15850769Sdfr
15950769Sdfr#endif
16050769Sdfr
16150769Sdfr/*
16250769Sdfr * Send Initiation LFSR as described in "Plug and Play ISA Specification",
16350769Sdfr * Intel May 94.
16450769Sdfr */
16550769Sdfrstatic void
16650769Sdfrpnp_send_initiation_key()
16750769Sdfr{
16850769Sdfr	int cur, i;
16950769Sdfr
17050769Sdfr	/* Reset the LSFR */
17150769Sdfr	outb(_PNP_ADDRESS, 0);
17250769Sdfr	outb(_PNP_ADDRESS, 0); /* yes, we do need it twice! */
17350769Sdfr
17450769Sdfr	cur = 0x6a;
17550769Sdfr	outb(_PNP_ADDRESS, cur);
17650769Sdfr
17750769Sdfr	for (i = 1; i < 32; i++) {
17850769Sdfr		cur = (cur >> 1) | (((cur ^ (cur >> 1)) << 7) & 0xff);
17950769Sdfr		outb(_PNP_ADDRESS, cur);
18050769Sdfr	}
18150769Sdfr}
18250769Sdfr
18350769Sdfr
18450769Sdfr/*
18550769Sdfr * Get the device's serial number.  Returns 1 if the serial is valid.
18650769Sdfr */
18750769Sdfrstatic int
18850769Sdfrpnp_get_serial(pnp_id *p)
18950769Sdfr{
19050769Sdfr	int i, bit, valid = 0, sum = 0x6a;
19150769Sdfr	u_char *data = (u_char *)p;
19250769Sdfr
19350769Sdfr	bzero(data, sizeof(char) * 9);
19450769Sdfr	outb(_PNP_ADDRESS, PNP_SERIAL_ISOLATION);
19550769Sdfr	for (i = 0; i < 72; i++) {
19650769Sdfr		bit = inb((pnp_rd_port << 2) | 0x3) == 0x55;
19750769Sdfr		DELAY(250);	/* Delay 250 usec */
19850769Sdfr
19950769Sdfr		/* Can't Short Circuit the next evaluation, so 'and' is last */
20050769Sdfr		bit = (inb((pnp_rd_port << 2) | 0x3) == 0xaa) && bit;
20150769Sdfr		DELAY(250);	/* Delay 250 usec */
20250769Sdfr
20350769Sdfr		valid = valid || bit;
20450769Sdfr
20550769Sdfr		if (i < 64)
20650769Sdfr			sum = (sum >> 1) |
20750769Sdfr				(((sum ^ (sum >> 1) ^ bit) << 7) & 0xff);
20850769Sdfr
20950769Sdfr		data[i / 8] = (data[i / 8] >> 1) | (bit ? 0x80 : 0);
21050769Sdfr	}
21150769Sdfr
21250769Sdfr	valid = valid && (data[8] == sum);
21350769Sdfr
21450769Sdfr	return valid;
21550769Sdfr}
21650769Sdfr
21750769Sdfr/*
21850769Sdfr * Fill's the buffer with resource info from the device.
21952059Sdfr * Returns the number of characters read.
22050769Sdfr */
22150769Sdfrstatic int
22250769Sdfrpnp_get_resource_info(u_char *buffer, int len)
22350769Sdfr{
22452059Sdfr	int i, j, count;
22550769Sdfr	u_char temp;
22650769Sdfr
22752059Sdfr	count = 0;
22850769Sdfr	for (i = 0; i < len; i++) {
22950769Sdfr		outb(_PNP_ADDRESS, PNP_STATUS);
23050769Sdfr		for (j = 0; j < 100; j++) {
23150769Sdfr			if ((inb((pnp_rd_port << 2) | 0x3)) & 0x1)
23250769Sdfr				break;
23350769Sdfr			DELAY(1);
23450769Sdfr		}
23550769Sdfr		if (j == 100) {
23650769Sdfr			printf("PnP device failed to report resource data\n");
23752059Sdfr			return count;
23850769Sdfr		}
23950769Sdfr		outb(_PNP_ADDRESS, PNP_RESOURCE_DATA);
24050769Sdfr		temp = inb((pnp_rd_port << 2) | 0x3);
24150769Sdfr		if (buffer != NULL)
24250769Sdfr			buffer[i] = temp;
24352059Sdfr		count++;
24450769Sdfr	}
24552059Sdfr	return count;
24650769Sdfr}
24750769Sdfr
24850769Sdfr#if 0
24950769Sdfr/*
25050769Sdfr * write_pnp_parms initializes a logical device with the parms
25150769Sdfr * in d, and then activates the board if the last parameter is 1.
25250769Sdfr */
25350769Sdfr
25450769Sdfrstatic int
25550769Sdfrwrite_pnp_parms(struct pnp_cinfo *d, pnp_id *p, int ldn)
25650769Sdfr{
25750769Sdfr    int i, empty = -1 ;
25850769Sdfr
25950769Sdfr    pnp_write (SET_LDN, ldn );
26050769Sdfr    i = pnp_read(SET_LDN) ;
26150769Sdfr    if (i != ldn) {
26250769Sdfr	printf("Warning: LDN %d does not exist\n", ldn);
26350769Sdfr    }
26450769Sdfr    for (i = 0; i < 8; i++) {
26550769Sdfr	pnp_write(IO_CONFIG_BASE + i * 2, d->ic_port[i] >> 8 );
26650769Sdfr	pnp_write(IO_CONFIG_BASE + i * 2 + 1, d->ic_port[i] & 0xff );
26750769Sdfr    }
26850769Sdfr    for (i = 0; i < 4; i++) {
26950769Sdfr	pnp_write(MEM_CONFIG + i*8, (d->ic_mem[i].base >> 16) & 0xff );
27050769Sdfr	pnp_write(MEM_CONFIG + i*8+1, (d->ic_mem[i].base >> 8) & 0xff );
27150769Sdfr	pnp_write(MEM_CONFIG + i*8+2, d->ic_mem[i].control & 0xff );
27250769Sdfr	pnp_write(MEM_CONFIG + i*8+3, (d->ic_mem[i].range >> 16) & 0xff );
27350769Sdfr	pnp_write(MEM_CONFIG + i*8+4, (d->ic_mem[i].range >> 8) & 0xff );
27450769Sdfr    }
27550769Sdfr    for (i = 0; i < 2; i++) {
27650769Sdfr	pnp_write(IRQ_CONFIG + i*2    , d->irq[i] );
27750769Sdfr	pnp_write(IRQ_CONFIG + i*2 + 1, d->irq_type[i] );
27850769Sdfr	pnp_write(DRQ_CONFIG + i, d->drq[i] );
27950769Sdfr    }
28050769Sdfr    /*
28150769Sdfr     * store parameters read into the current kernel
28250769Sdfr     * so manual editing next time is easier
28350769Sdfr     */
28450769Sdfr    for (i = 0 ; i < MAX_PNP_LDN; i++) {
28550769Sdfr	if (pnp_ldn_overrides[i].csn == d->csn &&
28650769Sdfr		pnp_ldn_overrides[i].ldn == ldn) {
28750769Sdfr	    d->flags = pnp_ldn_overrides[i].flags ;
28850769Sdfr	    pnp_ldn_overrides[i] = *d ;
28950769Sdfr	    break ;
29050769Sdfr	} else if (pnp_ldn_overrides[i].csn < 1 ||
29150769Sdfr		pnp_ldn_overrides[i].csn == 255)
29250769Sdfr	    empty = i ;
29350769Sdfr    }
29450769Sdfr    if (i== MAX_PNP_LDN && empty != -1)
29550769Sdfr	pnp_ldn_overrides[empty] = *d;
29650769Sdfr
29750769Sdfr    /*
29850769Sdfr     * Here should really perform the range check, and
29950769Sdfr     * return a failure if not successful.
30050769Sdfr     */
30150769Sdfr    pnp_write (IO_RANGE_CHECK, 0);
30250769Sdfr    DELAY(1000); /* XXX is it really necessary ? */
30350769Sdfr    pnp_write (ACTIVATE, d->enable ? 1 : 0);
30450769Sdfr    DELAY(1000); /* XXX is it really necessary ? */
30550769Sdfr    return 1 ;
30650769Sdfr}
30750769Sdfr#endif
30850769Sdfr
30950769Sdfr/*
31050769Sdfr * This function is called after the bus has assigned resource
31150769Sdfr * locations for a logical device.
31250769Sdfr */
31350769Sdfrstatic void
31450769Sdfrpnp_set_config(void *arg, struct isa_config *config, int enable)
31550769Sdfr{
31650769Sdfr	int csn = ((struct pnp_set_config_arg *) arg)->csn;
31750769Sdfr	int ldn = ((struct pnp_set_config_arg *) arg)->ldn;
31850769Sdfr	int i;
31950769Sdfr
32050769Sdfr	/*
32150769Sdfr	 * First put all cards into Sleep state with the initiation
32250769Sdfr	 * key, then put our card into Config state.
32350769Sdfr	 */
32450769Sdfr	pnp_send_initiation_key();
32550769Sdfr	pnp_write(PNP_WAKE, csn);
32650769Sdfr
32750769Sdfr	/*
32850769Sdfr	 * Select our logical device so that we can program it.
32950769Sdfr	 */
33050769Sdfr	pnp_write(PNP_SET_LDN, ldn);
33150769Sdfr
33250769Sdfr	/*
33366840Smsmith	 * Constrain the number of resources we will try to program
33466840Smsmith	 */
33566840Smsmith	if (config->ic_nmem > ISA_PNP_NMEM) {
33666840Smsmith	    printf("too many ISA memory ranges (%d > %d)\n", config->ic_nmem, ISA_PNP_NMEM);
33766840Smsmith	    config->ic_nmem = ISA_PNP_NMEM;
33866840Smsmith	}
33966840Smsmith	if (config->ic_nport > ISA_PNP_NPORT) {
34066840Smsmith	    printf("too many ISA I/O ranges (%d > %d)\n", config->ic_nport, ISA_PNP_NPORT);
34166840Smsmith	    config->ic_nport = ISA_PNP_NPORT;
34266840Smsmith	}
34366840Smsmith	if (config->ic_nirq > ISA_PNP_NIRQ) {
34466840Smsmith	    printf("too many ISA IRQs (%d > %d)\n", config->ic_nirq, ISA_PNP_NIRQ);
34566840Smsmith	    config->ic_nirq = ISA_PNP_NIRQ;
34666840Smsmith	}
34766840Smsmith	if (config->ic_ndrq > ISA_PNP_NDRQ) {
34866840Smsmith	    printf("too many ISA DRQs (%d > %d)\n", config->ic_ndrq, ISA_PNP_NDRQ);
34966840Smsmith	    config->ic_ndrq = ISA_PNP_NDRQ;
35066840Smsmith	}
35166840Smsmith
35266840Smsmith	/*
35350769Sdfr	 * Now program the resources.
35450769Sdfr	 */
35550769Sdfr	for (i = 0; i < config->ic_nmem; i++) {
35683051Syokota		u_int32_t start;
35783051Syokota		u_int32_t size;
35883051Syokota
35983051Syokota		/* XXX: should handle memory control register, 32 bit memory */
36083051Syokota		if (config->ic_mem[i].ir_size == 0) {
36183051Syokota			pnp_write(PNP_MEM_BASE_HIGH(i), 0);
36283051Syokota			pnp_write(PNP_MEM_BASE_LOW(i), 0);
36383051Syokota			pnp_write(PNP_MEM_RANGE_HIGH(i), 0);
36483051Syokota			pnp_write(PNP_MEM_RANGE_LOW(i), 0);
36583051Syokota		} else {
36683051Syokota			start = config->ic_mem[i].ir_start;
36783051Syokota			size =  config->ic_mem[i].ir_size;
36883051Syokota			if (start & 0xff)
36983051Syokota				panic("pnp_set_config: bogus memory assignment");
37083051Syokota			pnp_write(PNP_MEM_BASE_HIGH(i), (start >> 16) & 0xff);
37183051Syokota			pnp_write(PNP_MEM_BASE_LOW(i), (start >> 8) & 0xff);
37283051Syokota			pnp_write(PNP_MEM_RANGE_HIGH(i), (size >> 16) & 0xff);
37383051Syokota			pnp_write(PNP_MEM_RANGE_LOW(i), (size >> 8) & 0xff);
37483051Syokota		}
37550769Sdfr	}
37666840Smsmith	for (; i < ISA_PNP_NMEM; i++) {
37750769Sdfr		pnp_write(PNP_MEM_BASE_HIGH(i), 0);
37850769Sdfr		pnp_write(PNP_MEM_BASE_LOW(i), 0);
37950769Sdfr		pnp_write(PNP_MEM_RANGE_HIGH(i), 0);
38050769Sdfr		pnp_write(PNP_MEM_RANGE_LOW(i), 0);
38150769Sdfr	}
38250769Sdfr
38350769Sdfr	for (i = 0; i < config->ic_nport; i++) {
38483051Syokota		u_int32_t start;
38583051Syokota
38683051Syokota		if (config->ic_port[i].ir_size == 0) {
38783051Syokota			pnp_write(PNP_IO_BASE_HIGH(i), 0);
38883051Syokota			pnp_write(PNP_IO_BASE_LOW(i), 0);
38983051Syokota		} else {
39083051Syokota			start = config->ic_port[i].ir_start;
39183051Syokota			pnp_write(PNP_IO_BASE_HIGH(i), (start >> 8) & 0xff);
39283051Syokota			pnp_write(PNP_IO_BASE_LOW(i), (start >> 0) & 0xff);
39383051Syokota		}
39450769Sdfr	}
39566840Smsmith	for (; i < ISA_PNP_NPORT; i++) {
39650769Sdfr		pnp_write(PNP_IO_BASE_HIGH(i), 0);
39750769Sdfr		pnp_write(PNP_IO_BASE_LOW(i), 0);
39850769Sdfr	}
39950769Sdfr
40050769Sdfr	for (i = 0; i < config->ic_nirq; i++) {
40183051Syokota		int irq;
40283051Syokota
40383051Syokota		/* XXX: interrupt type */
40483051Syokota		if (config->ic_irqmask[i] == 0) {
40583051Syokota			pnp_write(PNP_IRQ_LEVEL(i), 0);
40683051Syokota			pnp_write(PNP_IRQ_TYPE(i), 2);
40783051Syokota		} else {
40883051Syokota			irq = ffs(config->ic_irqmask[i]) - 1;
40983051Syokota			pnp_write(PNP_IRQ_LEVEL(i), irq);
41083051Syokota			pnp_write(PNP_IRQ_TYPE(i), 2); /* XXX */
41183051Syokota		}
41250769Sdfr	}
41366840Smsmith	for (; i < ISA_PNP_NIRQ; i++) {
41450769Sdfr		/*
41550769Sdfr		 * IRQ 0 is not a valid interrupt selection and
41650769Sdfr		 * represents no interrupt selection.
41750769Sdfr		 */
41850769Sdfr		pnp_write(PNP_IRQ_LEVEL(i), 0);
41983051Syokota		pnp_write(PNP_IRQ_TYPE(i), 2);
42050769Sdfr	}
42150769Sdfr
42250769Sdfr	for (i = 0; i < config->ic_ndrq; i++) {
42383051Syokota		int drq;
42483051Syokota
42583051Syokota		if (config->ic_drqmask[i] == 0) {
42683051Syokota			pnp_write(PNP_DMA_CHANNEL(i), 4);
42783051Syokota		} else {
42883051Syokota			drq = ffs(config->ic_drqmask[i]) - 1;
42983051Syokota			pnp_write(PNP_DMA_CHANNEL(i), drq);
43083051Syokota		}
43150769Sdfr	}
43266840Smsmith	for (; i < ISA_PNP_NDRQ; i++) {
43350769Sdfr		/*
43450769Sdfr		 * DMA channel 4, the cascade channel is used to
43550769Sdfr		 * indicate no DMA channel is active.
43650769Sdfr		 */
43750769Sdfr		pnp_write(PNP_DMA_CHANNEL(i), 4);
43850769Sdfr	}
43950769Sdfr
44050769Sdfr	pnp_write(PNP_ACTIVATE, enable ? 1 : 0);
44150769Sdfr
44250769Sdfr	/*
44350769Sdfr	 * Wake everyone up again, we are finished.
44450769Sdfr	 */
44550769Sdfr	pnp_write(PNP_CONFIG_CONTROL, PNP_CONFIG_CONTROL_WAIT_FOR_KEY);
44650769Sdfr}
44750769Sdfr
44850769Sdfr/*
44950769Sdfr * Process quirks for a logical device.. The card must be in Config state.
45050769Sdfr */
45162947Stanimuravoid
45262947Stanimurapnp_check_quirks(u_int32_t vendor_id, u_int32_t logical_id, int ldn, struct isa_config *config)
45350769Sdfr{
45450769Sdfr	struct pnp_quirk *qp;
45550769Sdfr
45650769Sdfr	for (qp = &pnp_quirks[0]; qp->vendor_id; qp++) {
45750769Sdfr		if (qp->vendor_id == vendor_id
45850769Sdfr		    && (qp->logical_id == 0
45950769Sdfr			|| qp->logical_id == logical_id)) {
46050769Sdfr			switch (qp->type) {
46150769Sdfr			case PNP_QUIRK_WRITE_REG:
46250769Sdfr				pnp_write(PNP_SET_LDN, ldn);
46350769Sdfr				pnp_write(qp->arg1, qp->arg2);
46450769Sdfr				break;
46562947Stanimura			case PNP_QUIRK_EXTRA_IO:
46662947Stanimura				if (config == NULL)
46762947Stanimura					break;
46862947Stanimura				if (qp->arg1 != 0) {
46962947Stanimura					config->ic_nport++;
47062947Stanimura					config->ic_port[config->ic_nport - 1] = config->ic_port[0];
47162947Stanimura					config->ic_port[config->ic_nport - 1].ir_start += qp->arg1;
47262947Stanimura					config->ic_port[config->ic_nport - 1].ir_end += qp->arg1;
47362947Stanimura				}
47462947Stanimura				if (qp->arg2 != 0) {
47562947Stanimura					config->ic_nport++;
47662947Stanimura					config->ic_port[config->ic_nport - 1] = config->ic_port[0];
47762947Stanimura					config->ic_port[config->ic_nport - 1].ir_start += qp->arg2;
47862947Stanimura					config->ic_port[config->ic_nport - 1].ir_end += qp->arg2;
47962947Stanimura				}
48062947Stanimura				break;
48150769Sdfr			}
48250769Sdfr		}
48350769Sdfr	}
48450769Sdfr}
48550769Sdfr
48650769Sdfr/*
48750769Sdfr * Scan Resource Data for Logical Devices.
48850769Sdfr *
48950769Sdfr * This function exits as soon as it gets an error reading *ANY*
49052059Sdfr * Resource Data or it reaches the end of Resource Data.  In the first
49150769Sdfr * case the return value will be TRUE, FALSE otherwise.
49250769Sdfr */
49350769Sdfrstatic int
49452059Sdfrpnp_create_devices(device_t parent, pnp_id *p, int csn,
49552059Sdfr		   u_char *resources, int len)
49650769Sdfr{
49752059Sdfr	u_char tag, *resp, *resinfo, *startres = 0;
49852059Sdfr	int large_len, scanning = len, retval = FALSE;
49950769Sdfr	u_int32_t logical_id;
50050769Sdfr	device_t dev = 0;
50150769Sdfr	int ldn = 0;
50250769Sdfr	struct pnp_set_config_arg *csnldn;
50352059Sdfr	char buf[100];
50450769Sdfr	char *desc = 0;
50550769Sdfr
50652059Sdfr	resp = resources;
50752059Sdfr	while (scanning > 0) {
50852059Sdfr		tag = *resp++;
50952059Sdfr		scanning--;
51052059Sdfr		if (PNP_RES_TYPE(tag) != 0) {
51152059Sdfr			/* Large resource */
51252059Sdfr			if (scanning < 2) {
51350769Sdfr				scanning = 0;
51450769Sdfr				continue;
51550769Sdfr			}
51652059Sdfr			large_len = resp[0] + (resp[1] << 8);
51752059Sdfr			resp += 2;
51850769Sdfr
51952059Sdfr			if (scanning < large_len) {
52050769Sdfr				scanning = 0;
52150769Sdfr				continue;
52250769Sdfr			}
52352059Sdfr			resinfo = resp;
52452059Sdfr			resp += large_len;
52552059Sdfr			scanning -= large_len;
52650769Sdfr
52750769Sdfr			if (PNP_LRES_NUM(tag) == PNP_TAG_ID_ANSI) {
52883504Syokota				if (dev) {
52983504Syokota					/*
53083504Syokota					 * This is an optional device
53183504Syokota					 * indentifier string. Skipt it
53283504Syokota					 * for now.
53383504Syokota					 */
53483504Syokota					continue;
53583504Syokota				}
53683504Syokota				/* else mandately card identifier string */
53752059Sdfr				if (large_len > sizeof(buf) - 1)
53852059Sdfr					large_len = sizeof(buf) - 1;
53952059Sdfr				bcopy(resinfo, buf, large_len);
54052059Sdfr
54150769Sdfr				/*
54252059Sdfr				 * Trim trailing spaces.
54350769Sdfr				 */
54452059Sdfr				while (buf[large_len-1] == ' ')
54552059Sdfr					large_len--;
54652059Sdfr				buf[large_len] = '\0';
54752059Sdfr				desc = buf;
54850769Sdfr				continue;
54950769Sdfr			}
55050769Sdfr
55152059Sdfr			continue;
55252059Sdfr		}
55352059Sdfr
55452059Sdfr		/* Small resource */
55552059Sdfr		if (scanning < PNP_SRES_LEN(tag)) {
55652059Sdfr			scanning = 0;
55752059Sdfr			continue;
55852059Sdfr		}
55952059Sdfr		resinfo = resp;
56052059Sdfr		resp += PNP_SRES_LEN(tag);
56152059Sdfr		scanning -= PNP_SRES_LEN(tag);;
56252059Sdfr
56352059Sdfr		switch (PNP_SRES_NUM(tag)) {
56452059Sdfr		case PNP_TAG_LOGICAL_DEVICE:
56552059Sdfr			/*
56652059Sdfr			 * Parse the resources for the previous
56752059Sdfr			 * logical device (if any).
56852059Sdfr			 */
56952059Sdfr			if (startres) {
57052059Sdfr				pnp_parse_resources(dev, startres,
57162947Stanimura						    resinfo - startres - 1,
57283051Syokota						    ldn);
57352059Sdfr				dev = 0;
57452059Sdfr				startres = 0;
57550769Sdfr			}
57650769Sdfr
57752059Sdfr			/*
57852059Sdfr			 * A new logical device. Scan for end of
57952059Sdfr			 * resources.
58052059Sdfr			 */
58152059Sdfr			bcopy(resinfo, &logical_id, 4);
58262947Stanimura			pnp_check_quirks(p->vendor_id, logical_id, ldn, NULL);
58352059Sdfr			dev = BUS_ADD_CHILD(parent, ISA_ORDER_PNP, NULL, -1);
58452059Sdfr			if (desc)
58552059Sdfr				device_set_desc_copy(dev, desc);
58683504Syokota			else
58783504Syokota				device_set_desc_copy(dev,
58883504Syokota						     pnp_eisaformat(logical_id));
58952059Sdfr			isa_set_vendorid(dev, p->vendor_id);
59052059Sdfr			isa_set_serial(dev, p->serial);
59152059Sdfr			isa_set_logicalid(dev, logical_id);
59283051Syokota			isa_set_configattr(dev,
59383051Syokota					   ISACFGATTR_CANDISABLE |
59483051Syokota					   ISACFGATTR_DYNAMIC);
59552059Sdfr			csnldn = malloc(sizeof *csnldn, M_DEVBUF, M_NOWAIT);
59652059Sdfr			if (!csnldn) {
59752059Sdfr				device_printf(parent,
59852059Sdfr					      "out of memory\n");
59950769Sdfr				scanning = 0;
60052059Sdfr				break;
60150769Sdfr			}
60252059Sdfr			csnldn->csn = csn;
60352059Sdfr			csnldn->ldn = ldn;
60452059Sdfr			ISA_SET_CONFIG_CALLBACK(parent, dev,
60552059Sdfr						pnp_set_config, csnldn);
60652059Sdfr			ldn++;
60752059Sdfr			startres = resp;
60852059Sdfr			break;
60952059Sdfr
61052059Sdfr		case PNP_TAG_END:
61152059Sdfr			if (!startres) {
61250769Sdfr				device_printf(parent,
61352059Sdfr					      "malformed resources\n");
61450769Sdfr				scanning = 0;
61550769Sdfr				break;
61650769Sdfr			}
61752059Sdfr			pnp_parse_resources(dev, startres,
61883051Syokota					    resinfo - startres - 1, ldn);
61952059Sdfr			dev = 0;
62052059Sdfr			startres = 0;
62152059Sdfr			scanning = 0;
62252059Sdfr			break;
62350769Sdfr
62452059Sdfr		default:
62552059Sdfr			/* Skip this resource */
62652059Sdfr			break;
62750769Sdfr		}
62850769Sdfr	}
62950769Sdfr
63050769Sdfr	return retval;
63150769Sdfr}
63250769Sdfr
63350769Sdfr/*
63452059Sdfr * Read 'amount' bytes of resources from the card, allocating memory
63552059Sdfr * as needed. If a buffer is already available, it should be passed in
63652059Sdfr * '*resourcesp' and its length in '*spacep'. The number of resource
63752059Sdfr * bytes already in the buffer should be passed in '*lenp'. The memory
63852059Sdfr * allocated will be returned in '*resourcesp' with its size and the
63952059Sdfr * number of bytes of resources in '*spacep' and '*lenp' respectively.
640105226Sphk *
641105226Sphk * XXX: Multiple problems here, we forget to free() stuff in one
642105226Sphk * XXX: error return, and in another case we free (*resourcesp) but
643105226Sphk * XXX: don't tell the caller.
64452059Sdfr */
64552059Sdfrstatic int
64652059Sdfrpnp_read_bytes(int amount, u_char **resourcesp, int *spacep, int *lenp)
64752059Sdfr{
64852059Sdfr	u_char *resources = *resourcesp;
64952059Sdfr	u_char *newres;
65052059Sdfr	int space = *spacep;
65152059Sdfr	int len = *lenp;
65252059Sdfr
65352059Sdfr	if (space == 0) {
65452059Sdfr		space = 1024;
65552059Sdfr		resources = malloc(space, M_TEMP, M_NOWAIT);
65652059Sdfr		if (!resources)
65752059Sdfr			return ENOMEM;
65852059Sdfr	}
65952059Sdfr
66052059Sdfr	if (len + amount > space) {
66152059Sdfr		int extra = 1024;
66252059Sdfr		while (len + amount > space + extra)
66352059Sdfr			extra += 1024;
66452059Sdfr		newres = malloc(space + extra, M_TEMP, M_NOWAIT);
665115543Sphk		if (!newres) {
666115543Sphk			/* XXX: free resources */
66752059Sdfr			return ENOMEM;
668115543Sphk		}
66952059Sdfr		bcopy(resources, newres, len);
67052059Sdfr		free(resources, M_TEMP);
67152059Sdfr		resources = newres;
67252059Sdfr		space += extra;
67352059Sdfr	}
67452059Sdfr
67552059Sdfr	if (pnp_get_resource_info(resources + len, amount) != amount)
67652059Sdfr		return EINVAL;
67752059Sdfr	len += amount;
67852059Sdfr
67952059Sdfr	*resourcesp = resources;
68052059Sdfr	*spacep = space;
68152059Sdfr	*lenp = len;
68252059Sdfr
68352059Sdfr	return 0;
68452059Sdfr}
68552059Sdfr
68652059Sdfr/*
68752059Sdfr * Read all resources from the card, allocating memory as needed. If a
68852059Sdfr * buffer is already available, it should be passed in '*resourcesp'
68952059Sdfr * and its length in '*spacep'. The memory allocated will be returned
69052059Sdfr * in '*resourcesp' with its size and the number of bytes of resources
69152059Sdfr * in '*spacep' and '*lenp' respectively.
69252059Sdfr */
69352059Sdfrstatic int
69452059Sdfrpnp_read_resources(u_char **resourcesp, int *spacep, int *lenp)
69552059Sdfr{
69652059Sdfr	u_char *resources = *resourcesp;
69752059Sdfr	int space = *spacep;
69852059Sdfr	int len = 0;
69952059Sdfr	int error, done;
70052059Sdfr	u_char tag;
70152059Sdfr
70252059Sdfr	error = 0;
70352059Sdfr	done = 0;
70452059Sdfr	while (!done) {
70552059Sdfr		error = pnp_read_bytes(1, &resources, &space, &len);
70652059Sdfr		if (error)
70752059Sdfr			goto out;
70852059Sdfr		tag = resources[len-1];
70952059Sdfr		if (PNP_RES_TYPE(tag) == 0) {
71052059Sdfr			/*
71152059Sdfr			 * Small resource, read contents.
71252059Sdfr			 */
71352059Sdfr			error = pnp_read_bytes(PNP_SRES_LEN(tag),
71452059Sdfr					       &resources, &space, &len);
71552059Sdfr			if (error)
71652059Sdfr				goto out;
71752059Sdfr			if (PNP_SRES_NUM(tag) == PNP_TAG_END)
71852059Sdfr				done = 1;
71952059Sdfr		} else {
72052059Sdfr			/*
72152059Sdfr			 * Large resource, read length and contents.
72252059Sdfr			 */
72352059Sdfr			error = pnp_read_bytes(2, &resources, &space, &len);
72452059Sdfr			if (error)
72552059Sdfr				goto out;
72652059Sdfr			error = pnp_read_bytes(resources[len-2]
72752059Sdfr					       + (resources[len-1] << 8),
72852059Sdfr					       &resources, &space, &len);
72952059Sdfr			if (error)
73052059Sdfr				goto out;
73152059Sdfr		}
73252059Sdfr	}
73352059Sdfr
73452059Sdfr out:
73552059Sdfr	*resourcesp = resources;
73652059Sdfr	*spacep = space;
73752059Sdfr	*lenp = len;
73852059Sdfr	return error;
73952059Sdfr}
74052059Sdfr
74152059Sdfr/*
74250769Sdfr * Run the isolation protocol. Use pnp_rd_port as the READ_DATA port
74350769Sdfr * value (caller should try multiple READ_DATA locations before giving
74450769Sdfr * up). Upon exiting, all cards are aware that they should use
74550769Sdfr * pnp_rd_port as the READ_DATA port.
74650769Sdfr *
74750769Sdfr * In the first pass, a csn is assigned to each board and pnp_id's
74850769Sdfr * are saved to an array, pnp_devices. In the second pass, each
74950769Sdfr * card is woken up and the device configuration is called.
75050769Sdfr */
75150769Sdfrstatic int
75250769Sdfrpnp_isolation_protocol(device_t parent)
75350769Sdfr{
75450769Sdfr	int csn;
75550769Sdfr	pnp_id id;
75652059Sdfr	int found = 0, len;
75752059Sdfr	u_char *resources = 0;
75852059Sdfr	int space = 0;
75952059Sdfr	int error;
760104142Snyan#ifdef PC98
761104142Snyan	int n, necpnp;
762104142Snyan	u_char buffer[10];
763104142Snyan#endif
76450769Sdfr
76550769Sdfr	/*
76650769Sdfr	 * Put all cards into the Sleep state so that we can clear
76750769Sdfr	 * their CSNs.
76850769Sdfr	 */
76950769Sdfr	pnp_send_initiation_key();
77050769Sdfr
77150769Sdfr	/*
77250769Sdfr	 * Clear the CSN for all cards.
77350769Sdfr	 */
77450769Sdfr	pnp_write(PNP_CONFIG_CONTROL, PNP_CONFIG_CONTROL_RESET_CSN);
77550769Sdfr
77650769Sdfr	/*
77750769Sdfr	 * Move all cards to the Isolation state.
77850769Sdfr	 */
77950769Sdfr	pnp_write(PNP_WAKE, 0);
78050769Sdfr
78150769Sdfr	/*
78250769Sdfr	 * Tell them where the read point is going to be this time.
78350769Sdfr	 */
78450769Sdfr	pnp_write(PNP_SET_RD_DATA, pnp_rd_port);
78550769Sdfr
78650769Sdfr	for (csn = 1; csn < PNP_MAX_CARDS; csn++) {
78750769Sdfr		/*
78850769Sdfr		 * Start the serial isolation protocol.
78950769Sdfr		 */
79050769Sdfr		outb(_PNP_ADDRESS, PNP_SERIAL_ISOLATION);
79150769Sdfr		DELAY(1000);	/* Delay 1 msec */
79250769Sdfr
79350769Sdfr		if (pnp_get_serial(&id)) {
79450769Sdfr			/*
79550769Sdfr			 * We have read the id from a card
79650769Sdfr			 * successfully. The card which won the
79750769Sdfr			 * isolation protocol will be in Isolation
79852059Sdfr			 * mode and all others will be in Sleep.
79950769Sdfr			 * Program the CSN of the isolated card
80050769Sdfr			 * (taking it to Config state) and read its
80150769Sdfr			 * resources, creating devices as we find
80250769Sdfr			 * logical devices on the card.
80350769Sdfr			 */
80450769Sdfr			pnp_write(PNP_SET_CSN, csn);
805104142Snyan#ifdef PC98
806104142Snyan			if (bootverbose)
807104142Snyan				printf("PnP Vendor ID = %x\n", id.vendor_id);
808104142Snyan			/* Check for NEC PnP (9 bytes serial). */
809104142Snyan			for (n = necpnp = 0; necids[n].vendor_id; n++) {
810104142Snyan				if (id.vendor_id == necids[n].vendor_id) {
811104142Snyan					necpnp = 1;
812104142Snyan					break;
813104142Snyan				}
814104142Snyan			}
815104142Snyan			if (necpnp) {
816104142Snyan				if (bootverbose)
817104142Snyan					printf("It seems to NEC-PnP card (%s).\n",
818104142Snyan					       pnp_eisaformat(id.vendor_id));
819104142Snyan				/*  Read dummy 9 bytes serial area. */
820104142Snyan				pnp_get_resource_info(buffer, 9);
821104142Snyan			} else {
822104142Snyan				if (bootverbose)
823104142Snyan					printf("It seems to Normal-ISA-PnP card (%s).\n",
824104142Snyan					       pnp_eisaformat(id.vendor_id));
825104142Snyan			}
826104142Snyan			if (bootverbose)
827104142Snyan				printf("Reading PnP configuration for %s.\n",
828104142Snyan				       pnp_eisaformat(id.vendor_id));
829104142Snyan#endif
83052059Sdfr			error = pnp_read_resources(&resources,
83152059Sdfr						   &space,
83252059Sdfr						   &len);
83352059Sdfr			if (error)
83452059Sdfr				break;
83552059Sdfr			pnp_create_devices(parent, &id, csn,
83652059Sdfr					   resources, len);
83750769Sdfr			found++;
83850769Sdfr		} else
83950769Sdfr			break;
84050769Sdfr
84150769Sdfr		/*
84250769Sdfr		 * Put this card back to the Sleep state and
84350769Sdfr		 * simultaneously move all cards which don't have a
84450769Sdfr		 * CSN yet to Isolation state.
84550769Sdfr		 */
84650769Sdfr		pnp_write(PNP_WAKE, 0);
84750769Sdfr	}
84850769Sdfr
84950769Sdfr	/*
85050769Sdfr	 * Unless we have chosen the wrong read port, all cards will
85150769Sdfr	 * be in Sleep state. Put them back into WaitForKey for
85250769Sdfr	 * now. Their resources will be programmed later.
85350769Sdfr	 */
85450769Sdfr	pnp_write(PNP_CONFIG_CONTROL, PNP_CONFIG_CONTROL_WAIT_FOR_KEY);
85550769Sdfr
85652059Sdfr	/*
85752059Sdfr	 * Cleanup.
85852059Sdfr	 */
85952059Sdfr	if (resources)
86052059Sdfr		free(resources, M_TEMP);
86152059Sdfr
86250769Sdfr	return found;
86350769Sdfr}
86450769Sdfr
86550769Sdfr
86650769Sdfr/*
86750769Sdfr * pnp_identify()
86850769Sdfr *
86950769Sdfr * autoconfiguration of pnp devices. This routine just runs the
87050769Sdfr * isolation protocol over several ports, until one is successful.
87150769Sdfr *
87250769Sdfr * may be called more than once ?
87350769Sdfr *
87450769Sdfr */
87550769Sdfr
87650769Sdfrstatic void
87750769Sdfrpnp_identify(driver_t *driver, device_t parent)
87850769Sdfr{
87950769Sdfr	int num_pnp_devs;
88050769Sdfr
88150769Sdfr#if 0
88250769Sdfr	if (pnp_ldn_overrides[0].csn == 0) {
88350769Sdfr		if (bootverbose)
88450769Sdfr			printf("Initializing PnP override table\n");
88550769Sdfr		bzero (pnp_ldn_overrides, sizeof(pnp_ldn_overrides));
88650769Sdfr		pnp_ldn_overrides[0].csn = 255 ;
88750769Sdfr	}
88850769Sdfr#endif
88950769Sdfr
89050769Sdfr	/* Try various READ_DATA ports from 0x203-0x3ff */
89150769Sdfr	for (pnp_rd_port = 0x80; (pnp_rd_port < 0xff); pnp_rd_port += 0x10) {
89250769Sdfr		if (bootverbose)
89350769Sdfr			printf("Trying Read_Port at %x\n", (pnp_rd_port << 2) | 0x3);
89450769Sdfr
89550769Sdfr		num_pnp_devs = pnp_isolation_protocol(parent);
89650769Sdfr		if (num_pnp_devs)
89750769Sdfr			break;
89850769Sdfr	}
89950769Sdfr}
90050769Sdfr
90150769Sdfrstatic device_method_t pnp_methods[] = {
90250769Sdfr	/* Device interface */
90350769Sdfr	DEVMETHOD(device_identify,	pnp_identify),
90450769Sdfr
90550769Sdfr	{ 0, 0 }
90650769Sdfr};
90750769Sdfr
90850769Sdfrstatic driver_t pnp_driver = {
90950769Sdfr	"pnp",
91050769Sdfr	pnp_methods,
91150769Sdfr	1,			/* no softc */
91250769Sdfr};
91350769Sdfr
91450769Sdfrstatic devclass_t pnp_devclass;
91550769Sdfr
91650769SdfrDRIVER_MODULE(pnp, isa, pnp_driver, pnp_devclass, 0, 0);
917