Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4a456592 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
* '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()
parents 7954d5cf c2fdd36b
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -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);
+7 −9
Original line number Diff line number Diff line
@@ -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);
+27 −64
Original line number Diff line number Diff line
@@ -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 {
		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.
	 * 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_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);

+57 −6
Original line number Diff line number Diff line
@@ -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
+6 −0
Original line number Diff line number Diff line
@@ -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);
Loading