pnp.c revision 83051
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 83051 2001-09-05 03:54:33Z yokota $ 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 }, 8662947Stanimura { 0xe4008c0e /* SB64(CTL00e4) */, 0x22008c0e, 8762947Stanimura PNP_QUIRK_EXTRA_IO, 0x400, 0x800 }, 8850769Sdfr 8950769Sdfr { 0 } 9050769Sdfr}; 9150769Sdfr 9250769Sdfr#if 0 9350769Sdfr/* 9450769Sdfr * these entries are initialized using the autoconfig menu 9550769Sdfr * The struct is invalid (and must be initialized) if the first 9650769Sdfr * CSN is zero. The init code fills invalid entries with CSN 255 9750769Sdfr * which is not a supported value. 9850769Sdfr */ 9950769Sdfr 10050769Sdfrstruct pnp_cinfo pnp_ldn_overrides[MAX_PNP_LDN] = { 10150769Sdfr { 0 } 10250769Sdfr}; 10350769Sdfr#endif 10450769Sdfr 10550769Sdfr/* The READ_DATA port that we are using currently */ 10650769Sdfrstatic int pnp_rd_port; 10750769Sdfr 10850769Sdfrstatic void pnp_send_initiation_key(void); 10950769Sdfrstatic int pnp_get_serial(pnp_id *p); 11050769Sdfrstatic int pnp_isolation_protocol(device_t parent); 11150769Sdfr 11252241Sdfrchar * 11352241Sdfrpnp_eisaformat(u_int32_t id) 11452241Sdfr{ 11552241Sdfr u_int8_t *data = (u_int8_t *) &id; 11652241Sdfr static char idbuf[8]; 11752241Sdfr const char hextoascii[] = "0123456789abcdef"; 11852241Sdfr 11952241Sdfr idbuf[0] = '@' + ((data[0] & 0x7c) >> 2); 12052241Sdfr idbuf[1] = '@' + (((data[0] & 0x3) << 3) + ((data[1] & 0xe0) >> 5)); 12152241Sdfr idbuf[2] = '@' + (data[1] & 0x1f); 12252241Sdfr idbuf[3] = hextoascii[(data[2] >> 4)]; 12352241Sdfr idbuf[4] = hextoascii[(data[2] & 0xf)]; 12452241Sdfr idbuf[5] = hextoascii[(data[3] >> 4)]; 12552241Sdfr idbuf[6] = hextoascii[(data[3] & 0xf)]; 12652241Sdfr idbuf[7] = 0; 12752241Sdfr return(idbuf); 12852241Sdfr} 12952241Sdfr 13050769Sdfrstatic void 13150769Sdfrpnp_write(int d, u_char r) 13250769Sdfr{ 13350769Sdfr outb (_PNP_ADDRESS, d); 13450769Sdfr outb (_PNP_WRITE_DATA, r); 13550769Sdfr} 13650769Sdfr 13750769Sdfr#if 0 13850769Sdfr 13950769Sdfrstatic u_char 14050769Sdfrpnp_read(int d) 14150769Sdfr{ 14250769Sdfr outb (_PNP_ADDRESS, d); 14350769Sdfr return (inb(3 | (pnp_rd_port <<2))); 14450769Sdfr} 14550769Sdfr 14650769Sdfr#endif 14750769Sdfr 14850769Sdfr/* 14950769Sdfr * Send Initiation LFSR as described in "Plug and Play ISA Specification", 15050769Sdfr * Intel May 94. 15150769Sdfr */ 15250769Sdfrstatic void 15350769Sdfrpnp_send_initiation_key() 15450769Sdfr{ 15550769Sdfr int cur, i; 15650769Sdfr 15750769Sdfr /* Reset the LSFR */ 15850769Sdfr outb(_PNP_ADDRESS, 0); 15950769Sdfr outb(_PNP_ADDRESS, 0); /* yes, we do need it twice! */ 16050769Sdfr 16150769Sdfr cur = 0x6a; 16250769Sdfr outb(_PNP_ADDRESS, cur); 16350769Sdfr 16450769Sdfr for (i = 1; i < 32; i++) { 16550769Sdfr cur = (cur >> 1) | (((cur ^ (cur >> 1)) << 7) & 0xff); 16650769Sdfr outb(_PNP_ADDRESS, cur); 16750769Sdfr } 16850769Sdfr} 16950769Sdfr 17050769Sdfr 17150769Sdfr/* 17250769Sdfr * Get the device's serial number. Returns 1 if the serial is valid. 17350769Sdfr */ 17450769Sdfrstatic int 17550769Sdfrpnp_get_serial(pnp_id *p) 17650769Sdfr{ 17750769Sdfr int i, bit, valid = 0, sum = 0x6a; 17850769Sdfr u_char *data = (u_char *)p; 17950769Sdfr 18050769Sdfr bzero(data, sizeof(char) * 9); 18150769Sdfr outb(_PNP_ADDRESS, PNP_SERIAL_ISOLATION); 18250769Sdfr for (i = 0; i < 72; i++) { 18350769Sdfr bit = inb((pnp_rd_port << 2) | 0x3) == 0x55; 18450769Sdfr DELAY(250); /* Delay 250 usec */ 18550769Sdfr 18650769Sdfr /* Can't Short Circuit the next evaluation, so 'and' is last */ 18750769Sdfr bit = (inb((pnp_rd_port << 2) | 0x3) == 0xaa) && bit; 18850769Sdfr DELAY(250); /* Delay 250 usec */ 18950769Sdfr 19050769Sdfr valid = valid || bit; 19150769Sdfr 19250769Sdfr if (i < 64) 19350769Sdfr sum = (sum >> 1) | 19450769Sdfr (((sum ^ (sum >> 1) ^ bit) << 7) & 0xff); 19550769Sdfr 19650769Sdfr data[i / 8] = (data[i / 8] >> 1) | (bit ? 0x80 : 0); 19750769Sdfr } 19850769Sdfr 19950769Sdfr valid = valid && (data[8] == sum); 20050769Sdfr 20150769Sdfr return valid; 20250769Sdfr} 20350769Sdfr 20450769Sdfr/* 20550769Sdfr * Fill's the buffer with resource info from the device. 20652059Sdfr * Returns the number of characters read. 20750769Sdfr */ 20850769Sdfrstatic int 20950769Sdfrpnp_get_resource_info(u_char *buffer, int len) 21050769Sdfr{ 21152059Sdfr int i, j, count; 21250769Sdfr u_char temp; 21350769Sdfr 21452059Sdfr count = 0; 21550769Sdfr for (i = 0; i < len; i++) { 21650769Sdfr outb(_PNP_ADDRESS, PNP_STATUS); 21750769Sdfr for (j = 0; j < 100; j++) { 21850769Sdfr if ((inb((pnp_rd_port << 2) | 0x3)) & 0x1) 21950769Sdfr break; 22050769Sdfr DELAY(1); 22150769Sdfr } 22250769Sdfr if (j == 100) { 22350769Sdfr printf("PnP device failed to report resource data\n"); 22452059Sdfr return count; 22550769Sdfr } 22650769Sdfr outb(_PNP_ADDRESS, PNP_RESOURCE_DATA); 22750769Sdfr temp = inb((pnp_rd_port << 2) | 0x3); 22850769Sdfr if (buffer != NULL) 22950769Sdfr buffer[i] = temp; 23052059Sdfr count++; 23150769Sdfr } 23252059Sdfr return count; 23350769Sdfr} 23450769Sdfr 23550769Sdfr#if 0 23650769Sdfr/* 23750769Sdfr * write_pnp_parms initializes a logical device with the parms 23850769Sdfr * in d, and then activates the board if the last parameter is 1. 23950769Sdfr */ 24050769Sdfr 24150769Sdfrstatic int 24250769Sdfrwrite_pnp_parms(struct pnp_cinfo *d, pnp_id *p, int ldn) 24350769Sdfr{ 24450769Sdfr int i, empty = -1 ; 24550769Sdfr 24650769Sdfr pnp_write (SET_LDN, ldn ); 24750769Sdfr i = pnp_read(SET_LDN) ; 24850769Sdfr if (i != ldn) { 24950769Sdfr printf("Warning: LDN %d does not exist\n", ldn); 25050769Sdfr } 25150769Sdfr for (i = 0; i < 8; i++) { 25250769Sdfr pnp_write(IO_CONFIG_BASE + i * 2, d->ic_port[i] >> 8 ); 25350769Sdfr pnp_write(IO_CONFIG_BASE + i * 2 + 1, d->ic_port[i] & 0xff ); 25450769Sdfr } 25550769Sdfr for (i = 0; i < 4; i++) { 25650769Sdfr pnp_write(MEM_CONFIG + i*8, (d->ic_mem[i].base >> 16) & 0xff ); 25750769Sdfr pnp_write(MEM_CONFIG + i*8+1, (d->ic_mem[i].base >> 8) & 0xff ); 25850769Sdfr pnp_write(MEM_CONFIG + i*8+2, d->ic_mem[i].control & 0xff ); 25950769Sdfr pnp_write(MEM_CONFIG + i*8+3, (d->ic_mem[i].range >> 16) & 0xff ); 26050769Sdfr pnp_write(MEM_CONFIG + i*8+4, (d->ic_mem[i].range >> 8) & 0xff ); 26150769Sdfr } 26250769Sdfr for (i = 0; i < 2; i++) { 26350769Sdfr pnp_write(IRQ_CONFIG + i*2 , d->irq[i] ); 26450769Sdfr pnp_write(IRQ_CONFIG + i*2 + 1, d->irq_type[i] ); 26550769Sdfr pnp_write(DRQ_CONFIG + i, d->drq[i] ); 26650769Sdfr } 26750769Sdfr /* 26850769Sdfr * store parameters read into the current kernel 26950769Sdfr * so manual editing next time is easier 27050769Sdfr */ 27150769Sdfr for (i = 0 ; i < MAX_PNP_LDN; i++) { 27250769Sdfr if (pnp_ldn_overrides[i].csn == d->csn && 27350769Sdfr pnp_ldn_overrides[i].ldn == ldn) { 27450769Sdfr d->flags = pnp_ldn_overrides[i].flags ; 27550769Sdfr pnp_ldn_overrides[i] = *d ; 27650769Sdfr break ; 27750769Sdfr } else if (pnp_ldn_overrides[i].csn < 1 || 27850769Sdfr pnp_ldn_overrides[i].csn == 255) 27950769Sdfr empty = i ; 28050769Sdfr } 28150769Sdfr if (i== MAX_PNP_LDN && empty != -1) 28250769Sdfr pnp_ldn_overrides[empty] = *d; 28350769Sdfr 28450769Sdfr /* 28550769Sdfr * Here should really perform the range check, and 28650769Sdfr * return a failure if not successful. 28750769Sdfr */ 28850769Sdfr pnp_write (IO_RANGE_CHECK, 0); 28950769Sdfr DELAY(1000); /* XXX is it really necessary ? */ 29050769Sdfr pnp_write (ACTIVATE, d->enable ? 1 : 0); 29150769Sdfr DELAY(1000); /* XXX is it really necessary ? */ 29250769Sdfr return 1 ; 29350769Sdfr} 29450769Sdfr#endif 29550769Sdfr 29650769Sdfr/* 29750769Sdfr * This function is called after the bus has assigned resource 29850769Sdfr * locations for a logical device. 29950769Sdfr */ 30050769Sdfrstatic void 30150769Sdfrpnp_set_config(void *arg, struct isa_config *config, int enable) 30250769Sdfr{ 30350769Sdfr int csn = ((struct pnp_set_config_arg *) arg)->csn; 30450769Sdfr int ldn = ((struct pnp_set_config_arg *) arg)->ldn; 30550769Sdfr int i; 30650769Sdfr 30750769Sdfr /* 30850769Sdfr * First put all cards into Sleep state with the initiation 30950769Sdfr * key, then put our card into Config state. 31050769Sdfr */ 31150769Sdfr pnp_send_initiation_key(); 31250769Sdfr pnp_write(PNP_WAKE, csn); 31350769Sdfr 31450769Sdfr /* 31550769Sdfr * Select our logical device so that we can program it. 31650769Sdfr */ 31750769Sdfr pnp_write(PNP_SET_LDN, ldn); 31850769Sdfr 31950769Sdfr /* 32066840Smsmith * Constrain the number of resources we will try to program 32166840Smsmith */ 32266840Smsmith if (config->ic_nmem > ISA_PNP_NMEM) { 32366840Smsmith printf("too many ISA memory ranges (%d > %d)\n", config->ic_nmem, ISA_PNP_NMEM); 32466840Smsmith config->ic_nmem = ISA_PNP_NMEM; 32566840Smsmith } 32666840Smsmith if (config->ic_nport > ISA_PNP_NPORT) { 32766840Smsmith printf("too many ISA I/O ranges (%d > %d)\n", config->ic_nport, ISA_PNP_NPORT); 32866840Smsmith config->ic_nport = ISA_PNP_NPORT; 32966840Smsmith } 33066840Smsmith if (config->ic_nirq > ISA_PNP_NIRQ) { 33166840Smsmith printf("too many ISA IRQs (%d > %d)\n", config->ic_nirq, ISA_PNP_NIRQ); 33266840Smsmith config->ic_nirq = ISA_PNP_NIRQ; 33366840Smsmith } 33466840Smsmith if (config->ic_ndrq > ISA_PNP_NDRQ) { 33566840Smsmith printf("too many ISA DRQs (%d > %d)\n", config->ic_ndrq, ISA_PNP_NDRQ); 33666840Smsmith config->ic_ndrq = ISA_PNP_NDRQ; 33766840Smsmith } 33866840Smsmith 33966840Smsmith /* 34050769Sdfr * Now program the resources. 34150769Sdfr */ 34250769Sdfr for (i = 0; i < config->ic_nmem; i++) { 34383051Syokota u_int32_t start; 34483051Syokota u_int32_t size; 34583051Syokota 34683051Syokota /* XXX: should handle memory control register, 32 bit memory */ 34783051Syokota if (config->ic_mem[i].ir_size == 0) { 34883051Syokota pnp_write(PNP_MEM_BASE_HIGH(i), 0); 34983051Syokota pnp_write(PNP_MEM_BASE_LOW(i), 0); 35083051Syokota pnp_write(PNP_MEM_RANGE_HIGH(i), 0); 35183051Syokota pnp_write(PNP_MEM_RANGE_LOW(i), 0); 35283051Syokota } else { 35383051Syokota start = config->ic_mem[i].ir_start; 35483051Syokota size = config->ic_mem[i].ir_size; 35583051Syokota if (start & 0xff) 35683051Syokota panic("pnp_set_config: bogus memory assignment"); 35783051Syokota pnp_write(PNP_MEM_BASE_HIGH(i), (start >> 16) & 0xff); 35883051Syokota pnp_write(PNP_MEM_BASE_LOW(i), (start >> 8) & 0xff); 35983051Syokota pnp_write(PNP_MEM_RANGE_HIGH(i), (size >> 16) & 0xff); 36083051Syokota pnp_write(PNP_MEM_RANGE_LOW(i), (size >> 8) & 0xff); 36183051Syokota } 36250769Sdfr } 36366840Smsmith for (; i < ISA_PNP_NMEM; i++) { 36450769Sdfr pnp_write(PNP_MEM_BASE_HIGH(i), 0); 36550769Sdfr pnp_write(PNP_MEM_BASE_LOW(i), 0); 36650769Sdfr pnp_write(PNP_MEM_RANGE_HIGH(i), 0); 36750769Sdfr pnp_write(PNP_MEM_RANGE_LOW(i), 0); 36850769Sdfr } 36950769Sdfr 37050769Sdfr for (i = 0; i < config->ic_nport; i++) { 37183051Syokota u_int32_t start; 37283051Syokota 37383051Syokota if (config->ic_port[i].ir_size == 0) { 37483051Syokota pnp_write(PNP_IO_BASE_HIGH(i), 0); 37583051Syokota pnp_write(PNP_IO_BASE_LOW(i), 0); 37683051Syokota } else { 37783051Syokota start = config->ic_port[i].ir_start; 37883051Syokota pnp_write(PNP_IO_BASE_HIGH(i), (start >> 8) & 0xff); 37983051Syokota pnp_write(PNP_IO_BASE_LOW(i), (start >> 0) & 0xff); 38083051Syokota } 38150769Sdfr } 38266840Smsmith for (; i < ISA_PNP_NPORT; i++) { 38350769Sdfr pnp_write(PNP_IO_BASE_HIGH(i), 0); 38450769Sdfr pnp_write(PNP_IO_BASE_LOW(i), 0); 38550769Sdfr } 38650769Sdfr 38750769Sdfr for (i = 0; i < config->ic_nirq; i++) { 38883051Syokota int irq; 38983051Syokota 39083051Syokota /* XXX: interrupt type */ 39183051Syokota if (config->ic_irqmask[i] == 0) { 39283051Syokota pnp_write(PNP_IRQ_LEVEL(i), 0); 39383051Syokota pnp_write(PNP_IRQ_TYPE(i), 2); 39483051Syokota } else { 39583051Syokota irq = ffs(config->ic_irqmask[i]) - 1; 39683051Syokota pnp_write(PNP_IRQ_LEVEL(i), irq); 39783051Syokota pnp_write(PNP_IRQ_TYPE(i), 2); /* XXX */ 39883051Syokota } 39950769Sdfr } 40066840Smsmith for (; i < ISA_PNP_NIRQ; i++) { 40150769Sdfr /* 40250769Sdfr * IRQ 0 is not a valid interrupt selection and 40350769Sdfr * represents no interrupt selection. 40450769Sdfr */ 40550769Sdfr pnp_write(PNP_IRQ_LEVEL(i), 0); 40683051Syokota pnp_write(PNP_IRQ_TYPE(i), 2); 40750769Sdfr } 40850769Sdfr 40950769Sdfr for (i = 0; i < config->ic_ndrq; i++) { 41083051Syokota int drq; 41183051Syokota 41283051Syokota if (config->ic_drqmask[i] == 0) { 41383051Syokota pnp_write(PNP_DMA_CHANNEL(i), 4); 41483051Syokota } else { 41583051Syokota drq = ffs(config->ic_drqmask[i]) - 1; 41683051Syokota pnp_write(PNP_DMA_CHANNEL(i), drq); 41783051Syokota } 41850769Sdfr } 41966840Smsmith for (; i < ISA_PNP_NDRQ; i++) { 42050769Sdfr /* 42150769Sdfr * DMA channel 4, the cascade channel is used to 42250769Sdfr * indicate no DMA channel is active. 42350769Sdfr */ 42450769Sdfr pnp_write(PNP_DMA_CHANNEL(i), 4); 42550769Sdfr } 42650769Sdfr 42750769Sdfr pnp_write(PNP_ACTIVATE, enable ? 1 : 0); 42850769Sdfr 42950769Sdfr /* 43050769Sdfr * Wake everyone up again, we are finished. 43150769Sdfr */ 43250769Sdfr pnp_write(PNP_CONFIG_CONTROL, PNP_CONFIG_CONTROL_WAIT_FOR_KEY); 43350769Sdfr} 43450769Sdfr 43550769Sdfr/* 43650769Sdfr * Process quirks for a logical device.. The card must be in Config state. 43750769Sdfr */ 43862947Stanimuravoid 43962947Stanimurapnp_check_quirks(u_int32_t vendor_id, u_int32_t logical_id, int ldn, struct isa_config *config) 44050769Sdfr{ 44150769Sdfr struct pnp_quirk *qp; 44250769Sdfr 44350769Sdfr for (qp = &pnp_quirks[0]; qp->vendor_id; qp++) { 44450769Sdfr if (qp->vendor_id == vendor_id 44550769Sdfr && (qp->logical_id == 0 44650769Sdfr || qp->logical_id == logical_id)) { 44750769Sdfr switch (qp->type) { 44850769Sdfr case PNP_QUIRK_WRITE_REG: 44950769Sdfr pnp_write(PNP_SET_LDN, ldn); 45050769Sdfr pnp_write(qp->arg1, qp->arg2); 45150769Sdfr break; 45262947Stanimura case PNP_QUIRK_EXTRA_IO: 45362947Stanimura if (config == NULL) 45462947Stanimura break; 45562947Stanimura if (qp->arg1 != 0) { 45662947Stanimura config->ic_nport++; 45762947Stanimura config->ic_port[config->ic_nport - 1] = config->ic_port[0]; 45862947Stanimura config->ic_port[config->ic_nport - 1].ir_start += qp->arg1; 45962947Stanimura config->ic_port[config->ic_nport - 1].ir_end += qp->arg1; 46062947Stanimura } 46162947Stanimura if (qp->arg2 != 0) { 46262947Stanimura config->ic_nport++; 46362947Stanimura config->ic_port[config->ic_nport - 1] = config->ic_port[0]; 46462947Stanimura config->ic_port[config->ic_nport - 1].ir_start += qp->arg2; 46562947Stanimura config->ic_port[config->ic_nport - 1].ir_end += qp->arg2; 46662947Stanimura } 46762947Stanimura break; 46850769Sdfr } 46950769Sdfr } 47050769Sdfr } 47150769Sdfr} 47250769Sdfr 47350769Sdfr/* 47450769Sdfr * Scan Resource Data for Logical Devices. 47550769Sdfr * 47650769Sdfr * This function exits as soon as it gets an error reading *ANY* 47752059Sdfr * Resource Data or it reaches the end of Resource Data. In the first 47850769Sdfr * case the return value will be TRUE, FALSE otherwise. 47950769Sdfr */ 48050769Sdfrstatic int 48152059Sdfrpnp_create_devices(device_t parent, pnp_id *p, int csn, 48252059Sdfr u_char *resources, int len) 48350769Sdfr{ 48452059Sdfr u_char tag, *resp, *resinfo, *startres = 0; 48552059Sdfr int large_len, scanning = len, retval = FALSE; 48650769Sdfr u_int32_t logical_id; 48750769Sdfr u_int32_t compat_id; 48850769Sdfr device_t dev = 0; 48950769Sdfr int ldn = 0; 49050769Sdfr struct pnp_set_config_arg *csnldn; 49152059Sdfr char buf[100]; 49250769Sdfr char *desc = 0; 49350769Sdfr 49452059Sdfr resp = resources; 49552059Sdfr while (scanning > 0) { 49652059Sdfr tag = *resp++; 49752059Sdfr scanning--; 49852059Sdfr if (PNP_RES_TYPE(tag) != 0) { 49952059Sdfr /* Large resource */ 50052059Sdfr if (scanning < 2) { 50150769Sdfr scanning = 0; 50250769Sdfr continue; 50350769Sdfr } 50452059Sdfr large_len = resp[0] + (resp[1] << 8); 50552059Sdfr resp += 2; 50650769Sdfr 50752059Sdfr if (scanning < large_len) { 50850769Sdfr scanning = 0; 50950769Sdfr continue; 51050769Sdfr } 51152059Sdfr resinfo = resp; 51252059Sdfr resp += large_len; 51352059Sdfr scanning -= large_len; 51450769Sdfr 51550769Sdfr if (PNP_LRES_NUM(tag) == PNP_TAG_ID_ANSI) { 51652059Sdfr if (large_len > sizeof(buf) - 1) 51752059Sdfr large_len = sizeof(buf) - 1; 51852059Sdfr bcopy(resinfo, buf, large_len); 51952059Sdfr 52050769Sdfr /* 52152059Sdfr * Trim trailing spaces. 52250769Sdfr */ 52352059Sdfr while (buf[large_len-1] == ' ') 52452059Sdfr large_len--; 52552059Sdfr buf[large_len] = '\0'; 52652059Sdfr desc = buf; 52752059Sdfr if (dev) 52852059Sdfr device_set_desc_copy(dev, desc); 52950769Sdfr continue; 53050769Sdfr } 53150769Sdfr 53252059Sdfr continue; 53352059Sdfr } 53452059Sdfr 53552059Sdfr /* Small resource */ 53652059Sdfr if (scanning < PNP_SRES_LEN(tag)) { 53752059Sdfr scanning = 0; 53852059Sdfr continue; 53952059Sdfr } 54052059Sdfr resinfo = resp; 54152059Sdfr resp += PNP_SRES_LEN(tag); 54252059Sdfr scanning -= PNP_SRES_LEN(tag);; 54352059Sdfr 54452059Sdfr switch (PNP_SRES_NUM(tag)) { 54552059Sdfr case PNP_TAG_LOGICAL_DEVICE: 54652059Sdfr /* 54752059Sdfr * Parse the resources for the previous 54852059Sdfr * logical device (if any). 54952059Sdfr */ 55052059Sdfr if (startres) { 55152059Sdfr pnp_parse_resources(dev, startres, 55262947Stanimura resinfo - startres - 1, 55383051Syokota ldn); 55452059Sdfr dev = 0; 55552059Sdfr startres = 0; 55650769Sdfr } 55750769Sdfr 55852059Sdfr /* 55952059Sdfr * A new logical device. Scan for end of 56052059Sdfr * resources. 56152059Sdfr */ 56252059Sdfr bcopy(resinfo, &logical_id, 4); 56362947Stanimura pnp_check_quirks(p->vendor_id, logical_id, ldn, NULL); 56452059Sdfr compat_id = 0; 56552059Sdfr dev = BUS_ADD_CHILD(parent, ISA_ORDER_PNP, NULL, -1); 56652059Sdfr if (desc) 56752059Sdfr device_set_desc_copy(dev, desc); 56852059Sdfr isa_set_vendorid(dev, p->vendor_id); 56952059Sdfr isa_set_serial(dev, p->serial); 57052059Sdfr isa_set_logicalid(dev, logical_id); 57183051Syokota isa_set_configattr(dev, 57283051Syokota ISACFGATTR_CANDISABLE | 57383051Syokota ISACFGATTR_DYNAMIC); 57452059Sdfr csnldn = malloc(sizeof *csnldn, M_DEVBUF, M_NOWAIT); 57552059Sdfr if (!csnldn) { 57652059Sdfr device_printf(parent, 57752059Sdfr "out of memory\n"); 57850769Sdfr scanning = 0; 57952059Sdfr break; 58050769Sdfr } 58152059Sdfr csnldn->csn = csn; 58252059Sdfr csnldn->ldn = ldn; 58352059Sdfr ISA_SET_CONFIG_CALLBACK(parent, dev, 58452059Sdfr pnp_set_config, csnldn); 58552059Sdfr ldn++; 58652059Sdfr startres = resp; 58752059Sdfr break; 58852059Sdfr 58952059Sdfr case PNP_TAG_END: 59052059Sdfr if (!startres) { 59150769Sdfr device_printf(parent, 59252059Sdfr "malformed resources\n"); 59350769Sdfr scanning = 0; 59450769Sdfr break; 59550769Sdfr } 59652059Sdfr pnp_parse_resources(dev, startres, 59783051Syokota resinfo - startres - 1, ldn); 59852059Sdfr dev = 0; 59952059Sdfr startres = 0; 60052059Sdfr scanning = 0; 60152059Sdfr break; 60250769Sdfr 60352059Sdfr default: 60452059Sdfr /* Skip this resource */ 60552059Sdfr break; 60650769Sdfr } 60750769Sdfr } 60850769Sdfr 60950769Sdfr return retval; 61050769Sdfr} 61150769Sdfr 61250769Sdfr/* 61352059Sdfr * Read 'amount' bytes of resources from the card, allocating memory 61452059Sdfr * as needed. If a buffer is already available, it should be passed in 61552059Sdfr * '*resourcesp' and its length in '*spacep'. The number of resource 61652059Sdfr * bytes already in the buffer should be passed in '*lenp'. The memory 61752059Sdfr * allocated will be returned in '*resourcesp' with its size and the 61852059Sdfr * number of bytes of resources in '*spacep' and '*lenp' respectively. 61952059Sdfr */ 62052059Sdfrstatic int 62152059Sdfrpnp_read_bytes(int amount, u_char **resourcesp, int *spacep, int *lenp) 62252059Sdfr{ 62352059Sdfr u_char *resources = *resourcesp; 62452059Sdfr u_char *newres; 62552059Sdfr int space = *spacep; 62652059Sdfr int len = *lenp; 62752059Sdfr 62852059Sdfr if (space == 0) { 62952059Sdfr space = 1024; 63052059Sdfr resources = malloc(space, M_TEMP, M_NOWAIT); 63152059Sdfr if (!resources) 63252059Sdfr return ENOMEM; 63352059Sdfr } 63452059Sdfr 63552059Sdfr if (len + amount > space) { 63652059Sdfr int extra = 1024; 63752059Sdfr while (len + amount > space + extra) 63852059Sdfr extra += 1024; 63952059Sdfr newres = malloc(space + extra, M_TEMP, M_NOWAIT); 64052059Sdfr if (!newres) 64152059Sdfr return ENOMEM; 64252059Sdfr bcopy(resources, newres, len); 64352059Sdfr free(resources, M_TEMP); 64452059Sdfr resources = newres; 64552059Sdfr space += extra; 64652059Sdfr } 64752059Sdfr 64852059Sdfr if (pnp_get_resource_info(resources + len, amount) != amount) 64952059Sdfr return EINVAL; 65052059Sdfr len += amount; 65152059Sdfr 65252059Sdfr *resourcesp = resources; 65352059Sdfr *spacep = space; 65452059Sdfr *lenp = len; 65552059Sdfr 65652059Sdfr return 0; 65752059Sdfr} 65852059Sdfr 65952059Sdfr/* 66052059Sdfr * Read all resources from the card, allocating memory as needed. If a 66152059Sdfr * buffer is already available, it should be passed in '*resourcesp' 66252059Sdfr * and its length in '*spacep'. The memory allocated will be returned 66352059Sdfr * in '*resourcesp' with its size and the number of bytes of resources 66452059Sdfr * in '*spacep' and '*lenp' respectively. 66552059Sdfr */ 66652059Sdfrstatic int 66752059Sdfrpnp_read_resources(u_char **resourcesp, int *spacep, int *lenp) 66852059Sdfr{ 66952059Sdfr u_char *resources = *resourcesp; 67052059Sdfr int space = *spacep; 67152059Sdfr int len = 0; 67252059Sdfr int error, done; 67352059Sdfr u_char tag; 67452059Sdfr 67552059Sdfr error = 0; 67652059Sdfr done = 0; 67752059Sdfr while (!done) { 67852059Sdfr error = pnp_read_bytes(1, &resources, &space, &len); 67952059Sdfr if (error) 68052059Sdfr goto out; 68152059Sdfr tag = resources[len-1]; 68252059Sdfr if (PNP_RES_TYPE(tag) == 0) { 68352059Sdfr /* 68452059Sdfr * Small resource, read contents. 68552059Sdfr */ 68652059Sdfr error = pnp_read_bytes(PNP_SRES_LEN(tag), 68752059Sdfr &resources, &space, &len); 68852059Sdfr if (error) 68952059Sdfr goto out; 69052059Sdfr if (PNP_SRES_NUM(tag) == PNP_TAG_END) 69152059Sdfr done = 1; 69252059Sdfr } else { 69352059Sdfr /* 69452059Sdfr * Large resource, read length and contents. 69552059Sdfr */ 69652059Sdfr error = pnp_read_bytes(2, &resources, &space, &len); 69752059Sdfr if (error) 69852059Sdfr goto out; 69952059Sdfr error = pnp_read_bytes(resources[len-2] 70052059Sdfr + (resources[len-1] << 8), 70152059Sdfr &resources, &space, &len); 70252059Sdfr if (error) 70352059Sdfr goto out; 70452059Sdfr } 70552059Sdfr } 70652059Sdfr 70752059Sdfr out: 70852059Sdfr *resourcesp = resources; 70952059Sdfr *spacep = space; 71052059Sdfr *lenp = len; 71152059Sdfr return error; 71252059Sdfr} 71352059Sdfr 71452059Sdfr/* 71550769Sdfr * Run the isolation protocol. Use pnp_rd_port as the READ_DATA port 71650769Sdfr * value (caller should try multiple READ_DATA locations before giving 71750769Sdfr * up). Upon exiting, all cards are aware that they should use 71850769Sdfr * pnp_rd_port as the READ_DATA port. 71950769Sdfr * 72050769Sdfr * In the first pass, a csn is assigned to each board and pnp_id's 72150769Sdfr * are saved to an array, pnp_devices. In the second pass, each 72250769Sdfr * card is woken up and the device configuration is called. 72350769Sdfr */ 72450769Sdfrstatic int 72550769Sdfrpnp_isolation_protocol(device_t parent) 72650769Sdfr{ 72750769Sdfr int csn; 72850769Sdfr pnp_id id; 72952059Sdfr int found = 0, len; 73052059Sdfr u_char *resources = 0; 73152059Sdfr int space = 0; 73252059Sdfr int error; 73350769Sdfr 73450769Sdfr /* 73550769Sdfr * Put all cards into the Sleep state so that we can clear 73650769Sdfr * their CSNs. 73750769Sdfr */ 73850769Sdfr pnp_send_initiation_key(); 73950769Sdfr 74050769Sdfr /* 74150769Sdfr * Clear the CSN for all cards. 74250769Sdfr */ 74350769Sdfr pnp_write(PNP_CONFIG_CONTROL, PNP_CONFIG_CONTROL_RESET_CSN); 74450769Sdfr 74550769Sdfr /* 74650769Sdfr * Move all cards to the Isolation state. 74750769Sdfr */ 74850769Sdfr pnp_write(PNP_WAKE, 0); 74950769Sdfr 75050769Sdfr /* 75150769Sdfr * Tell them where the read point is going to be this time. 75250769Sdfr */ 75350769Sdfr pnp_write(PNP_SET_RD_DATA, pnp_rd_port); 75450769Sdfr 75550769Sdfr for (csn = 1; csn < PNP_MAX_CARDS; csn++) { 75650769Sdfr /* 75750769Sdfr * Start the serial isolation protocol. 75850769Sdfr */ 75950769Sdfr outb(_PNP_ADDRESS, PNP_SERIAL_ISOLATION); 76050769Sdfr DELAY(1000); /* Delay 1 msec */ 76150769Sdfr 76250769Sdfr if (pnp_get_serial(&id)) { 76350769Sdfr /* 76450769Sdfr * We have read the id from a card 76550769Sdfr * successfully. The card which won the 76650769Sdfr * isolation protocol will be in Isolation 76752059Sdfr * mode and all others will be in Sleep. 76850769Sdfr * Program the CSN of the isolated card 76950769Sdfr * (taking it to Config state) and read its 77050769Sdfr * resources, creating devices as we find 77150769Sdfr * logical devices on the card. 77250769Sdfr */ 77350769Sdfr pnp_write(PNP_SET_CSN, csn); 77452059Sdfr error = pnp_read_resources(&resources, 77552059Sdfr &space, 77652059Sdfr &len); 77752059Sdfr if (error) 77852059Sdfr break; 77952059Sdfr pnp_create_devices(parent, &id, csn, 78052059Sdfr resources, len); 78150769Sdfr found++; 78250769Sdfr } else 78350769Sdfr break; 78450769Sdfr 78550769Sdfr /* 78650769Sdfr * Put this card back to the Sleep state and 78750769Sdfr * simultaneously move all cards which don't have a 78850769Sdfr * CSN yet to Isolation state. 78950769Sdfr */ 79050769Sdfr pnp_write(PNP_WAKE, 0); 79150769Sdfr } 79250769Sdfr 79350769Sdfr /* 79450769Sdfr * Unless we have chosen the wrong read port, all cards will 79550769Sdfr * be in Sleep state. Put them back into WaitForKey for 79650769Sdfr * now. Their resources will be programmed later. 79750769Sdfr */ 79850769Sdfr pnp_write(PNP_CONFIG_CONTROL, PNP_CONFIG_CONTROL_WAIT_FOR_KEY); 79950769Sdfr 80052059Sdfr /* 80152059Sdfr * Cleanup. 80252059Sdfr */ 80352059Sdfr if (resources) 80452059Sdfr free(resources, M_TEMP); 80552059Sdfr 80650769Sdfr return found; 80750769Sdfr} 80850769Sdfr 80950769Sdfr 81050769Sdfr/* 81150769Sdfr * pnp_identify() 81250769Sdfr * 81350769Sdfr * autoconfiguration of pnp devices. This routine just runs the 81450769Sdfr * isolation protocol over several ports, until one is successful. 81550769Sdfr * 81650769Sdfr * may be called more than once ? 81750769Sdfr * 81850769Sdfr */ 81950769Sdfr 82050769Sdfrstatic void 82150769Sdfrpnp_identify(driver_t *driver, device_t parent) 82250769Sdfr{ 82350769Sdfr int num_pnp_devs; 82450769Sdfr 82550769Sdfr#if 0 82650769Sdfr if (pnp_ldn_overrides[0].csn == 0) { 82750769Sdfr if (bootverbose) 82850769Sdfr printf("Initializing PnP override table\n"); 82950769Sdfr bzero (pnp_ldn_overrides, sizeof(pnp_ldn_overrides)); 83050769Sdfr pnp_ldn_overrides[0].csn = 255 ; 83150769Sdfr } 83250769Sdfr#endif 83350769Sdfr 83450769Sdfr /* Try various READ_DATA ports from 0x203-0x3ff */ 83550769Sdfr for (pnp_rd_port = 0x80; (pnp_rd_port < 0xff); pnp_rd_port += 0x10) { 83650769Sdfr if (bootverbose) 83750769Sdfr printf("Trying Read_Port at %x\n", (pnp_rd_port << 2) | 0x3); 83850769Sdfr 83950769Sdfr num_pnp_devs = pnp_isolation_protocol(parent); 84050769Sdfr if (num_pnp_devs) 84150769Sdfr break; 84250769Sdfr } 84350769Sdfr} 84450769Sdfr 84550769Sdfrstatic device_method_t pnp_methods[] = { 84650769Sdfr /* Device interface */ 84750769Sdfr DEVMETHOD(device_identify, pnp_identify), 84850769Sdfr 84950769Sdfr { 0, 0 } 85050769Sdfr}; 85150769Sdfr 85250769Sdfrstatic driver_t pnp_driver = { 85350769Sdfr "pnp", 85450769Sdfr pnp_methods, 85550769Sdfr 1, /* no softc */ 85650769Sdfr}; 85750769Sdfr 85850769Sdfrstatic devclass_t pnp_devclass; 85950769Sdfr 86050769SdfrDRIVER_MODULE(pnp, isa, pnp_driver, pnp_devclass, 0, 0); 861