linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [CFT] PCI probing for cardbus
@ 2003-03-05  0:36 Russell King
  2003-03-05  0:39 ` [CFT] PCI probing for cardbus (1/5) Russell King
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: Russell King @ 2003-03-05  0:36 UTC (permalink / raw)
  To: Linux Kernel List

Could people please test these patches and let me know of any problems.
They work for me.  There should be no user-visible changes in behaviour
from this set, apart from drivers/hotplug breaking again.  These are all
against base 2.5.63, and will follow in separate mails.

pci-1.diff

- Separate out bus resource allocator (pci_bus_alloc_resource)
- Provide pci_enable_bridges to setup command register for all
  pci bridges

pci-2.diff

- Eliminate the stack allocation of a struct pci_dev, and make
  pci_scan_slot() take a bus and a devfn argument.
- Add "dev->multifunction" to indicate whether this is a multifunction
  device.
- Run header fixups before inserting the new pci device into any
  device lists or announcing it to the drivers.
- Convert some more stuff to use the list_for_each* macro(s).

  No real behavioural change yet.

pci-3.diff

  This is the first patch which we start breaking things.

  The pci_find* functions search using the following lists:
        bus->children   (for subordinate buses)
        pci_root_buses  (for all root buses)
        pci_devices     (for devices)

  This leaves one list which we can add devices to without any drivers
  finding the new devices before we've finished with them.  (Jeff - some
  drivers do go scanning the bus_list, notably de4x5.c:srom_search.)

- initialise bus->node list head.

- pci_scan_slot will scan the specified slot, and add the discovered
  devices to the bus->devices list only.  These devices will not
  appear on the global device list, and do not show in sysfs, procfs.
  pci_scan_slot returns the number of functions found.  If you want
  to find the devices, you have to scan bus->devices and look for
  devices where list_empty(&dev->global_list) is true.

- new function "pci_bus_add_devices" adds newly discovered devices
  to the global device lists, and handles the sysfs and procfs
  stuff, making the devices available to drivers.  All our buses
  which have an empty list head are treated as "new" (since they
  are not attached to the parent buses list of children) and are
  also added.  Currently, no buses will be in this state when this
  function is called.

- new function "pci_scan_child_bus" scans a complete bus, building
  a list of devices on bus->devices only, performing bus fixups
  via pcibios_fixup_bus() and scanning behind bridges.  It does
  make devices externally visible.

- pci_do_scan_bus retains its original behaviour - ie, it scans
  and makes devices available immediately.

pci-4.diff

- Convert setup-bus.c resource allocation to scan bus->devices rather
  than bus->children.  As noted previously, newly discovered child
  buses will not be on the parents list of children buses, so when
  we're trying to assign resources, we need to scan the bus for
  devices with subordinate buses rather than using the list of children
  buses.

pci-5.diff

  Now we tackle pci_add_new_bus and pci_scan_bridge.  The hotplug code
  currently uses this, but I'd like it to die off; pci_scan_bridge()
  should be used to scan behind bridges.  This may mean hotplug needs
  some changes to pci_scan_bridge - if so, we need to find out what
  changes are required and fix it.

  pci_alloc_child_bus() does what pci_add_new_bus() did, except it
  doesn't attach the new bus to the parents list of child buses.  The
  only way this bus can be reached from the parent bus is by scanning
  the parents devices list, and locating a device with a non-NULL
  subordinate bus.  The only code which should be doing this is the
  PCI code.

  Since the new bus will have an empty list head for bus->node, we can
  detect unattached buses prety easily.  (see pci-3.diff.  maybe we want
  a pci_bus_unattached() to make it more readable?)

  pci_scan_bridge() changes slightly - we use our new pci_scan_child_bus()
  function from pci-3.diff, which doesn't attach devices to the global
  tree.  This means callers of pci_scan_child_bus() and pci_scan_bridge()
  (ie, hotplug) will need to call pci_bus_add_devices().


-- 
Russell King (rmk@arm.linux.org.uk)                The developer of ARM Linux
             http://www.arm.linux.org.uk/personal/aboutme.html


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [CFT] PCI probing for cardbus (1/5)
  2003-03-05  0:36 [CFT] PCI probing for cardbus Russell King
@ 2003-03-05  0:39 ` Russell King
  2003-03-05  0:39 ` [CFT] PCI probing for cardbus (2/5) Russell King
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Russell King @ 2003-03-05  0:39 UTC (permalink / raw)
  To: Linux Kernel List

diff -uN orig/drivers/pci/Makefile linux/drivers/pci/Makefile
--- orig/drivers/pci/Makefile	Tue Feb 25 10:57:49 2003
+++ linux/drivers/pci/Makefile	Sat Mar  1 15:41:20 2003
@@ -2,7 +2,7 @@
 # Makefile for the PCI bus specific drivers.
 #
 
-obj-y		+= access.o probe.o pci.o pool.o quirks.o \
+obj-y		+= access.o bus.o probe.o pci.o pool.o quirks.o \
 			names.o pci-driver.o search.o hotplug.o \
 			pci-sysfs.o
 obj-$(CONFIG_PM)  += power.o
diff -uN orig/drivers/pci/bus.c linux/drivers/pci/bus.c
--- orig/drivers/pci/bus.c	Thu Jan  1 01:00:00 1970
+++ linux/drivers/pci/bus.c	Sat Mar  1 16:36:43 2003
@@ -0,0 +1,81 @@
+/*
+ *	drivers/pci/bus.c
+ *
+ * From setup-res.c, by:
+ *	Dave Rusling (david.rusling@reo.mts.dec.com)
+ *	David Mosberger (davidm@cs.arizona.edu)
+ *	David Miller (davem@redhat.com)
+ *	Ivan Kokshaysky (ink@jurassic.park.msu.ru)
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+
+/**
+ * pci_bus_alloc_resource - allocate a resource from a parent bus
+ * @bus: PCI bus
+ * @res: resource to allocate
+ * @size: size of resource to allocate
+ * @align: alignment of resource to allocate
+ * @min: minimum /proc/iomem address to allocate
+ * @type_mask: IORESOURCE_* type flags
+ * @alignf: resource alignment function
+ * @alignf_data: data argument for resource alignment function
+ *
+ * Given the PCI bus a device resides on, the size, minimum address,
+ * alignment and type, try to find an acceptable resource allocation
+ * for a specific device resource.
+ */
+int
+pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
+	unsigned long size, unsigned long align, unsigned long min,
+	unsigned int type_mask,
+	void (*alignf)(void *, struct resource *,
+			unsigned long, unsigned long),
+	void *alignf_data)
+{
+	int i, ret = -ENOMEM;
+
+	type_mask |= IORESOURCE_IO | IORESOURCE_MEM;
+
+	for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) {
+		struct resource *r = bus->resource[i];
+		if (!r)
+			continue;
+
+		/* type_mask must match */
+		if ((res->flags ^ r->flags) & type_mask)
+			continue;
+
+		/* We cannot allocate a non-prefetching resource
+		   from a pre-fetching area */
+		if ((r->flags & IORESOURCE_PREFETCH) &&
+		    !(res->flags & IORESOURCE_PREFETCH))
+			continue;
+
+		/* Ok, try it out.. */
+		ret = allocate_resource(r, res, size, min, -1, align,
+					alignf, alignf_data);
+		if (ret == 0)
+			break;
+	}
+	return ret;
+}
+
+void pci_enable_bridges(struct pci_bus *bus)
+{
+	struct pci_dev *dev;
+
+	list_for_each_entry(dev, &bus->devices, bus_list) {
+		if (dev->subordinate) {
+			pci_enable_device(dev);
+			pci_set_master(dev);
+			pci_enable_bridges(dev->subordinate);
+		}
+	}
+}
+
+EXPORT_SYMBOL(pci_bus_alloc_resource);
+EXPORT_SYMBOL(pci_enable_bridges);
diff -uN orig/drivers/pci/setup-res.c linux/drivers/pci/setup-res.c
--- orig/drivers/pci/setup-res.c	Sat Dec 28 17:07:35 2002
+++ linux/drivers/pci/setup-res.c	Sat Mar  1 10:11:51 2003
@@ -55,84 +55,47 @@
 	return err;
 }
 
-/*
- * Given the PCI bus a device resides on, try to
- * find an acceptable resource allocation for a
- * specific device resource..
- */
-static int pci_assign_bus_resource(const struct pci_bus *bus,
-	struct pci_dev *dev,
-	struct resource *res,
-	unsigned long size,
-	unsigned long min,
-	unsigned int type_mask,
-	int resno)
+int pci_assign_resource(struct pci_dev *dev, int resno)
 {
-	unsigned long align;
-	int i;
-
-	type_mask |= IORESOURCE_IO | IORESOURCE_MEM;
-	for (i = 0 ; i < PCI_BUS_NUM_RESOURCES; i++) {
-		struct resource *r = bus->resource[i];
-		if (!r)
-			continue;
-
-		/* type_mask must match */
-		if ((res->flags ^ r->flags) & type_mask)
-			continue;
-
-		/* We cannot allocate a non-prefetching resource
-		   from a pre-fetching area */
-		if ((r->flags & IORESOURCE_PREFETCH) &&
-		    !(res->flags & IORESOURCE_PREFETCH))
-			continue;
-
-		/* The bridge resources are special, as their
-		   size != alignment. Sizing routines return
-		   required alignment in the "start" field. */
-		align = (resno < PCI_BRIDGE_RESOURCES) ? size : res->start;
-
-		/* Ok, try it out.. */
-		if (allocate_resource(r, res, size, min, -1, align,
-				      pcibios_align_resource, dev) < 0)
-			continue;
-
-		/* Update PCI config space.  */
-		pcibios_update_resource(dev, r, res, resno);
-		return 0;
-	}
-	return -EBUSY;
-}
-
-int 
-pci_assign_resource(struct pci_dev *dev, int i)
-{
-	const struct pci_bus *bus = dev->bus;
-	struct resource *res = dev->resource + i;
-	unsigned long size, min;
+	struct pci_bus *bus = dev->bus;
+	struct resource *res = dev->resource + resno;
+	unsigned long size, min, align;
+	int ret;
 
 	size = res->end - res->start + 1;
 	min = (res->flags & IORESOURCE_IO) ? PCIBIOS_MIN_IO : PCIBIOS_MIN_MEM;
+	/* The bridge resources are special, as their
+	   size != alignment. Sizing routines return
+	   required alignment in the "start" field. */
+	align = (resno < PCI_BRIDGE_RESOURCES) ? size : res->start;
 
 	/* First, try exact prefetching match.. */
-	if (pci_assign_bus_resource(bus, dev, res, size, min, IORESOURCE_PREFETCH, i) < 0) {
+	ret = pci_bus_alloc_resource(bus, res, size, align, min,
+				     IORESOURCE_PREFETCH,
+				     pcibios_align_resource, dev);
+
+	if (ret < 0 && (res->flags & IORESOURCE_PREFETCH)) {
 		/*
 		 * That failed.
 		 *
 		 * But a prefetching area can handle a non-prefetching
 		 * window (it will just not perform as well).
 		 */
-		if (!(res->flags & IORESOURCE_PREFETCH) || pci_assign_bus_resource(bus, dev, res, size, min, 0, i) < 0) {
-			printk(KERN_ERR "PCI: Failed to allocate resource %d(%lx-%lx) for %s\n",
-			       i, res->start, res->end, dev->slot_name);
-			return -EBUSY;
-		}
+		ret = pci_bus_alloc_resource(bus, res, size, align, min, 0,
+					     pcibios_align_resource, dev);
 	}
 
-	DBGC((KERN_ERR "  got res[%lx:%lx] for resource %d of %s\n", res->start,
-						res->end, i, dev->dev.name));
+	if (ret) {
+		printk(KERN_ERR "PCI: Failed to allocate resource %d(%lx-%lx) for %s\n",
+		       resno, res->start, res->end, dev->slot_name);
+	} else {
+		DBGC((KERN_ERR "  got res[%lx:%lx] for resource %d of %s\n",
+		      res->start, res->end, i, dev->dev.name));
+		/* Update PCI config space.  */
+		pcibios_update_resource(dev, res->parent, res, resno);
+	}
 
-	return 0;
+	return ret;
 }
 
 /* Sort resources by alignment */
--- orig/include/linux/pci.h	Sun Mar  2 22:42:29 2003
+++ linux/include/linux/pci.h	Sat Mar  1 15:36:42 2003
@@ -641,6 +641,16 @@
 int pci_request_region(struct pci_dev *, int, char *);
 void pci_release_region(struct pci_dev *, int);
 
+/* drivers/pci/bus.c */
+
+int pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
+			   unsigned long size, unsigned long align,
+			   unsigned long min, unsigned int type_mask,
+			   void (*alignf)(void *, struct resource *,
+					  unsigned long, unsigned long),
+			   void *alignf_data);
+void pci_enable_bridges(struct pci_bus *bus);
+
 /* New-style probing supporting hot-pluggable devices */
 int pci_register_driver(struct pci_driver *);
 void pci_unregister_driver(struct pci_driver *);

-- 
Russell King (rmk@arm.linux.org.uk)                The developer of ARM Linux
             http://www.arm.linux.org.uk/personal/aboutme.html


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [CFT] PCI probing for cardbus (2/5)
  2003-03-05  0:36 [CFT] PCI probing for cardbus Russell King
  2003-03-05  0:39 ` [CFT] PCI probing for cardbus (1/5) Russell King
