This patch adds a basic PCI<->PCI bridge driver that utilizes the new
PCI bus class API.

Signed-off-by: Adam Belay <[EMAIL PROTECTED]>

--- a/drivers/pci/bus/pci-bridge.c      1969-12-31 19:00:00.000000000 -0500
+++ b/drivers/pci/bus/pci-bridge.c      2005-07-08 02:18:43.000000000 -0400
@@ -0,0 +1,165 @@
+/*
+ * pci-bridge.c - a generic PCI bus driver for PCI<->PCI bridges
+ *
+ */
+
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/module.h>
+
+static struct pci_device_id ppb_id_tbl[] = {
+       { PCI_DEVICE_CLASS(PCI_CLASS_BRIDGE_PCI << 8, 0xffff00) },
+       { 0 },
+};
+
+MODULE_DEVICE_TABLE(pci, ppb_id_tbl);
+
+/**
+ * ppb_create_bus - allocates a bus and fills in basic information
+ * @dev: the pci bridge device
+ */
+static struct pci_bus * ppb_create_bus(struct pci_dev *dev)
+{
+       int i;
+       struct pci_bus *bus = pci_alloc_bus();
+
+       if (!bus)
+               return NULL;
+
+       bus->parent = dev->bus;
+       bus->bridge = &dev->dev;
+       bus->ops = bus->parent->ops;
+       bus->sysdata = bus->parent->sysdata;
+       bus->bridge = get_device(&dev->dev);
+
+       /* Set up default resource pointers and names.. */
+       for (i = 0; i < 4; i++) {
+               bus->resource[i] = &dev->resource[PCI_BRIDGE_RESOURCES+i];
+               bus->resource[i]->name = bus->name;
+       }
+
+       return bus;
+}
+
+/**
+ * ppb_detect_bus - creates a bus and reads configuration space data
+ * @dev: the pci bridge device
+ *
+ * This function will do some verification to ensure we should drive this
+ * bridge.
+ */
+static struct pci_bus * ppb_detect_bus(struct pci_dev *dev)
+{
+       struct pci_bus *bus;
+       u32 buses;
+       u16 bctl;
+       unsigned int busnr;
+
+       pci_read_config_dword(dev, PCI_PRIMARY_BUS, &buses);
+       busnr = (buses >> 8) & 0xFF;
+
+       /*
+        * FIXME: This driver currently doesn't support bridges that haven't
+        * been configured by the BIOS.
+        */
+       if (!(buses & 0xffff00)) {
+               printk(KERN_INFO "PCI: Unable to drive bus %04x:%02x\n",
+                               pci_domain_nr(dev->bus), busnr);
+               return NULL;
+       }
+
+       /*
+        * If we already got to this bus through a different bridge,
+        * ignore it.  This can happen with the i450NX chipset.
+        */
+       if (pci_find_bus(pci_domain_nr(dev->bus), busnr)) {
+               printk(KERN_INFO "PCI: Bus %04x:%02x already known\n",
+                               pci_domain_nr(dev->bus), busnr);
+               return NULL;
+       }
+
+       bus = ppb_create_bus(dev);
+       if (!bus)
+               return NULL;
+
+       /* Disable MasterAbortMode during probing to avoid reporting
+        * of bus errors (in some architectures)
+        */ 
+       pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &bctl);
+       pci_write_config_word(dev, PCI_BRIDGE_CONTROL,
+                             bctl & ~PCI_BRIDGE_CTL_MASTER_ABORT);
+
+       bus->number = bus->secondary = busnr;
+       bus->primary = buses & 0xFF;
+       bus->subordinate = (buses >> 16) & 0xFF;
+       bus->bridge_ctl = bctl;
+
+       return bus;
+}
+
+/**
+ * ppb_detect_children - detects and registers child devices
+ * @bus: pci bus
+ */
+static void ppb_detect_children(struct pci_bus *bus)
+{
+       unsigned int devfn;
+
+       /* Go find them, Rover! */
+       for (devfn = 0; devfn < 0x100; devfn += 8)
+               pci_scan_slot(bus, devfn);
+
+       pcibios_fixup_bus(bus);
+       pci_bus_add_devices(bus);
+}
+
+static int ppb_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+       int err;
+       struct pci_bus *bus;
+
+       if (dev->hdr_type != PCI_HEADER_TYPE_BRIDGE)
+               return -ENODEV;
+
+       bus = ppb_detect_bus(dev);
+       if (!bus)
+               return -ENODEV;
+
+       err = pci_add_bus(bus);
+       if (err)
+               goto out;
+
+       dev->subordinate = bus;
+       ppb_detect_children(bus);
+       return 0;
+
+out:
+       kfree(bus);
+       return err;
+}
+
+static void ppb_remove(struct pci_dev *dev)
+{
+       pci_remove_behind_bridge(dev);
+       pci_remove_bus(dev->subordinate);
+}
+
+static struct pci_driver ppb_driver = {
+       .name           = "pci-bridge",
+       .id_table       = ppb_id_tbl,
+       .probe          = ppb_probe,
+       .remove         = ppb_remove,
+};
+
+static int __init ppb_init(void)
+{
+       return pci_register_driver(&ppb_driver);
+}
+
+static void __exit ppb_exit(void)
+{
+       pci_unregister_driver(&ppb_driver);
+}
+
+module_init(ppb_init);
+module_exit(ppb_exit);
--- a/drivers/pci/bus/Makefile  2005-07-07 22:22:49.000000000 -0400
+++ b/drivers/pci/bus/Makefile  2005-07-08 02:16:39.000000000 -0400
@@ -2,4 +2,4 @@
 # Makefile for the PCI device detection
 #
 
-obj-y :=  bus.o config.o device.o probe.o
+obj-y :=  bus.o config.o device.o probe.o pci-bridge.o


-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to