Commit 6b5e9bdc authored by Bjorn Helgaas's avatar Bjorn Helgaas
Browse files

Merge branch 'pci/pm'

- Define pci_restore_standard_config() only for CONFIG_PM_SLEEP, since it's
  not used otherwise (Krzysztof Kozlowski)

- Power up all devices during runtime resume (Rafael J. Wysocki)

- Resume subordinate bus in bus type callbacks (Rafael J. Wysocki)

- Drop pci_dev runtime_d3cold flag since no uses remain (Rafael J. Wysocki)

- Move power-up to D0 code to pci_power_up() and rename
  pci_raw_set_power_state() to pci_set_low_power_state() (Rafael J.
  Wysocki)

- Set current_state to D3cold if the device is not accessible (Rafael J.
  Wysocki)

- Do not call pci_update_current_state() from pci_power_up() (Rafael J.
  Wysocki)

- Write 0 to PMCSR in pci_power_up() in all cases (Rafael J. Wysocki)

- Split part of pci_power_up() off to pci_set_full_power_state() (Rafael J.
  Wysocki)

- Do not restore BARs if device is not in D0 (Rafael J. Wysocki)

* pci/pm:
  PCI/PM: Replace pci_set_power_state() in pci_pm_thaw_noirq()
  PCI/PM: Rearrange pci_set_power_state()
  PCI/PM: Clean up pci_set_low_power_state()
  PCI/PM: Do not restore BARs if device is not in D0
  PCI/PM: Split pci_power_up()
  PCI/PM: Write 0 to PMCSR in pci_power_up() in all cases
  PCI/PM: Do not call pci_update_current_state() from pci_power_up()
  PCI/PM: Unfold pci_platform_power_transition() in pci_power_up()
  PCI/PM: Set current_state to D3cold if the device is not accessible
  PCI/PM: Relocate pci_set_low_power_state()
  PCI/PM: Split pci_raw_set_power_state()
  PCI/PM: Rearrange pci_update_current_state()
  PCI/PM: Drop the runtime_d3cold device flag
  PCI/PM: Resume subordinate bus in bus type callbacks
  PCI/PM: Power up all devices during runtime resume
  PCI/PM: Define pci_restore_standard_config() only for CONFIG_PM_SLEEP
parents 4f5bc91c 0f40ac35
Loading
Loading
Loading
Loading
+32 −13
Original line number Diff line number Diff line
@@ -522,9 +522,9 @@ static void pci_device_shutdown(struct device *dev)
		pci_clear_master(pci_dev);
}

#ifdef CONFIG_PM
#ifdef CONFIG_PM_SLEEP

/* Auxiliary functions used for system resume and run-time resume. */
/* Auxiliary functions used for system resume */

/**
 * pci_restore_standard_config - restore standard config registers of PCI device
@@ -544,6 +544,11 @@ static int pci_restore_standard_config(struct pci_dev *pci_dev)
	pci_pme_restore(pci_dev);
	return 0;
}
#endif /* CONFIG_PM_SLEEP */

#ifdef CONFIG_PM

/* Auxiliary functions used for system resume and run-time resume */

static void pci_pm_default_resume(struct pci_dev *pci_dev)
{
@@ -551,18 +556,34 @@ static void pci_pm_default_resume(struct pci_dev *pci_dev)
	pci_enable_wake(pci_dev, PCI_D0, false);
}

#endif

#ifdef CONFIG_PM_SLEEP

static void pci_pm_default_resume_early(struct pci_dev *pci_dev)
static void pci_pm_power_up_and_verify_state(struct pci_dev *pci_dev)
{
	pci_power_up(pci_dev);
	pci_update_current_state(pci_dev, PCI_D0);
}

static void pci_pm_default_resume_early(struct pci_dev *pci_dev)
{
	pci_pm_power_up_and_verify_state(pci_dev);
	pci_restore_state(pci_dev);
	pci_pme_restore(pci_dev);
}

static void pci_pm_bridge_power_up_actions(struct pci_dev *pci_dev)
{
	pci_bridge_wait_for_secondary_bus(pci_dev);
	/*
	 * When powering on a bridge from D3cold, the whole hierarchy may be
	 * powered on into D0uninitialized state, resume them to give them a
	 * chance to suspend again
	 */
	pci_resume_bus(pci_dev->subordinate);
}

