diff options
author | Ingo Molnar <mingo@elte.hu> | 2009-02-04 14:54:56 +0100 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-02-04 14:54:56 +0100 |
commit | bb960a1e42042e82447a5bc0941b3ab6d614bac3 (patch) | |
tree | d2295a923fabb1b01b25bb015c4c2e42ee9df5ca /drivers/pci | |
parent | 858770619debfb9269add63e4ba8b7c6b5538dd1 (diff) | |
parent | 06fc732c33a7ff5e4c91bcf4a6ca86b5e335ad9a (diff) |
Merge branch 'core/xen' into x86/urgent
Diffstat (limited to 'drivers/pci')
-rw-r--r-- | drivers/pci/hotplug/Makefile | 6 | ||||
-rw-r--r-- | drivers/pci/hotplug/acpiphp_glue.c | 2 | ||||
-rw-r--r-- | drivers/pci/hotplug/fakephp.c | 42 | ||||
-rw-r--r-- | drivers/pci/hotplug/pciehp_core.c | 4 | ||||
-rw-r--r-- | drivers/pci/intel-iommu.c | 3 | ||||
-rw-r--r-- | drivers/pci/msi.c | 29 | ||||
-rw-r--r-- | drivers/pci/pci-driver.c | 112 | ||||
-rw-r--r-- | drivers/pci/pci.c | 71 | ||||
-rw-r--r-- | drivers/pci/pci.h | 6 | ||||
-rw-r--r-- | drivers/pci/syscall.c | 12 |
10 files changed, 172 insertions, 115 deletions
diff --git a/drivers/pci/hotplug/Makefile b/drivers/pci/hotplug/Makefile index e31fb91652c..2aa117c8cd8 100644 --- a/drivers/pci/hotplug/Makefile +++ b/drivers/pci/hotplug/Makefile @@ -5,11 +5,15 @@ obj-$(CONFIG_HOTPLUG_PCI) += pci_hotplug.o obj-$(CONFIG_HOTPLUG_PCI_COMPAQ) += cpqphp.o obj-$(CONFIG_HOTPLUG_PCI_IBM) += ibmphp.o + +# pciehp should be linked before acpiphp in order to allow the native driver +# to attempt to bind first. We can then fall back to generic support. + +obj-$(CONFIG_HOTPLUG_PCI_PCIE) += pciehp.o obj-$(CONFIG_HOTPLUG_PCI_ACPI) += acpiphp.o obj-$(CONFIG_HOTPLUG_PCI_ACPI_IBM) += acpiphp_ibm.o obj-$(CONFIG_HOTPLUG_PCI_CPCI_ZT5550) += cpcihp_zt5550.o obj-$(CONFIG_HOTPLUG_PCI_CPCI_GENERIC) += cpcihp_generic.o -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 diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index f09b1010d47..803d9ddd6e7 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -266,6 +266,8 @@ static int detect_ejectable_slots(struct pci_bus *pbus) int found = acpi_pci_detect_ejectable(pbus); if (!found) { acpi_handle bridge_handle = acpi_pci_get_bridge_handle(pbus); + if (!bridge_handle) + return 0; acpi_walk_namespace(ACPI_TYPE_DEVICE, bridge_handle, (u32)1, is_pci_dock_device, (void *)&found, NULL); } diff --git a/drivers/pci/hotplug/fakephp.c b/drivers/pci/hotplug/fakephp.c index b0e7de9e536..d8649e12729 100644 --- a/drivers/pci/hotplug/fakephp.c +++ b/drivers/pci/hotplug/fakephp.c @@ -195,13 +195,13 @@ static void remove_slot_worker(struct work_struct *work) * Tries hard not to re-enable already existing devices; * also handles scanning of subfunctions. */ -static void pci_rescan_slot(struct pci_dev *temp) +static int pci_rescan_slot(struct pci_dev *temp) { struct pci_bus *bus = temp->bus; struct pci_dev *dev; int func; - int retval; u8 hdr_type; + int count = 0; if (!pci_read_config_byte(temp, PCI_HEADER_TYPE, &hdr_type)) { temp->hdr_type = hdr_type & 0x7f; @@ -213,17 +213,12 @@ static void pci_rescan_slot(struct pci_dev *temp) dbg("New device on %s function %x:%x\n", bus->name, temp->devfn >> 3, temp->devfn & 7); - retval = pci_bus_add_device(dev); - if (retval) - dev_err(&dev->dev, "error adding " - "device, continuing.\n"); - else - add_slot(dev); + count++; } } /* multifunction device? */ if (!(hdr_type & 0x80)) - return; + return count; /* continue scanning for other functions */ for (func = 1, temp->devfn++; func < 8; func++, temp->devfn++) { @@ -239,16 +234,13 @@ static void pci_rescan_slot(struct pci_dev *temp) dbg("New device on %s function %x:%x\n", bus->name, temp->devfn >> 3, temp->devfn & 7); - retval = pci_bus_add_device(dev); - if (retval) - dev_err(&dev->dev, "error adding " - "device, continuing.\n"); - else - add_slot(dev); + count++; } } } } + + return count; } @@ -262,6 +254,8 @@ static void pci_rescan_bus(const struct pci_bus *bus) { unsigned int devfn; struct pci_dev *dev; + int retval; + int found = 0; dev = alloc_pci_dev(); if (!dev) return; @@ -270,7 +264,23 @@ static void pci_rescan_bus(const struct pci_bus *bus) dev->sysdata = bus->sysdata; for (devfn = 0; devfn < 0x100; devfn += 8) { dev->devfn = devfn; - pci_rescan_slot(dev); + found += pci_rescan_slot(dev); + } + + if (found) { + pci_bus_assign_resources(bus); + list_for_each_entry(dev, &bus->devices, bus_list) { + /* Skip already-added devices */ + if (dev->is_added) + continue; + retval = pci_bus_add_device(dev); + if (retval) + dev_err(&dev->dev, + "Error adding device, continuing\n"); + else + add_slot(dev); + } + pci_bus_add_devices(bus); } kfree(dev); } diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c index 5482d4ed825..c2485542f54 100644 --- a/drivers/pci/hotplug/pciehp_core.c +++ b/drivers/pci/hotplug/pciehp_core.c @@ -126,8 +126,10 @@ static int set_lock_status(struct hotplug_slot *hotplug_slot, u8 status) mutex_lock(&slot->ctrl->crit_sect); /* has it been >1 sec since our last toggle? */ - if ((get_seconds() - slot->last_emi_toggle) < 1) + if ((get_seconds() - slot->last_emi_toggle) < 1) { + mutex_unlock(&slot->ctrl->crit_sect); return -EINVAL; + } /* see what our current state is */ retval = get_lock_status(hotplug_slot, &value); diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 235fb7a5a8a..3dfecb20d5e 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -438,7 +438,8 @@ static struct intel_iommu *device_to_iommu(u8 bus, u8 devfn) continue; for (i = 0; i < drhd->devices_cnt; i++) - if (drhd->devices[i]->bus->number == bus && + if (drhd->devices[i] && + drhd->devices[i]->bus->number == bus && drhd->devices[i]->devfn == devfn) return drhd->iommu; diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index b4a90badd0a..44f15ff70c1 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -103,6 +103,16 @@ static void msix_set_enable(struct pci_dev *dev, int enable) } } +/* + * Essentially, this is ((1 << (1 << x)) - 1), but without the + * undefinedness of a << 32. + */ +static inline __attribute_const__ u32 msi_mask(unsigned x) +{ + static const u32 mask[] = { 1, 2, 4, 0xf, 0xff, 0xffff, 0xffffffff }; + return mask[x]; +} + static void msix_flush_writes(struct irq_desc *desc) { struct msi_desc *entry; @@ -398,21 +408,18 @@ static int msi_capability_init(struct pci_dev *dev) entry->msi_attrib.masked = 1; entry->msi_attrib.default_irq = dev->irq; /* Save IOAPIC IRQ */ entry->msi_attrib.pos = pos; - if (entry->msi_attrib.maskbit) { - entry->mask_base = (void __iomem *)(long)msi_mask_bits_reg(pos, - entry->msi_attrib.is_64); - } entry->dev = dev; if (entry->msi_attrib.maskbit) { - unsigned int maskbits, temp; + unsigned int base, maskbits, temp; + + base = msi_mask_bits_reg(pos, entry->msi_attrib.is_64); + entry->mask_base = (void __iomem *)(long)base; + /* All MSIs are unmasked by default, Mask them all */ - pci_read_config_dword(dev, - msi_mask_bits_reg(pos, entry->msi_attrib.is_64), - &maskbits); - temp = (1 << multi_msi_capable(control)); - temp = ((temp - 1) & ~temp); + pci_read_config_dword(dev, base, &maskbits); + temp = msi_mask((control & PCI_MSI_FLAGS_QMASK) >> 1); maskbits |= temp; - pci_write_config_dword(dev, entry->msi_attrib.is_64, maskbits); + pci_write_config_dword(dev, base, maskbits); entry->msi_attrib.maskbits_mask = temp; } list_add_tail(&entry->list, &dev->msi_list); diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index c697f268085..ab1d615425a 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -355,17 +355,28 @@ static int pci_legacy_suspend(struct device *dev, pm_message_t state) int i = 0; if (drv && drv->suspend) { + pci_dev->state_saved = false; + i = drv->suspend(pci_dev, state); suspend_report_result(drv->suspend, i); - } else { - pci_save_state(pci_dev); - /* - * This is for compatibility with existing code with legacy PM - * support. - */ - pci_pm_set_unknown_state(pci_dev); + if (i) + return i; + + if (pci_dev->state_saved) + goto Fixup; + + if (WARN_ON_ONCE(pci_dev->current_state != PCI_D0)) + goto Fixup; } + pci_save_state(pci_dev); + pci_dev->state_saved = true; + /* + * This is for compatibility with existing code with legacy PM support. + */ + pci_pm_set_unknown_state(pci_dev); + + Fixup: pci_fixup_device(pci_fixup_suspend, pci_dev); return i; @@ -386,81 +397,35 @@ static int pci_legacy_suspend_late(struct device *dev, pm_message_t state) static int pci_legacy_resume_early(struct device *dev) { - int error = 0; struct pci_dev * pci_dev = to_pci_dev(dev); struct pci_driver * drv = pci_dev->driver; - pci_fixup_device(pci_fixup_resume_early, pci_dev); - - if (drv && drv->resume_early) - error = drv->resume_early(pci_dev); - return error; + return drv && drv->resume_early ? + drv->resume_early(pci_dev) : 0; } static int pci_legacy_resume(struct device *dev) { - int error; struct pci_dev * pci_dev = to_pci_dev(dev); struct pci_driver * drv = pci_dev->driver; pci_fixup_device(pci_fixup_resume, pci_dev); - if (drv && drv->resume) { - error = drv->resume(pci_dev); - } else { - /* restore the PCI config space */ - pci_restore_state(pci_dev); - error = pci_pm_reenable_device(pci_dev); - } - return error; + return drv && drv->resume ? + drv->resume(pci_dev) : pci_pm_reenable_device(pci_dev); } /* Auxiliary functions used by the new power management framework */ -static int pci_restore_standard_config(struct pci_dev *pci_dev) -{ - struct pci_dev *parent = pci_dev->bus->self; - int error = 0; - - /* Check if the device's bus is operational */ - if (!parent || parent->current_state == PCI_D0) { - pci_restore_state(pci_dev); - pci_update_current_state(pci_dev, PCI_D0); - } else { - dev_warn(&pci_dev->dev, "unable to restore config, " - "bridge %s in low power state D%d\n", pci_name(parent), - parent->current_state); - pci_dev->current_state = PCI_UNKNOWN; - error = -EAGAIN; - } - - return error; -} - -static bool pci_is_bridge(struct pci_dev *pci_dev) -{ - return !!(pci_dev->subordinate); -} - static void pci_pm_default_resume_noirq(struct pci_dev *pci_dev) { - if (pci_restore_standard_config(pci_dev)) - pci_fixup_device(pci_fixup_resume_early, pci_dev); + pci_restore_standard_config(pci_dev); + pci_dev->state_saved = false; + pci_fixup_device(pci_fixup_resume_early, pci_dev); } static int pci_pm_default_resume(struct pci_dev *pci_dev) { - /* - * pci_restore_standard_config() should have been called once already, - * but it would have failed if the device's parent bridge had not been - * in power state D0 at that time. Check it and try again if necessary. - */ - if (pci_dev->current_state == PCI_UNKNOWN) { - int error = pci_restore_standard_config(pci_dev); - if (error) - return error; - } - pci_fixup_device(pci_fixup_resume, pci_dev); if (!pci_is_bridge(pci_dev)) @@ -575,11 +540,11 @@ static int pci_pm_resume_noirq(struct device *dev) struct device_driver *drv = dev->driver; int error = 0; + pci_pm_default_resume_noirq(pci_dev); + if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_resume_early(dev); - pci_pm_default_resume_noirq(pci_dev); - if (drv && drv->pm && drv->pm->resume_noirq) error = drv->pm->resume_noirq(dev); @@ -592,6 +557,13 @@ static int pci_pm_resume(struct device *dev) struct device_driver *drv = dev->driver; int error = 0; + /* + * This is necessary for the suspend error path in which resume is + * called without restoring the standard config registers of the device. + */ + if (pci_dev->state_saved) + pci_restore_standard_config(pci_dev); + if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_resume(dev); @@ -697,7 +669,10 @@ static int pci_pm_poweroff(struct device *dev) if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_suspend(dev, PMSG_HIBERNATE); - if (drv && drv->pm && drv->pm->poweroff) { + if (!drv || !drv->pm) + return 0; + + if (drv->pm->poweroff) { error = drv->pm->poweroff(dev); suspend_report_result(drv->pm->poweroff, error); } @@ -730,11 +705,11 @@ static int pci_pm_restore_noirq(struct device *dev) struct device_driver *drv = dev->driver; int error = 0; + pci_pm_default_resume_noirq(pci_dev); + if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_resume_early(dev); - pci_pm_default_resume_noirq(pci_dev); - if (drv && drv->pm && drv->pm->restore_noirq) error = drv->pm->restore_noirq(dev); @@ -747,6 +722,13 @@ static int pci_pm_restore(struct device *dev) struct device_driver *drv = dev->driver; int error = 0; + /* + * This is necessary for the hibernation error path in which restore is + * called without restoring the standard config registers of the device. + */ + if (pci_dev->state_saved) + pci_restore_standard_config(pci_dev); + if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_resume(dev); diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index c12f6c79069..48807556b47 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -22,7 +22,7 @@ #include <asm/dma.h> /* isa_dma_bridge_buggy */ #include "pci.h" -unsigned int pci_pm_d3_delay = 10; +unsigned int pci_pm_d3_delay = PCI_PM_D3_WAIT; #ifdef CONFIG_PCI_DOMAINS int pci_domains_supported = 1; @@ -426,6 +426,7 @@ static inline int platform_pci_sleep_wake(struct pci_dev *dev, bool enable) * given PCI device * @dev: PCI device to handle. * @state: PCI power state (D0, D1, D2, D3hot) to put the device into. + * @wait: If 'true', wait for the device to change its power state * * RETURN VALUE: * -EINVAL if the requested state is invalid. @@ -435,7 +436,7 @@ static inline int platform_pci_sleep_wake(struct pci_dev *dev, bool enable) * 0 if device's power state has been successfully changed. */ static int -pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state) +pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state, bool wait) { u16 pmcsr; bool need_restore = false; @@ -480,8 +481,10 @@ pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state) break; case PCI_UNKNOWN: /* Boot-up */ if ((pmcsr & PCI_PM_CTRL_STATE_MASK) == PCI_D3hot - && !(pmcsr & PCI_PM_CTRL_NO_SOFT_RESET)) + && !(pmcsr & PCI_PM_CTRL_NO_SOFT_RESET)) { need_restore = true; + wait = true; + } /* Fall-through: force to D0 */ default: pmcsr = 0; @@ -491,12 +494,15 @@ pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state) /* enter specified state */ pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, pmcsr); + if (!wait) + return 0; + /* Mandatory power management transition delays */ /* see PCI PM 1.1 5.6.1 table 18 */ if (state == PCI_D3hot || dev->current_state == PCI_D3hot) msleep(pci_pm_d3_delay); else if (state == PCI_D2 || dev->current_state == PCI_D2) - udelay(200); + udelay(PCI_PM_D2_DELAY); dev->current_state = state; @@ -515,7 +521,7 @@ pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state) if (need_restore) pci_restore_bars(dev); - if (dev->bus->self) + if (wait && dev->bus->self) pcie_aspm_pm_state_change(dev->bus->self); return 0; @@ -585,7 +591,7 @@ int pci_set_power_state(struct pci_dev *dev, pci_power_t state) if (state == PCI_D3hot && (dev->dev_flags & PCI_DEV_FLAGS_NO_D3)) return 0; - error = pci_raw_set_power_state(dev, state); + error = pci_raw_set_power_state(dev, state, true); if (state > PCI_D0 && platform_pci_power_manageable(dev)) { /* Allow the platform to finalize the transition */ @@ -730,6 +736,7 @@ pci_save_state(struct pci_dev *dev) /* XXX: 100% dword access ok here? */ for (i = 0; i < 16; i++) pci_read_config_dword(dev, i * 4,&dev->saved_config_space[i]); + dev->state_saved = true; if ((i = pci_save_pcie_state(dev)) != 0) return i; if ((i = pci_save_pcix_state(dev)) != 0) @@ -1260,15 +1267,14 @@ void pci_pm_init(struct pci_dev *dev) /* find PCI PM capability in list */ pm = pci_find_capability(dev, PCI_CAP_ID_PM); if (!pm) - goto Exit; - + return; /* Check device's ability to generate PME# */ pci_read_config_word(dev, pm + PCI_PM_PMC, &pmc); if ((pmc & PCI_PM_CAP_VER_MASK) > 3) { dev_err(&dev->dev, "unsupported PM cap regs version (%u)\n", pmc & PCI_PM_CAP_VER_MASK); - goto Exit; + return; } dev->pm_cap = pm; @@ -1307,9 +1313,6 @@ void pci_pm_init(struct pci_dev *dev) } else { dev->pme_support = 0; } - - Exit: - pci_update_current_state(dev, PCI_D0); } /** @@ -1378,6 +1381,50 @@ void pci_allocate_cap_save_buffers(struct pci_dev *dev) } /** + * pci_restore_standard_config - restore standard config registers of PCI device + * @dev: PCI device to handle + * + * This function assumes that the device's configuration space is accessible. + * If the device needs to be powered up, the function will wait for it to + * change the state. + */ +int pci_restore_standard_config(struct pci_dev *dev) +{ + pci_power_t prev_state; + int error; + + pci_update_current_state(dev, PCI_D0); + + prev_state = dev->current_state; + if (prev_state == PCI_D0) + goto Restore; + + error = pci_raw_set_power_state(dev, PCI_D0, false); + if (error) + return error; + + /* + * This assumes that we won't get a bus in B2 or B3 from the BIOS, but + * we've made this assumption forever and it appears to be universally + * satisfied. + */ + switch(prev_state) { + case PCI_D3cold: + case PCI_D3hot: + mdelay(pci_pm_d3_delay); + break; + case PCI_D2: + udelay(PCI_PM_D2_DELAY); + break; + } + + dev->current_state = PCI_D0; + + Restore: + return pci_restore_state(dev); +} + +/** * pci_enable_ari - enable ARI forwarding if hardware support it * @dev: the PCI device */ diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 1351bb4addd..26ddf78ac30 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -49,6 +49,12 @@ extern void pci_disable_enabled_device(struct pci_dev *dev); extern void pci_pm_init(struct pci_dev *dev); extern void platform_pci_wakeup_init(struct pci_dev *dev); extern void pci_allocate_cap_save_buffers(struct pci_dev *dev); +extern int pci_restore_standard_config(struct pci_dev *dev); + +static inline bool pci_is_bridge(struct pci_dev *pci_dev) +{ + return !!(pci_dev->subordinate); +} extern int pci_user_read_config_byte(struct pci_dev *dev, int where, u8 *val); extern int pci_user_read_config_word(struct pci_dev *dev, int where, u16 *val); diff --git a/drivers/pci/syscall.c b/drivers/pci/syscall.c index 645d7a60e41..ec22284eed3 100644 --- a/drivers/pci/syscall.c +++ b/drivers/pci/syscall.c @@ -14,10 +14,8 @@ #include <asm/uaccess.h> #include "pci.h" -asmlinkage long -sys_pciconfig_read(unsigned long bus, unsigned long dfn, - unsigned long off, unsigned long len, - void __user *buf) +SYSCALL_DEFINE5(pciconfig_read, unsigned long, bus, unsigned long, dfn, + unsigned long, off, unsigned long, len, void __user *, buf) { struct pci_dev *dev; u8 byte; @@ -86,10 +84,8 @@ error: return err; } -asmlinkage long -sys_pciconfig_write(unsigned long bus, unsigned long dfn, - unsigned long off, unsigned long len, - void __user *buf) +SYSCALL_DEFINE5(pciconfig_write, unsigned long, bus, unsigned long, dfn, + unsigned long, off, unsigned long, len, void __user *, buf) { struct pci_dev *dev; u8 byte; |