From 80a129afb75cba8434fc5071bd6919172442315c Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Mon, 2 Nov 2020 15:15:20 +0100 Subject: PCI: Add sysfs attribute for device power state While PCI power states D0-D3hot can be queried from user-space via lspci, D3cold cannot. lspci cannot provide an accurate value when the device is in D3cold as it has to restore the device to D0 before it can access its power state via the configuration space, leading to it reporting D0 or another on-state. Thus lspci cannot be used to diagnose power consumption issues for devices that can enter D3cold or to ensure that devices properly enter D3cold at all. Add a new sysfs device attribute for the PCI power state, showing the current power state as seen by the kernel. [bhelgaas: drop READ_ONCE(), see discussion at the link] Link: https://lore.kernel.org/r/20201102141520.831630-1-luzmaximilian@gmail.com Signed-off-by: Maximilian Luz Signed-off-by: Bjorn Helgaas --- drivers/pci/pci-sysfs.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index d15c881e2e7e..fb072f4b3176 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -124,6 +124,15 @@ static ssize_t cpulistaffinity_show(struct device *dev, } static DEVICE_ATTR_RO(cpulistaffinity); +static ssize_t power_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pci_dev *pdev = to_pci_dev(dev); + + return sprintf(buf, "%s\n", pci_power_name(pdev->current_state)); +} +static DEVICE_ATTR_RO(power_state); + /* show resources */ static ssize_t resource_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -581,6 +590,7 @@ static ssize_t driver_override_show(struct device *dev, static DEVICE_ATTR_RW(driver_override); static struct attribute *pci_dev_attrs[] = { + &dev_attr_power_state.attr, &dev_attr_resource.attr, &dev_attr_vendor.attr, &dev_attr_device.attr, -- cgit v1.2.3 From 99efde6c9bb7b42eac0459876bf964fe08e5cef9 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Wed, 25 Nov 2020 12:07:33 +0300 Subject: PCI/PM: Rename pci_wakeup_bus() to pci_resume_bus() A "wakeup" is a signal from a device telling the system that the device or the whole system should be awakened and made active. PCI devices are made active by "resuming" them. pci_wakeup_bus() is not involved with the wakeup signal; it *resumes* devices on a bus (possibly in response to a wakeup signal, but that's at a higher level). Rename pci_wakeup_bus() to pci_resume_bus() to better reflect what it does. No functional change intended. [bhelgaas: commit log, reorder before removal of pci_wakeup_event()] Link: https://lore.kernel.org/r/20201125090733.77782-2-mika.westerberg@linux.intel.com Signed-off-by: Mika Westerberg Signed-off-by: Bjorn Helgaas Reviewed-by: Rafael J. Wysocki --- drivers/gpu/vga/vga_switcheroo.c | 2 +- drivers/pci/pci.c | 15 +++++---------- include/linux/pci.h | 2 +- 3 files changed, 7 insertions(+), 12 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c index 087304b1a5d7..8843b078ad4e 100644 --- a/drivers/gpu/vga/vga_switcheroo.c +++ b/drivers/gpu/vga/vga_switcheroo.c @@ -1039,7 +1039,7 @@ static int vga_switcheroo_runtime_resume(struct device *dev) mutex_lock(&vgasr_mutex); vga_switcheroo_power_switch(pdev, VGA_SWITCHEROO_ON); mutex_unlock(&vgasr_mutex); - pci_wakeup_bus(pdev->bus); + pci_resume_bus(pdev->bus); ret = dev->bus->pm->runtime_resume(dev); if (ret) return ret; diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index e578d34095e9..664aada79de2 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1174,12 +1174,7 @@ int pci_platform_power_transition(struct pci_dev *dev, pci_power_t state) } EXPORT_SYMBOL_GPL(pci_platform_power_transition); -/** - * pci_wakeup - Wake up a PCI device - * @pci_dev: Device to handle. - * @ign: ignored parameter - */ -static int pci_wakeup(struct pci_dev *pci_dev, void *ign) +static int pci_resume_one(struct pci_dev *pci_dev, void *ign) { pci_wakeup_event(pci_dev); pm_request_resume(&pci_dev->dev); @@ -1187,13 +1182,13 @@ static int pci_wakeup(struct pci_dev *pci_dev, void *ign) } /** - * pci_wakeup_bus - Walk given bus and wake up devices on it + * pci_resume_bus - Walk given bus and runtime resume devices on it * @bus: Top bus of the subtree to walk. */ -void pci_wakeup_bus(struct pci_bus *bus) +void pci_resume_bus(struct pci_bus *bus) { if (bus) - pci_walk_bus(bus, pci_wakeup, NULL); + pci_walk_bus(bus, pci_resume_one, NULL); } static int pci_dev_wait(struct pci_dev *dev, char *reset_type, int timeout) @@ -1256,7 +1251,7 @@ int pci_power_up(struct pci_dev *dev) * may be powered on into D0uninitialized state, resume them to * give them a chance to suspend again */ - pci_wakeup_bus(dev->subordinate); + pci_resume_bus(dev->subordinate); } return pci_raw_set_power_state(dev, PCI_D0); diff --git a/include/linux/pci.h b/include/linux/pci.h index 22207a79762c..9256ef2e4327 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1271,7 +1271,7 @@ bool pci_dev_run_wake(struct pci_dev *dev); void pci_d3cold_enable(struct pci_dev *dev); void pci_d3cold_disable(struct pci_dev *dev); bool pcie_relaxed_ordering_enabled(struct pci_dev *dev); -void pci_wakeup_bus(struct pci_bus *bus); +void pci_resume_bus(struct pci_bus *bus); void pci_bus_set_current_state(struct pci_bus *bus, pci_power_t state); /* For use by arch with custom probe code */ -- cgit v1.2.3 From 9c2cc571f92500d2d0f4e70466c90ee8b2b440e6 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Wed, 25 Nov 2020 12:07:32 +0300 Subject: PCI/PM: Do not generate wakeup event when runtime resuming device When a PCI bridge is runtime resumed from D3cold, we resume any downstream devices as well. Previously, we also generated a wakeup event for each device even though this is not a wakeup signal coming from the hardware. Normally this does not cause problems but when combined with /sys/power/wakeup_count like using the steps below: # count=$(cat /sys/power/wakeup_count) # echo $count > /sys/power/wakeup_count # echo mem > /sys/power/state The system suspend cycle might fail at this point if a PCI bridge that was runtime suspended (D3cold) was runtime resumed for any reason. The runtime resume calls pci_resume_bus(), which generates a wakeup event and increases wakeup_count. Since this is not a real wakeup event, remove the call to pci_wakeup_event() from pci_resume_one(). [bhelgaas: reorder, commit log] Link: https://lore.kernel.org/r/20201125090733.77782-1-mika.westerberg@linux.intel.com Reported-by: Utkarsh Patel Signed-off-by: Mika Westerberg Signed-off-by: Bjorn Helgaas Reviewed-by: Rafael J. Wysocki --- drivers/pci/pci.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 664aada79de2..0102cc973783 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1176,7 +1176,6 @@ EXPORT_SYMBOL_GPL(pci_platform_power_transition); static int pci_resume_one(struct pci_dev *pci_dev, void *ign) { - pci_wakeup_event(pci_dev); pm_request_resume(&pci_dev->dev); return 0; } -- cgit v1.2.3