@ 2003-03-05  0:39 ` Russell King
  2003-03-05  0:40 ` [CFT] PCI probing for cardbus (3/5) Russell King
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Russell King @ 2003-03-05  0:39 UTC (permalink / raw)
  To: Linux Kernel List

diff -ur orig/drivers/hotplug/acpiphp_glue.c linux/drivers/hotplug/acpiphp_glue.c
--- orig/drivers/hotplug/acpiphp_glue.c	Tue Feb 11 16:10:08 2003
+++ linux/drivers/hotplug/acpiphp_glue.c	Mon Feb 24 11:28:46 2003
@@ -879,7 +879,7 @@
 static int enable_device (struct acpiphp_slot *slot)
 {
 	u8 bus;
-	struct pci_dev dev0, *dev;
+	struct pci_dev *dev;
 	struct pci_bus *child;
 	struct list_head *l;
 	struct acpiphp_func *func;
@@ -902,16 +902,8 @@
 	if (retval)
 		goto err_exit;
 
-	memset(&dev0, 0, sizeof (struct pci_dev));
-
-	dev0.bus = slot->bridge->pci_bus;
-	dev0.devfn = PCI_DEVFN(slot->device, 0);
-	dev0.sysdata = dev0.bus->sysdata;
-	dev0.dev.parent = dev0.bus->dev;
-	dev0.dev.bus = &pci_bus_type;
-
 	/* returned `dev' is the *first function* only! */
-	dev = pci_scan_slot (&dev0);
+	dev = pci_scan_slot(slot->bridge->pci_bus, PCI_DEVFN(slot->device, 0));
 
 	if (!dev) {
 		err("No new device found\n");
diff -ur orig/drivers/hotplug/cpci_hotplug_pci.c linux/drivers/hotplug/cpci_hotplug_pci.c
--- orig/drivers/hotplug/cpci_hotplug_pci.c	Tue Feb 11 16:10:08 2003
+++ linux/drivers/hotplug/cpci_hotplug_pci.c	Mon Feb 24 11:29:36 2003
@@ -601,19 +601,13 @@
 
 	/* Still NULL? Well then scan for it! */
 	if(slot->dev == NULL) {
-		struct pci_dev dev0;
-
 		dbg("pci_dev still null");
-		memset(&dev0, 0, sizeof (struct pci_dev));
-		dev0.bus = slot->bus;
-		dev0.devfn = slot->devfn;
-		dev0.sysdata = slot->bus->self->sysdata;
 
 		/*
 		 * This will generate pci_dev structures for all functions, but
 		 * we will only call this case when lookup fails.
 		 */
-		slot->dev = pci_scan_slot(&dev0);
+		slot->dev = pci_scan_slot(slot->bus, slot->devfn);
 		if(slot->dev == NULL) {
 			err("Could not find PCI device for slot %02x", slot->number);
 			return 0;
diff -ur orig/drivers/hotplug/cpqphp_pci.c linux/drivers/hotplug/cpqphp_pci.c
--- orig/drivers/hotplug/cpqphp_pci.c	Tue Feb 11 16:10:09 2003
+++ linux/drivers/hotplug/cpqphp_pci.c	Mon Feb 24 11:31:07 2003
@@ -250,7 +250,6 @@
 int cpqhp_configure_device (struct controller* ctrl, struct pci_func* func)  
 {
 	unsigned char bus;
-	struct pci_dev dev0;
 	struct pci_bus *child;
 	struct pci_dev* temp;
 	int rc = 0;
@@ -260,20 +259,16 @@
 	memset(&wrapped_dev, 0, sizeof(struct pci_dev_wrapped));
 	memset(&wrapped_bus, 0, sizeof(struct pci_bus_wrapped));
 
-	memset(&dev0, 0, sizeof(struct pci_dev));
-
 	if (func->pci_dev == NULL)
 		func->pci_dev = pci_find_slot(func->bus, (func->device << 3) | (func->function & 0x7));
 
 	//Still NULL ? Well then scan for it !
 	if (func->pci_dev == NULL) {
 		dbg("INFO: pci_dev still null\n");
-		dev0.bus = ctrl->pci_dev->bus;
-		dev0.devfn = (func->device << 3) + (func->function & 0x7);
-		dev0.sysdata = ctrl->pci_dev->sysdata;
 
 		//this will generate pci_dev structures for all functions, but we will only call this case when lookup fails
-		func->pci_dev = pci_scan_slot(&dev0);
+		func->pci_dev = pci_scan_slot(ctrl->pci_dev->bus,
+				 (func->device << 3) + (func->function & 0x7));
 		if (func->pci_dev == NULL) {
 			dbg("ERROR: pci_dev still null\n");
 			return 0;
diff -ur orig/drivers/hotplug/ibmphp_core.c linux/drivers/hotplug/ibmphp_core.c
--- orig/drivers/hotplug/ibmphp_core.c	Tue Feb 11 16:10:09 2003
+++ linux/drivers/hotplug/ibmphp_core.c	Mon Feb 24 11:32:11 2003
@@ -1035,7 +1035,6 @@
 static int ibm_configure_device (struct pci_func *func)
 {
 	unsigned char bus;
-	struct pci_dev dev0;
 	struct pci_bus *child;
 	struct pci_dev *temp;
 	int rc = 0;
@@ -1046,7 +1045,6 @@
 
 	memset (&wrapped_dev, 0, sizeof (struct pci_dev_wrapped));
 	memset (&wrapped_bus, 0, sizeof (struct pci_bus_wrapped));
-	memset (&dev0, 0, sizeof (struct pci_dev));
 
 	if (!(bus_structure_fixup (func->busno)))
 		flag = 1;
@@ -1054,13 +1052,12 @@
 		func->dev = pci_find_slot (func->busno, (func->device << 3) | (func->function & 0x7));
 
 	if (func->dev == NULL) {
-		dev0.bus = ibmphp_find_bus (func->busno);
-		if (!dev0.bus)
+		struct pci_bus *bus = ibmphp_find_bus (func->busno);
+		if (!bus)
 			return 0;
-		dev0.devfn = ((func->device << 3) + (func->function & 0x7));
-		dev0.sysdata = dev0.bus->sysdata;
 
-		func->dev = pci_scan_slot (&dev0);
+		func->dev = pci_scan_slot(bus,
+				 (func->device << 3) + (func->function & 0x7));
 
 		if (func->dev == NULL) {
 			err ("ERROR... : pci_dev still NULL \n");
--- orig/drivers/pci/probe.c	Mon Jan 13 22:32:31 2003
+++ linux/drivers/pci/probe.c	Mon Feb 24 14:01:48 2003
@@ -374,7 +374,8 @@
 	dev->class = class;
 	class >>= 8;
 
-	DBG("Found %02x:%02x [%04x/%04x] %06x %02x\n", dev->bus->number, dev->devfn, dev->vendor, dev->device, class, dev->hdr_type);
+	DBG("Found %02x:%02x [%04x/%04x] %06x %02x\n", dev->bus->number, dev->devfn,
+		dev->vendor, dev->device, class, dev->hdr_type);
 
 	/* "Unknown power state" */
 	dev->current_state = 4;
@@ -427,23 +428,35 @@
  * Read the config data for a PCI device, sanity-check it
  * and fill in the dev structure...
  */
-struct pci_dev * __devinit pci_scan_device(struct pci_dev *temp)
+static struct pci_dev * __devinit
+pci_scan_device(struct pci_bus *bus, int devfn)
 {
 	struct pci_dev *dev;
 	u32 l;
+	u8 hdr_type;
+
+	if (pci_bus_read_config_byte(bus, devfn, PCI_HEADER_TYPE, &hdr_type))
+		return NULL;
 
-	if (pci_read_config_dword(temp, PCI_VENDOR_ID, &l))
+	if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, &l))
 		return NULL;
 
 	/* some broken boards return 0 or ~0 if a slot is empty: */
 	if (l == 0xffffffff || l == 0x00000000 || l == 0x0000ffff || l == 0xffff0000)
 		return NULL;
 
-	dev = kmalloc(sizeof(*dev), GFP_KERNEL);
+	dev = kmalloc(sizeof(struct pci_dev), GFP_KERNEL);
 	if (!dev)
 		return NULL;
 
-	memcpy(dev, temp, sizeof(*dev));
+	memset(dev, 0, sizeof(struct pci_dev));
+	dev->bus = bus;
+	dev->sysdata = bus->sysdata;
+	dev->dev.parent = bus->dev;
+	dev->dev.bus = &pci_bus_type;
+	dev->devfn = devfn;
+	dev->hdr_type = hdr_type & 0x7f;
+	dev->multifunction = !!(hdr_type & 0x80);
 	dev->vendor = l & 0xffff;
 	dev->device = (l >> 16) & 0xffff;
 
@@ -461,42 +474,44 @@
 	strcpy(dev->dev.bus_id,dev->slot_name);
 	dev->dev.dma_mask = &dev->dma_mask;
 
-	device_register(&dev->dev);
 	return dev;
 }
 
-struct pci_dev * __devinit pci_scan_slot(struct pci_dev *temp)
+struct pci_dev * __devinit pci_scan_slot(struct pci_bus *bus, int devfn)
 {
-	struct pci_bus *bus = temp->bus;
-	struct pci_dev *dev;
 	struct pci_dev *first_dev = NULL;
-	int func = 0;
-	int is_multi = 0;
-	u8 hdr_type;
+	int func;
 
-	for (func = 0; func < 8; func++, temp->devfn++) {
-		if (func && !is_multi)		/* not a multi-function device */
-			continue;
-		if (pci_read_config_byte(temp, PCI_HEADER_TYPE, &hdr_type))
-			continue;
-		temp->hdr_type = hdr_type & 0x7f;
+	for (func = 0; func < 8; func++, devfn++) {
+		struct pci_dev *dev;
 
-		dev = pci_scan_device(temp);
+		dev = pci_scan_device(bus, devfn);
 		if (!dev)
 			continue;
-		if (!func) {
-			is_multi = hdr_type & 0x80;
+
+		if (func == 0) {
 			first_dev = dev;
+		} else {
+			dev->multifunction = 1;
 		}
 
+		/* Fix up broken headers */
+		pci_fixup_device(PCI_FIXUP_HEADER, dev);
+
 		/*
 		 * Link the device to both the global PCI device chain and
 		 * the per-bus list of devices and add the /proc entry.
+		 * Note: this also runs the hotplug notifiers (bad!) --rmk
 		 */
+		device_register(&dev->dev);
 		pci_insert_device (dev, bus);
 
-		/* Fix up broken headers */
-		pci_fixup_device(PCI_FIXUP_HEADER, dev);
+		/*
+		 * If this is a single function device,
+		 * don't scan past the first function.
+		 */
+		if (!dev->multifunction)
+			break;
 	}
 	return first_dev;
 }
@@ -507,28 +522,12 @@
 	struct list_head *ln;
 	struct pci_dev *dev;
 
-	dev = kmalloc(sizeof(*dev), GFP_KERNEL);
-	if (!dev) {
-		printk(KERN_ERR "Out of memory in %s\n", __FUNCTION__);
-		return 0;
-	}
-
 	DBG("Scanning bus %02x\n", bus->number);
 	max = bus->secondary;
 
-	/* Create a device template */
-	memset(dev, 0, sizeof(*dev));
-	dev->bus = bus;
-	dev->sysdata = bus->sysdata;
-	dev->dev.parent = bus->dev;
-	dev->dev.bus = &pci_bus_type;
-
 	/* Go find them, Rover! */
-	for (devfn = 0; devfn < 0x100; devfn += 8) {
-		dev->devfn = devfn;
-		pci_scan_slot(dev);
-	}
-	kfree(dev);
+	for (devfn = 0; devfn < 0x100; devfn += 8)
+		pci_scan_slot(bus, devfn);
 
 	/*
 	 * After performing arch-dependent fixup of the bus, look behind
@@ -537,9 +536,9 @@
 	DBG("Fixups for bus %02x\n", bus->number);
 	pcibios_fixup_bus(bus);
 	for (pass=0; pass < 2; pass++)
-		for (ln=bus->devices.next; ln != &bus->devices; ln=ln->next) {
-			dev = pci_dev_b(ln);
-			if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE || dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
+		list_for_each_entry(dev, &bus->devices, bus_list) {
+			if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE ||
+			    dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
 				max = pci_scan_bridge(bus, dev, max, pass);
 		}
 
--- orig/include/linux/pci.h	Fri Feb 21 19:48:58 2003
+++ linux/include/linux/pci.h	Mon Feb 24 12:51:22 2003
@@ -413,6 +413,7 @@
 
 	/* These fields are used by common fixups */
 	unsigned int	transparent:1;	/* Transparent PCI bridge */
+	unsigned int	multifunction:1;/* Part of multi-function device */
 };
 
 #define pci_dev_g(n) list_entry(n, struct pci_dev, global_list)
@@ -548,7 +549,7 @@
 {
 	return pci_alloc_primary_bus_parented(NULL, bus);
 }
-struct pci_dev *pci_scan_slot(struct pci_dev *temp);
+struct pci_dev *pci_scan_slot(struct pci_bus *bus, int devfn);
 int pci_proc_attach_device(struct pci_dev *dev);
 int pci_proc_detach_device(struct pci_dev *dev);
 int pci_proc_attach_bus(struct pci_bus *bus);

-- 
Russell King (rmk@arm.linux.org.uk)                The developer of ARM Linux
             http://www.arm.linux.org.uk/personal/aboutme.html


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [CFT] PCI probing for cardbus (3/5)
  2003-03-05  0:36 [CFT] PCI probing for cardbus Russell King
  2003-03-05  0:39 ` [CFT] PCI probing for cardbus (1/5) Russell King
  2003-03-05  0:39 ` [CFT] PCI probing for cardbus (2/5) Russell King
@ 2003-03-05  0:40 ` Russell King
  2003-03-05  0:40 ` [CFT] PCI probing for cardbus (4/5) Russell King
  2003-03-05  0:40 ` [CFT] PCI probing for cardbus (5/5) Russell King
  4 siblings, 0 replies; 6+ messages in thread
From: Russell King @ 2003-03-05  0:40 UTC (permalink / raw)
  To: Linux Kernel List

diff -u orig/drivers/pci/bus.c linux/drivers/pci/bus.c
--- orig/drivers/pci/bus.c	Mon Mar  3 21:24:55 2003
+++ orig/drivers/pci/bus.c	Mon Mar  3 21:26:41 2003
@@ -12,6 +12,10 @@
 #include <linux/pci.h>
 #include <linux/errno.h>
 #include <linux/ioport.h>
+#include <linux/proc_fs.h>
+#include <linux/init.h>
+
+#include "pci.h"
 
 /**
  * pci_bus_alloc_resource - allocate a resource from a parent bus
@@ -64,6 +68,47 @@
 	return ret;
 }
 
+/**
+ * pci_bus_add_devices - insert newly discovered PCI devices
+ * @bus: bus to check for new devices
+ *
+ * Add newly discovered PCI devices (which are on the bus->devices
+ * list) to the global PCI device list, add the sysfs and procfs
+ * entries.  Where a bridge is found, add the discovered bus to
+ * the parents list of child buses, and recurse.
+ *
+ * Call hotplug for each new devices.
+ */
+void __devinit pci_bus_add_devices(struct pci_bus *bus)
+{
+	struct pci_dev *dev;
+
+	list_for_each_entry(dev, &bus->devices, bus_list) {
+		/*
+		 * Skip already-present devices (which are on the
+		 * global device list.)
+		 */
+		if (!list_empty(&dev->global_list))
+			continue;
+
+		device_register(&dev->dev);
+		list_add_tail(&dev->global_list, &pci_devices);
+#ifdef CONFIG_PROC_FS
+		pci_proc_attach_device(dev);
+#endif
+		pci_create_sysfs_dev_files(dev);
+
+		/*
+		 * If there is an unattached subordinate bus, attach
+		 * it and then scan for unattached PCI devices.
+		 */
+		if (dev->subordinate && list_empty(&dev->subordinate->node)) {
+			list_add_tail(&dev->subordinate->node, &dev->bus->children);
+			pci_bus_add_devices(dev->subordinate);
+		}
+	}
+}
+
 void pci_enable_bridges(struct pci_bus *bus)
 {
 	struct pci_dev *dev;
@@ -78,4 +123,5 @@
 }
 
 EXPORT_SYMBOL(pci_bus_alloc_resource);
+EXPORT_SYMBOL(pci_bus_add_devices);
 EXPORT_SYMBOL(pci_enable_bridges);
diff -u orig/drivers/pci/probe.c linux/drivers/pci/probe.c
--- orig/drivers/pci/probe.c	Mon Mar  3 21:24:47 2003
+++ linux/drivers/pci/probe.c	Mon Mar  3 21:25:47 2003
@@ -221,6 +221,7 @@
 	b = kmalloc(sizeof(*b), GFP_KERNEL);
 	if (b) {
 		memset(b, 0, sizeof(*b));
+		INIT_LIST_HEAD(&b->node);
 		INIT_LIST_HEAD(&b->children);
 		INIT_LIST_HEAD(&b->devices);
 	}
@@ -477,10 +478,18 @@
 	return dev;
 }
 
-struct pci_dev * __devinit pci_scan_slot(struct pci_bus *bus, int devfn)
+/**
+ * pci_scan_slot - scan a PCI slot on a bus for devices.
+ * @bus: PCI bus to scan
+ * @devfn: slot number to scan (must have zero function.)
+ *
+ * Scan a PCI slot on the specified PCI bus for devices, adding
+ * discovered devices to the @bus->devices list.  New devices
+ * will have an empty dev->global_list head.
+ */
+int __devinit pci_scan_slot(struct pci_bus *bus, int devfn)
 {
-	struct pci_dev *first_dev = NULL;
-	int func;
+	int func, nr = 0;
 
 	for (func = 0; func < 8; func++, devfn++) {
 		struct pci_dev *dev;
@@ -489,22 +498,19 @@
 		if (!dev)
 			continue;
 
-		if (func == 0) {
-			first_dev = dev;
-		} else {
+		if (func != 0)
 			dev->multifunction = 1;
-		}
 
 		/* Fix up broken headers */
 		pci_fixup_device(PCI_FIXUP_HEADER, dev);
 
 		/*
-		 * Link the device to both the global PCI device chain and
-		 * the per-bus list of devices and add the /proc entry.
-		 * Note: this also runs the hotplug notifiers (bad!) --rmk
+		 * Add the device to our list of discovered devices
+		 * and the bus list for fixup functions, etc.
 		 */
-		device_register(&dev->dev);
-		pci_insert_device (dev, bus);
+		INIT_LIST_HEAD(&dev->global_list);
+		list_add_tail(&dev->bus_list, &bus->devices);
+		nr++;
 
 		/*
 		 * If this is a single function device,
@@ -513,17 +519,15 @@
 		if (!dev->multifunction)
 			break;
 	}
-	return first_dev;
+	return nr;
 }
 
-unsigned int __devinit pci_do_scan_bus(struct pci_bus *bus)
+static unsigned int __devinit pci_scan_child_bus(struct pci_bus *bus)
 {
-	unsigned int devfn, max, pass;
-	struct list_head *ln;
+	unsigned int devfn, pass, max = bus->secondary;
 	struct pci_dev *dev;
 
 	DBG("Scanning bus %02x\n", bus->number);
-	max = bus->secondary;
 
 	/* Go find them, Rover! */
 	for (devfn = 0; devfn < 0x100; devfn += 8)
@@ -550,6 +554,20 @@
 	 * Return how far we've got finding sub-buses.
 	 */
 	DBG("Bus scan for %02x returning with max=%02x\n", bus->number, max);
+	return max;
+}
+
+unsigned int __devinit pci_do_scan_bus(struct pci_bus *bus)
+{
+	unsigned int max;
+
+	max = pci_scan_child_bus(bus);
+
+	/*
+	 * Make the discovered devices available.
+	 */
+	pci_bus_add_devices(bus);
+
 	return max;
 }
 
--- orig/include/linux/pci.h	Tue Feb 25 23:34:29 2003
+++ linux/include/linux/pci.h	Tue Feb 25 23:37:27 2003
@@ -549,7 +549,8 @@
 {
 	return pci_alloc_primary_bus_parented(NULL, bus);
 }
-struct pci_dev *pci_scan_slot(struct pci_bus *bus, int devfn);
+int pci_scan_slot(struct pci_bus *bus, int devfn);
+void pci_bus_add_devices(struct pci_bus *bus);
 int pci_proc_attach_device(struct pci_dev *dev);
 int pci_proc_detach_device(struct pci_dev *dev);
 int pci_proc_attach_bus(struct pci_bus *bus);

-- 
Russell King (rmk@arm.linux.org.uk)                The developer of ARM Linux
             http://www.arm.linux.org.uk/personal/aboutme.html


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [CFT] PCI probing for cardbus (4/5)
  2003-03-05  0:36 [CFT] PCI probing for cardbus Russell King
                   ` (2 preceding siblings ...)
  2003-03-05  0:40 ` [CFT] PCI probing for cardbus (3/5) Russell King
@ 2003-03-05  0:40 ` Russell King
  2003-03-05  0:40 ` [CFT] PCI probing for cardbus (5/5) Russell King
  4 siblings, 0 replies; 6+ messages in thread
From: Russell King @ 2003-03-05  0:40 UTC (permalink / raw)
  To: Linux Kernel List

diff -u orig/drivers/pci/setup-bus.c linux/drivers/pci/setup-bus.c
--- orig/drivers/pci/setup-bus.c	Tue Feb 25 10:57:49 2003
+++ linux/drivers/pci/setup-bus.c	Tue Feb 25 11:17:33 2003
@@ -350,6 +350,7 @@
 {
 	struct pci_bus *b;
 	int found_vga = pbus_assign_resources_sorted(bus);
+	struct pci_dev *dev;
 
 	if (found_vga) {
 		/* Propagate presence of the VGA to upstream bridges */
@@ -357,9 +358,12 @@
			b->resource[0]->flags |= IORESOURCE_BUS_HAS_VGA;
 		}
 	}
-	list_for_each_entry(b, &bus->children, node) {
-		pci_bus_assign_resources(b);
-		pci_setup_bridge(b);
+	list_for_each_entry(dev, &bus->devices, bus_list) {
+		b = dev->subordinate;
+		if (b) {
+			pci_bus_assign_resources(b);
+			pci_setup_bridge(b);
+		}
 	}
 }
 EXPORT_SYMBOL(pci_bus_assign_resources);

-- 
Russell King (rmk@arm.linux.org.uk)                The developer of ARM Linux
             http://www.arm.linux.org.uk/personal/aboutme.html


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [CFT] PCI probing for cardbus (5/5)
  2003-03-05  0:36 [CFT] PCI probing for cardbus Russell King
                   ` (3 preceding siblings ...)
  2003-03-05  0:40 ` [CFT] PCI probing for cardbus (4/5) Russell King
@ 2003-03-05  0:40 ` Russell King
  4 siblings, 0 replies; 6+ messages in thread
From: Russell King @ 2003-03-05  0:40 UTC (permalink / raw)
  To: Linux Kernel List

diff -u orig/drivers/pci/probe.c linux/drivers/pci/probe.c
--- orig/drivers/pci/probe.c	Tue Feb 25 23:46:00 2003
+++ linux/drivers/pci/probe.c	Tue Feb 25 23:47:51 2003
@@ -228,41 +228,57 @@
 	return b;
 }
 
-struct pci_bus * __devinit pci_add_new_bus(struct pci_bus *parent, struct pci_dev *dev, int busnr)
+static struct pci_bus * __devinit
+pci_alloc_child_bus(struct pci_bus *parent, struct pci_dev *bridge, int busnr)
 {
 	struct pci_bus *child;
-	int i;
 
 	/*
 	 * Allocate a new bus, and inherit stuff from the parent..
 	 */
 	child = pci_alloc_bus();
 
-	list_add_tail(&child->node, &parent->children);
-	child->self = dev;
-	dev->subordinate = child;
-	child->parent = parent;
-	child->ops = parent->ops;
-	child->sysdata = parent->sysdata;
-	child->dev = &dev->dev;
+	if (child) {
+		int i;
 
-	/*
-	 * Set up the primary, secondary and subordinate
-	 * bus numbers.
-	 */
-	child->number = child->secondary = busnr;
-	child->primary = parent->secondary;
-	child->subordinate = 0xff;
-
-	/* Set up default resource pointers and names.. */
-	for (i = 0; i < 4; i++) {
-		child->resource[i] = &dev->resource[PCI_BRIDGE_RESOURCES+i];
-		child->resource[i]->name = child->name;
+		child->self = bridge;
+		child->parent = parent;
+		child->ops = parent->ops;
+		child->sysdata = parent->sysdata;
+		child->dev = &bridge->dev;
+
+		/*
+		 * Set up the primary, secondary and subordinate
+		 * bus numbers.
+		 */
+		child->number = child->secondary = busnr;
+		child->primary = parent->secondary;
+		child->subordinate = 0xff;
+
+		/* Set up default resource pointers and names.. */
+		for (i = 0; i < 4; i++) {
+			child->resource[i] = &bridge->resource[PCI_BRIDGE_RESOURCES+i];
+			child->resource[i]->name = child->name;
+		}
+
+		bridge->subordinate = child;
 	}
 
 	return child;
 }
 
+struct pci_bus * __devinit pci_add_new_bus(struct pci_bus *parent, struct pci_dev *dev, int busnr)
+{
+	struct pci_bus *child;
+
+	child = pci_alloc_child_bus(parent, dev, busnr);
+	if (child)
+		list_add_tail(&child->node, &parent->children);
+	return child;
+}
+
+static unsigned int __devinit pci_scan_child_bus(struct pci_bus *bus);
+
 /*
  * If it's a bridge, configure it and scan the bus behind it.
  * For CardBus bridges, we don't scan behind as the devices will
@@ -289,12 +305,12 @@
 		 */
 		if (pass)
 			return max;
-		child = pci_add_new_bus(bus, dev, 0);
+		child = pci_alloc_child_bus(bus, dev, 0);
 		child->primary = buses & 0xFF;
 		child->secondary = (buses >> 8) & 0xFF;
 		child->subordinate = (buses >> 16) & 0xFF;
 		child->number = child->secondary;
-		cmax = pci_do_scan_bus(child);
+		cmax = pci_scan_child_bus(child);
 		if (cmax > max) max = cmax;
 	} else {
 		/*
@@ -307,18 +323,27 @@
 		/* Clear errors */
 		pci_write_config_word(dev, PCI_STATUS, 0xffff);
 
-		child = pci_add_new_bus(bus, dev, ++max);
+		child = pci_alloc_child_bus(bus, dev, ++max);
 		buses = (buses & 0xff000000)
 		      | ((unsigned int)(child->primary)     <<  0)
 		      | ((unsigned int)(child->secondary)   <<  8)
 		      | ((unsigned int)(child->subordinate) << 16);
+
+		/*
+		 * yenta.c forces a secondary latency timer of 176.
+		 * Copy that behaviour here.
+		 */
+		if (is_cardbus)
+			buses = (buses & 0x00ffffff) | (176 << 24);
+			
 		/*
 		 * We need to blast all three values with a single write.
 		 */
 		pci_write_config_dword(dev, PCI_PRIMARY_BUS, buses);
+
 		if (!is_cardbus) {
 			/* Now we can scan all subordinate buses... */
-			max = pci_do_scan_bus(child);
+			max = pci_scan_child_bus(child);
 		} else {
 			/*
 			 * For CardBus bridges, we leave 4 bus numbers

-- 
Russell King (rmk@arm.linux.org.uk)                The developer of ARM Linux
             http://www.arm.linux.org.uk/personal/aboutme.html


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2003-03-05  0:32 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2003-03-05  0:36 [CFT] PCI probing for cardbus Russell King
2003-03-05  0:39 ` [CFT] PCI probing for cardbus (1/5) Russell King
2003-03-05  0:39 ` [CFT] PCI probing for cardbus (2/5) Russell King
2003-03-05  0:40 ` [CFT] PCI probing for cardbus (3/5) Russell King
2003-03-05  0:40 ` [CFT] PCI probing for cardbus (4/5) Russell King
2003-03-05  0:40 ` [CFT] PCI probing for cardbus (5/5) Russell King

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).