From 59dc33252ee777e02332774fbdf3381b1d5d5f5d Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 24 Aug 2021 16:43:55 +0200 Subject: PCI: VMD: ACPI: Make ACPI companion lookup work for VMD bus On some systems, in order to get to the deepest low-power state of the platform (which may be necessary to save significant enough amounts of energy while suspended to idle. for example), devices on the PCI bus exposed by the VMD driver need to be power-managed via ACPI. However, the layout of the ACPI namespace below the VMD controller device object does not reflect the layout of the PCI bus under the VMD host bridge, so in order to identify the ACPI companion objects for the devices on that bus, it is necessary to use a special _ADR encoding on the ACPI side. In other words, acpi_pci_find_companion() does not work for these devices, so it needs to be amended with a special lookup logic specific to the VMD bus. Address this issue by allowing the VMD driver to temporarily install an ACPI companion lookup hook containing the code matching the devices on the VMD PCI bus with the corresponding objects in the ACPI namespace. Signed-off-by: Rafael J. Wysocki Acked-by: Jon Derrick --- drivers/pci/controller/vmd.c | 55 ++++++++++++++++++++++++++++++++ drivers/pci/host-bridge.c | 1 + drivers/pci/pci-acpi.c | 74 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/pci-acpi.h | 3 ++ 4 files changed, 133 insertions(+) diff --git a/drivers/pci/controller/vmd.c b/drivers/pci/controller/vmd.c index e3fcdfec58b3..a5987e52700e 100644 --- a/drivers/pci/controller/vmd.c +++ b/drivers/pci/controller/vmd.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -447,6 +448,56 @@ static struct pci_ops vmd_ops = { .write = vmd_pci_write, }; +#ifdef CONFIG_ACPI +static struct acpi_device *vmd_acpi_find_companion(struct pci_dev *pci_dev) +{ + struct pci_host_bridge *bridge; + u32 busnr, addr; + + if (pci_dev->bus->ops != &vmd_ops) + return NULL; + + bridge = pci_find_host_bridge(pci_dev->bus); + busnr = pci_dev->bus->number - bridge->bus->number; + /* + * The address computation below is only applicable to relative bus + * numbers below 32. + */ + if (busnr > 31) + return NULL; + + addr = (busnr << 24) | ((u32)pci_dev->devfn << 16) | 0x8000FFFFU; + + dev_dbg(&pci_dev->dev, "Looking for ACPI companion (address 0x%x)\n", + addr); + + return acpi_find_child_device(ACPI_COMPANION(bridge->dev.parent), addr, + false); +} + +static bool hook_installed; + +static void vmd_acpi_begin(void) +{ + if (pci_acpi_set_companion_lookup_hook(vmd_acpi_find_companion)) + return; + + hook_installed = true; +} + +static void vmd_acpi_end(void) +{ + if (!hook_installed) + return; + + pci_acpi_clear_companion_lookup_hook(); + hook_installed = false; +} +#else +static inline void vmd_acpi_begin(void) { } +static inline void vmd_acpi_end(void) { } +#endif /* CONFIG_ACPI */ + static void vmd_attach_resources(struct vmd_dev *vmd) { vmd->dev->resource[VMD_MEMBAR1].child = &vmd->resources[1]; @@ -747,6 +798,8 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features) if (vmd->irq_domain) dev_set_msi_domain(&vmd->bus->dev, vmd->irq_domain); + vmd_acpi_begin(); + pci_scan_child_bus(vmd->bus); pci_assign_unassigned_bus_resources(vmd->bus); @@ -760,6 +813,8 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features) pci_bus_add_devices(vmd->bus); + vmd_acpi_end(); + WARN(sysfs_create_link(&vmd->dev->dev.kobj, &vmd->bus->dev.kobj, "domain"), "Can't create symlink to domain\n"); return 0; diff --git a/drivers/pci/host-bridge.c b/drivers/pci/host-bridge.c index e01d53f5b32f..afa50b446567 100644 --- a/drivers/pci/host-bridge.c +++ b/drivers/pci/host-bridge.c @@ -23,6 +23,7 @@ struct pci_host_bridge *pci_find_host_bridge(struct pci_bus *bus) return to_pci_host_bridge(root_bus->bridge); } +EXPORT_SYMBOL_GPL(pci_find_host_bridge); struct device *pci_get_host_bridge_device(struct pci_dev *dev) { diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 36bc23e21759..825988a5c074 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "pci.h" /* @@ -1159,6 +1160,69 @@ void acpi_pci_remove_bus(struct pci_bus *bus) } /* ACPI bus type */ + + +static DECLARE_RWSEM(pci_acpi_companion_lookup_sem); +static struct acpi_device *(*pci_acpi_find_companion_hook)(struct pci_dev *); + +/** + * pci_acpi_set_companion_lookup_hook - Set ACPI companion lookup callback. + * @func: ACPI companion lookup callback pointer or NULL. + * + * Set a special ACPI companion lookup callback for PCI devices whose companion + * objects in the ACPI namespace have _ADR with non-standard bus-device-function + * encodings. + * + * Return 0 on success or a negative error code on failure (in which case no + * changes are made). + * + * The caller is responsible for the appropriate ordering of the invocations of + * this function with respect to the enumeration of the PCI devices needing the + * callback installed by it. + */ +int pci_acpi_set_companion_lookup_hook(struct acpi_device *(*func)(struct pci_dev *)) +{ + int ret; + + if (!func) + return -EINVAL; + + down_write(&pci_acpi_companion_lookup_sem); + + if (pci_acpi_find_companion_hook) { + ret = -EBUSY; + } else { + pci_acpi_find_companion_hook = func; + ret = 0; + } + + up_write(&pci_acpi_companion_lookup_sem); + + return ret; +} +EXPORT_SYMBOL_GPL(pci_acpi_set_companion_lookup_hook); + +/** + * pci_acpi_clear_companion_lookup_hook - Clear ACPI companion lookup callback. + * + * Clear the special ACPI companion lookup callback previously set by + * pci_acpi_set_companion_lookup_hook(). Block until the last running instance + * of the callback returns before clearing it. + * + * The caller is responsible for the appropriate ordering of the invocations of + * this function with respect to the enumeration of the PCI devices needing the + * callback cleared by it. + */ +void pci_acpi_clear_companion_lookup_hook(void) +{ + down_write(&pci_acpi_companion_lookup_sem); + + pci_acpi_find_companion_hook = NULL; + + up_write(&pci_acpi_companion_lookup_sem); +} +EXPORT_SYMBOL_GPL(pci_acpi_clear_companion_lookup_hook); + static struct acpi_device *acpi_pci_find_companion(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); @@ -1166,6 +1230,16 @@ static struct acpi_device *acpi_pci_find_companion(struct device *dev) bool check_children; u64 addr; + down_read(&pci_acpi_companion_lookup_sem); + + adev = pci_acpi_find_companion_hook ? + pci_acpi_find_companion_hook(pci_dev) : NULL; + + up_read(&pci_acpi_companion_lookup_sem); + + if (adev) + return adev; + check_children = pci_is_bridge(pci_dev); /* Please ref to ACPI spec for the syntax of _ADR */ addr = (PCI_SLOT(pci_dev->devfn) << 16) | PCI_FUNC(pci_dev->devfn); diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h index 5ba475ca9078..f16de399d2de 100644 --- a/include/linux/pci-acpi.h +++ b/include/linux/pci-acpi.h @@ -122,6 +122,9 @@ static inline void pci_acpi_add_edr_notifier(struct pci_dev *pdev) { } static inline void pci_acpi_remove_edr_notifier(struct pci_dev *pdev) { } #endif /* CONFIG_PCIE_EDR */ +int pci_acpi_set_companion_lookup_hook(struct acpi_device *(*func)(struct pci_dev *)); +void pci_acpi_clear_companion_lookup_hook(void); + #else /* CONFIG_ACPI */ static inline void acpi_pci_add_bus(struct pci_bus *bus) { } static inline void acpi_pci_remove_bus(struct pci_bus *bus) { } -- cgit v1.2.3 From 17b121ad0c43342bc894632f6710b894849ca372 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Sun, 29 Aug 2021 16:22:43 +0300 Subject: Documentation: ACPI: Align the SSDT overlays file with the code This updates the following: 1) The ASL code to follow latest ACPI requirements, i.e. - static buffer to be defined outside of the method - The _ADR and _HID shouldn't be together for the same device 2) EFI section relies on the additional kernel configuration option, i.e. CONFIG_EFI_CUSTOM_SSDT_OVERLAYS 3) Refer to ACPI machine language as AML (capitalized) 4) Miscellaneous amendments Signed-off-by: Andy Shevchenko Signed-off-by: Rafael J. Wysocki --- Documentation/admin-guide/acpi/ssdt-overlays.rst | 49 ++++++++++++------------ 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/Documentation/admin-guide/acpi/ssdt-overlays.rst b/Documentation/admin-guide/acpi/ssdt-overlays.rst index 5d7e25988085..b5fbf54dca19 100644 --- a/Documentation/admin-guide/acpi/ssdt-overlays.rst +++ b/Documentation/admin-guide/acpi/ssdt-overlays.rst @@ -30,22 +30,21 @@ following ASL code can be used:: { Device (STAC) { - Name (_ADR, Zero) Name (_HID, "BMA222E") + Name (RBUF, ResourceTemplate () + { + I2cSerialBus (0x0018, ControllerInitiated, 0x00061A80, + AddressingMode7Bit, "\\_SB.I2C6", 0x00, + ResourceConsumer, ,) + GpioInt (Edge, ActiveHigh, Exclusive, PullDown, 0x0000, + "\\_SB.GPO2", 0x00, ResourceConsumer, , ) + { // Pin list + 0 + } + }) Method (_CRS, 0, Serialized) { - Name (RBUF, ResourceTemplate () - { - I2cSerialBus (0x0018, ControllerInitiated, 0x00061A80, - AddressingMode7Bit, "\\_SB.I2C6", 0x00, - ResourceConsumer, ,) - GpioInt (Edge, ActiveHigh, Exclusive, PullDown, 0x0000, - "\\_SB.GPO2", 0x00, ResourceConsumer, , ) - { // Pin list - 0 - } - }) Return (RBUF) } } @@ -75,7 +74,7 @@ This option allows loading of user defined SSDTs from initrd and it is useful when the system does not support EFI or when there is not enough EFI storage. It works in a similar way with initrd based ACPI tables override/upgrade: SSDT -aml code must be placed in the first, uncompressed, initrd under the +AML code must be placed in the first, uncompressed, initrd under the "kernel/firmware/acpi" path. Multiple files can be used and this will translate in loading multiple tables. Only SSDT and OEM tables are allowed. See initrd_table_override.txt for more details. @@ -103,12 +102,14 @@ This is the preferred method, when EFI is supported on the platform, because it allows a persistent, OS independent way of storing the user defined SSDTs. There is also work underway to implement EFI support for loading user defined SSDTs and using this method will make it easier to convert to the EFI loading -mechanism when that will arrive. +mechanism when that will arrive. To enable it, the +CONFIG_EFI_CUSTOM_SSDT_OVERLAYS shoyld be chosen to y. -In order to load SSDTs from an EFI variable the efivar_ssdt kernel command line -parameter can be used. The argument for the option is the variable name to -use. If there are multiple variables with the same name but with different -vendor GUIDs, all of them will be loaded. +In order to load SSDTs from an EFI variable the ``"efivar_ssdt=..."`` kernel +command line parameter can be used (the name has a limitation of 16 characters). +The argument for the option is the variable name to use. If there are multiple +variables with the same name but with different vendor GUIDs, all of them will +be loaded. In order to store the AML code in an EFI variable the efivarfs filesystem can be used. It is enabled and mounted by default in /sys/firmware/efi/efivars in all @@ -127,7 +128,7 @@ variable with the content from a given file:: #!/bin/sh -e - while ! [ -z "$1" ]; do + while [ -n "$1" ]; do case "$1" in "-f") filename="$2"; shift;; "-g") guid="$2"; shift;; @@ -167,14 +168,14 @@ variable with the content from a given file:: Loading ACPI SSDTs from configfs ================================ -This option allows loading of user defined SSDTs from userspace via the configfs +This option allows loading of user defined SSDTs from user space via the configfs interface. The CONFIG_ACPI_CONFIGFS option must be select and configfs must be mounted. In the following examples, we assume that configfs has been mounted in -/config. +/sys/kernel/config. -New tables can be loading by creating new directories in /config/acpi/table/ and -writing the SSDT aml code in the aml attribute:: +New tables can be loading by creating new directories in /sys/kernel/config/acpi/table +and writing the SSDT AML code in the aml attribute:: - cd /config/acpi/table + cd /sys/kernel/config/acpi/table mkdir my_ssdt cat ~/ssdt.aml > my_ssdt/aml -- cgit v1.2.3 From fa209644a7124b3f4cf811ced55daef49ae39ac6 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Wed, 1 Sep 2021 09:21:11 -0500 Subject: ACPI: PM: s2idle: Run both AMD and Microsoft methods if both are supported It was reported that on "HP ENVY x360" that power LED does not come back, certain keys like brightness controls do not work, and the fan never spins up, even under load on 5.14 final. In analysis of the SSDT it's clear that the Microsoft UUID doesn't provide functional support, but rather the AMD UUID should be supporting this system. Because this is a gap in the expected logic, we checked back with internal team. The conclusion was that on Windows AMD uPEP *does* run even when Microsoft UUID present, but most OEM systems have adopted value of "0x3" for supported functions and hence nothing runs. Henceforth add support for running both Microsoft and AMD methods. This approach will also allow the same logic on Intel systems if desired at a future time as well by pulling the evaluation of `lps0_dsm_func_mask_microsoft` out of the `if` block for `acpi_s2idle_vendor_amd`. Link: https://gitlab.freedesktop.org/drm/amd/uploads/9fbcd7ec3a385cc6949c9bacf45dc41b/acpi-f.20.bin BugLink: https://gitlab.freedesktop.org/drm/amd/-/issues/1691 Reported-by: Maxwell Beck Signed-off-by: Mario Limonciello [ rjw: Edits of the new comments ] Signed-off-by: Rafael J. Wysocki --- drivers/acpi/x86/s2idle.c | 67 +++++++++++++++++++++++++++-------------------- 1 file changed, 39 insertions(+), 28 deletions(-) diff --git a/drivers/acpi/x86/s2idle.c b/drivers/acpi/x86/s2idle.c index 3a308461246a..bd92b549fd5a 100644 --- a/drivers/acpi/x86/s2idle.c +++ b/drivers/acpi/x86/s2idle.c @@ -449,25 +449,30 @@ int acpi_s2idle_prepare_late(void) if (pm_debug_messages_on) lpi_check_constraints(); - if (lps0_dsm_func_mask_microsoft > 0) { + /* Screen off */ + if (lps0_dsm_func_mask > 0) + acpi_sleep_run_lps0_dsm(acpi_s2idle_vendor_amd() ? + ACPI_LPS0_SCREEN_OFF_AMD : + ACPI_LPS0_SCREEN_OFF, + lps0_dsm_func_mask, lps0_dsm_guid); + + if (lps0_dsm_func_mask_microsoft > 0) acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_OFF, lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft); - acpi_sleep_run_lps0_dsm(ACPI_LPS0_MS_ENTRY, - lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft); + + /* LPS0 entry */ + if (lps0_dsm_func_mask > 0) + acpi_sleep_run_lps0_dsm(acpi_s2idle_vendor_amd() ? + ACPI_LPS0_ENTRY_AMD : + ACPI_LPS0_ENTRY, + lps0_dsm_func_mask, lps0_dsm_guid); + if (lps0_dsm_func_mask_microsoft > 0) { acpi_sleep_run_lps0_dsm(ACPI_LPS0_ENTRY, lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft); - } else if (acpi_s2idle_vendor_amd()) { - acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_OFF_AMD, - lps0_dsm_func_mask, lps0_dsm_guid); - acpi_sleep_run_lps0_dsm(ACPI_LPS0_ENTRY_AMD, - lps0_dsm_func_mask, lps0_dsm_guid); - } else { - acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_OFF, - lps0_dsm_func_mask, lps0_dsm_guid); - acpi_sleep_run_lps0_dsm(ACPI_LPS0_ENTRY, - lps0_dsm_func_mask, lps0_dsm_guid); + /* modern standby entry */ + acpi_sleep_run_lps0_dsm(ACPI_LPS0_MS_ENTRY, + lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft); } - return 0; } @@ -476,24 +481,30 @@ void acpi_s2idle_restore_early(void) if (!lps0_device_handle || sleep_no_lps0) return; - if (lps0_dsm_func_mask_microsoft > 0) { - acpi_sleep_run_lps0_dsm(ACPI_LPS0_EXIT, - lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft); + /* Modern standby exit */ + if (lps0_dsm_func_mask_microsoft > 0) acpi_sleep_run_lps0_dsm(ACPI_LPS0_MS_EXIT, lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft); - acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_ON, - lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft); - } else if (acpi_s2idle_vendor_amd()) { - acpi_sleep_run_lps0_dsm(ACPI_LPS0_EXIT_AMD, - lps0_dsm_func_mask, lps0_dsm_guid); - acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_ON_AMD, - lps0_dsm_func_mask, lps0_dsm_guid); - } else { + + /* LPS0 exit */ + if (lps0_dsm_func_mask > 0) + acpi_sleep_run_lps0_dsm(acpi_s2idle_vendor_amd() ? + ACPI_LPS0_EXIT_AMD : + ACPI_LPS0_EXIT, + lps0_dsm_func_mask, lps0_dsm_guid); + if (lps0_dsm_func_mask_microsoft > 0) acpi_sleep_run_lps0_dsm(ACPI_LPS0_EXIT, - lps0_dsm_func_mask, lps0_dsm_guid); + lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft); + + /* Screen on */ + if (lps0_dsm_func_mask_microsoft > 0) acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_ON, - lps0_dsm_func_mask, lps0_dsm_guid); - } + lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft); + if (lps0_dsm_func_mask > 0) + acpi_sleep_run_lps0_dsm(acpi_s2idle_vendor_amd() ? + ACPI_LPS0_SCREEN_ON_AMD : + ACPI_LPS0_SCREEN_ON, + lps0_dsm_func_mask, lps0_dsm_guid); } static const struct platform_s2idle_ops acpi_s2idle_ops_lps0 = { -- cgit v1.2.3