From 4795448117824cc4900d696f17d1516c56371045 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 18 Sep 2021 14:53:44 +0200 Subject: PCI: ACPI: Drop acpi_pci_bus The acpi_pci_bus structure was used primarily for running acpi_pci_find_companion() during PCI device objects registration, but after commit 375553a93201 ("PCI: Setup ACPI fwnode early and at the same time with OF") that function is called by pci_setup_device() via pci_set_acpi_fwnode(), which happens before calling pci_device_add() on the new PCI device object, so its ACPI companion has been set already when acpi_device_notify() runs and it will never call ->find_companion() from acpi_pci_bus. For this reason, modify acpi_device_notify() and acpi_device_notify_remove() to call pci_acpi_setup() and pci_acpi_cleanup(), respectively, directly on PCI device objects and drop acpi_pci_bus altogether. While at it, notice that pci_acpi_setup() and pci_acpi_cleanup() can obtain the ACPI companion pointer, which is guaranteed to not be NULL, from their callers and modify them to work that way so as to reduce the number of redundant checks somewhat. Signed-off-by: Rafael J. Wysocki Tested-by: Ferry Toth --- drivers/acpi/glue.c | 34 ++++++++++++++++++++++------------ drivers/pci/pci-acpi.c | 31 +++---------------------------- include/linux/pci-acpi.h | 8 ++++++++ 3 files changed, 33 insertions(+), 40 deletions(-) diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index 7a33a6d985f8..68768d6c8a47 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c @@ -17,6 +17,8 @@ #include #include #include +#include +#include #include #include "internal.h" @@ -307,13 +309,17 @@ void acpi_device_notify(struct device *dev) } adev = ACPI_COMPANION(dev); - if (dev_is_platform(dev)) - acpi_configure_pmsi_domain(dev); + if (dev_is_pci(dev)) { + pci_acpi_setup(dev, adev); + } else { + if (dev_is_platform(dev)) + acpi_configure_pmsi_domain(dev); - if (type && type->setup) - type->setup(dev); - else if (adev->handler && adev->handler->bind) - adev->handler->bind(dev); + if (type && type->setup) + type->setup(dev); + else if (adev->handler && adev->handler->bind) + adev->handler->bind(dev); + } acpi_handle_debug(ACPI_HANDLE(dev), "Bound to device %s\n", dev_name(dev)); @@ -327,16 +333,20 @@ err: void acpi_device_notify_remove(struct device *dev) { struct acpi_device *adev = ACPI_COMPANION(dev); - struct acpi_bus_type *type; if (!adev) return; - type = acpi_get_bus_type(dev); - if (type && type->cleanup) - type->cleanup(dev); - else if (adev->handler && adev->handler->unbind) - adev->handler->unbind(dev); + if (dev_is_pci(dev)) { + pci_acpi_cleanup(dev, adev); + } else { + struct acpi_bus_type *type = acpi_get_bus_type(dev); + + if (type && type->cleanup) + type->cleanup(dev); + else if (adev->handler && adev->handler->unbind) + adev->handler->unbind(dev); + } acpi_unbind_one(dev); } diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 0f40943a9a18..d6720ebb252d 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -1353,13 +1353,9 @@ static void pci_acpi_set_external_facing(struct pci_dev *dev) dev->external_facing = 1; } -static void pci_acpi_setup(struct device *dev) +void pci_acpi_setup(struct device *dev, struct acpi_device *adev) { struct pci_dev *pci_dev = to_pci_dev(dev); - struct acpi_device *adev = ACPI_COMPANION(dev); - - if (!adev) - return; pci_acpi_optimize_delay(pci_dev, adev->handle); pci_acpi_set_external_facing(pci_dev); @@ -1383,14 +1379,10 @@ static void pci_acpi_setup(struct device *dev) acpi_device_power_add_dependent(adev, dev); } -static void pci_acpi_cleanup(struct device *dev) +void pci_acpi_cleanup(struct device *dev, struct acpi_device *adev) { - struct acpi_device *adev = ACPI_COMPANION(dev); struct pci_dev *pci_dev = to_pci_dev(dev); - if (!adev) - return; - pci_acpi_remove_edr_notifier(pci_dev); pci_acpi_remove_pm_notifier(adev); if (adev->wakeup.flags.valid) { @@ -1402,20 +1394,6 @@ static void pci_acpi_cleanup(struct device *dev) } } -static bool pci_acpi_bus_match(struct device *dev) -{ - return dev_is_pci(dev); -} - -static struct acpi_bus_type acpi_pci_bus = { - .name = "PCI", - .match = pci_acpi_bus_match, - .find_companion = acpi_pci_find_companion, - .setup = pci_acpi_setup, - .cleanup = pci_acpi_cleanup, -}; - - static struct fwnode_handle *(*pci_msi_get_fwnode_cb)(struct device *dev); /** @@ -1457,8 +1435,6 @@ struct irq_domain *pci_host_bridge_acpi_msi_domain(struct pci_bus *bus) static int __init acpi_pci_init(void) { - int ret; - if (acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_MSI) { pr_info("ACPI FADT declares the system doesn't support MSI, so disable it\n"); pci_no_msi(); @@ -1469,8 +1445,7 @@ static int __init acpi_pci_init(void) pcie_no_aspm(); } - ret = register_acpi_bus_type(&acpi_pci_bus); - if (ret) + if (acpi_pci_disabled) return 0; pci_set_platform_pm(&acpi_pci_platform_pm); diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h index f16de399d2de..078225b514d4 100644 --- a/include/linux/pci-acpi.h +++ b/include/linux/pci-acpi.h @@ -84,6 +84,14 @@ extern struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root, void acpi_pci_add_bus(struct pci_bus *bus); void acpi_pci_remove_bus(struct pci_bus *bus); +#ifdef CONFIG_PCI +void pci_acpi_setup(struct device *dev, struct acpi_device *adev); +void pci_acpi_cleanup(struct device *dev, struct acpi_device *adev); +#else +static inline void pci_acpi_setup(struct device *dev, struct acpi_device *adev) {} +static inline void pci_acpi_cleanup(struct device *dev, struct acpi_device *adev) {} +#endif + #ifdef CONFIG_ACPI_PCI_SLOT void acpi_pci_slot_init(void); void acpi_pci_slot_enumerate(struct pci_bus *bus); -- cgit v1.2.3 From c4d19838d8c42828c7e39e4432ea5b33f02dac27 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 18 Sep 2021 14:56:17 +0200 Subject: ACPI: glue: Drop cleanup callback from struct acpi_bus_type Since PCI was the only user of the ->cleanup callback in struct acpi_bus_type and it is not using struct acpi_bus_type any more, drop that callback from there and update acpi_device_notify_remove() accordingly. Signed-off-by: Rafael J. Wysocki Tested-by: Ferry Toth --- drivers/acpi/glue.c | 12 +++--------- include/acpi/acpi_bus.h | 1 - 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index 68768d6c8a47..49aaf18243f6 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c @@ -337,16 +337,10 @@ void acpi_device_notify_remove(struct device *dev) if (!adev) return; - if (dev_is_pci(dev)) { + if (dev_is_pci(dev)) pci_acpi_cleanup(dev, adev); - } else { - struct acpi_bus_type *type = acpi_get_bus_type(dev); - - if (type && type->cleanup) - type->cleanup(dev); - else if (adev->handler && adev->handler->unbind) - adev->handler->unbind(dev); - } + else if (adev->handler && adev->handler->unbind) + adev->handler->unbind(dev); acpi_unbind_one(dev); } diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 13d93371790e..53b6e9f9de7b 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -570,7 +570,6 @@ struct acpi_bus_type { bool (*match)(struct device *dev); struct acpi_device * (*find_companion)(struct device *); void (*setup)(struct device *); - void (*cleanup)(struct device *); }; int register_acpi_bus_type(struct acpi_bus_type *); int unregister_acpi_bus_type(struct acpi_bus_type *); -- cgit v1.2.3 From 2ef5236660b677db15527b6d6235af4ce38ea0e3 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 18 Sep 2021 15:02:23 +0200 Subject: ACPI: glue: Look for ACPI bus type only if ACPI companion is not known Notice that it is not necessary to look for the "ACPI bus type" of the device in acpi_device_notify() if the device's ACPI companion is set upfront, so modify the code to do that lookup only if it is necessary to find the ACPI companion. Also notice that if the device's ACPI companion is not set upfront in acpi_device_notify(), the device cannot be either a PCI one or a platform one, so check for these bus types only if the device's ACPI companion is set. Signed-off-by: Rafael J. Wysocki Tested-by: Ferry Toth --- drivers/acpi/glue.c | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index 49aaf18243f6..17ec6e57c4b7 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c @@ -289,12 +289,13 @@ EXPORT_SYMBOL_GPL(acpi_unbind_one); void acpi_device_notify(struct device *dev) { - struct acpi_bus_type *type = acpi_get_bus_type(dev); struct acpi_device *adev; int ret; ret = acpi_bind_one(dev, NULL); if (ret) { + struct acpi_bus_type *type = acpi_get_bus_type(dev); + if (!type) goto err; @@ -306,21 +307,26 @@ void acpi_device_notify(struct device *dev) ret = acpi_bind_one(dev, adev); if (ret) goto err; - } - adev = ACPI_COMPANION(dev); - if (dev_is_pci(dev)) { - pci_acpi_setup(dev, adev); + if (type->setup) { + type->setup(dev); + goto done; + } } else { - if (dev_is_platform(dev)) - acpi_configure_pmsi_domain(dev); + adev = ACPI_COMPANION(dev); - if (type && type->setup) - type->setup(dev); - else if (adev->handler && adev->handler->bind) - adev->handler->bind(dev); + if (dev_is_pci(dev)) { + pci_acpi_setup(dev, adev); + goto done; + } else if (dev_is_platform(dev)) { + acpi_configure_pmsi_domain(dev); + } } + if (adev->handler && adev->handler->bind) + adev->handler->bind(dev); + +done: acpi_handle_debug(ACPI_HANDLE(dev), "Bound to device %s\n", dev_name(dev)); -- cgit v1.2.3 From d5b0d88385f5a5f865f6761d7c93e373221914a4 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 20 Sep 2021 21:16:59 +0200 Subject: PCI: PM: Do not use pci_platform_pm_ops for Intel MID PM There are only two users of struct pci_platform_pm_ops in the tree, one of which is Intel MID PM and the other one is ACPI. They are mutually exclusive and the MID PM should take precedence when they both are enabled, but whether or not this really is the case hinges on the specific ordering of arch_initcall() calls made by them. The struct pci_platform_pm_ops abstraction is not really necessary for just these two users, but it adds complexity and overhead because of retoplines involved in using all of the function pointers in there. It also makes following the code a bit more difficult than it would be otherwise. Moreover, Intel MID PCI PM doesn't even implement the majority of the function pointers in struct pci_platform_pm_ops in a meaningful way, so switch over the PCI core to calling the relevant MID PM routines, mid_pci_set_power_state() and mid_pci_set_power_state(), directly as needed and drop mid_pci_platform_pm. Signed-off-by: Rafael J. Wysocki Tested-by: Ferry Toth --- drivers/pci/pci-mid.c | 37 ++++++++----------------------------- drivers/pci/pci.c | 23 ++++++++++++++++++++++- drivers/pci/pci.h | 19 +++++++++++++++++++ 3 files changed, 49 insertions(+), 30 deletions(-) diff --git a/drivers/pci/pci-mid.c b/drivers/pci/pci-mid.c index aafd58da3a89..fbfd78127123 100644 --- a/drivers/pci/pci-mid.c +++ b/drivers/pci/pci-mid.c @@ -16,45 +16,23 @@ #include "pci.h" -static bool mid_pci_power_manageable(struct pci_dev *dev) +static bool pci_mid_pm_enabled __read_mostly; + +bool pci_use_mid_pm(void) { - return true; + return pci_mid_pm_enabled; } -static int mid_pci_set_power_state(struct pci_dev *pdev, pci_power_t state) +int mid_pci_set_power_state(struct pci_dev *pdev, pci_power_t state) { return intel_mid_pci_set_power_state(pdev, state); } -static pci_power_t mid_pci_get_power_state(struct pci_dev *pdev) +pci_power_t mid_pci_get_power_state(struct pci_dev *pdev) { return intel_mid_pci_get_power_state(pdev); } -static pci_power_t mid_pci_choose_state(struct pci_dev *pdev) -{ - return PCI_D3hot; -} - -static int mid_pci_wakeup(struct pci_dev *dev, bool enable) -{ - return 0; -} - -static bool mid_pci_need_resume(struct pci_dev *dev) -{ - return false; -} - -static const struct pci_platform_pm_ops mid_pci_platform_pm = { - .is_manageable = mid_pci_power_manageable, - .set_state = mid_pci_set_power_state, - .get_state = mid_pci_get_power_state, - .choose_state = mid_pci_choose_state, - .set_wakeup = mid_pci_wakeup, - .need_resume = mid_pci_need_resume, -}; - /* * This table should be in sync with the one in * arch/x86/platform/intel-mid/pwr.c. @@ -71,7 +49,8 @@ static int __init mid_pci_init(void) id = x86_match_cpu(lpss_cpu_ids); if (id) - pci_set_platform_pm(&mid_pci_platform_pm); + pci_mid_pm_enabled = true; + return 0; } arch_initcall(mid_pci_init); diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index ce2ab62b64cf..d4438e7bb761 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -985,45 +985,66 @@ int pci_set_platform_pm(const struct pci_platform_pm_ops *ops) static inline bool platform_pci_power_manageable(struct pci_dev *dev) { + if (pci_use_mid_pm()) + return true; + return pci_platform_pm ? pci_platform_pm->is_manageable(dev) : false; } static inline int platform_pci_set_power_state(struct pci_dev *dev, pci_power_t t) { + if (pci_use_mid_pm()) + return mid_pci_set_power_state(dev, t); + return pci_platform_pm ? pci_platform_pm->set_state(dev, t) : -ENOSYS; } static inline pci_power_t platform_pci_get_power_state(struct pci_dev *dev) { + if (pci_use_mid_pm()) + return mid_pci_get_power_state(dev); + return pci_platform_pm ? pci_platform_pm->get_state(dev) : PCI_UNKNOWN; } static inline void platform_pci_refresh_power_state(struct pci_dev *dev) { - if (pci_platform_pm && pci_platform_pm->refresh_state) + if (!pci_use_mid_pm() && pci_platform_pm && pci_platform_pm->refresh_state) pci_platform_pm->refresh_state(dev); } static inline pci_power_t platform_pci_choose_state(struct pci_dev *dev) { + if (pci_use_mid_pm()) + return PCI_POWER_ERROR; + return pci_platform_pm ? pci_platform_pm->choose_state(dev) : PCI_POWER_ERROR; } static inline int platform_pci_set_wakeup(struct pci_dev *dev, bool enable) { + if (pci_use_mid_pm()) + return PCI_POWER_ERROR; + return pci_platform_pm ? pci_platform_pm->set_wakeup(dev, enable) : -ENODEV; } static inline bool platform_pci_need_resume(struct pci_dev *dev) { + if (pci_use_mid_pm()) + return false; + return pci_platform_pm ? pci_platform_pm->need_resume(dev) : false; } static inline bool platform_pci_bridge_d3(struct pci_dev *dev) { + if (pci_use_mid_pm()) + return false; + if (pci_platform_pm && pci_platform_pm->bridge_d3) return pci_platform_pm->bridge_d3(dev); return false; diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 1cce56c2aea0..0cbade42ea48 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -744,4 +744,23 @@ extern const struct attribute_group aspm_ctrl_attr_group; extern const struct attribute_group pci_dev_reset_method_attr_group; +#ifdef CONFIG_X86_INTEL_MID +bool pci_use_mid_pm(void); +int mid_pci_set_power_state(struct pci_dev *pdev, pci_power_t state); +pci_power_t mid_pci_get_power_state(struct pci_dev *pdev); +#else +static inline bool pci_use_mid_pm(void) +{ + return false; +} +static inline int mid_pci_set_power_state(struct pci_dev *pdev, pci_power_t state) +{ + return -ENODEV; +} +static inline pci_power_t mid_pci_get_power_state(struct pci_dev *pdev) +{ + return PCI_UNKNOWN; +} +#endif + #endif /* DRIVERS_PCI_H */ -- cgit v1.2.3 From d97c5d4c622f6acfd5eddac81799d37c9a4e6a92 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 20 Sep 2021 21:17:08 +0200 Subject: PCI: ACPI: PM: Do not use pci_platform_pm_ops for ACPI Using struct pci_platform_pm_ops for ACPI adds unnecessary indirection to the interactions between the PCI core and ACPI PM, which is also subject to retpolines. Moreover, it is not particularly clear from the current code that, as far as PCI PM is concerned, "platform" really means just ACPI except for the special casess when Intel MID PCI PM is used or when ACPI support is disabled (through the kernel config or command line, or because there are no usable ACPI tables on the system). To address the above, rework the PCI PM code to invoke ACPI PM functions directly as needed and drop the acpi_pci_platform_pm object that is not necessary any more. Accordingly, update some of the ACPI PM functions in question to do extra checks in case the ACPI support is disabled (which previously was taken care of by avoiding to set the pci_platform_ops pointer in those cases). Signed-off-by: Rafael J. Wysocki Tested-by: Ferry Toth --- drivers/pci/pci-acpi.c | 42 ++++++++++++++++++++---------------------- drivers/pci/pci.c | 22 +++++++++------------- drivers/pci/pci.h | 38 +++++++++++++++++++++++++++++++++++++- 3 files changed, 66 insertions(+), 36 deletions(-) diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index d6720ebb252d..141ed146e271 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -906,10 +906,13 @@ acpi_status pci_acpi_add_pm_notifier(struct acpi_device *dev, * choose highest power _SxD or any lower power */ -static pci_power_t acpi_pci_choose_state(struct pci_dev *pdev) +pci_power_t acpi_pci_choose_state(struct pci_dev *pdev) { int acpi_state, d_max; + if (acpi_pci_disabled) + return PCI_POWER_ERROR; + if (pdev->no_d3cold) d_max = ACPI_STATE_D3_HOT; else @@ -965,7 +968,7 @@ int pci_dev_acpi_reset(struct pci_dev *dev, bool probe) return 0; } -static bool acpi_pci_power_manageable(struct pci_dev *dev) +bool acpi_pci_power_manageable(struct pci_dev *dev) { struct acpi_device *adev = ACPI_COMPANION(&dev->dev); @@ -974,13 +977,13 @@ static bool acpi_pci_power_manageable(struct pci_dev *dev) return acpi_device_power_manageable(adev); } -static bool acpi_pci_bridge_d3(struct pci_dev *dev) +bool acpi_pci_bridge_d3(struct pci_dev *dev) { const union acpi_object *obj; struct acpi_device *adev; struct pci_dev *rpdev; - if (!dev->is_hotplug_bridge) + if (acpi_pci_disabled || !dev->is_hotplug_bridge) return false; /* Assume D3 support if the bridge is power-manageable by ACPI. */ @@ -1008,7 +1011,7 @@ static bool acpi_pci_bridge_d3(struct pci_dev *dev) return obj->integer.value == 1; } -static int acpi_pci_set_power_state(struct pci_dev *dev, pci_power_t state) +int acpi_pci_set_power_state(struct pci_dev *dev, pci_power_t state) { struct acpi_device *adev = ACPI_COMPANION(&dev->dev); static const u8 state_conv[] = { @@ -1046,7 +1049,7 @@ static int acpi_pci_set_power_state(struct pci_dev *dev, pci_power_t state) return error; } -static pci_power_t acpi_pci_get_power_state(struct pci_dev *dev) +pci_power_t acpi_pci_get_power_state(struct pci_dev *dev) { struct acpi_device *adev = ACPI_COMPANION(&dev->dev); static const pci_power_t state_conv[] = { @@ -1068,7 +1071,7 @@ static pci_power_t acpi_pci_get_power_state(struct pci_dev *dev) return state_conv[state]; } -static void acpi_pci_refresh_power_state(struct pci_dev *dev) +void acpi_pci_refresh_power_state(struct pci_dev *dev) { struct acpi_device *adev = ACPI_COMPANION(&dev->dev); @@ -1093,17 +1096,23 @@ static int acpi_pci_propagate_wakeup(struct pci_bus *bus, bool enable) return 0; } -static int acpi_pci_wakeup(struct pci_dev *dev, bool enable) +int acpi_pci_wakeup(struct pci_dev *dev, bool enable) { + if (acpi_pci_disabled) + return 0; + if (acpi_pm_device_can_wakeup(&dev->dev)) return acpi_pm_set_device_wakeup(&dev->dev, enable); return acpi_pci_propagate_wakeup(dev->bus, enable); } -static bool acpi_pci_need_resume(struct pci_dev *dev) +bool acpi_pci_need_resume(struct pci_dev *dev) { - struct acpi_device *adev = ACPI_COMPANION(&dev->dev); + struct acpi_device *adev; + + if (acpi_pci_disabled) + return false; /* * In some cases (eg. Samsung 305V4A) leaving a bridge in suspend over @@ -1115,6 +1124,7 @@ static bool acpi_pci_need_resume(struct pci_dev *dev) if (pci_is_bridge(dev) && acpi_target_system_state() != ACPI_STATE_S0) return true; + adev = ACPI_COMPANION(&dev->dev); if (!adev || !acpi_device_power_manageable(adev)) return false; @@ -1128,17 +1138,6 @@ static bool acpi_pci_need_resume(struct pci_dev *dev) return !!adev->power.flags.dsw_present; } -static const struct pci_platform_pm_ops acpi_pci_platform_pm = { - .bridge_d3 = acpi_pci_bridge_d3, - .is_manageable = acpi_pci_power_manageable, - .set_state = acpi_pci_set_power_state, - .get_state = acpi_pci_get_power_state, - .refresh_state = acpi_pci_refresh_power_state, - .choose_state = acpi_pci_choose_state, - .set_wakeup = acpi_pci_wakeup, - .need_resume = acpi_pci_need_resume, -}; - void acpi_pci_add_bus(struct pci_bus *bus) { union acpi_object *obj; @@ -1448,7 +1447,6 @@ static int __init acpi_pci_init(void) if (acpi_pci_disabled) return 0; - pci_set_platform_pm(&acpi_pci_platform_pm); acpi_pci_slot_init(); acpiphp_init(); diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index d4438e7bb761..925f275daa54 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -988,7 +988,7 @@ static inline bool platform_pci_power_manageable(struct pci_dev *dev) if (pci_use_mid_pm()) return true; - return pci_platform_pm ? pci_platform_pm->is_manageable(dev) : false; + return acpi_pci_power_manageable(dev); } static inline int platform_pci_set_power_state(struct pci_dev *dev, @@ -997,7 +997,7 @@ static inline int platform_pci_set_power_state(struct pci_dev *dev, if (pci_use_mid_pm()) return mid_pci_set_power_state(dev, t); - return pci_platform_pm ? pci_platform_pm->set_state(dev, t) : -ENOSYS; + return acpi_pci_set_power_state(dev, t); } static inline pci_power_t platform_pci_get_power_state(struct pci_dev *dev) @@ -1005,13 +1005,13 @@ static inline pci_power_t platform_pci_get_power_state(struct pci_dev *dev) if (pci_use_mid_pm()) return mid_pci_get_power_state(dev); - return pci_platform_pm ? pci_platform_pm->get_state(dev) : PCI_UNKNOWN; + return acpi_pci_get_power_state(dev); } static inline void platform_pci_refresh_power_state(struct pci_dev *dev) { - if (!pci_use_mid_pm() && pci_platform_pm && pci_platform_pm->refresh_state) - pci_platform_pm->refresh_state(dev); + if (!pci_use_mid_pm()) + acpi_pci_refresh_power_state(dev); } static inline pci_power_t platform_pci_choose_state(struct pci_dev *dev) @@ -1019,8 +1019,7 @@ static inline pci_power_t platform_pci_choose_state(struct pci_dev *dev) if (pci_use_mid_pm()) return PCI_POWER_ERROR; - return pci_platform_pm ? - pci_platform_pm->choose_state(dev) : PCI_POWER_ERROR; + return acpi_pci_choose_state(dev); } static inline int platform_pci_set_wakeup(struct pci_dev *dev, bool enable) @@ -1028,8 +1027,7 @@ static inline int platform_pci_set_wakeup(struct pci_dev *dev, bool enable) if (pci_use_mid_pm()) return PCI_POWER_ERROR; - return pci_platform_pm ? - pci_platform_pm->set_wakeup(dev, enable) : -ENODEV; + return acpi_pci_wakeup(dev, enable); } static inline bool platform_pci_need_resume(struct pci_dev *dev) @@ -1037,7 +1035,7 @@ static inline bool platform_pci_need_resume(struct pci_dev *dev) if (pci_use_mid_pm()) return false; - return pci_platform_pm ? pci_platform_pm->need_resume(dev) : false; + return acpi_pci_need_resume(dev); } static inline bool platform_pci_bridge_d3(struct pci_dev *dev) @@ -1045,9 +1043,7 @@ static inline bool platform_pci_bridge_d3(struct pci_dev *dev) if (pci_use_mid_pm()) return false; - if (pci_platform_pm && pci_platform_pm->bridge_d3) - return pci_platform_pm->bridge_d3(dev); - return false; + return acpi_pci_bridge_d3(dev); } /** diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 0cbade42ea48..e6fc14d87a07 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -725,17 +725,53 @@ int pci_acpi_program_hp_params(struct pci_dev *dev); extern const struct attribute_group pci_dev_acpi_attr_group; void pci_set_acpi_fwnode(struct pci_dev *dev); int pci_dev_acpi_reset(struct pci_dev *dev, bool probe); +bool acpi_pci_power_manageable(struct pci_dev *dev); +bool acpi_pci_bridge_d3(struct pci_dev *dev); +int acpi_pci_set_power_state(struct pci_dev *dev, pci_power_t state); +pci_power_t acpi_pci_get_power_state(struct pci_dev *dev); +void acpi_pci_refresh_power_state(struct pci_dev *dev); +int acpi_pci_wakeup(struct pci_dev *dev, bool enable); +bool acpi_pci_need_resume(struct pci_dev *dev); +pci_power_t acpi_pci_choose_state(struct pci_dev *pdev); #else static inline int pci_dev_acpi_reset(struct pci_dev *dev, bool probe) { return -ENOTTY; } - static inline void pci_set_acpi_fwnode(struct pci_dev *dev) {} static inline int pci_acpi_program_hp_params(struct pci_dev *dev) { return -ENODEV; } +static inline bool acpi_pci_power_manageable(struct pci_dev *dev) +{ + return false; +} +static inline bool acpi_pci_bridge_d3(struct pci_dev *dev) +{ + return false; +} +static inline int acpi_pci_set_power_state(struct pci_dev *dev, pci_power_t state) +{ + return -ENODEV; +} +static inline pci_power_t acpi_pci_get_power_state(struct pci_dev *dev) +{ + return PCI_UNKNOWN; +} +static inline void acpi_pci_refresh_power_state(struct pci_dev *dev) {} +static inline int acpi_pci_wakeup(struct pci_dev *dev, bool enable) +{ + return -ENODEV; +} +static inline bool acpi_pci_need_resume(struct pci_dev *dev) +{ + return false; +} +static inline pci_power_t acpi_pci_choose_state(struct pci_dev *pdev) +{ + return PCI_POWER_ERROR; +} #endif #ifdef CONFIG_PCIEASPM -- cgit v1.2.3 From 98634aa8d837b38d96c06e99022be6b46ed91109 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 20 Sep 2021 21:17:16 +0200 Subject: PCI: PM: Drop struct pci_platform_pm_ops After previous changes there are no more users of struct pci_platform_pm_ops in the tree, so drop it along with all of the remaining related code. No functional impact. Signed-off-by: Rafael J. Wysocki Tested-by: Ferry Toth --- drivers/pci/pci.c | 11 ----------- drivers/pci/pci.h | 39 --------------------------------------- 2 files changed, 50 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 925f275daa54..e61e5f956306 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -972,17 +972,6 @@ static void pci_restore_bars(struct pci_dev *dev) pci_update_resource(dev, i); } -static const struct pci_platform_pm_ops *pci_platform_pm; - -int pci_set_platform_pm(const struct pci_platform_pm_ops *ops) -{ - if (!ops->is_manageable || !ops->set_state || !ops->get_state || - !ops->choose_state || !ops->set_wakeup || !ops->need_resume) - return -EINVAL; - pci_platform_pm = ops; - return 0; -} - static inline bool platform_pci_power_manageable(struct pci_dev *dev) { if (pci_use_mid_pm()) diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index e6fc14d87a07..bd39098c57da 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -63,45 +63,6 @@ struct pci_cap_saved_state *pci_find_saved_ext_cap(struct pci_dev *dev, #define PCI_PM_D3HOT_WAIT 10 /* msec */ #define PCI_PM_D3COLD_WAIT 100 /* msec */ -/** - * struct pci_platform_pm_ops - Firmware PM callbacks - * - * @bridge_d3: Does the bridge allow entering into D3 - * - * @is_manageable: returns 'true' if given device is power manageable by the - * platform firmware - * - * @set_state: invokes the platform firmware to set the device's power state - * - * @get_state: queries the platform firmware for a device's current power state - * - * @refresh_state: asks the platform to refresh the device's power state data - * - * @choose_state: returns PCI power state of given device preferred by the - * platform; to be used during system-wide transitions from a - * sleeping state to the working state and vice versa - * - * @set_wakeup: enables/disables wakeup capability for the device - * - * @need_resume: returns 'true' if the given device (which is currently - * suspended) needs to be resumed to be configured for system - * wakeup. - * - * If given platform is generally capable of power managing PCI devices, all of - * these callbacks are mandatory. - */ -struct pci_platform_pm_ops { - bool (*bridge_d3)(struct pci_dev *dev); - bool (*is_manageable)(struct pci_dev *dev); - int (*set_state)(struct pci_dev *dev, pci_power_t state); - pci_power_t (*get_state)(struct pci_dev *dev); - void (*refresh_state)(struct pci_dev *dev); - pci_power_t (*choose_state)(struct pci_dev *dev); - int (*set_wakeup)(struct pci_dev *dev, bool enable); - bool (*need_resume)(struct pci_dev *dev); -}; - -int pci_set_platform_pm(const struct pci_platform_pm_ops *ops); void pci_update_current_state(struct pci_dev *dev, pci_power_t state); void pci_refresh_power_state(struct pci_dev *dev); int pci_power_up(struct pci_dev *dev); -- cgit v1.2.3 From f09183712146909d56c9555e6901c10cc285339f Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 20 Sep 2021 21:17:39 +0200 Subject: PCI: PM: Simplify acpi_pci_power_manageable() Make acpi_pci_power_manageable() more straightforward. No functional impact. Signed-off-by: Rafael J. Wysocki Tested-by: Ferry Toth --- drivers/pci/pci-acpi.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 141ed146e271..9663b13944ad 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -972,9 +972,7 @@ bool acpi_pci_power_manageable(struct pci_dev *dev) { struct acpi_device *adev = ACPI_COMPANION(&dev->dev); - if (!adev) - return false; - return acpi_device_power_manageable(adev); + return adev && acpi_device_power_manageable(adev); } bool acpi_pci_bridge_d3(struct pci_dev *dev) -- cgit v1.2.3 From bf39c929f9053cd840155a1c39494c9a9946836b Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 29 Sep 2021 20:09:39 +0200 Subject: PCI: PM: Rearrange pci_target_state() Make pci_target_state() return D3cold or D0 without checking PME support if the current power state of the device is D3cold or if it does not support the standard PCI PM, respectively. Next, drop the tergat_state local variable that has become redundant from it. Signed-off-by: Rafael J. Wysocki Tested-by: Ferry Toth --- drivers/pci/pci.c | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index e61e5f956306..121d9deea58a 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2583,8 +2583,6 @@ EXPORT_SYMBOL(pci_wake_from_d3); */ static pci_power_t pci_target_state(struct pci_dev *dev, bool wakeup) { - pci_power_t target_state = PCI_D3hot; - if (platform_pci_power_manageable(dev)) { /* * Call the platform to find the target state for the device. @@ -2594,32 +2592,29 @@ static pci_power_t pci_target_state(struct pci_dev *dev, bool wakeup) switch (state) { case PCI_POWER_ERROR: case PCI_UNKNOWN: - break; + return PCI_D3hot; + case PCI_D1: case PCI_D2: if (pci_no_d1d2(dev)) - break; - fallthrough; - default: - target_state = state; + return PCI_D3hot; } - return target_state; + return state; } - if (!dev->pm_cap) - target_state = PCI_D0; - /* * If the device is in D3cold even though it's not power-manageable by * the platform, it may have been powered down by non-standard means. * Best to let it slumber. */ if (dev->current_state == PCI_D3cold) - target_state = PCI_D3cold; + return PCI_D3cold; + else if (!dev->pm_cap) + return PCI_D0; if (wakeup && dev->pme_support) { - pci_power_t state = target_state; + pci_power_t state = PCI_D3hot; /* * Find the deepest state from which the device can generate @@ -2634,7 +2629,7 @@ static pci_power_t pci_target_state(struct pci_dev *dev, bool wakeup) return PCI_D0; } - return target_state; + return PCI_D3hot; } /** -- cgit v1.2.3 From 6407e5ecdc66cd9da760e751a7c092c87a683861 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 29 Sep 2021 20:11:18 +0200 Subject: PCI: PM: Make pci_choose_state() call pci_target_state() The pci_choose_state() and pci_target_state() implementations are somewhat divergent without a good reason, because they are used for similar purposes. Change the pci_choose_state() implementation to use pci_target_state() internally except for transitions to the working state of the system in which case it is expected to return D0. Signed-off-by: Rafael J. Wysocki Tested-by: Ferry Toth --- drivers/pci/pci-acpi.c | 3 --- drivers/pci/pci.c | 54 +++++++++++++++----------------------------------- 2 files changed, 16 insertions(+), 41 deletions(-) diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 9663b13944ad..889dc8c0d409 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -910,9 +910,6 @@ pci_power_t acpi_pci_choose_state(struct pci_dev *pdev) { int acpi_state, d_max; - if (acpi_pci_disabled) - return PCI_POWER_ERROR; - if (pdev->no_d3cold) d_max = ACPI_STATE_D3_HOT; else diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 121d9deea58a..ae8fb60843dd 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1394,44 +1394,6 @@ int pci_set_power_state(struct pci_dev *dev, pci_power_t state) } EXPORT_SYMBOL(pci_set_power_state); -/** - * pci_choose_state - Choose the power state of a PCI device - * @dev: PCI device to be suspended - * @state: target sleep state for the whole system. This is the value - * that is passed to suspend() function. - * - * Returns PCI power state suitable for given device and given system - * message. - */ -pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state) -{ - pci_power_t ret; - - if (!dev->pm_cap) - return PCI_D0; - - ret = platform_pci_choose_state(dev); - if (ret != PCI_POWER_ERROR) - return ret; - - switch (state.event) { - case PM_EVENT_ON: - return PCI_D0; - case PM_EVENT_FREEZE: - case PM_EVENT_PRETHAW: - /* REVISIT both freeze and pre-thaw "should" use D0 */ - case PM_EVENT_SUSPEND: - case PM_EVENT_HIBERNATE: - return PCI_D3hot; - default: - pci_info(dev, "unrecognized suspend event %d\n", - state.event); - BUG(); - } - return PCI_D0; -} -EXPORT_SYMBOL(pci_choose_state); - #define PCI_EXP_SAVE_REGS 7 static struct pci_cap_saved_state *_pci_find_saved_cap(struct pci_dev *pci_dev, @@ -2843,6 +2805,22 @@ void pci_dev_complete_resume(struct pci_dev *pci_dev) spin_unlock_irq(&dev->power.lock); } +/** + * pci_choose_state - Choose the power state of a PCI device. + * @dev: Target PCI device. + * @state: Target state for the whole system. + * + * Returns PCI power state suitable for @dev and @state. + */ +pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state) +{ + if (state.event == PM_EVENT_ON) + return PCI_D0; + + return pci_target_state(dev, false); +} +EXPORT_SYMBOL(pci_choose_state); + void pci_config_pm_runtime_get(struct pci_dev *pdev) { struct device *dev = &pdev->dev; -- cgit v1.2.3 From fa1a25c51d02f153b49444aa872bee9da3c13ecf Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 29 Sep 2021 20:15:06 +0200 Subject: PCI: PM: Do not call platform_pci_power_manageable() unnecessarily Drop two invocations of platform_pci_power_manageable() that are not necessary, because the functions called when it returns 'true' do the requisite "power manageable" checks themselves. Signed-off-by: Rafael J. Wysocki Tested-by: Ferry Toth --- drivers/pci/pci.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index ae8fb60843dd..b0409bd33c45 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1191,9 +1191,7 @@ void pci_update_current_state(struct pci_dev *dev, pci_power_t state) */ void pci_refresh_power_state(struct pci_dev *dev) { - if (platform_pci_power_manageable(dev)) - platform_pci_refresh_power_state(dev); - + platform_pci_refresh_power_state(dev); pci_update_current_state(dev, dev->current_state); } @@ -1206,14 +1204,10 @@ int pci_platform_power_transition(struct pci_dev *dev, pci_power_t state) { int error; - if (platform_pci_power_manageable(dev)) { - error = platform_pci_set_power_state(dev, state); - if (!error) - pci_update_current_state(dev, state); - } else - error = -ENODEV; - - if (error && !dev->pm_cap) /* Fall back to PCI_D0 */ + error = platform_pci_set_power_state(dev, state); + if (!error) + pci_update_current_state(dev, state); + else if (!dev->pm_cap) /* Fall back to PCI_D0 */ dev->current_state = PCI_D0; return error; -- cgit v1.2.3 From 6f9f0eef0096caddc3fd92760f818970033f06ea Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 15 Oct 2021 18:45:59 +0200 Subject: PCI: PM: Fix ordering of operations in pci_back_from_sleep() The ordering of operations in pci_back_from_sleep() is incorrect, because the device may be in D3cold when it runs and pci_enable_wake() needs to access the device's configuration space which cannot be done in D3cold. Fix this by calling pci_set_power_state() to put the device into D0 before calling pci_enable_wake() for it. Signed-off-by: Rafael J. Wysocki Acked-by: Bjorn Helgaas --- drivers/pci/pci.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index b0409bd33c45..11c88ffb51d9 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2638,8 +2638,13 @@ EXPORT_SYMBOL(pci_prepare_to_sleep); */ int pci_back_from_sleep(struct pci_dev *dev) { + int ret = pci_set_power_state(dev, PCI_D0); + + if (ret) + return ret; + pci_enable_wake(dev, PCI_D0, false); - return pci_set_power_state(dev, PCI_D0); + return 0; } EXPORT_SYMBOL(pci_back_from_sleep); -- cgit v1.2.3