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