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:
Коммит
4a4565921a
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче