Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jbarnes/pci-2.6

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jbarnes/pci-2.6:
  PCI hotplug: fix lock imbalance in pciehp
  PCI PM: Restore standard config registers of all devices early
  PCI/MSI: bugfix/utilize for msi_capability_init()
This commit is contained in:
Linus Torvalds 2009-01-26 10:13:36 -08:00
commit 4a4565921a
6 changed files with 105 additions and 80 deletions

View File

@ -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);

View File

@ -398,21 +398,19 @@ 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);
pci_read_config_dword(dev, base, &maskbits);
temp = (1 << multi_msi_capable(control));
temp = ((temp - 1) & ~temp);
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);

View File

@ -355,17 +355,27 @@ 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);
/*
* 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 +396,34 @@ 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_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 +538,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);
@ -730,11 +693,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);

View File

@ -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)
@ -1373,6 +1380,50 @@ void pci_allocate_cap_save_buffers(struct pci_dev *dev)
"unable to preallocate PCI-X save buffer\n");
}
/**
* 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_restore_state(dev);
pci_update_current_state(dev, PCI_D0);
prev_state = dev->current_state;
if (prev_state == PCI_D0)
return 0;
error = pci_raw_set_power_state(dev, PCI_D0, false);
if (error)
return error;
if (pci_is_bridge(dev)) {
if (prev_state > PCI_D1)
mdelay(PCI_PM_BUS_WAIT);
} else {
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;
return 0;
}
/**
* pci_enable_ari - enable ARI forwarding if hardware support it
* @dev: the PCI device

View File

@ -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);

View File

@ -117,6 +117,10 @@ typedef int __bitwise pci_power_t;
#define PCI_UNKNOWN ((pci_power_t __force) 5)
#define PCI_POWER_ERROR ((pci_power_t __force) -1)
#define PCI_PM_D2_DELAY 200
#define PCI_PM_D3_WAIT 10
#define PCI_PM_BUS_WAIT 50
/** The pci_channel state describes connectivity between the CPU and
* the pci device. If some PCI bus between here and the pci device
* has crashed or locked up, this info is reflected here.
@ -252,6 +256,7 @@ struct pci_dev {
unsigned int ari_enabled:1; /* ARI forwarding */
unsigned int is_managed:1;
unsigned int is_pcie:1;
unsigned int state_saved:1;
pci_dev_flags_t dev_flags;
atomic_t enable_cnt; /* pci_enable_device has been called */