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); mutex_lock(&slot->ctrl->crit_sect);
/* has it been >1 sec since our last toggle? */ /* 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; return -EINVAL;
}
/* see what our current state is */ /* see what our current state is */
retval = get_lock_status(hotplug_slot, &value); 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.masked = 1;
entry->msi_attrib.default_irq = dev->irq; /* Save IOAPIC IRQ */ entry->msi_attrib.default_irq = dev->irq; /* Save IOAPIC IRQ */
entry->msi_attrib.pos = pos; 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; entry->dev = dev;
if (entry->msi_attrib.maskbit) { 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 */ /* All MSIs are unmasked by default, Mask them all */
pci_read_config_dword(dev, pci_read_config_dword(dev, base, &maskbits);
msi_mask_bits_reg(pos, entry->msi_attrib.is_64),
&maskbits);
temp = (1 << multi_msi_capable(control)); temp = (1 << multi_msi_capable(control));
temp = ((temp - 1) & ~temp); temp = ((temp - 1) & ~temp);
maskbits |= 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; entry->msi_attrib.maskbits_mask = temp;
} }
list_add_tail(&entry->list, &dev->msi_list); 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; int i = 0;
if (drv && drv->suspend) { if (drv && drv->suspend) {
pci_dev->state_saved = false;
i = drv->suspend(pci_dev, state); i = drv->suspend(pci_dev, state);
suspend_report_result(drv->suspend, i); suspend_report_result(drv->suspend, i);
} else { if (i)
pci_save_state(pci_dev); return i;
/*
* This is for compatibility with existing code with legacy PM if (pci_dev->state_saved)
* support. goto Fixup;
*/
pci_pm_set_unknown_state(pci_dev); 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); pci_fixup_device(pci_fixup_suspend, pci_dev);
return i; 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) static int pci_legacy_resume_early(struct device *dev)
{ {
int error = 0;
struct pci_dev * pci_dev = to_pci_dev(dev); struct pci_dev * pci_dev = to_pci_dev(dev);
struct pci_driver * drv = pci_dev->driver; struct pci_driver * drv = pci_dev->driver;
pci_fixup_device(pci_fixup_resume_early, pci_dev); return drv && drv->resume_early ?
drv->resume_early(pci_dev) : 0;
if (drv && drv->resume_early)
error = drv->resume_early(pci_dev);
return error;
} }
static int pci_legacy_resume(struct device *dev) static int pci_legacy_resume(struct device *dev)
{ {
int error;
struct pci_dev * pci_dev = to_pci_dev(dev); struct pci_dev * pci_dev = to_pci_dev(dev);
struct pci_driver * drv = pci_dev->driver; struct pci_driver * drv = pci_dev->driver;
pci_fixup_device(pci_fixup_resume, pci_dev); pci_fixup_device(pci_fixup_resume, pci_dev);
if (drv && drv->resume) { return drv && drv->resume ?
error = drv->resume(pci_dev); drv->resume(pci_dev) : pci_pm_reenable_device(pci_dev);
} else {
/* restore the PCI config space */
pci_restore_state(pci_dev);
error = pci_pm_reenable_device(pci_dev);
}
return error;
} }
/* Auxiliary functions used by the new power management framework */ /* 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) static void pci_pm_default_resume_noirq(struct pci_dev *pci_dev)
{ {
if (pci_restore_standard_config(pci_dev)) pci_restore_standard_config(pci_dev);
pci_fixup_device(pci_fixup_resume_early, pci_dev); pci_fixup_device(pci_fixup_resume_early, pci_dev);
} }
static int pci_pm_default_resume(struct pci_dev *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); pci_fixup_device(pci_fixup_resume, pci_dev);
if (!pci_is_bridge(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; struct device_driver *drv = dev->driver;
int error = 0; int error = 0;
pci_pm_default_resume_noirq(pci_dev);
if (pci_has_legacy_pm_support(pci_dev)) if (pci_has_legacy_pm_support(pci_dev))
return pci_legacy_resume_early(dev); return pci_legacy_resume_early(dev);
pci_pm_default_resume_noirq(pci_dev);
if (drv && drv->pm && drv->pm->resume_noirq) if (drv && drv->pm && drv->pm->resume_noirq)
error = drv->pm->resume_noirq(dev); 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; struct device_driver *drv = dev->driver;
int error = 0; int error = 0;
pci_pm_default_resume_noirq(pci_dev);
if (pci_has_legacy_pm_support(pci_dev)) if (pci_has_legacy_pm_support(pci_dev))
return pci_legacy_resume_early(dev); return pci_legacy_resume_early(dev);
pci_pm_default_resume_noirq(pci_dev);
if (drv && drv->pm && drv->pm->restore_noirq) if (drv && drv->pm && drv->pm->restore_noirq)
error = drv->pm->restore_noirq(dev); error = drv->pm->restore_noirq(dev);

View File

@ -22,7 +22,7 @@
#include <asm/dma.h> /* isa_dma_bridge_buggy */ #include <asm/dma.h> /* isa_dma_bridge_buggy */
#include "pci.h" #include "pci.h"
unsigned int pci_pm_d3_delay = 10; unsigned int pci_pm_d3_delay = PCI_PM_D3_WAIT;
#ifdef CONFIG_PCI_DOMAINS #ifdef CONFIG_PCI_DOMAINS
int pci_domains_supported = 1; 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 * given PCI device
* @dev: PCI device to handle. * @dev: PCI device to handle.
* @state: PCI power state (D0, D1, D2, D3hot) to put the device into. * @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: * RETURN VALUE:
* -EINVAL if the requested state is invalid. * -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. * 0 if device's power state has been successfully changed.
*/ */
static int 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; u16 pmcsr;
bool need_restore = false; bool need_restore = false;
@ -480,8 +481,10 @@ pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state)
break; break;
case PCI_UNKNOWN: /* Boot-up */ case PCI_UNKNOWN: /* Boot-up */
if ((pmcsr & PCI_PM_CTRL_STATE_MASK) == PCI_D3hot 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; need_restore = true;
wait = true;
}
/* Fall-through: force to D0 */ /* Fall-through: force to D0 */
default: default:
pmcsr = 0; pmcsr = 0;
@ -491,12 +494,15 @@ pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state)
/* enter specified state */ /* enter specified state */
pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, pmcsr); pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, pmcsr);
if (!wait)
return 0;
/* Mandatory power management transition delays */ /* Mandatory power management transition delays */
/* see PCI PM 1.1 5.6.1 table 18 */ /* see PCI PM 1.1 5.6.1 table 18 */
if (state == PCI_D3hot || dev->current_state == PCI_D3hot) if (state == PCI_D3hot || dev->current_state == PCI_D3hot)
msleep(pci_pm_d3_delay); msleep(pci_pm_d3_delay);
else if (state == PCI_D2 || dev->current_state == PCI_D2) else if (state == PCI_D2 || dev->current_state == PCI_D2)
udelay(200); udelay(PCI_PM_D2_DELAY);
dev->current_state = state; dev->current_state = state;
@ -515,7 +521,7 @@ pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state)
if (need_restore) if (need_restore)
pci_restore_bars(dev); pci_restore_bars(dev);
if (dev->bus->self) if (wait && dev->bus->self)
pcie_aspm_pm_state_change(dev->bus->self); pcie_aspm_pm_state_change(dev->bus->self);
return 0; 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)) if (state == PCI_D3hot && (dev->dev_flags & PCI_DEV_FLAGS_NO_D3))
return 0; 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)) { if (state > PCI_D0 && platform_pci_power_manageable(dev)) {
/* Allow the platform to finalize the transition */ /* Allow the platform to finalize the transition */
@ -730,6 +736,7 @@ pci_save_state(struct pci_dev *dev)
/* XXX: 100% dword access ok here? */ /* XXX: 100% dword access ok here? */
for (i = 0; i < 16; i++) for (i = 0; i < 16; i++)
pci_read_config_dword(dev, i * 4,&dev->saved_config_space[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) if ((i = pci_save_pcie_state(dev)) != 0)
return i; return i;
if ((i = pci_save_pcix_state(dev)) != 0) 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"); "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 * pci_enable_ari - enable ARI forwarding if hardware support it
* @dev: the PCI device * @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 pci_pm_init(struct pci_dev *dev);
extern void platform_pci_wakeup_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 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_byte(struct pci_dev *dev, int where, u8 *val);
extern int pci_user_read_config_word(struct pci_dev *dev, int where, u16 *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_UNKNOWN ((pci_power_t __force) 5)
#define PCI_POWER_ERROR ((pci_power_t __force) -1) #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_channel state describes connectivity between the CPU and
* the pci device. If some PCI bus between here and the pci device * the pci device. If some PCI bus between here and the pci device
* has crashed or locked up, this info is reflected here. * 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 ari_enabled:1; /* ARI forwarding */
unsigned int is_managed:1; unsigned int is_managed:1;
unsigned int is_pcie:1; unsigned int is_pcie:1;
unsigned int state_saved:1;
pci_dev_flags_t dev_flags; pci_dev_flags_t dev_flags;
atomic_t enable_cnt; /* pci_enable_device has been called */ atomic_t enable_cnt; /* pci_enable_device has been called */