From 92f1abfa6a5849c167cfc1ea30fef35a81d9fe4e Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Wed, 4 Nov 2020 01:48:42 +0100 Subject: [PATCH] Update v5.9 patches Changes: - SAM: - Remove explicit dGPU power-setting code and rely on (patched) PCI/PM core instead for automatic and proper power management. See [1] and [2] for details. - Minor fixes. - GPE: - Add copyright notice Links: - SAM: https://github.com/linux-surface/surface-aggregator-module/commit/e6338f7b04abdbd677207c71ef13bbc75746e3fd - GPE: https://github.com/linux-surface/surface-gpe/commit/245c2645045ea6e655c07357b2d8062b5f33ae15 - kernel: https://github.com/linux-surface/kernel/commit/062dca1b2621b6086359c7a038bc6e61c95387c3 [1] https://github.com/linux-surface/surface-aggregator-module/commit/4138dabe851372a6af197fa6525d8d5f41367c58 [2] https://github.com/linux-surface/surface-aggregator-module/wiki/Discrete-GPU --- configs/surface-5.9.config | 1 - patches/5.9/0001-surface3-oemb.patch | 4 +- patches/5.9/0002-wifi.patch | 48 +- patches/5.9/0003-ipts.patch | 12 +- patches/5.9/0004-surface-gpe.patch | 14 +- patches/5.9/0005-surface-sam-over-hid.patch | 14 +- patches/5.9/0006-surface-sam.patch | 1649 +++++------------ ...-acpi_walk_dep_device_list-getting-c.patch | 70 - pkg/arch/kernel/PKGBUILD | 14 +- 9 files changed, 496 insertions(+), 1330 deletions(-) delete mode 100644 patches/5.9/0007-i2c-core-Restore-acpi_walk_dep_device_list-getting-c.patch diff --git a/configs/surface-5.9.config b/configs/surface-5.9.config index c49b26db9..285d92703 100644 --- a/configs/surface-5.9.config +++ b/configs/surface-5.9.config @@ -1,7 +1,6 @@ # # Surface Aggregator Module # -CONFIG_GPIO_SYSFS=y # required for SURFACE_HOTPLUG CONFIG_SURFACE_AGGREGATOR=m CONFIG_SURFACE_AGGREGATOR_ERROR_INJECTION=n CONFIG_SURFACE_AGGREGATOR_BUS=y diff --git a/patches/5.9/0001-surface3-oemb.patch b/patches/5.9/0001-surface3-oemb.patch index 65e923ec3..f204646bb 100644 --- a/patches/5.9/0001-surface3-oemb.patch +++ b/patches/5.9/0001-surface3-oemb.patch @@ -1,4 +1,4 @@ -From 98d612006b94231362c3b5f16deb4a8e9a4ea4d0 Mon Sep 17 00:00:00 2001 +From 35b94287ce1e118cecd1636c54e0cd931b66ddf0 Mon Sep 17 00:00:00 2001 From: Tsuchiya Yuto Date: Sun, 18 Oct 2020 16:42:44 +0900 Subject: [PATCH] (surface3-oemb) add DMI matches for Surface 3 with broken DMI @@ -97,5 +97,5 @@ index 2752dc955733..ef36a316e2ed 100644 }; -- -2.28.0 +2.29.2 diff --git a/patches/5.9/0002-wifi.patch b/patches/5.9/0002-wifi.patch index 8161d097d..44de7db9b 100644 --- a/patches/5.9/0002-wifi.patch +++ b/patches/5.9/0002-wifi.patch @@ -1,4 +1,4 @@ -From 1040020890118b08d89401905c6c3c63f0127ccf Mon Sep 17 00:00:00 2001 +From 344f5a695c99e176964c1d8e3250c8913a0863e6 Mon Sep 17 00:00:00 2001 From: Tsuchiya Yuto Date: Thu, 24 Sep 2020 18:02:06 +0900 Subject: [PATCH] mwifiex: pcie: skip cancel_work_sync() on reset failure path @@ -154,9 +154,9 @@ index fc59b522f670..048f4db6027a 100644 static inline int -- -2.28.0 +2.29.2 -From 36c7b429bd54df9e55fbdc653139d4baf16c9953 Mon Sep 17 00:00:00 2001 +From f856acbb4ffc08dadde43b012a0b7de8b57cd7c8 Mon Sep 17 00:00:00 2001 From: Tsuchiya Yuto Date: Mon, 28 Sep 2020 17:46:49 +0900 Subject: [PATCH] mwifiex: pcie: add DMI-based quirk impl for Surface devices @@ -362,9 +362,9 @@ index 000000000000..5326ae7e5671 + +void mwifiex_initialize_quirks(struct pcie_service_card *card); -- -2.28.0 +2.29.2 -From e4cedba33eff73175314de2c93bd8c2ddf01e441 Mon Sep 17 00:00:00 2001 +From 75190ee0ec83aadac31094c92658b3b2fb60d917 Mon Sep 17 00:00:00 2001 From: Tsuchiya Yuto Date: Tue, 29 Sep 2020 17:25:22 +0900 Subject: [PATCH] mwifiex: pcie: add reset_d3cold quirk for Surface gen4+ @@ -563,9 +563,9 @@ index 5326ae7e5671..8b9dcb5070d8 100644 void mwifiex_initialize_quirks(struct pcie_service_card *card); +int mwifiex_pcie_reset_d3cold_quirk(struct pci_dev *pdev); -- -2.28.0 +2.29.2 -From acaf58ea2c66fb0e7141952b54ec2f0eb7d6934e Mon Sep 17 00:00:00 2001 +From 7678156d317fb6fe7c8630d0351d7835e159fa0d Mon Sep 17 00:00:00 2001 From: Tsuchiya Yuto Date: Tue, 29 Sep 2020 17:32:22 +0900 Subject: [PATCH] mwifiex: pcie: add reset_wsid quirk for Surface 3 @@ -742,9 +742,9 @@ index 8b9dcb5070d8..3ef7440418e3 100644 int mwifiex_pcie_reset_d3cold_quirk(struct pci_dev *pdev); +int mwifiex_pcie_reset_wsid_quirk(struct pci_dev *pdev); -- -2.28.0 +2.29.2 -From 4a498b49a7f7141895741fcdd28032b790ff9d35 Mon Sep 17 00:00:00 2001 +From 3ce2a07dcc042765314049b4f2ecc67aa0c20d48 Mon Sep 17 00:00:00 2001 From: Tsuchiya Yuto Date: Wed, 30 Sep 2020 18:08:24 +0900 Subject: [PATCH] mwifiex: pcie: (OEMB) add quirk for Surface 3 with broken DMI @@ -804,9 +804,9 @@ index f0a6fa0a7ae5..34dcd84f02a6 100644 .ident = "Surface Pro 3", .matches = { -- -2.28.0 +2.29.2 -From 28158ef411e579c66eb33cafe1b7af5cd9dbcc89 Mon Sep 17 00:00:00 2001 +From b7fd7192e0791964c8dd4f9082316c2fdc903324 Mon Sep 17 00:00:00 2001 From: Tsuchiya Yuto Date: Thu, 24 Sep 2020 01:56:29 +0900 Subject: [PATCH] mwifiex: fix mwifiex_shutdown_sw() causing sw reset failure @@ -881,9 +881,9 @@ index 9ee5600351a7..5965999f1b9b 100644 adapter->is_up = false; -- -2.28.0 +2.29.2 -From f0a8812d81ab425af896717c2ef339d8f7d0557e Mon Sep 17 00:00:00 2001 +From e810d3b79d5cb2da17313fb818c0d014160566ee Mon Sep 17 00:00:00 2001 From: Tsuchiya Yuto Date: Thu, 24 Sep 2020 01:56:34 +0900 Subject: [PATCH] mwifiex: pcie: use shutdown_sw()/reinit_sw() on @@ -1023,9 +1023,9 @@ index daae572ce94e..b46d56389c3b 100644 return 0; } -- -2.28.0 +2.29.2 -From 3754079592ed651678caaaf85ba6e974bcc5acf1 Mon Sep 17 00:00:00 2001 +From 7b1b2992e5b48ca78e676babefe9ee8742087b2e Mon Sep 17 00:00:00 2001 From: Tsuchiya Yuto Date: Mon, 24 Aug 2020 17:11:35 +0900 Subject: [PATCH] mwifiex: pcie: add enable_device_dump module parameter @@ -1072,9 +1072,9 @@ index b46d56389c3b..1847a0274991 100644 if (!adapter->devdump_data) { mwifiex_dbg(adapter, ERROR, -- -2.28.0 +2.29.2 -From 56e9e15e3c774e324afed5f8e2a9465c36cb0378 Mon Sep 17 00:00:00 2001 +From 2135e0c6f4b0a6f8a38e467855c9950bbb066e36 Mon Sep 17 00:00:00 2001 From: Tsuchiya Yuto Date: Sun, 4 Oct 2020 00:11:49 +0900 Subject: [PATCH] mwifiex: pcie: disable bridge_d3 for Surface gen4+ @@ -1227,9 +1227,9 @@ index 3ef7440418e3..a95ebac06e13 100644 void mwifiex_initialize_quirks(struct pcie_service_card *card); int mwifiex_pcie_reset_d3cold_quirk(struct pci_dev *pdev); -- -2.28.0 +2.29.2 -From 532503b8b7b9d60a57fc5d15cae07ddbf6c95627 Mon Sep 17 00:00:00 2001 +From 130cdeb74b99af7dcaefe2a7ac8e41d68da82c2c Mon Sep 17 00:00:00 2001 From: Tsuchiya Yuto Date: Sun, 4 Oct 2020 00:25:48 +0900 Subject: [PATCH] mwifiex: add allow_ps_mode module parameter @@ -1289,9 +1289,9 @@ index 96848fa0e417..786f7a197613 100644 } -- -2.28.0 +2.29.2 -From 0972cfcfd39efd4e4ef96db7843adb1742b1da7d Mon Sep 17 00:00:00 2001 +From 49bb5edf570046c429f7b4c55f1225e840948f1d Mon Sep 17 00:00:00 2001 From: Tsuchiya Yuto Date: Sun, 4 Oct 2020 00:38:48 +0900 Subject: [PATCH] mwifiex: print message when changing ps_mode @@ -1324,9 +1324,9 @@ index 786f7a197613..8f4b8bc5ff03 100644 } -- -2.28.0 +2.29.2 -From e22985a52f59503c3e7e85e71dae28f19d333beb Mon Sep 17 00:00:00 2001 +From ada5d2a04c623004cbbb6a01d02476dd2ecff07a Mon Sep 17 00:00:00 2001 From: Tsuchiya Yuto Date: Sun, 4 Oct 2020 00:59:37 +0900 Subject: [PATCH] mwifiex: disable ps_mode explicitly by default instead @@ -1372,5 +1372,5 @@ index d3a968ef21ef..9b7b52fbc9c4 100644 if (drcs) { -- -2.28.0 +2.29.2 diff --git a/patches/5.9/0003-ipts.patch b/patches/5.9/0003-ipts.patch index 6427f1749..ed2364929 100644 --- a/patches/5.9/0003-ipts.patch +++ b/patches/5.9/0003-ipts.patch @@ -1,4 +1,4 @@ -From 4d69a6adcc5ddac18b1db15c3ac08448f5e9bd39 Mon Sep 17 00:00:00 2001 +From 8b515545bffc2eae3dda29600ad2bc9c53216c9b Mon Sep 17 00:00:00 2001 From: Dorian Stoll Date: Fri, 25 Sep 2020 18:06:05 +0200 Subject: [PATCH] mei: Remove client devices before shutting down @@ -25,9 +25,9 @@ index bcee77768b91..21ed765003e1 100644 mei_cancel_work(dev); -- -2.28.0 +2.29.2 -From 2a15f76fdbeeb3f3b1ac1a7ae4a9591d017c5f53 Mon Sep 17 00:00:00 2001 +From 358acad2d442d408e4ffb8dad4919dd2d39a63ff Mon Sep 17 00:00:00 2001 From: Dorian Stoll Date: Thu, 30 Jul 2020 13:21:53 +0200 Subject: [PATCH] misc: mei: Add missing IPTS device IDs @@ -63,9 +63,9 @@ index 1de9ef7a272b..e12484840f88 100644 {MEI_PCI_DEVICE(MEI_DEV_ID_TGP_LP, MEI_ME_PCH15_CFG)}, {MEI_PCI_DEVICE(MEI_DEV_ID_TGP_H, MEI_ME_PCH15_SPS_CFG)}, -- -2.28.0 +2.29.2 -From 0dfdb2c47e57d7e1511e4b6085424ba8f7e7565e Mon Sep 17 00:00:00 2001 +From 5c682c2a3ff988c14e7905935bbca3a7241920d9 Mon Sep 17 00:00:00 2001 From: Dorian Stoll Date: Thu, 6 Aug 2020 11:20:41 +0200 Subject: [PATCH] misc: Add support for Intel Precise Touch & Stylus @@ -1412,5 +1412,5 @@ index 000000000000..4c667bb6a7f2 +#endif /* _IPTS_UAPI_H_ */ + -- -2.28.0 +2.29.2 diff --git a/patches/5.9/0004-surface-gpe.patch b/patches/5.9/0004-surface-gpe.patch index f9655db4d..2a8274924 100644 --- a/patches/5.9/0004-surface-gpe.patch +++ b/patches/5.9/0004-surface-gpe.patch @@ -1,4 +1,4 @@ -From 1e39f55f2a8c3c816fac769e915a898d3f7ed87a Mon Sep 17 00:00:00 2001 +From 4ebc750fed6330a867efa48c85918d2996f3d27a Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sun, 16 Aug 2020 23:39:56 +0200 Subject: [PATCH] platform/x86: Add Driver to set up lid GPEs on MS Surface @@ -33,8 +33,8 @@ Patchset: surface-gpe --- drivers/platform/x86/Kconfig | 9 + drivers/platform/x86/Makefile | 1 + - drivers/platform/x86/surface_gpe.c | 307 +++++++++++++++++++++++++++++ - 3 files changed, 317 insertions(+) + drivers/platform/x86/surface_gpe.c | 309 +++++++++++++++++++++++++++++ + 3 files changed, 319 insertions(+) create mode 100644 drivers/platform/x86/surface_gpe.c diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig @@ -71,15 +71,17 @@ index 5f823f7eff45..c0d1c753eb3c 100644 obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o diff --git a/drivers/platform/x86/surface_gpe.c b/drivers/platform/x86/surface_gpe.c new file mode 100644 -index 000000000000..2857e3862ca4 +index 000000000000..88b952ee9b8b --- /dev/null +++ b/drivers/platform/x86/surface_gpe.c -@@ -0,0 +1,307 @@ +@@ -0,0 +1,309 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Surface GPE/Lid driver to enable wakeup from suspend via the lid by + * properly configuring the respective GPEs. Required for wakeup via lid on + * newer Intel-based Microsoft Surface devices. ++ * ++ * Copyright (C) 2020 Maximilian Luz + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -383,5 +385,5 @@ index 000000000000..2857e3862ca4 +MODULE_LICENSE("GPL"); +MODULE_ALIAS("dmi:*:svnMicrosoftCorporation:pnSurface*:*"); -- -2.28.0 +2.29.2 diff --git a/patches/5.9/0005-surface-sam-over-hid.patch b/patches/5.9/0005-surface-sam-over-hid.patch index 727264627..579cd9935 100644 --- a/patches/5.9/0005-surface-sam-over-hid.patch +++ b/patches/5.9/0005-surface-sam-over-hid.patch @@ -1,4 +1,4 @@ -From 824f294b006f50b558734a7d0240e132dd613ca9 Mon Sep 17 00:00:00 2001 +From e127b51248880adf78caa4b2e186aa1f87cbab34 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sat, 25 Jul 2020 17:19:53 +0200 Subject: [PATCH] i2c: acpi: Implement RawBytes read access @@ -54,10 +54,10 @@ Patchset: surface-sam-over-hid 1 file changed, 35 insertions(+) diff --git a/drivers/i2c/i2c-core-acpi.c b/drivers/i2c/i2c-core-acpi.c -index e627d7b2790f..8820131da748 100644 +index 37c510d9347a..aed579942436 100644 --- a/drivers/i2c/i2c-core-acpi.c +++ b/drivers/i2c/i2c-core-acpi.c -@@ -564,6 +564,28 @@ static int acpi_gsb_i2c_write_bytes(struct i2c_client *client, +@@ -574,6 +574,28 @@ static int acpi_gsb_i2c_write_bytes(struct i2c_client *client, return (ret == 1) ? 0 : -EIO; } @@ -86,7 +86,7 @@ index e627d7b2790f..8820131da748 100644 static acpi_status i2c_acpi_space_handler(u32 function, acpi_physical_address command, u32 bits, u64 *value64, -@@ -665,6 +687,19 @@ i2c_acpi_space_handler(u32 function, acpi_physical_address command, +@@ -675,6 +697,19 @@ i2c_acpi_space_handler(u32 function, acpi_physical_address command, } break; @@ -107,9 +107,9 @@ index e627d7b2790f..8820131da748 100644 dev_warn(&adapter->dev, "protocol 0x%02x not supported for client 0x%02x\n", accessor_type, client->addr); -- -2.28.0 +2.29.2 -From e47b3deefb0fb2c24b65d7b9271c15c0abf4491e Mon Sep 17 00:00:00 2001 +From 3d13ed4cc56176579d0f24f6d70dacf972284456 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sun, 6 Sep 2020 04:01:19 +0200 Subject: [PATCH] platform/x86: Add driver for Surface Book 1 dGPU switch @@ -330,5 +330,5 @@ index 000000000000..8c66ed5110fd +MODULE_DESCRIPTION("Discrete GPU Power-Switch for Surface Book 1"); +MODULE_LICENSE("GPL"); -- -2.28.0 +2.29.2 diff --git a/patches/5.9/0006-surface-sam.patch b/patches/5.9/0006-surface-sam.patch index 11168837e..334c569f5 100644 --- a/patches/5.9/0006-surface-sam.patch +++ b/patches/5.9/0006-surface-sam.patch @@ -1,4 +1,245 @@ -From 6841d3c1ca0d911bf0e01d9d818b240b95f44176 Mon Sep 17 00:00:00 2001 +From 3846ff4b9f01b75acc4531341b2b592b10f5c40b Mon Sep 17 00:00:00 2001 +From: Maximilian Luz +Date: Thu, 29 Oct 2020 22:04:38 +0100 +Subject: [PATCH] PCI: Allow D3cold for hot-plug ports on Surface Books + +The Microsoft Surface Book series of devices have a tablet part (so +called clipboard) that can be detached from the base of the device. +While the clipboard contains the CPU, the base can contain a discrete +GPU (dGPU). This dGPU is connected via a PCIe hot-plug port. + +Currently D3cold is disallowed for all hot-plug ports. On the Surface +Book 2 and 3, this leads to increased power consumption during suspend +and when the dGPU is not used (i.e. runtime suspended). This can be +observed not only in battery drain, but also by the dGPU getting notably +warm while suspended and not in D3cold. + +Testing shows that the Surface Books behave well with D3cold enabled for +hot-plug ports, alleviating the aforementioned issues. Thus white-list +D3cold for hot-plug ports on those devices. + +Note: PCIe hot-plug signalling while the device is in D3cold is handled +via ACPI, out-of-band interrupts, and the surface_hotplug driver +(combined). The device will work without the surface_hotplug driver, +however, device removal/addition will only be detected on device resume. + +Signed-off-by: Maximilian Luz +Patchset: surface-sam +--- + drivers/pci/pci.c | 31 +++++++++++++++++++++++++++++-- + 1 file changed, 29 insertions(+), 2 deletions(-) + +diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c +index e39c5499770f..31d8811ee5c5 100644 +--- a/drivers/pci/pci.c ++++ b/drivers/pci/pci.c +@@ -2814,6 +2814,32 @@ static const struct dmi_system_id bridge_d3_blacklist[] = { + { } + }; + ++static const struct dmi_system_id bridge_d3_hotplug_whitelist[] = { ++#ifdef CONFIG_X86 ++ { ++ /* ++ * Microsoft Surface Books have a hot-plug root port for the ++ * discrete GPU (the device containing it can be detached form ++ * the top-part, containing the cpu). ++ * ++ * If this discrete GPU is not transitioned into D3cold for ++ * suspend, the device will become notably warm and also ++ * consume a lot more power than desirable. ++ * ++ * We assume that since those devices have been confirmed ++ * working with D3, future Surface devices will too. So let's ++ * keep this match generic. ++ */ ++ .ident = "Microsoft Surface", ++ .matches = { ++ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "Surface"), ++ }, ++ }, ++#endif ++ { } ++}; ++ + /** + * pci_bridge_d3_possible - Is it possible to put the bridge into D3 + * @bridge: Bridge to check +@@ -2854,10 +2880,11 @@ bool pci_bridge_d3_possible(struct pci_dev *bridge) + /* + * Hotplug ports handled natively by the OS were not validated + * by vendors for runtime D3 at least until 2018 because there +- * was no OS support. ++ * was no OS support. Explicitly whitelist systems that have ++ * been confirmed working. + */ + if (bridge->is_hotplug_bridge) +- return false; ++ return dmi_check_system(bridge_d3_hotplug_whitelist); + + if (dmi_check_system(bridge_d3_blacklist)) + return false; +-- +2.29.2 + +From f79f89f6c76cb6f50f454d833aad7314f39b744c Mon Sep 17 00:00:00 2001 +From: Maximilian Luz +Date: Sat, 31 Oct 2020 19:48:06 +0100 +Subject: [PATCH] PCI: Update platform power state when updating PCI state + +On some devices and platforms, the initial platform power state is not +in sync with the power state of the PCI device. + +Specifically, on the Surface Book 2 and 3, some ACPI power regions that +should be "on" for the D0 state (and others) are initialized as "off" in +ACPI, whereas the PCI device is in D0. As the state is updated in +pci_enable_device_flags() without ensuring that the platform state is +also updated, the power resource will never be properly turned on. +Instead, it lives in a sort of on-but-marked-as-off zombie-state, which +confuses things down the line when attempting to transition the device +into D3cold: As the resource is already marked as off, it won't be +turned off and the device does not fully enter D3cold, causing increased +power consumption during (runtime-)suspend. + +Ensuring that the platform power state is in sync with the PCI power +state when updating the latter guarantees that all required ACPI power +regions are powered on/off in accordance with the requirements +(specified in the ACPI _PRn fields) for the current PCI power state. + +Signed-off-by: Maximilian Luz +Patchset: surface-sam +--- + drivers/pci/pci.c | 30 ++++++++++++++++++++++++++++-- + 1 file changed, 28 insertions(+), 2 deletions(-) + +diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c +index 31d8811ee5c5..8efdd71f66c4 100644 +--- a/drivers/pci/pci.c ++++ b/drivers/pci/pci.c +@@ -1858,7 +1858,7 @@ static void pci_enable_bridge(struct pci_dev *dev) + static int pci_enable_device_flags(struct pci_dev *dev, unsigned long flags) + { + struct pci_dev *bridge; +- int err; ++ int err = 0; + int i, bars = 0; + + /* +@@ -1868,9 +1868,35 @@ static int pci_enable_device_flags(struct pci_dev *dev, unsigned long flags) + * (e.g. if the device really is in D0 at enable time). + */ + if (dev->pm_cap) { ++ pci_power_t current_state; + u16 pmcsr; ++ + pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr); +- dev->current_state = (pmcsr & PCI_PM_CTRL_STATE_MASK); ++ current_state = (pmcsr & PCI_PM_CTRL_STATE_MASK); ++ ++ /* ++ * On some platforms, the initial power state may not be in ++ * sync with the PCI power state. Specifically, on ACPI based ++ * platforms, power-resources for the current state may not ++ * have been properly enabled (or power-resources not required ++ * for the current state disabled) yet. Thus, ensure that the ++ * platform power state reflects the PCI state. ++ * ++ * Update platform state before actually setting current state ++ * so that it can still be accessed in platform code, if ++ * necessary. ++ */ ++ if (platform_pci_power_manageable(dev)) ++ err = platform_pci_set_power_state(dev, current_state); ++ ++ // always update current state ++ dev->current_state = current_state; ++ ++ if (err) { ++ pci_err(dev, "failed to update platform power state: %d\n", ++ err); ++ return err; ++ } + } + + if (atomic_inc_return(&dev->enable_cnt) > 1) +-- +2.29.2 + +From 959462f7242189d74e93803cf62547779bbd931b Mon Sep 17 00:00:00 2001 +From: Maximilian Luz +Date: Sat, 31 Oct 2020 20:46:33 +0100 +Subject: [PATCH] PCI: Add sysfs attribute for PCI device power state + +While most PCI power-states can be queried from user-space via lspci, +this has some limits. Specifically, lspci fails to provide an accurate +value when the device is in D3cold as it has to resume the device before +it can access its power state via the configuration space, leading to it +reporting D0 or another on-state. Thus lspci can, for example, not be +used to diagnose power-consumption issues for devices that can enter +D3cold or to ensure that devices properly enter D3cold at all. + +To alleviate this issue, introduce a new sysfs device attribute for the +PCI power state, showing the current power state as seen by the kernel. + +Signed-off-by: Maximilian Luz +Patchset: surface-sam +--- + Documentation/ABI/testing/sysfs-bus-pci | 9 +++++++++ + drivers/pci/pci-sysfs.c | 12 ++++++++++++ + 2 files changed, 21 insertions(+) + +diff --git a/Documentation/ABI/testing/sysfs-bus-pci b/Documentation/ABI/testing/sysfs-bus-pci +index 450296cc7948..881040af2611 100644 +--- a/Documentation/ABI/testing/sysfs-bus-pci ++++ b/Documentation/ABI/testing/sysfs-bus-pci +@@ -360,3 +360,12 @@ Contact: Heiner Kallweit + Description: If ASPM is supported for an endpoint, these files can be + used to disable or enable the individual power management + states. Write y/1/on to enable, n/0/off to disable. ++ ++What: /sys/bus/pci/devices/.../power_state ++Date: November 2020 ++Contact: Linux PCI developers ++Description: ++ This file contains the current PCI power state of the device. ++ The value comes from the PCI kernel device state and can be one ++ of: "unknown", "error", "D0", D1", "D2", "D3hot", "D3cold". ++ The file is read only. +diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c +index 6d78df981d41..17f186ce8e87 100644 +--- a/drivers/pci/pci-sysfs.c ++++ b/drivers/pci/pci-sysfs.c +@@ -124,6 +124,17 @@ static ssize_t cpulistaffinity_show(struct device *dev, + } + static DEVICE_ATTR_RO(cpulistaffinity); + ++/* PCI power state */ ++static ssize_t power_state_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct pci_dev *pci_dev = to_pci_dev(dev); ++ pci_power_t state = READ_ONCE(pci_dev->current_state); ++ ++ return sprintf(buf, "%s\n", pci_power_name(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 +592,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, +-- +2.29.2 + +From 7772a3785ade124014c17a0bc05970f9341dc730 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Mon, 17 Aug 2020 01:23:20 +0200 Subject: [PATCH] misc: surface_sam: Add file2alias support for Surface SAM @@ -98,9 +339,9 @@ index 2417dd1dee33..a6c583362b92 100644 /* Create MODULE_ALIAS() statements. -- -2.28.0 +2.29.2 -From ca97ca61e1b003b34e4f713397c7e8f3d878cf0b Mon Sep 17 00:00:00 2001 +From 7ffe090d82dbe0c8cf1a7cc1f4e5f63e94a1214a Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Mon, 17 Aug 2020 01:44:30 +0200 Subject: [PATCH] misc: Add support for Surface System Aggregator Module @@ -139,11 +380,11 @@ Patchset: surface-sam .../misc/surface_aggregator/clients/Makefile | 11 + .../clients/surface_acpi_notify.c | 884 ++++++ .../clients/surface_aggregator_cdev.c | 299 ++ - .../clients/surface_aggregator_registry.c | 652 +++++ - .../clients/surface_battery.c | 1196 ++++++++ - .../surface_aggregator/clients/surface_dtx.c | 1270 ++++++++ + .../clients/surface_aggregator_registry.c | 656 +++++ + .../clients/surface_battery.c | 1195 ++++++++ + .../surface_aggregator/clients/surface_dtx.c | 1277 ++++++++ .../surface_aggregator/clients/surface_hid.c | 925 ++++++ - .../clients/surface_hotplug.c | 1285 +++++++++ + .../clients/surface_hotplug.c | 269 ++ .../clients/surface_perfmode.c | 122 + drivers/misc/surface_aggregator/controller.c | 2555 +++++++++++++++++ drivers/misc/surface_aggregator/controller.h | 288 ++ @@ -165,7 +406,7 @@ Patchset: surface-sam include/uapi/linux/surface_aggregator/dtx.h | 150 + scripts/mod/devicetable-offsets.c | 3 +- scripts/mod/file2alias.c | 10 +- - 48 files changed, 19814 insertions(+), 7 deletions(-) + 48 files changed, 18808 insertions(+), 7 deletions(-) create mode 100644 Documentation/driver-api/surface_aggregator/client-api.rst create mode 100644 Documentation/driver-api/surface_aggregator/client.rst create mode 100644 Documentation/driver-api/surface_aggregator/clients/cdev.rst @@ -4090,10 +4331,10 @@ index 000000000000..f5e81cd67357 +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/surface_aggregator/clients/surface_aggregator_registry.c b/drivers/misc/surface_aggregator/clients/surface_aggregator_registry.c new file mode 100644 -index 000000000000..7499c6d107cb +index 000000000000..569d1b96bb1a --- /dev/null +++ b/drivers/misc/surface_aggregator/clients/surface_aggregator_registry.c -@@ -0,0 +1,652 @@ +@@ -0,0 +1,656 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Surface System Aggregator Module (SSAM) client device registry. @@ -4520,7 +4761,7 @@ index 000000000000..7499c6d107cb + return 0; +} + -+static int ssam_base_hub_resume(struct device *dev) ++static int __maybe_unused ssam_base_hub_resume(struct device *dev) +{ + struct ssam_device *sdev = to_ssam_device(dev); + enum ssam_base_hub_state state; @@ -4544,6 +4785,8 @@ index 000000000000..7499c6d107cb + if (!hub) + return -ENOMEM; + ++ mutex_init(&hub->lock); ++ + hub->sdev = sdev; + hub->state = SSAM_BASE_HUB_UNINITIALIZED; + @@ -4590,6 +4833,8 @@ index 000000000000..7499c6d107cb + + ssam_notifier_unregister(sdev->ctrl, &hub->notif); + ssam_hub_remove_devices(&sdev->dev); ++ ++ mutex_destroy(&hub->lock); +} + +static const struct ssam_device_id ssam_base_hub_match[] = { @@ -4748,10 +4993,10 @@ index 000000000000..7499c6d107cb +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/surface_aggregator/clients/surface_battery.c b/drivers/misc/surface_aggregator/clients/surface_battery.c new file mode 100644 -index 000000000000..21ee212a945a +index 000000000000..e1e0804eeb54 --- /dev/null +++ b/drivers/misc/surface_aggregator/clients/surface_battery.c -@@ -0,0 +1,1196 @@ +@@ -0,0 +1,1195 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Surface battery and AC device driver. @@ -5771,7 +6016,6 @@ index 000000000000..21ee212a945a + cancel_delayed_work_sync(&bat->update_work); + device_remove_file(&bat->psy->dev, &alarm_attr); + power_supply_unregister(bat->psy); -+ mutex_destroy(&bat->lock); +} + + @@ -5950,10 +6194,10 @@ index 000000000000..21ee212a945a +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/surface_aggregator/clients/surface_dtx.c b/drivers/misc/surface_aggregator/clients/surface_dtx.c new file mode 100644 -index 000000000000..1ac1208edd13 +index 000000000000..588931ddcb48 --- /dev/null +++ b/drivers/misc/surface_aggregator/clients/surface_dtx.c -@@ -0,0 +1,1270 @@ +@@ -0,0 +1,1277 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Surface Book (gen. 2 and later) detachment system (DTX) driver. @@ -6143,7 +6387,10 @@ index 000000000000..1ac1208edd13 + +static void __sdtx_device_release(struct kref *kref) +{ -+ kfree(container_of(kref, struct sdtx_device, kref)); ++ struct sdtx_device *ddev = container_of(kref, struct sdtx_device, kref); ++ ++ mutex_destroy(&ddev->write_lock); ++ kfree(ddev); +} + +static struct sdtx_device *sdtx_device_get(struct sdtx_device *ddev) @@ -6395,6 +6642,7 @@ index 000000000000..1ac1208edd13 + up_write(&client->ddev->client_lock); + + sdtx_device_put(client->ddev); ++ mutex_destroy(&client->read_lock); + kfree(client); + + return 0; @@ -7013,6 +7261,9 @@ index 000000000000..1ac1208edd13 + // stop mode_work, prevent access to mode_switch + cancel_delayed_work_sync(&ddev->mode_work); + ++ // stop state_work ++ cancel_delayed_work_sync(&ddev->state_work); ++ + // with mode_work canceled, we can unregister the mode_switch + input_unregister_device(ddev->mode_switch); + @@ -7226,7 +7477,7 @@ index 000000000000..1ac1208edd13 +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/surface_aggregator/clients/surface_hid.c b/drivers/misc/surface_aggregator/clients/surface_hid.c new file mode 100644 -index 000000000000..567da224e60e +index 000000000000..60691800e7e5 --- /dev/null +++ b/drivers/misc/surface_aggregator/clients/surface_hid.c @@ -0,0 +1,925 @@ @@ -7912,7 +8163,7 @@ index 000000000000..567da224e60e + +/* -- PM ops. --------------------------------------------------------------- */ + -+#ifdef CONFIG_PM ++#ifdef CONFIG_PM_SLEEP + +static int surface_hid_suspend(struct device *dev) +{ @@ -7973,11 +8224,11 @@ index 000000000000..567da224e60e + .restore = surface_hid_restore, +}; + -+#else /* CONFIG_PM */ ++#else /* CONFIG_PM_SLEEP */ + +const struct dev_pm_ops surface_hid_pm_ops = { }; + -+#endif /* CONFIG_PM */ ++#endif /* CONFIG_PM_SLEEP */ + + +/* -- Driver setup (HID). --------------------------------------------------- */ @@ -8157,1294 +8408,278 @@ index 000000000000..567da224e60e +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/surface_aggregator/clients/surface_hotplug.c b/drivers/misc/surface_aggregator/clients/surface_hotplug.c new file mode 100644 -index 000000000000..f18cc17d019d +index 000000000000..9afddfc6a358 --- /dev/null +++ b/drivers/misc/surface_aggregator/clients/surface_hotplug.c -@@ -0,0 +1,1285 @@ +@@ -0,0 +1,269 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* -+ * Surface Book (gen. 2 and later) discrete GPU (dGPU) hot-plug system driver. ++ * Surface Book (gen. 2 and later) hot-plug driver. + * -+ * Supports explicit setting of the dGPU power-state on the Surface Books via -+ * a user-space interface. Properly handles dGPU hot-plugging by detaching the -+ * base of the device. ++ * Surface Book devices (can) have a hot-pluggable discrete GPU (dGPU). This ++ * driver is responsible for out-of-band hot-plug event signalling on these ++ * devices. It is specifically required when the hot-plug device is in D3cold ++ * and can thus not generate PCIe hot-plug events itself. ++ * ++ * Event signalling is handled via ACPI, which will generate the appropriate ++ * device-check notifications to be picked up by the PCIe hot-plug driver. + * + * Copyright (C) 2019-2020 Maximilian Luz + */ + +#include -+#include +#include ++#include +#include +#include +#include -+#include +#include -+#include + -+#include -+#include -+ -+ -+// TODO: vgaswitcheroo integration -+ -+ -+static void dbg_dump_drvsta(struct platform_device *pdev, const char *prefix); -+ -+ -+#define SHPS_DSM_REVISION 1 -+#define SHPS_DSM_GPU_ADDRS 0x02 -+#define SHPS_DSM_GPU_POWER 0x05 -+static const guid_t SHPS_DSM_UUID = -+ GUID_INIT(0x5515a847, 0xed55, 0x4b27, 0x83, 0x52, 0xcd, -+ 0x32, 0x0e, 0x10, 0x36, 0x0a); -+ -+ -+#define SAM_DGPU_TC 0x13 -+#define SAM_DGPU_CID_POWERON 0x02 -+#define ACPI_SGCP_NOTIFY_POWER_ON 0x81 -+ -+#define SHPS_DSM_GPU_ADDRS_RP "RP5_PCIE" -+#define SHPS_DSM_GPU_ADDRS_DGPU "DGPU_PCIE" -+#define SHPS_PCI_GPU_ADDR_RP "\\_SB.PCI0.RP13._ADR" -+ -+static const struct acpi_gpio_params gpio_base_presence_int = { 0, 0, false }; -+static const struct acpi_gpio_params gpio_base_presence = { 1, 0, false }; -+static const struct acpi_gpio_params gpio_dgpu_power_int = { 2, 0, false }; -+static const struct acpi_gpio_params gpio_dgpu_power = { 3, 0, false }; -+static const struct acpi_gpio_params gpio_dgpu_presence_int = { 4, 0, false }; -+static const struct acpi_gpio_params gpio_dgpu_presence = { 5, 0, false }; ++static const struct acpi_gpio_params shps_base_presence_int = { 0, 0, false }; ++static const struct acpi_gpio_params shps_base_presence = { 1, 0, false }; ++static const struct acpi_gpio_params shps_device_power_int = { 2, 0, false }; ++static const struct acpi_gpio_params shps_device_power = { 3, 0, false }; ++static const struct acpi_gpio_params shps_device_presence_int = { 4, 0, false }; ++static const struct acpi_gpio_params shps_device_presence = { 5, 0, false }; + +static const struct acpi_gpio_mapping shps_acpi_gpios[] = { -+ { "base_presence-int-gpio", &gpio_base_presence_int, 1 }, -+ { "base_presence-gpio", &gpio_base_presence, 1 }, -+ { "dgpu_power-int-gpio", &gpio_dgpu_power_int, 1 }, -+ { "dgpu_power-gpio", &gpio_dgpu_power, 1 }, -+ { "dgpu_presence-int-gpio", &gpio_dgpu_presence_int, 1 }, -+ { "dgpu_presence-gpio", &gpio_dgpu_presence, 1 }, ++ { "base_presence-int-gpio", &shps_base_presence_int, 1 }, ++ { "base_presence-gpio", &shps_base_presence, 1 }, ++ { "device_power-int-gpio", &shps_device_power_int, 1 }, ++ { "device_power-gpio", &shps_device_power, 1 }, ++ { "device_presence-int-gpio", &shps_device_presence_int, 1 }, ++ { "device_presence-gpio", &shps_device_presence, 1 }, + { }, +}; + ++static const guid_t shps_dsm_guid = ++ GUID_INIT(0x5515a847, 0xed55, 0x4b27, 0x83, 0x52, 0xcd, ++ 0x32, 0x0e, 0x10, 0x36, 0x0a); + -+enum shps_dgpu_power { -+ SHPS_DGPU_POWER_OFF = 0, -+ SHPS_DGPU_POWER_ON = 1, -+ SHPS_DGPU_POWER_UNKNOWN = 2, ++#define SHPS_DSM_REVISION 1 ++ ++enum shps_dsm_fn { ++ SHPS_DSM_FN_PCI_NUM_ENTRIES = 0x01, ++ SHPS_DSM_FN_PCI_GET_ENTRIES = 0x02, ++ SHPS_DSM_FN_IRQ_BASE_PRESENCE = 0x03, ++ SHPS_DSM_FN_IRQ_DEVICE_POWER = 0x04, ++ SHPS_DSM_FN_IRQ_DEVICE_PRESENCE = 0x05, +}; + -+static const char *shps_dgpu_power_str(enum shps_dgpu_power power) -+{ -+ if (power == SHPS_DGPU_POWER_OFF) -+ return "off"; -+ else if (power == SHPS_DGPU_POWER_ON) -+ return "on"; -+ else if (power == SHPS_DGPU_POWER_UNKNOWN) -+ return "unknown"; -+ else -+ return ""; -+} -+ -+enum shps_notification_method { -+ SHPS_NOTIFICATION_METHOD_SAN = 1, -+ SHPS_NOTIFICATION_METHOD_SGCP = 2 -+}; -+ -+struct shps_hardware_traits { -+ enum shps_notification_method notification_method; -+ const char *dgpu_rp_pci_address; -+}; -+ -+struct shps_driver_data { -+ struct ssam_controller *ctrl; -+ struct platform_device *pdev; -+ -+ struct mutex lock; -+ struct pci_dev *dgpu_root_port; -+ struct pci_saved_state *dgpu_root_port_state; -+ struct gpio_desc *gpio_dgpu_power; -+ struct gpio_desc *gpio_dgpu_presence; -+ struct gpio_desc *gpio_base_presence; -+ unsigned int irq_dgpu_presence; -+ unsigned int irq_base_presence; -+ unsigned long state; -+ acpi_handle sgpc_handle; -+ struct shps_hardware_traits hardware_traits; -+ -+ struct notifier_block dgpu_nf; -+}; -+ -+struct shps_hardware_probe { -+ const char *hardware_id; -+ int generation; -+ struct shps_hardware_traits *hardware_traits; -+}; -+ -+static struct shps_hardware_traits shps_gen1_hwtraits = { -+ .notification_method = SHPS_NOTIFICATION_METHOD_SAN -+}; -+ -+static struct shps_hardware_traits shps_gen2_hwtraits = { -+ .notification_method = SHPS_NOTIFICATION_METHOD_SGCP, -+ .dgpu_rp_pci_address = SHPS_PCI_GPU_ADDR_RP -+}; -+ -+static const struct shps_hardware_probe shps_hardware_probe_match[] = { -+ /* Surface Book 3 */ -+ { "MSHW0117", 2, &shps_gen2_hwtraits }, -+ -+ /* Surface Book 2 (default, must be last entry) */ -+ { NULL, 1, &shps_gen1_hwtraits } -+}; -+ -+#define SHPS_STATE_BIT_PWRTGT 0 /* desired power state: 1 for on, 0 for off */ -+#define SHPS_STATE_BIT_RPPWRON_SYNC 1 /* synchronous/requested power-up in progress */ -+#define SHPS_STATE_BIT_WAKE_ENABLED 2 /* wakeup via base-presence GPIO enabled */ -+ -+ -+#define SHPS_DGPU_PARAM_PERM 0644 -+ -+enum shps_dgpu_power_mp { -+ SHPS_DGPU_MP_POWER_OFF = SHPS_DGPU_POWER_OFF, -+ SHPS_DGPU_MP_POWER_ON = SHPS_DGPU_POWER_ON, -+ SHPS_DGPU_MP_POWER_ASIS = -1, -+ -+ __SHPS_DGPU_MP_POWER_START = -1, -+ __SHPS_DGPU_MP_POWER_END = 1, -+}; -+ -+static int param_dgpu_power_set(const char *val, const struct kernel_param *kp) -+{ -+ int power = SHPS_DGPU_MP_POWER_OFF; -+ int status; -+ -+ status = kstrtoint(val, 0, &power); -+ if (status) -+ return status; -+ -+ if (power < __SHPS_DGPU_MP_POWER_START || power > __SHPS_DGPU_MP_POWER_END) -+ return -EINVAL; -+ -+ return param_set_int(val, kp); -+} -+ -+static const struct kernel_param_ops param_dgpu_power_ops = { -+ .set = param_dgpu_power_set, -+ .get = param_get_int, -+}; -+ -+static int param_dgpu_power_init = SHPS_DGPU_MP_POWER_OFF; -+static int param_dgpu_power_exit = SHPS_DGPU_MP_POWER_ON; -+static int param_dgpu_power_susp = SHPS_DGPU_MP_POWER_ASIS; -+static bool param_dtx_latch = true; -+ -+module_param_cb(dgpu_power_init, ¶m_dgpu_power_ops, ¶m_dgpu_power_init, SHPS_DGPU_PARAM_PERM); -+module_param_cb(dgpu_power_exit, ¶m_dgpu_power_ops, ¶m_dgpu_power_exit, SHPS_DGPU_PARAM_PERM); -+module_param_cb(dgpu_power_susp, ¶m_dgpu_power_ops, ¶m_dgpu_power_susp, SHPS_DGPU_PARAM_PERM); -+module_param_named(dtx_latch, param_dtx_latch, bool, SHPS_DGPU_PARAM_PERM); -+ -+MODULE_PARM_DESC(dgpu_power_init, "dGPU power state to be set on init (0: off / 1: on / 2: as-is, default: off)"); -+MODULE_PARM_DESC(dgpu_power_exit, "dGPU power state to be set on exit (0: off / 1: on / 2: as-is, default: on)"); -+MODULE_PARM_DESC(dgpu_power_susp, "dGPU power state to be set on exit (0: off / 1: on / 2: as-is, default: as-is)"); -+MODULE_PARM_DESC(dtx_latch, "lock/unlock DTX base latch in accordance to power-state (Y/n)"); -+ -+static SSAM_DEFINE_SYNC_REQUEST_N(ssam_bas_latch_lock, { -+ .target_category = SSAM_SSH_TC_BAS, -+ .target_id = 0x01, -+ .command_id = 0x06, -+ .instance_id = 0x00, -+}); -+ -+static SSAM_DEFINE_SYNC_REQUEST_N(ssam_bas_latch_unlock, { -+ .target_category = SSAM_SSH_TC_BAS, -+ .target_id = 0x01, -+ .command_id = 0x07, -+ .instance_id = 0x00, -+}); -+ -+static int shps_dgpu_dsm_get_pci_addr_from_adr(struct platform_device *pdev, const char *entry) { -+ acpi_handle handle = ACPI_HANDLE(&pdev->dev); -+ acpi_status status; -+ struct acpi_object_list input; -+ union acpi_object input_args[0]; -+ u64 device_addr; -+ u8 bus, dev, fun; -+ -+ input.count = 0; -+ input.pointer = input_args; -+ -+ -+ status = acpi_evaluate_integer(handle, (acpi_string)entry, &input, &device_addr); -+ if (ACPI_FAILURE(status)) -+ return -ENODEV; -+ -+ bus = 0; -+ dev = (device_addr & 0xFF0000) >> 16; -+ fun = device_addr & 0xFF; -+ -+ dev_info(&pdev->dev, "found pci device at bus = %d, dev = %x, fun = %x\n", -+ (u32)bus, (u32)dev, (u32)fun); -+ -+ return bus << 8 | PCI_DEVFN(dev, fun); -+} -+ -+static int shps_dgpu_dsm_get_pci_addr_from_dsm(struct platform_device *pdev, const char *entry) -+{ -+ acpi_handle handle = ACPI_HANDLE(&pdev->dev); -+ union acpi_object *result; -+ union acpi_object *e0; -+ union acpi_object *e1; -+ union acpi_object *e2; -+ u64 device_addr = 0; -+ u8 bus, dev, fun; -+ int i; -+ -+ -+ result = acpi_evaluate_dsm_typed(handle, &SHPS_DSM_UUID, SHPS_DSM_REVISION, -+ SHPS_DSM_GPU_ADDRS, NULL, ACPI_TYPE_PACKAGE); -+ if (!result) -+ return -EFAULT; -+ -+ // three entries per device: name, address, -+ for (i = 0; i + 2 < result->package.count; i += 3) { -+ e0 = &result->package.elements[i]; -+ e1 = &result->package.elements[i + 1]; -+ e2 = &result->package.elements[i + 2]; -+ -+ if (e0->type != ACPI_TYPE_STRING) { -+ ACPI_FREE(result); -+ return -EIO; -+ } -+ -+ if (e1->type != ACPI_TYPE_INTEGER) { -+ ACPI_FREE(result); -+ return -EIO; -+ } -+ -+ if (e2->type != ACPI_TYPE_INTEGER) { -+ ACPI_FREE(result); -+ return -EIO; -+ } -+ -+ if (strncmp(e0->string.pointer, entry, 64) == 0) -+ device_addr = e1->integer.value; -+ } -+ -+ ACPI_FREE(result); -+ if (device_addr == 0) -+ return -ENODEV; -+ -+ -+ // convert address -+ bus = (device_addr & 0x0FF00000) >> 20; -+ dev = (device_addr & 0x000F8000) >> 15; -+ fun = (device_addr & 0x00007000) >> 12; -+ -+ return bus << 8 | PCI_DEVFN(dev, fun); -+} -+ -+static struct pci_dev *shps_dgpu_dsm_get_pci_dev(struct platform_device *pdev) -+{ -+ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); -+ struct pci_dev *dev; -+ int addr; -+ -+ -+ if (drvdata->hardware_traits.dgpu_rp_pci_address) { -+ addr = shps_dgpu_dsm_get_pci_addr_from_adr(pdev, drvdata->hardware_traits.dgpu_rp_pci_address); -+ } else { -+ addr = shps_dgpu_dsm_get_pci_addr_from_dsm(pdev, SHPS_DSM_GPU_ADDRS_RP); -+ } -+ -+ if (addr < 0) -+ return ERR_PTR(addr); -+ -+ dev = pci_get_domain_bus_and_slot(0, (addr & 0xFF00) >> 8, addr & 0xFF); -+ return dev ? dev : ERR_PTR(-ENODEV); -+} -+ -+ -+static int shps_dgpu_dsm_get_power_unlocked(struct platform_device *pdev) -+{ -+ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); -+ struct gpio_desc *gpio = drvdata->gpio_dgpu_power; -+ int status; -+ -+ status = gpiod_get_value_cansleep(gpio); -+ if (status < 0) -+ return status; -+ -+ return status == 0 ? SHPS_DGPU_POWER_OFF : SHPS_DGPU_POWER_ON; -+} -+ -+static int shps_dgpu_dsm_get_power(struct platform_device *pdev) -+{ -+ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); -+ int status; -+ -+ mutex_lock(&drvdata->lock); -+ status = shps_dgpu_dsm_get_power_unlocked(pdev); -+ mutex_unlock(&drvdata->lock); -+ -+ return status; -+} -+ -+static int __shps_dgpu_dsm_set_power_unlocked(struct platform_device *pdev, enum shps_dgpu_power power) ++enum shps_irq_type { ++ /* NOTE: Must be in order of DSM function */ ++ SHPS_IRQ_TYPE_BASE_PRESENCE = 0, ++ SHPS_IRQ_TYPE_DEVICE_POWER = 1, ++ SHPS_IRQ_TYPE_DEVICE_PRESENCE = 2, ++ ++ SHPS_NUM_IRQS, ++}; ++ ++static const char *const shps_gpio_names[] = { ++ [SHPS_IRQ_TYPE_BASE_PRESENCE] = "base_presence", ++ [SHPS_IRQ_TYPE_DEVICE_POWER] = "device_power", ++ [SHPS_IRQ_TYPE_DEVICE_PRESENCE] = "device_presence", ++}; ++ ++struct shps_device { ++ struct mutex lock[SHPS_NUM_IRQS]; ++ struct gpio_desc *gpio[SHPS_NUM_IRQS]; ++ unsigned int irq[SHPS_NUM_IRQS]; ++}; ++ ++#define SHPS_IRQ_NOT_PRESENT ((unsigned int)-1) ++ ++static void shps_dsm_notify_irq(struct platform_device *pdev, ++ enum shps_irq_type type) +{ ++ struct shps_device *sdev = platform_get_drvdata(pdev); + acpi_handle handle = ACPI_HANDLE(&pdev->dev); + union acpi_object *result; + union acpi_object param; ++ int value; + -+ dev_info(&pdev->dev, "setting dGPU direct power to \'%s\'\n", shps_dgpu_power_str(power)); ++ mutex_lock(&sdev->lock[type]); ++ ++ value = gpiod_get_value_cansleep(sdev->gpio[type]); ++ if (value < 0) { ++ mutex_unlock(&sdev->lock[type]); ++ dev_err(&pdev->dev, "failed to get gpio: %d (irq=%d)\n", ++ type, value); ++ return; ++ } ++ ++ dev_dbg(&pdev->dev, "IRQ notification via DSM (irq=%d, value=%d)\n", ++ type, value); + + param.type = ACPI_TYPE_INTEGER; -+ param.integer.value = power == SHPS_DGPU_POWER_ON; ++ param.integer.value = value; + -+ result = acpi_evaluate_dsm_typed(handle, &SHPS_DSM_UUID, SHPS_DSM_REVISION, -+ SHPS_DSM_GPU_POWER, ¶m, ACPI_TYPE_BUFFER); -+ if (!result) -+ return -EFAULT; ++ result = acpi_evaluate_dsm(handle, &shps_dsm_guid, SHPS_DSM_REVISION, ++ SHPS_DSM_FN_IRQ_BASE_PRESENCE + type, ¶m); + -+ // check for the expected result -+ if (result->buffer.length != 1 || result->buffer.pointer[0] != 0) { -+ ACPI_FREE(result); -+ return -EIO; -+ } -+ -+ ACPI_FREE(result); -+ return 0; -+} -+ -+static int shps_dgpu_dsm_set_power_unlocked(struct platform_device *pdev, enum shps_dgpu_power power) -+{ -+ int status; -+ -+ if (power != SHPS_DGPU_POWER_ON && power != SHPS_DGPU_POWER_OFF) -+ return -EINVAL; -+ -+ status = shps_dgpu_dsm_get_power_unlocked(pdev); -+ if (status < 0) -+ return status; -+ if (status == power) -+ return 0; -+ -+ return __shps_dgpu_dsm_set_power_unlocked(pdev, power); -+} -+ -+static int shps_dgpu_dsm_set_power(struct platform_device *pdev, enum shps_dgpu_power power) -+{ -+ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); -+ int status; -+ -+ mutex_lock(&drvdata->lock); -+ status = shps_dgpu_dsm_set_power_unlocked(pdev, power); -+ mutex_unlock(&drvdata->lock); -+ -+ return status; -+} -+ -+ -+static bool shps_rp_link_up(struct pci_dev *rp) -+{ -+ u16 lnksta = 0, sltsta = 0; -+ -+ pcie_capability_read_word(rp, PCI_EXP_LNKSTA, &lnksta); -+ pcie_capability_read_word(rp, PCI_EXP_SLTSTA, &sltsta); -+ -+ return (lnksta & PCI_EXP_LNKSTA_DLLLA) || (sltsta & PCI_EXP_SLTSTA_PDS); -+} -+ -+ -+static int shps_dgpu_rp_get_power_unlocked(struct platform_device *pdev) -+{ -+ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); -+ struct pci_dev *rp = drvdata->dgpu_root_port; -+ -+ if (rp->current_state == PCI_D3hot || rp->current_state == PCI_D3cold) -+ return SHPS_DGPU_POWER_OFF; -+ else if (rp->current_state == PCI_UNKNOWN || rp->current_state == PCI_POWER_ERROR) -+ return SHPS_DGPU_POWER_UNKNOWN; -+ else -+ return SHPS_DGPU_POWER_ON; -+} -+ -+static int shps_dgpu_rp_get_power(struct platform_device *pdev) -+{ -+ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); -+ int status; -+ -+ mutex_lock(&drvdata->lock); -+ status = shps_dgpu_rp_get_power_unlocked(pdev); -+ mutex_unlock(&drvdata->lock); -+ -+ return status; -+} -+ -+static int __shps_dgpu_rp_set_power_unlocked(struct platform_device *pdev, enum shps_dgpu_power power) -+{ -+ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); -+ struct pci_dev *rp = drvdata->dgpu_root_port; -+ int status, i; -+ -+ dev_info(&pdev->dev, "setting dGPU power state to \'%s\'\n", shps_dgpu_power_str(power)); -+ -+ dbg_dump_drvsta(pdev, "__shps_dgpu_rp_set_power_unlocked.1"); -+ if (power == SHPS_DGPU_POWER_ON) { -+ set_bit(SHPS_STATE_BIT_RPPWRON_SYNC, &drvdata->state); -+ pci_set_power_state(rp, PCI_D0); -+ -+ if (drvdata->dgpu_root_port_state) -+ pci_load_and_free_saved_state(rp, &drvdata->dgpu_root_port_state); -+ -+ pci_restore_state(rp); -+ -+ if (!pci_is_enabled(rp)) -+ pci_enable_device(rp); -+ -+ pci_set_master(rp); -+ clear_bit(SHPS_STATE_BIT_RPPWRON_SYNC, &drvdata->state); -+ -+ set_bit(SHPS_STATE_BIT_PWRTGT, &drvdata->state); -+ } else { -+ if (!drvdata->dgpu_root_port_state) { -+ pci_save_state(rp); -+ drvdata->dgpu_root_port_state = pci_store_saved_state(rp); -+ } -+ -+ /* -+ * To properly update the hot-plug system we need to "remove" the dGPU -+ * before disabling it and sending it to D3cold. Following this, we -+ * need to wait for the link and slot status to actually change. -+ */ -+ status = shps_dgpu_dsm_set_power_unlocked(pdev, SHPS_DGPU_POWER_OFF); -+ if (status) -+ return status; -+ -+ for (i = 0; i < 20 && shps_rp_link_up(rp); i++) -+ msleep(50); -+ -+ if (shps_rp_link_up(rp)) -+ dev_err(&pdev->dev, "dGPU removal via DSM timed out\n"); -+ -+ pci_clear_master(rp); -+ -+ if (pci_is_enabled(rp)) -+ pci_disable_device(rp); -+ -+ pci_set_power_state(rp, PCI_D3cold); -+ -+ clear_bit(SHPS_STATE_BIT_PWRTGT, &drvdata->state); -+ } -+ dbg_dump_drvsta(pdev, "__shps_dgpu_rp_set_power_unlocked.2"); -+ -+ return 0; -+} -+ -+static int shps_dgpu_rp_set_power_unlocked(struct platform_device *pdev, enum shps_dgpu_power power) -+{ -+ int status; -+ -+ if (power != SHPS_DGPU_POWER_ON && power != SHPS_DGPU_POWER_OFF) -+ return -EINVAL; -+ -+ status = shps_dgpu_rp_get_power_unlocked(pdev); -+ if (status < 0) -+ return status; -+ if (status == power) -+ return 0; -+ -+ return __shps_dgpu_rp_set_power_unlocked(pdev, power); -+} -+ -+static int shps_dgpu_rp_set_power(struct platform_device *pdev, enum shps_dgpu_power power) -+{ -+ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); -+ int status; -+ -+ mutex_lock(&drvdata->lock); -+ status = shps_dgpu_rp_set_power_unlocked(pdev, power); -+ mutex_unlock(&drvdata->lock); -+ -+ return status; -+} -+ -+ -+static int shps_dgpu_set_power(struct platform_device *pdev, enum shps_dgpu_power power) -+{ -+ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); -+ int status; -+ -+ if (!param_dtx_latch) -+ return shps_dgpu_rp_set_power(pdev, power); -+ -+ if (power == SHPS_DGPU_POWER_ON) { -+ status = ssam_bas_latch_lock(drvdata->ctrl); -+ if (status) -+ return status; -+ -+ status = shps_dgpu_rp_set_power(pdev, power); -+ if (status) -+ ssam_bas_latch_unlock(drvdata->ctrl); -+ -+ } else { -+ status = shps_dgpu_rp_set_power(pdev, power); -+ if (status) -+ return status; -+ -+ status = ssam_bas_latch_unlock(drvdata->ctrl); -+ } -+ -+ return status; -+} -+ -+ -+static int shps_dgpu_is_present(struct platform_device *pdev) -+{ -+ struct shps_driver_data *drvdata; -+ -+ drvdata = platform_get_drvdata(pdev); -+ return gpiod_get_value_cansleep(drvdata->gpio_dgpu_presence); -+} -+ -+ -+static ssize_t dgpu_power_show(struct device *dev, struct device_attribute *attr, char *data) -+{ -+ struct platform_device *pdev = to_platform_device(dev); -+ int power = shps_dgpu_rp_get_power(pdev); -+ -+ if (power < 0) -+ return power; -+ -+ return sprintf(data, "%s\n", shps_dgpu_power_str(power)); -+} -+ -+static ssize_t dgpu_power_store(struct device *dev, struct device_attribute *attr, -+ const char *data, size_t count) -+{ -+ struct platform_device *pdev = to_platform_device(dev); -+ enum shps_dgpu_power power; -+ bool b = false; -+ int status; -+ -+ status = kstrtobool(data, &b); -+ if (status) -+ return status; -+ -+ status = shps_dgpu_is_present(pdev); -+ if (status <= 0) -+ return status < 0 ? status : -EPERM; -+ -+ power = b ? SHPS_DGPU_POWER_ON : SHPS_DGPU_POWER_OFF; -+ status = shps_dgpu_set_power(pdev, power); -+ -+ return status < 0 ? status : count; -+} -+ -+static ssize_t dgpu_power_dsm_show(struct device *dev, struct device_attribute *attr, char *data) -+{ -+ struct platform_device *pdev = to_platform_device(dev); -+ int power = shps_dgpu_dsm_get_power(pdev); -+ -+ if (power < 0) -+ return power; -+ -+ return sprintf(data, "%s\n", shps_dgpu_power_str(power)); -+} -+ -+static ssize_t dgpu_power_dsm_store(struct device *dev, struct device_attribute *attr, -+ const char *data, size_t count) -+{ -+ struct platform_device *pdev = to_platform_device(dev); -+ enum shps_dgpu_power power; -+ bool b = false; -+ int status; -+ -+ status = kstrtobool(data, &b); -+ if (status) -+ return status; -+ -+ status = shps_dgpu_is_present(pdev); -+ if (status <= 0) -+ return status < 0 ? status : -EPERM; -+ -+ power = b ? SHPS_DGPU_POWER_ON : SHPS_DGPU_POWER_OFF; -+ status = shps_dgpu_dsm_set_power(pdev, power); -+ -+ return status < 0 ? status : count; -+} -+ -+static DEVICE_ATTR_RW(dgpu_power); -+static DEVICE_ATTR_RW(dgpu_power_dsm); -+ -+static struct attribute *shps_power_attrs[] = { -+ &dev_attr_dgpu_power.attr, -+ &dev_attr_dgpu_power_dsm.attr, -+ NULL, -+}; -+ATTRIBUTE_GROUPS(shps_power); -+ -+ -+static void dbg_dump_power_states(struct platform_device *pdev, const char *prefix) -+{ -+ enum shps_dgpu_power power_dsm; -+ enum shps_dgpu_power power_rp; -+ int status; -+ -+ status = shps_dgpu_rp_get_power_unlocked(pdev); -+ if (status < 0) -+ dev_err(&pdev->dev, "%s: failed to get root-port power state: %d\n", prefix, status); -+ power_rp = status; -+ -+ status = shps_dgpu_rp_get_power_unlocked(pdev); -+ if (status < 0) -+ dev_err(&pdev->dev, "%s: failed to get direct power state: %d\n", prefix, status); -+ power_dsm = status; -+ -+ dev_dbg(&pdev->dev, "%s: root-port power state: %d\n", prefix, power_rp); -+ dev_dbg(&pdev->dev, "%s: direct power state: %d\n", prefix, power_dsm); -+} -+ -+static void dbg_dump_pciesta(struct platform_device *pdev, const char *prefix) -+{ -+ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); -+ struct pci_dev *rp = drvdata->dgpu_root_port; -+ u16 lnksta, lnksta2, sltsta, sltsta2; -+ -+ pcie_capability_read_word(rp, PCI_EXP_LNKSTA, &lnksta); -+ pcie_capability_read_word(rp, PCI_EXP_LNKSTA2, &lnksta2); -+ pcie_capability_read_word(rp, PCI_EXP_SLTSTA, &sltsta); -+ pcie_capability_read_word(rp, PCI_EXP_SLTSTA2, &sltsta2); -+ -+ dev_dbg(&pdev->dev, "%s: LNKSTA: 0x%04x\n", prefix, lnksta); -+ dev_dbg(&pdev->dev, "%s: LNKSTA2: 0x%04x\n", prefix, lnksta2); -+ dev_dbg(&pdev->dev, "%s: SLTSTA: 0x%04x\n", prefix, sltsta); -+ dev_dbg(&pdev->dev, "%s: SLTSTA2: 0x%04x\n", prefix, sltsta2); -+} -+ -+static void dbg_dump_drvsta(struct platform_device *pdev, const char *prefix) -+{ -+ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); -+ struct pci_dev *rp = drvdata->dgpu_root_port; -+ -+ dev_dbg(&pdev->dev, "%s: RP power: %d\n", prefix, rp->current_state); -+ dev_dbg(&pdev->dev, "%s: RP state saved: %d\n", prefix, rp->state_saved); -+ dev_dbg(&pdev->dev, "%s: RP state stored: %d\n", prefix, !!drvdata->dgpu_root_port_state); -+ dev_dbg(&pdev->dev, "%s: RP enabled: %d\n", prefix, atomic_read(&rp->enable_cnt)); -+ dev_dbg(&pdev->dev, "%s: RP mastered: %d\n", prefix, rp->is_busmaster); -+} -+ -+static int shps_pm_prepare(struct device *dev) -+{ -+ struct platform_device *pdev = to_platform_device(dev); -+ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); -+ bool pwrtgt; -+ int status = 0; -+ -+ dbg_dump_power_states(pdev, "shps_pm_prepare"); -+ -+ if (param_dgpu_power_susp != SHPS_DGPU_MP_POWER_ASIS) { -+ pwrtgt = test_bit(SHPS_STATE_BIT_PWRTGT, &drvdata->state); -+ -+ status = shps_dgpu_set_power(pdev, param_dgpu_power_susp); -+ if (status) { -+ dev_err(&pdev->dev, "failed to power %s dGPU: %d\n", -+ param_dgpu_power_susp == SHPS_DGPU_MP_POWER_OFF ? "off" : "on", -+ status); -+ return status; -+ } -+ -+ if (pwrtgt) -+ set_bit(SHPS_STATE_BIT_PWRTGT, &drvdata->state); -+ else -+ clear_bit(SHPS_STATE_BIT_PWRTGT, &drvdata->state); -+ } -+ -+ return 0; -+} -+ -+static void shps_pm_complete(struct device *dev) -+{ -+ struct platform_device *pdev = to_platform_device(dev); -+ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); -+ int status; -+ -+ dbg_dump_power_states(pdev, "shps_pm_complete"); -+ dbg_dump_pciesta(pdev, "shps_pm_complete"); -+ dbg_dump_drvsta(pdev, "shps_pm_complete.1"); -+ -+ // update power target, dGPU may have been detached while suspended -+ status = shps_dgpu_is_present(pdev); -+ if (status < 0) { -+ dev_err(&pdev->dev, "failed to get dGPU presence: %d\n", status); ++ if (!result) { ++ mutex_unlock(&sdev->lock[type]); ++ dev_err(&pdev->dev, "IRQ notification via DSM failed" ++ " (irq=%d, gpio=%d)\n", type, value); + return; -+ } else if (status == 0) { -+ clear_bit(SHPS_STATE_BIT_PWRTGT, &drvdata->state); + } + -+ /* -+ * During resume, the PCIe core will power on the root-port, which in turn -+ * will power on the dGPU. Most of the state synchronization is already -+ * handled via the SAN RQSG handler, so it is in a fully consistent -+ * on-state here. If requested, turn it off here. -+ * -+ * As there seem to be some synchronization issues turning off the dGPU -+ * directly after the power-on SAN RQSG notification during the resume -+ * process, let's do this here. -+ * -+ * TODO/FIXME: -+ * This does not combat unhandled power-ons when the device is not fully -+ * resumed, i.e. re-suspended before shps_pm_complete is called. Those -+ * should normally not be an issue, but the dGPU does get hot even though -+ * it is suspended, so ideally we want to keep it off. -+ */ -+ if (!test_bit(SHPS_STATE_BIT_PWRTGT, &drvdata->state)) { -+ status = shps_dgpu_set_power(pdev, SHPS_DGPU_POWER_OFF); -+ if (status) -+ dev_err(&pdev->dev, "failed to power-off dGPU: %d\n", status); ++ if (result->type != ACPI_TYPE_BUFFER) { ++ dev_err(&pdev->dev, "IRQ notification via DSM failed:" ++ " unexpected result type (irq=%d, gpio=%d)\n", ++ type, value); + } + -+ dbg_dump_drvsta(pdev, "shps_pm_complete.2"); -+} -+ -+static int shps_pm_suspend(struct device *dev) -+{ -+ struct platform_device *pdev = to_platform_device(dev); -+ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); -+ int status; -+ -+ if (device_may_wakeup(dev)) { -+ status = enable_irq_wake(drvdata->irq_base_presence); -+ if (status) -+ return status; -+ -+ set_bit(SHPS_STATE_BIT_WAKE_ENABLED, &drvdata->state); ++ if (result->buffer.length != 1 || result->buffer.pointer[0] != 0) { ++ dev_err(&pdev->dev, "IRQ notification via DSM failed:" ++ "unexpected result value (irq=%d, gpio=%d)\n", ++ type, value); + } + -+ return 0; ++ mutex_unlock(&sdev->lock[type]); ++ ACPI_FREE(result); +} + -+static int shps_pm_resume(struct device *dev) -+{ -+ struct platform_device *pdev = to_platform_device(dev); -+ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); -+ int status = 0; -+ -+ if (test_and_clear_bit(SHPS_STATE_BIT_WAKE_ENABLED, &drvdata->state)) -+ status = disable_irq_wake(drvdata->irq_base_presence); -+ -+ return status; -+} -+ -+static void shps_shutdown(struct platform_device *pdev) -+{ -+ int status; -+ -+ /* -+ * Turn on dGPU before shutting down. This allows the core drivers to -+ * properly shut down the device. If we don't do this, the pcieport driver -+ * will complain that the device has already been disabled. -+ */ -+ status = shps_dgpu_set_power(pdev, SHPS_DGPU_POWER_ON); -+ if (status) -+ dev_err(&pdev->dev, "failed to turn on dGPU: %d\n", status); -+} -+ -+static int shps_dgpu_detached(struct platform_device *pdev) -+{ -+ dbg_dump_power_states(pdev, "shps_dgpu_detached"); -+ return shps_dgpu_set_power(pdev, SHPS_DGPU_POWER_OFF); -+} -+ -+static int shps_dgpu_attached(struct platform_device *pdev) -+{ -+ dbg_dump_power_states(pdev, "shps_dgpu_attached"); -+ return 0; -+} -+ -+static int shps_dgpu_powered_on(struct platform_device *pdev) -+{ -+ /* -+ * This function gets called directly after a power-state transition of -+ * the dGPU root port out of D3cold state, indicating a power-on of the -+ * dGPU. Specifically, this function is called from the RQSG handler of -+ * SAN, invoked by the ACPI _ON method of the dGPU root port. This means -+ * that this function is run inside `pci_set_power_state(rp, ...)` -+ * synchronously and thus returns before the `pci_set_power_state` call -+ * does. -+ * -+ * `pci_set_power_state` may either be called by us or when the PCI -+ * subsystem decides to power up the root port (e.g. during resume). Thus -+ * we should use this function to ensure that the dGPU and root port -+ * states are consistent when an unexpected power-up is encountered. -+ */ -+ -+ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); -+ struct pci_dev *rp = drvdata->dgpu_root_port; -+ int status; -+ -+ dbg_dump_drvsta(pdev, "shps_dgpu_powered_on.1"); -+ -+ // if we caused the root port to power-on, return -+ if (test_bit(SHPS_STATE_BIT_RPPWRON_SYNC, &drvdata->state)) -+ return 0; -+ -+ // if dGPU is not present, force power-target to off and return -+ status = shps_dgpu_is_present(pdev); -+ if (status == 0) -+ clear_bit(SHPS_STATE_BIT_PWRTGT, &drvdata->state); -+ if (status <= 0) -+ return status; -+ -+ mutex_lock(&drvdata->lock); -+ -+ dbg_dump_power_states(pdev, "shps_dgpu_powered_on.1"); -+ dbg_dump_pciesta(pdev, "shps_dgpu_powered_on.1"); -+ if (drvdata->dgpu_root_port_state) -+ pci_load_and_free_saved_state(rp, &drvdata->dgpu_root_port_state); -+ pci_restore_state(rp); -+ if (!pci_is_enabled(rp)) -+ pci_enable_device(rp); -+ pci_set_master(rp); -+ dbg_dump_drvsta(pdev, "shps_dgpu_powered_on.2"); -+ dbg_dump_power_states(pdev, "shps_dgpu_powered_on.2"); -+ dbg_dump_pciesta(pdev, "shps_dgpu_powered_on.2"); -+ -+ mutex_unlock(&drvdata->lock); -+ -+ if (!test_bit(SHPS_STATE_BIT_PWRTGT, &drvdata->state)) { -+ dev_warn(&pdev->dev, "unexpected dGPU power-on detected\n"); -+ // TODO: schedule state re-check and update -+ } -+ -+ return 0; -+} -+ -+static int shps_dgpu_handle_rqsg(struct notifier_block *nb, unsigned long action, void *data) -+{ -+ struct shps_driver_data *drvdata = container_of(nb, struct shps_driver_data, dgpu_nf); -+ struct platform_device *pdev = drvdata->pdev; -+ struct san_dgpu_event *evt = data; -+ -+ if (evt->category == SAM_DGPU_TC && evt->command == SAM_DGPU_CID_POWERON) -+ return shps_dgpu_powered_on(pdev); -+ -+ dev_warn(&pdev->dev, "unimplemented dGPU request: RQSG(0x%02x, 0x%02x, 0x%02x)\n", -+ evt->category, evt->command, evt->instance); -+ return 0; -+} -+ -+static irqreturn_t shps_dgpu_presence_irq(int irq, void *data) ++static irqreturn_t shps_handle_irq(int irq, void *data) +{ + struct platform_device *pdev = data; -+ bool dgpu_present; -+ int status; ++ struct shps_device *sdev = platform_get_drvdata(pdev); ++ int type; + -+ status = shps_dgpu_is_present(pdev); -+ if (status < 0) { -+ dev_err(&pdev->dev, "failed to check physical dGPU presence: %d\n", status); ++ // figure out which IRQ we're handling ++ for (type = 0; type < SHPS_NUM_IRQS; type++) ++ if (irq == sdev->irq[type]) ++ break; ++ ++ // we should have found our interrupt, if not: this is a bug ++ if (WARN(type >= SHPS_NUM_IRQS, "invalid IRQ number: %d\n", irq)) + return IRQ_HANDLED; -+ } -+ -+ dgpu_present = status != 0; -+ dev_info(&pdev->dev, "dGPU physically %s\n", dgpu_present ? "attached" : "detached"); -+ -+ if (dgpu_present) -+ status = shps_dgpu_attached(pdev); -+ else -+ status = shps_dgpu_detached(pdev); -+ -+ if (status) -+ dev_err(&pdev->dev, "error handling dGPU interrupt: %d\n", status); + ++ // forward interrupt to ACPI via DSM ++ shps_dsm_notify_irq(pdev, type); + return IRQ_HANDLED; +} + -+static irqreturn_t shps_base_presence_irq(int irq, void *data) ++static int shps_setup_irq(struct platform_device *pdev, enum shps_irq_type type) +{ -+ return IRQ_HANDLED; // nothing to do, just wake -+} ++ struct shps_device *sdev = platform_get_drvdata(pdev); ++ struct gpio_desc *gpiod; ++ acpi_handle handle = ACPI_HANDLE(&pdev->dev); ++ const char *irq_name; ++ const int dsm = SHPS_DSM_FN_IRQ_BASE_PRESENCE + type; ++ int status, irq; + ++ // initialize as "not present" ++ sdev->gpio[type] = NULL; ++ sdev->irq[type] = SHPS_IRQ_NOT_PRESENT; + -+static int shps_gpios_setup(struct platform_device *pdev) -+{ -+ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); -+ struct gpio_desc *gpio_dgpu_power; -+ struct gpio_desc *gpio_dgpu_presence; -+ struct gpio_desc *gpio_base_presence; -+ int status; -+ -+ // get GPIOs -+ gpio_dgpu_power = devm_gpiod_get(&pdev->dev, "dgpu_power", GPIOD_IN); -+ if (IS_ERR(gpio_dgpu_power)) { -+ status = PTR_ERR(gpio_dgpu_power); -+ goto err_out; ++ // only set up interrupts that we actually need ++ if (!acpi_check_dsm(handle, &shps_dsm_guid, SHPS_DSM_REVISION, BIT(dsm))) { ++ dev_dbg(&pdev->dev, "IRQ notification via DSM not present (irq=%d)\n", ++ type); ++ return 0; + } + -+ gpio_dgpu_presence = devm_gpiod_get(&pdev->dev, "dgpu_presence", GPIOD_IN); -+ if (IS_ERR(gpio_dgpu_presence)) { -+ status = PTR_ERR(gpio_dgpu_presence); -+ goto err_out; -+ } ++ gpiod = devm_gpiod_get(&pdev->dev, shps_gpio_names[type], GPIOD_ASIS); ++ if (IS_ERR(gpiod)) ++ return PTR_ERR(gpiod); + -+ gpio_base_presence = devm_gpiod_get(&pdev->dev, "base_presence", GPIOD_IN); -+ if (IS_ERR(gpio_base_presence)) { -+ status = PTR_ERR(gpio_base_presence); -+ goto err_out; -+ } ++ irq = gpiod_to_irq(gpiod); ++ if (irq < 0) ++ return irq; + -+ // export GPIOs -+ status = gpiod_export(gpio_dgpu_power, false); ++ irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "shps-irq-%d", type); ++ if (!irq_name) ++ return -ENOMEM; ++ ++ status = devm_request_threaded_irq(&pdev->dev, irq, NULL, shps_handle_irq, ++ IRQF_ONESHOT | IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, ++ irq_name, pdev); + if (status) -+ goto err_out; -+ -+ status = gpiod_export(gpio_dgpu_presence, false); -+ if (status) -+ goto err_export_dgpu_presence; -+ -+ status = gpiod_export(gpio_base_presence, false); -+ if (status) -+ goto err_export_base_presence; -+ -+ // create sysfs links -+ status = gpiod_export_link(&pdev->dev, "gpio-dgpu_power", gpio_dgpu_power); -+ if (status) -+ goto err_link_dgpu_power; -+ -+ status = gpiod_export_link(&pdev->dev, "gpio-dgpu_presence", gpio_dgpu_presence); -+ if (status) -+ goto err_link_dgpu_presence; -+ -+ status = gpiod_export_link(&pdev->dev, "gpio-base_presence", gpio_base_presence); -+ if (status) -+ goto err_link_base_presence; -+ -+ drvdata->gpio_dgpu_power = gpio_dgpu_power; -+ drvdata->gpio_dgpu_presence = gpio_dgpu_presence; -+ drvdata->gpio_base_presence = gpio_base_presence; -+ return 0; -+ -+err_link_base_presence: -+ sysfs_remove_link(&pdev->dev.kobj, "gpio-dgpu_presence"); -+err_link_dgpu_presence: -+ sysfs_remove_link(&pdev->dev.kobj, "gpio-dgpu_power"); -+err_link_dgpu_power: -+ gpiod_unexport(gpio_base_presence); -+err_export_base_presence: -+ gpiod_unexport(gpio_dgpu_presence); -+err_export_dgpu_presence: -+ gpiod_unexport(gpio_dgpu_power); -+err_out: -+ return status; -+} -+ -+static void shps_gpios_remove(struct platform_device *pdev) -+{ -+ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); -+ -+ sysfs_remove_link(&pdev->dev.kobj, "gpio-base_presence"); -+ sysfs_remove_link(&pdev->dev.kobj, "gpio-dgpu_presence"); -+ sysfs_remove_link(&pdev->dev.kobj, "gpio-dgpu_power"); -+ gpiod_unexport(drvdata->gpio_base_presence); -+ gpiod_unexport(drvdata->gpio_dgpu_presence); -+ gpiod_unexport(drvdata->gpio_dgpu_power); -+} -+ -+static int shps_gpios_setup_irq(struct platform_device *pdev) -+{ -+ const int irqf_dgpu = IRQF_SHARED | IRQF_ONESHOT | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING; -+ const int irqf_base = IRQF_SHARED; -+ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); -+ int status; -+ -+ status = gpiod_to_irq(drvdata->gpio_base_presence); -+ if (status < 0) + return status; -+ drvdata->irq_base_presence = status; + -+ status = gpiod_to_irq(drvdata->gpio_dgpu_presence); -+ if (status < 0) -+ return status; -+ drvdata->irq_dgpu_presence = status; ++ dev_dbg(&pdev->dev, "set up irq %d as type %d\n", irq, type); + -+ status = request_irq(drvdata->irq_base_presence, -+ shps_base_presence_irq, irqf_base, -+ "shps_base_presence_irq", pdev); -+ if (status) { -+ dev_err(&pdev->dev, "base irq failed: %d\n", status); -+ return status; -+ } -+ -+ status = request_threaded_irq(drvdata->irq_dgpu_presence, -+ NULL, shps_dgpu_presence_irq, irqf_dgpu, -+ "shps_dgpu_presence_irq", pdev); -+ if (status) { -+ free_irq(drvdata->irq_base_presence, pdev); -+ return status; -+ } ++ sdev->gpio[type] = gpiod; ++ sdev->irq[type] = irq; + + return 0; +} + -+static void shps_gpios_remove_irq(struct platform_device *pdev) ++static int surface_hotplug_probe(struct platform_device *pdev) +{ -+ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); -+ -+ free_irq(drvdata->irq_base_presence, pdev); -+ free_irq(drvdata->irq_dgpu_presence, pdev); -+} -+ -+static void shps_sgcp_notify(acpi_handle device, u32 value, void *context) { -+ struct platform_device *pdev = context; -+ switch (value) { -+ case ACPI_SGCP_NOTIFY_POWER_ON: -+ shps_dgpu_powered_on(pdev); -+ } -+} -+ -+static int shps_start_sgcp_notification(struct platform_device *pdev, acpi_handle *sgpc_handle) { -+ acpi_handle handle; -+ acpi_status status; -+ -+ status = acpi_get_handle(NULL, "\\_SB.SGPC", &handle); -+ if (ACPI_FAILURE(status)) { -+ dev_err(&pdev->dev, "error in get_handle %x\n", status); -+ return -ENXIO; -+ } -+ -+ status = acpi_install_notify_handler(handle, ACPI_DEVICE_NOTIFY, shps_sgcp_notify, pdev); -+ if (ACPI_FAILURE(status)) { -+ dev_err(&pdev->dev, "error in install notify %x\n", status); -+ *sgpc_handle = NULL; -+ return -EFAULT; -+ } -+ -+ *sgpc_handle = handle; -+ return 0; -+} -+ -+static void shps_remove_sgcp_notification(struct platform_device *pdev) { -+ acpi_status status; -+ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); -+ -+ if (drvdata->sgpc_handle) { -+ status = acpi_remove_notify_handler(drvdata->sgpc_handle, ACPI_DEVICE_NOTIFY, shps_sgcp_notify); -+ if (ACPI_FAILURE(status)) -+ dev_err(&pdev->dev, "failed to remove notify handler: %x\n", status); -+ } -+} -+ -+static struct shps_hardware_traits shps_detect_hardware_traits(struct platform_device *pdev) { -+ const struct shps_hardware_probe *p; -+ -+ for (p = shps_hardware_probe_match; p->hardware_id; ++p) { -+ if (acpi_dev_present(p->hardware_id, NULL, -1)) { -+ break; -+ } -+ } -+ -+ dev_info(&pdev->dev, -+ "shps_detect_hardware_traits found device %s, generation %d\n", -+ p->hardware_id ? p->hardware_id : "SAN (default)", -+ p->generation); -+ -+ return *p->hardware_traits; -+} -+ -+static int shps_probe(struct platform_device *pdev) -+{ -+ struct shps_driver_data *drvdata; -+ struct ssam_controller *ctrl; -+ struct device_link *link; -+ int power, status; -+ struct shps_hardware_traits detected_traits; ++ struct shps_device *sdev; ++ int status, i; + + if (gpiod_count(&pdev->dev, NULL) < 0) { + dev_err(&pdev->dev, "gpiod_count returned < 0\n"); + return -ENODEV; + } + -+ // link to SSH -+ status = ssam_client_bind(&pdev->dev, &ctrl); -+ if (status) { -+ return status == -ENXIO ? -EPROBE_DEFER : status; -+ } -+ -+ // detect what kind of hardware we're running -+ detected_traits = shps_detect_hardware_traits(pdev); -+ -+ if (detected_traits.notification_method == SHPS_NOTIFICATION_METHOD_SAN) { -+ // link to SAN -+ status = san_client_link(&pdev->dev); -+ if (status) { -+ dev_err(&pdev->dev, "failed to register as SAN client: %d\n", status); -+ return status == -ENXIO ? -EPROBE_DEFER : status; -+ } -+ } -+ + status = devm_acpi_dev_add_driver_gpios(&pdev->dev, shps_acpi_gpios); + if (status) { + dev_err(&pdev->dev, "failed to add gpios: %d\n", status); + return status; + } + -+ drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL); -+ if (!drvdata) ++ sdev = devm_kzalloc(&pdev->dev, sizeof(*sdev), GFP_KERNEL); ++ if (!sdev) + return -ENOMEM; + -+ mutex_init(&drvdata->lock); -+ platform_set_drvdata(pdev, drvdata); ++ platform_set_drvdata(pdev, sdev); + -+ drvdata->ctrl = ctrl; -+ drvdata->pdev = pdev; -+ drvdata->hardware_traits = detected_traits; ++ // set up IRQs ++ for (i = 0; i < SHPS_NUM_IRQS; i++) { ++ mutex_init(&sdev->lock[i]); + -+ drvdata->dgpu_root_port = shps_dgpu_dsm_get_pci_dev(pdev); -+ if (IS_ERR(drvdata->dgpu_root_port)) { -+ status = PTR_ERR(drvdata->dgpu_root_port); -+ dev_err(&pdev->dev, "failed to get pci dev: %d\n", status); -+ return status; -+ } -+ -+ status = shps_gpios_setup(pdev); -+ if (status) { -+ dev_err(&pdev->dev, "unable to set up gpios, %d\n", status); -+ goto err_gpio; -+ } -+ -+ status = shps_gpios_setup_irq(pdev); -+ if (status) { -+ dev_err(&pdev->dev, "unable to set up irqs %d\n", status); -+ goto err_gpio_irqs; -+ } -+ -+ status = device_add_groups(&pdev->dev, shps_power_groups); -+ if (status) -+ goto err_devattr; -+ -+ link = device_link_add(&pdev->dev, &drvdata->dgpu_root_port->dev, -+ DL_FLAG_PM_RUNTIME | DL_FLAG_AUTOREMOVE_CONSUMER); -+ if (!link) -+ goto err_devlink; -+ -+ if (detected_traits.notification_method == SHPS_NOTIFICATION_METHOD_SAN) { -+ drvdata->dgpu_nf.priority = 1; -+ drvdata->dgpu_nf.notifier_call = shps_dgpu_handle_rqsg; -+ -+ status = san_dgpu_notifier_register(&drvdata->dgpu_nf); ++ status = shps_setup_irq(pdev, i); + if (status) { -+ dev_err(&pdev->dev, "unable to register SAN notification handler (%d)\n", status); -+ goto err_devlink; -+ } -+ } else if (detected_traits.notification_method == SHPS_NOTIFICATION_METHOD_SGCP) { -+ status = shps_start_sgcp_notification(pdev, &drvdata->sgpc_handle); -+ if (status) { -+ dev_err(&pdev->dev, "unable to install SGCP notification handler (%d)\n", status); -+ goto err_devlink; ++ dev_err(&pdev->dev, "failed to set up IRQ %d: %d\n", ++ i, status); ++ return status; + } + } + -+ // if dGPU is not present turn-off root-port, else obey module param -+ status = shps_dgpu_is_present(pdev); -+ if (status < 0) -+ goto err_post_notification; ++ // ensure everything is up-to-date ++ for (i = 0; i < SHPS_NUM_IRQS; i++) ++ if (sdev->irq[i] != SHPS_IRQ_NOT_PRESENT) ++ shps_dsm_notify_irq(pdev, i); + -+ power = status == 0 ? SHPS_DGPU_POWER_OFF : param_dgpu_power_init; -+ if (power != SHPS_DGPU_MP_POWER_ASIS) { -+ status = shps_dgpu_set_power(pdev, power); -+ if (status) -+ goto err_post_notification; -+ } -+ -+ // initialize power target -+ status = shps_dgpu_rp_get_power(pdev); -+ if (status < 0) -+ goto err_pwrtgt; -+ -+ if (status) -+ set_bit(SHPS_STATE_BIT_PWRTGT, &drvdata->state); -+ else -+ clear_bit(SHPS_STATE_BIT_PWRTGT, &drvdata->state); -+ -+ device_init_wakeup(&pdev->dev, true); + return 0; -+ -+err_pwrtgt: -+ if (param_dgpu_power_exit != SHPS_DGPU_MP_POWER_ASIS) { -+ status = shps_dgpu_set_power(pdev, param_dgpu_power_exit); -+ if (status) -+ dev_err(&pdev->dev, "failed to set dGPU power state: %d\n", status); -+ } -+err_post_notification: -+ if (detected_traits.notification_method == SHPS_NOTIFICATION_METHOD_SGCP) { -+ shps_remove_sgcp_notification(pdev); -+ } else if (detected_traits.notification_method == SHPS_NOTIFICATION_METHOD_SAN) { -+ san_dgpu_notifier_unregister(&drvdata->dgpu_nf); -+ } -+err_devlink: -+ device_remove_groups(&pdev->dev, shps_power_groups); -+err_devattr: -+ shps_gpios_remove_irq(pdev); -+err_gpio_irqs: -+ shps_gpios_remove(pdev); -+err_gpio: -+ pci_dev_put(drvdata->dgpu_root_port); -+ return status; +} + -+static int shps_remove(struct platform_device *pdev) ++static int surface_hotplug_remove(struct platform_device *pdev) +{ -+ struct shps_driver_data *drvdata = platform_get_drvdata(pdev); -+ int status; ++ struct shps_device *sdev = platform_get_drvdata(pdev); ++ int i; + -+ if (param_dgpu_power_exit != SHPS_DGPU_MP_POWER_ASIS) { -+ status = shps_dgpu_set_power(pdev, param_dgpu_power_exit); -+ if (status) -+ dev_err(&pdev->dev, "failed to set dGPU power state: %d\n", status); -+ } -+ -+ device_set_wakeup_capable(&pdev->dev, false); -+ -+ if (drvdata->hardware_traits.notification_method == SHPS_NOTIFICATION_METHOD_SGCP) { -+ shps_remove_sgcp_notification(pdev); -+ } else if (drvdata->hardware_traits.notification_method == SHPS_NOTIFICATION_METHOD_SAN) { -+ san_dgpu_notifier_unregister(&drvdata->dgpu_nf); -+ } -+ device_remove_groups(&pdev->dev, shps_power_groups); -+ shps_gpios_remove_irq(pdev); -+ shps_gpios_remove(pdev); -+ pci_dev_put(drvdata->dgpu_root_port); ++ // ensure that IRQs have been fully handled and won't trigger any more ++ for (i = 0; i < SHPS_NUM_IRQS; i++) ++ if (sdev->irq[i] != SHPS_IRQ_NOT_PRESENT) ++ disable_irq(sdev->irq[i]); + + return 0; +} + -+ -+static const struct dev_pm_ops shps_pm_ops = { -+ .prepare = shps_pm_prepare, -+ .complete = shps_pm_complete, -+ .suspend = shps_pm_suspend, -+ .resume = shps_pm_resume, -+}; -+ -+static const struct acpi_device_id shps_acpi_match[] = { ++static const struct acpi_device_id surface_hotplug_acpi_match[] = { + { "MSHW0153", 0 }, + { }, +}; -+MODULE_DEVICE_TABLE(acpi, shps_acpi_match); ++MODULE_DEVICE_TABLE(acpi, surface_hotplug_acpi_match); + -+static struct platform_driver surface_sam_hps = { -+ .probe = shps_probe, -+ .remove = shps_remove, -+ .shutdown = shps_shutdown, ++static struct platform_driver surface_hotplug_driver = { ++ .probe = surface_hotplug_probe, ++ .remove = surface_hotplug_remove, + .driver = { -+ .name = "surface_dgpu_hotplug", -+ .acpi_match_table = shps_acpi_match, -+ .pm = &shps_pm_ops, ++ .name = "surface_hotplug", ++ .acpi_match_table = surface_hotplug_acpi_match, + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, +}; -+module_platform_driver(surface_sam_hps); ++module_platform_driver(surface_hotplug_driver); + +MODULE_AUTHOR("Maximilian Luz "); -+MODULE_DESCRIPTION("DGPU hot-plug system driver for Surface System Aggregator Module"); ++MODULE_DESCRIPTION("Surface Hot-Plug Signalling Driver for Surface Book Devices"); +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/surface_aggregator/clients/surface_perfmode.c b/drivers/misc/surface_aggregator/clients/surface_perfmode.c new file mode 100644 @@ -20162,7 +19397,7 @@ index 000000000000..1a8bc0249f8e +#endif /* _UAPI_LINUX_SURFACE_AGGREGATOR_CDEV_H */ diff --git a/include/uapi/linux/surface_aggregator/dtx.h b/include/uapi/linux/surface_aggregator/dtx.h new file mode 100644 -index 000000000000..d88cabfb8dd7 +index 000000000000..fb6c49e254af --- /dev/null +++ b/include/uapi/linux/surface_aggregator/dtx.h @@ -0,0 +1,150 @@ @@ -20260,7 +19495,7 @@ index 000000000000..d88cabfb8dd7 + __u16 length; + __u16 code; + __u8 data[]; -+} __packed; ++} __attribute__((__packed__)); + +/** + * enum sdtx_event_code - Code describing the type of an event. @@ -20296,7 +19531,7 @@ index 000000000000..d88cabfb8dd7 +struct sdtx_base_info { + __u16 state; + __u16 base_id; -+} __packed; ++} __attribute__((__packed__)); + + +/* IOCTLs */ @@ -20363,5 +19598,5 @@ index a6c583362b92..5b79fdc42641 100644 ADD(alias, "f", match_flags & SSAM_MATCH_FUNCTION, function); -- -2.28.0 +2.29.2 diff --git a/patches/5.9/0007-i2c-core-Restore-acpi_walk_dep_device_list-getting-c.patch b/patches/5.9/0007-i2c-core-Restore-acpi_walk_dep_device_list-getting-c.patch deleted file mode 100644 index 1b032a99b..000000000 --- a/patches/5.9/0007-i2c-core-Restore-acpi_walk_dep_device_list-getting-c.patch +++ /dev/null @@ -1,70 +0,0 @@ -From 93bb0042f8df962612190d85ffa178477833af51 Mon Sep 17 00:00:00 2001 -From: Hans de Goede -Date: Wed, 14 Oct 2020 16:41:58 +0200 -Subject: [PATCH] i2c: core: Restore acpi_walk_dep_device_list() getting called - after registering the ACPI i2c devs - -Commit 21653a4181ff ("i2c: core: Call i2c_acpi_install_space_handler() -before i2c_acpi_register_devices()")'s intention was to only move the -acpi_install_address_space_handler() call to the point before where -the ACPI declared i2c-children of the adapter where instantiated by -i2c_acpi_register_devices(). - -But i2c_acpi_install_space_handler() had a call to -acpi_walk_dep_device_list() hidden (that is I missed it) at the end -of it, so as an unwanted side-effect now acpi_walk_dep_device_list() -was also being called before i2c_acpi_register_devices(). - -Move the acpi_walk_dep_device_list() call to the end of -i2c_acpi_register_devices(), so that it is once again called *after* -the i2c_client-s hanging of the adapter have been created. - -This fixes the Microsoft Surface Go 2 hanging at boot. - -Fixes: 21653a4181ff ("i2c: core: Call i2c_acpi_install_space_handler() before i2c_acpi_register_devices()") -Suggested-by: Maximilian Luz -Reported-and-tested-by: Kieran Bingham -Signed-off-by: Hans de Goede ---- - drivers/i2c/i2c-core-acpi.c | 11 ++++++++++- - 1 file changed, 10 insertions(+), 1 deletion(-) - -diff --git a/drivers/i2c/i2c-core-acpi.c b/drivers/i2c/i2c-core-acpi.c -index 8820131da748..aed579942436 100644 ---- a/drivers/i2c/i2c-core-acpi.c -+++ b/drivers/i2c/i2c-core-acpi.c -@@ -264,6 +264,7 @@ static acpi_status i2c_acpi_add_device(acpi_handle handle, u32 level, - void i2c_acpi_register_devices(struct i2c_adapter *adap) - { - acpi_status status; -+ acpi_handle handle; - - if (!has_acpi_companion(&adap->dev)) - return; -@@ -274,6 +275,15 @@ void i2c_acpi_register_devices(struct i2c_adapter *adap) - adap, NULL); - if (ACPI_FAILURE(status)) - dev_warn(&adap->dev, "failed to enumerate I2C slaves\n"); -+ -+ if (!adap->dev.parent) -+ return; -+ -+ handle = ACPI_HANDLE(adap->dev.parent); -+ if (!handle) -+ return; -+ -+ acpi_walk_dep_device_list(handle); - } - - static const struct acpi_device_id i2c_acpi_force_400khz_device_ids[] = { -@@ -754,7 +764,6 @@ int i2c_acpi_install_space_handler(struct i2c_adapter *adapter) - return -ENOMEM; - } - -- acpi_walk_dep_device_list(handle); - return 0; - } - --- -2.28.0 - diff --git a/pkg/arch/kernel/PKGBUILD b/pkg/arch/kernel/PKGBUILD index 16bcc162a..36781614e 100644 --- a/pkg/arch/kernel/PKGBUILD +++ b/pkg/arch/kernel/PKGBUILD @@ -39,14 +39,14 @@ validpgpkeys=( ) sha256sums=('d2c06044e80b49a7d029d52d297c86efb1726aa0c66d16dd21c6d1df27d7c314' '36439a90c9d2f860298d90e141f3bf9d897dd8ece9e21cd46508f4ed7b2151bb' - '7f52b09f0bf62d9b2ba162a424ce99caa50cb671f2b78fca1c1dc81e33e9b57f' + 'cad2f23b058938c27ccdeb1944dcc492cf8696651b1ca9c74a86eb73a039f8c1' '8cd2b019aac6d3807a5cdcbbbe0aad81e63193ff3e8dffd7a79d4a1421b858f6' - '09251faa27823c6e6d7e89f6556d7ed2d7649533ce30e57dd9afdab4713cfc1e' - 'e83f95d8336985cfff0ba1f45928f4c6e75902db4cafdebfc2c50eb0d4f940d1' - '60e719ec00b39aea1d0f1527a59c225126e633dd7b80da6264451585d44dfdce' - 'd0ea6d34040aeeadabc306b50784a701d97ffbc26786af8948dab2658552c643' - '2513728862cc3f3bc8c32b5de8de13f171b5bed9cbc9e7ce5bdbd88f3c7b021b' - '86b09d0c65a7d1bd04550d39dbced9745a7f5be90d5f92c51c4fb2ba1a6d4d8e') + 'ab3886ff7266cf518d409a03261e496ffa992e1e9e5f74160f38907d392810a1' + 'affe80a0d097740a0523cbad5dd7657df5772a57e93af3dd318138866987198f' + 'c5143277de5086f20f4bd605d5e82f7abdfc725871c103f461b701588b7558e1' + '1bd872785623a547ee0e7f0d4c6e68c2067ebde73fcc96bc2bfabfaf4fda5515' + '8d4fd88b450e9e7bc6050d9a69e42ef255a664f8b70bdb10eb8fff49bd7d6484' + '184693d8003fc85213c5f662a2bbd729402e2ddba6035f67fa360179414fea36') export KBUILD_BUILD_HOST=archlinux