#endif /* CONFIG_PM */

#ifdef CONFIG_PM_SLEEP

/*
 * Default "suspend" method for devices that have no driver provided suspend,
 * or not even a driver at all (second part).
@@ -934,7 +955,7 @@ static int pci_pm_resume_noirq(struct device *dev)
	pcie_pme_root_status_cleanup(pci_dev);

	if (!skip_bus_pm && prev_state == PCI_D3cold)
		pci_bridge_wait_for_secondary_bus(pci_dev);
		pci_pm_bridge_power_up_actions(pci_dev);

	if (pci_has_legacy_pm_support(pci_dev))
		return 0;
@@ -1068,7 +1089,7 @@ static int pci_pm_thaw_noirq(struct device *dev)
	 * in case the driver's "freeze" callbacks put it into a low-power
	 * state.
	 */
	pci_set_power_state(pci_dev, PCI_D0);
	pci_pm_power_up_and_verify_state(pci_dev);
	pci_restore_state(pci_dev);

	if (pci_has_legacy_pm_support(pci_dev))
@@ -1312,7 +1333,7 @@ static int pci_pm_runtime_resume(struct device *dev)
	 * to a driver because although we left it in D0, it may have gone to
	 * D3cold when the bridge above it runtime suspended.
	 */
	pci_restore_standard_config(pci_dev);
	pci_pm_default_resume_early(pci_dev);

	if (!pci_dev->driver)
		return 0;
@@ -1321,13 +1342,11 @@ static int pci_pm_runtime_resume(struct device *dev)
	pci_pm_default_resume(pci_dev);

	if (prev_state == PCI_D3cold)
		pci_bridge_wait_for_secondary_bus(pci_dev);
		pci_pm_bridge_power_up_actions(pci_dev);

	if (pm && pm->runtime_resume)
		error = pm->runtime_resume(dev);

	pci_dev->runtime_d3cold = false;

	return error;
}

+195 −148
Original line number Diff line number Diff line
@@ -1067,126 +1067,6 @@ static inline bool platform_pci_bridge_d3(struct pci_dev *dev)
	return acpi_pci_bridge_d3(dev);
}

/**
 * pci_raw_set_power_state - Use PCI PM registers to set the power state of
 *			     given PCI device
 * @dev: PCI device to handle.
 * @state: PCI power state (D0, D1, D2, D3hot) to put the device into.
 *
 * RETURN VALUE:
 * -EINVAL if the requested state is invalid.
 * -EIO if device does not support PCI PM or its PM capabilities register has a
 * wrong version, or device doesn't support the requested state.
 * 0 if device already is in the requested state.
 * 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)
{
	u16 pmcsr;
	bool need_restore = false;

	/* Check if we're already there */
	if (dev->current_state == state)
		return 0;

	if (!dev->pm_cap)
		return -EIO;

	if (state < PCI_D0 || state > PCI_D3hot)
		return -EINVAL;

	/*
	 * Validate transition: We can enter D0 from any state, but if
	 * we're already in a low-power state, we can only go deeper.  E.g.,
	 * we can go from D1 to D3, but we can't go directly from D3 to D1;
	 * we'd have to go from D3 to D0, then to D1.
	 */
	if (state != PCI_D0 && dev->current_state <= PCI_D3cold
	    && dev->current_state > state) {
		pci_err(dev, "invalid power transition (from %s to %s)\n",
			pci_power_name(dev->current_state),
			pci_power_name(state));
		return -EINVAL;
	}

	/* Check if this device supports the desired state */
	if ((state == PCI_D1 && !dev->d1_support)
	   || (state == PCI_D2 && !dev->d2_support))
		return -EIO;

	pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr);
	if (PCI_POSSIBLE_ERROR(pmcsr)) {
		pci_err(dev, "can't change power state from %s to %s (config space inaccessible)\n",
			pci_power_name(dev->current_state),
			pci_power_name(state));
		return -EIO;
	}

	/*
	 * If we're (effectively) in D3, force entire word to 0.
	 * This doesn't affect PME_Status, disables PME_En, and
	 * sets PowerState to 0.
	 */
	switch (dev->current_state) {
	case PCI_D0:
	case PCI_D1:
	case PCI_D2:
		pmcsr &= ~PCI_PM_CTRL_STATE_MASK;
		pmcsr |= state;
		break;
	case PCI_D3hot:
	case PCI_D3cold:
	case PCI_UNKNOWN: /* Boot-up */
		if ((pmcsr & PCI_PM_CTRL_STATE_MASK) == PCI_D3hot
		 && !(pmcsr & PCI_PM_CTRL_NO_SOFT_RESET))
			need_restore = true;
		fallthrough;	/* force to D0 */
	default:
		pmcsr = 0;
		break;
	}

	/* Enter specified state */
	pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, pmcsr);

	/*
	 * Mandatory power management transition delays; see PCI PM 1.1
	 * 5.6.1 table 18
	 */
	if (state == PCI_D3hot || dev->current_state == PCI_D3hot)
		pci_dev_d3_sleep(dev);
	else if (state == PCI_D2 || dev->current_state == PCI_D2)
		udelay(PCI_PM_D2_DELAY);

	pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr);
	dev->current_state = (pmcsr & PCI_PM_CTRL_STATE_MASK);
	if (dev->current_state != state)
		pci_info_ratelimited(dev, "refused to change power state from %s to %s\n",
			 pci_power_name(dev->current_state),
			 pci_power_name(state));

	/*
	 * According to section 5.4.1 of the "PCI BUS POWER MANAGEMENT
	 * INTERFACE SPECIFICATION, REV. 1.2", a device transitioning
	 * from D3hot to D0 _may_ perform an internal reset, thereby
	 * going to "D0 Uninitialized" rather than "D0 Initialized".
	 * For example, at least some versions of the 3c905B and the
	 * 3c556B exhibit this behaviour.
	 *
	 * At least some laptop BIOSen (e.g. the Thinkpad T21) leave
	 * devices in a D3hot state at boot.  Consequently, we need to
	 * restore at least the BARs so that the device will be
	 * accessible to its driver.
	 */
	if (need_restore)
		pci_restore_bars(dev);

	if (dev->bus->self)
		pcie_aspm_pm_state_change(dev->bus->self);

	return 0;
}

