RFC: PCI probing

Jeff Garzik (jgarzik@pobox.com)
Wed, 15 Sep 1999 02:31:49 -0400


This is a multi-part message in MIME format.
--------------B7211832D15538D64F4A746E
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

Attached is a patch against 2.3.18ac3, which adds simple PCI probing,
and demonstrates the usage of it in drivers/block/cpqarray.c, which
shrinks quite a bit. Comments welcome.

Jeff

-- 
Custom driver development	|    Never worry about theory as long
Open source programming		|    as the machinery does what it's
				|    supposed to do.  -- R. A. Heinlein
--------------B7211832D15538D64F4A746E
Content-Type: text/plain; charset=us-ascii;
 name="patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="patch"

diff -urN linux.ac3/drivers/block/cpqarray.c linux/drivers/block/cpqarray.c --- linux.ac3/drivers/block/cpqarray.c Wed Sep 8 14:51:22 1999 +++ linux/drivers/block/cpqarray.c Wed Sep 15 02:22:40 1999 @@ -87,6 +87,16 @@ { 0x40510E11, "Smart Array 4250ES", &smart4_access }, }; +static struct pci_simple_probe_entry cpqarray_pci_table[] __initdata = { + { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_COMPAQ_42XX, + 0, 0, NULL }, + { PCI_VENDOR_ID_NCR, PCI_DEVICE_ID_NCR_53C1510, + PCI_VENDOR_ID_COMPAQ, 0, NULL }, + { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_SMART2P, + 0, 0, NULL }, + { 0, 0, } /* terminate table */ +}; + static struct hd_struct * ida; static int * ida_sizes; static int * ida_blocksizes; @@ -105,8 +115,9 @@ #define DBGPX(s) do { } while(0) void cpqarray_init(void); -static int cpqarray_pci_detect(void); -static int cpqarray_pci_init(ctlr_info_t *c, unchar bus, unchar device_fn); +static int cpqarray_pci_probe_cb (struct pci_dev *dev, int match_num, + void *dev_data, void *drvr_data); +static int cpqarray_pci_init(ctlr_info_t *c, struct pci_dev *pdev); static ulong remap_pci_mem(ulong base, ulong size); static int cpqarray_eisa_detect(void); static int pollcomplete(int ctlr); @@ -381,7 +392,7 @@ int i; /* detect controllers */ - cpqarray_pci_detect(); + pci_simple_probe (cpqarray_pci_table, 9, cpqarray_pci_probe_cb, NULL); cpqarray_eisa_detect(); if (nr_ctlr == 0) @@ -504,128 +515,54 @@ } /* - * Find the controller and initialize it - * Cannot use the class code to search, because older array controllers use - * 0x018000 and new ones use 0x010400. So I might as well search for each - * each device IDs, being there are only going to be three of them. + * Receive a callback from the PCI probe helper. Allocate the + * structure necessary to hold controller info, then pass + * control to cpqarray_pci_init(). */ -static int cpqarray_pci_detect(void) +static int cpqarray_pci_probe_cb (struct pci_dev *dev, int match_num, + void *dev_data, void *drvr_data) { - int index; - unchar bus=0, dev_fn=0; + int rc; - /* This seems dumb, surely we could use an array of types to match ?? */ - - for(index=0; ; index++) { - if (pcibios_find_device(PCI_VENDOR_ID_DEC, - PCI_DEVICE_ID_COMPAQ_42XX, index, &bus, &dev_fn)) - break; - printk(KERN_DEBUG "42XX Device has been found at %x %x\n", - bus, dev_fn); - if (index == 1000000) break; - if (nr_ctlr == 8) { - printk("This driver supports a maximum of " + if (match_num > 8) { + printk(KERN_ERR "cpqarray: This driver supports a maximum of " "8 controllers.\n"); - break; - } - - hba[nr_ctlr] = kmalloc(sizeof(ctlr_info_t), GFP_KERNEL); - if(hba[nr_ctlr]==NULL) - { - printk(KERN_ERR "cpqarray: out of memory.\n"); - continue; - } - memset(hba[nr_ctlr], 0, sizeof(ctlr_info_t)); - if (cpqarray_pci_init(hba[nr_ctlr], bus, dev_fn) != 0) - continue; - sprintf(hba[nr_ctlr]->devname, "ida%d", nr_ctlr); - hba[nr_ctlr]->ctlr = nr_ctlr; - nr_ctlr++; + return 0; } - - for(index=0; ; index++) { - unsigned short subvendor=0; - - if (pcibios_find_device(PCI_VENDOR_ID_NCR, - PCI_DEVICE_ID_NCR_53C1510, index, &bus, &dev_fn)) - break; - printk(KERN_DEBUG "Integrated RAID Chip has been found at %x %x\n", - bus, dev_fn); - if(pcibios_read_config_word(bus, dev_fn, - PCI_SUBSYSTEM_VENDOR_ID, &subvendor)) - { - printk(KERN_DEBUG "cpqarray failed to read subvendor\n"); - break; - } - if(subvendor != PCI_VENDOR_ID_COMPAQ) - break; - printk(KERN_DEBUG "Its a compaq RAID Chip\n"); - if (index == 1000000) break; - if (nr_ctlr == 8) { - printk("This driver supports a maximum of " - "8 controllers.\n"); - break; - } - - hba[nr_ctlr] = kmalloc(sizeof(ctlr_info_t), GFP_KERNEL); - if(hba[nr_ctlr]==NULL) - { - printk(KERN_ERR "cpqarray: out of memory.\n"); - continue; - } - memset(hba[nr_ctlr], 0, sizeof(ctlr_info_t)); - /* DOESNT THIS LEAK MEMORY ?????? - AC */ - if (cpqarray_pci_init(hba[nr_ctlr], bus, dev_fn) != 0) - continue; - sprintf(hba[nr_ctlr]->devname, "ida%d", nr_ctlr); - hba[nr_ctlr]->ctlr = nr_ctlr; - nr_ctlr++; - } - - for(index=0; ; index++) { - if (pcibios_find_device(PCI_VENDOR_ID_COMPAQ, - PCI_DEVICE_ID_COMPAQ_SMART2P, index, &bus, &dev_fn)) - break; - - if (index == 1000000) break; - if (nr_ctlr == 8) { - printk("This driver supports a maximum of " - "8 controllers.\n"); - break; - } - - hba[nr_ctlr] = kmalloc(sizeof(ctlr_info_t), GFP_KERNEL); - if(hba[nr_ctlr]==NULL) - { - printk(KERN_ERR "cpqarray: out of memory.\n"); - continue; - } - memset(hba[nr_ctlr], 0, sizeof(ctlr_info_t)); - if (cpqarray_pci_init(hba[nr_ctlr], bus, dev_fn) != 0) - continue; - sprintf(hba[nr_ctlr]->devname, "ida%d", nr_ctlr); - hba[nr_ctlr]->ctlr = nr_ctlr; - nr_ctlr++; + hba[nr_ctlr] = kmalloc(sizeof(ctlr_info_t), GFP_KERNEL); + if(hba[nr_ctlr]==NULL) + { + printk(KERN_ERR "cpqarray: out of memory.\n"); + return -ENOMEM; } + memset(hba[nr_ctlr], 0, sizeof(ctlr_info_t)); - return nr_ctlr; + /* DOESNT THIS LEAK MEMORY ?????? - AC */ + rc = cpqarray_pci_init(hba[nr_ctlr], dev); + if (rc != 0) + return rc; + + sprintf(hba[nr_ctlr]->devname, "ida%d", nr_ctlr); + hba[nr_ctlr]->ctlr = nr_ctlr; + nr_ctlr++; + + return 0; } + /* * Find the IO address of the controller, its IRQ and so forth. Fill * in some basic stuff into the ctlr_info_t structure. */ -static int cpqarray_pci_init(ctlr_info_t *c, unchar bus, unchar device_fn) +static int cpqarray_pci_init(ctlr_info_t *c, struct pci_dev *pdev) { ushort vendor_id, device_id, command; unchar cache_line_size, latency_timer; unchar irq, revision; uint addr[6]; __u32 board_id; - struct pci_dev *pdev; int i; - pdev = pci_find_slot(bus, device_fn); vendor_id = pdev->vendor; device_id = pdev->device; irq = pdev->irq; @@ -633,16 +570,12 @@ for(i=0; i<6; i++) addr[i] = pdev->resource[i].flags; - (void) pcibios_read_config_word(bus, device_fn, - PCI_COMMAND,&command); - (void) pcibios_read_config_byte(bus, device_fn, - PCI_CLASS_REVISION,&revision); - (void) pcibios_read_config_byte(bus, device_fn, - PCI_CACHE_LINE_SIZE, &cache_line_size); - (void) pcibios_read_config_byte(bus, device_fn, - PCI_LATENCY_TIMER, &latency_timer); + (void) pci_read_config_word(pdev,PCI_COMMAND,&command); + (void) pci_read_config_byte(pdev,PCI_CLASS_REVISION,&revision); + (void) pci_read_config_byte(pdev,PCI_CACHE_LINE_SIZE, &cache_line_size); + (void) pci_read_config_byte(pdev,PCI_LATENCY_TIMER, &latency_timer); - (void) pcibios_read_config_dword(bus, device_fn, 0x2c, &board_id); + (void) pci_read_config_dword(pdev, 0x2c, &board_id); DBGINFO( printk("vendor_id = %x\n", vendor_id); diff -urN linux.ac3/drivers/pci/Makefile linux/drivers/pci/Makefile --- linux.ac3/drivers/pci/Makefile Wed Sep 1 17:37:25 1999 +++ linux/drivers/pci/Makefile Wed Sep 15 02:18:12 1999 @@ -25,6 +25,6 @@ L_OBJS += proc.o endif -L_OBJS += compat.o quirks.o names.o syscall.o setup.o +L_OBJS += compat.o quirks.o names.o syscall.o setup.o helper.o include $(TOPDIR)/Rules.make diff -urN linux.ac3/drivers/pci/helper.c linux/drivers/pci/helper.c --- linux.ac3/drivers/pci/helper.c Wed Dec 31 19:00:00 1969 +++ linux/drivers/pci/helper.c Wed Sep 15 02:17:39 1999 @@ -0,0 +1,55 @@ +/* + * $Id$ + * + * PCI helpers, see include/linux/pci.h for further explanation. + * + * Copyright 1999 Jeff Garzik <jgarzik@pobox.com> + */ + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/pci.h> + + +int pci_simple_probe (struct pci_simple_probe_entry *list, size_t match_limit, + pci_simple_probe_callback cb, void *drvr_data) +{ + struct pci_dev *dev; + struct pci_simple_probe_entry *ent; + size_t matches = 0; + unsigned short vendor, device; + int rc; + + if (!list || !cb) + return -1; + + ent = list; + vendor = ent->vendor; + device = ent->device; + while (vendor && device) { + dev = pci_find_device (vendor, device, NULL); + for (; dev; dev = pci_find_device (vendor, device, dev)) { + + if (((ent->subsys_vendor) && + (ent->subsys_vendor != dev->subsystem_vendor)) || + ((ent->subsys_device) && + (ent->subsys_device != dev->subsystem_device))) + continue; + + rc = (* cb) (dev, matches, ent->dev_data, drvr_data); + if (rc < 0) + return rc; + + matches++; + + if (match_limit && match_limit == matches) + return matches; + } + + ent++; + vendor = ent->vendor; + device = ent->device; + } + + return matches; +} diff -urN linux.ac3/include/linux/pci.h linux/include/linux/pci.h --- linux.ac3/include/linux/pci.h Wed Sep 15 01:00:42 1999 +++ linux/include/linux/pci.h Wed Sep 15 02:15:35 1999 @@ -1598,5 +1598,25 @@ void pci_fixup_device(int pass, struct pci_dev *dev); + +/* + * simple PCI probing for drivers + */ + +typedef int (*pci_simple_probe_callback) (struct pci_dev *dev, int match_num, + void *dev_data, void *drvr_data); + +struct pci_simple_probe_entry { + unsigned short vendor; /* vendor id, PCI_ANY_ID, or 0 for last entry */ + unsigned short device; /* device id, PCI_ANY_ID, or 0 for last entry */ + unsigned short subsys_vendor; /* subsystem vendor id, 0 for don't care */ + unsigned short subsys_device; /* subsystem device id, 0 for don't care */ + void *dev_data; /* driver-private, entry-specific data */ +}; + +int pci_simple_probe (struct pci_simple_probe_entry *list, size_t match_limit, + pci_simple_probe_callback cb, void *drvr_data); + + #endif /* __KERNEL__ */ #endif /* LINUX_PCI_H */

--------------B7211832D15538D64F4A746E--

- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.rutgers.edu Please read the FAQ at http://www.tux.org/lkml/