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:
commit
4a4565921a
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue