diff --git a/configs/surface-5.4.config b/configs/surface-5.4.config index da486c257..55937aa53 100644 --- a/configs/surface-5.4.config +++ b/configs/surface-5.4.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.4/0001-surface3-power.patch b/patches/5.4/0001-surface3-power.patch index 2c380fcbd..79364cdc2 100644 --- a/patches/5.4/0001-surface3-power.patch +++ b/patches/5.4/0001-surface3-power.patch @@ -1,4 +1,4 @@ -From 34fd11b0f32b5c15987dabbb7fcf6590341bd62c Mon Sep 17 00:00:00 2001 +From 920bb46ce522c4e28237071fb3a93ec17ab56e49 Mon Sep 17 00:00:00 2001 From: qzed Date: Tue, 17 Sep 2019 17:17:56 +0200 Subject: [PATCH] platform/x86: Surface 3 battery platform operation region @@ -653,5 +653,5 @@ index 000000000000..e0af01a60302 +MODULE_DESCRIPTION("mshw0011 driver"); +MODULE_LICENSE("GPL v2"); -- -2.28.0 +2.29.2 diff --git a/patches/5.4/0002-surface3-oemb.patch b/patches/5.4/0002-surface3-oemb.patch index a3ec3cf8f..ddbde8c20 100644 --- a/patches/5.4/0002-surface3-oemb.patch +++ b/patches/5.4/0002-surface3-oemb.patch @@ -1,4 +1,4 @@ -From afc394c0540ca91a9ee31c84c500a90d82cf34ac Mon Sep 17 00:00:00 2001 +From 407b720283ead9bd4a5e6c3a5aa3c8c5fdbf2787 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 d0fb43c2b9f6..0e938713cb13 100644 }; -- -2.28.0 +2.29.2 diff --git a/patches/5.4/0003-wifi.patch b/patches/5.4/0003-wifi.patch index 2a1450bc4..fc2b15834 100644 --- a/patches/5.4/0003-wifi.patch +++ b/patches/5.4/0003-wifi.patch @@ -1,4 +1,4 @@ -From d87415b12bf41348cb809096313ec152acf006a6 Mon Sep 17 00:00:00 2001 +From 57064d3d4c35d097736f2cdf8ef6f1e7562da433 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 f7ce9b6db6b4..72d0c01ff359 100644 static inline int -- -2.28.0 +2.29.2 -From 485e47491fdf3380380351eae257289cc3a45fe2 Mon Sep 17 00:00:00 2001 +From 742fc7984e000b2d3c5d39235da39e5fe3bf18af 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 9e9258cd222851f0a4926139fcc53233b4cf4707 Mon Sep 17 00:00:00 2001 +From c8bf3ea2f7e6a382afadf7feda8998614619f652 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 2b2bb6db9675605dc6812ae1aa0ad62b93dcb23d Mon Sep 17 00:00:00 2001 +From 9083372666e4be1331463944fdc85f9caf077236 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 d7a83ffb7faaed1215873f48c9cd541ff911f41d Mon Sep 17 00:00:00 2001 +From b61057bb3c00b1a45e706ec25fedf92f585f2196 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 9b8c3430c962d4bcf7066da6bfc9a5522330aceb Mon Sep 17 00:00:00 2001 +From fb2339ab3530a8007e8a0293fae1f50c41ee25b6 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 d14e55e3c9da..5894566ec480 100644 adapter->is_up = false; -- -2.28.0 +2.29.2 -From 74c1334d28d50df496d030bb1dc5275bbb657643 Mon Sep 17 00:00:00 2001 +From 3fc9d46d5b02f346a227e48546618c85c01f4635 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 263d918767bd..bd6791dc3a0f 100644 return 0; } -- -2.28.0 +2.29.2 -From 8129314ab33e169a93c943d01b286156fd183551 Mon Sep 17 00:00:00 2001 +From b2c53074824e2f54b14ef972e0b947d5e94d1961 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 bd6791dc3a0f..d7ff898c1767 100644 if (!adapter->devdump_data) { mwifiex_dbg(adapter, ERROR, -- -2.28.0 +2.29.2 -From 9a7695152a4e6a911655f78dc8cad996710cb6cc Mon Sep 17 00:00:00 2001 +From b7071b89e24c1cf97dd4190e1cb55e35cba74bbc 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 07b927908d6034e00fb5109b0713f73aeef2df42 Mon Sep 17 00:00:00 2001 +From dfd218b1815eff411722992ebd18c1d7dd00c66e 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 9e6dc289ec3e..20f5ee3fe7e3 100644 } -- -2.28.0 +2.29.2 -From 043a356af095e1eaebda74fddba83be8bdc2b621 Mon Sep 17 00:00:00 2001 +From bb33cb017be76dff82f01e05ecb27581fdbdb2a8 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 20f5ee3fe7e3..8020a2929069 100644 } -- -2.28.0 +2.29.2 -From eeab41594e4e420118326788215d102d8b5d1684 Mon Sep 17 00:00:00 2001 +From 2971cc13ce677cb3512beb4ab96d39a4b2937443 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 4ed10cf82f9a..ed0fffb9eba6 100644 if (drcs) { -- -2.28.0 +2.29.2 diff --git a/patches/5.4/0004-ipts.patch b/patches/5.4/0004-ipts.patch index 2494ed732..14f113e73 100644 --- a/patches/5.4/0004-ipts.patch +++ b/patches/5.4/0004-ipts.patch @@ -1,4 +1,4 @@ -From 4596b258b819644d212a9f8e4d20ebc4456c187d Mon Sep 17 00:00:00 2001 +From f46f184003cfb534077e48c4f56346af3d84a399 Mon Sep 17 00:00:00 2001 From: Dorian Stoll Date: Mon, 27 Jan 2020 21:16:20 +0100 Subject: [PATCH] mei: Add IPTS device IDs @@ -51,9 +51,9 @@ index 75ab2ffbf235..78790904d77c 100644 {MEI_PCI_DEVICE(MEI_DEV_ID_TGP_LP, MEI_ME_PCH12_CFG)}, -- -2.28.0 +2.29.2 -From 87fea2c77d752224b06dc4beee817fa26f2b1356 Mon Sep 17 00:00:00 2001 +From deb28840b7d00286d576e36e6493b1d3c209808c Mon Sep 17 00:00:00 2001 From: Dorian Stoll Date: Fri, 20 Dec 2019 23:15:58 +0100 Subject: [PATCH] uapi: Add MEI bus ID @@ -77,9 +77,9 @@ index 9a61c28ed3ae..47fc20975245 100644 /* * MT_TOOL types -- -2.28.0 +2.29.2 -From 8c1f8a2a71c1c382d8b1606ff57a9f72cba1e5a6 Mon Sep 17 00:00:00 2001 +From 7c1cd0659188f679bc0e70271e513cbeb8cca983 Mon Sep 17 00:00:00 2001 From: Dorian Stoll Date: Mon, 27 Jan 2020 21:22:42 +0100 Subject: [PATCH] input: Add support for Intel Precise Touch & Stylus @@ -2085,5 +2085,5 @@ index 000000000000..5b93add1eac2 + +#endif /* _IPTS_STYLUS_H_ */ -- -2.28.0 +2.29.2 diff --git a/patches/5.4/0005-surface-gpe.patch b/patches/5.4/0005-surface-gpe.patch index 8ce4bcd4e..e3cbb31fd 100644 --- a/patches/5.4/0005-surface-gpe.patch +++ b/patches/5.4/0005-surface-gpe.patch @@ -1,4 +1,4 @@ -From ea2243f30da79e2b97bab0140a9b28c049e5ca25 Mon Sep 17 00:00:00 2001 +From 2f8c97dac94f186ec0e48105d630679b4bd66d0d 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 6dd955ad9bf1..5db303f43bda 100644 obj-$(CONFIG_INTEL_TELEMETRY) += intel_telemetry_core.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.4/0006-surface-sam-over-hid.patch b/patches/5.4/0006-surface-sam-over-hid.patch index f4f511bb4..2bcc51894 100644 --- a/patches/5.4/0006-surface-sam-over-hid.patch +++ b/patches/5.4/0006-surface-sam-over-hid.patch @@ -1,4 +1,4 @@ -From 2f88ea832d68be4bd47a3702389371dcfab92dd5 Mon Sep 17 00:00:00 2001 +From a5ebb9332254f494203c6d3a576df789e6074495 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 ce70b5288472..5df647c4d9a5 100644 +index c70983780ae7..1c90651161a6 100644 --- a/drivers/i2c/i2c-core-acpi.c +++ b/drivers/i2c/i2c-core-acpi.c -@@ -582,6 +582,28 @@ static int acpi_gsb_i2c_write_bytes(struct i2c_client *client, +@@ -592,6 +592,28 @@ static int acpi_gsb_i2c_write_bytes(struct i2c_client *client, return (ret == 1) ? 0 : -EIO; } @@ -86,7 +86,7 @@ index ce70b5288472..5df647c4d9a5 100644 static acpi_status i2c_acpi_space_handler(u32 function, acpi_physical_address command, u32 bits, u64 *value64, -@@ -683,6 +705,19 @@ i2c_acpi_space_handler(u32 function, acpi_physical_address command, +@@ -693,6 +715,19 @@ i2c_acpi_space_handler(u32 function, acpi_physical_address command, } break; @@ -107,9 +107,9 @@ index ce70b5288472..5df647c4d9a5 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 94a75b92c382a5b72e7114f8b4bb593a5734534e Mon Sep 17 00:00:00 2001 +From aee5409945fc3b0d30d8407e60aa3095e438dd37 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.4/0007-surface-sam.patch b/patches/5.4/0007-surface-sam.patch index 69156271a..f1ec3ae0d 100644 --- a/patches/5.4/0007-surface-sam.patch +++ b/patches/5.4/0007-surface-sam.patch @@ -1,4 +1,4 @@ -From 04ed5900788dccac4296278208874e58452be706 Mon Sep 17 00:00:00 2001 +From ac17033208f929d076dd302839c9473648d2958d Mon Sep 17 00:00:00 2001 From: qzed Date: Mon, 26 Aug 2019 01:11:08 +0200 Subject: [PATCH] ACPI: Fix buffer/integer type mismatch @@ -58,9 +58,9 @@ index d3d2dbfba680..0b7f617a6e9b 100644 buffer_desc = acpi_ut_create_buffer_object(buffer_length); if (!buffer_desc) { -- -2.28.0 +2.29.2 -From 65ad00e9a18e592127f00fdd17b5b58fef13d8f2 Mon Sep 17 00:00:00 2001 +From a757e866a23d47d85adda8e734fd9f4183d12fac Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Tue, 24 Sep 2019 17:38:12 +0200 Subject: [PATCH] serdev: Add ACPI devices by ResourceSource field @@ -246,9 +246,250 @@ index a9719858c950..ce5309d00280 100644 if (!ctrl->serdev) return -ENODEV; -- -2.28.0 +2.29.2 -From 5a29607b9de9a2a278b7f431c242734ba00511c0 Mon Sep 17 00:00:00 2001 +From 09933f9205430ef74c98c492d40a3423feed3165 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 b1b2c8ddbc92..15566ec8f75d 100644 +--- a/drivers/pci/pci.c ++++ b/drivers/pci/pci.c +@@ -2611,6 +2611,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 +@@ -2651,10 +2677,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 0e455871fcc6d9ea928be2846e137c4f5c48dd15 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 15566ec8f75d..6839053db5a5 100644 +--- a/drivers/pci/pci.c ++++ b/drivers/pci/pci.c +@@ -1663,7 +1663,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; + + /* +@@ -1673,9 +1673,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 fd5cd1327bf1d3ad4afae59883c72377e1c4afd3 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 8bfee557e50e..460032b4e950 100644 +--- a/Documentation/ABI/testing/sysfs-bus-pci ++++ b/Documentation/ABI/testing/sysfs-bus-pci +@@ -347,3 +347,12 @@ Description: + If the device has any Peer-to-Peer memory registered, this + file contains a '1' if the memory has been published for + use outside the driver that owns the device. ++ ++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 e401f040f157..418927872ae6 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) +@@ -598,6 +609,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 b0dc7bcd746fd167d16f35f298c099797be5f7a3 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 @@ -348,9 +589,9 @@ index c91eba751804..bc06f7631200 100644 /* Create MODULE_ALIAS() statements. -- -2.28.0 +2.29.2 -From f0aeeaafb0ac9d8638030a29e3bf3987abe3ec37 Mon Sep 17 00:00:00 2001 +From 21fd6098eee8750891488d2e1e99d36bea9f17ac 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 @@ -389,11 +630,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 | 605 ++++ - .../clients/surface_battery.c | 1196 ++++++++ - .../surface_aggregator/clients/surface_dtx.c | 1270 ++++++++ + .../clients/surface_aggregator_registry.c | 609 ++++ + .../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 ++ @@ -415,7 +656,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, 19778 insertions(+), 7 deletions(-) + 48 files changed, 18772 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 @@ -4339,10 +4580,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..c7e84316b88b +index 000000000000..282f4409535b --- /dev/null +++ b/drivers/misc/surface_aggregator/clients/surface_aggregator_registry.c -@@ -0,0 +1,605 @@ +@@ -0,0 +1,609 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Surface System Aggregator Module (SSAM) client device registry. @@ -4733,7 +4974,7 @@ index 000000000000..c7e84316b88b + 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; @@ -4766,6 +5007,8 @@ index 000000000000..c7e84316b88b + if (!hub) + return -ENOMEM; + ++ mutex_init(&hub->lock); ++ + hub->sdev = sdev; + hub->devices = desc; + hub->state = SSAM_BASE_HUB_UNINITIALIZED; @@ -4813,6 +5056,8 @@ index 000000000000..c7e84316b88b + + 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[] = { @@ -4950,10 +5195,10 @@ index 000000000000..c7e84316b88b +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. @@ -5973,7 +6218,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); +} + + @@ -6152,10 +6396,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. @@ -6345,7 +6589,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) @@ -6597,6 +6844,7 @@ index 000000000000..1ac1208edd13 + up_write(&client->ddev->client_lock); + + sdtx_device_put(client->ddev); ++ mutex_destroy(&client->read_lock); + kfree(client); + + return 0; @@ -7215,6 +7463,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); + @@ -7428,7 +7679,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 @@ @@ -8114,7 +8365,7 @@ index 000000000000..567da224e60e + +/* -- PM ops. --------------------------------------------------------------- */ + -+#ifdef CONFIG_PM ++#ifdef CONFIG_PM_SLEEP + +static int surface_hid_suspend(struct device *dev) +{ @@ -8175,11 +8426,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). --------------------------------------------------- */ @@ -8359,1294 +8610,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 @@ -20375,7 +19610,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 @@ @@ -20473,7 +19708,7 @@ index 000000000000..d88cabfb8dd7 + __u16 length; + __u16 code; + __u8 data[]; -+} __packed; ++} __attribute__((__packed__)); + +/** + * enum sdtx_event_code - Code describing the type of an event. @@ -20509,7 +19744,7 @@ index 000000000000..d88cabfb8dd7 +struct sdtx_base_info { + __u16 state; + __u16 base_id; -+} __packed; ++} __attribute__((__packed__)); + + +/* IOCTLs */ @@ -20576,5 +19811,5 @@ index bc06f7631200..c86de633d7ea 100644 ADD(alias, "f", match_flags & SSAM_MATCH_FUNCTION, function); -- -2.28.0 +2.29.2 diff --git a/patches/5.4/0008-i2c-core-Restore-acpi_walk_dep_device_list-getting-c.patch b/patches/5.4/0008-i2c-core-Restore-acpi_walk_dep_device_list-getting-c.patch deleted file mode 100644 index 0c234a587..000000000 --- a/patches/5.4/0008-i2c-core-Restore-acpi_walk_dep_device_list-getting-c.patch +++ /dev/null @@ -1,70 +0,0 @@ -From 7e11df4d0af5a21f93c8af446bcfd24432a2bfa6 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 5df647c4d9a5..1c90651161a6 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 * -@@ -772,7 +782,6 @@ int i2c_acpi_install_space_handler(struct i2c_adapter *adapter) - return -ENOMEM; - } - -- acpi_walk_dep_device_list(handle); - return 0; - } - --- -2.28.0 -