/**
 * pci_update_current_state - Read power state of given device and cache it
 * @dev: PCI device to handle.
@@ -1201,14 +1081,17 @@ static int pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state)
 */
void pci_update_current_state(struct pci_dev *dev, pci_power_t state)
{
	if (platform_pci_get_power_state(dev) == PCI_D3cold ||
	    !pci_device_is_present(dev)) {
	if (platform_pci_get_power_state(dev) == PCI_D3cold) {
		dev->current_state = PCI_D3cold;
	} else if (dev->pm_cap) {
		u16 pmcsr;

		pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr);
		dev->current_state = (pmcsr & PCI_PM_CTRL_STATE_MASK);
		if (PCI_POSSIBLE_ERROR(pmcsr)) {
			dev->current_state = PCI_D3cold;
			return;
		}
		dev->current_state = pmcsr & PCI_PM_CTRL_STATE_MASK;
	} else {
		dev->current_state = state;
	}
@@ -1306,26 +1189,114 @@ static int pci_dev_wait(struct pci_dev *dev, char *reset_type, int timeout)
/**
 * pci_power_up - Put the given device into D0
 * @dev: PCI device to power up
 *
 * On success, return 0 or 1, depending on whether or not it is necessary to
 * restore the device's BARs subsequently (1 is returned in that case).
 */
int pci_power_up(struct pci_dev *dev)
{
	pci_platform_power_transition(dev, PCI_D0);
	bool need_restore;
	pci_power_t state;
	u16 pmcsr;

	platform_pci_set_power_state(dev, PCI_D0);

	if (!dev->pm_cap) {
		state = platform_pci_get_power_state(dev);
		if (state == PCI_UNKNOWN)
			dev->current_state = PCI_D0;
		else
			dev->current_state = state;

		if (state == PCI_D0)
			return 0;

		return -EIO;
	}

	pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr);
	if (PCI_POSSIBLE_ERROR(pmcsr)) {
		pci_err(dev, "Unable to change power state from %s to D0, device inaccessible\n",
			pci_power_name(dev->current_state));
		dev->current_state = PCI_D3cold;
		return -EIO;
	}

	state = pmcsr & PCI_PM_CTRL_STATE_MASK;

	need_restore = (state == PCI_D3hot || dev->current_state >= PCI_D3hot) &&
			!(pmcsr & PCI_PM_CTRL_NO_SOFT_RESET);

	if (state == PCI_D0)
		goto end;

	/*
	 * Mandatory power management transition delays are handled in
	 * pci_pm_resume_noirq() and pci_pm_runtime_resume() of the
	 * corresponding bridge.
	 * Force the entire word to 0. This doesn't affect PME_Status, disables
	 * PME_En, and sets PowerState to 0.
	 */
	if (dev->runtime_d3cold) {
	pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, 0);

	/* Mandatory transition delays; see PCI PM 1.2. */
	if (state == PCI_D3hot)
		pci_dev_d3_sleep(dev);
	else if (state == PCI_D2)
		udelay(PCI_PM_D2_DELAY);

end:
	dev->current_state = PCI_D0;
	if (need_restore)
		return 1;

	return 0;
}

