aboutsummaryrefslogtreecommitdiff
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/hotplug/Kconfig5
-rw-r--r--drivers/pci/hotplug/Makefile1
-rw-r--r--drivers/pci/hotplug/sgi_hotplug.c611
-rw-r--r--drivers/pci/pci-acpi.c110
-rw-r--r--drivers/pci/pci-driver.c198
-rw-r--r--drivers/pci/pci.c28
-rw-r--r--drivers/pci/pci.h4
-rw-r--r--drivers/pci/pcie/portdrv.h5
-rw-r--r--drivers/pci/pcie/portdrv_core.c14
-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/search.c1
-rw-r--r--drivers/pci/setup-bus.c3
16 files changed, 938 insertions, 149 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/hotplug/Kconfig b/drivers/pci/hotplug/Kconfig
index 1a4d4ca2a4d..9c4a39ee89b 100644
--- a/drivers/pci/hotplug/Kconfig
+++ b/drivers/pci/hotplug/Kconfig
@@ -187,9 +187,10 @@ config HOTPLUG_PCI_RPA_DLPAR
config HOTPLUG_PCI_SGI
tristate "SGI PCI Hotplug Support"
- depends on HOTPLUG_PCI && IA64_SGI_SN2
+ depends on HOTPLUG_PCI && (IA64_SGI_SN2 || IA64_GENERIC)
help
- Say Y here if you have an SGI IA64 Altix system.
+ Say Y here if you want to use the SGI Altix Hotplug
+ Driver for PCI devices.
When in doubt, say N.
diff --git a/drivers/pci/hotplug/Makefile b/drivers/pci/hotplug/Makefile
index 3e632ff8c71..31a307004b9 100644
--- a/drivers/pci/hotplug/Makefile
+++ b/drivers/pci/hotplug/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_HOTPLUG_PCI_PCIE) += pciehp.o
obj-$(CONFIG_HOTPLUG_PCI_SHPC) += shpchp.o
obj-$(CONFIG_HOTPLUG_PCI_RPA) += rpaphp.o
obj-$(CONFIG_HOTPLUG_PCI_RPA_DLPAR) += rpadlpar_io.o
+obj-$(CONFIG_HOTPLUG_PCI_SGI) += sgi_hotplug.o
pci_hotplug-objs := pci_hotplug_core.o
diff --git a/drivers/pci/hotplug/sgi_hotplug.c b/drivers/pci/hotplug/sgi_hotplug.c
new file mode 100644
index 00000000000..323041fd41d
--- /dev/null
+++ b/drivers/pci/hotplug/sgi_hotplug.c
@@ -0,0 +1,611 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2005 Silicon Graphics, Inc. All rights reserved.
+ *
+ * This work was based on the 2.4/2.6 kernel development by Dick Reigner.
+ * Work to add BIOS PROM support was completed by Mike Habeck.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/proc_fs.h>
+#include <linux/types.h>
+
+#include <asm/sn/addrs.h>
+#include <asm/sn/l1.h>
+#include <asm/sn/module.h>
+#include <asm/sn/pcibr_provider.h>
+#include <asm/sn/pcibus_provider_defs.h>
+#include <asm/sn/pcidev.h>
+#include <asm/sn/sn_sal.h>
+#include <asm/sn/types.h>
+
+#include "../pci.h"
+#include "pci_hotplug.h"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("SGI (prarit@sgi.com, dickie@sgi.com, habeck@sgi.com)");
+MODULE_DESCRIPTION("SGI Altix Hot Plug PCI Controller Driver");
+
+#define PCIIO_ASIC_TYPE_TIOCA 4
+#define PCI_SLOT_ALREADY_UP 2 /* slot already up */
+#define PCI_SLOT_ALREADY_DOWN 3 /* slot already down */
+#define PCI_L1_ERR 7 /* L1 console command error */
+#define PCI_EMPTY_33MHZ 15 /* empty 33 MHz bus */
+#define PCI_L1_QSIZE 128 /* our L1 message buffer size */
+#define SN_MAX_HP_SLOTS 32 /* max number of hotplug slots */
+#define SGI_HOTPLUG_PROM_REV 0x0420 /* Min. required PROM version */
+
+/* internal list head */
+static struct list_head sn_hp_list;
+
+/* hotplug_slot struct's private pointer */
+struct slot {
+ int device_num;
+ struct pci_bus *pci_bus;
+ /* this struct for glue internal only */
+ struct hotplug_slot *hotplug_slot;
+ struct list_head hp_list;
+};
+
+struct pcibr_slot_enable_resp {
+ int resp_sub_errno;
+ char resp_l1_msg[PCI_L1_QSIZE + 1];
+};
+
+struct pcibr_slot_disable_resp {
+ int resp_sub_errno;
+ char resp_l1_msg[PCI_L1_QSIZE + 1];
+};
+
+enum sn_pci_req_e {
+ PCI_REQ_SLOT_ELIGIBLE,
+ PCI_REQ_SLOT_DISABLE
+};
+
+static int enable_slot(struct hotplug_slot *slot);
+static int disable_slot(struct hotplug_slot *slot);
+static int get_power_status(struct hotplug_slot *slot, u8 *value);
+
+static struct hotplug_slot_ops sn_hotplug_slot_ops = {
+ .owner = THIS_MODULE,
+ .enable_slot = enable_slot,
+ .disable_slot = disable_slot,
+ .get_power_status = get_power_status,
+};
+
+static DECLARE_MUTEX(sn_hotplug_sem);
+
+static int sn_pci_slot_valid(struct pci_bus *pci_bus, int device)
+{
+ struct pcibus_info *pcibus_info;
+ int bricktype;
+ int bus_num;
+
+ pcibus_info = SN_PCIBUS_BUSSOFT_INFO(pci_bus);
+
+ /* Check to see if this is a valid slot on 'pci_bus' */
+ if (!(pcibus_info->pbi_valid_devices & (1 << device)))
+ return -EPERM;
+
+ bricktype = MODULE_GET_BTYPE(pcibus_info->pbi_moduleid);
+ bus_num = pcibus_info->pbi_buscommon.bs_persist_busnum & 0xf;
+
+ /* Do not allow hotplug operations on base I/O cards */
+ if ((bricktype == L1_BRICKTYPE_IX || bricktype == L1_BRICKTYPE_IA) &&
+ (bus_num == 1 && device != 1))
+ return -EPERM;
+
+ return 1;
+}
+
+static int sn_pci_bus_valid(struct pci_bus *pci_bus)
+{
+ struct pcibus_info *pcibus_info;
+ int asic_type;
+ int bricktype;
+
+ pcibus_info = SN_PCIBUS_BUSSOFT_INFO(pci_bus);
+
+ /* Don't register slots hanging off the TIOCA bus */
+ asic_type = pcibus_info->pbi_buscommon.bs_asic_type;
+ if (asic_type == PCIIO_ASIC_TYPE_TIOCA)
+ return -EPERM;
+
+ /* Only register slots in I/O Bricks that support hotplug */
+ bricktype = MODULE_GET_BTYPE(pcibus_info->pbi_moduleid);
+ switch (bricktype) {
+ case L1_BRICKTYPE_IX:
+ case L1_BRICKTYPE_PX:
+ case L1_BRICKTYPE_IA:
+ case L1_BRICKTYPE_PA:
+ return 1;
+ break;
+ default:
+ return -EPERM;
+ break;
+ }
+
+ return -EIO;
+}
+
+static int sn_hp_slot_private_alloc(struct hotplug_slot *bss_hotplug_slot,
+ struct pci_bus *pci_bus, int device)
+{
+ struct pcibus_info *pcibus_info;
+ struct slot *slot;
+
+ pcibus_info = SN_PCIBUS_BUSSOFT_INFO(pci_bus);
+
+ bss_hotplug_slot->private = kcalloc(1, sizeof(struct slot),
+ GFP_KERNEL);
+ if (!bss_hotplug_slot->private)
+ return -ENOMEM;
+ slot = (struct slot *)bss_hotplug_slot->private;
+
+ bss_hotplug_slot->name = kmalloc(33, GFP_KERNEL);
+ if (!bss_hotplug_slot->name) {
+ kfree(bss_hotplug_slot->private);
+ return -ENOMEM;
+ }
+
+ slot->device_num = device;
+ slot->pci_bus = pci_bus;
+
+ sprintf(bss_hotplug_slot->name, "module_%c%c%c%c%.2d_b_%d_s_%d",
+ '0'+RACK_GET_CLASS(MODULE_GET_RACK(pcibus_info->pbi_moduleid)),
+ '0'+RACK_GET_GROUP(MODULE_GET_RACK(pcibus_info->pbi_moduleid)),
+ '0'+RACK_GET_NUM(MODULE_GET_RACK(pcibus_info->pbi_moduleid)),
+ MODULE_GET_BTCHAR(pcibus_info->pbi_moduleid),
+ MODULE_GET_BPOS(pcibus_info->pbi_moduleid),
+ ((int)pcibus_info->pbi_buscommon.bs_persist_busnum) & 0xf,
+ device + 1);
+
+ slot->hotplug_slot = bss_hotplug_slot;
+ list_add(&slot->hp_list, &sn_hp_list);
+
+ return 0;
+}
+
+static struct hotplug_slot * sn_hp_destroy(void)
+{
+ struct slot *slot;
+ struct list_head *list;
+ struct hotplug_slot *bss_hotplug_slot = NULL;
+
+ list_for_each(list, &sn_hp_list) {
+ slot = list_entry(list, struct slot, hp_list);
+ bss_hotplug_slot = slot->hotplug_slot;
+ list_del(&((struct slot *)bss_hotplug_slot->private)->
+ hp_list);
+ break;
+ }
+ return bss_hotplug_slot;
+}
+
+static void sn_bus_alloc_data(struct pci_dev *dev)
+{
+ struct list_head *node;
+ struct pci_bus *subordinate_bus;
+ struct pci_dev *child;
+
+ sn_pci_fixup_slot(dev);
+
+ /* Recursively sets up the sn_irq_info structs */
+ if (dev->subordinate) {
+ subordinate_bus = dev->subordinate;
+ list_for_each(node, &subordinate_bus->devices) {
+ child = list_entry(node, struct pci_dev, bus_list);
+ sn_bus_alloc_data(child);
+ }
+ }
+}
+
+static void sn_bus_free_data(struct pci_dev *dev)
+{
+ struct list_head *node;
+ struct pci_bus *subordinate_bus;
+ struct pci_dev *child;
+
+ /* Recursively clean up sn_irq_info structs */
+ if (dev->subordinate) {
+ subordinate_bus = dev->subordinate;
+ list_for_each(node, &subordinate_bus->devices) {
+ child = list_entry(node, struct pci_dev, bus_list);
+ sn_bus_free_data(child);
+ }
+ }
+ sn_pci_unfixup_slot(dev);
+}
+
+static u8 sn_power_status_get(struct hotplug_slot *bss_hotplug_slot)
+{
+ struct slot *slot = (struct slot *)bss_hotplug_slot->private;
+ struct pcibus_info *pcibus_info;
+ u8 retval;
+
+ pcibus_info = SN_PCIBUS_BUSSOFT_INFO(slot->pci_bus);
+ retval = pcibus_info->pbi_enabled_devices & (1 << slot->device_num);
+
+ return retval ? 1 : 0;
+}
+
+static void sn_slot_mark_enable(struct hotplug_slot *bss_hotplug_slot,
+ int device_num)
+{
+ struct slot *slot = (struct slot *)bss_hotplug_slot->private;
+ struct pcibus_info *pcibus_info;
+
+ pcibus_info = SN_PCIBUS_BUSSOFT_INFO(slot->pci_bus);
+ pcibus_info->pbi_enabled_devices |= (1 << device_num);
+}
+
+static void sn_slot_mark_disable(struct hotplug_slot *bss_hotplug_slot,
+ int device_num)
+{
+ struct slot *slot = (struct slot *)bss_hotplug_slot->private;
+ struct pcibus_info *pcibus_info;
+
+ pcibus_info = SN_PCIBUS_BUSSOFT_INFO(slot->pci_bus);
+ pcibus_info->pbi_enabled_devices &= ~(1 << device_num);
+}
+
+static int sn_slot_enable(struct hotplug_slot *bss_hotplug_slot,
+ int device_num)
+{
+ struct slot *slot = (struct slot *)bss_hotplug_slot->private;
+ struct pcibus_info *pcibus_info;
+ struct pcibr_slot_enable_resp resp;
+ int rc;
+
+ pcibus_info = SN_PCIBUS_BUSSOFT_INFO(slot->pci_bus);
+
+ /*
+ * Power-on and initialize the slot in the SN
+ * PCI infrastructure.
+ */
+ rc = sal_pcibr_slot_enable(pcibus_info, device_num, &resp);
+
+ if (rc == PCI_SLOT_ALREADY_UP) {
+ dev_dbg(slot->pci_bus->self, "is already active\n");
+ return -EPERM;
+ }
+
+ if (rc == PCI_L1_ERR) {
+ dev_dbg(slot->pci_bus->self,
+ "L1 failure %d with message: %s",
+ resp.resp_sub_errno, resp.resp_l1_msg);
+ return -EPERM;
+ }
+
+ if (rc) {
+ dev_dbg(slot->pci_bus->self,
+ "insert failed with error %d sub-error %d\n",
+ rc, resp.resp_sub_errno);
+ return -EIO;
+ }
+
+ sn_slot_mark_enable(bss_hotplug_slot, device_num);
+
+ return 0;
+}
+
+static int sn_slot_disable(struct hotplug_slot *bss_hotplug_slot,
+ int device_num, int action)
+{
+ struct slot *slot = (struct slot *)bss_hotplug_slot->private;
+ struct pcibus_info *pcibus_info;
+ struct pcibr_slot_disable_resp resp;
+ int rc;
+
+ pcibus_info = SN_PCIBUS_BUSSOFT_INFO(slot->pci_bus);
+
+ rc = sal_pcibr_slot_disable(pcibus_info, device_num, action, &resp);
+
+ if (action == PCI_REQ_SLOT_ELIGIBLE && rc == PCI_SLOT_ALREADY_DOWN) {
+ dev_dbg(slot->pci_bus->self, "Slot %s already inactive\n");
+ return -ENODEV;
+ }
+
+ if (action == PCI_REQ_SLOT_ELIGIBLE && rc == PCI_EMPTY_33MHZ) {
+ dev_dbg(slot->pci_bus->self,
+ "Cannot remove last 33MHz card\n");
+ return -EPERM;
+ }
+
+ if (action == PCI_REQ_SLOT_ELIGIBLE && rc == PCI_L1_ERR) {
+ dev_dbg(slot->pci_bus->self,
+ "L1 failure %d with message \n%s\n",
+ resp.resp_sub_errno, resp.resp_l1_msg);
+ return -EPERM;
+ }
+
+ if (action == PCI_REQ_SLOT_ELIGIBLE && rc) {
+ dev_dbg(slot->pci_bus->self,
+ "remove failed with error %d sub-error %d\n",
+ rc, resp.resp_sub_errno);
+ return -EIO;
+ }
+
+ if (action == PCI_REQ_SLOT_ELIGIBLE && !rc)
+ return 0;
+
+ if (action == PCI_REQ_SLOT_DISABLE && !rc) {
+ sn_slot_mark_disable(bss_hotplug_slot, device_num);
+ dev_dbg(slot->pci_bus->self, "remove successful\n");
+ return 0;
+ }
+
+ if (action == PCI_REQ_SLOT_DISABLE && rc) {
+ dev_dbg(slot->pci_bus->self,"remove failed rc = %d\n", rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+static int enable_slot(struct hotplug_slot *bss_hotplug_slot)
+{
+ struct slot *slot = (struct slot *)bss_hotplug_slot->private;
+ struct pci_bus *new_bus = NULL;
+ struct pci_dev *dev;
+ int func, num_funcs;
+ int new_ppb = 0;
+ int rc;
+
+ /* Serialize the Linux PCI infrastructure */
+ down(&sn_hotplug_sem);
+
+ /*
+ * Power-on and initialize the slot in the SN
+ * PCI infrastructure.
+ */
+ rc = sn_slot_enable(bss_hotplug_slot, slot->device_num);
+ if (rc) {
+ up(&sn_hotplug_sem);
+ return rc;
+ }
+
+ num_funcs = pci_scan_slot(slot->pci_bus, PCI_DEVFN(slot->device_num+1,
+ PCI_FUNC(0)));
+ if (!num_funcs) {
+ dev_dbg(slot->pci_bus->self, "no device in slot\n");
+ up(&sn_hotplug_sem);
+ return -ENODEV;
+ }
+
+ sn_pci_controller_fixup(pci_domain_nr(slot->pci_bus),
+ slot->pci_bus->number,
+ slot->pci_bus);
+ /*
+ * Map SN resources for all functions on the card
+ * to the Linux PCI interface and tell the drivers
+ * about them.
+ */
+ for (func = 0; func < num_funcs; func++) {
+ dev = pci_get_slot(slot->pci_bus,
+ PCI_DEVFN(slot->device_num + 1,
+ PCI_FUNC(func)));
+
+
+ if (dev) {
+ if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
+ unsigned char sec_bus;
+ pci_read_config_byte(dev, PCI_SECONDARY_BUS,
+ &sec_bus);
+ new_bus = pci_add_new_bus(dev->bus, dev,
+ sec_bus);
+ pci_scan_child_bus(new_bus);
+ sn_pci_controller_fixup(pci_domain_nr(new_bus),
+ new_bus->number,
+ new_bus);
+ new_ppb = 1;
+ }
+ sn_bus_alloc_data(dev);
+ pci_dev_put(dev);
+ }
+ }
+
+ /* Call the driver for the new device */
+ pci_bus_add_devices(slot->pci_bus);
+ /* Call the drivers for the new devices subordinate to PPB */
+ if (new_ppb)
+ pci_bus_add_devices(new_bus);
+
+ up(&sn_hotplug_sem);
+
+ if (rc == 0)
+ dev_dbg(slot->pci_bus->self,
+ "insert operation successful\n");
+ else
+ dev_dbg(slot->pci_bus->self,
+ "insert operation failed rc = %d\n", rc);
+
+ return rc;
+}
+
+static int disable_slot(struct hotplug_slot *bss_hotplug_slot)
+{
+ struct slot *slot = (struct slot *)bss_hotplug_slot->private;
+ struct pci_dev *dev;
+ int func;
+ int rc;
+
+ /* Acquire update access to the bus */
+ down(&sn_hotplug_sem);
+
+ /* is it okay to bring this slot down? */
+ rc = sn_slot_disable(bss_hotplug_slot, slot->device_num,
+ PCI_REQ_SLOT_ELIGIBLE);
+ if (rc)
+ goto leaving;
+
+ /* Free the SN resources assigned to the Linux device.*/
+ for (func = 0; func < 8; func++) {
+ dev = pci_get_slot(slot->pci_bus,
+ PCI_DEVFN(slot->device_num+1,
+ PCI_FUNC(func)));
+ if (dev) {
+ /*
+ * Some drivers may use dma accesses during the
+ * driver remove function. We release the sysdata
+ * areas after the driver remove functions have
+ * been called.
+ */
+ sn_bus_store_sysdata(dev);
+ sn_bus_free_data(dev);
+ pci_remove_bus_device(dev);
+ pci_dev_put(dev);
+ }
+ }
+
+ /* free the collected sysdata pointers */
+ sn_bus_free_sysdata();
+
+ /* Deactivate slot */
+ rc = sn_slot_disable(bss_hotplug_slot, slot->device_num,
+ PCI_REQ_SLOT_DISABLE);
+ leaving:
+ /* Release the bus lock */
+ up(&sn_hotplug_sem);
+
+ return rc;
+}
+
+static int get_power_status(struct hotplug_slot *bss_hotplug_slot, u8 *value)
+{
+ down(&sn_hotplug_sem);
+ *value = sn_power_status_get(bss_hotplug_slot);
+ up(&sn_hotplug_sem);
+ return 0;
+}
+
+static void sn_release_slot(struct hotplug_slot *bss_hotplug_slot)
+{
+ kfree(bss_hotplug_slot->info);
+ kfree(bss_hotplug_slot->name);
+ kfree(bss_hotplug_slot->private);
+ kfree(bss_hotplug_slot);
+}
+
+static int sn_hotplug_slot_register(struct pci_bus *pci_bus)
+{
+ int device;
+ struct hotplug_slot *bss_hotplug_slot;
+ int rc = 0;
+
+ /*
+ * Currently only four devices are supported,
+ * in the future there maybe more -- up to 32.
+ */
+
+ for (device = 0; device < SN_MAX_HP_SLOTS ; device++) {
+ if (sn_pci_slot_valid(pci_bus, device) != 1)
+ continue;
+
+ bss_hotplug_slot = kcalloc(1,sizeof(struct hotplug_slot),
+ GFP_KERNEL);
+ if (!bss_hotplug_slot) {
+ rc = -ENOMEM;
+ goto alloc_err;
+ }
+
+ bss_hotplug_slot->info =
+ kcalloc(1,sizeof(struct hotplug_slot_info),
+ GFP_KERNEL);
+ if (!bss_hotplug_slot->info) {
+ rc = -ENOMEM;
+ goto alloc_err;
+ }
+
+ if (sn_hp_slot_private_alloc(bss_hotplug_slot,
+ pci_bus, device)) {
+ rc = -ENOMEM;
+ goto alloc_err;
+ }
+
+ bss_hotplug_slot->ops = &sn_hotplug_slot_ops;
+ bss_hotplug_slot->release = &sn_release_slot;
+
+ rc = pci_hp_register(bss_hotplug_slot);
+ if (rc)
+ goto register_err;
+ }
+ dev_dbg(pci_bus->self, "Registered bus with hotplug\n");
+ return rc;
+
+register_err:
+ dev_dbg(pci_bus->self, "bus failed to register with err = %d\n",
+ rc);
+
+alloc_err:
+ if (rc == -ENOMEM)
+ dev_dbg(pci_bus->self, "Memory allocation error\n");
+
+ /* destroy THIS element */
+ if (bss_hotplug_slot)
+ sn_release_slot(bss_hotplug_slot);
+
+ /* destroy anything else on the list */
+ while ((bss_hotplug_slot = sn_hp_destroy()))
+ pci_hp_deregister(bss_hotplug_slot);
+
+ return rc;
+}
+
+static int sn_pci_hotplug_init(void)
+{
+ struct pci_bus *pci_bus = NULL;
+ int rc;
+ int registered = 0;
+
+ INIT_LIST_HEAD(&sn_hp_list);
+
+ if (sn_sal_rev() < SGI_HOTPLUG_PROM_REV) {
+ printk(KERN_ERR "%s: PROM version must be greater than 4.05\n",
+ __FUNCTION__);
+ return -EPERM;
+ }
+
+ while ((pci_bus = pci_find_next_bus(pci_bus))) {
+ if (!pci_bus->sysdata)
+ continue;
+
+ rc = sn_pci_bus_valid(pci_bus);
+ if (rc != 1) {
+ dev_dbg(pci_bus->self, "not a valid hotplug bus\n");
+ continue;
+ }
+ dev_dbg(pci_bus->self, "valid hotplug bus\n");
+
+ rc = sn_hotplug_slot_register(pci_bus);
+ if (!rc)
+ registered = 1;
+ else {
+ registered = 0;
+ break;
+ }
+ }
+
+ return registered == 1 ? 0 : -ENODEV;
+}
+
+static void sn_pci_hotplug_exit(void)
+{
+ struct hotplug_slot *bss_hotplug_slot;
+
+ while ((bss_hotplug_slot = sn_hp_destroy())) {
+ pci_hp_deregister(bss_hotplug_slot);
+ }
+
+ if (!list_empty(&sn_hp_list))
+ printk(KERN_ERR "%s: internal list is not empty\n", __FILE__);
+}
+
+module_init(sn_pci_hotplug_init);
+module_exit(sn_pci_hotplug_exit);
diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c
index bc01d34e263..e9e37abe1f7 100644
--- a/drivers/pci/pci-acpi.c
+++ b/drivers/pci/pci-acpi.c
@@ -1,9 +1,10 @@
/*
* File: pci-acpi.c
- * Purpose: Provide PCI supports in ACPI
+ * Purpose: Provide PCI support in ACPI
*
- * Copyright (C) 2004 Intel
- * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com)
+ * Copyright (C) 2005 David Shaohua Li <shaohua.li@intel.com>
+ * Copyright (C) 2004 Tom Long Nguyen <tom.l.nguyen@intel.com>
+ * Copyright (C) 2004 Intel Corp.
*/
#include <linux/delay.h>
@@ -16,6 +17,7 @@
#include <acpi/acpi_bus.h>
#include <linux/pci-acpi.h>
+#include "pci.h"
static u32 ctrlset_buf[3] = {0, 0, 0};
static u32 global_ctrlsets = 0;
@@ -207,3 +209,105 @@ acpi_status pci_osc_control_set(u32 flags)
return status;
}
EXPORT_SYMBOL(pci_osc_control_set);
+
+/*
+ * _SxD returns the D-state with the highest power
+ * (lowest D-state number) supported in the S-state "x".
+ *
+ * If the devices does not have a _PRW
+ * (Power Resources for Wake) supporting system wakeup from "x"
+ * then the OS is free to choose a lower power (higher number
+ * D-state) than the return value from _SxD.
+ *
+ * But if _PRW is enabled at S-state "x", the OS
+ * must not choose a power lower than _SxD --
+ * unless the device has an _SxW method specifying
+ * the lowest power (highest D-state number) the device
+ * may enter while still able to wake the system.
+ *
+ * ie. depending on global OS policy:
+ *
+ * if (_PRW at S-state x)
+ * choose from highest power _SxD to lowest power _SxW
+ * else // no _PRW at S-state x
+ * choose highest power _SxD or any lower power
+ *
+ * currently we simply return _SxD, if present.
+ */
+
+static int acpi_pci_choose_state(struct pci_dev *pdev, pm_message_t state)
+{
+ /* TBD */
+
+ return -ENODEV;
+}
+
+static int acpi_pci_set_power_state(struct pci_dev *dev, pci_power_t state)
+{
+ acpi_handle handle = DEVICE_ACPI_HANDLE(&dev->dev);
+ static int state_conv[] = {
+ [0] = 0,
+ [1] = 1,
+ [2] = 2,
+ [3] = 3,
+ [4] = 3
+ };
+ int acpi_state = state_conv[(int __force) state];
+
+ if (!handle)
+ return -ENODEV;
+ return acpi_bus_set_power(handle, acpi_state);
+}
+
+
+/* ACPI bus type */
+static int pci_acpi_find_device(struct device *dev, acpi_handle *handle)
+{
+ struct pci_dev * pci_dev;
+ acpi_integer addr;
+
+ pci_dev = to_pci_dev(dev);
+ /* Please ref to ACPI spec for the syntax of _ADR */
+ addr = (PCI_SLOT(pci_dev->devfn) << 16) | PCI_FUNC(pci_dev->devfn);
+ *handle = acpi_get_child(DEVICE_ACPI_HANDLE(dev->parent), addr);
+ if (!*handle)
+ return -ENODEV;
+ return 0;
+}
+
+static int pci_acpi_find_root_bridge(struct device *dev, acpi_handle *handle)
+{
+ int num;
+ unsigned int seg, bus;
+
+ /*
+ * The string should be the same as root bridge's name
+ * Please look at 'pci_scan_bus_parented'
+ */
+ num = sscanf(dev->bus_id, "pci%04x:%02x", &seg, &bus);
+ if (num != 2)
+ return -ENODEV;
+ *handle = acpi_get_pci_rootbridge_handle(seg, bus);
+ if (!*handle)
+ return -ENODEV;
+ return 0;
+}
+
+static struct acpi_bus_type pci_acpi_bus = {
+ .bus = &pci_bus_type,
+ .find_device = pci_acpi_find_device,
+ .find_bridge = pci_acpi_find_root_bridge,
+};
+
+static int __init pci_acpi_init(void)
+{
+ int ret;
+
+ ret = register_acpi_bus_type(&pci_acpi_bus);
+ if (ret)
+ return 0;
+ platform_pci_choose_state = acpi_pci_choose_state;
+ platform_pci_set_power_state = acpi_pci_set_power_state;
+ return 0;
+}
+arch_initcall(pci_acpi_init);
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..1b34fc56067 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -235,7 +235,7 @@ pci_find_parent_resource(const struct pci_dev *dev, struct resource *res)
* -EIO if device does not support PCI PM.
* 0 if we can successfully change the power state.
*/
-
+int (*platform_pci_set_power_state)(struct pci_dev *dev, pci_power_t t);
int
pci_set_power_state(struct pci_dev *dev, pci_power_t state)
{
@@ -299,11 +299,20 @@ pci_set_power_state(struct pci_dev *dev, pci_power_t state)
msleep(10);
else if (state == PCI_D2 || dev->current_state == PCI_D2)
udelay(200);
- dev->current_state = state;
+ /*
+ * Give firmware a chance to be called, such as ACPI _PRx, _PSx
+ * Firmware method after natice method ?
+ */
+ if (platform_pci_set_power_state)
+ platform_pci_set_power_state(dev, state);
+
+ dev->current_state = state;
return 0;
}
+int (*platform_pci_choose_state)(struct pci_dev *dev, pm_message_t state);
+
/**
* pci_choose_state - Choose the power state of a PCI device
* @dev: PCI device to be suspended
@@ -316,10 +325,17 @@ pci_set_power_state(struct pci_dev *dev, pci_power_t state)
pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state)
{
+ int ret;
+
if (!pci_find_capability(dev, PCI_CAP_ID_PM))
return PCI_D0;
- switch (state) {
+ if (platform_pci_choose_state) {
+ ret = platform_pci_choose_state(dev, state);
+ if (ret >= 0)
+ state = ret;
+ }
+ switch (state) {
case 0: return PCI_D0;
case 3: return PCI_D3hot;
default:
@@ -334,10 +350,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 +364,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/pci.h b/drivers/pci/pci.h
index 744da0d4ae5..d94d7af4f7a 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -11,6 +11,10 @@ extern int pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
void (*alignf)(void *, struct resource *,
unsigned long, unsigned long),
void *alignf_data);
+/* Firmware callbacks */
+extern int (*platform_pci_choose_state)(struct pci_dev *dev, pm_message_t state);
+extern int (*platform_pci_set_power_state)(struct pci_dev *dev, pci_power_t state);
+
/* PCI /proc functions */
#ifdef CONFIG_PROC_FS
extern int pci_proc_attach_device(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..393e0cee91a 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++) {
@@ -317,7 +325,7 @@ int pcie_port_device_register(struct pci_dev *dev)
static int suspend_iter(struct device *dev, void *data)
{
struct pcie_port_service_driver *service_driver;
- u32 state = (u32)data;
+ pm_message_t state = * (pm_message_t *) data;
if ((dev->bus == &pcie_port_bus_type) &&
(dev->driver)) {
@@ -328,9 +336,9 @@ static int suspend_iter(struct device *dev, void *data)
return 0;
}
-int pcie_port_device_suspend(struct pci_dev *dev, u32 state)
+int pcie_port_device_suspend(struct pci_dev *dev, pm_message_t state)
{
- device_for_each_child(&dev->dev, (void *)state, suspend_iter);
+ device_for_each_child(&dev->dev, &state, suspend_iter);
return 0;
}
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/search.c b/drivers/pci/search.c
index a90a533eba0..05fa91a31c6 100644
--- a/drivers/pci/search.c
+++ b/drivers/pci/search.c
@@ -379,6 +379,7 @@ exit:
EXPORT_SYMBOL(pci_dev_present);
EXPORT_SYMBOL(pci_find_bus);
+EXPORT_SYMBOL(pci_find_next_bus);
EXPORT_SYMBOL(pci_find_device);
EXPORT_SYMBOL(pci_find_device_reverse);
EXPORT_SYMBOL(pci_find_slot);
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c
index 6b628de948a..9fe48f712be 100644
--- a/drivers/pci/setup-bus.c
+++ b/drivers/pci/setup-bus.c
@@ -74,6 +74,7 @@ pbus_assign_resources_sorted(struct pci_bus *bus)
idx = res - &list->dev->resource[0];
if (pci_assign_resource(list->dev, idx)) {
res->start = 0;
+ res->end = 0;
res->flags = 0;
}
tmp = list;
@@ -273,6 +274,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;
}