From e420429a234bff2e5f53f14c7afa69bf6eb0cb8f Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Wed, 4 Nov 2020 01:43:18 +0100 Subject: [PATCH] Update v5.8 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/fb58dd7a0161e663e78688e3b7f1d27f559f1890 [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.8.config | 1 - patches/5.8/0001-surface3-oemb.patch | 4 +- patches/5.8/0002-wifi.patch | 52 +- patches/5.8/0003-ipts.patch | 12 +- patches/5.8/0004-surface-gpe.patch | 14 +- patches/5.8/0005-surface-sam-over-hid.patch | 14 +- patches/5.8/0006-surface-sam.patch | 1649 +++++------------ ...-acpi_walk_dep_device_list-getting-c.patch | 70 - 8 files changed, 491 insertions(+), 1325 deletions(-) delete mode 100644 patches/5.8/0007-i2c-core-Restore-acpi_walk_dep_device_list-getting-c.patch diff --git a/configs/surface-5.8.config b/configs/surface-5.8.config index c49b26db9..285d92703 100644 --- a/configs/surface-5.8.config +++ b/configs/surface-5.8.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.8/0001-surface3-oemb.patch b/patches/5.8/0001-surface3-oemb.patch index c27864a9e..a2af9d4a8 100644 --- a/patches/5.8/0001-surface3-oemb.patch +++ b/patches/5.8/0001-surface3-oemb.patch @@ -1,4 +1,4 @@ -From 783117ef972d0dcc29d1065e4609690c9093a8c3 Mon Sep 17 00:00:00 2001 +From a071f62f9ccad02cbd533c29d268af206bfe8ef5 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.8/0002-wifi.patch b/patches/5.8/0002-wifi.patch index dc2adfc9d..0e9718ac2 100644 --- a/patches/5.8/0002-wifi.patch +++ b/patches/5.8/0002-wifi.patch @@ -1,4 +1,4 @@ -From c6be0add7574727c5ab9081ff4b46b4f6ef1fdf5 Mon Sep 17 00:00:00 2001 +From df4f3b986e95327601b3d7b3129a054d5c4f5c37 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 2b463669a76bc7cc2146481fd131cbc1233b9711 Mon Sep 17 00:00:00 2001 +From b54be410021bbb564378a6a7b7d619caac7ebe27 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 e344e3aa7d35ea66edb2d0792bda98ef311a9f98 Mon Sep 17 00:00:00 2001 +From 9abbacea22bbefde01c8ea4caa67bfd3df258b6b 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 a835d3d17471990f1c33e8a566876b1774856ac9 Mon Sep 17 00:00:00 2001 +From 3e66895299b5d5d82d87b7908eda2dd2d1ecba34 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 1a027d9e20179b26a9a89cbc67570dc57decc30e Mon Sep 17 00:00:00 2001 +From 80a35612b6395e96c9dd7c815ed318ff470f1963 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 b83d011dd7c568c2db41c520d3fdaadc4ff8954a Mon Sep 17 00:00:00 2001 +From cc6c7458415e05e875ec833869e4297a5a3018ce 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 529099137644..c21f916c05a3 100644 adapter->is_up = false; -- -2.28.0 +2.29.2 -From 919ef1f9830f57b14ad8af2b812bb25ec7a5dba0 Mon Sep 17 00:00:00 2001 +From 065d5d54022849cc2d4e260ad2c3c878c112b5e0 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 @@ -1000,9 +1000,9 @@ index daae572ce94e..b46d56389c3b 100644 return 0; } -- -2.28.0 +2.29.2 -From d63ccbb60c210c361cf48612ea3fc8f882565da6 Mon Sep 17 00:00:00 2001 +From 0f8208b32b86341d0ab6d92d1ebc116fd120e11d Mon Sep 17 00:00:00 2001 From: Tsuchiya Yuto Date: Sun, 27 Sep 2020 00:51:38 +0900 Subject: [PATCH] mwifiex: update comment for shutdown_sw()/reinit_sw() @@ -1043,9 +1043,9 @@ index c21f916c05a3..a26eb66865e2 100644 */ int -- -2.28.0 +2.29.2 -From c7a3556e53d7053e3bfed69c23e4a80a12ec03d4 Mon Sep 17 00:00:00 2001 +From 4e829ac5be071b4f8c80bac826d1d6583987da10 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 @@ -1092,9 +1092,9 @@ index b46d56389c3b..1847a0274991 100644 if (!adapter->devdump_data) { mwifiex_dbg(adapter, ERROR, -- -2.28.0 +2.29.2 -From bcf9d4b3af74128a676e6e5b79ac83e6e29fbbef Mon Sep 17 00:00:00 2001 +From e29d83531bb3b7768e86a471f455a350d6922269 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+ @@ -1247,9 +1247,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 7dda35220e0c86d787ea4d1299b7ff645e992acb Mon Sep 17 00:00:00 2001 +From 62e1318d5fd6a568f95ff04328db2f17ce36ed44 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 @@ -1309,9 +1309,9 @@ index 4e4f59c17ded..1074bcb2606b 100644 } -- -2.28.0 +2.29.2 -From 390fa12d3af6558f2b4bdb145c45b27129b29ba1 Mon Sep 17 00:00:00 2001 +From 16fce93f337297dcba4c95f2f35d4276b0a775b3 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 @@ -1357,9 +1357,9 @@ index 8bd355d7974e..621519826685 100644 if (drcs) { -- -2.28.0 +2.29.2 -From 570c6d717002dd3734452e1d6db0ac8414418b7e Mon Sep 17 00:00:00 2001 +From f80a820c8f03bc7a0fcaf6ec0304cf4be6174145 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 @@ -1392,5 +1392,5 @@ index 1074bcb2606b..67800980a7f0 100644 } -- -2.28.0 +2.29.2 diff --git a/patches/5.8/0003-ipts.patch b/patches/5.8/0003-ipts.patch index 0142da4cf..e3944fcc7 100644 --- a/patches/5.8/0003-ipts.patch +++ b/patches/5.8/0003-ipts.patch @@ -1,4 +1,4 @@ -From 1fb79b576315c036f08e778546e71212230d9ddc Mon Sep 17 00:00:00 2001 +From 7a6081ff09e02f8771382ddfe292e807741bffb9 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 b9b4c4f84f5111e3213225be406e43cc11623dbc Mon Sep 17 00:00:00 2001 +From a0fcd7564e07c6b140b6db2a615d38b8ae844b34 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 @@ -95,9 +95,9 @@ index 2a3f2fd5df50..319158fd4393 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 e6812c7fe4d645c770c87071d6e30cb6b960c169 Mon Sep 17 00:00:00 2001 +From 8fa42c4ef1cc0c1026803467117d46f6099d6699 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 @@ -1444,5 +1444,5 @@ index 000000000000..4c667bb6a7f2 +#endif /* _IPTS_UAPI_H_ */ + -- -2.28.0 +2.29.2 diff --git a/patches/5.8/0004-surface-gpe.patch b/patches/5.8/0004-surface-gpe.patch index 3e4585007..1a755a824 100644 --- a/patches/5.8/0004-surface-gpe.patch +++ b/patches/5.8/0004-surface-gpe.patch @@ -1,4 +1,4 @@ -From aa25cec8fd87df94cabc33c37cc0a2778d63f3d4 Mon Sep 17 00:00:00 2001 +From fae166412b1c31270f47033e6146719132cd0255 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 2b85852a1a87..a64ce216719a 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.8/0005-surface-sam-over-hid.patch b/patches/5.8/0005-surface-sam-over-hid.patch index e1c2f793f..2745fe215 100644 --- a/patches/5.8/0005-surface-sam-over-hid.patch +++ b/patches/5.8/0005-surface-sam-over-hid.patch @@ -1,4 +1,4 @@ -From 256ed7b2d0a06dd15968828ac55d355773b19958 Mon Sep 17 00:00:00 2001 +From c55292610c890ec39f40734413a0a4d737b5d700 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 2ade99b105b9..60b9cb51d5f7 100644 +index bbf8dd491d24..72f7305ab3fc 100644 --- a/drivers/i2c/i2c-core-acpi.c +++ b/drivers/i2c/i2c-core-acpi.c -@@ -574,6 +574,28 @@ static int acpi_gsb_i2c_write_bytes(struct i2c_client *client, +@@ -584,6 +584,28 @@ static int acpi_gsb_i2c_write_bytes(struct i2c_client *client, return (ret == 1) ? 0 : -EIO; } @@ -86,7 +86,7 @@ index 2ade99b105b9..60b9cb51d5f7 100644 static acpi_status i2c_acpi_space_handler(u32 function, acpi_physical_address command, u32 bits, u64 *value64, -@@ -675,6 +697,19 @@ i2c_acpi_space_handler(u32 function, acpi_physical_address command, +@@ -685,6 +707,19 @@ i2c_acpi_space_handler(u32 function, acpi_physical_address command, } break; @@ -107,9 +107,9 @@ index 2ade99b105b9..60b9cb51d5f7 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 03be5ddae354964501f25958d53525b23c9c6243 Mon Sep 17 00:00:00 2001 +From 7ec30997f8d15e3e62c9ad5d38bd8ad84a3b7328 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.8/0006-surface-sam.patch b/patches/5.8/0006-surface-sam.patch index f73bd9518..df5062d0b 100644 --- a/patches/5.8/0006-surface-sam.patch +++ b/patches/5.8/0006-surface-sam.patch @@ -1,4 +1,245 @@ -From 4f5646ac1b6fca049b627b1cff5c4a7403845c62 Mon Sep 17 00:00:00 2001 +From be8c81503d5c12f775ad4530c39152a94a270cc9 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 c9338f914a0e..94371684783e 100644 +--- a/drivers/pci/pci.c ++++ b/drivers/pci/pci.c +@@ -2679,6 +2679,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 +@@ -2719,10 +2745,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 8b64dbc1c773df9c81e18a6bb3213bab01ec19bb 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 94371684783e..0d1ef894e9e6 100644 +--- a/drivers/pci/pci.c ++++ b/drivers/pci/pci.c +@@ -1731,7 +1731,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; + + /* +@@ -1741,9 +1741,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 dd133384e8eee7077766e76d7c26e878f4cc76c6 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 5d1e25f6d0d9688d4961c2d85a6efb014182d5d0 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 9599e2a3f1e6..079672e0747a 100644 /* Create MODULE_ALIAS() statements. -- -2.28.0 +2.29.2 -From 04c0ee68e67efe9a33446ae8f02f108c7de051dc Mon Sep 17 00:00:00 2001 +From 10e60e8efb629e95fb3984e807a929a416bbc75f 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 079672e0747a..23d9d368fde9 100644 ADD(alias, "f", match_flags & SSAM_MATCH_FUNCTION, function); -- -2.28.0 +2.29.2 diff --git a/patches/5.8/0007-i2c-core-Restore-acpi_walk_dep_device_list-getting-c.patch b/patches/5.8/0007-i2c-core-Restore-acpi_walk_dep_device_list-getting-c.patch deleted file mode 100644 index 07664c55f..000000000 --- a/patches/5.8/0007-i2c-core-Restore-acpi_walk_dep_device_list-getting-c.patch +++ /dev/null @@ -1,70 +0,0 @@ -From ccc695a9170dc8469d8637568129d4ed8780448c 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 60b9cb51d5f7..72f7305ab3fc 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); - } - - const struct acpi_device_id * -@@ -764,7 +774,6 @@ int i2c_acpi_install_space_handler(struct i2c_adapter *adapter) - return -ENOMEM; - } - -- acpi_walk_dep_device_list(handle); - return 0; - } - --- -2.28.0 -