/**
 * pci_set_full_power_state - Put a PCI device into D0 and update its state
 * @dev: PCI device to power up
 *
 * Call pci_power_up() to put @dev into D0, read from its PCI_PM_CTRL register
 * to confirm the state change, restore its BARs if they might be lost and
 * reconfigure ASPM in acordance with the new power state.
 *
 * If pci_restore_state() is going to be called right after a power state change
 * to D0, it is more efficient to use pci_power_up() directly instead of this
 * function.
 */
static int pci_set_full_power_state(struct pci_dev *dev)
{
	u16 pmcsr;
	int ret;

	ret = pci_power_up(dev);
	if (ret < 0)
		return ret;

	pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr);
	dev->current_state = pmcsr & PCI_PM_CTRL_STATE_MASK;
	if (dev->current_state != PCI_D0) {
		pci_info_ratelimited(dev, "Refused to change power state from %s to D0\n",
				     pci_power_name(dev->current_state));
	} else if (ret > 0) {
		/*
		 * When powering on a bridge from D3cold, the whole hierarchy
		 * may be powered on into D0uninitialized state, resume them to
		 * give them a chance to suspend again
		 * According to section 5.4.1 of the "PCI BUS POWER MANAGEMENT
		 * INTERFACE SPECIFICATION, REV. 1.2", a device transitioning
		 * from D3hot to D0 _may_ perform an internal reset, thereby
		 * going to "D0 Uninitialized" rather than "D0 Initialized".
		 * For example, at least some versions of the 3c905B and the
		 * 3c556B exhibit this behaviour.
		 *
		 * At least some laptop BIOSen (e.g. the Thinkpad T21) leave
		 * devices in a D3hot state at boot.  Consequently, we need to
		 * restore at least the BARs so that the device will be
		 * accessible to its driver.
		 */
		pci_resume_bus(dev->subordinate);
		pci_restore_bars(dev);
	}

	return pci_raw_set_power_state(dev, PCI_D0);
	if (dev->bus->self)
		pcie_aspm_pm_state_change(dev->bus->self);

	return 0;
}

/**
@@ -1352,6 +1323,79 @@ void pci_bus_set_current_state(struct pci_bus *bus, pci_power_t state)
		pci_walk_bus(bus, __pci_dev_set_current_state, &state);
}

/**
 * pci_set_low_power_state - Put a PCI device into a low-power state.
 * @dev: PCI device to handle.
 * @state: PCI power state (D1, D2, D3hot) to put the device into.
 *
 * Use the device's PCI_PM_CTRL register to put it into a low-power state.
 *
 * RETURN VALUE:
 * -EINVAL if the requested state is invalid.
 * -EIO if device does not support PCI PM or its PM capabilities register has a
 * wrong version, or device doesn't support the requested state.
 * 0 if device already is in the requested state.
 * 0 if device's power state has been successfully changed.
 */
