summaryrefslogtreecommitdiffstats
path: root/drivers/pci
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pci')
-rw-r--r--drivers/pci/Makefile1
-rw-r--r--drivers/pci/hotplug.c2
-rw-r--r--drivers/pci/pci-driver.c198
-rw-r--r--drivers/pci/pci.c6
-rw-r--r--drivers/pci/pcie/portdrv.h5
-rw-r--r--drivers/pci/pcie/portdrv_core.c8
-rw-r--r--drivers/pci/pcie/portdrv_pci.c79
-rw-r--r--drivers/pci/probe.c24
-rw-r--r--drivers/pci/quirks.c1
-rw-r--r--drivers/pci/setup-bus.c2
10 files changed, 188 insertions, 138 deletions
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index 7dea494c0d7..3657f6199c4 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_HOTPLUG_PCI) += hotplug/
#
# Some architectures use the generic PCI setup functions
#
+obj-$(CONFIG_X86) += setup-bus.o
obj-$(CONFIG_ALPHA) += setup-bus.o setup-irq.o
obj-$(CONFIG_ARM) += setup-bus.o setup-irq.o
obj-$(CONFIG_PARISC) += setup-bus.o
diff --git a/drivers/pci/hotplug.c b/drivers/pci/hotplug.c
index 3903f8c559b..b844bc97232 100644
--- a/drivers/pci/hotplug.c
+++ b/drivers/pci/hotplug.c
@@ -54,7 +54,7 @@ int pci_hotplug (struct device *dev, char **envp, int num_envp,
envp[i++] = scratch;
length += scnprintf (scratch, buffer_size - length,
- "MODALIAS=pci:v%08Xd%08Xsv%08Xsd%08Xbc%02Xsc%02Xi%02x\n",
+ "MODALIAS=pci:v%08Xd%08Xsv%08Xsd%08Xbc%02Xsc%02Xi%02x",
pdev->vendor, pdev->device,
pdev->subsystem_vendor, pdev->subsystem_device,
(u8)(pdev->class >> 16), (u8)(pdev->class >> 8),
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index e65bf2b395a..e4115a0d5ba 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -7,7 +7,6 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
-#include <linux/pci-dynids.h>
#include "pci.h"
/*
@@ -18,36 +17,12 @@
* Dynamic device IDs are disabled for !CONFIG_HOTPLUG
*/
-#ifdef CONFIG_HOTPLUG
-/**
- * pci_device_probe_dynamic()
- *
- * Walk the dynamic ID list looking for a match.
- * returns 0 and sets pci_dev->driver when drv claims pci_dev, else error.
- */
-static int
-pci_device_probe_dynamic(struct pci_driver *drv, struct pci_dev *pci_dev)
-{
- int error = -ENODEV;
- struct list_head *pos;
- struct dynid *dynid;
+struct pci_dynid {
+ struct list_head node;
+ struct pci_device_id id;
+};
- spin_lock(&drv->dynids.lock);
- list_for_each(pos, &drv->dynids.list) {
- dynid = list_entry(pos, struct dynid, node);
- if (pci_match_one_device(&dynid->id, pci_dev)) {
- spin_unlock(&drv->dynids.lock);
- error = drv->probe(pci_dev, &dynid->id);
- if (error >= 0) {
- pci_dev->driver = drv;
- return 0;
- }
- return error;
- }
- }
- spin_unlock(&drv->dynids.lock);
- return error;
-}
+#ifdef CONFIG_HOTPLUG
/**
* store_new_id
@@ -58,8 +33,7 @@ pci_device_probe_dynamic(struct pci_driver *drv, struct pci_dev *pci_dev)
static inline ssize_t
store_new_id(struct device_driver *driver, const char *buf, size_t count)
{
- struct dynid *dynid;
- struct bus_type * bus;
+ struct pci_dynid *dynid;
struct pci_driver *pdrv = to_pci_driver(driver);
__u32 vendor=PCI_ANY_ID, device=PCI_ANY_ID, subvendor=PCI_ANY_ID,
subdevice=PCI_ANY_ID, class=0, class_mask=0;
@@ -91,37 +65,22 @@ store_new_id(struct device_driver *driver, const char *buf, size_t count)
list_add_tail(&pdrv->dynids.list, &dynid->node);
spin_unlock(&pdrv->dynids.lock);
- bus = get_bus(pdrv->driver.bus);
- if (bus) {
- if (get_driver(&pdrv->driver)) {
- down_write(&bus->subsys.rwsem);
- driver_attach(&pdrv->driver);
- up_write(&bus->subsys.rwsem);
- put_driver(&pdrv->driver);
- }
- put_bus(bus);
+ if (get_driver(&pdrv->driver)) {
+ driver_attach(&pdrv->driver);
+ put_driver(&pdrv->driver);
}
return count;
}
-
static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id);
-static inline void
-pci_init_dynids(struct pci_dynids *dynids)
-{
- spin_lock_init(&dynids->lock);
- INIT_LIST_HEAD(&dynids->list);
-}
static void
pci_free_dynids(struct pci_driver *drv)
{
- struct list_head *pos, *n;
- struct dynid *dynid;
+ struct pci_dynid *dynid, *n;
spin_lock(&drv->dynids.lock);
- list_for_each_safe(pos, n, &drv->dynids.list) {
- dynid = list_entry(pos, struct dynid, node);
+ list_for_each_entry_safe(dynid, n, &drv->dynids.list, node) {
list_del(&dynid->node);
kfree(dynid);
}
@@ -138,83 +97,70 @@ pci_create_newid_file(struct pci_driver *drv)
return error;
}
-static int
-pci_bus_match_dynids(const struct pci_dev *pci_dev, struct pci_driver *pci_drv)
-{
- struct list_head *pos;
- struct dynid *dynid;
-
- spin_lock(&pci_drv->dynids.lock);
- list_for_each(pos, &pci_drv->dynids.list) {
- dynid = list_entry(pos, struct dynid, node);
- if (pci_match_one_device(&dynid->id, pci_dev)) {
- spin_unlock(&pci_drv->dynids.lock);
- return 1;
- }
- }
- spin_unlock(&pci_drv->dynids.lock);
- return 0;
-}
-
#else /* !CONFIG_HOTPLUG */
-static inline int pci_device_probe_dynamic(struct pci_driver *drv, struct pci_dev *pci_dev)
-{
- return -ENODEV;
-}
-static inline void pci_init_dynids(struct pci_dynids *dynids) {}
static inline void pci_free_dynids(struct pci_driver *drv) {}
static inline int pci_create_newid_file(struct pci_driver *drv)
{
return 0;
}
-static inline int pci_bus_match_dynids(const struct pci_dev *pci_dev, struct pci_driver *pci_drv)
-{
- return 0;
-}
#endif
/**
- * pci_match_device - Tell if a PCI device structure has a matching
- * PCI device id structure
+ * pci_match_id - See if a pci device matches a given pci_id table
* @ids: array of PCI device id structures to search in
- * @dev: the PCI device structure to match against
- *
+ * @dev: the PCI device structure to match against.
+ *
* Used by a driver to check whether a PCI device present in the
- * system is in its list of supported devices.Returns the matching
+ * system is in its list of supported devices. Returns the matching
* pci_device_id structure or %NULL if there is no match.
+ *
+ * Depreciated, don't use this as it will not catch any dynamic ids
+ * that a driver might want to check for.
*/
-const struct pci_device_id *
-pci_match_device(const struct pci_device_id *ids, const struct pci_dev *dev)
+const struct pci_device_id *pci_match_id(const struct pci_device_id *ids,
+ struct pci_dev *dev)
{
- while (ids->vendor || ids->subvendor || ids->class_mask) {
- if (pci_match_one_device(ids, dev))
- return ids;
- ids++;
+ if (ids) {
+ while (ids->vendor || ids->subvendor || ids->class_mask) {
+ if (pci_match_one_device(ids, dev))
+ return ids;
+ ids++;
+ }
}
return NULL;
}
/**
- * pci_device_probe_static()
- *
- * returns 0 and sets pci_dev->driver when drv claims pci_dev, else error.
+ * pci_match_device - Tell if a PCI device structure has a matching
+ * PCI device id structure
+ * @ids: array of PCI device id structures to search in
+ * @dev: the PCI device structure to match against
+ * @drv: the PCI driver to match against
+ *
+ * Used by a driver to check whether a PCI device present in the
+ * system is in its list of supported devices. Returns the matching
+ * pci_device_id structure or %NULL if there is no match.
*/
-static int
-pci_device_probe_static(struct pci_driver *drv, struct pci_dev *pci_dev)
-{
- int error = -ENODEV;
+const struct pci_device_id *pci_match_device(struct pci_driver *drv,
+ struct pci_dev *dev)
+{
const struct pci_device_id *id;
+ struct pci_dynid *dynid;
- if (!drv->id_table)
- return error;
- id = pci_match_device(drv->id_table, pci_dev);
+ id = pci_match_id(drv->id_table, dev);
if (id)
- error = drv->probe(pci_dev, id);
- if (error >= 0) {
- pci_dev->driver = drv;
- error = 0;
+ return id;
+
+ /* static ids didn't match, lets look at the dynamic ones */
+ spin_lock(&drv->dynids.lock);
+ list_for_each_entry(dynid, &drv->dynids.list, node) {
+ if (pci_match_one_device(&dynid->id, dev)) {
+ spin_unlock(&drv->dynids.lock);
+ return &dynid->id;
+ }
}
- return error;
+ spin_unlock(&drv->dynids.lock);
+ return NULL;
}
/**
@@ -225,13 +171,20 @@ pci_device_probe_static(struct pci_driver *drv, struct pci_dev *pci_dev)
*/
static int
__pci_device_probe(struct pci_driver *drv, struct pci_dev *pci_dev)
-{
+{
+ const struct pci_device_id *id;
int error = 0;
if (!pci_dev->driver && drv->probe) {
- error = pci_device_probe_static(drv, pci_dev);
- if (error == -ENODEV)
- error = pci_device_probe_dynamic(drv, pci_dev);
+ error = -ENODEV;
+
+ id = pci_match_device(drv, pci_dev);
+ if (id)
+ error = drv->probe(pci_dev, id);
+ if (error >= 0) {
+ pci_dev->driver = drv;
+ error = 0;
+ }
}
return error;
}
@@ -371,12 +324,6 @@ static struct kobj_type pci_driver_kobj_type = {
.sysfs_ops = &pci_driver_sysfs_ops,
};
-static int
-pci_populate_driver_dir(struct pci_driver *drv)
-{
- return pci_create_newid_file(drv);
-}
-
/**
* pci_register_driver - register a new pci driver
* @drv: the driver structure to register
@@ -401,13 +348,15 @@ int pci_register_driver(struct pci_driver *drv)
drv->driver.shutdown = pci_device_shutdown;
drv->driver.owner = drv->owner;
drv->driver.kobj.ktype = &pci_driver_kobj_type;
- pci_init_dynids(&drv->dynids);
+
+ spin_lock_init(&drv->dynids.lock);
+ INIT_LIST_HEAD(&drv->dynids.list);
/* register with core */
error = driver_register(&drv->driver);
if (!error)
- pci_populate_driver_dir(drv);
+ error = pci_create_newid_file(drv);
return error;
}
@@ -463,21 +412,17 @@ pci_dev_driver(const struct pci_dev *dev)
* system is in its list of supported devices.Returns the matching
* pci_device_id structure or %NULL if there is no match.
*/
-static int pci_bus_match(struct device * dev, struct device_driver * drv)
+static int pci_bus_match(struct device *dev, struct device_driver *drv)
{
- const struct pci_dev * pci_dev = to_pci_dev(dev);
- struct pci_driver * pci_drv = to_pci_driver(drv);
- const struct pci_device_id * ids = pci_drv->id_table;
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ struct pci_driver *pci_drv = to_pci_driver(drv);
const struct pci_device_id *found_id;
- if (!ids)
- return 0;
-
- found_id = pci_match_device(ids, pci_dev);
+ found_id = pci_match_device(pci_drv, pci_dev);
if (found_id)
return 1;
- return pci_bus_match_dynids(pci_dev, pci_drv);
+ return 0;
}
/**
@@ -536,6 +481,7 @@ static int __init pci_driver_init(void)
postcore_initcall(pci_driver_init);
+EXPORT_SYMBOL(pci_match_id);
EXPORT_SYMBOL(pci_match_device);
EXPORT_SYMBOL(pci_register_driver);
EXPORT_SYMBOL(pci_unregister_driver);
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index f04b9ffe415..d382bdb7b56 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -334,10 +334,6 @@ EXPORT_SYMBOL(pci_choose_state);
/**
* pci_save_state - save the PCI configuration space of a device before suspending
* @dev: - PCI device that we're dealing with
- * @buffer: - buffer to hold config space context
- *
- * @buffer must be large enough to hold the entire PCI 2.2 config space
- * (>= 64 bytes).
*/
int
pci_save_state(struct pci_dev *dev)
@@ -352,8 +348,6 @@ pci_save_state(struct pci_dev *dev)
/**
* pci_restore_state - Restore the saved state of a PCI device
* @dev: - PCI device that we're dealing with
- * @buffer: - saved PCI config space
- *
*/
int
pci_restore_state(struct pci_dev *dev)
diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h
index 537b372dc34..a63bd8f7260 100644
--- a/drivers/pci/pcie/portdrv.h
+++ b/drivers/pci/pcie/portdrv.h
@@ -27,6 +27,11 @@
#define get_descriptor_id(type, service) (((type - 4) << 4) | service)
+struct pcie_port_device_ext {
+ int interrupt_mode; /* [0:INTx | 1:MSI | 2:MSI-X] */
+ unsigned int saved_msi_config_space[5];
+};
+
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);
diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c
index f5c5f10a3d2..4db69982876 100644
--- a/drivers/pci/pcie/portdrv_core.c
+++ b/drivers/pci/pcie/portdrv_core.c
@@ -275,10 +275,17 @@ int pcie_port_device_probe(struct pci_dev *dev)
int pcie_port_device_register(struct pci_dev *dev)
{
+ struct pcie_port_device_ext *p_ext;
int status, type, capabilities, irq_mode, i;
int vectors[PCIE_PORT_DEVICE_MAXSERVICES];
u16 reg16;
+ /* Allocate port device extension */
+ if (!(p_ext = kmalloc(sizeof(struct pcie_port_device_ext), GFP_KERNEL)))
+ return -ENOMEM;
+
+ pci_set_drvdata(dev, p_ext);
+
/* Get port type */
pci_read_config_word(dev,
pci_find_capability(dev, PCI_CAP_ID_EXP) +
@@ -288,6 +295,7 @@ int pcie_port_device_register(struct pci_dev *dev)
/* Now get port services */
capabilities = get_port_device_capability(dev);
irq_mode = assign_interrupt_mode(dev, vectors, capabilities);
+ p_ext->interrupt_mode = irq_mode;
/* Allocate child services if any */
for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) {
diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c
index e9095ee508e..30bac7ed7c1 100644
--- a/drivers/pci/pcie/portdrv_pci.c
+++ b/drivers/pci/pcie/portdrv_pci.c
@@ -29,6 +29,78 @@ MODULE_LICENSE("GPL");
/* global data */
static const char device_name[] = "pcieport-driver";
+static void pci_save_msi_state(struct pci_dev *dev)
+{
+ struct pcie_port_device_ext *p_ext = pci_get_drvdata(dev);
+ int i = 0, pos;
+ u16 control;
+
+ if ((pos = pci_find_capability(dev, PCI_CAP_ID_MSI)) <= 0)
+ return;
+
+ pci_read_config_dword(dev, pos, &p_ext->saved_msi_config_space[i++]);
+ control = p_ext->saved_msi_config_space[0] >> 16;
+ pci_read_config_dword(dev, pos + PCI_MSI_ADDRESS_LO,
+ &p_ext->saved_msi_config_space[i++]);
+ if (control & PCI_MSI_FLAGS_64BIT) {
+ pci_read_config_dword(dev, pos + PCI_MSI_ADDRESS_HI,
+ &p_ext->saved_msi_config_space[i++]);
+ pci_read_config_dword(dev, pos + PCI_MSI_DATA_64,
+ &p_ext->saved_msi_config_space[i++]);
+ } else
+ pci_read_config_dword(dev, pos + PCI_MSI_DATA_32,
+ &p_ext->saved_msi_config_space[i++]);
+ if (control & PCI_MSI_FLAGS_MASKBIT)
+ pci_read_config_dword(dev, pos + PCI_MSI_MASK_BIT,
+ &p_ext->saved_msi_config_space[i++]);
+}
+
+static void pci_restore_msi_state(struct pci_dev *dev)
+{
+ struct pcie_port_device_ext *p_ext = pci_get_drvdata(dev);
+ int i = 0, pos;
+ u16 control;
+
+ if ((pos = pci_find_capability(dev, PCI_CAP_ID_MSI)) <= 0)
+ return;
+
+ control = p_ext->saved_msi_config_space[i++] >> 16;
+ pci_write_config_word(dev, pos + PCI_MSI_FLAGS, control);
+ pci_write_config_dword(dev, pos + PCI_MSI_ADDRESS_LO,
+ p_ext->saved_msi_config_space[i++]);
+ if (control & PCI_MSI_FLAGS_64BIT) {
+ pci_write_config_dword(dev, pos + PCI_MSI_ADDRESS_HI,
+ p_ext->saved_msi_config_space[i++]);
+ pci_write_config_dword(dev, pos + PCI_MSI_DATA_64,
+ p_ext->saved_msi_config_space[i++]);
+ } else
+ pci_write_config_dword(dev, pos + PCI_MSI_DATA_32,
+ p_ext->saved_msi_config_space[i++]);
+ if (control & PCI_MSI_FLAGS_MASKBIT)
+ pci_write_config_dword(dev, pos + PCI_MSI_MASK_BIT,
+ p_ext->saved_msi_config_space[i++]);
+}
+
+static void pcie_portdrv_save_config(struct pci_dev *dev)
+{
+ struct pcie_port_device_ext *p_ext = pci_get_drvdata(dev);
+
+ pci_save_state(dev);
+ if (p_ext->interrupt_mode == PCIE_PORT_MSI_MODE)
+ pci_save_msi_state(dev);
+}
+
+static void pcie_portdrv_restore_config(struct pci_dev *dev)
+{
+ struct pcie_port_device_ext *p_ext = pci_get_drvdata(dev);
+
+ pci_restore_state(dev);
+ if (p_ext->interrupt_mode == PCIE_PORT_MSI_MODE)
+ pci_restore_msi_state(dev);
+ pci_enable_device(dev);
+ pci_set_master(dev);
+}
+
/*
* pcie_portdrv_probe - Probe PCI-Express port devices
* @dev: PCI-Express port device being probed
@@ -64,16 +136,21 @@ static int __devinit pcie_portdrv_probe (struct pci_dev *dev,
static void pcie_portdrv_remove (struct pci_dev *dev)
{
pcie_port_device_remove(dev);
+ kfree(pci_get_drvdata(dev));
}
#ifdef CONFIG_PM
static int pcie_portdrv_suspend (struct pci_dev *dev, pm_message_t state)
{
- return pcie_port_device_suspend(dev, state);
+ int ret = pcie_port_device_suspend(dev, state);
+
+ pcie_portdrv_save_config(dev);
+ return ret;
}
static int pcie_portdrv_resume (struct pci_dev *dev)
{
+ pcie_portdrv_restore_config(dev);
return pcie_port_device_resume(dev);
}
#endif
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 6a0a82f0508..df3bdae2040 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -239,9 +239,8 @@ void __devinit pci_read_bridge_bases(struct pci_bus *child)
if (dev->transparent) {
printk(KERN_INFO "PCI: Transparent bridge - %s\n", pci_name(dev));
- for(i = 0; i < PCI_BUS_NUM_RESOURCES; i++)
- child->resource[i] = child->parent->resource[i];
- return;
+ for(i = 3; i < PCI_BUS_NUM_RESOURCES; i++)
+ child->resource[i] = child->parent->resource[i - 3];
}
for(i=0; i<3; i++)
@@ -398,6 +397,16 @@ static void pci_enable_crs(struct pci_dev *dev)
pci_write_config_word(dev, rpcap + PCI_EXP_RTCTL, rpctl);
}
+static void __devinit pci_fixup_parent_subordinate_busnr(struct pci_bus *child, int max)
+{
+ struct pci_bus *parent = child->parent;
+ while (parent->parent && parent->subordinate < max) {
+ parent->subordinate = max;
+ pci_write_config_byte(parent->self, PCI_SUBORDINATE_BUS, max);
+ parent = parent->parent;
+ }
+}
+
unsigned int __devinit pci_scan_child_bus(struct pci_bus *bus);
/*
@@ -499,7 +508,13 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev * dev, int max
if (!is_cardbus) {
child->bridge_ctl = PCI_BRIDGE_CTL_NO_ISA;
-
+ /*
+ * Adjust subordinate busnr in parent buses.
+ * We do this before scanning for children because
+ * some devices may not be detected if the bios
+ * was lazy.
+ */
+ pci_fixup_parent_subordinate_busnr(child, max);
/* Now we can scan all subordinate buses... */
max = pci_scan_child_bus(child);
} else {
@@ -513,6 +528,7 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev * dev, int max
max+i+1))
break;
max += i;
+ pci_fixup_parent_subordinate_busnr(child, max);
}
/*
* Set the subordinate bus number to its real value.
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index 968033fd29f..1521fd5d95c 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -767,6 +767,7 @@ static void __init asus_hides_smbus_hostbridge(struct pci_dev *dev)
if (unlikely(dev->subsystem_vendor == PCI_VENDOR_ID_ASUSTEK)) {
if (dev->device == PCI_DEVICE_ID_INTEL_82845_HB)
switch(dev->subsystem_device) {
+ case 0x8025: /* P4B-LX */
case 0x8070: /* P4B */
case 0x8088: /* P4B533 */
case 0x1626: /* L3C notebook */
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c
index 6b628de948a..c1bdfb42465 100644
--- a/drivers/pci/setup-bus.c
+++ b/drivers/pci/setup-bus.c
@@ -273,6 +273,8 @@ find_free_bus_resource(struct pci_bus *bus, unsigned long type)
for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) {
r = bus->resource[i];
+ if (r == &ioport_resource || r == &iomem_resource)
+ continue;
if (r && (r->flags & type_mask) == type && !r->parent)
return r;
}