summaryrefslogtreecommitdiff
path: root/drivers/pci/pcie
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/pci/pcie
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'drivers/pci/pcie')
-rw-r--r--drivers/pci/pcie/Kconfig36
-rw-r--r--drivers/pci/pcie/Makefile7
-rw-r--r--drivers/pci/pcie/portdrv.h41
-rw-r--r--drivers/pci/pcie/portdrv_bus.c77
-rw-r--r--drivers/pci/pcie/portdrv_core.c434
-rw-r--r--drivers/pci/pcie/portdrv_pci.c122
6 files changed, 717 insertions, 0 deletions
diff --git a/drivers/pci/pcie/Kconfig b/drivers/pci/pcie/Kconfig
new file mode 100644
index 000000000000..1012db8b8b2c
--- /dev/null
+++ b/drivers/pci/pcie/Kconfig
@@ -0,0 +1,36 @@
+#
+# PCI Express Port Bus Configuration
+#
+config PCIEPORTBUS
+ bool "PCI Express support"
+ depends on PCI
+ help
+ This automatically enables PCI Express Port Bus support. Users can
+ choose Native Hot-Plug support, Advanced Error Reporting support,
+ Power Management Event support and Virtual Channel support to run
+ on PCI Express Ports (Root or Switch).
+
+#
+# Include service Kconfig here
+#
+config HOTPLUG_PCI_PCIE
+ tristate "PCI Express Hotplug driver"
+ depends on HOTPLUG_PCI && PCIEPORTBUS
+ help
+ Say Y here if you have a motherboard that supports PCI Express Native
+ Hotplug
+
+ To compile this driver as a module, choose M here: the
+ module will be called pciehp.
+
+ When in doubt, say N.
+
+config HOTPLUG_PCI_PCIE_POLL_EVENT_MODE
+ bool "Use polling mechanism for hot-plug events (for testing purpose)"
+ depends on HOTPLUG_PCI_PCIE
+ help
+ Say Y here if you want to use the polling mechanism for hot-plug
+ events for early platform testing.
+
+ When in doubt, say N.
+
diff --git a/drivers/pci/pcie/Makefile b/drivers/pci/pcie/Makefile
new file mode 100644
index 000000000000..984fa87283e3
--- /dev/null
+++ b/drivers/pci/pcie/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for PCI-Express PORT Driver
+#
+
+pcieportdrv-y := portdrv_core.o portdrv_pci.o portdrv_bus.o
+
+obj-$(CONFIG_PCIEPORTBUS) += pcieportdrv.o
diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h
new file mode 100644
index 000000000000..6c3ad8918667
--- /dev/null
+++ b/drivers/pci/pcie/portdrv.h
@@ -0,0 +1,41 @@
+/*
+ * File: portdrv.h
+ * Purpose: PCI Express Port Bus Driver's Internal Data Structures
+ *
+ * Copyright (C) 2004 Intel
+ * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com)
+ */
+
+#ifndef _PORTDRV_H_
+#define _PORTDRV_H_
+
+#if !defined(PCI_CAP_ID_PME)
+#define PCI_CAP_ID_PME 1
+#endif
+
+#if !defined(PCI_CAP_ID_EXP)
+#define PCI_CAP_ID_EXP 0x10
+#endif
+
+#define PORT_TYPE_MASK 0xf
+#define PORT_TO_SLOT_MASK 0x100
+#define SLOT_HP_CAPABLE_MASK 0x40
+#define PCIE_CAPABILITIES_REG 0x2
+#define PCIE_SLOT_CAPABILITIES_REG 0x14
+#define PCIE_PORT_DEVICE_MAXSERVICES 4
+#define PCI_CFG_SPACE_SIZE 256
+
+#define get_descriptor_id(type, service) (((type - 4) << 4) | service)
+
+extern struct bus_type pcie_port_bus_type;
+extern int pcie_port_device_probe(struct pci_dev *dev);
+extern int pcie_port_device_register(struct pci_dev *dev);
+#ifdef CONFIG_PM
+extern int pcie_port_device_suspend(struct pci_dev *dev, u32 state);
+extern int pcie_port_device_resume(struct pci_dev *dev);
+#endif
+extern void pcie_port_device_remove(struct pci_dev *dev);
+extern void pcie_port_bus_register(void);
+extern void pcie_port_bus_unregister(void);
+
+#endif /* _PORTDRV_H_ */
diff --git a/drivers/pci/pcie/portdrv_bus.c b/drivers/pci/pcie/portdrv_bus.c
new file mode 100644
index 000000000000..9a02f28ed05f
--- /dev/null
+++ b/drivers/pci/pcie/portdrv_bus.c
@@ -0,0 +1,77 @@
+/*
+ * File: portdrv_bus.c
+ * Purpose: PCI Express Port Bus Driver's Bus Overloading Functions
+ *
+ * Copyright (C) 2004 Intel
+ * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com)
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/pm.h>
+
+#include <linux/pcieport_if.h>
+
+static int pcie_port_bus_match(struct device *dev, struct device_driver *drv);
+static int pcie_port_bus_suspend(struct device *dev, u32 state);
+static int pcie_port_bus_resume(struct device *dev);
+
+struct bus_type pcie_port_bus_type = {
+ .name = "pci_express",
+ .match = pcie_port_bus_match,
+ .suspend = pcie_port_bus_suspend,
+ .resume = pcie_port_bus_resume,
+};
+
+static int pcie_port_bus_match(struct device *dev, struct device_driver *drv)
+{
+ struct pcie_device *pciedev;
+ struct pcie_port_service_driver *driver;
+
+ if (drv->bus != &pcie_port_bus_type || dev->bus != &pcie_port_bus_type)
+ return 0;
+
+ pciedev = to_pcie_device(dev);
+ driver = to_service_driver(drv);
+ if ( (driver->id_table->vendor != PCI_ANY_ID &&
+ driver->id_table->vendor != pciedev->id.vendor) ||
+ (driver->id_table->device != PCI_ANY_ID &&
+ driver->id_table->device != pciedev->id.device) ||
+ driver->id_table->port_type != pciedev->id.port_type ||
+ driver->id_table->service_type != pciedev->id.service_type )
+ return 0;
+
+ return 1;
+}
+
+static int pcie_port_bus_suspend(struct device *dev, u32 state)
+{
+ struct pcie_device *pciedev;
+ struct pcie_port_service_driver *driver;
+
+ if (!dev || !dev->driver)
+ return 0;
+
+ pciedev = to_pcie_device(dev);
+ driver = to_service_driver(dev->driver);
+ if (driver && driver->suspend)
+ driver->suspend(pciedev, state);
+ return 0;
+}
+
+static int pcie_port_bus_resume(struct device *dev)
+{
+ struct pcie_device *pciedev;
+ struct pcie_port_service_driver *driver;
+
+ if (!dev || !dev->driver)
+ return 0;
+
+ pciedev = to_pcie_device(dev);
+ driver = to_service_driver(dev->driver);
+ if (driver && driver->resume)
+ driver->resume(pciedev);
+ return 0;
+}
diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c
new file mode 100644
index 000000000000..127f64f85dc5
--- /dev/null
+++ b/drivers/pci/pcie/portdrv_core.c
@@ -0,0 +1,434 @@
+/*
+ * File: portdrv_core.c
+ * Purpose: PCI Express Port Bus Driver's Core Functions
+ *
+ * Copyright (C) 2004 Intel
+ * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com)
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/pm.h>
+#include <linux/pcieport_if.h>
+
+#include "portdrv.h"
+
+extern int pcie_mch_quirk; /* MSI-quirk Indicator */
+
+static int pcie_port_probe_service(struct device *dev)
+{
+ struct pcie_device *pciedev;
+ struct pcie_port_service_driver *driver;
+ int status = -ENODEV;
+
+ if (!dev || !dev->driver)
+ return status;
+
+ driver = to_service_driver(dev->driver);
+ if (!driver || !driver->probe)
+ return status;
+
+ pciedev = to_pcie_device(dev);
+ status = driver->probe(pciedev, driver->id_table);
+ if (!status) {
+ printk(KERN_DEBUG "Load service driver %s on pcie device %s\n",
+ driver->name, dev->bus_id);
+ get_device(dev);
+ }
+ return status;
+}
+
+static int pcie_port_remove_service(struct device *dev)
+{
+ struct pcie_device *pciedev;
+ struct pcie_port_service_driver *driver;
+
+ if (!dev || !dev->driver)
+ return 0;
+
+ pciedev = to_pcie_device(dev);
+ driver = to_service_driver(dev->driver);
+ if (driver && driver->remove) {
+ printk(KERN_DEBUG "Unload service driver %s on pcie device %s\n",
+ driver->name, dev->bus_id);
+ driver->remove(pciedev);
+ put_device(dev);
+ }
+ return 0;
+}
+
+static void pcie_port_shutdown_service(struct device *dev) {}
+
+static int pcie_port_suspend_service(struct device *dev, u32 state, u32 level)
+{
+ struct pcie_device *pciedev;
+ struct pcie_port_service_driver *driver;
+
+ if (!dev || !dev->driver)
+ return 0;
+
+ pciedev = to_pcie_device(dev);
+ driver = to_service_driver(dev->driver);
+ if (driver && driver->suspend)
+ driver->suspend(pciedev, state);
+ return 0;
+}
+
+static int pcie_port_resume_service(struct device *dev, u32 state)
+{
+ struct pcie_device *pciedev;
+ struct pcie_port_service_driver *driver;
+
+ if (!dev || !dev->driver)
+ return 0;
+
+ pciedev = to_pcie_device(dev);
+ driver = to_service_driver(dev->driver);
+
+ if (driver && driver->resume)
+ driver->resume(pciedev);
+ return 0;
+}
+
+/*
+ * release_pcie_device
+ *
+ * Being invoked automatically when device is being removed
+ * in response to device_unregister(dev) call.
+ * Release all resources being claimed.
+ */
+static void release_pcie_device(struct device *dev)
+{
+ printk(KERN_DEBUG "Free Port Service[%s]\n", dev->bus_id);
+ kfree(to_pcie_device(dev));
+}
+
+static int is_msi_quirked(struct pci_dev *dev)
+{
+ int port_type, quirk = 0;
+ u16 reg16;
+
+ pci_read_config_word(dev,
+ pci_find_capability(dev, PCI_CAP_ID_EXP) +
+ PCIE_CAPABILITIES_REG, &reg16);
+ port_type = (reg16 >> 4) & PORT_TYPE_MASK;
+ switch(port_type) {
+ case PCIE_RC_PORT:
+ if (pcie_mch_quirk == 1)
+ quirk = 1;
+ break;
+ case PCIE_SW_UPSTREAM_PORT:
+ case PCIE_SW_DOWNSTREAM_PORT:
+ default:
+ break;
+ }
+ return quirk;
+}
+
+static int assign_interrupt_mode(struct pci_dev *dev, int *vectors, int mask)
+{
+ int i, pos, nvec, status = -EINVAL;
+ int interrupt_mode = PCIE_PORT_INTx_MODE;
+
+ /* Set INTx as default */
+ for (i = 0, nvec = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) {
+ if (mask & (1 << i))
+ nvec++;
+ vectors[i] = dev->irq;
+ }
+
+ /* Check MSI quirk */
+ if (is_msi_quirked(dev))
+ return interrupt_mode;
+
+ /* Select MSI-X over MSI if supported */
+ pos = pci_find_capability(dev, PCI_CAP_ID_MSIX);
+ if (pos) {
+ struct msix_entry msix_entries[PCIE_PORT_DEVICE_MAXSERVICES] =
+ {{0, 0}, {0, 1}, {0, 2}, {0, 3}};
+ printk("%s Found MSIX capability\n", __FUNCTION__);
+ status = pci_enable_msix(dev, msix_entries, nvec);
+ if (!status) {
+ int j = 0;
+
+ interrupt_mode = PCIE_PORT_MSIX_MODE;
+ for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) {
+ if (mask & (1 << i))
+ vectors[i] = msix_entries[j++].vector;
+ }
+ }
+ }
+ if (status) {
+ pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
+ if (pos) {
+ printk("%s Found MSI capability\n", __FUNCTION__);
+ status = pci_enable_msi(dev);
+ if (!status) {
+ interrupt_mode = PCIE_PORT_MSI_MODE;
+ for (i = 0;i < PCIE_PORT_DEVICE_MAXSERVICES;i++)
+ vectors[i] = dev->irq;
+ }
+ }
+ }
+ return interrupt_mode;
+}
+
+static int get_port_device_capability(struct pci_dev *dev)
+{
+ int services = 0, pos;
+ u16 reg16;
+ u32 reg32;
+
+ pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
+ pci_read_config_word(dev, pos + PCIE_CAPABILITIES_REG, &reg16);
+ /* Hot-Plug Capable */
+ if (reg16 & PORT_TO_SLOT_MASK) {
+ pci_read_config_dword(dev,
+ pos + PCIE_SLOT_CAPABILITIES_REG, &reg32);
+ if (reg32 & SLOT_HP_CAPABLE_MASK)
+ services |= PCIE_PORT_SERVICE_HP;
+ }
+ /* PME Capable */
+ pos = pci_find_capability(dev, PCI_CAP_ID_PME);
+ if (pos)
+ services |= PCIE_PORT_SERVICE_PME;
+
+ pos = PCI_CFG_SPACE_SIZE;
+ while (pos) {
+ pci_read_config_dword(dev, pos, &reg32);
+ switch (reg32 & 0xffff) {
+ case PCI_EXT_CAP_ID_ERR:
+ services |= PCIE_PORT_SERVICE_AER;
+ pos = reg32 >> 20;
+ break;
+ case PCI_EXT_CAP_ID_VC:
+ services |= PCIE_PORT_SERVICE_VC;
+ pos = reg32 >> 20;
+ break;
+ default:
+ pos = 0;
+ break;
+ }
+ }
+
+ return services;
+}
+
+static void pcie_device_init(struct pci_dev *parent, struct pcie_device *dev,
+ int port_type, int service_type, int irq, int irq_mode)
+{
+ struct device *device;
+
+ dev->port = parent;
+ dev->interrupt_mode = irq_mode;
+ dev->irq = irq;
+ dev->id.vendor = parent->vendor;
+ dev->id.device = parent->device;
+ dev->id.port_type = port_type;
+ dev->id.service_type = (1 << service_type);
+
+ /* Initialize generic device interface */
+ device = &dev->device;
+ memset(device, 0, sizeof(struct device));
+ INIT_LIST_HEAD(&device->node);
+ INIT_LIST_HEAD(&device->children);
+ INIT_LIST_HEAD(&device->bus_list);
+ device->bus = &pcie_port_bus_type;
+ device->driver = NULL;
+ device->driver_data = NULL;
+ device->release = release_pcie_device; /* callback to free pcie dev */
+ sprintf(&device->bus_id[0], "pcie%02x",
+ get_descriptor_id(port_type, service_type));
+ device->parent = &parent->dev;
+}
+
+static struct pcie_device* alloc_pcie_device(struct pci_dev *parent,
+ int port_type, int service_type, int irq, int irq_mode)
+{
+ struct pcie_device *device;
+
+ device = kmalloc(sizeof(struct pcie_device), GFP_KERNEL);
+ if (!device)
+ return NULL;
+
+ memset(device, 0, sizeof(struct pcie_device));
+ pcie_device_init(parent, device, port_type, service_type, irq,irq_mode);
+ printk(KERN_DEBUG "Allocate Port Service[%s]\n", device->device.bus_id);
+ return device;
+}
+
+int pcie_port_device_probe(struct pci_dev *dev)
+{
+ int pos, type;
+ u16 reg;
+
+ if (!(pos = pci_find_capability(dev, PCI_CAP_ID_EXP)))
+ return -ENODEV;
+
+ pci_read_config_word(dev, pos + PCIE_CAPABILITIES_REG, &reg);
+ type = (reg >> 4) & PORT_TYPE_MASK;
+ if ( type == PCIE_RC_PORT || type == PCIE_SW_UPSTREAM_PORT ||
+ type == PCIE_SW_DOWNSTREAM_PORT )
+ return 0;
+
+ return -ENODEV;
+}
+
+int pcie_port_device_register(struct pci_dev *dev)
+{
+ int status, type, capabilities, irq_mode, i;
+ int vectors[PCIE_PORT_DEVICE_MAXSERVICES];
+ u16 reg16;
+
+ /* Get port type */
+ pci_read_config_word(dev,
+ pci_find_capability(dev, PCI_CAP_ID_EXP) +
+ PCIE_CAPABILITIES_REG, &reg16);
+ type = (reg16 >> 4) & PORT_TYPE_MASK;
+
+ /* Now get port services */
+ capabilities = get_port_device_capability(dev);
+ irq_mode = assign_interrupt_mode(dev, vectors, capabilities);
+
+ /* Allocate child services if any */
+ for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) {
+ struct pcie_device *child;
+
+ if (capabilities & (1 << i)) {
+ child = alloc_pcie_device(
+ dev, /* parent */
+ type, /* port type */
+ i, /* service type */
+ vectors[i], /* irq */
+ irq_mode /* interrupt mode */);
+ if (child) {
+ status = device_register(&child->device);
+ if (status) {
+ kfree(child);
+ continue;
+ }
+ get_device(&child->device);
+ }
+ }
+ }
+ return 0;
+}
+
+#ifdef CONFIG_PM
+int pcie_port_device_suspend(struct pci_dev *dev, u32 state)
+{
+ struct list_head *head, *tmp;
+ struct device *parent, *child;
+ struct device_driver *driver;
+ struct pcie_port_service_driver *service_driver;
+
+ parent = &dev->dev;
+ head = &parent->children;
+ tmp = head->next;
+ while (head != tmp) {
+ child = container_of(tmp, struct device, node);
+ tmp = tmp->next;
+ if (child->bus != &pcie_port_bus_type)
+ continue;
+ driver = child->driver;
+ if (!driver)
+ continue;
+ service_driver = to_service_driver(driver);
+ if (service_driver->suspend)
+ service_driver->suspend(to_pcie_device(child), state);
+ }
+ return 0;
+}
+
+int pcie_port_device_resume(struct pci_dev *dev)
+{
+ struct list_head *head, *tmp;
+ struct device *parent, *child;
+ struct device_driver *driver;
+ struct pcie_port_service_driver *service_driver;
+
+ parent = &dev->dev;
+ head = &parent->children;
+ tmp = head->next;
+ while (head != tmp) {
+ child = container_of(tmp, struct device, node);
+ tmp = tmp->next;
+ if (child->bus != &pcie_port_bus_type)
+ continue;
+ driver = child->driver;
+ if (!driver)
+ continue;
+ service_driver = to_service_driver(driver);
+ if (service_driver->resume)
+ service_driver->resume(to_pcie_device(child));
+ }
+ return 0;
+
+}
+#endif
+
+void pcie_port_device_remove(struct pci_dev *dev)
+{
+ struct list_head *head, *tmp;
+ struct device *parent, *child;
+ struct device_driver *driver;
+ struct pcie_port_service_driver *service_driver;
+ int interrupt_mode = PCIE_PORT_INTx_MODE;
+
+ parent = &dev->dev;
+ head = &parent->children;
+ tmp = head->next;
+ while (head != tmp) {
+ child = container_of(tmp, struct device, node);
+ tmp = tmp->next;
+ if (child->bus != &pcie_port_bus_type)
+ continue;
+ driver = child->driver;
+ if (driver) {
+ service_driver = to_service_driver(driver);
+ if (service_driver->remove)
+ service_driver->remove(to_pcie_device(child));
+ }
+ interrupt_mode = (to_pcie_device(child))->interrupt_mode;
+ put_device(child);
+ device_unregister(child);
+ }
+ /* Switch to INTx by default if MSI enabled */
+ if (interrupt_mode == PCIE_PORT_MSIX_MODE)
+ pci_disable_msix(dev);
+ else if (interrupt_mode == PCIE_PORT_MSI_MODE)
+ pci_disable_msi(dev);
+}
+
+void pcie_port_bus_register(void)
+{
+ bus_register(&pcie_port_bus_type);
+}
+
+void pcie_port_bus_unregister(void)
+{
+ bus_unregister(&pcie_port_bus_type);
+}
+
+int pcie_port_service_register(struct pcie_port_service_driver *new)
+{
+ new->driver.name = (char *)new->name;
+ new->driver.bus = &pcie_port_bus_type;
+ new->driver.probe = pcie_port_probe_service;
+ new->driver.remove = pcie_port_remove_service;
+ new->driver.shutdown = pcie_port_shutdown_service;
+ new->driver.suspend = pcie_port_suspend_service;
+ new->driver.resume = pcie_port_resume_service;
+
+ return driver_register(&new->driver);
+}
+
+void pcie_port_service_unregister(struct pcie_port_service_driver *new)
+{
+ driver_unregister(&new->driver);
+}
+
+EXPORT_SYMBOL(pcie_port_service_register);
+EXPORT_SYMBOL(pcie_port_service_unregister);
diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c
new file mode 100644
index 000000000000..3184843c3649
--- /dev/null
+++ b/drivers/pci/pcie/portdrv_pci.c
@@ -0,0 +1,122 @@
+/*
+ * File: portdrv_pci.c
+ * Purpose: PCI Express Port Bus Driver
+ *
+ * Copyright (C) 2004 Intel
+ * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com)
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/pm.h>
+#include <linux/init.h>
+#include <linux/pcieport_if.h>
+
+#include "portdrv.h"
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v1.0"
+#define DRIVER_AUTHOR "tom.l.nguyen@intel.com"
+#define DRIVER_DESC "PCIE Port Bus Driver"
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/* global data */
+static const char device_name[] = "pcieport-driver";
+
+/*
+ * pcie_portdrv_probe - Probe PCI-Express port devices
+ * @dev: PCI-Express port device being probed
+ *
+ * If detected invokes the pcie_port_device_register() method for
+ * this port device.
+ *
+ */
+static int __devinit pcie_portdrv_probe (struct pci_dev *dev,
+ const struct pci_device_id *id )
+{
+ int status;
+
+ status = pcie_port_device_probe(dev);
+ if (status)
+ return status;
+
+ if (pci_enable_device(dev) < 0)
+ return -ENODEV;
+
+ pci_set_master(dev);
+ if (!dev->irq) {
+ printk(KERN_WARNING
+ "%s->Dev[%04x:%04x] has invalid IRQ. Check vendor BIOS\n",
+ __FUNCTION__, dev->device, dev->vendor);
+ }
+ if (pcie_port_device_register(dev))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void pcie_portdrv_remove (struct pci_dev *dev)
+{
+ pcie_port_device_remove(dev);
+}
+
+#ifdef CONFIG_PM
+static int pcie_portdrv_suspend (struct pci_dev *dev, u32 state)
+{
+ return pcie_port_device_suspend(dev, state);
+}
+
+static int pcie_portdrv_resume (struct pci_dev *dev)
+{
+ return pcie_port_device_resume(dev);
+}
+#endif
+
+/*
+ * LINUX Device Driver Model
+ */
+static const struct pci_device_id port_pci_ids[] = { {
+ /* handle any PCI-Express port */
+ PCI_DEVICE_CLASS(((PCI_CLASS_BRIDGE_PCI << 8) | 0x00), ~0),
+ }, { /* end: all zeroes */ }
+};
+MODULE_DEVICE_TABLE(pci, port_pci_ids);
+
+static struct pci_driver pcie_portdrv = {
+ .name = (char *)device_name,
+ .id_table = &port_pci_ids[0],
+
+ .probe = pcie_portdrv_probe,
+ .remove = pcie_portdrv_remove,
+
+#ifdef CONFIG_PM
+ .suspend = pcie_portdrv_suspend,
+ .resume = pcie_portdrv_resume,
+#endif /* PM */
+};
+
+static int __init pcie_portdrv_init(void)
+{
+ int retval = 0;
+
+ pcie_port_bus_register();
+ retval = pci_register_driver(&pcie_portdrv);
+ if (retval)
+ pcie_port_bus_unregister();
+ return retval;
+}
+
+static void __exit pcie_portdrv_exit(void)
+{
+ pci_unregister_driver(&pcie_portdrv);
+ pcie_port_bus_unregister();
+}
+
+module_init(pcie_portdrv_init);
+module_exit(pcie_portdrv_exit);