0007-surface-hotplug.patch 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494
  1. From b3c054a53fb7a9d4ce5a3d8e8122300a47eb2b71 Mon Sep 17 00:00:00 2001
  2. From: Maximilian Luz <luzmaximilian@gmail.com>
  3. Date: Sat, 31 Oct 2020 20:46:33 +0100
  4. Subject: [PATCH] PCI: Add sysfs attribute for PCI device power state
  5. While most PCI power-states can be queried from user-space via lspci,
  6. this has some limits. Specifically, lspci fails to provide an accurate
  7. value when the device is in D3cold as it has to resume the device before
  8. it can access its power state via the configuration space, leading to it
  9. reporting D0 or another on-state. Thus lspci can, for example, not be
  10. used to diagnose power-consumption issues for devices that can enter
  11. D3cold or to ensure that devices properly enter D3cold at all.
  12. To alleviate this issue, introduce a new sysfs device attribute for the
  13. PCI power state, showing the current power state as seen by the kernel.
  14. Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com>
  15. Patchset: surface-hotplug
  16. ---
  17. Documentation/ABI/testing/sysfs-bus-pci | 9 +++++++++
  18. drivers/pci/pci-sysfs.c | 12 ++++++++++++
  19. 2 files changed, 21 insertions(+)
  20. diff --git a/Documentation/ABI/testing/sysfs-bus-pci b/Documentation/ABI/testing/sysfs-bus-pci
  21. index 77ad9ec3c801..25c9c39770c6 100644
  22. --- a/Documentation/ABI/testing/sysfs-bus-pci
  23. +++ b/Documentation/ABI/testing/sysfs-bus-pci
  24. @@ -366,3 +366,12 @@ Contact: Heiner Kallweit <hkallweit1@gmail.com>
  25. Description: If ASPM is supported for an endpoint, these files can be
  26. used to disable or enable the individual power management
  27. states. Write y/1/on to enable, n/0/off to disable.
  28. +
  29. +What: /sys/bus/pci/devices/.../power_state
  30. +Date: November 2020
  31. +Contact: Linux PCI developers <linux-pci@vger.kernel.org>
  32. +Description:
  33. + This file contains the current PCI power state of the device.
  34. + The value comes from the PCI kernel device state and can be one
  35. + of: "unknown", "error", "D0", D1", "D2", "D3hot", "D3cold".
  36. + The file is read only.
  37. diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
  38. index d15c881e2e7e..b15f754e6346 100644
  39. --- a/drivers/pci/pci-sysfs.c
  40. +++ b/drivers/pci/pci-sysfs.c
  41. @@ -124,6 +124,17 @@ static ssize_t cpulistaffinity_show(struct device *dev,
  42. }
  43. static DEVICE_ATTR_RO(cpulistaffinity);
  44. +/* PCI power state */
  45. +static ssize_t power_state_show(struct device *dev,
  46. + struct device_attribute *attr, char *buf)
  47. +{
  48. + struct pci_dev *pci_dev = to_pci_dev(dev);
  49. + pci_power_t state = READ_ONCE(pci_dev->current_state);
  50. +
  51. + return sprintf(buf, "%s\n", pci_power_name(state));
  52. +}
  53. +static DEVICE_ATTR_RO(power_state);
  54. +
  55. /* show resources */
  56. static ssize_t resource_show(struct device *dev, struct device_attribute *attr,
  57. char *buf)
  58. @@ -581,6 +592,7 @@ static ssize_t driver_override_show(struct device *dev,
  59. static DEVICE_ATTR_RW(driver_override);
  60. static struct attribute *pci_dev_attrs[] = {
  61. + &dev_attr_power_state.attr,
  62. &dev_attr_resource.attr,
  63. &dev_attr_vendor.attr,
  64. &dev_attr_device.attr,
  65. --
  66. 2.31.0
  67. From 48c5b30c34cb86933e33ce81ce20571fccc7c00e Mon Sep 17 00:00:00 2001
  68. From: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
  69. Date: Tue, 16 Mar 2021 16:51:40 +0100
  70. Subject: [PATCH] PCI: PM: Do not read power state in pci_enable_device_flags()
  71. It should not be necessary to update the current_state field of
  72. struct pci_dev in pci_enable_device_flags() before calling
  73. do_pci_enable_device() for the device, because none of the
  74. code between that point and the pci_set_power_state() call in
  75. do_pci_enable_device() invoked later depends on it.
  76. Moreover, doing that is actively harmful in some cases. For example,
  77. if the given PCI device depends on an ACPI power resource whose _STA
  78. method initially returns 0 ("off"), but the config space of the PCI
  79. device is accessible and the power state retrieved from the
  80. PCI_PM_CTRL register is D0, the current_state field in the struct
  81. pci_dev representing that device will get out of sync with the
  82. power.state of its ACPI companion object and that will lead to
  83. power management issues going forward.
  84. To avoid such issues it is better to leave the current_state value
  85. as is until it is changed to PCI_D0 by do_pci_enable_device() as
  86. appropriate. However, the power state of the device is not changed
  87. to PCI_D0 if it is already enabled when pci_enable_device_flags()
  88. gets called for it, so update its current_state in that case, but
  89. use pci_update_current_state() covering platform PM too for that.
  90. Link: https://lore.kernel.org/lkml/20210314000439.3138941-1-luzmaximilian@gmail.com/
  91. Reported-by: Maximilian Luz <luzmaximilian@gmail.com>
  92. Tested-by: Maximilian Luz <luzmaximilian@gmail.com>
  93. Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
  94. Reviewed-by: Mika Westerberg <mika.westerberg@linux.intel.com>
  95. Patchset: surface-hotplug
  96. ---
  97. drivers/pci/pci.c | 16 +++-------------
  98. 1 file changed, 3 insertions(+), 13 deletions(-)
  99. diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
  100. index 9e971fffeb6a..d5d9ea864fe6 100644
  101. --- a/drivers/pci/pci.c
  102. +++ b/drivers/pci/pci.c
  103. @@ -1874,20 +1874,10 @@ static int pci_enable_device_flags(struct pci_dev *dev, unsigned long flags)
  104. int err;
  105. int i, bars = 0;
  106. - /*
  107. - * Power state could be unknown at this point, either due to a fresh
  108. - * boot or a device removal call. So get the current power state
  109. - * so that things like MSI message writing will behave as expected
  110. - * (e.g. if the device really is in D0 at enable time).
  111. - */
  112. - if (dev->pm_cap) {
  113. - u16 pmcsr;
  114. - pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr);
  115. - dev->current_state = (pmcsr & PCI_PM_CTRL_STATE_MASK);
  116. - }
  117. -
  118. - if (atomic_inc_return(&dev->enable_cnt) > 1)
  119. + if (atomic_inc_return(&dev->enable_cnt) > 1) {
  120. + pci_update_current_state(dev, dev->current_state);
  121. return 0; /* already enabled */
  122. + }
  123. bridge = pci_upstream_bridge(dev);
  124. if (bridge)
  125. --
  126. 2.31.0
  127. From 465ebd8e50e7f01a604e47b89ac8089a3e4f88dd Mon Sep 17 00:00:00 2001
  128. From: Maximilian Luz <luzmaximilian@gmail.com>
  129. Date: Mon, 14 Dec 2020 20:50:59 +0100
  130. Subject: [PATCH] platform/x86: Add Surface Hotplug driver
  131. Add a driver to handle out-of-band hot-plug signaling for the discrete
  132. GPU (dGPU) on Microsoft Surface Book 2 and 3 devices. This driver is
  133. required to properly detect hot-plugging of the dGPU and relay the
  134. appropriate signal to the PCIe hot-plug driver core.
  135. Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com>
  136. Patchset: surface-hotplug
  137. ---
  138. drivers/platform/x86/Kconfig | 20 ++
  139. drivers/platform/x86/Makefile | 1 +
  140. drivers/platform/x86/surface_hotplug.c | 282 +++++++++++++++++++++++++
  141. 3 files changed, 303 insertions(+)
  142. create mode 100644 drivers/platform/x86/surface_hotplug.c
  143. diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
  144. index dbb07644c312..0c513c8bbd2b 100644
  145. --- a/drivers/platform/x86/Kconfig
  146. +++ b/drivers/platform/x86/Kconfig
  147. @@ -1012,6 +1012,26 @@ config SURFACE_GPE
  148. accordingly. It is required on those devices to allow wake-ups from
  149. suspend by opening the lid.
  150. +config SURFACE_HOTPLUG
  151. + tristate "Surface Hot-Plug System Driver"
  152. + depends on ACPI
  153. + default m
  154. + help
  155. + Driver for out-of-band hot-plug event signaling on Microsoft Surface
  156. + devices with hot-pluggable PCIe cards.
  157. +
  158. + This driver is used on Surface Book (2 and 3) devices with a
  159. + hot-pluggable discrete GPU (dGPU). When not in use, the dGPU on those
  160. + devices can enter D3cold, which prevents in-band (standard) PCIe
  161. + hot-plug signaling. Thus, without this driver, detaching the base
  162. + containing the dGPU will not correctly update the state of the
  163. + corresponding PCIe device if it is in D3cold. This driver adds support
  164. + for out-of-band hot-plug notifications, ensuring that the device state
  165. + is properly updated even when the device in question is in D3cold.
  166. +
  167. + Select M or Y here, if you want to (fully) support hot-plugging of
  168. + dGPU devices on the Surface Book 2 and/or 3 during D3cold.
  169. +
  170. config SURFACE_BOOK1_DGPU_SWITCH
  171. tristate "Surface Book 1 dGPU Switch Driver"
  172. depends on ACPI && SYSFS
  173. diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
  174. index 2e0a2896c78d..f552cbfb7914 100644
  175. --- a/drivers/platform/x86/Makefile
  176. +++ b/drivers/platform/x86/Makefile
  177. @@ -94,6 +94,7 @@ obj-$(CONFIG_SURFACE_3_BUTTON) += surface3_button.o
  178. obj-$(CONFIG_SURFACE_3_POWER_OPREGION) += surface3_power.o
  179. obj-$(CONFIG_SURFACE_PRO3_BUTTON) += surfacepro3_button.o
  180. obj-$(CONFIG_SURFACE_GPE) += surface_gpe.o
  181. +obj-$(CONFIG_SURFACE_HOTPLUG) += surface_hotplug.o
  182. obj-$(CONFIG_SURFACE_BOOK1_DGPU_SWITCH) += sb1_dgpu_sw.o
  183. # MSI
  184. diff --git a/drivers/platform/x86/surface_hotplug.c b/drivers/platform/x86/surface_hotplug.c
  185. new file mode 100644
  186. index 000000000000..cfcc15cfbacb
  187. --- /dev/null
  188. +++ b/drivers/platform/x86/surface_hotplug.c
  189. @@ -0,0 +1,282 @@
  190. +// SPDX-License-Identifier: GPL-2.0+
  191. +/*
  192. + * Surface Book (2 and later) hot-plug driver.
  193. + *
  194. + * Surface Book devices (can) have a hot-pluggable discrete GPU (dGPU). This
  195. + * driver is responsible for out-of-band hot-plug event signaling on these
  196. + * devices. It is specifically required when the hot-plug device is in D3cold
  197. + * and can thus not generate PCIe hot-plug events itself.
  198. + *
  199. + * Event signaling is handled via ACPI, which will generate the appropriate
  200. + * device-check notifications to be picked up by the PCIe hot-plug driver.
  201. + *
  202. + * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com>
  203. + */
  204. +
  205. +#include <linux/acpi.h>
  206. +#include <linux/gpio.h>
  207. +#include <linux/interrupt.h>
  208. +#include <linux/kernel.h>
  209. +#include <linux/module.h>
  210. +#include <linux/mutex.h>
  211. +#include <linux/platform_device.h>
  212. +
  213. +static const struct acpi_gpio_params shps_base_presence_int = { 0, 0, false };
  214. +static const struct acpi_gpio_params shps_base_presence = { 1, 0, false };
  215. +static const struct acpi_gpio_params shps_device_power_int = { 2, 0, false };
  216. +static const struct acpi_gpio_params shps_device_power = { 3, 0, false };
  217. +static const struct acpi_gpio_params shps_device_presence_int = { 4, 0, false };
  218. +static const struct acpi_gpio_params shps_device_presence = { 5, 0, false };
  219. +
  220. +static const struct acpi_gpio_mapping shps_acpi_gpios[] = {
  221. + { "base_presence-int-gpio", &shps_base_presence_int, 1 },
  222. + { "base_presence-gpio", &shps_base_presence, 1 },
  223. + { "device_power-int-gpio", &shps_device_power_int, 1 },
  224. + { "device_power-gpio", &shps_device_power, 1 },
  225. + { "device_presence-int-gpio", &shps_device_presence_int, 1 },
  226. + { "device_presence-gpio", &shps_device_presence, 1 },
  227. + { },
  228. +};
  229. +
  230. +/* 5515a847-ed55-4b27-8352-cd320e10360a */
  231. +static const guid_t shps_dsm_guid =
  232. + GUID_INIT(0x5515a847, 0xed55, 0x4b27, 0x83, 0x52, 0xcd, 0x32, 0x0e, 0x10, 0x36, 0x0a);
  233. +
  234. +#define SHPS_DSM_REVISION 1
  235. +
  236. +enum shps_dsm_fn {
  237. + SHPS_DSM_FN_PCI_NUM_ENTRIES = 0x01,
  238. + SHPS_DSM_FN_PCI_GET_ENTRIES = 0x02,
  239. + SHPS_DSM_FN_IRQ_BASE_PRESENCE = 0x03,
  240. + SHPS_DSM_FN_IRQ_DEVICE_POWER = 0x04,
  241. + SHPS_DSM_FN_IRQ_DEVICE_PRESENCE = 0x05,
  242. +};
  243. +
  244. +enum shps_irq_type {
  245. + /* NOTE: Must be in order of enum shps_dsm_fn above. */
  246. + SHPS_IRQ_TYPE_BASE_PRESENCE = 0,
  247. + SHPS_IRQ_TYPE_DEVICE_POWER = 1,
  248. + SHPS_IRQ_TYPE_DEVICE_PRESENCE = 2,
  249. + SHPS_NUM_IRQS,
  250. +};
  251. +
  252. +static const char *const shps_gpio_names[] = {
  253. + [SHPS_IRQ_TYPE_BASE_PRESENCE] = "base_presence",
  254. + [SHPS_IRQ_TYPE_DEVICE_POWER] = "device_power",
  255. + [SHPS_IRQ_TYPE_DEVICE_PRESENCE] = "device_presence",
  256. +};
  257. +
  258. +struct shps_device {
  259. + struct mutex lock[SHPS_NUM_IRQS]; /* Protects update in shps_dsm_notify_irq() */
  260. + struct gpio_desc *gpio[SHPS_NUM_IRQS];
  261. + unsigned int irq[SHPS_NUM_IRQS];
  262. +};
  263. +
  264. +#define SHPS_IRQ_NOT_PRESENT ((unsigned int)-1)
  265. +
  266. +static enum shps_dsm_fn shps_dsm_fn_for_irq(enum shps_irq_type type)
  267. +{
  268. + return SHPS_DSM_FN_IRQ_BASE_PRESENCE + type;
  269. +}
  270. +
  271. +static void shps_dsm_notify_irq(struct platform_device *pdev, enum shps_irq_type type)
  272. +{
  273. + struct shps_device *sdev = platform_get_drvdata(pdev);
  274. + acpi_handle handle = ACPI_HANDLE(&pdev->dev);
  275. + union acpi_object *result;
  276. + union acpi_object param;
  277. + int value;
  278. +
  279. + mutex_lock(&sdev->lock[type]);
  280. +
  281. + value = gpiod_get_value_cansleep(sdev->gpio[type]);
  282. + if (value < 0) {
  283. + mutex_unlock(&sdev->lock[type]);
  284. + dev_err(&pdev->dev, "failed to get gpio: %d (irq=%d)\n", type, value);
  285. + return;
  286. + }
  287. +
  288. + dev_dbg(&pdev->dev, "IRQ notification via DSM (irq=%d, value=%d)\n", type, value);
  289. +
  290. + param.type = ACPI_TYPE_INTEGER;
  291. + param.integer.value = value;
  292. +
  293. + result = acpi_evaluate_dsm(handle, &shps_dsm_guid, SHPS_DSM_REVISION,
  294. + shps_dsm_fn_for_irq(type), &param);
  295. +
  296. + if (!result) {
  297. + dev_err(&pdev->dev, "IRQ notification via DSM failed (irq=%d, gpio=%d)\n",
  298. + type, value);
  299. +
  300. + } else if (result->type != ACPI_TYPE_BUFFER) {
  301. + dev_err(&pdev->dev,
  302. + "IRQ notification via DSM failed: unexpected result type (irq=%d, gpio=%d)\n",
  303. + type, value);
  304. +
  305. + } else if (result->buffer.length != 1 || result->buffer.pointer[0] != 0) {
  306. + dev_err(&pdev->dev,
  307. + "IRQ notification via DSM failed: unexpected result value (irq=%d, gpio=%d)\n",
  308. + type, value);
  309. + }
  310. +
  311. + mutex_unlock(&sdev->lock[type]);
  312. +
  313. + if (result)
  314. + ACPI_FREE(result);
  315. +}
  316. +
  317. +static irqreturn_t shps_handle_irq(int irq, void *data)
  318. +{
  319. + struct platform_device *pdev = data;
  320. + struct shps_device *sdev = platform_get_drvdata(pdev);
  321. + int type;
  322. +
  323. + /* Figure out which IRQ we're handling. */
  324. + for (type = 0; type < SHPS_NUM_IRQS; type++)
  325. + if (irq == sdev->irq[type])
  326. + break;
  327. +
  328. + /* We should have found our interrupt, if not: this is a bug. */
  329. + if (WARN(type >= SHPS_NUM_IRQS, "invalid IRQ number: %d\n", irq))
  330. + return IRQ_HANDLED;
  331. +
  332. + /* Forward interrupt to ACPI via DSM. */
  333. + shps_dsm_notify_irq(pdev, type);
  334. + return IRQ_HANDLED;
  335. +}
  336. +
  337. +static int shps_setup_irq(struct platform_device *pdev, enum shps_irq_type type)
  338. +{
  339. + unsigned long flags = IRQF_ONESHOT | IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING;
  340. + struct shps_device *sdev = platform_get_drvdata(pdev);
  341. + struct gpio_desc *gpiod;
  342. + acpi_handle handle = ACPI_HANDLE(&pdev->dev);
  343. + const char *irq_name;
  344. + const int dsm = shps_dsm_fn_for_irq(type);
  345. + int status, irq;
  346. +
  347. + /*
  348. + * Only set up interrupts that we actually need: The Surface Book 3
  349. + * does not have a DSM for base presence, so don't set up an interrupt
  350. + * for that.
  351. + */
  352. + if (!acpi_check_dsm(handle, &shps_dsm_guid, SHPS_DSM_REVISION, BIT(dsm))) {
  353. + dev_dbg(&pdev->dev, "IRQ notification via DSM not present (irq=%d)\n", type);
  354. + return 0;
  355. + }
  356. +
  357. + gpiod = devm_gpiod_get(&pdev->dev, shps_gpio_names[type], GPIOD_ASIS);
  358. + if (IS_ERR(gpiod))
  359. + return PTR_ERR(gpiod);
  360. +
  361. + irq = gpiod_to_irq(gpiod);
  362. + if (irq < 0)
  363. + return irq;
  364. +
  365. + irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "shps-irq-%d", type);
  366. + if (!irq_name)
  367. + return -ENOMEM;
  368. +
  369. + status = devm_request_threaded_irq(&pdev->dev, irq, NULL, shps_handle_irq,
  370. + flags, irq_name, pdev);
  371. + if (status)
  372. + return status;
  373. +
  374. + dev_dbg(&pdev->dev, "set up irq %d as type %d\n", irq, type);
  375. +
  376. + sdev->gpio[type] = gpiod;
  377. + sdev->irq[type] = irq;
  378. +
  379. + return 0;
  380. +}
  381. +
  382. +static int surface_hotplug_remove(struct platform_device *pdev)
  383. +{
  384. + struct shps_device *sdev = platform_get_drvdata(pdev);
  385. + int i;
  386. +
  387. + /* Ensure that IRQs have been fully handled and won't trigger any more. */
  388. + for (i = 0; i < SHPS_NUM_IRQS; i++) {
  389. + if (sdev->irq[i] != SHPS_IRQ_NOT_PRESENT)
  390. + disable_irq(sdev->irq[i]);
  391. +
  392. + mutex_destroy(&sdev->lock[i]);
  393. + }
  394. +
  395. + return 0;
  396. +}
  397. +
  398. +static int surface_hotplug_probe(struct platform_device *pdev)
  399. +{
  400. + struct shps_device *sdev;
  401. + int status, i;
  402. +
  403. + /*
  404. + * The MSHW0153 device is also present on the Surface Laptop 3,
  405. + * however that doesn't have a hot-pluggable PCIe device. It also
  406. + * doesn't have any GPIO interrupts/pins under the MSHW0153, so filter
  407. + * it out here.
  408. + */
  409. + if (gpiod_count(&pdev->dev, NULL) < 0)
  410. + return -ENODEV;
  411. +
  412. + status = devm_acpi_dev_add_driver_gpios(&pdev->dev, shps_acpi_gpios);
  413. + if (status)
  414. + return status;
  415. +
  416. + sdev = devm_kzalloc(&pdev->dev, sizeof(*sdev), GFP_KERNEL);
  417. + if (!sdev)
  418. + return -ENOMEM;
  419. +
  420. + platform_set_drvdata(pdev, sdev);
  421. +
  422. + /*
  423. + * Initialize IRQs so that we can safely call surface_hotplug_remove()
  424. + * on errors.
  425. + */
  426. + for (i = 0; i < SHPS_NUM_IRQS; i++)
  427. + sdev->irq[i] = SHPS_IRQ_NOT_PRESENT;
  428. +
  429. + /* Set up IRQs. */
  430. + for (i = 0; i < SHPS_NUM_IRQS; i++) {
  431. + mutex_init(&sdev->lock[i]);
  432. +
  433. + status = shps_setup_irq(pdev, i);
  434. + if (status) {
  435. + dev_err(&pdev->dev, "failed to set up IRQ %d: %d\n", i, status);
  436. + goto err;
  437. + }
  438. + }
  439. +
  440. + /* Ensure everything is up-to-date. */
  441. + for (i = 0; i < SHPS_NUM_IRQS; i++)
  442. + if (sdev->irq[i] != SHPS_IRQ_NOT_PRESENT)
  443. + shps_dsm_notify_irq(pdev, i);
  444. +
  445. + return 0;
  446. +
  447. +err:
  448. + surface_hotplug_remove(pdev);
  449. + return status;
  450. +}
  451. +
  452. +static const struct acpi_device_id surface_hotplug_acpi_match[] = {
  453. + { "MSHW0153", 0 },
  454. + { },
  455. +};
  456. +MODULE_DEVICE_TABLE(acpi, surface_hotplug_acpi_match);
  457. +
  458. +static struct platform_driver surface_hotplug_driver = {
  459. + .probe = surface_hotplug_probe,
  460. + .remove = surface_hotplug_remove,
  461. + .driver = {
  462. + .name = "surface_hotplug",
  463. + .acpi_match_table = surface_hotplug_acpi_match,
  464. + .probe_type = PROBE_PREFER_ASYNCHRONOUS,
  465. + },
  466. +};
  467. +module_platform_driver(surface_hotplug_driver);
  468. +
  469. +MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>");
  470. +MODULE_DESCRIPTION("Surface Hot-Plug Signaling Driver for Surface Book Devices");
  471. +MODULE_LICENSE("GPL");
  472. --
  473. 2.31.0