static int pci_set_low_power_state(struct pci_dev *dev, pci_power_t state)
{
	u16 pmcsr;

	if (!dev->pm_cap)
		return -EIO;

	/*
	 * Validate transition: We can enter D0 from any state, but if
	 * we're already in a low-power state, we can only go deeper.  E.g.,
	 * we can go from D1 to D3, but we can't go directly from D3 to D1;
	 * we'd have to go from D3 to D0, then to D1.
	 */
	if (dev->current_state <= PCI_D3cold && dev->current_state > state) {
		pci_dbg(dev, "Invalid power transition (from %s to %s)\n",
			pci_power_name(dev->current_state),
			pci_power_name(state));
		return -EINVAL;
	}

	/* Check if this device supports the desired state */
	if ((state == PCI_D1 && !dev->d1_support)
	   || (state == PCI_D2 && !dev->d2_support))
		return -EIO;

	pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr);
	if (PCI_POSSIBLE_ERROR(pmcsr)) {
		pci_err(dev, "Unable to change power state from %s to %s, device inaccessible\n",
			pci_power_name(dev->current_state),
			pci_power_name(state));
		dev->current_state = PCI_D3cold;
		return -EIO;
	}

	pmcsr &= ~PCI_PM_CTRL_STATE_MASK;
	pmcsr |= state;

	/* Enter specified state */
	pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, pmcsr);

	/* Mandatory power management transition delays; see PCI PM 1.2. */
	if (state == PCI_D3hot)
		pci_dev_d3_sleep(dev);
	else if (state == PCI_D2)
		udelay(PCI_PM_D2_DELAY);

	pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr);
	dev->current_state = pmcsr & PCI_PM_CTRL_STATE_MASK;
	if (dev->current_state != state)
		pci_info_ratelimited(dev, "Refused to change power state from %s to %s\n",
				     pci_power_name(dev->current_state),
				     pci_power_name(state));

	if (dev->bus->self)
		pcie_aspm_pm_state_change(dev->bus->self);

	return 0;
}

/**
 * pci_set_power_state - Set the power state of a PCI device
 * @dev: PCI device to handle.
@@ -1393,7 +1437,7 @@ int pci_set_power_state(struct pci_dev *dev, pci_power_t state)
		return 0;

	if (state == PCI_D0)
		return pci_power_up(dev);
		return pci_set_full_power_state(dev);

	/*
	 * This device is quirked not to be put into D3, so don't put it in
@@ -1402,19 +1446,25 @@ 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;

	if (state == PCI_D3cold) {
		/*
	 * To put device in D3cold, we put device into D3hot in native
	 * way, then put device into D3cold with platform ops
		 * To put the device in D3cold, put it into D3hot in the native
		 * way, then put it into D3cold using platform ops.
		 */
	error = pci_raw_set_power_state(dev, state > PCI_D3hot ?
					PCI_D3hot : state);
		error = pci_set_low_power_state(dev, PCI_D3hot);

	if (pci_platform_power_transition(dev, state))
		if (pci_platform_power_transition(dev, PCI_D3cold))
			return error;

		/* Powering off a bridge may power off the whole hierarchy */
	if (state == PCI_D3cold)
		if (dev->current_state == PCI_D3cold)
			pci_bus_set_current_state(dev->subordinate, PCI_D3cold);
	} else {
		error = pci_set_low_power_state(dev, state);

		if (pci_platform_power_transition(dev, state))
			return error;
	}

	return 0;
}
@@ -2718,8 +2768,6 @@ int pci_finish_runtime_suspend(struct pci_dev *dev)
	if (target_state == PCI_POWER_ERROR)
		return -EIO;

	dev->runtime_d3cold = target_state == PCI_D3cold;

	/*
	 * There are systems (for example, Intel mobile chips since Coffee
	 * Lake) where the power drawn while suspended can be significantly
@@ -2737,7 +2785,6 @@ int pci_finish_runtime_suspend(struct pci_dev *dev)
	if (error) {
		pci_enable_wake(dev, target_state, false);
		pci_restore_ptm_state(dev);
		dev->runtime_d3cold = false;
	}

	return error;
+0 −4
Original line number Diff line number Diff line
@@ -379,10 +379,6 @@ struct pci_dev {
	unsigned int	mmio_always_on:1;	/* Disallow turning off io/mem
						   decoding during BAR sizing */
	unsigned int	wakeup_prepared:1;
	unsigned int	runtime_d3cold:1;	/* Whether go through runtime
						   D3cold, not set for devices
						   powered on/off by the
						   corresponding bridge */
	unsigned int	skip_bus_pm:1;	/* Internal: Skip bus-level PM */
	unsigned int	ignore_hotplug:1;	/* Ignore hotplug events */
	unsigned int	hotplug_user_indicators:1; /* SlotCtl indicators