0006-surface-gpe.patch 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. From a151e4b8740bd6dd4fe439a217a58f24f5127621 Mon Sep 17 00:00:00 2001
  2. From: Maximilian Luz <luzmaximilian@gmail.com>
  3. Date: Sun, 16 Aug 2020 23:39:56 +0200
  4. Subject: [PATCH 6/6] surface-gpe
  5. ---
  6. drivers/platform/x86/Kconfig | 9 +
  7. drivers/platform/x86/Makefile | 1 +
  8. drivers/platform/x86/surface_gpe.c | 302 +++++++++++++++++++++++++++++
  9. 3 files changed, 312 insertions(+)
  10. create mode 100644 drivers/platform/x86/surface_gpe.c
  11. diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
  12. index 0ad7ad8cf8e17..47e75acd6c333 100644
  13. --- a/drivers/platform/x86/Kconfig
  14. +++ b/drivers/platform/x86/Kconfig
  15. @@ -868,6 +868,15 @@ config SURFACE_PRO3_BUTTON
  16. ---help---
  17. This driver handles the power/home/volume buttons on the Microsoft Surface Pro 3/4 tablet.
  18. +config SURFACE_GPE
  19. + tristate "Surface GPE/Lid Driver"
  20. + depends on ACPI
  21. + help
  22. + This driver marks the GPEs related to the ACPI lid device found on
  23. + Microsoft Surface devices as wakeup sources and prepares them
  24. + accordingly. It is required on those devices to allow wake-ups from
  25. + suspend by opening the lid.
  26. +
  27. config MSI_LAPTOP
  28. tristate "MSI Laptop Extras"
  29. depends on ACPI
  30. diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
  31. index 53408d9658740..0df0165ba340b 100644
  32. --- a/drivers/platform/x86/Makefile
  33. +++ b/drivers/platform/x86/Makefile
  34. @@ -84,6 +84,7 @@ obj-$(CONFIG_SURFACE3_WMI) += surface3-wmi.o
  35. obj-$(CONFIG_SURFACE_3_BUTTON) += surface3_button.o
  36. obj-$(CONFIG_SURFACE_3_POWER_OPREGION) += surface3_power.o
  37. obj-$(CONFIG_SURFACE_PRO3_BUTTON) += surfacepro3_button.o
  38. +obj-$(CONFIG_SURFACE_GPE) += surface_gpe.o
  39. # MSI
  40. obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o
  41. diff --git a/drivers/platform/x86/surface_gpe.c b/drivers/platform/x86/surface_gpe.c
  42. new file mode 100644
  43. index 0000000000000..3031a94cddeb7
  44. --- /dev/null
  45. +++ b/drivers/platform/x86/surface_gpe.c
  46. @@ -0,0 +1,302 @@
  47. +// SPDX-License-Identifier: GPL-2.0-or-later
  48. +/*
  49. + * Surface GPE/Lid driver to enable wakeup from suspend via the lid by
  50. + * properly configuring the respective GPEs.
  51. + */
  52. +
  53. +#include <linux/acpi.h>
  54. +#include <linux/dmi.h>
  55. +#include <linux/kernel.h>
  56. +#include <linux/module.h>
  57. +#include <linux/platform_device.h>
  58. +
  59. +
  60. +struct surface_lid_device {
  61. + u32 gpe_number;
  62. +};
  63. +
  64. +static const struct surface_lid_device lid_device_l17 = {
  65. + .gpe_number = 0x17,
  66. +};
  67. +
  68. +static const struct surface_lid_device lid_device_l4D = {
  69. + .gpe_number = 0x4D,
  70. +};
  71. +
  72. +static const struct surface_lid_device lid_device_l4F = {
  73. + .gpe_number = 0x4F,
  74. +};
  75. +
  76. +static const struct surface_lid_device lid_device_l57 = {
  77. + .gpe_number = 0x57,
  78. +};
  79. +
  80. +
  81. +// Note: When changing this don't forget to change the MODULE_ALIAS below.
  82. +static const struct dmi_system_id dmi_lid_device_table[] = {
  83. + {
  84. + .ident = "Surface Pro 4",
  85. + .matches = {
  86. + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
  87. + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Pro 4"),
  88. + },
  89. + .driver_data = (void *)&lid_device_l17,
  90. + },
  91. + {
  92. + .ident = "Surface Pro 5",
  93. + .matches = {
  94. + /*
  95. + * We match for SKU here due to generic product name
  96. + * "Surface Pro".
  97. + */
  98. + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
  99. + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Pro_1796"),
  100. + },
  101. + .driver_data = (void *)&lid_device_l4F,
  102. + },
  103. + {
  104. + .ident = "Surface Pro 5 (LTE)",
  105. + .matches = {
  106. + /*
  107. + * We match for SKU here due to generic product name
  108. + * "Surface Pro"
  109. + */
  110. + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
  111. + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Pro_1807"),
  112. + },
  113. + .driver_data = (void *)&lid_device_l4F,
  114. + },
  115. + {
  116. + .ident = "Surface Pro 6",
  117. + .matches = {
  118. + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
  119. + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Pro 6"),
  120. + },
  121. + .driver_data = (void *)&lid_device_l4F,
  122. + },
  123. + {
  124. + .ident = "Surface Pro 7",
  125. + .matches = {
  126. + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
  127. + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Pro 7"),
  128. + },
  129. + .driver_data = (void *)&lid_device_l4D,
  130. + },
  131. + {
  132. + .ident = "Surface Book 1",
  133. + .matches = {
  134. + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
  135. + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Book"),
  136. + },
  137. + .driver_data = (void *)&lid_device_l17,
  138. + },
  139. + {
  140. + .ident = "Surface Book 2",
  141. + .matches = {
  142. + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
  143. + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Book 2"),
  144. + },
  145. + .driver_data = (void *)&lid_device_l17,
  146. + },
  147. + {
  148. + .ident = "Surface Book 3",
  149. + .matches = {
  150. + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
  151. + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Book 3"),
  152. + },
  153. + .driver_data = (void *)&lid_device_l4D,
  154. + },
  155. + {
  156. + .ident = "Surface Laptop 1",
  157. + .matches = {
  158. + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
  159. + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Laptop"),
  160. + },
  161. + .driver_data = (void *)&lid_device_l57,
  162. + },
  163. + {
  164. + .ident = "Surface Laptop 2",
  165. + .matches = {
  166. + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
  167. + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Laptop 2"),
  168. + },
  169. + .driver_data = (void *)&lid_device_l57,
  170. + },
  171. + {
  172. + .ident = "Surface Laptop 3 (Intel 13\")",
  173. + .matches = {
  174. + /*
  175. + * We match for SKU here due to different vairants: The
  176. + * AMD (15") version does not rely on GPEs.
  177. + */
  178. + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
  179. + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Laptop_3_1867:1868"),
  180. + },
  181. + .driver_data = (void *)&lid_device_l4D,
  182. + },
  183. + { }
  184. +};
  185. +
  186. +
  187. +static int surface_lid_enable_wakeup(struct device *dev,
  188. + const struct surface_lid_device *lid,
  189. + bool enable)
  190. +{
  191. + int action = enable ? ACPI_GPE_ENABLE : ACPI_GPE_DISABLE;
  192. + acpi_status status;
  193. +
  194. + status = acpi_set_gpe_wake_mask(NULL, lid->gpe_number, action);
  195. + if (status) {
  196. + dev_err(dev, "failed to set GPE wake mask: %d\n", status);
  197. + return -EINVAL;
  198. + }
  199. +
  200. + return 0;
  201. +}
  202. +
  203. +
  204. +static int surface_gpe_suspend(struct device *dev)
  205. +{
  206. + const struct surface_lid_device *lid;
  207. +
  208. + lid = dev_get_platdata(dev);
  209. + return surface_lid_enable_wakeup(dev, lid, true);
  210. +}
  211. +
  212. +static int surface_gpe_resume(struct device *dev)
  213. +{
  214. + const struct surface_lid_device *lid;
  215. +
  216. + lid = dev_get_platdata(dev);
  217. + return surface_lid_enable_wakeup(dev, lid, false);
  218. +}
  219. +
  220. +static SIMPLE_DEV_PM_OPS(surface_gpe_pm, surface_gpe_suspend, surface_gpe_resume);
  221. +
  222. +
  223. +static int surface_gpe_probe(struct platform_device *pdev)
  224. +{
  225. + const struct surface_lid_device *lid;
  226. + int status;
  227. +
  228. + lid = dev_get_platdata(&pdev->dev);
  229. + if (!lid)
  230. + return -ENODEV;
  231. +
  232. + status = acpi_mark_gpe_for_wake(NULL, lid->gpe_number);
  233. + if (status) {
  234. + dev_err(&pdev->dev, "failed to mark GPE for wake: %d\n", status);
  235. + return -EINVAL;
  236. + }
  237. +
  238. + status = acpi_enable_gpe(NULL, lid->gpe_number);
  239. + if (status) {
  240. + dev_err(&pdev->dev, "failed to enable GPE: %d\n", status);
  241. + return -EINVAL;
  242. + }
  243. +
  244. + status = surface_lid_enable_wakeup(&pdev->dev, lid, false);
  245. + if (status) {
  246. + acpi_disable_gpe(NULL, lid->gpe_number);
  247. + return status;
  248. + }
  249. +
  250. + return 0;
  251. +}
  252. +
  253. +static int surface_gpe_remove(struct platform_device *pdev)
  254. +{
  255. + struct surface_lid_device *lid = dev_get_platdata(&pdev->dev);
  256. +
  257. + /* restore default behavior without this module */
  258. + surface_lid_enable_wakeup(&pdev->dev, lid, false);
  259. + acpi_disable_gpe(NULL, lid->gpe_number);
  260. +
  261. + return 0;
  262. +}
  263. +
  264. +static struct platform_driver surface_gpe_driver = {
  265. + .probe = surface_gpe_probe,
  266. + .remove = surface_gpe_remove,
  267. + .driver = {
  268. + .name = "surface_gpe",
  269. + .pm = &surface_gpe_pm,
  270. + .probe_type = PROBE_PREFER_ASYNCHRONOUS,
  271. + },
  272. +};
  273. +
  274. +
  275. +static struct platform_device *surface_gpe_device;
  276. +
  277. +static int __init surface_gpe_init(void)
  278. +{
  279. + const struct dmi_system_id *match;
  280. + const struct surface_lid_device *lid;
  281. +
  282. + struct platform_device *pdev;
  283. + int status;
  284. +
  285. + surface_gpe_device = NULL;
  286. +
  287. + match = dmi_first_match(dmi_lid_device_table);
  288. + if (!match) {
  289. + pr_info(KBUILD_MODNAME": no device detected, exiting\n");
  290. + return 0;
  291. + }
  292. +
  293. + lid = match->driver_data;
  294. +
  295. + status = platform_driver_register(&surface_gpe_driver);
  296. + if (status)
  297. + return status;
  298. +
  299. + pdev = platform_device_alloc("surface_gpe", PLATFORM_DEVID_NONE);
  300. + if (!pdev) {
  301. + platform_driver_unregister(&surface_gpe_driver);
  302. + return -ENOMEM;
  303. + }
  304. +
  305. + status = platform_device_add_data(pdev, lid, sizeof(*lid));
  306. + if (status) {
  307. + platform_device_put(pdev);
  308. + platform_driver_unregister(&surface_gpe_driver);
  309. + return status;
  310. + }
  311. +
  312. + status = platform_device_add(pdev);
  313. + if (status) {
  314. + platform_device_put(pdev);
  315. + platform_driver_unregister(&surface_gpe_driver);
  316. + return status;
  317. + }
  318. +
  319. + surface_gpe_device = pdev;
  320. + return 0;
  321. +}
  322. +
  323. +static void __exit surface_gpe_exit(void)
  324. +{
  325. + if (!surface_gpe_device)
  326. + return;
  327. +
  328. + platform_device_unregister(surface_gpe_device);
  329. + platform_driver_unregister(&surface_gpe_driver);
  330. +}
  331. +
  332. +module_init(surface_gpe_init);
  333. +module_exit(surface_gpe_exit);
  334. +
  335. +MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>");
  336. +MODULE_DESCRIPTION("Surface GPE/Lid Driver");
  337. +MODULE_LICENSE("GPL");
  338. +
  339. +MODULE_ALIAS("dmi:*:svnMicrosoftCorporation:pnSurfacePro:*");
  340. +MODULE_ALIAS("dmi:*:svnMicrosoftCorporation:pnSurfacePro4:*");
  341. +MODULE_ALIAS("dmi:*:svnMicrosoftCorporation:pnSurfacePro6:*");
  342. +MODULE_ALIAS("dmi:*:svnMicrosoftCorporation:pnSurfacePro7:*");
  343. +MODULE_ALIAS("dmi:*:svnMicrosoftCorporation:pnSurfaceBook:*");
  344. +MODULE_ALIAS("dmi:*:svnMicrosoftCorporation:pnSurfaceBook2:*");
  345. +MODULE_ALIAS("dmi:*:svnMicrosoftCorporation:pnSurfaceBook3:*");
  346. +MODULE_ALIAS("dmi:*:svnMicrosoftCorporation:pnSurfaceLaptop:*");
  347. +MODULE_ALIAS("dmi:*:svnMicrosoftCorporation:pnSurfaceLaptop2:*");
  348. +MODULE_ALIAS("dmi:*:svnMicrosoftCorporation:pnSurfaceLaptop3:*");
  349. --
  350. 2.28.0