From 4383b106db5c3c75a1a70d4bdd95e77a596bdd65 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Wed, 4 Nov 2020 01:03:43 +0100 Subject: [PATCH] Update v4.19 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/69ddd7cbb49ba09943ea055f298c60e9ef97a664 [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-4.19.config | 1 - patches/4.19/0001-surface3-power.patch | 4 +- .../0002-surface3-touchscreen-dma-fix.patch | 4 +- patches/4.19/0003-surface3-oemb.patch | 4 +- patches/4.19/0004-surface-buttons.patch | 16 +- patches/4.19/0005-suspend.patch | 4 +- patches/4.19/0006-ipts.patch | 4 +- patches/4.19/0007-wifi.patch | 52 +- patches/4.19/0008-surface-gpe.patch | 19 +- patches/4.19/0009-surface-sam-over-hid.patch | 14 +- patches/4.19/0010-surface-sam.patch | 5399 +++++++++++++---- ...-acpi_walk_dep_device_list-getting-c.patch | 70 - ...-acpi_walk_dep_device_list-getting-c.patch | 1 - pkg/arch/kernel-lts/PKGBUILD | 24 +- 14 files changed, 4261 insertions(+), 1355 deletions(-) delete mode 100644 patches/4.19/0011-i2c-core-Restore-acpi_walk_dep_device_list-getting-c.patch delete mode 120000 pkg/arch/kernel-lts/0011-i2c-core-Restore-acpi_walk_dep_device_list-getting-c.patch diff --git a/configs/surface-4.19.config b/configs/surface-4.19.config index 5a07ea74a..e53764185 100644 --- a/configs/surface-4.19.config +++ b/configs/surface-4.19.config @@ -7,7 +7,6 @@ CONFIG_INTEL_IPTS_SURFACE=m # # 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/4.19/0001-surface3-power.patch b/patches/4.19/0001-surface3-power.patch index 4dfc108fc..5d046ef03 100644 --- a/patches/4.19/0001-surface3-power.patch +++ b/patches/4.19/0001-surface3-power.patch @@ -1,4 +1,4 @@ -From 7712e7c7b39ac4af2c8d4d5a9a22ecc0d5a25077 Mon Sep 17 00:00:00 2001 +From 499a3fc8d3eaecb325078e20ab9af8ae3db9869e Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sat, 28 Sep 2019 18:00:43 +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/4.19/0002-surface3-touchscreen-dma-fix.patch b/patches/4.19/0002-surface3-touchscreen-dma-fix.patch index 97ea2bd21..b735874ad 100644 --- a/patches/4.19/0002-surface3-touchscreen-dma-fix.patch +++ b/patches/4.19/0002-surface3-touchscreen-dma-fix.patch @@ -1,4 +1,4 @@ -From 6893037808c47ada1b0bf8705ebe7f60dfd600b0 Mon Sep 17 00:00:00 2001 +From 9e03012c9a960d8874b0e73715ea5e874423f1a5 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Sun, 5 Jul 2020 14:56:20 +0300 Subject: [PATCH] dmaengine: dw: Initialize channel before each transfer @@ -85,5 +85,5 @@ index 055d83b6cb68..acf64302a2b2 100644 channel_clear_bit(dw, MASK.XFER, dwc->mask); channel_clear_bit(dw, MASK.BLOCK, dwc->mask); -- -2.28.0 +2.29.2 diff --git a/patches/4.19/0003-surface3-oemb.patch b/patches/4.19/0003-surface3-oemb.patch index ef6fa9ff5..8d4d8ff65 100644 --- a/patches/4.19/0003-surface3-oemb.patch +++ b/patches/4.19/0003-surface3-oemb.patch @@ -1,4 +1,4 @@ -From bbc85da670e31aa65b92bead468c33f5d50ff55b Mon Sep 17 00:00:00 2001 +From 003010b5d9a3b27eaa95cc14c330173060b14c07 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 91bb99b69601..8418938b32ad 100644 }; -- -2.28.0 +2.29.2 diff --git a/patches/4.19/0004-surface-buttons.patch b/patches/4.19/0004-surface-buttons.patch index aea0a8722..efd24270e 100644 --- a/patches/4.19/0004-surface-buttons.patch +++ b/patches/4.19/0004-surface-buttons.patch @@ -1,4 +1,4 @@ -From f6d0899d982873dad26c280ebc52be5470556b30 Mon Sep 17 00:00:00 2001 +From 70d1cb3eb11fe9b78fa193570d037428b6dec4ea Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sat, 27 Jul 2019 17:51:37 +0200 Subject: [PATCH] platform/x86: surfacepro3_button: Fix device check @@ -90,9 +90,9 @@ index 1b491690ce07..96627627060e 100644 if (!button) return -ENOMEM; -- -2.28.0 +2.29.2 -From a8202a8b7e876c56055d62ab2dea519af0071297 Mon Sep 17 00:00:00 2001 +From 8fc9430c6f015abdc2bfb99606268741260e4ce7 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sat, 27 Jul 2019 17:52:01 +0200 Subject: [PATCH] Input: soc_button_array - Add support for newer surface @@ -295,9 +295,9 @@ index 55cd6e0b409c..8f21c062c85d 100644 }; -- -2.28.0 +2.29.2 -From 0f703888a68cbdbd9bafae8b601a7180c8126eb8 Mon Sep 17 00:00:00 2001 +From 1b07afa7a3f18c5f9dba5e75d699041b712bd879 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 5 Oct 2019 14:11:58 +0200 Subject: [PATCH] Input: soc_button_array - partial revert of support for newer @@ -384,9 +384,9 @@ index 8f21c062c85d..5983733d78dd 100644 } -- -2.28.0 +2.29.2 -From 38ad6b1492e4126a664f6247bd7dc8ee87a10c76 Mon Sep 17 00:00:00 2001 +From 266031a4c24c8d9b91774c80fb8e98cb9dacda54 Mon Sep 17 00:00:00 2001 From: "Tsuchiya Yuto (kitakar5525)" Date: Mon, 11 May 2020 17:40:21 +0900 Subject: [PATCH] Input: soc_button_array - fix Wdiscarded-qualifiers for @@ -436,5 +436,5 @@ index 5983733d78dd..c564ea99f47d 100644 return 0; } -- -2.28.0 +2.29.2 diff --git a/patches/4.19/0005-suspend.patch b/patches/4.19/0005-suspend.patch index fc40fef64..f57e001b1 100644 --- a/patches/4.19/0005-suspend.patch +++ b/patches/4.19/0005-suspend.patch @@ -1,4 +1,4 @@ -From 8273cec8ad42f5a6d2349347878331069c296f07 Mon Sep 17 00:00:00 2001 +From cacf4579399486621367171238a82ec6df64bf16 Mon Sep 17 00:00:00 2001 From: kitakar5525 <34676735+kitakar5525@users.noreply.github.com> Date: Sat, 28 Sep 2019 17:48:21 +0200 Subject: [PATCH] nvme: Backport changes for suspend @@ -330,5 +330,5 @@ index 2fda9893962d..ec6c48ecd7d5 100644 #ifdef CONFIG_PCIEAER -- -2.28.0 +2.29.2 diff --git a/patches/4.19/0006-ipts.patch b/patches/4.19/0006-ipts.patch index 2c98f62e0..425eab53a 100644 --- a/patches/4.19/0006-ipts.patch +++ b/patches/4.19/0006-ipts.patch @@ -1,4 +1,4 @@ -From 067c4fbb383132758816350e07d5003b48d14fda Mon Sep 17 00:00:00 2001 +From cc37e06fbfa6b8e97b2059865fc328a983e9c0b2 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sat, 28 Sep 2019 17:58:17 +0200 Subject: [PATCH] Add support for Intel IPTS touch devices @@ -7310,5 +7310,5 @@ index 000000000000..f229a3436851 + +#endif // IPTS_H -- -2.28.0 +2.29.2 diff --git a/patches/4.19/0007-wifi.patch b/patches/4.19/0007-wifi.patch index 3db3a180e..d557f1b7a 100644 --- a/patches/4.19/0007-wifi.patch +++ b/patches/4.19/0007-wifi.patch @@ -1,4 +1,4 @@ -From 80f7516fb978e63b6a7e04db4e774d5496071509 Mon Sep 17 00:00:00 2001 +From 06f303aa916cda090b16dc65f3e0619ae1d1f9f1 Mon Sep 17 00:00:00 2001 From: Chuhong Yuan Date: Wed, 24 Jul 2019 19:27:45 +0800 Subject: [PATCH] mwifiex: pcie: Use dev_get_drvdata @@ -47,9 +47,9 @@ index 991b9cc18000..2aa0436d3548 100644 if (!card->adapter) { dev_err(dev, "adapter structure is not valid\n"); -- -2.28.0 +2.29.2 -From ed5d12e11559f0340ea3e73283a33ddb0b410e4c Mon Sep 17 00:00:00 2001 +From 2b3f03f528381d8efdb83d43f4d9d295dcc09f1b 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 @@ -204,9 +204,9 @@ index f7ce9b6db6b4..72d0c01ff359 100644 static inline int -- -2.28.0 +2.29.2 -From 45ddc398a7d6afa875840ab9f4fb920da3b3eb0f Mon Sep 17 00:00:00 2001 +From 14e3a0b204c5a998b5b0873757c551e934411069 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 @@ -412,9 +412,9 @@ index 000000000000..5326ae7e5671 + +void mwifiex_initialize_quirks(struct pcie_service_card *card); -- -2.28.0 +2.29.2 -From 73c434f78849f6483b76719748abef519a20faa7 Mon Sep 17 00:00:00 2001 +From c2f0354a1f66c24ce1d7cba579601a3a677dcad3 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+ @@ -613,9 +613,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 68d4b66ab18907af945a39efb8bd02666edf073b Mon Sep 17 00:00:00 2001 +From 334cc2d7ef55aefc664a1652a10b07ca3892a9e6 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 @@ -792,9 +792,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 570dab9301a4f95fc96f635b3d58d55a39ba2fcf Mon Sep 17 00:00:00 2001 +From 1bacc3349013e74daeda5ffd9b79eaf8d1767784 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 @@ -854,9 +854,9 @@ index f0a6fa0a7ae5..34dcd84f02a6 100644 .ident = "Surface Pro 3", .matches = { -- -2.28.0 +2.29.2 -From 34c2ccb41cddec7c3346d11808008d2c00897c8d Mon Sep 17 00:00:00 2001 +From d6938cf12fc888775a98a17f3a6621e9250ee45e 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 @@ -931,9 +931,9 @@ index e48b47f42554..ceac611ef086 100644 if (adapter->if_ops.down_dev) -- -2.28.0 +2.29.2 -From 2c363d783f3605142464fa5049101a76b01df40d Mon Sep 17 00:00:00 2001 +From b38c4b6616f13cfd2f02ee97a3bd34562b40a97e 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 @@ -1073,9 +1073,9 @@ index 290427c98630..d80eb18fb0d1 100644 return 0; } -- -2.28.0 +2.29.2 -From e29ab6dce0e19ec82c04421da520d6a8cd4a7f17 Mon Sep 17 00:00:00 2001 +From fc4220fde9f826159b3d9e2519652b37da473089 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 @@ -1122,9 +1122,9 @@ index d80eb18fb0d1..ea766584d3b7 100644 if (!adapter->devdump_data) { mwifiex_dbg(adapter, ERROR, -- -2.28.0 +2.29.2 -From 3cf8c0e5130ca4accf9548c6851d08ebf73b2fc6 Mon Sep 17 00:00:00 2001 +From 34df63e9063f14a1e61f7602c61b498c93ec9ed5 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+ @@ -1277,9 +1277,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 52e3edee84ced375bb626773318578f3fd020194 Mon Sep 17 00:00:00 2001 +From 0c81c19f58f53ae517560ccf93b1ea773cccc98c 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 @@ -1339,9 +1339,9 @@ index 650191db25cb..dd1f08a2325f 100644 } -- -2.28.0 +2.29.2 -From 2a4cdeb78a41d90ee8062d4ed03ad0acd7b83c71 Mon Sep 17 00:00:00 2001 +From a31c76382e21d9b4cfe82a6382124f29f670383c 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 @@ -1374,9 +1374,9 @@ index dd1f08a2325f..ee88da92b97c 100644 } -- -2.28.0 +2.29.2 -From 13febaae5f0d645765c83a0c4141514434458b39 Mon Sep 17 00:00:00 2001 +From 5c67ce2123bfdbb8bf281cc2bc49b7aa2afa7e6b 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 @@ -1422,5 +1422,5 @@ index 4ed10cf82f9a..ed0fffb9eba6 100644 if (drcs) { -- -2.28.0 +2.29.2 diff --git a/patches/4.19/0008-surface-gpe.patch b/patches/4.19/0008-surface-gpe.patch index 49b3d7181..906dfcfdd 100644 --- a/patches/4.19/0008-surface-gpe.patch +++ b/patches/4.19/0008-surface-gpe.patch @@ -1,4 +1,4 @@ -From a80e327970c6f03b0516fd354f2df0563b1497f2 Mon Sep 17 00:00:00 2001 +From 8d21f4f4f4846dcc0bfffb04ac334282e60b0509 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 | 299 +++++++++++++++++++++++++++++ - 3 files changed, 309 insertions(+) + drivers/platform/x86/surface_gpe.c | 302 +++++++++++++++++++++++++++++ + 3 files changed, 312 insertions(+) create mode 100644 drivers/platform/x86/surface_gpe.c diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig @@ -71,14 +71,17 @@ index 2ea90039a3e4..49238e9d4abf 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..451ca1f981fe +index 000000000000..8e8f3ea7c864 --- /dev/null +++ b/drivers/platform/x86/surface_gpe.c -@@ -0,0 +1,299 @@ +@@ -0,0 +1,302 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* -+ * Pseudo device and driver to enable wakeup from suspend via the lid by -+ * properly configuring the respective GPEs on Microsoft Surface devices. ++ * 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 + */ + +#include @@ -375,5 +378,5 @@ index 000000000000..451ca1f981fe +MODULE_ALIAS("dmi:*:svnMicrosoftCorporation:pnSurfaceLaptop2:*"); +MODULE_ALIAS("dmi:*:svnMicrosoftCorporation:pnSurfaceLaptop3:*"); -- -2.28.0 +2.29.2 diff --git a/patches/4.19/0009-surface-sam-over-hid.patch b/patches/4.19/0009-surface-sam-over-hid.patch index 82a867dc9..91aac942f 100644 --- a/patches/4.19/0009-surface-sam-over-hid.patch +++ b/patches/4.19/0009-surface-sam-over-hid.patch @@ -1,4 +1,4 @@ -From bcb8584fbaa4854a8bcfc35c167712df1230cd5a Mon Sep 17 00:00:00 2001 +From 57296dc70cc52019878857232e94db3f1a3569cf 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 eb0569359387..c2b5a2aca731 100644 +index 8ba4122fb340..f9a24b56fec0 100644 --- a/drivers/i2c/i2c-core-acpi.c +++ b/drivers/i2c/i2c-core-acpi.c -@@ -532,6 +532,28 @@ static int acpi_gsb_i2c_write_bytes(struct i2c_client *client, +@@ -542,6 +542,28 @@ static int acpi_gsb_i2c_write_bytes(struct i2c_client *client, return (ret == 1) ? 0 : -EIO; } @@ -86,7 +86,7 @@ index eb0569359387..c2b5a2aca731 100644 static acpi_status i2c_acpi_space_handler(u32 function, acpi_physical_address command, u32 bits, u64 *value64, -@@ -639,6 +661,19 @@ i2c_acpi_space_handler(u32 function, acpi_physical_address command, +@@ -649,6 +671,19 @@ i2c_acpi_space_handler(u32 function, acpi_physical_address command, } break; @@ -107,9 +107,9 @@ index eb0569359387..c2b5a2aca731 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 53e895a0bfdf74dddb2f295f4d4e1ab93229fc40 Mon Sep 17 00:00:00 2001 +From 338675ab867f045eff4106fde8f8f941e3ad9352 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/4.19/0010-surface-sam.patch b/patches/4.19/0010-surface-sam.patch index f35c51ecd..d0b66a324 100644 --- a/patches/4.19/0010-surface-sam.patch +++ b/patches/4.19/0010-surface-sam.patch @@ -1,4 +1,4 @@ -From 5d78acea2443324844d4535f30e1026fb14799fc Mon Sep 17 00:00:00 2001 +From be77582b7b07355bfcc4cac4da80f2da43c4f097 Mon Sep 17 00:00:00 2001 From: qzed Date: Mon, 26 Aug 2019 01:15:40 +0200 Subject: [PATCH] ACPI: Fix buffer/integer type mismatch @@ -79,9 +79,9 @@ index b272c329d45d..cf547883a993 100644 } else { /* IPMI */ -- -2.28.0 +2.29.2 -From 5a933ea960eb31d07159b624e74c243f1bfbef76 Mon Sep 17 00:00:00 2001 +From 4331e6ee0f21ce7e51c03325d3693238203af948 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 @@ -267,9 +267,3992 @@ index c66a04d24f1d..1b18d12d217f 100644 if (!ctrl->serdev) return -ENODEV; -- -2.28.0 +2.29.2 -From 632b5dc29badc5b21510aa8d6f3a436d9f452dc9 Mon Sep 17 00:00:00 2001 +From fa8c370c44eaef3cd75200b51247a04c12b779d7 Mon Sep 17 00:00:00 2001 +From: Lukas Wunner +Date: Tue, 31 Jul 2018 07:50:37 +0200 +Subject: [PATCH] PCI: pciehp: Differentiate between surprise and safe removal + +When removing PCI devices below a hotplug bridge, pciehp marks them as +disconnected if the card is no longer present in the slot or it quiesces +them if the card is still present (by disabling INTx interrupts, bus +mastering and SERR# reporting). + +To detect whether the card is still present, pciehp checks the Presence +Detect State bit in the Slot Status register. The problem with this +approach is that even if the card is present, the link to it may be +down, and it that case it would be better to mark the devices as +disconnected instead of trying to quiesce them. Moreover, if the card +in the slot was quickly replaced by another one, the Presence Detect +State bit would be set, yet trying to quiesce the new card's devices +would be wrong and the correct thing to do is to mark the previous +card's devices as disconnected. + +Instead of looking at the Presence Detect State bit, it is better to +differentiate whether the card was surprise removed versus safely +removed (via sysfs or an Attention Button press). On surprise removal, +the devices should be marked as disconnected, whereas on safe removal it +is correct to quiesce the devices. + +The knowledge whether a surprise removal or a safe removal is at hand +does exist further up in the call stack: A surprise removal is +initiated by pciehp_handle_presence_or_link_change(), a safe removal by +pciehp_handle_disable_request(). + +Pass that information down to pciehp_unconfigure_device() and use it in +lieu of the Presence Detect State bit. While there, add kernel-doc to +pciehp_unconfigure_device() and pciehp_configure_device(). + +Tested-by: Alexandru Gagniuc +Signed-off-by: Lukas Wunner +Signed-off-by: Bjorn Helgaas +Cc: Keith Busch + +Patchset: surface-sam +--- + drivers/pci/hotplug/pciehp.h | 2 +- + drivers/pci/hotplug/pciehp_ctrl.c | 22 +++++++++++++--------- + drivers/pci/hotplug/pciehp_pci.c | 23 ++++++++++++++++++++--- + 3 files changed, 34 insertions(+), 13 deletions(-) + +diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h +index ef6071807072..13379bc6a466 100644 +--- a/drivers/pci/hotplug/pciehp.h ++++ b/drivers/pci/hotplug/pciehp.h +@@ -183,7 +183,7 @@ void pciehp_handle_button_press(struct slot *slot); + void pciehp_handle_disable_request(struct slot *slot); + void pciehp_handle_presence_or_link_change(struct slot *slot, u32 events); + int pciehp_configure_device(struct slot *p_slot); +-void pciehp_unconfigure_device(struct slot *p_slot); ++void pciehp_unconfigure_device(struct slot *p_slot, bool presence); + void pciehp_queue_pushbutton_work(struct work_struct *work); + struct controller *pcie_init(struct pcie_device *dev); + int pcie_init_notification(struct controller *ctrl); +diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c +index c71964e29b01..60a925066b0a 100644 +--- a/drivers/pci/hotplug/pciehp_ctrl.c ++++ b/drivers/pci/hotplug/pciehp_ctrl.c +@@ -26,6 +26,9 @@ + hotplug controller logic + */ + ++#define SAFE_REMOVAL true ++#define SURPRISE_REMOVAL false ++ + static void set_slot_off(struct controller *ctrl, struct slot *pslot) + { + /* turn off slot, turn on Amber LED, turn off Green LED if supported*/ +@@ -101,12 +104,13 @@ static int board_added(struct slot *p_slot) + /** + * remove_board - Turns off slot and LEDs + * @p_slot: slot where board is being removed ++ * @safe_removal: whether the board is safely removed (versus surprise removed) + */ +-static void remove_board(struct slot *p_slot) ++static void remove_board(struct slot *p_slot, bool safe_removal) + { + struct controller *ctrl = p_slot->ctrl; + +- pciehp_unconfigure_device(p_slot); ++ pciehp_unconfigure_device(p_slot, safe_removal); + + if (POWER_CTRL(ctrl)) { + pciehp_power_off_slot(p_slot); +@@ -128,7 +132,7 @@ static void remove_board(struct slot *p_slot) + } + + static int pciehp_enable_slot(struct slot *slot); +-static int pciehp_disable_slot(struct slot *slot); ++static int pciehp_disable_slot(struct slot *slot, bool safe_removal); + + void pciehp_request(struct controller *ctrl, int action) + { +@@ -220,7 +224,7 @@ void pciehp_handle_disable_request(struct slot *slot) + slot->state = POWEROFF_STATE; + mutex_unlock(&slot->lock); + +- ctrl->request_result = pciehp_disable_slot(slot); ++ ctrl->request_result = pciehp_disable_slot(slot, SAFE_REMOVAL); + } + + void pciehp_handle_presence_or_link_change(struct slot *slot, u32 events) +@@ -247,7 +251,7 @@ void pciehp_handle_presence_or_link_change(struct slot *slot, u32 events) + if (events & PCI_EXP_SLTSTA_PDC) + ctrl_info(ctrl, "Slot(%s): Card not present\n", + slot_name(slot)); +- pciehp_disable_slot(slot); ++ pciehp_disable_slot(slot, SURPRISE_REMOVAL); + break; + default: + mutex_unlock(&slot->lock); +@@ -333,7 +337,7 @@ static int pciehp_enable_slot(struct slot *slot) + return ret; + } + +-static int __pciehp_disable_slot(struct slot *p_slot) ++static int __pciehp_disable_slot(struct slot *p_slot, bool safe_removal) + { + u8 getstatus = 0; + struct controller *ctrl = p_slot->ctrl; +@@ -347,17 +351,17 @@ static int __pciehp_disable_slot(struct slot *p_slot) + } + } + +- remove_board(p_slot); ++ remove_board(p_slot, safe_removal); + return 0; + } + +-static int pciehp_disable_slot(struct slot *slot) ++static int pciehp_disable_slot(struct slot *slot, bool safe_removal) + { + struct controller *ctrl = slot->ctrl; + int ret; + + pm_runtime_get_sync(&ctrl->pcie->port->dev); +- ret = __pciehp_disable_slot(slot); ++ ret = __pciehp_disable_slot(slot, safe_removal); + pm_runtime_put(&ctrl->pcie->port->dev); + + mutex_lock(&slot->lock); +diff --git a/drivers/pci/hotplug/pciehp_pci.c b/drivers/pci/hotplug/pciehp_pci.c +index 5c58c22e0c08..18f83e554c73 100644 +--- a/drivers/pci/hotplug/pciehp_pci.c ++++ b/drivers/pci/hotplug/pciehp_pci.c +@@ -20,6 +20,14 @@ + #include "../pci.h" + #include "pciehp.h" + ++/** ++ * pciehp_configure_device() - enumerate PCI devices below a hotplug bridge ++ * @p_slot: PCIe hotplug slot ++ * ++ * Enumerate PCI devices below a hotplug bridge and add them to the system. ++ * Return 0 on success, %-EEXIST if the devices are already enumerated or ++ * %-ENODEV if enumeration failed. ++ */ + int pciehp_configure_device(struct slot *p_slot) + { + struct pci_dev *dev; +@@ -62,9 +70,19 @@ int pciehp_configure_device(struct slot *p_slot) + return ret; + } + +-void pciehp_unconfigure_device(struct slot *p_slot) ++/** ++ * pciehp_unconfigure_device() - remove PCI devices below a hotplug bridge ++ * @p_slot: PCIe hotplug slot ++ * @presence: whether the card is still present in the slot; ++ * true for safe removal via sysfs or an Attention Button press, ++ * false for surprise removal ++ * ++ * Unbind PCI devices below a hotplug bridge from their drivers and remove ++ * them from the system. Safely removed devices are quiesced. Surprise ++ * removed devices are marked as such to prevent further accesses. ++ */ ++void pciehp_unconfigure_device(struct slot *p_slot, bool presence) + { +- u8 presence = 0; + struct pci_dev *dev, *temp; + struct pci_bus *parent = p_slot->ctrl->pcie->port->subordinate; + u16 command; +@@ -72,7 +90,6 @@ void pciehp_unconfigure_device(struct slot *p_slot) + + ctrl_dbg(ctrl, "%s: domain:bus:dev = %04x:%02x:00\n", + __func__, pci_domain_nr(parent), parent->number); +- pciehp_get_adapter_status(p_slot, &presence); + + pci_lock_rescan_remove(); + +-- +2.29.2 + +From 5790c8b33cc4b0a19a5b1c2c065baf3ba3b1c5cf Mon Sep 17 00:00:00 2001 +From: Lukas Wunner +Date: Sun, 19 Aug 2018 16:29:00 +0200 +Subject: [PATCH] PCI: pciehp: Drop unnecessary includes + +Drop the following includes from pciehp source files which no longer use +any of the included symbols: + +* in pciehp.h + in pciehp_hpc.c + Added by commit de25968cc87c ("fix more missing includes") to + accommodate for a call to signal_pending(). + The call was removed by commit 262303fe329a ("pciehp: fix wait command + completion"). + +* in pciehp_core.c + Added by historic commit f308a2dfbe63 ("PCI: add PCI Express Port Bus + Driver subsystem") to accommodate for a call to free_irq(): + https://git.kernel.org/tglx/history/c/f308a2dfbe63 + The call was removed by commit 407f452b05f9 ("pciehp: remove + unnecessary free_irq"). + +* in pciehp_core.c and pciehp_hpc.c + Added by commit 34d03419f03b ("PCIEHP: Add Electro Mechanical + Interlock (EMI) support to the PCIE hotplug driver."), + which was reverted by commit bd3d99c17039 ("PCI: Remove untested + Electromechanical Interlock (EMI) support in pciehp."). + +* in pciehp_ctrl.c, pciehp_hpc.c and pciehp_pci.c + Added by historic commit c16b4b14d980 ("PCI Hotplug: Add SHPC and PCI + Express hot-plug drivers"): + https://git.kernel.org/tglx/history/c/c16b4b14d980 + Module-related symbols were neither used back then in those files, + nor are they used today. + +* in pciehp_ctrl.c + Added by commit 5a0e3ad6af86 ("include cleanup: Update gfp.h and + slab.h includes to prepare for breaking implicit slab.h inclusion from + percpu.h") to accommodate for calls to kmalloc(). + The calls were removed by commit 0e94916e6091 ("PCI: pciehp: Handle + events synchronously"). + +* "../pci.h" in pciehp_ctrl.c + Added by historic commit 67f4660b72f2 ("PCI: ASPM patch for") to + accommodate for usage of the global variable pcie_mch_quirk: + https://git.kernel.org/tglx/history/c/67f4660b72f2 + The global variable was removed by commit 0ba379ec0fb1 ("PCI: Simplify + hotplug mch quirk"). + +Signed-off-by: Lukas Wunner +Signed-off-by: Bjorn Helgaas + +Patchset: surface-sam +--- + drivers/pci/hotplug/pciehp.h | 1 - + drivers/pci/hotplug/pciehp_core.c | 2 -- + drivers/pci/hotplug/pciehp_ctrl.c | 3 --- + drivers/pci/hotplug/pciehp_hpc.c | 3 --- + drivers/pci/hotplug/pciehp_pci.c | 1 - + 5 files changed, 10 deletions(-) + +diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h +index 13379bc6a466..48d138c59f5d 100644 +--- a/drivers/pci/hotplug/pciehp.h ++++ b/drivers/pci/hotplug/pciehp.h +@@ -19,7 +19,6 @@ + #include + #include + #include +-#include /* signal_pending() */ + #include + #include + #include +diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c +index 518c46f8e63b..8e5b0bdb16b6 100644 +--- a/drivers/pci/hotplug/pciehp_core.c ++++ b/drivers/pci/hotplug/pciehp_core.c +@@ -23,8 +23,6 @@ + #include + #include + #include "pciehp.h" +-#include +-#include + + #include "../pci.h" + +diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c +index 60a925066b0a..10e46e3cdcea 100644 +--- a/drivers/pci/hotplug/pciehp_ctrl.c ++++ b/drivers/pci/hotplug/pciehp_ctrl.c +@@ -13,13 +13,10 @@ + * + */ + +-#include + #include + #include +-#include + #include + #include +-#include "../pci.h" + #include "pciehp.h" + + /* The following routines constitute the bulk of the +diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c +index 005817e40ad3..d4b7049cbc70 100644 +--- a/drivers/pci/hotplug/pciehp_hpc.c ++++ b/drivers/pci/hotplug/pciehp_hpc.c +@@ -13,15 +13,12 @@ + */ + + #include +-#include + #include +-#include + #include + #include + #include + #include + #include +-#include + #include + + #include "../pci.h" +diff --git a/drivers/pci/hotplug/pciehp_pci.c b/drivers/pci/hotplug/pciehp_pci.c +index 18f83e554c73..c512b2ed85ed 100644 +--- a/drivers/pci/hotplug/pciehp_pci.c ++++ b/drivers/pci/hotplug/pciehp_pci.c +@@ -13,7 +13,6 @@ + * + */ + +-#include + #include + #include + #include +-- +2.29.2 + +From b83555bde6d001be3bb9e09996684da2c2518f67 Mon Sep 17 00:00:00 2001 +From: Lukas Wunner +Date: Sun, 19 Aug 2018 16:29:00 +0200 +Subject: [PATCH] PCI: pciehp: Drop hotplug_slot_ops wrappers + +pciehp's ->enable_slot, ->disable_slot, ->get_attention_status and +->reset_slot callbacks are currently implemented by wrapper functions +that do nothing else but call down to a backend function. The backends +are not called from anywhere else, so drop the wrappers and use the +backends directly as callbacks, thereby shaving off a few lines of +unnecessary code. + +No functional change intended. + +Signed-off-by: Lukas Wunner +Signed-off-by: Bjorn Helgaas + +Patchset: surface-sam +--- + drivers/pci/hotplug/pciehp.h | 8 +++--- + drivers/pci/hotplug/pciehp_core.c | 43 +++---------------------------- + drivers/pci/hotplug/pciehp_ctrl.c | 6 +++-- + drivers/pci/hotplug/pciehp_hpc.c | 8 ++++-- + 4 files changed, 18 insertions(+), 47 deletions(-) + +diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h +index 48d138c59f5d..79feeb0a6716 100644 +--- a/drivers/pci/hotplug/pciehp.h ++++ b/drivers/pci/hotplug/pciehp.h +@@ -175,8 +175,6 @@ struct controller { + #define NO_CMD_CMPL(ctrl) ((ctrl)->slot_cap & PCI_EXP_SLTCAP_NCCS) + #define PSN(ctrl) (((ctrl)->slot_cap & PCI_EXP_SLTCAP_PSN) >> 19) + +-int pciehp_sysfs_enable_slot(struct slot *slot); +-int pciehp_sysfs_disable_slot(struct slot *slot); + void pciehp_request(struct controller *ctrl, int action); + void pciehp_handle_button_press(struct slot *slot); + void pciehp_handle_disable_request(struct slot *slot); +@@ -191,7 +189,6 @@ void pcie_clear_hotplug_events(struct controller *ctrl); + int pciehp_power_on_slot(struct slot *slot); + void pciehp_power_off_slot(struct slot *slot); + void pciehp_get_power_status(struct slot *slot, u8 *status); +-void pciehp_get_attention_status(struct slot *slot, u8 *status); + + void pciehp_set_attention_status(struct slot *slot, u8 status); + void pciehp_get_latch_status(struct slot *slot, u8 *status); +@@ -203,8 +200,11 @@ void pciehp_green_led_blink(struct slot *slot); + int pciehp_check_link_status(struct controller *ctrl); + bool pciehp_check_link_active(struct controller *ctrl); + void pciehp_release_ctrl(struct controller *ctrl); +-int pciehp_reset_slot(struct slot *slot, int probe); + ++int pciehp_sysfs_enable_slot(struct hotplug_slot *hotplug_slot); ++int pciehp_sysfs_disable_slot(struct hotplug_slot *hotplug_slot); ++int pciehp_reset_slot(struct hotplug_slot *hotplug_slot, int probe); ++int pciehp_get_attention_status(struct hotplug_slot *hotplug_slot, u8 *status); + int pciehp_set_raw_indicator_status(struct hotplug_slot *h_slot, u8 status); + int pciehp_get_raw_indicator_status(struct hotplug_slot *h_slot, u8 *status); + +diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c +index 8e5b0bdb16b6..c796c4643bbc 100644 +--- a/drivers/pci/hotplug/pciehp_core.c ++++ b/drivers/pci/hotplug/pciehp_core.c +@@ -45,13 +45,9 @@ MODULE_PARM_DESC(pciehp_poll_time, "Polling mechanism frequency, in seconds"); + #define PCIE_MODULE_NAME "pciehp" + + static int set_attention_status(struct hotplug_slot *slot, u8 value); +-static int enable_slot(struct hotplug_slot *slot); +-static int disable_slot(struct hotplug_slot *slot); + static int get_power_status(struct hotplug_slot *slot, u8 *value); +-static int get_attention_status(struct hotplug_slot *slot, u8 *value); + static int get_latch_status(struct hotplug_slot *slot, u8 *value); + static int get_adapter_status(struct hotplug_slot *slot, u8 *value); +-static int reset_slot(struct hotplug_slot *slot, int probe); + + static int init_slot(struct controller *ctrl) + { +@@ -75,15 +71,15 @@ static int init_slot(struct controller *ctrl) + if (!ops) + goto out; + +- ops->enable_slot = enable_slot; +- ops->disable_slot = disable_slot; ++ ops->enable_slot = pciehp_sysfs_enable_slot; ++ ops->disable_slot = pciehp_sysfs_disable_slot; + ops->get_power_status = get_power_status; + ops->get_adapter_status = get_adapter_status; +- ops->reset_slot = reset_slot; ++ ops->reset_slot = pciehp_reset_slot; + if (MRL_SENS(ctrl)) + ops->get_latch_status = get_latch_status; + if (ATTN_LED(ctrl)) { +- ops->get_attention_status = get_attention_status; ++ ops->get_attention_status = pciehp_get_attention_status; + ops->set_attention_status = set_attention_status; + } else if (ctrl->pcie->port->hotplug_user_indicators) { + ops->get_attention_status = pciehp_get_raw_indicator_status; +@@ -134,22 +130,6 @@ static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status) + return 0; + } + +- +-static int enable_slot(struct hotplug_slot *hotplug_slot) +-{ +- struct slot *slot = hotplug_slot->private; +- +- return pciehp_sysfs_enable_slot(slot); +-} +- +- +-static int disable_slot(struct hotplug_slot *hotplug_slot) +-{ +- struct slot *slot = hotplug_slot->private; +- +- return pciehp_sysfs_disable_slot(slot); +-} +- + static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value) + { + struct slot *slot = hotplug_slot->private; +@@ -161,14 +141,6 @@ static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value) + return 0; + } + +-static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value) +-{ +- struct slot *slot = hotplug_slot->private; +- +- pciehp_get_attention_status(slot, value); +- return 0; +-} +- + static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value) + { + struct slot *slot = hotplug_slot->private; +@@ -191,13 +163,6 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) + return 0; + } + +-static int reset_slot(struct hotplug_slot *hotplug_slot, int probe) +-{ +- struct slot *slot = hotplug_slot->private; +- +- return pciehp_reset_slot(slot, probe); +-} +- + /** + * pciehp_check_presence() - synthesize event if presence has changed + * +diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c +index 10e46e3cdcea..b101f179147e 100644 +--- a/drivers/pci/hotplug/pciehp_ctrl.c ++++ b/drivers/pci/hotplug/pciehp_ctrl.c +@@ -368,8 +368,9 @@ static int pciehp_disable_slot(struct slot *slot, bool safe_removal) + return ret; + } + +-int pciehp_sysfs_enable_slot(struct slot *p_slot) ++int pciehp_sysfs_enable_slot(struct hotplug_slot *hotplug_slot) + { ++ struct slot *p_slot = hotplug_slot->private; + struct controller *ctrl = p_slot->ctrl; + + mutex_lock(&p_slot->lock); +@@ -407,8 +408,9 @@ int pciehp_sysfs_enable_slot(struct slot *p_slot) + return -ENODEV; + } + +-int pciehp_sysfs_disable_slot(struct slot *p_slot) ++int pciehp_sysfs_disable_slot(struct hotplug_slot *hotplug_slot) + { ++ struct slot *p_slot = hotplug_slot->private; + struct controller *ctrl = p_slot->ctrl; + + mutex_lock(&p_slot->lock); +diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c +index d4b7049cbc70..576362d0b1cd 100644 +--- a/drivers/pci/hotplug/pciehp_hpc.c ++++ b/drivers/pci/hotplug/pciehp_hpc.c +@@ -326,8 +326,9 @@ int pciehp_get_raw_indicator_status(struct hotplug_slot *hotplug_slot, + return 0; + } + +-void pciehp_get_attention_status(struct slot *slot, u8 *status) ++int pciehp_get_attention_status(struct hotplug_slot *hotplug_slot, u8 *status) + { ++ struct slot *slot = hotplug_slot->private; + struct controller *ctrl = slot->ctrl; + struct pci_dev *pdev = ctrl_dev(ctrl); + u16 slot_ctrl; +@@ -352,6 +353,8 @@ void pciehp_get_attention_status(struct slot *slot, u8 *status) + *status = 0xFF; + break; + } ++ ++ return 0; + } + + void pciehp_get_power_status(struct slot *slot, u8 *status) +@@ -769,8 +772,9 @@ void pcie_clear_hotplug_events(struct controller *ctrl) + * momentarily, if we see that they could interfere. Also, clear any spurious + * events after. + */ +-int pciehp_reset_slot(struct slot *slot, int probe) ++int pciehp_reset_slot(struct hotplug_slot *hotplug_slot, int probe) + { ++ struct slot *slot = hotplug_slot->private; + struct controller *ctrl = slot->ctrl; + struct pci_dev *pdev = ctrl_dev(ctrl); + u16 stat_mask = 0, ctrl_mask = 0; +-- +2.29.2 + +From b2639a0c1b7648269b338daf87ce4a149203fbe8 Mon Sep 17 00:00:00 2001 +From: Lukas Wunner +Date: Sat, 8 Sep 2018 09:59:01 +0200 +Subject: [PATCH] PCI: pciehp: Tolerate Presence Detect hardwired to zero + +The WiGig Bus Extension (WBE) specification allows tunneling PCIe over +IEEE 802.11. A product implementing this spec is the wil6210 from +Wilocity (now part of Qualcomm Atheros). It integrates a PCIe switch +with a wireless network adapter: + + 00.0-+ [1ae9:0101] Upstream Port + +-00.0-+ [1ae9:0200] Downstream Port + | +-00.0 [168c:0034] Atheros AR9462 Wireless Network Adapter + +-02.0 [1ae9:0201] Downstream Port + +-03.0 [1ae9:0201] Downstream Port + +Wirelessly attached devices presumably appear below the hotplug ports +with device ID [1ae9:0201]. Oddly, the Downstream Port [1ae9:0200] +leading to the wireless network adapter is likewise Hotplug Capable, +but has its Presence Detect State bit hardwired to zero. Even if the +Link Active bit is set, Presence Detect is zero, so this cannot be +caused by in-band presence detection but only by broken hardware. + +pciehp assumes an empty slot if Presence Detect State is zero, +regardless of Link Active being one. Consequently, up until v4.18 it +removes the wireless network adapter in pciehp_resume(). From v4.19 it +already does so in pciehp_probe(). + +Be lenient towards broken hardware and assume the slot is occupied if +Link Active is set: Introduce pciehp_card_present_or_link_active() +and use it in lieu of pciehp_get_adapter_status() everywhere, except +in pciehp_handle_presence_or_link_change() whose log messages depend +on which of Presence Detect State or Link Active is set. + +Remove the Presence Detect State check from __pciehp_enable_slot() +because it is only called if either of Presence Detect State or Link +Active is set. + +Caution: There is a possibility that broken hardware exists which has +working Presence Detect but hardwires Link Active to one. On such +hardware the slot will now incorrectly be considered always occupied. +If such hardware is discovered, this commit can be rolled back and a +quirk can be added which sets is_hotplug_bridge = 0 for [1ae9:0200]. + +Link: https://bugzilla.kernel.org/show_bug.cgi?id=200839 +Reported-and-tested-by: David Yang +Signed-off-by: Lukas Wunner +Signed-off-by: Bjorn Helgaas +Cc: Rajat Jain +Cc: Ashok Raj + +Patchset: surface-sam +--- + drivers/pci/hotplug/pciehp.h | 3 ++- + drivers/pci/hotplug/pciehp_core.c | 6 +++--- + drivers/pci/hotplug/pciehp_ctrl.c | 10 ++-------- + drivers/pci/hotplug/pciehp_hpc.c | 25 +++++++++++++++++++------ + 4 files changed, 26 insertions(+), 18 deletions(-) + +diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h +index 79feeb0a6716..d1fa2ce71ad9 100644 +--- a/drivers/pci/hotplug/pciehp.h ++++ b/drivers/pci/hotplug/pciehp.h +@@ -192,11 +192,12 @@ void pciehp_get_power_status(struct slot *slot, u8 *status); + + void pciehp_set_attention_status(struct slot *slot, u8 status); + void pciehp_get_latch_status(struct slot *slot, u8 *status); +-void pciehp_get_adapter_status(struct slot *slot, u8 *status); + int pciehp_query_power_fault(struct slot *slot); + void pciehp_green_led_on(struct slot *slot); + void pciehp_green_led_off(struct slot *slot); + void pciehp_green_led_blink(struct slot *slot); ++bool pciehp_card_present(struct controller *ctrl); ++bool pciehp_card_present_or_link_active(struct controller *ctrl); + int pciehp_check_link_status(struct controller *ctrl); + bool pciehp_check_link_active(struct controller *ctrl); + void pciehp_release_ctrl(struct controller *ctrl); +diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c +index c796c4643bbc..c052efe421f3 100644 +--- a/drivers/pci/hotplug/pciehp_core.c ++++ b/drivers/pci/hotplug/pciehp_core.c +@@ -158,7 +158,7 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) + struct pci_dev *pdev = slot->ctrl->pcie->port; + + pci_config_pm_runtime_get(pdev); +- pciehp_get_adapter_status(slot, value); ++ *value = pciehp_card_present_or_link_active(slot->ctrl); + pci_config_pm_runtime_put(pdev); + return 0; + } +@@ -176,12 +176,12 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) + static void pciehp_check_presence(struct controller *ctrl) + { + struct slot *slot = ctrl->slot; +- u8 occupied; ++ bool occupied; + + down_read(&ctrl->reset_lock); + mutex_lock(&slot->lock); + +- pciehp_get_adapter_status(slot, &occupied); ++ occupied = pciehp_card_present_or_link_active(ctrl); + if ((occupied && (slot->state == OFF_STATE || + slot->state == BLINKINGON_STATE)) || + (!occupied && (slot->state == ON_STATE || +diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c +index b101f179147e..97fa865717ec 100644 +--- a/drivers/pci/hotplug/pciehp_ctrl.c ++++ b/drivers/pci/hotplug/pciehp_ctrl.c +@@ -227,8 +227,7 @@ void pciehp_handle_disable_request(struct slot *slot) + void pciehp_handle_presence_or_link_change(struct slot *slot, u32 events) + { + struct controller *ctrl = slot->ctrl; +- bool link_active; +- u8 present; ++ bool present, link_active; + + /* + * If the slot is on and presence or link has changed, turn it off. +@@ -257,7 +256,7 @@ void pciehp_handle_presence_or_link_change(struct slot *slot, u32 events) + + /* Turn the slot on if it's occupied or link is up */ + mutex_lock(&slot->lock); +- pciehp_get_adapter_status(slot, &present); ++ present = pciehp_card_present(ctrl); + link_active = pciehp_check_link_active(ctrl); + if (!present && !link_active) { + mutex_unlock(&slot->lock); +@@ -290,11 +289,6 @@ static int __pciehp_enable_slot(struct slot *p_slot) + u8 getstatus = 0; + struct controller *ctrl = p_slot->ctrl; + +- pciehp_get_adapter_status(p_slot, &getstatus); +- if (!getstatus) { +- ctrl_info(ctrl, "Slot(%s): No adapter\n", slot_name(p_slot)); +- return -ENODEV; +- } + if (MRL_SENS(p_slot->ctrl)) { + pciehp_get_latch_status(p_slot, &getstatus); + if (getstatus) { +diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c +index 576362d0b1cd..7f4173d6771a 100644 +--- a/drivers/pci/hotplug/pciehp_hpc.c ++++ b/drivers/pci/hotplug/pciehp_hpc.c +@@ -389,13 +389,27 @@ void pciehp_get_latch_status(struct slot *slot, u8 *status) + *status = !!(slot_status & PCI_EXP_SLTSTA_MRLSS); + } + +-void pciehp_get_adapter_status(struct slot *slot, u8 *status) ++bool pciehp_card_present(struct controller *ctrl) + { +- struct pci_dev *pdev = ctrl_dev(slot->ctrl); ++ struct pci_dev *pdev = ctrl_dev(ctrl); + u16 slot_status; + + pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); +- *status = !!(slot_status & PCI_EXP_SLTSTA_PDS); ++ return slot_status & PCI_EXP_SLTSTA_PDS; ++} ++ ++/** ++ * pciehp_card_present_or_link_active() - whether given slot is occupied ++ * @ctrl: PCIe hotplug controller ++ * ++ * Unlike pciehp_card_present(), which determines presence solely from the ++ * Presence Detect State bit, this helper also returns true if the Link Active ++ * bit is set. This is a concession to broken hotplug ports which hardwire ++ * Presence Detect State to zero, such as Wilocity's [1ae9:0200]. ++ */ ++bool pciehp_card_present_or_link_active(struct controller *ctrl) ++{ ++ return pciehp_card_present(ctrl) || pciehp_check_link_active(ctrl); + } + + int pciehp_query_power_fault(struct slot *slot) +@@ -874,7 +888,7 @@ struct controller *pcie_init(struct pcie_device *dev) + { + struct controller *ctrl; + u32 slot_cap, link_cap; +- u8 occupied, poweron; ++ u8 poweron; + struct pci_dev *pdev = dev->port; + + ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); +@@ -934,9 +948,8 @@ struct controller *pcie_init(struct pcie_device *dev) + * requested yet, so avoid triggering a notification with this command. + */ + if (POWER_CTRL(ctrl)) { +- pciehp_get_adapter_status(ctrl->slot, &occupied); + pciehp_get_power_status(ctrl->slot, &poweron); +- if (!occupied && poweron) { ++ if (!pciehp_card_present_or_link_active(ctrl) && poweron) { + pcie_disable_notification(ctrl); + pciehp_power_off_slot(ctrl->slot); + } +-- +2.29.2 + +From f3bc2638976b69fbfc0f40f70b4211ff84e9c92d Mon Sep 17 00:00:00 2001 +From: Patrick Talbert +Date: Wed, 5 Sep 2018 09:12:53 +0200 +Subject: [PATCH] PCI/ASPM: Do not initialize link state when aspm_disabled is + set + +Now that ASPM is configured for *all* PCIe devices at boot, a problem is +seen with systems that set the FADT NO_ASPM bit. This bit indicates that +the OS should not alter the ASPM state, but when +pcie_aspm_init_link_state() runs it only checks for !aspm_support_enabled. +This misses the ACPI_FADT_NO_ASPM case because that is setting +aspm_disabled. + +The result is systems may hang at boot after 1302fcf; avoidable if they +boot with pcie_aspm=off (sets !aspm_support_enabled). + +Fix this by having aspm_init_link_state() check for either +!aspm_support_enabled or acpm_disabled. + +Link: https://bugzilla.kernel.org/show_bug.cgi?id=201001 +Fixes: 1302fcf0d03e ("PCI: Configure *all* devices, not just hot-added ones") +Signed-off-by: Patrick Talbert +Signed-off-by: Bjorn Helgaas + +Patchset: surface-sam +--- + drivers/pci/pcie/aspm.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c +index cb474338f39d..18aa830e79e4 100644 +--- a/drivers/pci/pcie/aspm.c ++++ b/drivers/pci/pcie/aspm.c +@@ -907,7 +907,7 @@ void pcie_aspm_init_link_state(struct pci_dev *pdev) + struct pcie_link_state *link; + int blacklist = !!pcie_aspm_sanity_check(pdev); + +- if (!aspm_support_enabled) ++ if (!aspm_support_enabled || aspm_disabled) + return; + + if (pdev->link_state) +-- +2.29.2 + +From a088c61501b4416c22057520accb8351a2e9ff6c Mon Sep 17 00:00:00 2001 +From: Lukas Wunner +Date: Sun, 19 Aug 2018 16:29:00 +0200 +Subject: [PATCH] PCI: Simplify disconnected marking + +Commit 89ee9f768003 ("PCI: Add device disconnected state") iterates over +the devices on a parent bus, marks each as disconnected, then marks +each device's children as disconnected using pci_walk_bus(). + +The same can be achieved more succinctly by calling pci_walk_bus() on +the parent bus. Moreover, this does not need to wait until acquiring +pci_lock_rescan_remove(), so move it out of that critical section. + +The critical section in err.c contains a pci_dev_get() / pci_dev_put() +pair which was apparently copy-pasted from pciehp_pci.c. In the latter +it serves the purpose of holding the struct pci_dev in place until the +Command register is updated. err.c doesn't do anything like that, hence +the pair is unnecessary. Remove it. + +Signed-off-by: Lukas Wunner +Signed-off-by: Bjorn Helgaas +Cc: Keith Busch +Cc: Oza Pawandeep +Cc: Sinan Kaya +Cc: Benjamin Herrenschmidt + +Patchset: surface-sam +--- + drivers/pci/hotplug/pciehp_pci.c | 9 +++------ + drivers/pci/pcie/err.c | 8 ++------ + 2 files changed, 5 insertions(+), 12 deletions(-) + +diff --git a/drivers/pci/hotplug/pciehp_pci.c b/drivers/pci/hotplug/pciehp_pci.c +index c512b2ed85ed..8da87931bd45 100644 +--- a/drivers/pci/hotplug/pciehp_pci.c ++++ b/drivers/pci/hotplug/pciehp_pci.c +@@ -90,6 +90,9 @@ void pciehp_unconfigure_device(struct slot *p_slot, bool presence) + ctrl_dbg(ctrl, "%s: domain:bus:dev = %04x:%02x:00\n", + __func__, pci_domain_nr(parent), parent->number); + ++ if (!presence) ++ pci_walk_bus(parent, pci_dev_set_disconnected, NULL); ++ + pci_lock_rescan_remove(); + + /* +@@ -101,12 +104,6 @@ void pciehp_unconfigure_device(struct slot *p_slot, bool presence) + list_for_each_entry_safe_reverse(dev, temp, &parent->devices, + bus_list) { + pci_dev_get(dev); +- if (!presence) { +- pci_dev_set_disconnected(dev, NULL); +- if (pci_has_subordinate(dev)) +- pci_walk_bus(dev->subordinate, +- pci_dev_set_disconnected, NULL); +- } + pci_stop_and_remove_bus_device(dev); + /* + * Ensure that no new Requests will be generated from +diff --git a/drivers/pci/pcie/err.c b/drivers/pci/pcie/err.c +index 2c3b5bd59b18..dee5a7507403 100644 +--- a/drivers/pci/pcie/err.c ++++ b/drivers/pci/pcie/err.c +@@ -239,17 +239,13 @@ void pcie_do_fatal_recovery(struct pci_dev *dev, u32 service) + udev = dev->bus->self; + + parent = udev->subordinate; ++ pci_walk_bus(parent, pci_dev_set_disconnected, NULL); ++ + pci_lock_rescan_remove(); + pci_dev_get(dev); + list_for_each_entry_safe_reverse(pdev, temp, &parent->devices, + bus_list) { +- pci_dev_get(pdev); +- pci_dev_set_disconnected(pdev, NULL); +- if (pci_has_subordinate(pdev)) +- pci_walk_bus(pdev->subordinate, +- pci_dev_set_disconnected, NULL); + pci_stop_and_remove_bus_device(pdev); +- pci_dev_put(pdev); + } + + result = reset_link(udev, service); +-- +2.29.2 + +From 47fec40f2551fb4a56c32276642fa0eac3e0e8b8 Mon Sep 17 00:00:00 2001 +From: Lukas Wunner +Date: Tue, 18 Sep 2018 21:46:17 +0200 +Subject: [PATCH] PCI: pciehp: Unify controller and slot structs + +pciehp was originally introduced together with shpchp in a single +commit, c16b4b14d980 ("PCI Hotplug: Add SHPC and PCI Express hot-plug +drivers"): +https://git.kernel.org/tglx/history/c/c16b4b14d980 + +shpchp supports up to 31 slots per controller, hence uses separate slot +and controller structs. pciehp has a 1:1 relationship between slot and +controller and therefore never required this separation. Nevertheless, +because much of the code had been copy-pasted between the two drivers, +pciehp likewise uses separate structs to this very day. + +The artificial separation of data structures adds unnecessary complexity +and bloat to pciehp and requires constantly chasing pointers at runtime. + +Simplify the driver by merging struct slot into struct controller. +Merge the slot constructor pcie_init_slot() and the destructor +pcie_cleanup_slot() into the controller counterparts. + +No functional change intended. + +Signed-off-by: Lukas Wunner +Signed-off-by: Bjorn Helgaas + +Patchset: surface-sam +--- + drivers/pci/hotplug/pciehp.h | 67 ++++---- + drivers/pci/hotplug/pciehp_core.c | 53 +++---- + drivers/pci/hotplug/pciehp_ctrl.c | 244 ++++++++++++++---------------- + drivers/pci/hotplug/pciehp_hpc.c | 114 +++++--------- + drivers/pci/hotplug/pciehp_pci.c | 14 +- + 5 files changed, 210 insertions(+), 282 deletions(-) + +diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h +index d1fa2ce71ad9..bf1c9d3a64a5 100644 +--- a/drivers/pci/hotplug/pciehp.h ++++ b/drivers/pci/hotplug/pciehp.h +@@ -58,24 +58,6 @@ do { \ + + #define SLOT_NAME_SIZE 10 + +-/** +- * struct slot - PCIe hotplug slot +- * @state: current state machine position +- * @ctrl: pointer to the slot's controller structure +- * @hotplug_slot: pointer to the structure registered with the PCI hotplug core +- * @work: work item to turn the slot on or off after 5 seconds in response to +- * an Attention Button press +- * @lock: protects reads and writes of @state; +- * protects scheduling, execution and cancellation of @work +- */ +-struct slot { +- u8 state; +- struct controller *ctrl; +- struct hotplug_slot *hotplug_slot; +- struct delayed_work work; +- struct mutex lock; +-}; +- + /** + * struct controller - PCIe hotplug controller + * @ctrl_lock: serializes writes to the Slot Control register +@@ -83,7 +65,6 @@ struct slot { + * @reset_lock: prevents access to the Data Link Layer Link Active bit in the + * Link Status register and to the Presence Detect State bit in the Slot + * Status register during a slot reset which may cause them to flap +- * @slot: pointer to the controller's slot structure + * @queue: wait queue to wake up on reception of a Command Completed event, + * used for synchronous writes to the Slot Control register + * @slot_cap: cached copy of the Slot Capabilities register +@@ -105,16 +86,24 @@ struct slot { + * that has not yet been cleared by the user + * @pending_events: used by the IRQ handler to save events retrieved from the + * Slot Status register for later consumption by the IRQ thread ++ * @state: current state machine position ++ * @lock: protects reads and writes of @state; ++ * protects scheduling, execution and cancellation of @work ++ * @work: work item to turn the slot on or off after 5 seconds ++ * in response to an Attention Button press ++ * @hotplug_slot: pointer to the structure registered with the PCI hotplug core + * @ist_running: flag to keep user request waiting while IRQ thread is running + * @request_result: result of last user request submitted to the IRQ thread + * @requester: wait queue to wake up on completion of user request, + * used for synchronous slot enable/disable request via sysfs ++ * ++ * PCIe hotplug has a 1:1 relationship between controller and slot, hence ++ * unlike other drivers, the two aren't represented by separate structures. + */ + struct controller { + struct mutex ctrl_lock; + struct pcie_device *pcie; + struct rw_semaphore reset_lock; +- struct slot *slot; + wait_queue_head_t queue; + u32 slot_cap; + u16 slot_ctrl; +@@ -125,6 +114,10 @@ struct controller { + unsigned int notification_enabled:1; + unsigned int power_fault_detected; + atomic_t pending_events; ++ u8 state; ++ struct mutex lock; ++ struct delayed_work work; ++ struct hotplug_slot *hotplug_slot; + unsigned int ist_running; + int request_result; + wait_queue_head_t requester; +@@ -176,26 +169,26 @@ struct controller { + #define PSN(ctrl) (((ctrl)->slot_cap & PCI_EXP_SLTCAP_PSN) >> 19) + + void pciehp_request(struct controller *ctrl, int action); +-void pciehp_handle_button_press(struct slot *slot); +-void pciehp_handle_disable_request(struct slot *slot); +-void pciehp_handle_presence_or_link_change(struct slot *slot, u32 events); +-int pciehp_configure_device(struct slot *p_slot); +-void pciehp_unconfigure_device(struct slot *p_slot, bool presence); ++void pciehp_handle_button_press(struct controller *ctrl); ++void pciehp_handle_disable_request(struct controller *ctrl); ++void pciehp_handle_presence_or_link_change(struct controller *ctrl, u32 events); ++int pciehp_configure_device(struct controller *ctrl); ++void pciehp_unconfigure_device(struct controller *ctrl, bool presence); + void pciehp_queue_pushbutton_work(struct work_struct *work); + struct controller *pcie_init(struct pcie_device *dev); + int pcie_init_notification(struct controller *ctrl); + void pcie_shutdown_notification(struct controller *ctrl); + void pcie_clear_hotplug_events(struct controller *ctrl); +-int pciehp_power_on_slot(struct slot *slot); +-void pciehp_power_off_slot(struct slot *slot); +-void pciehp_get_power_status(struct slot *slot, u8 *status); +- +-void pciehp_set_attention_status(struct slot *slot, u8 status); +-void pciehp_get_latch_status(struct slot *slot, u8 *status); +-int pciehp_query_power_fault(struct slot *slot); +-void pciehp_green_led_on(struct slot *slot); +-void pciehp_green_led_off(struct slot *slot); +-void pciehp_green_led_blink(struct slot *slot); ++int pciehp_power_on_slot(struct controller *ctrl); ++void pciehp_power_off_slot(struct controller *ctrl); ++void pciehp_get_power_status(struct controller *ctrl, u8 *status); ++ ++void pciehp_set_attention_status(struct controller *ctrl, u8 status); ++void pciehp_get_latch_status(struct controller *ctrl, u8 *status); ++int pciehp_query_power_fault(struct controller *ctrl); ++void pciehp_green_led_on(struct controller *ctrl); ++void pciehp_green_led_off(struct controller *ctrl); ++void pciehp_green_led_blink(struct controller *ctrl); + bool pciehp_card_present(struct controller *ctrl); + bool pciehp_card_present_or_link_active(struct controller *ctrl); + int pciehp_check_link_status(struct controller *ctrl); +@@ -209,9 +202,9 @@ int pciehp_get_attention_status(struct hotplug_slot *hotplug_slot, u8 *status); + int pciehp_set_raw_indicator_status(struct hotplug_slot *h_slot, u8 status); + int pciehp_get_raw_indicator_status(struct hotplug_slot *h_slot, u8 *status); + +-static inline const char *slot_name(struct slot *slot) ++static inline const char *slot_name(struct controller *ctrl) + { +- return hotplug_slot_name(slot->hotplug_slot); ++ return hotplug_slot_name(ctrl->hotplug_slot); + } + + #endif /* _PCIEHP_H */ +diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c +index c052efe421f3..307099c720ec 100644 +--- a/drivers/pci/hotplug/pciehp_core.c ++++ b/drivers/pci/hotplug/pciehp_core.c +@@ -51,7 +51,6 @@ static int get_adapter_status(struct hotplug_slot *slot, u8 *value); + + static int init_slot(struct controller *ctrl) + { +- struct slot *slot = ctrl->slot; + struct hotplug_slot *hotplug = NULL; + struct hotplug_slot_info *info = NULL; + struct hotplug_slot_ops *ops = NULL; +@@ -88,9 +87,9 @@ static int init_slot(struct controller *ctrl) + + /* register this slot with the hotplug pci core */ + hotplug->info = info; +- hotplug->private = slot; ++ hotplug->private = ctrl; + hotplug->ops = ops; +- slot->hotplug_slot = hotplug; ++ ctrl->hotplug_slot = hotplug; + snprintf(name, SLOT_NAME_SIZE, "%u", PSN(ctrl)); + + retval = pci_hp_initialize(hotplug, +@@ -108,7 +107,7 @@ static int init_slot(struct controller *ctrl) + + static void cleanup_slot(struct controller *ctrl) + { +- struct hotplug_slot *hotplug_slot = ctrl->slot->hotplug_slot; ++ struct hotplug_slot *hotplug_slot = ctrl->hotplug_slot; + + pci_hp_destroy(hotplug_slot); + kfree(hotplug_slot->ops); +@@ -121,44 +120,44 @@ static void cleanup_slot(struct controller *ctrl) + */ + static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status) + { +- struct slot *slot = hotplug_slot->private; +- struct pci_dev *pdev = slot->ctrl->pcie->port; ++ struct controller *ctrl = hotplug_slot->private; ++ struct pci_dev *pdev = ctrl->pcie->port; + + pci_config_pm_runtime_get(pdev); +- pciehp_set_attention_status(slot, status); ++ pciehp_set_attention_status(ctrl, status); + pci_config_pm_runtime_put(pdev); + return 0; + } + + static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value) + { +- struct slot *slot = hotplug_slot->private; +- struct pci_dev *pdev = slot->ctrl->pcie->port; ++ struct controller *ctrl = hotplug_slot->private; ++ struct pci_dev *pdev = ctrl->pcie->port; + + pci_config_pm_runtime_get(pdev); +- pciehp_get_power_status(slot, value); ++ pciehp_get_power_status(ctrl, value); + pci_config_pm_runtime_put(pdev); + return 0; + } + + static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value) + { +- struct slot *slot = hotplug_slot->private; +- struct pci_dev *pdev = slot->ctrl->pcie->port; ++ struct controller *ctrl = hotplug_slot->private; ++ struct pci_dev *pdev = ctrl->pcie->port; + + pci_config_pm_runtime_get(pdev); +- pciehp_get_latch_status(slot, value); ++ pciehp_get_latch_status(ctrl, value); + pci_config_pm_runtime_put(pdev); + return 0; + } + + static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) + { +- struct slot *slot = hotplug_slot->private; +- struct pci_dev *pdev = slot->ctrl->pcie->port; ++ struct controller *ctrl = hotplug_slot->private; ++ struct pci_dev *pdev = ctrl->pcie->port; + + pci_config_pm_runtime_get(pdev); +- *value = pciehp_card_present_or_link_active(slot->ctrl); ++ *value = pciehp_card_present_or_link_active(ctrl); + pci_config_pm_runtime_put(pdev); + return 0; + } +@@ -175,20 +174,19 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) + */ + static void pciehp_check_presence(struct controller *ctrl) + { +- struct slot *slot = ctrl->slot; + bool occupied; + + down_read(&ctrl->reset_lock); +- mutex_lock(&slot->lock); ++ mutex_lock(&ctrl->lock); + + occupied = pciehp_card_present_or_link_active(ctrl); +- if ((occupied && (slot->state == OFF_STATE || +- slot->state == BLINKINGON_STATE)) || +- (!occupied && (slot->state == ON_STATE || +- slot->state == BLINKINGOFF_STATE))) ++ if ((occupied && (ctrl->state == OFF_STATE || ++ ctrl->state == BLINKINGON_STATE)) || ++ (!occupied && (ctrl->state == ON_STATE || ++ ctrl->state == BLINKINGOFF_STATE))) + pciehp_request(ctrl, PCI_EXP_SLTSTA_PDC); + +- mutex_unlock(&slot->lock); ++ mutex_unlock(&ctrl->lock); + up_read(&ctrl->reset_lock); + } + +@@ -196,7 +194,6 @@ static int pciehp_probe(struct pcie_device *dev) + { + int rc; + struct controller *ctrl; +- struct slot *slot; + + /* If this is not a "hotplug" service, we have no business here. */ + if (dev->service != PCIE_PORT_SERVICE_HP) +@@ -234,8 +231,7 @@ static int pciehp_probe(struct pcie_device *dev) + } + + /* Publish to user space */ +- slot = ctrl->slot; +- rc = pci_hp_add(slot->hotplug_slot); ++ rc = pci_hp_add(ctrl->hotplug_slot); + if (rc) { + ctrl_err(ctrl, "Publication to user space failed (%d)\n", rc); + goto err_out_shutdown_notification; +@@ -258,7 +254,7 @@ static void pciehp_remove(struct pcie_device *dev) + { + struct controller *ctrl = get_service_data(dev); + +- pci_hp_del(ctrl->slot->hotplug_slot); ++ pci_hp_del(ctrl->hotplug_slot); + pcie_shutdown_notification(ctrl); + cleanup_slot(ctrl); + pciehp_release_ctrl(ctrl); +@@ -273,14 +269,13 @@ static int pciehp_suspend(struct pcie_device *dev) + static int pciehp_resume_noirq(struct pcie_device *dev) + { + struct controller *ctrl = get_service_data(dev); +- struct slot *slot = ctrl->slot; + + /* pci_restore_state() just wrote to the Slot Control register */ + ctrl->cmd_started = jiffies; + ctrl->cmd_busy = true; + + /* clear spurious events from rediscovery of inserted card */ +- if (slot->state == ON_STATE || slot->state == BLINKINGOFF_STATE) ++ if (ctrl->state == ON_STATE || ctrl->state == BLINKINGOFF_STATE) + pcie_clear_hotplug_events(ctrl); + + return 0; +diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c +index 97fa865717ec..dd6533a094a8 100644 +--- a/drivers/pci/hotplug/pciehp_ctrl.c ++++ b/drivers/pci/hotplug/pciehp_ctrl.c +@@ -26,11 +26,11 @@ + #define SAFE_REMOVAL true + #define SURPRISE_REMOVAL false + +-static void set_slot_off(struct controller *ctrl, struct slot *pslot) ++static void set_slot_off(struct controller *ctrl) + { + /* turn off slot, turn on Amber LED, turn off Green LED if supported*/ + if (POWER_CTRL(ctrl)) { +- pciehp_power_off_slot(pslot); ++ pciehp_power_off_slot(ctrl); + + /* + * After turning power off, we must wait for at least 1 second +@@ -40,31 +40,30 @@ static void set_slot_off(struct controller *ctrl, struct slot *pslot) + msleep(1000); + } + +- pciehp_green_led_off(pslot); +- pciehp_set_attention_status(pslot, 1); ++ pciehp_green_led_off(ctrl); ++ pciehp_set_attention_status(ctrl, 1); + } + + /** + * board_added - Called after a board has been added to the system. +- * @p_slot: &slot where board is added ++ * @ctrl: PCIe hotplug controller where board is added + * + * Turns power on for the board. + * Configures board. + */ +-static int board_added(struct slot *p_slot) ++static int board_added(struct controller *ctrl) + { + int retval = 0; +- struct controller *ctrl = p_slot->ctrl; + struct pci_bus *parent = ctrl->pcie->port->subordinate; + + if (POWER_CTRL(ctrl)) { + /* Power on slot */ +- retval = pciehp_power_on_slot(p_slot); ++ retval = pciehp_power_on_slot(ctrl); + if (retval) + return retval; + } + +- pciehp_green_led_blink(p_slot); ++ pciehp_green_led_blink(ctrl); + + /* Check link training status */ + retval = pciehp_check_link_status(ctrl); +@@ -74,13 +73,13 @@ static int board_added(struct slot *p_slot) + } + + /* Check for a power fault */ +- if (ctrl->power_fault_detected || pciehp_query_power_fault(p_slot)) { +- ctrl_err(ctrl, "Slot(%s): Power fault\n", slot_name(p_slot)); ++ if (ctrl->power_fault_detected || pciehp_query_power_fault(ctrl)) { ++ ctrl_err(ctrl, "Slot(%s): Power fault\n", slot_name(ctrl)); + retval = -EIO; + goto err_exit; + } + +- retval = pciehp_configure_device(p_slot); ++ retval = pciehp_configure_device(ctrl); + if (retval) { + if (retval != -EEXIST) { + ctrl_err(ctrl, "Cannot add device at %04x:%02x:00\n", +@@ -89,28 +88,26 @@ static int board_added(struct slot *p_slot) + } + } + +- pciehp_green_led_on(p_slot); +- pciehp_set_attention_status(p_slot, 0); ++ pciehp_green_led_on(ctrl); ++ pciehp_set_attention_status(ctrl, 0); + return 0; + + err_exit: +- set_slot_off(ctrl, p_slot); ++ set_slot_off(ctrl); + return retval; + } + + /** + * remove_board - Turns off slot and LEDs +- * @p_slot: slot where board is being removed ++ * @ctrl: PCIe hotplug controller where board is being removed + * @safe_removal: whether the board is safely removed (versus surprise removed) + */ +-static void remove_board(struct slot *p_slot, bool safe_removal) ++static void remove_board(struct controller *ctrl, bool safe_removal) + { +- struct controller *ctrl = p_slot->ctrl; +- +- pciehp_unconfigure_device(p_slot, safe_removal); ++ pciehp_unconfigure_device(ctrl, safe_removal); + + if (POWER_CTRL(ctrl)) { +- pciehp_power_off_slot(p_slot); ++ pciehp_power_off_slot(ctrl); + + /* + * After turning power off, we must wait for at least 1 second +@@ -125,11 +122,11 @@ static void remove_board(struct slot *p_slot, bool safe_removal) + } + + /* turn off Green LED */ +- pciehp_green_led_off(p_slot); ++ pciehp_green_led_off(ctrl); + } + +-static int pciehp_enable_slot(struct slot *slot); +-static int pciehp_disable_slot(struct slot *slot, bool safe_removal); ++static int pciehp_enable_slot(struct controller *ctrl); ++static int pciehp_disable_slot(struct controller *ctrl, bool safe_removal); + + void pciehp_request(struct controller *ctrl, int action) + { +@@ -140,11 +137,11 @@ void pciehp_request(struct controller *ctrl, int action) + + void pciehp_queue_pushbutton_work(struct work_struct *work) + { +- struct slot *p_slot = container_of(work, struct slot, work.work); +- struct controller *ctrl = p_slot->ctrl; ++ struct controller *ctrl = container_of(work, struct controller, ++ work.work); + +- mutex_lock(&p_slot->lock); +- switch (p_slot->state) { ++ mutex_lock(&ctrl->lock); ++ switch (ctrl->state) { + case BLINKINGOFF_STATE: + pciehp_request(ctrl, DISABLE_SLOT); + break; +@@ -154,30 +151,28 @@ void pciehp_queue_pushbutton_work(struct work_struct *work) + default: + break; + } +- mutex_unlock(&p_slot->lock); ++ mutex_unlock(&ctrl->lock); + } + +-void pciehp_handle_button_press(struct slot *p_slot) ++void pciehp_handle_button_press(struct controller *ctrl) + { +- struct controller *ctrl = p_slot->ctrl; +- +- mutex_lock(&p_slot->lock); +- switch (p_slot->state) { ++ mutex_lock(&ctrl->lock); ++ switch (ctrl->state) { + case OFF_STATE: + case ON_STATE: +- if (p_slot->state == ON_STATE) { +- p_slot->state = BLINKINGOFF_STATE; ++ if (ctrl->state == ON_STATE) { ++ ctrl->state = BLINKINGOFF_STATE; + ctrl_info(ctrl, "Slot(%s): Powering off due to button press\n", +- slot_name(p_slot)); ++ slot_name(ctrl)); + } else { +- p_slot->state = BLINKINGON_STATE; ++ ctrl->state = BLINKINGON_STATE; + ctrl_info(ctrl, "Slot(%s) Powering on due to button press\n", +- slot_name(p_slot)); ++ slot_name(ctrl)); + } + /* blink green LED and turn off amber */ +- pciehp_green_led_blink(p_slot); +- pciehp_set_attention_status(p_slot, 0); +- schedule_delayed_work(&p_slot->work, 5 * HZ); ++ pciehp_green_led_blink(ctrl); ++ pciehp_set_attention_status(ctrl, 0); ++ schedule_delayed_work(&ctrl->work, 5 * HZ); + break; + case BLINKINGOFF_STATE: + case BLINKINGON_STATE: +@@ -186,192 +181,184 @@ void pciehp_handle_button_press(struct slot *p_slot) + * press the attention again before the 5 sec. limit + * expires to cancel hot-add or hot-remove + */ +- ctrl_info(ctrl, "Slot(%s): Button cancel\n", slot_name(p_slot)); +- cancel_delayed_work(&p_slot->work); +- if (p_slot->state == BLINKINGOFF_STATE) { +- p_slot->state = ON_STATE; +- pciehp_green_led_on(p_slot); ++ ctrl_info(ctrl, "Slot(%s): Button cancel\n", slot_name(ctrl)); ++ cancel_delayed_work(&ctrl->work); ++ if (ctrl->state == BLINKINGOFF_STATE) { ++ ctrl->state = ON_STATE; ++ pciehp_green_led_on(ctrl); + } else { +- p_slot->state = OFF_STATE; +- pciehp_green_led_off(p_slot); ++ ctrl->state = OFF_STATE; ++ pciehp_green_led_off(ctrl); + } +- pciehp_set_attention_status(p_slot, 0); ++ pciehp_set_attention_status(ctrl, 0); + ctrl_info(ctrl, "Slot(%s): Action canceled due to button press\n", +- slot_name(p_slot)); ++ slot_name(ctrl)); + break; + default: + ctrl_err(ctrl, "Slot(%s): Ignoring invalid state %#x\n", +- slot_name(p_slot), p_slot->state); ++ slot_name(ctrl), ctrl->state); + break; + } +- mutex_unlock(&p_slot->lock); ++ mutex_unlock(&ctrl->lock); + } + +-void pciehp_handle_disable_request(struct slot *slot) ++void pciehp_handle_disable_request(struct controller *ctrl) + { +- struct controller *ctrl = slot->ctrl; +- +- mutex_lock(&slot->lock); +- switch (slot->state) { ++ mutex_lock(&ctrl->lock); ++ switch (ctrl->state) { + case BLINKINGON_STATE: + case BLINKINGOFF_STATE: +- cancel_delayed_work(&slot->work); ++ cancel_delayed_work(&ctrl->work); + break; + } +- slot->state = POWEROFF_STATE; +- mutex_unlock(&slot->lock); ++ ctrl->state = POWEROFF_STATE; ++ mutex_unlock(&ctrl->lock); + +- ctrl->request_result = pciehp_disable_slot(slot, SAFE_REMOVAL); ++ ctrl->request_result = pciehp_disable_slot(ctrl, SAFE_REMOVAL); + } + +-void pciehp_handle_presence_or_link_change(struct slot *slot, u32 events) ++void pciehp_handle_presence_or_link_change(struct controller *ctrl, u32 events) + { +- struct controller *ctrl = slot->ctrl; + bool present, link_active; + + /* + * If the slot is on and presence or link has changed, turn it off. + * Even if it's occupied again, we cannot assume the card is the same. + */ +- mutex_lock(&slot->lock); +- switch (slot->state) { ++ mutex_lock(&ctrl->lock); ++ switch (ctrl->state) { + case BLINKINGOFF_STATE: +- cancel_delayed_work(&slot->work); ++ cancel_delayed_work(&ctrl->work); + /* fall through */ + case ON_STATE: +- slot->state = POWEROFF_STATE; +- mutex_unlock(&slot->lock); ++ ctrl->state = POWEROFF_STATE; ++ mutex_unlock(&ctrl->lock); + if (events & PCI_EXP_SLTSTA_DLLSC) + ctrl_info(ctrl, "Slot(%s): Link Down\n", +- slot_name(slot)); ++ slot_name(ctrl)); + if (events & PCI_EXP_SLTSTA_PDC) + ctrl_info(ctrl, "Slot(%s): Card not present\n", +- slot_name(slot)); +- pciehp_disable_slot(slot, SURPRISE_REMOVAL); ++ slot_name(ctrl)); ++ pciehp_disable_slot(ctrl, SURPRISE_REMOVAL); + break; + default: +- mutex_unlock(&slot->lock); ++ mutex_unlock(&ctrl->lock); + break; + } + + /* Turn the slot on if it's occupied or link is up */ +- mutex_lock(&slot->lock); ++ mutex_lock(&ctrl->lock); + present = pciehp_card_present(ctrl); + link_active = pciehp_check_link_active(ctrl); + if (!present && !link_active) { +- mutex_unlock(&slot->lock); ++ mutex_unlock(&ctrl->lock); + return; + } + +- switch (slot->state) { ++ switch (ctrl->state) { + case BLINKINGON_STATE: +- cancel_delayed_work(&slot->work); ++ cancel_delayed_work(&ctrl->work); + /* fall through */ + case OFF_STATE: +- slot->state = POWERON_STATE; +- mutex_unlock(&slot->lock); ++ ctrl->state = POWERON_STATE; ++ mutex_unlock(&ctrl->lock); + if (present) + ctrl_info(ctrl, "Slot(%s): Card present\n", +- slot_name(slot)); ++ slot_name(ctrl)); + if (link_active) + ctrl_info(ctrl, "Slot(%s): Link Up\n", +- slot_name(slot)); +- ctrl->request_result = pciehp_enable_slot(slot); ++ slot_name(ctrl)); ++ ctrl->request_result = pciehp_enable_slot(ctrl); + break; + default: +- mutex_unlock(&slot->lock); ++ mutex_unlock(&ctrl->lock); + break; + } + } + +-static int __pciehp_enable_slot(struct slot *p_slot) ++static int __pciehp_enable_slot(struct controller *ctrl) + { + u8 getstatus = 0; +- struct controller *ctrl = p_slot->ctrl; + +- if (MRL_SENS(p_slot->ctrl)) { +- pciehp_get_latch_status(p_slot, &getstatus); ++ if (MRL_SENS(ctrl)) { ++ pciehp_get_latch_status(ctrl, &getstatus); + if (getstatus) { + ctrl_info(ctrl, "Slot(%s): Latch open\n", +- slot_name(p_slot)); ++ slot_name(ctrl)); + return -ENODEV; + } + } + +- if (POWER_CTRL(p_slot->ctrl)) { +- pciehp_get_power_status(p_slot, &getstatus); ++ if (POWER_CTRL(ctrl)) { ++ pciehp_get_power_status(ctrl, &getstatus); + if (getstatus) { + ctrl_info(ctrl, "Slot(%s): Already enabled\n", +- slot_name(p_slot)); ++ slot_name(ctrl)); + return 0; + } + } + +- return board_added(p_slot); ++ return board_added(ctrl); + } + +-static int pciehp_enable_slot(struct slot *slot) ++static int pciehp_enable_slot(struct controller *ctrl) + { +- struct controller *ctrl = slot->ctrl; + int ret; + + pm_runtime_get_sync(&ctrl->pcie->port->dev); +- ret = __pciehp_enable_slot(slot); ++ ret = __pciehp_enable_slot(ctrl); + if (ret && ATTN_BUTTN(ctrl)) +- pciehp_green_led_off(slot); /* may be blinking */ ++ pciehp_green_led_off(ctrl); /* may be blinking */ + pm_runtime_put(&ctrl->pcie->port->dev); + +- mutex_lock(&slot->lock); +- slot->state = ret ? OFF_STATE : ON_STATE; +- mutex_unlock(&slot->lock); ++ mutex_lock(&ctrl->lock); ++ ctrl->state = ret ? OFF_STATE : ON_STATE; ++ mutex_unlock(&ctrl->lock); + + return ret; + } + +-static int __pciehp_disable_slot(struct slot *p_slot, bool safe_removal) ++static int __pciehp_disable_slot(struct controller *ctrl, bool safe_removal) + { + u8 getstatus = 0; +- struct controller *ctrl = p_slot->ctrl; + +- if (POWER_CTRL(p_slot->ctrl)) { +- pciehp_get_power_status(p_slot, &getstatus); ++ if (POWER_CTRL(ctrl)) { ++ pciehp_get_power_status(ctrl, &getstatus); + if (!getstatus) { + ctrl_info(ctrl, "Slot(%s): Already disabled\n", +- slot_name(p_slot)); ++ slot_name(ctrl)); + return -EINVAL; + } + } + +- remove_board(p_slot, safe_removal); ++ remove_board(ctrl, safe_removal); + return 0; + } + +-static int pciehp_disable_slot(struct slot *slot, bool safe_removal) ++static int pciehp_disable_slot(struct controller *ctrl, bool safe_removal) + { +- struct controller *ctrl = slot->ctrl; + int ret; + + pm_runtime_get_sync(&ctrl->pcie->port->dev); +- ret = __pciehp_disable_slot(slot, safe_removal); ++ ret = __pciehp_disable_slot(ctrl, safe_removal); + pm_runtime_put(&ctrl->pcie->port->dev); + +- mutex_lock(&slot->lock); +- slot->state = OFF_STATE; +- mutex_unlock(&slot->lock); ++ mutex_lock(&ctrl->lock); ++ ctrl->state = OFF_STATE; ++ mutex_unlock(&ctrl->lock); + + return ret; + } + + int pciehp_sysfs_enable_slot(struct hotplug_slot *hotplug_slot) + { +- struct slot *p_slot = hotplug_slot->private; +- struct controller *ctrl = p_slot->ctrl; ++ struct controller *ctrl = hotplug_slot->private; + +- mutex_lock(&p_slot->lock); +- switch (p_slot->state) { ++ mutex_lock(&ctrl->lock); ++ switch (ctrl->state) { + case BLINKINGON_STATE: + case OFF_STATE: +- mutex_unlock(&p_slot->lock); ++ mutex_unlock(&ctrl->lock); + /* + * The IRQ thread becomes a no-op if the user pulls out the + * card before the thread wakes up, so initialize to -ENODEV. +@@ -384,34 +371,33 @@ int pciehp_sysfs_enable_slot(struct hotplug_slot *hotplug_slot) + return ctrl->request_result; + case POWERON_STATE: + ctrl_info(ctrl, "Slot(%s): Already in powering on state\n", +- slot_name(p_slot)); ++ slot_name(ctrl)); + break; + case BLINKINGOFF_STATE: + case ON_STATE: + case POWEROFF_STATE: + ctrl_info(ctrl, "Slot(%s): Already enabled\n", +- slot_name(p_slot)); ++ slot_name(ctrl)); + break; + default: + ctrl_err(ctrl, "Slot(%s): Invalid state %#x\n", +- slot_name(p_slot), p_slot->state); ++ slot_name(ctrl), ctrl->state); + break; + } +- mutex_unlock(&p_slot->lock); ++ mutex_unlock(&ctrl->lock); + + return -ENODEV; + } + + int pciehp_sysfs_disable_slot(struct hotplug_slot *hotplug_slot) + { +- struct slot *p_slot = hotplug_slot->private; +- struct controller *ctrl = p_slot->ctrl; ++ struct controller *ctrl = hotplug_slot->private; + +- mutex_lock(&p_slot->lock); +- switch (p_slot->state) { ++ mutex_lock(&ctrl->lock); ++ switch (ctrl->state) { + case BLINKINGOFF_STATE: + case ON_STATE: +- mutex_unlock(&p_slot->lock); ++ mutex_unlock(&ctrl->lock); + pciehp_request(ctrl, DISABLE_SLOT); + wait_event(ctrl->requester, + !atomic_read(&ctrl->pending_events) && +@@ -419,20 +405,20 @@ int pciehp_sysfs_disable_slot(struct hotplug_slot *hotplug_slot) + return ctrl->request_result; + case POWEROFF_STATE: + ctrl_info(ctrl, "Slot(%s): Already in powering off state\n", +- slot_name(p_slot)); ++ slot_name(ctrl)); + break; + case BLINKINGON_STATE: + case OFF_STATE: + case POWERON_STATE: + ctrl_info(ctrl, "Slot(%s): Already disabled\n", +- slot_name(p_slot)); ++ slot_name(ctrl)); + break; + default: + ctrl_err(ctrl, "Slot(%s): Invalid state %#x\n", +- slot_name(p_slot), p_slot->state); ++ slot_name(ctrl), ctrl->state); + break; + } +- mutex_unlock(&p_slot->lock); ++ mutex_unlock(&ctrl->lock); + + return -ENODEV; + } +diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c +index 7f4173d6771a..4a17d71e15d3 100644 +--- a/drivers/pci/hotplug/pciehp_hpc.c ++++ b/drivers/pci/hotplug/pciehp_hpc.c +@@ -40,7 +40,7 @@ static inline int pciehp_request_irq(struct controller *ctrl) + if (pciehp_poll_mode) { + ctrl->poll_thread = kthread_run(&pciehp_poll, ctrl, + "pciehp_poll-%s", +- slot_name(ctrl->slot)); ++ slot_name(ctrl)); + return PTR_ERR_OR_ZERO(ctrl->poll_thread); + } + +@@ -315,8 +315,8 @@ static int pciehp_link_enable(struct controller *ctrl) + int pciehp_get_raw_indicator_status(struct hotplug_slot *hotplug_slot, + u8 *status) + { +- struct slot *slot = hotplug_slot->private; +- struct pci_dev *pdev = ctrl_dev(slot->ctrl); ++ struct controller *ctrl = hotplug_slot->private; ++ struct pci_dev *pdev = ctrl_dev(ctrl); + u16 slot_ctrl; + + pci_config_pm_runtime_get(pdev); +@@ -328,8 +328,7 @@ int pciehp_get_raw_indicator_status(struct hotplug_slot *hotplug_slot, + + int pciehp_get_attention_status(struct hotplug_slot *hotplug_slot, u8 *status) + { +- struct slot *slot = hotplug_slot->private; +- struct controller *ctrl = slot->ctrl; ++ struct controller *ctrl = hotplug_slot->private; + struct pci_dev *pdev = ctrl_dev(ctrl); + u16 slot_ctrl; + +@@ -357,9 +356,8 @@ int pciehp_get_attention_status(struct hotplug_slot *hotplug_slot, u8 *status) + return 0; + } + +-void pciehp_get_power_status(struct slot *slot, u8 *status) ++void pciehp_get_power_status(struct controller *ctrl, u8 *status) + { +- struct controller *ctrl = slot->ctrl; + struct pci_dev *pdev = ctrl_dev(ctrl); + u16 slot_ctrl; + +@@ -380,9 +378,9 @@ void pciehp_get_power_status(struct slot *slot, u8 *status) + } + } + +-void pciehp_get_latch_status(struct slot *slot, u8 *status) ++void pciehp_get_latch_status(struct controller *ctrl, u8 *status) + { +- struct pci_dev *pdev = ctrl_dev(slot->ctrl); ++ struct pci_dev *pdev = ctrl_dev(ctrl); + u16 slot_status; + + pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); +@@ -412,9 +410,9 @@ bool pciehp_card_present_or_link_active(struct controller *ctrl) + return pciehp_card_present(ctrl) || pciehp_check_link_active(ctrl); + } + +-int pciehp_query_power_fault(struct slot *slot) ++int pciehp_query_power_fault(struct controller *ctrl) + { +- struct pci_dev *pdev = ctrl_dev(slot->ctrl); ++ struct pci_dev *pdev = ctrl_dev(ctrl); + u16 slot_status; + + pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); +@@ -424,8 +422,7 @@ int pciehp_query_power_fault(struct slot *slot) + int pciehp_set_raw_indicator_status(struct hotplug_slot *hotplug_slot, + u8 status) + { +- struct slot *slot = hotplug_slot->private; +- struct controller *ctrl = slot->ctrl; ++ struct controller *ctrl = hotplug_slot->private; + struct pci_dev *pdev = ctrl_dev(ctrl); + + pci_config_pm_runtime_get(pdev); +@@ -435,9 +432,8 @@ int pciehp_set_raw_indicator_status(struct hotplug_slot *hotplug_slot, + return 0; + } + +-void pciehp_set_attention_status(struct slot *slot, u8 value) ++void pciehp_set_attention_status(struct controller *ctrl, u8 value) + { +- struct controller *ctrl = slot->ctrl; + u16 slot_cmd; + + if (!ATTN_LED(ctrl)) +@@ -461,10 +457,8 @@ void pciehp_set_attention_status(struct slot *slot, u8 value) + pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_cmd); + } + +-void pciehp_green_led_on(struct slot *slot) ++void pciehp_green_led_on(struct controller *ctrl) + { +- struct controller *ctrl = slot->ctrl; +- + if (!PWR_LED(ctrl)) + return; + +@@ -475,10 +469,8 @@ void pciehp_green_led_on(struct slot *slot) + PCI_EXP_SLTCTL_PWR_IND_ON); + } + +-void pciehp_green_led_off(struct slot *slot) ++void pciehp_green_led_off(struct controller *ctrl) + { +- struct controller *ctrl = slot->ctrl; +- + if (!PWR_LED(ctrl)) + return; + +@@ -489,10 +481,8 @@ void pciehp_green_led_off(struct slot *slot) + PCI_EXP_SLTCTL_PWR_IND_OFF); + } + +-void pciehp_green_led_blink(struct slot *slot) ++void pciehp_green_led_blink(struct controller *ctrl) + { +- struct controller *ctrl = slot->ctrl; +- + if (!PWR_LED(ctrl)) + return; + +@@ -503,9 +493,8 @@ void pciehp_green_led_blink(struct slot *slot) + PCI_EXP_SLTCTL_PWR_IND_BLINK); + } + +-int pciehp_power_on_slot(struct slot *slot) ++int pciehp_power_on_slot(struct controller *ctrl) + { +- struct controller *ctrl = slot->ctrl; + struct pci_dev *pdev = ctrl_dev(ctrl); + u16 slot_status; + int retval; +@@ -529,10 +518,8 @@ int pciehp_power_on_slot(struct slot *slot) + return retval; + } + +-void pciehp_power_off_slot(struct slot *slot) ++void pciehp_power_off_slot(struct controller *ctrl) + { +- struct controller *ctrl = slot->ctrl; +- + pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PWR_OFF, PCI_EXP_SLTCTL_PCC); + ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, + pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, +@@ -644,7 +631,6 @@ static irqreturn_t pciehp_ist(int irq, void *dev_id) + { + struct controller *ctrl = (struct controller *)dev_id; + struct pci_dev *pdev = ctrl_dev(ctrl); +- struct slot *slot = ctrl->slot; + irqreturn_t ret; + u32 events; + +@@ -669,16 +655,16 @@ static irqreturn_t pciehp_ist(int irq, void *dev_id) + /* Check Attention Button Pressed */ + if (events & PCI_EXP_SLTSTA_ABP) { + ctrl_info(ctrl, "Slot(%s): Attention button pressed\n", +- slot_name(slot)); +- pciehp_handle_button_press(slot); ++ slot_name(ctrl)); ++ pciehp_handle_button_press(ctrl); + } + + /* Check Power Fault Detected */ + if ((events & PCI_EXP_SLTSTA_PFD) && !ctrl->power_fault_detected) { + ctrl->power_fault_detected = 1; +- ctrl_err(ctrl, "Slot(%s): Power fault\n", slot_name(slot)); +- pciehp_set_attention_status(slot, 1); +- pciehp_green_led_off(slot); ++ ctrl_err(ctrl, "Slot(%s): Power fault\n", slot_name(ctrl)); ++ pciehp_set_attention_status(ctrl, 1); ++ pciehp_green_led_off(ctrl); + } + + /* +@@ -687,9 +673,9 @@ static irqreturn_t pciehp_ist(int irq, void *dev_id) + */ + down_read(&ctrl->reset_lock); + if (events & DISABLE_SLOT) +- pciehp_handle_disable_request(slot); ++ pciehp_handle_disable_request(ctrl); + else if (events & (PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_DLLSC)) +- pciehp_handle_presence_or_link_change(slot, events); ++ pciehp_handle_presence_or_link_change(ctrl, events); + up_read(&ctrl->reset_lock); + + ret = IRQ_HANDLED; +@@ -788,8 +774,7 @@ void pcie_clear_hotplug_events(struct controller *ctrl) + */ + int pciehp_reset_slot(struct hotplug_slot *hotplug_slot, int probe) + { +- struct slot *slot = hotplug_slot->private; +- struct controller *ctrl = slot->ctrl; ++ struct controller *ctrl = hotplug_slot->private; + struct pci_dev *pdev = ctrl_dev(ctrl); + u16 stat_mask = 0, ctrl_mask = 0; + int rc; +@@ -839,34 +824,6 @@ void pcie_shutdown_notification(struct controller *ctrl) + } + } + +-static int pcie_init_slot(struct controller *ctrl) +-{ +- struct pci_bus *subordinate = ctrl_dev(ctrl)->subordinate; +- struct slot *slot; +- +- slot = kzalloc(sizeof(*slot), GFP_KERNEL); +- if (!slot) +- return -ENOMEM; +- +- down_read(&pci_bus_sem); +- slot->state = list_empty(&subordinate->devices) ? OFF_STATE : ON_STATE; +- up_read(&pci_bus_sem); +- +- slot->ctrl = ctrl; +- mutex_init(&slot->lock); +- INIT_DELAYED_WORK(&slot->work, pciehp_queue_pushbutton_work); +- ctrl->slot = slot; +- return 0; +-} +- +-static void pcie_cleanup_slot(struct controller *ctrl) +-{ +- struct slot *slot = ctrl->slot; +- +- cancel_delayed_work_sync(&slot->work); +- kfree(slot); +-} +- + static inline void dbg_ctrl(struct controller *ctrl) + { + struct pci_dev *pdev = ctrl->pcie->port; +@@ -890,10 +847,11 @@ struct controller *pcie_init(struct pcie_device *dev) + u32 slot_cap, link_cap; + u8 poweron; + struct pci_dev *pdev = dev->port; ++ struct pci_bus *subordinate = pdev->subordinate; + + ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); + if (!ctrl) +- goto abort; ++ return NULL; + + ctrl->pcie = dev; + pcie_capability_read_dword(pdev, PCI_EXP_SLTCAP, &slot_cap); +@@ -910,11 +868,17 @@ struct controller *pcie_init(struct pcie_device *dev) + + ctrl->slot_cap = slot_cap; + mutex_init(&ctrl->ctrl_lock); ++ mutex_init(&ctrl->lock); + init_rwsem(&ctrl->reset_lock); + init_waitqueue_head(&ctrl->requester); + init_waitqueue_head(&ctrl->queue); ++ INIT_DELAYED_WORK(&ctrl->work, pciehp_queue_pushbutton_work); + dbg_ctrl(ctrl); + ++ down_read(&pci_bus_sem); ++ ctrl->state = list_empty(&subordinate->devices) ? OFF_STATE : ON_STATE; ++ up_read(&pci_bus_sem); ++ + /* Check if Data Link Layer Link Active Reporting is implemented */ + pcie_capability_read_dword(pdev, PCI_EXP_LNKCAP, &link_cap); + if (link_cap & PCI_EXP_LNKCAP_DLLLARC) +@@ -940,32 +904,24 @@ struct controller *pcie_init(struct pcie_device *dev) + FLAG(link_cap, PCI_EXP_LNKCAP_DLLLARC), + pdev->broken_cmd_compl ? " (with Cmd Compl erratum)" : ""); + +- if (pcie_init_slot(ctrl)) +- goto abort_ctrl; +- + /* + * If empty slot's power status is on, turn power off. The IRQ isn't + * requested yet, so avoid triggering a notification with this command. + */ + if (POWER_CTRL(ctrl)) { +- pciehp_get_power_status(ctrl->slot, &poweron); ++ pciehp_get_power_status(ctrl, &poweron); + if (!pciehp_card_present_or_link_active(ctrl) && poweron) { + pcie_disable_notification(ctrl); +- pciehp_power_off_slot(ctrl->slot); ++ pciehp_power_off_slot(ctrl); + } + } + + return ctrl; +- +-abort_ctrl: +- kfree(ctrl); +-abort: +- return NULL; + } + + void pciehp_release_ctrl(struct controller *ctrl) + { +- pcie_cleanup_slot(ctrl); ++ cancel_delayed_work_sync(&ctrl->work); + kfree(ctrl); + } + +diff --git a/drivers/pci/hotplug/pciehp_pci.c b/drivers/pci/hotplug/pciehp_pci.c +index 8da87931bd45..b9c1396db6fe 100644 +--- a/drivers/pci/hotplug/pciehp_pci.c ++++ b/drivers/pci/hotplug/pciehp_pci.c +@@ -21,19 +21,18 @@ + + /** + * pciehp_configure_device() - enumerate PCI devices below a hotplug bridge +- * @p_slot: PCIe hotplug slot ++ * @ctrl: PCIe hotplug controller + * + * Enumerate PCI devices below a hotplug bridge and add them to the system. + * Return 0 on success, %-EEXIST if the devices are already enumerated or + * %-ENODEV if enumeration failed. + */ +-int pciehp_configure_device(struct slot *p_slot) ++int pciehp_configure_device(struct controller *ctrl) + { + struct pci_dev *dev; +- struct pci_dev *bridge = p_slot->ctrl->pcie->port; ++ struct pci_dev *bridge = ctrl->pcie->port; + struct pci_bus *parent = bridge->subordinate; + int num, ret = 0; +- struct controller *ctrl = p_slot->ctrl; + + pci_lock_rescan_remove(); + +@@ -71,7 +70,7 @@ int pciehp_configure_device(struct slot *p_slot) + + /** + * pciehp_unconfigure_device() - remove PCI devices below a hotplug bridge +- * @p_slot: PCIe hotplug slot ++ * @ctrl: PCIe hotplug controller + * @presence: whether the card is still present in the slot; + * true for safe removal via sysfs or an Attention Button press, + * false for surprise removal +@@ -80,12 +79,11 @@ int pciehp_configure_device(struct slot *p_slot) + * them from the system. Safely removed devices are quiesced. Surprise + * removed devices are marked as such to prevent further accesses. + */ +-void pciehp_unconfigure_device(struct slot *p_slot, bool presence) ++void pciehp_unconfigure_device(struct controller *ctrl, bool presence) + { + struct pci_dev *dev, *temp; +- struct pci_bus *parent = p_slot->ctrl->pcie->port->subordinate; ++ struct pci_bus *parent = ctrl->pcie->port->subordinate; + u16 command; +- struct controller *ctrl = p_slot->ctrl; + + ctrl_dbg(ctrl, "%s: domain:bus:dev = %04x:%02x:00\n", + __func__, pci_domain_nr(parent), parent->number); +-- +2.29.2 + +From a82359ba7d6698eeaf7ce9c50d0f57aa2d88350d Mon Sep 17 00:00:00 2001 +From: Lukas Wunner +Date: Sat, 8 Sep 2018 09:59:01 +0200 +Subject: [PATCH] PCI: pciehp: Rename controller struct members for clarity + +Of the members which were just moved from pciehp's slot struct to the +controller struct, rename "lock" to "state_lock" and rename "work" to +"button_work" for clarity. Perform the rename separately to the +unification of the two structs per Sinan's request. + +No functional change intended. + +Signed-off-by: Lukas Wunner +Signed-off-by: Bjorn Helgaas +Cc: Sinan Kaya + +Patchset: surface-sam +--- + drivers/pci/hotplug/pciehp.h | 10 +++--- + drivers/pci/hotplug/pciehp_core.c | 4 +-- + drivers/pci/hotplug/pciehp_ctrl.c | 58 +++++++++++++++---------------- + drivers/pci/hotplug/pciehp_hpc.c | 6 ++-- + 4 files changed, 39 insertions(+), 39 deletions(-) + +diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h +index bf1c9d3a64a5..2499489158cc 100644 +--- a/drivers/pci/hotplug/pciehp.h ++++ b/drivers/pci/hotplug/pciehp.h +@@ -87,9 +87,9 @@ do { \ + * @pending_events: used by the IRQ handler to save events retrieved from the + * Slot Status register for later consumption by the IRQ thread + * @state: current state machine position +- * @lock: protects reads and writes of @state; +- * protects scheduling, execution and cancellation of @work +- * @work: work item to turn the slot on or off after 5 seconds ++ * @state_lock: protects reads and writes of @state; ++ * protects scheduling, execution and cancellation of @button_work ++ * @button_work: work item to turn the slot on or off after 5 seconds + * in response to an Attention Button press + * @hotplug_slot: pointer to the structure registered with the PCI hotplug core + * @ist_running: flag to keep user request waiting while IRQ thread is running +@@ -115,8 +115,8 @@ struct controller { + unsigned int power_fault_detected; + atomic_t pending_events; + u8 state; +- struct mutex lock; +- struct delayed_work work; ++ struct mutex state_lock; ++ struct delayed_work button_work; + struct hotplug_slot *hotplug_slot; + unsigned int ist_running; + int request_result; +diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c +index 307099c720ec..7810000522dd 100644 +--- a/drivers/pci/hotplug/pciehp_core.c ++++ b/drivers/pci/hotplug/pciehp_core.c +@@ -177,7 +177,7 @@ static void pciehp_check_presence(struct controller *ctrl) + bool occupied; + + down_read(&ctrl->reset_lock); +- mutex_lock(&ctrl->lock); ++ mutex_lock(&ctrl->state_lock); + + occupied = pciehp_card_present_or_link_active(ctrl); + if ((occupied && (ctrl->state == OFF_STATE || +@@ -186,7 +186,7 @@ static void pciehp_check_presence(struct controller *ctrl) + ctrl->state == BLINKINGOFF_STATE))) + pciehp_request(ctrl, PCI_EXP_SLTSTA_PDC); + +- mutex_unlock(&ctrl->lock); ++ mutex_unlock(&ctrl->state_lock); + up_read(&ctrl->reset_lock); + } + +diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c +index dd6533a094a8..6513dd13b786 100644 +--- a/drivers/pci/hotplug/pciehp_ctrl.c ++++ b/drivers/pci/hotplug/pciehp_ctrl.c +@@ -138,9 +138,9 @@ void pciehp_request(struct controller *ctrl, int action) + void pciehp_queue_pushbutton_work(struct work_struct *work) + { + struct controller *ctrl = container_of(work, struct controller, +- work.work); ++ button_work.work); + +- mutex_lock(&ctrl->lock); ++ mutex_lock(&ctrl->state_lock); + switch (ctrl->state) { + case BLINKINGOFF_STATE: + pciehp_request(ctrl, DISABLE_SLOT); +@@ -151,12 +151,12 @@ void pciehp_queue_pushbutton_work(struct work_struct *work) + default: + break; + } +- mutex_unlock(&ctrl->lock); ++ mutex_unlock(&ctrl->state_lock); + } + + void pciehp_handle_button_press(struct controller *ctrl) + { +- mutex_lock(&ctrl->lock); ++ mutex_lock(&ctrl->state_lock); + switch (ctrl->state) { + case OFF_STATE: + case ON_STATE: +@@ -172,7 +172,7 @@ void pciehp_handle_button_press(struct controller *ctrl) + /* blink green LED and turn off amber */ + pciehp_green_led_blink(ctrl); + pciehp_set_attention_status(ctrl, 0); +- schedule_delayed_work(&ctrl->work, 5 * HZ); ++ schedule_delayed_work(&ctrl->button_work, 5 * HZ); + break; + case BLINKINGOFF_STATE: + case BLINKINGON_STATE: +@@ -182,7 +182,7 @@ void pciehp_handle_button_press(struct controller *ctrl) + * expires to cancel hot-add or hot-remove + */ + ctrl_info(ctrl, "Slot(%s): Button cancel\n", slot_name(ctrl)); +- cancel_delayed_work(&ctrl->work); ++ cancel_delayed_work(&ctrl->button_work); + if (ctrl->state == BLINKINGOFF_STATE) { + ctrl->state = ON_STATE; + pciehp_green_led_on(ctrl); +@@ -199,20 +199,20 @@ void pciehp_handle_button_press(struct controller *ctrl) + slot_name(ctrl), ctrl->state); + break; + } +- mutex_unlock(&ctrl->lock); ++ mutex_unlock(&ctrl->state_lock); + } + + void pciehp_handle_disable_request(struct controller *ctrl) + { +- mutex_lock(&ctrl->lock); ++ mutex_lock(&ctrl->state_lock); + switch (ctrl->state) { + case BLINKINGON_STATE: + case BLINKINGOFF_STATE: +- cancel_delayed_work(&ctrl->work); ++ cancel_delayed_work(&ctrl->button_work); + break; + } + ctrl->state = POWEROFF_STATE; +- mutex_unlock(&ctrl->lock); ++ mutex_unlock(&ctrl->state_lock); + + ctrl->request_result = pciehp_disable_slot(ctrl, SAFE_REMOVAL); + } +@@ -225,14 +225,14 @@ void pciehp_handle_presence_or_link_change(struct controller *ctrl, u32 events) + * If the slot is on and presence or link has changed, turn it off. + * Even if it's occupied again, we cannot assume the card is the same. + */ +- mutex_lock(&ctrl->lock); ++ mutex_lock(&ctrl->state_lock); + switch (ctrl->state) { + case BLINKINGOFF_STATE: +- cancel_delayed_work(&ctrl->work); ++ cancel_delayed_work(&ctrl->button_work); + /* fall through */ + case ON_STATE: + ctrl->state = POWEROFF_STATE; +- mutex_unlock(&ctrl->lock); ++ mutex_unlock(&ctrl->state_lock); + if (events & PCI_EXP_SLTSTA_DLLSC) + ctrl_info(ctrl, "Slot(%s): Link Down\n", + slot_name(ctrl)); +@@ -242,26 +242,26 @@ void pciehp_handle_presence_or_link_change(struct controller *ctrl, u32 events) + pciehp_disable_slot(ctrl, SURPRISE_REMOVAL); + break; + default: +- mutex_unlock(&ctrl->lock); ++ mutex_unlock(&ctrl->state_lock); + break; + } + + /* Turn the slot on if it's occupied or link is up */ +- mutex_lock(&ctrl->lock); ++ mutex_lock(&ctrl->state_lock); + present = pciehp_card_present(ctrl); + link_active = pciehp_check_link_active(ctrl); + if (!present && !link_active) { +- mutex_unlock(&ctrl->lock); ++ mutex_unlock(&ctrl->state_lock); + return; + } + + switch (ctrl->state) { + case BLINKINGON_STATE: +- cancel_delayed_work(&ctrl->work); ++ cancel_delayed_work(&ctrl->button_work); + /* fall through */ + case OFF_STATE: + ctrl->state = POWERON_STATE; +- mutex_unlock(&ctrl->lock); ++ mutex_unlock(&ctrl->state_lock); + if (present) + ctrl_info(ctrl, "Slot(%s): Card present\n", + slot_name(ctrl)); +@@ -271,7 +271,7 @@ void pciehp_handle_presence_or_link_change(struct controller *ctrl, u32 events) + ctrl->request_result = pciehp_enable_slot(ctrl); + break; + default: +- mutex_unlock(&ctrl->lock); ++ mutex_unlock(&ctrl->state_lock); + break; + } + } +@@ -311,9 +311,9 @@ static int pciehp_enable_slot(struct controller *ctrl) + pciehp_green_led_off(ctrl); /* may be blinking */ + pm_runtime_put(&ctrl->pcie->port->dev); + +- mutex_lock(&ctrl->lock); ++ mutex_lock(&ctrl->state_lock); + ctrl->state = ret ? OFF_STATE : ON_STATE; +- mutex_unlock(&ctrl->lock); ++ mutex_unlock(&ctrl->state_lock); + + return ret; + } +@@ -343,9 +343,9 @@ static int pciehp_disable_slot(struct controller *ctrl, bool safe_removal) + ret = __pciehp_disable_slot(ctrl, safe_removal); + pm_runtime_put(&ctrl->pcie->port->dev); + +- mutex_lock(&ctrl->lock); ++ mutex_lock(&ctrl->state_lock); + ctrl->state = OFF_STATE; +- mutex_unlock(&ctrl->lock); ++ mutex_unlock(&ctrl->state_lock); + + return ret; + } +@@ -354,11 +354,11 @@ int pciehp_sysfs_enable_slot(struct hotplug_slot *hotplug_slot) + { + struct controller *ctrl = hotplug_slot->private; + +- mutex_lock(&ctrl->lock); ++ mutex_lock(&ctrl->state_lock); + switch (ctrl->state) { + case BLINKINGON_STATE: + case OFF_STATE: +- mutex_unlock(&ctrl->lock); ++ mutex_unlock(&ctrl->state_lock); + /* + * The IRQ thread becomes a no-op if the user pulls out the + * card before the thread wakes up, so initialize to -ENODEV. +@@ -384,7 +384,7 @@ int pciehp_sysfs_enable_slot(struct hotplug_slot *hotplug_slot) + slot_name(ctrl), ctrl->state); + break; + } +- mutex_unlock(&ctrl->lock); ++ mutex_unlock(&ctrl->state_lock); + + return -ENODEV; + } +@@ -393,11 +393,11 @@ int pciehp_sysfs_disable_slot(struct hotplug_slot *hotplug_slot) + { + struct controller *ctrl = hotplug_slot->private; + +- mutex_lock(&ctrl->lock); ++ mutex_lock(&ctrl->state_lock); + switch (ctrl->state) { + case BLINKINGOFF_STATE: + case ON_STATE: +- mutex_unlock(&ctrl->lock); ++ mutex_unlock(&ctrl->state_lock); + pciehp_request(ctrl, DISABLE_SLOT); + wait_event(ctrl->requester, + !atomic_read(&ctrl->pending_events) && +@@ -418,7 +418,7 @@ int pciehp_sysfs_disable_slot(struct hotplug_slot *hotplug_slot) + slot_name(ctrl), ctrl->state); + break; + } +- mutex_unlock(&ctrl->lock); ++ mutex_unlock(&ctrl->state_lock); + + return -ENODEV; + } +diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c +index 4a17d71e15d3..e4d6ec960630 100644 +--- a/drivers/pci/hotplug/pciehp_hpc.c ++++ b/drivers/pci/hotplug/pciehp_hpc.c +@@ -868,11 +868,11 @@ struct controller *pcie_init(struct pcie_device *dev) + + ctrl->slot_cap = slot_cap; + mutex_init(&ctrl->ctrl_lock); +- mutex_init(&ctrl->lock); ++ mutex_init(&ctrl->state_lock); + init_rwsem(&ctrl->reset_lock); + init_waitqueue_head(&ctrl->requester); + init_waitqueue_head(&ctrl->queue); +- INIT_DELAYED_WORK(&ctrl->work, pciehp_queue_pushbutton_work); ++ INIT_DELAYED_WORK(&ctrl->button_work, pciehp_queue_pushbutton_work); + dbg_ctrl(ctrl); + + down_read(&pci_bus_sem); +@@ -921,7 +921,7 @@ struct controller *pcie_init(struct pcie_device *dev) + + void pciehp_release_ctrl(struct controller *ctrl) + { +- cancel_delayed_work_sync(&ctrl->work); ++ cancel_delayed_work_sync(&ctrl->button_work); + kfree(ctrl); + } + +-- +2.29.2 + +From 15686c23a0c8f9e876195fc50469bef486a6d5a0 Mon Sep 17 00:00:00 2001 +From: Lukas Wunner +Date: Sat, 8 Sep 2018 09:59:01 +0200 +Subject: [PATCH] PCI: pciehp: Reshuffle controller struct for clarity + +The members in pciehp's controller struct are arranged in a seemingly +arbitrary order and have grown to an amount that I no longer consider +easily graspable by contributors. + +Sort the members into 5 rubrics: +* Slot Capabilities register and quirks +* Slot Control register access +* Slot Status register event handling +* state machine +* hotplug core interface + +Obviously, this is just my personal bikeshed color and if anyone has a +better idea, please come forward. Any ordering will do as long as the +information is presented in a manageable manner. + +No functional change intended. + +Signed-off-by: Lukas Wunner +Signed-off-by: Bjorn Helgaas + +Patchset: surface-sam +--- + drivers/pci/hotplug/pciehp.h | 57 ++++++++++++++++++++---------------- + 1 file changed, 31 insertions(+), 26 deletions(-) + +diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h +index 2499489158cc..df82a0335515 100644 +--- a/drivers/pci/hotplug/pciehp.h ++++ b/drivers/pci/hotplug/pciehp.h +@@ -60,38 +60,38 @@ do { \ + + /** + * struct controller - PCIe hotplug controller +- * @ctrl_lock: serializes writes to the Slot Control register + * @pcie: pointer to the controller's PCIe port service device +- * @reset_lock: prevents access to the Data Link Layer Link Active bit in the +- * Link Status register and to the Presence Detect State bit in the Slot +- * Status register during a slot reset which may cause them to flap +- * @queue: wait queue to wake up on reception of a Command Completed event, +- * used for synchronous writes to the Slot Control register + * @slot_cap: cached copy of the Slot Capabilities register ++ * @link_active_reporting: cached copy of Data Link Layer Link Active Reporting ++ * Capable bit in Link Capabilities register; if this bit is zero, the ++ * Data Link Layer Link Active bit in the Link Status register will never ++ * be set and the driver is thus confined to wait 1 second before assuming ++ * the link to a hotplugged device is up and accessing it + * @slot_ctrl: cached copy of the Slot Control register +- * @poll_thread: thread to poll for slot events if no IRQ is available, +- * enabled with pciehp_poll_mode module parameter ++ * @ctrl_lock: serializes writes to the Slot Control register + * @cmd_started: jiffies when the Slot Control register was last written; + * the next write is allowed 1 second later, absent a Command Completed + * interrupt (PCIe r4.0, sec 6.7.3.2) + * @cmd_busy: flag set on Slot Control register write, cleared by IRQ handler + * on reception of a Command Completed event +- * @link_active_reporting: cached copy of Data Link Layer Link Active Reporting +- * Capable bit in Link Capabilities register; if this bit is zero, the +- * Data Link Layer Link Active bit in the Link Status register will never +- * be set and the driver is thus confined to wait 1 second before assuming +- * the link to a hotplugged device is up and accessing it ++ * @queue: wait queue to wake up on reception of a Command Completed event, ++ * used for synchronous writes to the Slot Control register ++ * @pending_events: used by the IRQ handler to save events retrieved from the ++ * Slot Status register for later consumption by the IRQ thread + * @notification_enabled: whether the IRQ was requested successfully + * @power_fault_detected: whether a power fault was detected by the hardware + * that has not yet been cleared by the user +- * @pending_events: used by the IRQ handler to save events retrieved from the +- * Slot Status register for later consumption by the IRQ thread ++ * @poll_thread: thread to poll for slot events if no IRQ is available, ++ * enabled with pciehp_poll_mode module parameter + * @state: current state machine position + * @state_lock: protects reads and writes of @state; + * protects scheduling, execution and cancellation of @button_work + * @button_work: work item to turn the slot on or off after 5 seconds + * in response to an Attention Button press + * @hotplug_slot: pointer to the structure registered with the PCI hotplug core ++ * @reset_lock: prevents access to the Data Link Layer Link Active bit in the ++ * Link Status register and to the Presence Detect State bit in the Slot ++ * Status register during a slot reset which may cause them to flap + * @ist_running: flag to keep user request waiting while IRQ thread is running + * @request_result: result of last user request submitted to the IRQ thread + * @requester: wait queue to wake up on completion of user request, +@@ -101,23 +101,28 @@ do { \ + * unlike other drivers, the two aren't represented by separate structures. + */ + struct controller { +- struct mutex ctrl_lock; + struct pcie_device *pcie; +- struct rw_semaphore reset_lock; +- wait_queue_head_t queue; +- u32 slot_cap; +- u16 slot_ctrl; +- struct task_struct *poll_thread; +- unsigned long cmd_started; /* jiffies */ +- unsigned int cmd_busy:1; ++ ++ u32 slot_cap; /* capabilities and quirks */ + unsigned int link_active_reporting:1; ++ ++ u16 slot_ctrl; /* control register access */ ++ struct mutex ctrl_lock; ++ unsigned long cmd_started; ++ unsigned int cmd_busy:1; ++ wait_queue_head_t queue; ++ ++ atomic_t pending_events; /* event handling */ + unsigned int notification_enabled:1; + unsigned int power_fault_detected; +- atomic_t pending_events; +- u8 state; ++ struct task_struct *poll_thread; ++ ++ u8 state; /* state machine */ + struct mutex state_lock; + struct delayed_work button_work; +- struct hotplug_slot *hotplug_slot; ++ ++ struct hotplug_slot *hotplug_slot; /* hotplug core interface */ ++ struct rw_semaphore reset_lock; + unsigned int ist_running; + int request_result; + wait_queue_head_t requester; +-- +2.29.2 + +From 8928c2cdfbf667c87ce75074627ee4f2de854d0c Mon Sep 17 00:00:00 2001 +From: Keith Busch +Date: Thu, 20 Sep 2018 10:27:17 -0600 +Subject: [PATCH] PCI: Make link active reporting detection generic + +The spec has timing requirements when waiting for a link to become active +after a conventional reset. Implement those hard delays when waiting for +an active link so pciehp and dpc drivers don't need to duplicate this. + +For devices that don't support data link layer active reporting, wait the +fixed time recommended by the PCIe spec. + +Signed-off-by: Keith Busch +[bhelgaas: changelog] +Signed-off-by: Bjorn Helgaas +Reviewed-by: Sinan Kaya + +Patchset: surface-sam +--- + drivers/pci/hotplug/pciehp.h | 6 ------ + drivers/pci/hotplug/pciehp_hpc.c | 22 ++------------------- + drivers/pci/pci.c | 33 ++++++++++++++++++++++++++------ + drivers/pci/pcie/dpc.c | 4 +++- + drivers/pci/probe.c | 1 + + include/linux/pci.h | 1 + + 6 files changed, 34 insertions(+), 33 deletions(-) + +diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h +index df82a0335515..4a6f46ca3b03 100644 +--- a/drivers/pci/hotplug/pciehp.h ++++ b/drivers/pci/hotplug/pciehp.h +@@ -62,11 +62,6 @@ do { \ + * struct controller - PCIe hotplug controller + * @pcie: pointer to the controller's PCIe port service device + * @slot_cap: cached copy of the Slot Capabilities register +- * @link_active_reporting: cached copy of Data Link Layer Link Active Reporting +- * Capable bit in Link Capabilities register; if this bit is zero, the +- * Data Link Layer Link Active bit in the Link Status register will never +- * be set and the driver is thus confined to wait 1 second before assuming +- * the link to a hotplugged device is up and accessing it + * @slot_ctrl: cached copy of the Slot Control register + * @ctrl_lock: serializes writes to the Slot Control register + * @cmd_started: jiffies when the Slot Control register was last written; +@@ -104,7 +99,6 @@ struct controller { + struct pcie_device *pcie; + + u32 slot_cap; /* capabilities and quirks */ +- unsigned int link_active_reporting:1; + + u16 slot_ctrl; /* control register access */ + struct mutex ctrl_lock; +diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c +index e4d6ec960630..0693870a9e24 100644 +--- a/drivers/pci/hotplug/pciehp_hpc.c ++++ b/drivers/pci/hotplug/pciehp_hpc.c +@@ -214,13 +214,6 @@ bool pciehp_check_link_active(struct controller *ctrl) + return ret; + } + +-static void pcie_wait_link_active(struct controller *ctrl) +-{ +- struct pci_dev *pdev = ctrl_dev(ctrl); +- +- pcie_wait_for_link(pdev, true); +-} +- + static bool pci_bus_check_dev(struct pci_bus *bus, int devfn) + { + u32 l; +@@ -253,18 +246,9 @@ int pciehp_check_link_status(struct controller *ctrl) + bool found; + u16 lnk_status; + +- /* +- * Data Link Layer Link Active Reporting must be capable for +- * hot-plug capable downstream port. But old controller might +- * not implement it. In this case, we wait for 1000 ms. +- */ +- if (ctrl->link_active_reporting) +- pcie_wait_link_active(ctrl); +- else +- msleep(1000); ++ if (!pcie_wait_for_link(pdev, true)) ++ return -1; + +- /* wait 100ms before read pci conf, and try in 1s */ +- msleep(100); + found = pci_bus_check_dev(ctrl->pcie->port->subordinate, + PCI_DEVFN(0, 0)); + +@@ -881,8 +865,6 @@ struct controller *pcie_init(struct pcie_device *dev) + + /* Check if Data Link Layer Link Active Reporting is implemented */ + pcie_capability_read_dword(pdev, PCI_EXP_LNKCAP, &link_cap); +- if (link_cap & PCI_EXP_LNKCAP_DLLLARC) +- ctrl->link_active_reporting = 1; + + /* Clear all remaining event bits in Slot Status register. */ + pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, +diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c +index 57a87a001b4f..99292b338401 100644 +--- a/drivers/pci/pci.c ++++ b/drivers/pci/pci.c +@@ -4525,21 +4525,42 @@ bool pcie_wait_for_link(struct pci_dev *pdev, bool active) + bool ret; + u16 lnk_status; + ++ /* ++ * Some controllers might not implement link active reporting. In this ++ * case, we wait for 1000 + 100 ms. ++ */ ++ if (!pdev->link_active_reporting) { ++ msleep(1100); ++ return true; ++ } ++ ++ /* ++ * PCIe r4.0 sec 6.6.1, a component must enter LTSSM Detect within 20ms, ++ * after which we should expect an link active if the reset was ++ * successful. If so, software must wait a minimum 100ms before sending ++ * configuration requests to devices downstream this port. ++ * ++ * If the link fails to activate, either the device was physically ++ * removed or the link is permanently failed. ++ */ ++ if (active) ++ msleep(20); + for (;;) { + pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status); + ret = !!(lnk_status & PCI_EXP_LNKSTA_DLLLA); + if (ret == active) +- return true; ++ break; + if (timeout <= 0) + break; + msleep(10); + timeout -= 10; + } +- +- pci_info(pdev, "Data Link Layer Link Active not %s in 1000 msec\n", +- active ? "set" : "cleared"); +- +- return false; ++ if (active && ret) ++ msleep(100); ++ else if (ret != active) ++ pci_info(pdev, "Data Link Layer Link Active not %s in 1000 msec\n", ++ active ? "set" : "cleared"); ++ return ret == active; + } + + void pci_reset_secondary_bus(struct pci_dev *dev) +diff --git a/drivers/pci/pcie/dpc.c b/drivers/pci/pcie/dpc.c +index 118b5bcae42e..87f8d8628d94 100644 +--- a/drivers/pci/pcie/dpc.c ++++ b/drivers/pci/pcie/dpc.c +@@ -93,10 +93,12 @@ static pci_ers_result_t dpc_reset_link(struct pci_dev *pdev) + pci_write_config_word(pdev, cap + PCI_EXP_DPC_STATUS, + PCI_EXP_DPC_STATUS_TRIGGER); + ++ if (!pcie_wait_for_link(pdev, true)) ++ return PCI_ERS_RESULT_DISCONNECT; ++ + return PCI_ERS_RESULT_RECOVERED; + } + +- + static void dpc_process_rp_pio_error(struct dpc_dev *dpc) + { + struct device *dev = &dpc->dev->device; +diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c +index 9a5b6a8e2502..6fc19c35582f 100644 +--- a/drivers/pci/probe.c ++++ b/drivers/pci/probe.c +@@ -769,6 +769,7 @@ static void pci_set_bus_speed(struct pci_bus *bus) + + pcie_capability_read_dword(bridge, PCI_EXP_LNKCAP, &linkcap); + bus->max_bus_speed = pcie_link_speed[linkcap & PCI_EXP_LNKCAP_SLS]; ++ bridge->link_active_reporting = !!(linkcap & PCI_EXP_LNKCAP_DLLLARC); + + pcie_capability_read_word(bridge, PCI_EXP_LNKSTA, &linksta); + pcie_update_link_speed(bus, linksta); +diff --git a/include/linux/pci.h b/include/linux/pci.h +index ec6c48ecd7d5..74c8e9190fed 100644 +--- a/include/linux/pci.h ++++ b/include/linux/pci.h +@@ -407,6 +407,7 @@ struct pci_dev { + unsigned int has_secondary_link:1; + unsigned int non_compliant_bars:1; /* Broken BARs; ignore them */ + unsigned int is_probed:1; /* Device probing in progress */ ++ unsigned int link_active_reporting:1;/* Device capable of reporting link active */ + pci_dev_flags_t dev_flags; + atomic_t enable_cnt; /* pci_enable_device has been called */ + +-- +2.29.2 + +From 828f314baaea82376c69919db201d7a9468c7544 Mon Sep 17 00:00:00 2001 +From: Mika Westerberg +Date: Thu, 27 Sep 2018 16:53:53 -0500 +Subject: [PATCH] PCI: Do not skip power-managed bridges in pci_enable_wake() + +Commit baecc470d5fd ("PCI / PM: Skip bridges in pci_enable_wake()") changed +pci_enable_wake() so that all bridges are skipped when wakeup is enabled +(or disabled) with the reasoning that bridges can only signal wakeup on +behalf of their subordinate devices. + +However, there are bridges that can signal wakeup themselves. For example +PCIe downstream and root ports supporting hotplug may signal wakeup upon +hotplug event. + +For this reason change pci_enable_wake() so that it skips all bridges +except those that we power manage (->bridge_d3 is set). Those are the ones +that can go into low power states and may need to signal wakeup. + +Signed-off-by: Mika Westerberg +Signed-off-by: Bjorn Helgaas +Reviewed-by: Rafael J. Wysocki + +Patchset: surface-sam +--- + drivers/pci/pci.c | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c +index 99292b338401..2898b7c1ac4b 100644 +--- a/drivers/pci/pci.c ++++ b/drivers/pci/pci.c +@@ -2151,10 +2151,13 @@ static int __pci_enable_wake(struct pci_dev *dev, pci_power_t state, bool enable + int ret = 0; + + /* +- * Bridges can only signal wakeup on behalf of subordinate devices, +- * but that is set up elsewhere, so skip them. ++ * Bridges that are not power-manageable directly only signal ++ * wakeup on behalf of subordinate devices which is set up ++ * elsewhere, so skip them. However, bridges that are ++ * power-manageable may signal wakeup for themselves (for example, ++ * on a hotplug event) and they need to be covered here. + */ +- if (pci_has_subordinate(dev)) ++ if (!pci_power_manageable(dev)) + return 0; + + /* Don't do the same thing twice in a row for one device. */ +-- +2.29.2 + +From 2f6cfa13e7a7d6413bea8b242e4838c632013c77 Mon Sep 17 00:00:00 2001 +From: Mika Westerberg +Date: Thu, 27 Sep 2018 16:38:19 -0500 +Subject: [PATCH] PCI: pciehp: Disable hotplug interrupt during suspend + +When PCIe hotplug port is transitioned into D3hot, the link to the +downstream component will go down. If hotplug interrupt generation is +enabled when that happens, it will trigger immediately, waking up the +system and bringing the link back up. + +To prevent this, disable hotplug interrupt generation when system suspend +is entered. This does not prevent wakeup from low power states according +to PCIe 4.0 spec section 6.7.3.4: + + Software enables a hot-plug event to generate a wakeup event by + enabling software notification of the event as described in Section + 6.7.3.1. Note that in order for software to disable interrupt generation + while keeping wakeup generation enabled, the Hot-Plug Interrupt Enable + bit must be cleared. + +So as long as we have set the slot event mask accordingly, wakeup should +work even if slot interrupt is disabled. The port should trigger wake and +then send PME to the root port when the PCIe hierarchy is brought back up. + +Limit this to systems using native PME mechanism to make sure older Apple +systems depending on commit e3354628c376 ("PCI: pciehp: Support interrupts +sent from D3hot") still continue working. + +Signed-off-by: Mika Westerberg +Signed-off-by: Bjorn Helgaas +Reviewed-by: Rafael J. Wysocki + +Patchset: surface-sam +--- + drivers/pci/hotplug/pciehp.h | 2 ++ + drivers/pci/hotplug/pciehp_core.c | 18 ++++++++++++++++++ + drivers/pci/hotplug/pciehp_hpc.c | 10 ++++++++++ + 3 files changed, 30 insertions(+) + +diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h +index 4a6f46ca3b03..80d3f76c1193 100644 +--- a/drivers/pci/hotplug/pciehp.h ++++ b/drivers/pci/hotplug/pciehp.h +@@ -178,6 +178,8 @@ struct controller *pcie_init(struct pcie_device *dev); + int pcie_init_notification(struct controller *ctrl); + void pcie_shutdown_notification(struct controller *ctrl); + void pcie_clear_hotplug_events(struct controller *ctrl); ++void pcie_enable_interrupt(struct controller *ctrl); ++void pcie_disable_interrupt(struct controller *ctrl); + int pciehp_power_on_slot(struct controller *ctrl); + void pciehp_power_off_slot(struct controller *ctrl); + void pciehp_get_power_status(struct controller *ctrl, u8 *status); +diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c +index 7810000522dd..8e6e4ce869fb 100644 +--- a/drivers/pci/hotplug/pciehp_core.c ++++ b/drivers/pci/hotplug/pciehp_core.c +@@ -261,8 +261,23 @@ static void pciehp_remove(struct pcie_device *dev) + } + + #ifdef CONFIG_PM ++static bool pme_is_native(struct pcie_device *dev) ++{ ++ const struct pci_host_bridge *host; ++ ++ host = pci_find_host_bridge(dev->port->bus); ++ return pcie_ports_native || host->native_pme; ++} ++ + static int pciehp_suspend(struct pcie_device *dev) + { ++ /* ++ * Disable hotplug interrupt so that it does not trigger ++ * immediately when the downstream link goes down. ++ */ ++ if (pme_is_native(dev)) ++ pcie_disable_interrupt(get_service_data(dev)); ++ + return 0; + } + +@@ -285,6 +300,9 @@ static int pciehp_resume(struct pcie_device *dev) + { + struct controller *ctrl = get_service_data(dev); + ++ if (pme_is_native(dev)) ++ pcie_enable_interrupt(ctrl); ++ + pciehp_check_presence(ctrl); + + return 0; +diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c +index 0693870a9e24..b5c7f5ef597a 100644 +--- a/drivers/pci/hotplug/pciehp_hpc.c ++++ b/drivers/pci/hotplug/pciehp_hpc.c +@@ -748,6 +748,16 @@ void pcie_clear_hotplug_events(struct controller *ctrl) + PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_DLLSC); + } + ++void pcie_enable_interrupt(struct controller *ctrl) ++{ ++ pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_HPIE, PCI_EXP_SLTCTL_HPIE); ++} ++ ++void pcie_disable_interrupt(struct controller *ctrl) ++{ ++ pcie_write_cmd(ctrl, 0, PCI_EXP_SLTCTL_HPIE); ++} ++ + /* + * pciehp has a 1:1 bus:slot relationship so we ultimately want a secondary + * bus reset of the bridge, but at the same time we want to ensure that it is +-- +2.29.2 + +From 6bb05aa2d4cdb7b8f72c0247ce047d27c2805a1c Mon Sep 17 00:00:00 2001 +From: Mika Westerberg +Date: Thu, 27 Sep 2018 16:41:46 -0500 +Subject: [PATCH] PCI: pciehp: Do not handle events if interrupts are masked + +PCIe native hotplug shares MSI vector with native PME so the interrupt +handler might get called even the hotplug interrupt is masked. In that case +we should not handle any events because the interrupt was not meant for us. + +Modify the PCIe hotplug interrupt handler to check this accordingly and +bail out if it finds out that the interrupt was not about hotplug. + +Signed-off-by: Mika Westerberg +Signed-off-by: Bjorn Helgaas +Reviewed-by: Lukas Wunner + +Patchset: surface-sam +--- + drivers/pci/hotplug/pciehp_hpc.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c +index b5c7f5ef597a..242b9f30210a 100644 +--- a/drivers/pci/hotplug/pciehp_hpc.c ++++ b/drivers/pci/hotplug/pciehp_hpc.c +@@ -518,9 +518,11 @@ static irqreturn_t pciehp_isr(int irq, void *dev_id) + u16 status, events = 0; + + /* +- * Interrupts only occur in D3hot or shallower (PCIe r4.0, sec 6.7.3.4). ++ * Interrupts only occur in D3hot or shallower and only if enabled ++ * in the Slot Control register (PCIe r4.0, sec 6.7.3.4). + */ +- if (pdev->current_state == PCI_D3cold) ++ if (pdev->current_state == PCI_D3cold || ++ (!(ctrl->slot_ctrl & PCI_EXP_SLTCTL_HPIE) && !pciehp_poll_mode)) + return IRQ_NONE; + + /* +-- +2.29.2 + +From 776d86cb203ee0b0aa77dbbb7ceb675fec976221 Mon Sep 17 00:00:00 2001 +From: Mika Westerberg +Date: Thu, 27 Sep 2018 16:41:47 -0500 +Subject: [PATCH] PCI/portdrv: Resume upon exit from system suspend if left + runtime suspended + +Currently we try to keep PCIe ports runtime suspended over system suspend +if possible. This mostly happens when entering suspend-to-idle because +there is no need to re-configure wake settings. + +This causes problems if the parent port goes into D3cold and it gets +resumed upon exit from system suspend. This may happen for example if the +port is part of PCIe switch and the same switch is connected to a PCIe +endpoint that needs to be resumed. The way exit from D3cold works according +PCIe 4.0 spec 5.3.1.4.2 is that power is restored and cold reset is +signaled. After this the device is in D0unitialized state keeping PME +context if it supports wake from D3cold. + +The problem occurs when a PCIe hotplug port is left suspended and the +parent port goes into D3cold and back to D0: the port keeps its PME context +but since everything else is reset back to defaults (D0unitialized) it is +not set to detect hotplug events anymore. + +For this reason change the PCIe portdrv power management logic so that it +is fine to keep the port runtime suspended over system suspend but it needs +to be resumed upon exit to make sure it gets properly re-initialized. + +Signed-off-by: Mika Westerberg +Signed-off-by: Bjorn Helgaas + +Patchset: surface-sam +--- + drivers/pci/pcie/portdrv_pci.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c +index 23a5a0c2c3fe..5badf8a1ce0a 100644 +--- a/drivers/pci/pcie/portdrv_pci.c ++++ b/drivers/pci/pcie/portdrv_pci.c +@@ -109,8 +109,8 @@ static int pcie_portdrv_probe(struct pci_dev *dev, + + pci_save_state(dev); + +- dev_pm_set_driver_flags(&dev->dev, DPM_FLAG_SMART_SUSPEND | +- DPM_FLAG_LEAVE_SUSPENDED); ++ dev_pm_set_driver_flags(&dev->dev, DPM_FLAG_NEVER_SKIP | ++ DPM_FLAG_SMART_SUSPEND); + + if (pci_bridge_d3_possible(dev)) { + /* +-- +2.29.2 + +From a0b5807960f56c96f7ac90fd19c7937bf8c01c15 Mon Sep 17 00:00:00 2001 +From: Mika Westerberg +Date: Thu, 27 Sep 2018 16:41:48 -0500 +Subject: [PATCH] PCI/portdrv: Add runtime PM hooks for port service drivers + +When PCIe port is runtime suspended/resumed some extra steps might be +needed to be executed from the port service driver side. For instance we +may need to disable PCIe hotplug interrupt to prevent it from triggering +immediately when PCIe link to the downstream component goes down. + +To make the above possible add optional ->runtime_suspend() and +->runtime_resume() callbacks to struct pcie_port_service_driver and call +them for each port service in runtime suspend/resume callbacks of portdrv. + +Signed-off-by: Mika Westerberg +[bhelgaas: adjust "slot->state" for 5790a9c78e78 ("PCI: pciehp: Unify +controller and slot structs")] +Signed-off-by: Bjorn Helgaas +Reviewed-by: Rafael J. Wysocki + +Patchset: surface-sam +--- + drivers/pci/pcie/portdrv.h | 4 ++++ + drivers/pci/pcie/portdrv_core.c | 20 ++++++++++++++++++++ + drivers/pci/pcie/portdrv_pci.c | 10 ++++------ + 3 files changed, 28 insertions(+), 6 deletions(-) + +diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h +index 2498b2d34009..abfdc2ae7979 100644 +--- a/drivers/pci/pcie/portdrv.h ++++ b/drivers/pci/pcie/portdrv.h +@@ -76,6 +76,8 @@ struct pcie_port_service_driver { + int (*suspend) (struct pcie_device *dev); + int (*resume_noirq) (struct pcie_device *dev); + int (*resume) (struct pcie_device *dev); ++ int (*runtime_suspend) (struct pcie_device *dev); ++ int (*runtime_resume) (struct pcie_device *dev); + + /* Device driver may resume normal operations */ + void (*error_resume)(struct pci_dev *dev); +@@ -109,6 +111,8 @@ int pcie_port_device_register(struct pci_dev *dev); + int pcie_port_device_suspend(struct device *dev); + int pcie_port_device_resume_noirq(struct device *dev); + int pcie_port_device_resume(struct device *dev); ++int pcie_port_device_runtime_suspend(struct device *dev); ++int pcie_port_device_runtime_resume(struct device *dev); + #endif + void pcie_port_device_remove(struct pci_dev *dev); + int __must_check pcie_port_bus_register(void); +diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c +index 7c37d815229e..6542c48c7f59 100644 +--- a/drivers/pci/pcie/portdrv_core.c ++++ b/drivers/pci/pcie/portdrv_core.c +@@ -395,6 +395,26 @@ int pcie_port_device_resume(struct device *dev) + size_t off = offsetof(struct pcie_port_service_driver, resume); + return device_for_each_child(dev, &off, pm_iter); + } ++ ++/** ++ * pcie_port_device_runtime_suspend - runtime suspend port services ++ * @dev: PCI Express port to handle ++ */ ++int pcie_port_device_runtime_suspend(struct device *dev) ++{ ++ size_t off = offsetof(struct pcie_port_service_driver, runtime_suspend); ++ return device_for_each_child(dev, &off, pm_iter); ++} ++ ++/** ++ * pcie_port_device_runtime_resume - runtime resume port services ++ * @dev: PCI Express port to handle ++ */ ++int pcie_port_device_runtime_resume(struct device *dev) ++{ ++ size_t off = offsetof(struct pcie_port_service_driver, runtime_resume); ++ return device_for_each_child(dev, &off, pm_iter); ++} + #endif /* PM */ + + static int remove_iter(struct device *dev, void *data) +diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c +index 5badf8a1ce0a..59d2567e2db2 100644 +--- a/drivers/pci/pcie/portdrv_pci.c ++++ b/drivers/pci/pcie/portdrv_pci.c +@@ -45,12 +45,10 @@ __setup("pcie_ports=", pcie_port_setup); + #ifdef CONFIG_PM + static int pcie_port_runtime_suspend(struct device *dev) + { +- return to_pci_dev(dev)->bridge_d3 ? 0 : -EBUSY; +-} ++ if (!to_pci_dev(dev)->bridge_d3) ++ return -EBUSY; + +-static int pcie_port_runtime_resume(struct device *dev) +-{ +- return 0; ++ return pcie_port_device_runtime_suspend(dev); + } + + static int pcie_port_runtime_idle(struct device *dev) +@@ -73,7 +71,7 @@ static const struct dev_pm_ops pcie_portdrv_pm_ops = { + .restore_noirq = pcie_port_device_resume_noirq, + .restore = pcie_port_device_resume, + .runtime_suspend = pcie_port_runtime_suspend, +- .runtime_resume = pcie_port_runtime_resume, ++ .runtime_resume = pcie_port_device_runtime_resume, + .runtime_idle = pcie_port_runtime_idle, + }; + +-- +2.29.2 + +From 611f6b8b569b4a7ed5721bbe1cf92343c09689ca Mon Sep 17 00:00:00 2001 +From: Mika Westerberg +Date: Thu, 27 Sep 2018 16:41:49 -0500 +Subject: [PATCH] PCI: pciehp: Implement runtime PM callbacks + +Basically we need to do the same thing when runtime suspending than with +system sleep so re-use those operations here. This makes sure hotplug +interrupt does not trigger immediately when the link goes down. + +Signed-off-by: Mika Westerberg +Signed-off-by: Bjorn Helgaas +Reviewed-by: Rafael J. Wysocki + +Patchset: surface-sam +--- + drivers/pci/hotplug/pciehp_core.c | 18 ++++++++++++++++++ + 1 file changed, 18 insertions(+) + +diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c +index 8e6e4ce869fb..e5de25ebc4cf 100644 +--- a/drivers/pci/hotplug/pciehp_core.c ++++ b/drivers/pci/hotplug/pciehp_core.c +@@ -307,6 +307,22 @@ static int pciehp_resume(struct pcie_device *dev) + + return 0; + } ++ ++static int pciehp_runtime_resume(struct pcie_device *dev) ++{ ++ struct controller *ctrl = get_service_data(dev); ++ ++ /* pci_restore_state() just wrote to the Slot Control register */ ++ ctrl->cmd_started = jiffies; ++ ctrl->cmd_busy = true; ++ ++ /* clear spurious events from rediscovery of inserted card */ ++ if ((ctrl->state == ON_STATE || ctrl->state == BLINKINGOFF_STATE) && ++ pme_is_native(dev)) ++ pcie_clear_hotplug_events(ctrl); ++ ++ return pciehp_resume(dev); ++} + #endif /* PM */ + + static struct pcie_port_service_driver hpdriver_portdrv = { +@@ -321,6 +337,8 @@ static struct pcie_port_service_driver hpdriver_portdrv = { + .suspend = pciehp_suspend, + .resume_noirq = pciehp_resume_noirq, + .resume = pciehp_resume, ++ .runtime_suspend = pciehp_suspend, ++ .runtime_resume = pciehp_runtime_resume, + #endif /* PM */ + }; + +-- +2.29.2 + +From 17d21b61dd014649657455e08cd58b2eab669ae5 Mon Sep 17 00:00:00 2001 +From: Mika Westerberg +Date: Thu, 27 Sep 2018 16:57:05 -0500 +Subject: [PATCH] ACPI / property: Allow multiple property compatible _DSD + entries + +It is possible to have _DSD entries where the data is compatible with +device properties format but are using different GUID for various reasons. +In addition to that there can be many such _DSD entries for a single device +such as for PCIe root port used to host a Thunderbolt hierarchy: + + Scope (\_SB.PCI0.RP21) + { + Name (_DSD, Package () { + ToUUID ("6211e2c0-58a3-4af3-90e1-927a4e0c55a4"), + Package () { + Package () {"HotPlugSupportInD3", 1} + }, + + ToUUID ("efcc06cc-73ac-4bc3-bff0-76143807c389"), + Package () { + Package () {"ExternalFacingPort", 1}, + Package () {"UID", 0 } + } + }) + } + +More information about these new _DSD entries can be found in: + + https://docs.microsoft.com/en-us/windows-hardware/drivers/pci/dsd-for-pcie-root-ports + +To make these available for drivers via unified device property APIs, +modify ACPI property core so that it supports multiple _DSD entries +organized in a linked list. We also store GUID of each _DSD entry in struct +acpi_device_properties in case there is need to differentiate between +entries. The supported GUIDs are then listed in prp_guids array. + +Signed-off-by: Mika Westerberg +Signed-off-by: Bjorn Helgaas +Reviewed-by: Rafael J. Wysocki +Acked-by: Sakari Ailus + +Patchset: surface-sam +--- + drivers/acpi/property.c | 94 +++++++++++++++++++++++++++---------- + drivers/acpi/x86/apple.c | 2 +- + drivers/gpio/gpiolib-acpi.c | 2 +- + include/acpi/acpi_bus.h | 8 +++- + include/linux/acpi.h | 9 ++++ + 5 files changed, 86 insertions(+), 29 deletions(-) + +diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c +index 288673cff85e..150003be8e1f 100644 +--- a/drivers/acpi/property.c ++++ b/drivers/acpi/property.c +@@ -24,11 +24,12 @@ static int acpi_data_get_property_array(const struct acpi_device_data *data, + acpi_object_type type, + const union acpi_object **obj); + +-/* ACPI _DSD device properties GUID: daffd814-6eba-4d8c-8a91-bc9bbf4aa301 */ +-static const guid_t prp_guid = ++static const guid_t prp_guids[] = { ++ /* ACPI _DSD device properties GUID: daffd814-6eba-4d8c-8a91-bc9bbf4aa301 */ + GUID_INIT(0xdaffd814, 0x6eba, 0x4d8c, +- 0x8a, 0x91, 0xbc, 0x9b, 0xbf, 0x4a, 0xa3, 0x01); +-/* ACPI _DSD data subnodes GUID: dbb8e3e6-5886-4ba6-8795-1319f52a966b */ ++ 0x8a, 0x91, 0xbc, 0x9b, 0xbf, 0x4a, 0xa3, 0x01), ++}; ++ + static const guid_t ads_guid = + GUID_INIT(0xdbb8e3e6, 0x5886, 0x4ba6, + 0x87, 0x95, 0x13, 0x19, 0xf5, 0x2a, 0x96, 0x6b); +@@ -56,6 +57,7 @@ static bool acpi_nondev_subnode_extract(const union acpi_object *desc, + dn->name = link->package.elements[0].string.pointer; + dn->fwnode.ops = &acpi_data_fwnode_ops; + dn->parent = parent; ++ INIT_LIST_HEAD(&dn->data.properties); + INIT_LIST_HEAD(&dn->data.subnodes); + + result = acpi_extract_properties(desc, &dn->data); +@@ -288,6 +290,35 @@ static void acpi_init_of_compatible(struct acpi_device *adev) + adev->flags.of_compatible_ok = 1; + } + ++static bool acpi_is_property_guid(const guid_t *guid) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(prp_guids); i++) { ++ if (guid_equal(guid, &prp_guids[i])) ++ return true; ++ } ++ ++ return false; ++} ++ ++struct acpi_device_properties * ++acpi_data_add_props(struct acpi_device_data *data, const guid_t *guid, ++ const union acpi_object *properties) ++{ ++ struct acpi_device_properties *props; ++ ++ props = kzalloc(sizeof(*props), GFP_KERNEL); ++ if (props) { ++ INIT_LIST_HEAD(&props->list); ++ props->guid = guid; ++ props->properties = properties; ++ list_add_tail(&props->list, &data->properties); ++ } ++ ++ return props; ++} ++ + static bool acpi_extract_properties(const union acpi_object *desc, + struct acpi_device_data *data) + { +@@ -312,7 +343,7 @@ static bool acpi_extract_properties(const union acpi_object *desc, + properties->type != ACPI_TYPE_PACKAGE) + break; + +- if (!guid_equal((guid_t *)guid->buffer.pointer, &prp_guid)) ++ if (!acpi_is_property_guid((guid_t *)guid->buffer.pointer)) + continue; + + /* +@@ -320,13 +351,13 @@ static bool acpi_extract_properties(const union acpi_object *desc, + * package immediately following it. + */ + if (!acpi_properties_format_valid(properties)) +- break; ++ continue; + +- data->properties = properties; +- return true; ++ acpi_data_add_props(data, (const guid_t *)guid->buffer.pointer, ++ properties); + } + +- return false; ++ return !list_empty(&data->properties); + } + + void acpi_init_properties(struct acpi_device *adev) +@@ -336,6 +367,7 @@ void acpi_init_properties(struct acpi_device *adev) + acpi_status status; + bool acpi_of = false; + ++ INIT_LIST_HEAD(&adev->data.properties); + INIT_LIST_HEAD(&adev->data.subnodes); + + if (!adev->handle) +@@ -398,11 +430,16 @@ static void acpi_destroy_nondev_subnodes(struct list_head *list) + + void acpi_free_properties(struct acpi_device *adev) + { ++ struct acpi_device_properties *props, *tmp; ++ + acpi_destroy_nondev_subnodes(&adev->data.subnodes); + ACPI_FREE((void *)adev->data.pointer); + adev->data.of_compatible = NULL; + adev->data.pointer = NULL; +- adev->data.properties = NULL; ++ list_for_each_entry_safe(props, tmp, &adev->data.properties, list) { ++ list_del(&props->list); ++ kfree(props); ++ } + } + + /** +@@ -427,32 +464,37 @@ static int acpi_data_get_property(const struct acpi_device_data *data, + const char *name, acpi_object_type type, + const union acpi_object **obj) + { +- const union acpi_object *properties; +- int i; ++ const struct acpi_device_properties *props; + + if (!data || !name) + return -EINVAL; + +- if (!data->pointer || !data->properties) ++ if (!data->pointer || list_empty(&data->properties)) + return -EINVAL; + +- properties = data->properties; +- for (i = 0; i < properties->package.count; i++) { +- const union acpi_object *propname, *propvalue; +- const union acpi_object *property; ++ list_for_each_entry(props, &data->properties, list) { ++ const union acpi_object *properties; ++ unsigned int i; + +- property = &properties->package.elements[i]; ++ properties = props->properties; ++ for (i = 0; i < properties->package.count; i++) { ++ const union acpi_object *propname, *propvalue; ++ const union acpi_object *property; + +- propname = &property->package.elements[0]; +- propvalue = &property->package.elements[1]; ++ property = &properties->package.elements[i]; + +- if (!strcmp(name, propname->string.pointer)) { +- if (type != ACPI_TYPE_ANY && propvalue->type != type) +- return -EPROTO; +- if (obj) +- *obj = propvalue; ++ propname = &property->package.elements[0]; ++ propvalue = &property->package.elements[1]; + +- return 0; ++ if (!strcmp(name, propname->string.pointer)) { ++ if (type != ACPI_TYPE_ANY && ++ propvalue->type != type) ++ return -EPROTO; ++ if (obj) ++ *obj = propvalue; ++ ++ return 0; ++ } + } + } + return -EINVAL; +diff --git a/drivers/acpi/x86/apple.c b/drivers/acpi/x86/apple.c +index 51b4cf9f25da..130df1c8ed7d 100644 +--- a/drivers/acpi/x86/apple.c ++++ b/drivers/acpi/x86/apple.c +@@ -132,8 +132,8 @@ void acpi_extract_apple_properties(struct acpi_device *adev) + } + WARN_ON(free_space != (void *)newprops + newsize); + +- adev->data.properties = newprops; + adev->data.pointer = newprops; ++ acpi_data_add_props(&adev->data, &apple_prp_guid, newprops); + + out_free: + ACPI_FREE(props); +diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c +index 18f5973b9697..dd7c6b247c01 100644 +--- a/drivers/gpio/gpiolib-acpi.c ++++ b/drivers/gpio/gpiolib-acpi.c +@@ -1300,7 +1300,7 @@ int acpi_gpio_count(struct device *dev, const char *con_id) + bool acpi_can_fallback_to_crs(struct acpi_device *adev, const char *con_id) + { + /* Never allow fallback if the device has properties */ +- if (adev->data.properties || adev->driver_gpios) ++ if (acpi_dev_has_props(adev) || adev->driver_gpios) + return false; + + return con_id == NULL; +diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h +index ba4dd54f2c82..cd35e3ce9a8b 100644 +--- a/include/acpi/acpi_bus.h ++++ b/include/acpi/acpi_bus.h +@@ -346,10 +346,16 @@ struct acpi_device_physical_node { + bool put_online:1; + }; + ++struct acpi_device_properties { ++ const guid_t *guid; ++ const union acpi_object *properties; ++ struct list_head list; ++}; ++ + /* ACPI Device Specific Data (_DSD) */ + struct acpi_device_data { + const union acpi_object *pointer; +- const union acpi_object *properties; ++ struct list_head properties; + const union acpi_object *of_compatible; + struct list_head subnodes; + }; +diff --git a/include/linux/acpi.h b/include/linux/acpi.h +index cd412817654f..4e2e30e340fe 100644 +--- a/include/linux/acpi.h ++++ b/include/linux/acpi.h +@@ -1069,6 +1069,15 @@ static inline int acpi_node_get_property_reference( + NR_FWNODE_REFERENCE_ARGS, args); + } + ++static inline bool acpi_dev_has_props(const struct acpi_device *adev) ++{ ++ return !list_empty(&adev->data.properties); ++} ++ ++struct acpi_device_properties * ++acpi_data_add_props(struct acpi_device_data *data, const guid_t *guid, ++ const union acpi_object *properties); ++ + int acpi_node_prop_get(const struct fwnode_handle *fwnode, const char *propname, + void **valptr); + int acpi_dev_prop_read_single(struct acpi_device *adev, +-- +2.29.2 + +From e13a0cc908833740788dca823b99b0d7f4e57b44 Mon Sep 17 00:00:00 2001 +From: Mika Westerberg +Date: Thu, 27 Sep 2018 16:57:14 -0500 +Subject: [PATCH] PCI / ACPI: Whitelist D3 for more PCIe hotplug ports + +In order to have better power management for Thunderbolt PCIe chains, +Windows enables power management for native PCIe hotplug ports if there is +the following ACPI _DSD attached to the root port: + + Name (_DSD, Package () { + ToUUID ("6211e2c0-58a3-4af3-90e1-927a4e0c55a4"), + Package () { + Package () {"HotPlugSupportInD3", 1} + } + }) + +This is also documented in: + + https://docs.microsoft.com/en-us/windows-hardware/drivers/pci/dsd-for-pcie-root-ports#identifying-pcie-root-ports-supporting-hot-plug-in-d3 + +Do the same in Linux by introducing new firmware PM callback +(->bridge_d3()) and then implement it for ACPI based systems so that the +above property is checked. + +There is one catch, though. The initial pci_dev->bridge_d3 is set before +the root port has ACPI companion bound (the device is not added to the PCI +bus either) so we need to look up the ACPI companion manually in that case +in acpi_pci_bridge_d3(). + +Signed-off-by: Mika Westerberg +Signed-off-by: Bjorn Helgaas +Reviewed-by: Rafael J. Wysocki + +Patchset: surface-sam +--- + drivers/acpi/property.c | 3 +++ + drivers/pci/pci-acpi.c | 41 +++++++++++++++++++++++++++++++++++++++++ + drivers/pci/pci.c | 9 +++++++++ + drivers/pci/pci.h | 3 +++ + 4 files changed, 56 insertions(+) + +diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c +index 150003be8e1f..5ca89166ea4e 100644 +--- a/drivers/acpi/property.c ++++ b/drivers/acpi/property.c +@@ -28,6 +28,9 @@ static const guid_t prp_guids[] = { + /* ACPI _DSD device properties GUID: daffd814-6eba-4d8c-8a91-bc9bbf4aa301 */ + GUID_INIT(0xdaffd814, 0x6eba, 0x4d8c, + 0x8a, 0x91, 0xbc, 0x9b, 0xbf, 0x4a, 0xa3, 0x01), ++ /* Hotplug in D3 GUID: 6211e2c0-58a3-4af3-90e1-927a4e0c55a4 */ ++ GUID_INIT(0x6211e2c0, 0x58a3, 0x4af3, ++ 0x90, 0xe1, 0x92, 0x7a, 0x4e, 0x0c, 0x55, 0xa4), + }; + + static const guid_t ads_guid = +diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c +index f7218c1673ce..4d9446ebcffe 100644 +--- a/drivers/pci/pci-acpi.c ++++ b/drivers/pci/pci-acpi.c +@@ -519,6 +519,46 @@ static pci_power_t acpi_pci_choose_state(struct pci_dev *pdev) + return PCI_POWER_ERROR; + } + ++static struct acpi_device *acpi_pci_find_companion(struct device *dev); ++ ++static bool acpi_pci_bridge_d3(struct pci_dev *dev) ++{ ++ const struct fwnode_handle *fwnode; ++ struct acpi_device *adev; ++ struct pci_dev *root; ++ u8 val; ++ ++ if (!dev->is_hotplug_bridge) ++ return false; ++ ++ /* ++ * Look for a special _DSD property for the root port and if it ++ * is set we know the hierarchy behind it supports D3 just fine. ++ */ ++ root = pci_find_pcie_root_port(dev); ++ if (!root) ++ return false; ++ ++ adev = ACPI_COMPANION(&root->dev); ++ if (root == dev) { ++ /* ++ * It is possible that the ACPI companion is not yet bound ++ * for the root port so look it up manually here. ++ */ ++ if (!adev && !pci_dev_is_added(root)) ++ adev = acpi_pci_find_companion(&root->dev); ++ } ++ ++ if (!adev) ++ return false; ++ ++ fwnode = acpi_fwnode_handle(adev); ++ if (fwnode_property_read_u8(fwnode, "HotPlugSupportInD3", &val)) ++ return false; ++ ++ return val == 1; ++} ++ + static bool acpi_pci_power_manageable(struct pci_dev *dev) + { + struct acpi_device *adev = ACPI_COMPANION(&dev->dev); +@@ -636,6 +676,7 @@ static bool acpi_pci_need_resume(struct pci_dev *dev) + } + + static const struct pci_platform_pm_ops acpi_pci_platform_pm = { ++ .bridge_d3 = acpi_pci_bridge_d3, + .is_manageable = acpi_pci_power_manageable, + .set_state = acpi_pci_set_power_state, + .get_state = acpi_pci_get_power_state, +diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c +index 2898b7c1ac4b..b53a6772ee09 100644 +--- a/drivers/pci/pci.c ++++ b/drivers/pci/pci.c +@@ -793,6 +793,11 @@ static inline bool platform_pci_need_resume(struct pci_dev *dev) + return pci_platform_pm ? pci_platform_pm->need_resume(dev) : false; + } + ++static inline bool platform_pci_bridge_d3(struct pci_dev *dev) ++{ ++ return pci_platform_pm ? pci_platform_pm->bridge_d3(dev) : false; ++} ++ + /** + * pci_raw_set_power_state - Use PCI PM registers to set the power state of + * given PCI device +@@ -2551,6 +2556,10 @@ bool pci_bridge_d3_possible(struct pci_dev *bridge) + if (bridge->is_thunderbolt) + return true; + ++ /* Platform might know better if the bridge supports D3 */ ++ if (platform_pci_bridge_d3(bridge)) ++ return true; ++ + /* + * Hotplug ports handled natively by the OS were not validated + * by vendors for runtime D3 at least until 2018 because there +diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h +index e9ede82ee2c2..0a5efc437bd1 100644 +--- a/drivers/pci/pci.h ++++ b/drivers/pci/pci.h +@@ -40,6 +40,8 @@ int pci_bus_error_reset(struct pci_dev *dev); + /** + * struct pci_platform_pm_ops - Firmware PM callbacks + * ++ * @bridge_d3: Does the bridge allow entering into D3 ++ * + * @is_manageable: returns 'true' if given device is power manageable by the + * platform firmware + * +@@ -61,6 +63,7 @@ int pci_bus_error_reset(struct pci_dev *dev); + * these callbacks are mandatory. + */ + struct pci_platform_pm_ops { ++ bool (*bridge_d3)(struct pci_dev *dev); + bool (*is_manageable)(struct pci_dev *dev); + int (*set_state)(struct pci_dev *dev, pci_power_t state); + pci_power_t (*get_state)(struct pci_dev *dev); +-- +2.29.2 + +From b7c856bcec6d02044f1737ff3d7fbfeda431f6a5 Mon Sep 17 00:00:00 2001 +From: Mika Westerberg +Date: Mon, 7 Jan 2019 16:09:40 +0300 +Subject: [PATCH] PCI: pciehp: Assign ctrl->slot_ctrl before writing it to + hardware + +Shameerali reported that running v4.20-rc1 as QEMU guest, the PCIe hotplug +port times out during boot: + + pciehp 0000:00:01.0:pcie004: Timeout on hotplug command 0x03f1 (issued 1016 msec ago) + pciehp 0000:00:01.0:pcie004: Timeout on hotplug command 0x03f1 (issued 1024 msec ago) + pciehp 0000:00:01.0:pcie004: Failed to check link status + pciehp 0000:00:01.0:pcie004: Timeout on hotplug command 0x02f1 (issued 2520 msec ago) + +The issue was bisected down to commit 720d6a671a6e ("PCI: pciehp: Do not +handle events if interrupts are masked") and was further analyzed by the +reporter to be caused by the fact that pciehp first updates the hardware +and only then cache the ctrl->slot_ctrl in pcie_do_write_cmd(). If the +interrupt happens before we cache the value, pciehp_isr() reads value 0 and +decides that the interrupt was not meant for it causing the above timeout +to trigger. + +Fix by moving ctrl->slot_ctrl assignment to happen before it is written to +the hardware. + +Fixes: 720d6a671a6e ("PCI: pciehp: Do not handle events if interrupts are masked") +Link: https://lore.kernel.org/linux-pci/5FC3163CFD30C246ABAA99954A238FA8387DD344@FRAEML521-MBX.china.huawei.com +Reported-by: Shameerali Kolothum Thodi +Signed-off-by: Mika Westerberg +Signed-off-by: Bjorn Helgaas + +Patchset: surface-sam +--- + drivers/pci/hotplug/pciehp_hpc.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c +index 242b9f30210a..7074d4923811 100644 +--- a/drivers/pci/hotplug/pciehp_hpc.c ++++ b/drivers/pci/hotplug/pciehp_hpc.c +@@ -156,9 +156,9 @@ static void pcie_do_write_cmd(struct controller *ctrl, u16 cmd, + slot_ctrl |= (cmd & mask); + ctrl->cmd_busy = 1; + smp_mb(); ++ ctrl->slot_ctrl = slot_ctrl; + pcie_capability_write_word(pdev, PCI_EXP_SLTCTL, slot_ctrl); + ctrl->cmd_started = jiffies; +- ctrl->slot_ctrl = slot_ctrl; + + /* + * Controllers with the Intel CF118 and similar errata advertise +-- +2.29.2 + +From b661fc6fc2c48a267e112a43dcd93a3c2a931f83 Mon Sep 17 00:00:00 2001 +From: Mika Westerberg +Date: Thu, 31 Jan 2019 20:07:46 +0300 +Subject: [PATCH] PCI: pciehp: Disable Data Link Layer State Changed event on + suspend + +Commit 0e157e528604 ("PCI/PME: Implement runtime PM callbacks") tried to +solve an issue where the hierarchy immediately wakes up when it is +transitioned into D3cold. However, it turns out to prevent PME +propagation on some systems that do not support D3cold. + +I looked more closely at what might cause the immediate wakeup. It happens +when the ACPI power resource of the root port is turned off. The AML code +associated with the _OFF() method of the ACPI power resource starts a PCIe +L2/L3 Ready transition and waits for it to complete. Right after the L2/L3 +Ready transition is started the root port receives a PME from the +downstream port. + +The simplest hierarchy where this happens looks like this: + + 00:1d.0 PCIe Root Port + ^ + | + v + 05:00.0 PCIe switch #1 upstream port + 06:01.0 PCIe switch #1 downstream hotplug port + ^ + | + v + 08:00.0 PCIe switch #2 upstream port + +It seems that the PCIe link between the two switches, before +PME_Turn_Off/PME_TO_Ack is complete for the whole hierarchy, goes +inactive and triggers PME towards the root port bringing it back to D0. +The L2/L3 Ready sequence is described in PCIe r4.0 spec sections 5.2 and +5.3.3 but unfortunately they do not state what happens if DLLSCE is +enabled during the sequence. + +Disabling Data Link Layer State Changed event (DLLSCE) seems to prevent +the issue and still allows the downstream hotplug port to notice when a +device is plugged/unplugged. + +Link: https://bugzilla.kernel.org/show_bug.cgi?id=202593 +Fixes: 0e157e528604 ("PCI/PME: Implement runtime PM callbacks") +Signed-off-by: Mika Westerberg +Signed-off-by: Bjorn Helgaas +Reviewed-by: Rafael J. Wysocki +CC: stable@vger.kernel.org # v4.20+ + +Patchset: surface-sam +--- + drivers/pci/hotplug/pciehp_hpc.c | 17 +++++++++++++++-- + 1 file changed, 15 insertions(+), 2 deletions(-) + +diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c +index 7074d4923811..a37ff79a6e9e 100644 +--- a/drivers/pci/hotplug/pciehp_hpc.c ++++ b/drivers/pci/hotplug/pciehp_hpc.c +@@ -752,12 +752,25 @@ void pcie_clear_hotplug_events(struct controller *ctrl) + + void pcie_enable_interrupt(struct controller *ctrl) + { +- pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_HPIE, PCI_EXP_SLTCTL_HPIE); ++ u16 mask; ++ ++ mask = PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_DLLSCE; ++ pcie_write_cmd(ctrl, mask, mask); + } + + void pcie_disable_interrupt(struct controller *ctrl) + { +- pcie_write_cmd(ctrl, 0, PCI_EXP_SLTCTL_HPIE); ++ u16 mask; ++ ++ /* ++ * Mask hot-plug interrupt to prevent it triggering immediately ++ * when the link goes inactive (we still get PME when any of the ++ * enabled events is detected). Same goes with Link Layer State ++ * changed event which generates PME immediately when the link goes ++ * inactive so mask it as well. ++ */ ++ mask = PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_DLLSCE; ++ pcie_write_cmd(ctrl, 0, mask); + } + + /* +-- +2.29.2 + +From 559532f683fcdfd1d94bd26d6e276d947b5e5c4c 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 b53a6772ee09..70af1713767f 100644 +--- a/drivers/pci/pci.c ++++ b/drivers/pci/pci.c +@@ -2523,6 +2523,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 +@@ -2563,10 +2589,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 53572f59250be10aaaf6dd82f773291c6df58108 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 70af1713767f..da8254eeba77 100644 +--- a/drivers/pci/pci.c ++++ b/drivers/pci/pci.c +@@ -1587,7 +1587,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; + + /* +@@ -1597,9 +1597,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 4589e6b130e1ee76e64094c06d7831fcfddfd386 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 44d4b2be92fd..daded06ae017 100644 +--- a/Documentation/ABI/testing/sysfs-bus-pci ++++ b/Documentation/ABI/testing/sysfs-bus-pci +@@ -323,3 +323,12 @@ Description: + + This is similar to /sys/bus/pci/drivers_autoprobe, but + affects only the VFs associated with a specific PF. ++ ++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 1edf5a1836ea..ee1518650d55 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) +@@ -745,6 +756,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 a9676aa474e9a0a056251ee93e902f3368dafcd0 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 @@ -369,9 +4352,9 @@ index 7f40b6aab689..76e3b1d7db45 100644 /* Create MODULE_ALIAS() statements. -- -2.28.0 +2.29.2 -From a14fa36daaee6d27744986e0cfdd336127146662 Mon Sep 17 00:00:00 2001 +From 7dd08a5b102a9e7b955ebe79acd7100a8c6ef14c 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 @@ -410,11 +4393,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 | 1192 ++++++++ - .../surface_aggregator/clients/surface_dtx.c | 1268 ++++++++ + .../clients/surface_aggregator_registry.c | 609 ++++ + .../clients/surface_battery.c | 1191 ++++++++ + .../surface_aggregator/clients/surface_dtx.c | 1275 ++++++++ .../surface_aggregator/clients/surface_hid.c | 919 ++++++ - .../clients/surface_hotplug.c | 1285 +++++++++ + .../clients/surface_hotplug.c | 269 ++ .../clients/surface_perfmode.c | 122 + drivers/misc/surface_aggregator/controller.c | 2553 +++++++++++++++++ drivers/misc/surface_aggregator/controller.h | 288 ++ @@ -436,7 +4419,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, 19760 insertions(+), 7 deletions(-) + 48 files changed, 18754 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 @@ -4360,10 +8343,10 @@ index 000000000000..7ebd0977fb37 +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. @@ -4754,7 +8737,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; @@ -4787,6 +8770,8 @@ index 000000000000..c7e84316b88b + if (!hub) + return -ENOMEM; + ++ mutex_init(&hub->lock); ++ + hub->sdev = sdev; + hub->devices = desc; + hub->state = SSAM_BASE_HUB_UNINITIALIZED; @@ -4834,6 +8819,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[] = { @@ -4971,10 +8958,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..d43797590cd9 +index 000000000000..540a5bd8957a --- /dev/null +++ b/drivers/misc/surface_aggregator/clients/surface_battery.c -@@ -0,0 +1,1192 @@ +@@ -0,0 +1,1191 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Surface battery and AC device driver. @@ -5990,7 +9977,6 @@ index 000000000000..d43797590cd9 + cancel_delayed_work_sync(&bat->update_work); + device_remove_file(&bat->psy->dev, &alarm_attr); + power_supply_unregister(bat->psy); -+ mutex_destroy(&bat->lock); +} + + @@ -6169,10 +10155,10 @@ index 000000000000..d43797590cd9 +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..114cf4c21172 +index 000000000000..6e2cef16f208 --- /dev/null +++ b/drivers/misc/surface_aggregator/clients/surface_dtx.c -@@ -0,0 +1,1268 @@ +@@ -0,0 +1,1275 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Surface Book (gen. 2 and later) detachment system (DTX) driver. @@ -6360,7 +10346,10 @@ index 000000000000..114cf4c21172 + +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) @@ -6612,6 +10601,7 @@ index 000000000000..114cf4c21172 + up_write(&client->ddev->client_lock); + + sdtx_device_put(client->ddev); ++ mutex_destroy(&client->read_lock); + kfree(client); + + return 0; @@ -7230,6 +11220,9 @@ index 000000000000..114cf4c21172 + // 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); + @@ -7443,7 +11436,7 @@ index 000000000000..114cf4c21172 +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..1cfdb5ee616d +index 000000000000..8a9a389ac396 --- /dev/null +++ b/drivers/misc/surface_aggregator/clients/surface_hid.c @@ -0,0 +1,919 @@ @@ -8123,7 +12116,7 @@ index 000000000000..1cfdb5ee616d + +/* -- PM ops. --------------------------------------------------------------- */ + -+#ifdef CONFIG_PM ++#ifdef CONFIG_PM_SLEEP + +static int surface_hid_suspend(struct device *dev) +{ @@ -8184,11 +12177,11 @@ index 000000000000..1cfdb5ee616d + .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). --------------------------------------------------- */ @@ -8368,1294 +12361,278 @@ index 000000000000..1cfdb5ee616d +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 @@ -20378,7 +23355,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 @@ @@ -20476,7 +23453,7 @@ index 000000000000..d88cabfb8dd7 + __u16 length; + __u16 code; + __u8 data[]; -+} __packed; ++} __attribute__((__packed__)); + +/** + * enum sdtx_event_code - Code describing the type of an event. @@ -20512,7 +23489,7 @@ index 000000000000..d88cabfb8dd7 +struct sdtx_base_info { + __u16 state; + __u16 base_id; -+} __packed; ++} __attribute__((__packed__)); + + +/* IOCTLs */ @@ -20579,5 +23556,5 @@ index 76e3b1d7db45..f171616ab318 100644 ADD(alias, "f", match_flags & SSAM_MATCH_FUNCTION, function); -- -2.28.0 +2.29.2 diff --git a/patches/4.19/0011-i2c-core-Restore-acpi_walk_dep_device_list-getting-c.patch b/patches/4.19/0011-i2c-core-Restore-acpi_walk_dep_device_list-getting-c.patch deleted file mode 100644 index 1ccc3d8b7..000000000 --- a/patches/4.19/0011-i2c-core-Restore-acpi_walk_dep_device_list-getting-c.patch +++ /dev/null @@ -1,70 +0,0 @@ -From c523e2ba98307da63f228ec499d9f011e4ebd916 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 c2b5a2aca731..f9a24b56fec0 100644 ---- a/drivers/i2c/i2c-core-acpi.c -+++ b/drivers/i2c/i2c-core-acpi.c -@@ -219,6 +219,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; -@@ -229,6 +230,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 * -@@ -728,7 +738,6 @@ int i2c_acpi_install_space_handler(struct i2c_adapter *adapter) - return -ENOMEM; - } - -- acpi_walk_dep_device_list(handle); - return 0; - } - --- -2.28.0 - diff --git a/pkg/arch/kernel-lts/0011-i2c-core-Restore-acpi_walk_dep_device_list-getting-c.patch b/pkg/arch/kernel-lts/0011-i2c-core-Restore-acpi_walk_dep_device_list-getting-c.patch deleted file mode 120000 index 9894f12f6..000000000 --- a/pkg/arch/kernel-lts/0011-i2c-core-Restore-acpi_walk_dep_device_list-getting-c.patch +++ /dev/null @@ -1 +0,0 @@ -../../../patches/4.19/0011-i2c-core-Restore-acpi_walk_dep_device_list-getting-c.patch \ No newline at end of file diff --git a/pkg/arch/kernel-lts/PKGBUILD b/pkg/arch/kernel-lts/PKGBUILD index abc9b8ade..b64518efe 100644 --- a/pkg/arch/kernel-lts/PKGBUILD +++ b/pkg/arch/kernel-lts/PKGBUILD @@ -30,7 +30,6 @@ source=( 0008-surface-gpe.patch 0009-surface-sam-over-hid.patch 0010-surface-sam.patch - 0011-i2c-core-Restore-acpi_walk_dep_device_list-getting-c.patch ) validpgpkeys=( 'ABAF11C65A2970B130ABE3C479BE3E4300411886' # Linus Torvalds @@ -41,18 +40,17 @@ sha256sums=('a5a6aa9c2c2810efa72b5d9723de86ccea3f965b2dd748d15e82d5fac23a055d' 'SKIP' '4e68572e7cc4c5368f0236e0792660ae8498373988625dca46e509399a7eaea6' 'a13581d3c6dc595206e4fe7fcf6b542e7a1bdbe96101f0f010fc5be49f99baf2' - '1c4963e4a911e74ed56f1fe0065c31201edca9a5e3f89eb30b7063f9981ebdd8' - '111b4f7814d49c06f3f328feba30c8991e423acce36aa9c737a31489a64b9e5d' - 'b85aa40e2c3c04514aa14538c7486653cc987276acffae532e3b8516d3328bfc' - '602fb64b0b2073e0b016f39be34d86113fb0b3e63b4490cc26611d1313b3665e' - '99bc4ce339713433a06d936bb6c339d2797b6bec8e7af91be017bcf30bc658b3' - '0d0bd51185191cdc29405db26cfa8d79debb85aa090931672b8ac5c3ee4a7e10' - '0383649af9c5f63c47b515d03e9279a4090fec1fb32a9d47692a491c80f5a1b5' - 'e5fd01b8fdb8c53f46ac58fdffd4d7a75f009217e5f2a484ac00a6bb9968eab4' - '4a5c643ec9a3c3e15ceff45e024fce0462dc314b516fe0620de6858178d96fc8' - 'f28bc22540ad92769c4bd787140ab759f665f262f1c9cb8827010d8c8f6c5d4c' - '71deb9a7421a7ba3be46059e5246a2900b1d1cd206339716f1fb9f1c848731d7' - '12dd1c955d2b9123afaad2690d65905ea394f297ba27ab4fec32dd5655553c8c') + 'dacf6a14239e151bae71587e6e604faeadd1e63975edeba4bb8033806b68c67a' + 'ea4ecb074b7da55a9e41aa94d16d39847bc861b347b3c5fb118f958f3066c477' + 'ec7f00b497369b43ce1ed3bfbd01cfecd8e1e1525f1cc294670c22b68a673e01' + 'cfe0ab5a6713490380ad0123f040a4889f11c45c27106e76e90b967a380c9f0f' + 'b73b1ece41ce596da7cbed9e9a5fba2e6383ca3f039004ab188a18c11012601b' + 'eb74146731751b76bd5edd0bb5127ad2ca089d9a9e38a55761ed0911688fa80f' + 'fe2d1edbb73f4c90cbbfc4e270ff39474db6095368125a3cf89f099ac40edaa0' + 'd3f61a02cdfb7fbb7a3f7dcebc96b4874dbcf3392f3e8349b3775ecc6a472107' + '132fe7136f5382d3785107f21ab0f74a8229a99368ab6d771c8f3c88d7bdde1f' + '441cc7f501314f59397f2a3af77fc45921bc0e3d8894cc2fb3b8a7ed9e735878' + 'cc19f511f564f0e291d27cec15f5360737ddc17777e5b642a5fa68935576e456') export KBUILD_BUILD_HOST=archlinux export KBUILD_BUILD_USER=$pkgbase