/* * ISA Plug & Play support * Copyright (c) by Jaroslav Kysela * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Changelog: * 2000-01-01 Added quirks handling for buggy hardware * Peter Denison * 2000-06-14 Added isapnp_probe_devs() and isapnp_activate_dev() * Christoph Hellwig * 2001-06-03 Added release_region calls to correspond with * request_region calls when a failure occurs. Also * added KERN_* constants to printk() calls. * 2001-11-07 Added isapnp_{,un}register_driver calls along the lines * of the pci driver interface * Kai Germaschewski * 2002-06-06 Made the use of dma channel 0 configurable * Gerald Teschl * 2002-10-06 Ported to PnP Layer - Adam Belay * 2003-08-11 Resource Management Updates - Adam Belay */ #include #include #include #include #include #include #include #include #include #if 0 #define ISAPNP_REGION_OK #endif #if 0 #define ISAPNP_DEBUG #endif int isapnp_disable; /* Disable ISA PnP */ static int isapnp_rdp; /* Read Data Port */ static int isapnp_reset = 1; /* reset all PnP cards (deactivate) */ static int isapnp_verbose = 1; /* verbose mode */ MODULE_AUTHOR("Jaroslav Kysela "); MODULE_DESCRIPTION("Generic ISA Plug & Play support"); module_param(isapnp_disable, int, 0); MODULE_PARM_DESC(isapnp_disable, "ISA Plug & Play disable"); module_param(isapnp_rdp, int, 0); MODULE_PARM_DESC(isapnp_rdp, "ISA Plug & Play read data port"); module_param(isapnp_reset, int, 0); MODULE_PARM_DESC(isapnp_reset, "ISA Plug & Play reset all cards"); module_param(isapnp_verbose, int, 0); MODULE_PARM_DESC(isapnp_verbose, "ISA Plug & Play verbose mode"); MODULE_LICENSE("GPL"); #define _PIDXR 0x279 #define _PNPWRP 0xa79 /* short tags */ #define _STAG_PNPVERNO 0x01 #define _STAG_LOGDEVID 0x02 #define _STAG_COMPATDEVID 0x03 #define _STAG_IRQ 0x04 #define _STAG_DMA 0x05 #define _STAG_STARTDEP 0x06 #define _STAG_ENDDEP 0x07 #define _STAG_IOPORT 0x08 #define _STAG_FIXEDIO 0x09 #define _STAG_VENDOR 0x0e #define _STAG_END 0x0f /* long tags */ #define _LTAG_MEMRANGE 0x81 #define _LTAG_ANSISTR 0x82 #define _LTAG_UNICODESTR 0x83 #define _LTAG_VENDOR 0x84 #define _LTAG_MEM32RANGE 0x85 #define _LTAG_FIXEDMEM32RANGE 0x86 static unsigned char isapnp_checksum_value; static DECLARE_MUTEX(isapnp_cfg_mutex); static int isapnp_detected; static int isapnp_csn_count; /* some prototypes */ static inline void write_data(unsigned char x) { outb(x, _PNPWRP); } static inline void write_address(unsigned char x) { outb(x, _PIDXR); udelay(20); } static inline unsigned char read_data(void) { unsigned char val = inb(isapnp_rdp); return val; } unsigned char isapnp_read_byte(unsigned char idx) { write_address(idx); return read_data(); } static unsigned short isapnp_read_word(unsigned char idx) { unsigned short val; val = isapnp_read_byte(idx); val = (val << 8) + isapnp_read_byte(idx+1); return val; } void isapnp_write_byte(unsigned char idx, unsigned char val) { write_address(idx); write_data(val); } static void isapnp_write_word(unsigned char idx, unsigned short val) { isapnp_write_byte(idx, val >> 8); isapnp_write_byte(idx+1, val); } static void isapnp_key(void) { unsigned char code = 0x6a, msb; int i; mdelay(1); write_address(0x00); write_address(0x00); write_address(code); for (i = 1; i < 32; i++) { msb = ((code & 0x01) ^ ((code & 0x02) >> 1)) << 7; code = (code >> 1) | msb; write_address(code); } } /* place all pnp cards in wait-for-key state */ static void isapnp_wait(void) { isapnp_write_byte(0x02, 0x02); } static void isapnp_wake(unsigned char csn) { isapnp_write_byte(0x03, csn); } static void isapnp_device(unsigned char logdev) { isapnp_write_byte(0x07, logdev); } static void isapnp_activate(unsigned char logdev) { isapnp_device(logdev); isapnp_write_byte(ISAPNP_CFG_ACTIVATE, 1); udelay(250); } static void isapnp_deactivate(unsigned char logdev) { isapnp_device(logdev); isapnp_write_byte(ISAPNP_CFG_ACTIVATE, 0); udelay(500); } static void __init isapnp_peek(unsigned char *data, int bytes) { int i, j; unsigned char d=0; for (i = 1; i <= bytes; i++) { for (j = 0; j < 20; j++) { d = isapnp_read_byte(0x05); if (d & 1) break; udelay(100); } if (!(d & 1)) { if (data != NULL) *data++ = 0xff; continue; } d = isapnp_read_byte(0x04); /* PRESDI */ isapnp_checksum_value += d; if (data != NULL) *data++ = d; } } #define RDP_STEP 32 /* minimum is 4 */ static int isapnp_next_rdp(void) { int rdp = isapnp_rdp; static int old_rdp = 0; if(old_rdp) { release_region(old_rdp, 1); old_rdp = 0; } while (rdp <= 0x3ff) { /* * We cannot use NE2000 probe spaces for ISAPnP or we * will lock up machines. */ if ((rdp < 0x280 || rdp > 0x380) && request_region(rdp, 1, "ISAPnP")) { isapnp_rdp = rdp; old_rdp = rdp; return 0; } rdp += RDP_STEP; } return -1; } /* Set read port address */ static inline void isapnp_set_rdp(void) { isapnp_write_byte(0x00, isapnp_rdp >> 2); udelay(100); } /* * Perform an isolation. The port selection code now tries to avoid * "dangerous to read" ports. */ static int __init isapnp_isolate_rdp_select(void) { isapnp_wait(); isapnp_key(); /* Control: reset CSN and conditionally everything else too */ isapnp_write_byte(0x02, isapnp_reset ? 0x05 : 0x04); mdelay(2); isapnp_wait(); isapnp_key(); isapnp_wake(0x00); if (isapnp_next_rdp() < 0) { isapnp_wait(); return -1; } isapnp_set_rdp(); udelay(1000); write_address(0x01); udelay(1000); return 0; } /* * Isolate (assign uniqued CSN) to all ISA PnP devices. */ static int __init isapnp_isolate(void) { unsigned char checksum = 0x6a; unsigned char chksum = 0x00; unsigned char bit = 0x00; int data; int csn = 0; int i; int iteration = 1; isapnp_rdp = 0x213; if (isapnp_isolate_rdp_select() < 0) return -1; while (1) { for (i = 1; i <= 64; i++) { data = read_data() << 8; udelay(250); data = data | read_data(); udelay(250); if (data == 0x55aa) bit = 0x01; checksum = ((((checksum ^ (checksum >> 1)) & 0x01) ^ bit) << 7) | (checksum >> 1); bit = 0x00; } for (i = 65; i <= 72; i++) { data = read_data() << 8; udelay(250); data = data | read_data(); udelay(250); if (data == 0x55aa) chksum |= (1 << (i - 65)); } if (checksum != 0x00 && checksum == chksum) { csn++; isapnp_write_byte(0x06, csn); udelay(250); iteration++; isapnp_wake(0x00); isapnp_set_rdp(); udelay(1000); write_address(0x01); udelay(1000); goto __next; } if (iteration == 1) { isapnp_rdp += RDP_STEP; if (isapnp_isolate_rdp_select() < 0) return -1; } else if (iteration > 1) { break; } __next: if (csn == 255) break; checksum = 0x6a; chksum = 0x00; bit = 0x00; } isapnp_wait(); isapnp_csn_count = csn; return csn; } /* * Read one tag from stream. */ static int __init isapnp_read_tag(unsigned char *type, unsigned short *size) { unsigned char tag, tmp[2]; isapnp_peek(&tag, 1); if (tag == 0) /* invalid tag */ return -1; if (tag & 0x80) { /* large item */ *type = tag; isapnp_peek(tmp, 2); *size = (tmp[1] << 8) | tmp[0]; } else { *type = (tag >> 3) & 0x0f; *size = tag & 0x07; } #if 0 printk(KERN_DEBUG "tag = 0x%x, type = 0x%x, size = %i\n", tag, *type, *size); #endif if (type == 0) /* wrong type */ return -1; if (*type == 0xff && *size == 0xffff) /* probably invalid data */ return -1; return 0; } /* * Skip specified number of bytes from stream. */ static void __init isapnp_skip_bytes(int count) { isapnp_peek(NULL, count); } /* * Parse EISA id. */ static void isapnp_parse_id(struct pnp_dev * dev, unsigned short vendor, unsigned short device) { struct pnp_id * id; if (!dev) return; id = kcalloc(1, sizeof(struct pnp_id), GFP_KERNEL); if (!id) return; sprintf(id->id, "%c%c%c%x%x%x%x", 'A' + ((vendor >> 2) & 0x3f) - 1, 'A' + (((vendor & 3) << 3) | ((vendor >> 13) & 7)) - 1, 'A' + ((vendor >> 8) & 0x1f) - 1, (device >> 4) & 0x0f, device & 0x0f, (device >> 12) & 0x0f, (device >> 8) & 0x0f); pnp_add_id(id, dev); } /* * Parse logical device tag. */ static struct pnp_dev * __init isapnp_parse_device(struct pnp_card *card, int size, int number) { unsigned char tmp[6]; struct pnp_dev *dev; isapnp_peek(tmp, size); dev = kcalloc(1, sizeof(struct pnp_dev), GFP_KERNEL); if (!dev) return NULL; dev->number = number; isapnp_parse_id(dev, (tmp[1] << 8) | tmp[0], (tmp[3] << 8) | tmp[2]); dev->regs = tmp[4]; dev->card = card; if (size > 5) dev->regs |= tmp[5] << 8; dev->protocol = &isapnp_protocol; dev->capabilities |= PNP_CONFIGURABLE; dev->capabilities |= PNP_READ; dev->capabilities |= PNP_WRITE; dev->capabilities |= PNP_DISABLE; pnp_init_resource_table(&dev->res); return dev; } /* * Add IRQ resource to resources list. */ static void __init isapnp_parse_irq_resource(struct pnp_option *option, int size) { unsigned char tmp[3]; struct pnp_irq *irq; unsigned long bits; isapnp_peek(tmp, size); irq = kcalloc(1, sizeof(struct pnp_irq), GFP_KERNEL); if (!irq) return; bits = (tmp[1] << 8) | tmp[0]; bitmap_copy(irq->map, &bits, 16); if (size > 2) irq->flags = tmp[2]; else irq->flags = IORESOURCE_IRQ_HIGHEDGE; pnp_register_irq_resource(option, irq); return; } /* * Add DMA resource to resources list. */ static void __init isapnp_parse_dma_resource(struct pnp_option *option, int size) { unsigned char tmp[2]; struct pnp_dma *dma; isapnp_peek(tmp, size); dma = kcalloc(1, sizeof(struct pnp_dma), GFP_KERNEL); if (!dma) return; dma->map = tmp[0]; dma->flags = tmp[1]; pnp_register_dma_resource(option, dma); return; } /* * Add port resource to resources list. */ static void __init isapnp_parse_port_resource(struct pnp_option *option, int size) { unsigned char tmp[7]; struct pnp_port *port; isapnp_peek(tmp, size); port = kcalloc(1, sizeof(struct pnp_port), GFP_KERNEL); if (!port) return; port->min = (tmp[2] << 8) | tmp[1]; port->max = (tmp[4] << 8) | tmp[3]; port->align = tmp[5]; port->size = tmp[6]; port->flags = tmp[0] ? PNP_PORT_FLAG_16BITADDR : 0; pnp_register_port_resource(option,port); return; } /* * Add fixed port resource to resources list. */ static void __init isapnp_parse_fixed_port_resource(struct pnp_option *option, int size) { unsigned char tmp[3]; struct pnp_port *port; isapnp_peek(tmp, size); port = kcalloc(1, sizeof(struct pnp_port), GFP_KERNEL); if (!port) return; port->min = port->max = (tmp[1] << 8) | tmp[0]; port->size = tmp[2]; port->align = 0; port->flags = PNP_PORT_FLAG_FIXED; pnp_register_port_resource(option,port); return; } /* * Add memory resource to resources list. */ static void __init isapnp_parse_mem_resource(struct pnp_option *option, int size) { unsigned char tmp[9]; struct pnp_mem *mem; isapnp_peek(tmp, size); mem = kcalloc(1, sizeof(struct pnp_mem), GFP_KERNEL); if (!mem) return; mem->min = ((tmp[2] << 8) | tmp[1]) << 8; mem->max = ((tmp[4] << 8) | tmp[3]) << 8; mem->align = (tmp[6] << 8) | tmp[5]; mem->size = ((tmp[8] << 8) | tmp[7]) << 8; mem->flags = tmp[0]; pnp_register_mem_resource(option,mem); return; } /* * Add 32-bit memory resource to resources list. */ static void __init isapnp_parse_mem32_resource(struct pnp_option *option, int size) { unsigned char tmp[17]; struct pnp_mem *mem; isapnp_peek(tmp, size); mem = kcalloc(1, sizeof(struct pnp_mem), GFP_KERNEL); if (!mem) return; mem->min = (tmp[4] << 24) | (tmp[3] << 16) | (tmp[2] << 8) | tmp[1]; mem->max = (tmp[8] << 24) | (tmp[7] << 16) | (tmp[6] << 8) | tmp[5]; mem->align = (tmp[12] << 24) | (tmp[11] << 16) | (tmp[10] << 8) | tmp[9]; mem->size = (tmp[16] << 24) | (tmp[15] << 16) | (tmp[14] << 8) | tmp[13]; mem->flags = tmp[0]; pnp_register_mem_resource(option,mem); } /* * Add 32-bit fixed memory resource to resources list. */ static void __init isapnp_parse_fixed_mem32_resource(struct pnp_option *option, int size) { unsigned char tmp[9]; struct pnp_mem *mem; isapnp_peek(tmp, size); mem = kcalloc(1, sizeof(struct pnp_mem), GFP_KERNEL); if (!mem) return; mem->min = mem->max = (tmp[4] << 24) | (tmp[3] << 16) | (tmp[2] << 8) | tmp[1]; mem->size = (tmp[8] << 24) | (tmp[7] << 16) | (tmp[6] << 8) | tmp[5]; mem->align = 0; mem->flags = tmp[0]; pnp_register_mem_resource(option,mem); } /* * Parse card name for ISA PnP device. */ static void __init isapnp_parse_name(char *name, unsigned int name_max, unsigned short *size) { if (name[0] == '\0') { unsigned short size1 = *size >= name_max ? (name_max - 1) : *size; isapnp_peek(name, size1); name[size1] = '\0'; *size -= size1; /* clean whitespace from end of string */ while (size1 > 0 && name[--size1] == ' ') name[size1] = '\0'; } } /* * Parse resource map for logical device. */ static int __init isapnp_create_device(struct pnp_card *card, unsigned short size) { int number = 0, skip = 0, priority = 0, compat = 0; unsigned char type, tmp[17]; struct pnp_option *option; struct pnp_dev *dev; if ((dev = isapnp_parse_device(card, size, number++)) == NULL) return 1; option = pnp_register_independent_option(dev); if (!option) { kfree(dev); return 1; } pnp_add_card_device(card,dev); while (1) { if (isapnp_read_tag(&type, &size)<0) return 1; if (skip && type != _STAG_LOGDEVID && type != _STAG_END) goto __skip; switch (type) { case _STAG_LOGDEVID: if (size >= 5 && size <= 6) { if ((dev = isapnp_parse_device(card, size, number++)) == NULL) return 1; size = 0; skip = 0; option = pnp_register_independent_option(dev); if (!option) return 1; pnp_add_card_device(card,dev); } else { skip = 1; } priority = 0; compat = 0; break; case _STAG_COMPATDEVID: if (size == 4 && compat < DEVICE_COUNT_COMPATIBLE) { isapnp_peek(tmp, 4); isapnp_parse_id(dev,(tmp[1] << 8) | tmp[0], (tmp[3] << 8) | tmp[2]); compat++; size = 0; } break; case _STAG_IRQ: if (size < 2 || size > 3) goto __skip; isapnp_parse_irq_resource(option, size); size = 0; break; case _STAG_DMA: if (size != 2) goto __skip; isapnp_parse_dma_resource(option, size); size = 0; break; case _STAG_STARTDEP: if (size > 1) goto __skip; priority = 0x100 | PNP_RES_PRIORITY_ACCEPTABLE; if (size > 0) { isapnp_peek(tmp, size); priority = 0x100 | tmp[0]; size = 0; } option = pnp_register_dependent_option(dev,priority); if (!option) return 1; break; case _STAG_ENDDEP: if (size != 0) goto __skip; priority = 0; break; case _STAG_IOPORT: if (size != 7) goto __skip; isapnp_parse_port_resource(option, size); size = 0; break; case _STAG_FIXEDIO: if (size != 3) goto __skip; isapnp_parse_fixed_port_resource(option, size); size = 0; break; case _STAG_VENDOR: break; case _LTAG_MEMRANGE: if (size != 9) goto __skip; isapnp_parse_mem_resource(option, size); size = 0; break; case _LTAG_ANSISTR: isapnp_parse_name(dev->name, sizeof(dev->name), &size); break; case _LTAG_UNICODESTR: /* silently ignore */ /* who use unicode for hardware identification? */ break; case _LTAG_VENDOR: break; case _LTAG_MEM32RANGE: if (size != 17) goto __skip; isapnp_parse_mem32_resource(option, size); size = 0; break; case _LTAG_FIXEDMEM32RANGE: if (size != 9) goto __skip; isapnp_parse_fixed_mem32_resource(option, size); size = 0; break; case _STAG_END: if (size > 0) isapnp_skip_bytes(size); return 1; default: printk(KERN_ERR "isapnp: unexpected or unknown tag type 0x%x for logical device %i (device %i), ignored\n", type, dev->number, card->number); } __skip: if (size > 0) isapnp_skip_bytes(size); } return 0; } /* * Parse resource map for ISA PnP card. */ static void __init isapnp_parse_resource_map(struct pnp_card *card) { unsigned char type, tmp[17]; unsigned short size; while (1) { if (isapnp_read_tag(&type, &size)<0) return; switch (type) { case _STAG_PNPVERNO: if (size != 2) goto __skip; isapnp_peek(tmp, 2); card->pnpver = tmp[0]; card->productver = tmp[1]; size = 0; break; case _STAG_LOGDEVID: if (size >= 5 && size <= 6) { if (isapnp_create_device(card, size)==1) return; size = 0; } break; case _STAG_VENDOR: break; case _LTAG_ANSISTR: isapnp_parse_name(card->name, sizeof(card->name), &size); break; case _LTAG_UNICODESTR: /* silently ignore */ /* who use unicode for hardware identification? */ break; case _LTAG_VENDOR: break; case _STAG_END: if (size > 0) isapnp_skip_bytes(size); return; default: printk(KERN_ERR "isapnp: unexpected or unknown tag type 0x%x for device %i, ignored\n", type, card->number); } __skip: if (size > 0) isapnp_skip_bytes(size); } } /* * Compute ISA PnP checksum for first eight bytes. */ static unsigned char __init isapnp_checksum(unsigned char *data) { int i, j; unsigned char checksum = 0x6a, bit, b; for (i = 0; i < 8; i++) { b = data[i]; for (j = 0; j < 8; j++) { bit = 0; if (b & (1 << j)) bit = 1; checksum = ((((checksum ^ (checksum >> 1)) & 0x01) ^ bit) << 7) | (checksum >> 1); } } return checksum; } /* * Parse EISA id for ISA PnP card. */ static void isapnp_parse_card_id(struct pnp_card * card, unsigned short vendor, unsigned short device) { struct pnp_id * id = kcalloc(1, sizeof(struct pnp_id), GFP_KERNEL); if (!id) return; sprintf(id->id, "%c%c%c%x%x%x%x", 'A' + ((vendor >> 2) & 0x3f) - 1, 'A' + (((vendor & 3) << 3) | ((vendor >> 13) & 7)) - 1, 'A' + ((vendor >> 8) & 0x1f) - 1, (device >> 4) & 0x0f, device & 0x0f, (device >> 12) & 0x0f, (device >> 8) & 0x0f); pnp_add_card_id(id,card); } /* * Build device list for all present ISA PnP devices. */ static int __init isapnp_build_device_list(void) { int csn; unsigned char header[9], checksum; struct pnp_card *card; isapnp_wait(); isapnp_key(); for (csn = 1; csn <= isapnp_csn_count; csn++) { isapnp_wake(csn); isapnp_peek(header, 9); checksum = isapnp_checksum(header); #if 0 printk(KERN_DEBUG "vendor: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", header[0], header[1], header[2], header[3], header[4], header[5], header[6], header[7], header[8]); printk(KERN_DEBUG "checksum = 0x%x\n", checksum); #endif if ((card = kcalloc(1, sizeof(struct pnp_card), GFP_KERNEL)) == NULL) continue; card->number = csn; INIT_LIST_HEAD(&card->devices); isapnp_parse_card_id(card, (header[1] << 8) | header[0], (header[3] << 8) | header[2]); card->serial = (header[7] << 24) | (header[6] << 16) | (header[5] << 8) | header[4]; isapnp_checksum_value = 0x00; isapnp_parse_resource_map(card); if (isapnp_checksum_value != 0x00) printk(KERN_ERR "isapnp: checksum for device %i is not valid (0x%x)\n", csn, isapnp_checksum_value); card->checksum = isapnp_checksum_value; card->protocol = &isapnp_protocol; pnp_add_card(card); } isapnp_wait(); return 0; } /* * Basic configuration routines. */ int isapnp_present(void) { struct pnp_card *card; pnp_for_each_card(card) { if (card->protocol == &isapnp_protocol) return 1; } return 0; } int isapnp_cfg_begin(int csn, int logdev) { if (csn < 1 || csn > isapnp_csn_count || logdev > 10) return -EINVAL; down(&isapnp_cfg_mutex); isapnp_wait(); isapnp_key(); isapnp_wake(csn); #if 0 /* to avoid malfunction when the isapnptools package is used */ /* we must set RDP to our value again */ /* it is possible to set RDP only in the isolation phase */ /* Jens Thoms Toerring */ isapnp_write_byte(0x02, 0x04); /* clear CSN of card */ mdelay(2); /* is this necessary? */ isapnp_wake(csn); /* bring card into sleep state */ isapnp_wake(0); /* bring card into isolation state */ isapnp_set_rdp(); /* reset the RDP port */ udelay(1000); /* delay 1000us */ isapnp_write_byte(0x06, csn); /* reset CSN to previous value */ udelay(250); /* is this necessary? */ #endif if (logdev >= 0) isapnp_device(logdev); return 0; } int isapnp_cfg_end(void) { isapnp_wait(); up(&isapnp_cfg_mutex); return 0; } /* * Inititialization. */ EXPORT_SYMBOL(isapnp_protocol); EXPORT_SYMBOL(isapnp_present); EXPORT_SYMBOL(isapnp_cfg_begin); EXPORT_SYMBOL(isapnp_cfg_end); EXPORT_SYMBOL(isapnp_read_byte); EXPORT_SYMBOL(isapnp_write_byte); static int isapnp_read_resources(struct pnp_dev *dev, struct pnp_resource_table *res) { int tmp, ret; dev->active = isapnp_read_byte(ISAPNP_CFG_ACTIVATE); if (dev->active) { for (tmp = 0; tmp < PNP_MAX_PORT; tmp++) { ret = isapnp_read_word(ISAPNP_CFG_PORT + (tmp << 1)); if (!ret) continue; res->port_resource[tmp].start = ret; res->port_resource[tmp].flags = IORESOURCE_IO; } for (tmp = 0; tmp < PNP_MAX_MEM; tmp++) { ret = isapnp_read_word(ISAPNP_CFG_MEM + (tmp << 3)) << 8; if (!ret) continue; res->mem_resource[tmp].start = ret; res->mem_resource[tmp].flags = IORESOURCE_MEM; } for (tmp = 0; tmp < PNP_MAX_IRQ; tmp++) { ret = (isapnp_read_word(ISAPNP_CFG_IRQ + (tmp << 1)) >> 8); if (!ret) continue; res->irq_resource[tmp].start = res->irq_resource[tmp].end = ret; res->irq_resource[tmp].flags = IORESOURCE_IRQ; } for (tmp = 0; tmp < PNP_MAX_DMA; tmp++) { ret = isapnp_read_byte(ISAPNP_CFG_DMA + tmp); if (ret == 4) continue; res->dma_resource[tmp].start = res->dma_resource[tmp].end = ret; res->dma_resource[tmp].flags = IORESOURCE_DMA; } } return 0; } static int isapnp_get_resources(struct pnp_dev *dev, struct pnp_resource_table * res) { int ret; pnp_init_resource_table(res); isapnp_cfg_begin(dev->card->number, dev->number); ret = isapnp_read_resources(dev, res); isapnp_cfg_end(); return ret; } static int isapnp_set_resources(struct pnp_dev *dev, struct pnp_resource_table * res) { int tmp; isapnp_cfg_begin(dev->card->number, dev->number); dev->active = 1; for (tmp = 0; tmp < PNP_MAX_PORT && (res->port_resource[tmp].flags & (IORESOURCE_IO | IORESOURCE_UNSET)) == IORESOURCE_IO; tmp++) isapnp_write_word(ISAPNP_CFG_PORT+(tmp<<1), res->port_resource[tmp].start); for (tmp = 0; tmp < PNP_MAX_IRQ && (res->irq_resource[tmp].flags & (IORESOURCE_IRQ | IORESOURCE_UNSET)) == IORESOURCE_IRQ; tmp++) { int irq = res->irq_resource[tmp].start; if (irq == 2) irq = 9; isapnp_write_byte(ISAPNP_CFG_IRQ+(tmp<<1), irq); } for (tmp = 0; tmp < PNP_MAX_DMA && (res->dma_resource[tmp].flags & (IORESOURCE_DMA | IORESOURCE_UNSET)) == IORESOURCE_DMA; tmp++) isapnp_write_byte(ISAPNP_CFG_DMA+tmp, res->dma_resource[tmp].start); for (tmp = 0; tmp < PNP_MAX_MEM && (res->mem_resource[tmp].flags & (IORESOURCE_MEM | IORESOURCE_UNSET)) == IORESOURCE_MEM; tmp++) isapnp_write_word(ISAPNP_CFG_MEM+(tmp<<3), (res->mem_resource[tmp].start >> 8) & 0xffff); /* FIXME: We aren't handling 32bit mems properly here */ isapnp_activate(dev->number); isapnp_cfg_end(); return 0; } static int isapnp_disable_resources(struct pnp_dev *dev) { if (!dev || !dev->active) return -EINVAL; isapnp_cfg_begin(dev->card->number, dev->number); isapnp_deactivate(dev->number); dev->active = 0; isapnp_cfg_end(); return 0; } struct pnp_protocol isapnp_protocol = { .name = "ISA Plug and Play", .get = isapnp_get_resources, .set = isapnp_set_resources, .disable = isapnp_disable_resources, }; static int __init isapnp_init(void) { int cards; struct pnp_card *card; struct pnp_dev *dev; if (isapnp_disable) { isapnp_detected = 0; printk(KERN_INFO "isapnp: ISA Plug & Play support disabled\n"); return 0; } #ifdef ISAPNP_REGION_OK if (!request_region(_PIDXR, 1, "isapnp index")) { printk(KERN_ERR "isapnp: Index Register 0x%x already used\n", _PIDXR); return -EBUSY; } #endif if (!request_region(_PNPWRP, 1, "isapnp write")) { printk(KERN_ERR "isapnp: Write Data Register 0x%x already used\n", _PNPWRP); #ifdef ISAPNP_REGION_OK release_region(_PIDXR, 1); #endif return -EBUSY; } if(pnp_register_protocol(&isapnp_protocol)<0) return -EBUSY; /* * Print a message. The existing ISAPnP code is hanging machines * so let the user know where. */ printk(KERN_INFO "isapnp: Scanning for PnP cards...\n"); if (isapnp_rdp >= 0x203 && isapnp_rdp <= 0x3ff) { isapnp_rdp |= 3; if (!request_region(isapnp_rdp, 1, "isapnp read")) { printk(KERN_ERR "isapnp: Read Data Register 0x%x already used\n", isapnp_rdp); #ifdef ISAPNP_REGION_OK release_region(_PIDXR, 1); #endif release_region(_PNPWRP, 1); return -EBUSY; } isapnp_set_rdp(); } isapnp_detected = 1; if (isapnp_rdp < 0x203 || isapnp_rdp > 0x3ff) { cards = isapnp_isolate(); if (cards < 0 || (isapnp_rdp < 0x203 || isapnp_rdp > 0x3ff)) { #ifdef ISAPNP_REGION_OK release_region(_PIDXR, 1); #endif release_region(_PNPWRP, 1); isapnp_detected = 0; printk(KERN_INFO "isapnp: No Plug & Play device found\n"); return 0; } request_region(isapnp_rdp, 1, "isapnp read"); } isapnp_build_device_list(); cards = 0; protocol_for_each_card(&isapnp_protocol,card) { cards++; if (isapnp_verbose) { printk(KERN_INFO "isapnp: Card '%s'\n", card->name[0]?card->name:"Unknown"); if (isapnp_verbose < 2) continue; card_for_each_dev(card,dev) { printk(KERN_INFO "isapnp: Device '%s'\n", dev->name[0]?dev->name:"Unknown"); } } } if (cards) { printk(KERN_INFO "isapnp: %i Plug & Play card%s detected total\n", cards, cards>1?"s":""); } else { printk(KERN_INFO "isapnp: No Plug & Play card found\n"); } isapnp_proc_init(); return 0; } device_initcall(isapnp_init); /* format is: noisapnp */ static int __init isapnp_setup_disable(char *str) { isapnp_disable = 1; return 1; } __setup("noisapnp", isapnp_setup_disable); /* format is: isapnp=rdp,reset,skip_pci_scan,verbose */ static int __init isapnp_setup_isapnp(char *str) { (void)((get_option(&str,&isapnp_rdp) == 2) && (get_option(&str,&isapnp_reset) == 2) && (get_option(&str,&isapnp_verbose) == 2)); return 1; } __setup("isapnp=", isapnp_setup_isapnp);