summaryrefslogtreecommitdiffstats
path: root/drivers/pnp
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pnp')
-rw-r--r--drivers/pnp/Makefile4
-rw-r--r--drivers/pnp/isapnp.c164
-rw-r--r--drivers/pnp/isapnp_proc.c23
-rw-r--r--drivers/pnp/quirks.c75
4 files changed, 170 insertions, 96 deletions
diff --git a/drivers/pnp/Makefile b/drivers/pnp/Makefile
index f12b5686d..9a8ad136c 100644
--- a/drivers/pnp/Makefile
+++ b/drivers/pnp/Makefile
@@ -19,10 +19,10 @@ MI_OBJS :=
MIX_OBJS :=
ifeq ($(CONFIG_ISAPNP),y)
- LX_OBJS += isapnp.o
+ LX_OBJS += isapnp.o quirks.o
else
ifeq ($(CONFIG_ISAPNP),m)
- MX_OBJS += isapnp.o
+ MX_OBJS += isapnp.o quirks.o
endif
endif
diff --git a/drivers/pnp/isapnp.c b/drivers/pnp/isapnp.c
index 3ec11f092..8b6a42b7d 100644
--- a/drivers/pnp/isapnp.c
+++ b/drivers/pnp/isapnp.c
@@ -17,6 +17,9 @@
* 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 ISAPnP quirks handling
+ * Peter Denison <peterd@pnd-pc.demon.co.uk>
*/
#include <linux/config.h>
@@ -39,6 +42,15 @@
#include <asm/uaccess.h>
#include <linux/isapnp.h>
+LIST_HEAD(isapnp_cards);
+LIST_HEAD(isapnp_devices);
+
+#define isapnp_for_each_card(card) \
+ for(card = pci_bus_b(isapnp_cards.next); card != pci_bus_b(&isapnp_cards); card = pci_bus_b(card->node.next))
+#define isapnp_for_each_dev(dev) \
+ for(dev = pci_dev_g(isapnp_devices.next); dev != pci_dev_g(&isapnp_devices); dev = pci_dev_g(dev->global_list.next))
+
+
#ifdef CONFIG_PROC_FS
#include "isapnp_proc.c"
#endif
@@ -108,9 +120,6 @@ MODULE_PARM_DESC(isapnp_reserve_mem, "ISA Plug & Play - reserve memory region(s)
#define _LTAG_MEM32RANGE 0x85
#define _LTAG_FIXEDMEM32RANGE 0x86
-struct pci_bus *isapnp_cards = NULL; /* ISA PnP cards */
-struct pci_dev *isapnp_devices = NULL; /* ISA PnP devices */
-static struct pci_dev *isapnp_last_device = NULL;
static unsigned char isapnp_checksum_value;
static DECLARE_MUTEX(isapnp_cfg_mutex);
static int isapnp_detected = 0;
@@ -184,7 +193,7 @@ void isapnp_write_dword(unsigned char idx, unsigned int val)
isapnp_write_byte(idx+3, val);
}
-static void *isapnp_alloc(long size)
+void *isapnp_alloc(long size)
{
void *result;
@@ -749,18 +758,13 @@ static int __init isapnp_create_device(struct pci_bus *card,
{
int number = 0, skip = 0, dependent = 0, compat = 0;
unsigned char type, tmp[17];
- struct pci_dev *dev, *prev_dev;
+ struct pci_dev *dev;
struct isapnp_resources *res = NULL;
if ((dev = isapnp_parse_device(card, size, number++)) == NULL)
return 1;
- card->devices = dev;
- if (isapnp_last_device) {
- isapnp_last_device->next = dev;
- isapnp_last_device = dev;
- } else {
- isapnp_devices = isapnp_last_device = dev;
- }
+ list_add(&dev->bus_list, &card->devices);
+ list_add_tail(&dev->global_list, &isapnp_devices);
while (1) {
if (isapnp_read_tag(&type, &size)<0)
return 1;
@@ -769,13 +773,11 @@ static int __init isapnp_create_device(struct pci_bus *card,
switch (type) {
case _STAG_LOGDEVID:
if (size >= 5 && size <= 6) {
- prev_dev = dev;
isapnp_config_prepare(dev);
if ((dev = isapnp_parse_device(card, size, number++)) == NULL)
return 1;
- prev_dev->sibling = dev;
- isapnp_last_device->next = dev;
- isapnp_last_device = dev;
+ list_add_tail(&dev->bus_list, &card->devices);
+ list_add_tail(&dev->global_list, &isapnp_devices);
size = 0;
skip = 0;
} else {
@@ -970,7 +972,8 @@ static int __init isapnp_build_device_list(void)
{
int csn;
unsigned char header[9], checksum;
- struct pci_bus *card, *prev = NULL;
+ struct pci_bus *card;
+ struct pci_dev *dev;
isapnp_wait();
isapnp_key();
@@ -997,11 +1000,11 @@ static int __init isapnp_build_device_list(void)
if (isapnp_checksum_value != 0x00)
printk("isapnp: checksum for device %i is not valid (0x%x)\n", csn, isapnp_checksum_value);
card->checksum = isapnp_checksum_value;
- if (!isapnp_cards)
- isapnp_cards = card;
- else
- prev->next = card;
- prev = card;
+
+ list_add_tail(&card->node, &isapnp_cards);
+ }
+ isapnp_for_each_dev(dev) {
+ isapnp_fixup_device(dev);
}
return 0;
}
@@ -1012,9 +1015,7 @@ static int __init isapnp_build_device_list(void)
int isapnp_present(void)
{
- if (isapnp_devices)
- return 1;
- return 0;
+ return !list_empty(&isapnp_devices);
}
int isapnp_cfg_begin(int csn, int logdev)
@@ -1152,16 +1153,17 @@ struct pci_bus *isapnp_find_card(unsigned short vendor,
unsigned short device,
struct pci_bus *from)
{
- struct pci_bus *card;
+ struct list_head *list;
- if (from == NULL) {
- from = isapnp_cards;
- } else {
- from = from->next;
- }
- for (card = from; card; card = card->next) {
+ list = isapnp_cards.next;
+ if (from)
+ list = from->node.next;
+
+ while (list != &isapnp_cards) {
+ struct pci_bus *card = pci_bus_b(list);
if (card->vendor == vendor && card->device == device)
return card;
+ list = list->next;
}
return NULL;
}
@@ -1171,39 +1173,45 @@ struct pci_dev *isapnp_find_dev(struct pci_bus *card,
unsigned short function,
struct pci_dev *from)
{
- struct pci_dev *dev;
- int idx;
-
if (card == NULL) { /* look for a logical device from all cards */
- if (from == NULL) {
- from = isapnp_devices;
- } else {
- from = from->next;
- }
- for (dev = from; dev; dev = dev->next) {
+ struct list_head *list;
+
+ list = isapnp_devices.next;
+ if (from)
+ list = from->global_list.next;
+
+ while (list != &isapnp_devices) {
+ int idx;
+ struct pci_dev *dev = pci_dev_g(list);
+
if (dev->vendor == vendor && dev->device == function)
return dev;
for (idx = 0; idx < DEVICE_COUNT_COMPATIBLE; idx++)
if (dev->vendor_compatible[idx] == vendor &&
dev->device_compatible[idx] == function)
return dev;
+ list = list->next;
}
} else {
- if (from == NULL) {
- from = card->devices;
- } else {
- from = from->next;
- }
+ struct list_head *list;
+
+ list = card->devices.next;
+ if (from)
+ list = from->bus_list.next;
if (from->bus != card) /* something is wrong */
return NULL;
- for (dev = from; dev; dev = dev->sibling) {
+ while (list != &card->devices) {
+ int idx;
+ struct pci_dev *dev = pci_dev_b(list);
+
if (dev->vendor == vendor && dev->device == function)
return dev;
for (idx = 0; idx < DEVICE_COUNT_COMPATIBLE; idx++)
if (dev->vendor_compatible[idx] == vendor &&
dev->device_compatible[idx] == function)
return dev;
- }
+ list = list->next;
+ }
}
return NULL;
}
@@ -1454,7 +1462,8 @@ static int isapnp_check_port(struct isapnp_cfgtmp *cfg, int port, int size, int
if (port + size > rport && port + size < (rport + rsize) - 1)
return 1;
}
- for (dev = isapnp_devices; dev; dev = dev->next) {
+
+ isapnp_for_each_dev(dev) {
if (dev->active) {
for (tmp = 0; tmp < 8; tmp++) {
if (dev->resource[tmp].flags) {
@@ -1551,7 +1560,7 @@ static int isapnp_check_interrupt(struct isapnp_cfgtmp *cfg, int irq, int idx)
if (isapnp_reserve_irq[i] == irq)
return 1;
}
- for (dev = isapnp_devices; dev; dev = dev->next) {
+ isapnp_for_each_dev(dev) {
if (dev->active) {
if (dev->irq_resource[0].start == irq ||
dev->irq_resource[1].start == irq)
@@ -1630,7 +1639,7 @@ static int isapnp_check_dma(struct isapnp_cfgtmp *cfg, int dma, int idx)
if (isapnp_reserve_dma[i] == dma)
return 1;
}
- for (dev = isapnp_devices; dev; dev = dev->next) {
+ isapnp_for_each_dev(dev) {
if (dev->active) {
if (dev->dma_resource[0].start == dma || dev->dma_resource[1].start == dma)
return 1;
@@ -1695,8 +1704,8 @@ static int isapnp_check_mem(struct isapnp_cfgtmp *cfg, unsigned int addr, unsign
{
int i, tmp;
unsigned int raddr, rsize;
- struct pci_dev *dev;
struct isapnp_mem *xmem;
+ struct pci_dev *dev;
for (i = 0; i < 8; i++) {
raddr = (unsigned int)isapnp_reserve_mem[i << 1];
@@ -1708,7 +1717,7 @@ static int isapnp_check_mem(struct isapnp_cfgtmp *cfg, unsigned int addr, unsign
if (__check_region(&iomem_resource, addr, size))
return 1;
}
- for (dev = isapnp_devices; dev; dev = dev->next) {
+ isapnp_for_each_dev(dev) {
if (dev->active) {
for (tmp = 0; tmp < 4; tmp++) {
if (dev->resource[tmp].flags) {
@@ -1988,26 +1997,22 @@ static void isapnp_free_resources(struct isapnp_resources *resources, int alt)
}
}
-static void isapnp_free_device(struct pci_dev *dev)
+static void isapnp_free_card(struct pci_bus *card)
{
- struct pci_dev *next;
-
- while (dev) {
- next = dev->sibling;
+ while (!list_empty(&card->devices)) {
+ struct list_head *list = card->devices.next;
+ struct pci_dev *dev = pci_dev_b(list);
+ list_del(list);
isapnp_free_resources((struct isapnp_resources *)dev->sysdata, 0);
kfree(dev);
- dev = next;
}
+ kfree(card);
}
#endif /* MODULE */
static void isapnp_free_all_resources(void)
{
-#ifdef MODULE
- struct pci_bus *card, *cardnext;
-#endif
-
#ifdef ISAPNP_REGION_OK
release_resource(pidxr_res);
#endif
@@ -2015,10 +2020,10 @@ static void isapnp_free_all_resources(void)
if (isapnp_rdp >= 0x203 && isapnp_rdp <= 0x3ff)
release_resource(isapnp_rdp_res);
#ifdef MODULE
- for (card = isapnp_cards; card; card = cardnext) {
- cardnext = card->next;
- isapnp_free_device(card->devices);
- kfree(card);
+ while (!list_empty(&isapnp_cards)) {
+ struct list_head *list = isapnp_cards.next;
+ list_del(list);
+ isapnp_free_card(pci_bus_b(list));
}
#ifdef CONFIG_PROC_FS
isapnp_proc_done();
@@ -2052,23 +2057,14 @@ static int __init isapnp_do_reserve_irq(int irq)
static void __init isapnp_pci_init(void)
{
- int devfn;
struct pci_dev *dev;
-
- for (devfn = 0; devfn < 255; devfn++) {
- dev = pci_find_slot(0, devfn);
- if (dev != NULL)
- break;
- }
- if (dev == NULL)
- return;
- while (dev) {
+
+ pci_for_each_dev(dev) {
#ifdef ISAPNP_DEBUG
printk("PCI: reserved IRQ: %i\n", dev->irq);
#endif
if (dev->irq > 0)
isapnp_do_reserve_irq(dev->irq);
- dev = dev->next;
}
}
@@ -2095,7 +2091,6 @@ int __init isapnp_init(void)
{
int cards;
struct pci_bus *card;
- struct pci_dev *dev;
if (isapnp_disable) {
isapnp_detected = 0;
@@ -2144,15 +2139,18 @@ int __init isapnp_init(void)
}
isapnp_build_device_list();
cards = 0;
- for (card = isapnp_cards; card; card = card->next)
+
+ isapnp_for_each_card(card) {
cards++;
- if (isapnp_verbose) {
- for (card = isapnp_cards; card; card = card->next) {
+ if (isapnp_verbose) {
+ struct list_head *devlist;
printk( "isapnp: Card '%s'\n", card->name[0]?card->name:"Unknown");
if (isapnp_verbose < 2)
continue;
- for (dev = card->devices; dev; dev = dev->next)
+ for (devlist = card->devices.next; devlist != &card->devices; devlist = devlist->next) {
+ struct pci_dev *dev = pci_dev_b(devlist);
printk("isapnp: Device '%s'\n", dev->name[0]?card->name:"Unknown");
+ }
}
}
if (cards) {
diff --git a/drivers/pnp/isapnp_proc.c b/drivers/pnp/isapnp_proc.c
index f78e3488b..1a742823f 100644
--- a/drivers/pnp/isapnp_proc.c
+++ b/drivers/pnp/isapnp_proc.c
@@ -19,10 +19,6 @@
*
*/
-static void *isapnp_alloc(long size);
-struct pci_bus *isapnp_cards;
-struct pci_dev *isapnp_devices;
-
struct isapnp_info_buffer {
char *buffer; /* pointer to begin of buffer */
char *curr; /* current position in buffer */
@@ -204,8 +200,6 @@ static struct file_operations isapnp_info_entry_operations =
isapnp_info_entry_release, /* release */
NULL, /* can't fsync */
NULL, /* fasync */
- NULL, /* check_media_change */
- NULL, /* revalidate */
NULL, /* lock */
};
@@ -539,10 +533,12 @@ static void isapnp_print_device(isapnp_info_buffer_t *buffer, struct pci_dev *de
static void isapnp_info_read(isapnp_info_buffer_t *buffer)
{
- struct pci_bus *card;
- struct pci_dev *dev;
+ struct list_head *card_list;
- for (card = isapnp_cards; card; card = card->next) {
+ for (card_list = isapnp_cards.next; card_list != &isapnp_cards; card_list = card_list->next) {
+ struct pci_bus *card = list_entry(card_list, struct pci_bus, node);
+ struct list_head *dev_list;
+
isapnp_printf(buffer, "Card %i '", card->number);
isapnp_print_devid(buffer, card->vendor, card->device);
isapnp_printf(buffer, ":%s'", card->name[0]?card->name:"Unknown");
@@ -551,8 +547,10 @@ static void isapnp_info_read(isapnp_info_buffer_t *buffer)
if (card->productver)
isapnp_printf(buffer, " Product version %x.%x", card->productver >> 4, card->productver & 0x0f);
isapnp_printf(buffer,"\n");
- for (dev = card->devices; dev; dev = dev->sibling)
+ for (dev_list = card->devices.next; dev_list != &card->devices; dev_list = dev_list->next) {
+ struct pci_dev *dev = list_entry(dev_list, struct pci_dev, bus_list);
isapnp_print_device(buffer, dev);
+ }
}
}
@@ -640,14 +638,17 @@ static int isapnp_set_card(char *line)
static int isapnp_select_csn(char *line)
{
int csn;
+ struct list_head *list;
char index[16], value[32];
isapnp_info_device = NULL;
isapnp_get_str(index, line, sizeof(index));
csn = simple_strtoul(index, NULL, 0);
- for (isapnp_info_card = isapnp_cards; isapnp_info_card; isapnp_info_card = isapnp_info_card->next)
+ for (list = isapnp_cards.next; list != &isapnp_cards; list = list->next) {
+ isapnp_info_card = list_entry(list, struct pci_bus, node);
if (isapnp_info_card->number == csn)
break;
+ }
if (isapnp_info_card == NULL) {
printk("isapnp: cannot find CSN %i\n", csn);
return 1;
diff --git a/drivers/pnp/quirks.c b/drivers/pnp/quirks.c
new file mode 100644
index 000000000..028eda7da
--- /dev/null
+++ b/drivers/pnp/quirks.c
@@ -0,0 +1,75 @@
+/*
+ * This file contains quirk handling code for ISAPnP devices
+ * Some devices do not report all their resources, and need to have extra
+ * resources added. This is most easily accomplished at initialisation time
+ * when building up the resource structure for the first time.
+ *
+ * Copyright (c) 2000 Peter Denison <peterd@pnd-pc.demon.co.uk>
+ *
+ * Heavily based on PCI quirks handling which is
+ *
+ * Copyright (c) 1999 Martin Mares <mj@suse.cz>
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/isapnp.h>
+
+static void __init quirk_awe32_resources(struct pci_dev *dev)
+{
+ struct isapnp_port *port, *port2, *port3;
+ struct isapnp_resources *res = dev->sysdata;
+
+ /*
+ * Unfortunately the isapnp_add_port_resource is too tightly bound
+ * into the PnP discovery sequence, and cannot be used. Link in the
+ * two extra ports (at offset 0x400 and 0x800 from the one given) by
+ * hand.
+ */
+ for ( ; res ; res = res->alt ) {
+ port2 = isapnp_alloc(sizeof(struct isapnp_port));
+ port3 = isapnp_alloc(sizeof(struct isapnp_port));
+ if (!port2 || !port3)
+ return;
+ port = res->port;
+ memcpy(port2, port, sizeof(struct isapnp_port));
+ memcpy(port3, port, sizeof(struct isapnp_port));
+ port->next = port2;
+ port2->next = port3;
+ port2->min += 0x400;
+ port2->max += 0x400;
+ port3->min += 0x800;
+ port3->max += 0x800;
+ }
+ printk(KERN_INFO "ISAPnP: AWE32 quirk - adding two ports\n");
+}
+
+
+/*
+ * ISAPnP Quirks
+ * Cards or devices that need some tweaking due to broken hardware
+ */
+
+static struct isapnp_fixup isapnp_fixups[] __initdata = {
+ { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0021),
+ quirk_awe32_resources },
+ { 0 }
+};
+
+void isapnp_fixup_device(struct pci_dev *dev)
+{
+ int i = 0;
+
+ while (isapnp_fixups[i].vendor != 0) {
+ if ((isapnp_fixups[i].vendor == dev->vendor) &&
+ (isapnp_fixups[i].device == dev->device)) {
+ printk(KERN_DEBUG "PnP: Calling quirk for %s\n",
+ dev->slot_name);
+ isapnp_fixups[i].quirk_function(dev);
+ }
+ i++;
+ }
+}
+