123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355 |
- From a151e4b8740bd6dd4fe439a217a58f24f5127621 Mon Sep 17 00:00:00 2001
- From: Maximilian Luz <luzmaximilian@gmail.com>
- Date: Sun, 16 Aug 2020 23:39:56 +0200
- Subject: [PATCH 6/6] surface-gpe
- ---
- drivers/platform/x86/Kconfig | 9 +
- drivers/platform/x86/Makefile | 1 +
- 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
- index 0ad7ad8cf8e17..47e75acd6c333 100644
- --- a/drivers/platform/x86/Kconfig
- +++ b/drivers/platform/x86/Kconfig
- @@ -868,6 +868,15 @@ config SURFACE_PRO3_BUTTON
- ---help---
- This driver handles the power/home/volume buttons on the Microsoft Surface Pro 3/4 tablet.
-
- +config SURFACE_GPE
- + tristate "Surface GPE/Lid Driver"
- + depends on ACPI
- + help
- + This driver marks the GPEs related to the ACPI lid device found on
- + Microsoft Surface devices as wakeup sources and prepares them
- + accordingly. It is required on those devices to allow wake-ups from
- + suspend by opening the lid.
- +
- config MSI_LAPTOP
- tristate "MSI Laptop Extras"
- depends on ACPI
- diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
- index 53408d9658740..0df0165ba340b 100644
- --- a/drivers/platform/x86/Makefile
- +++ b/drivers/platform/x86/Makefile
- @@ -84,6 +84,7 @@ obj-$(CONFIG_SURFACE3_WMI) += surface3-wmi.o
- obj-$(CONFIG_SURFACE_3_BUTTON) += surface3_button.o
- obj-$(CONFIG_SURFACE_3_POWER_OPREGION) += surface3_power.o
- obj-$(CONFIG_SURFACE_PRO3_BUTTON) += surfacepro3_button.o
- +obj-$(CONFIG_SURFACE_GPE) += surface_gpe.o
-
- # MSI
- obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o
- diff --git a/drivers/platform/x86/surface_gpe.c b/drivers/platform/x86/surface_gpe.c
- new file mode 100644
- index 0000000000000..3031a94cddeb7
- --- /dev/null
- +++ b/drivers/platform/x86/surface_gpe.c
- @@ -0,0 +1,302 @@
- +// SPDX-License-Identifier: GPL-2.0-or-later
- +/*
- + * Surface GPE/Lid driver to enable wakeup from suspend via the lid by
- + * properly configuring the respective GPEs.
- + */
- +
- +#include <linux/acpi.h>
- +#include <linux/dmi.h>
- +#include <linux/kernel.h>
- +#include <linux/module.h>
- +#include <linux/platform_device.h>
- +
- +
- +struct surface_lid_device {
- + u32 gpe_number;
- +};
- +
- +static const struct surface_lid_device lid_device_l17 = {
- + .gpe_number = 0x17,
- +};
- +
- +static const struct surface_lid_device lid_device_l4D = {
- + .gpe_number = 0x4D,
- +};
- +
- +static const struct surface_lid_device lid_device_l4F = {
- + .gpe_number = 0x4F,
- +};
- +
- +static const struct surface_lid_device lid_device_l57 = {
- + .gpe_number = 0x57,
- +};
- +
- +
- +// Note: When changing this don't forget to change the MODULE_ALIAS below.
- +static const struct dmi_system_id dmi_lid_device_table[] = {
- + {
- + .ident = "Surface Pro 4",
- + .matches = {
- + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
- + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Pro 4"),
- + },
- + .driver_data = (void *)&lid_device_l17,
- + },
- + {
- + .ident = "Surface Pro 5",
- + .matches = {
- + /*
- + * We match for SKU here due to generic product name
- + * "Surface Pro".
- + */
- + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
- + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Pro_1796"),
- + },
- + .driver_data = (void *)&lid_device_l4F,
- + },
- + {
- + .ident = "Surface Pro 5 (LTE)",
- + .matches = {
- + /*
- + * We match for SKU here due to generic product name
- + * "Surface Pro"
- + */
- + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
- + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Pro_1807"),
- + },
- + .driver_data = (void *)&lid_device_l4F,
- + },
- + {
- + .ident = "Surface Pro 6",
- + .matches = {
- + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
- + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Pro 6"),
- + },
- + .driver_data = (void *)&lid_device_l4F,
- + },
- + {
- + .ident = "Surface Pro 7",
- + .matches = {
- + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
- + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Pro 7"),
- + },
- + .driver_data = (void *)&lid_device_l4D,
- + },
- + {
- + .ident = "Surface Book 1",
- + .matches = {
- + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
- + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Book"),
- + },
- + .driver_data = (void *)&lid_device_l17,
- + },
- + {
- + .ident = "Surface Book 2",
- + .matches = {
- + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
- + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Book 2"),
- + },
- + .driver_data = (void *)&lid_device_l17,
- + },
- + {
- + .ident = "Surface Book 3",
- + .matches = {
- + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
- + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Book 3"),
- + },
- + .driver_data = (void *)&lid_device_l4D,
- + },
- + {
- + .ident = "Surface Laptop 1",
- + .matches = {
- + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
- + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Laptop"),
- + },
- + .driver_data = (void *)&lid_device_l57,
- + },
- + {
- + .ident = "Surface Laptop 2",
- + .matches = {
- + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
- + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Laptop 2"),
- + },
- + .driver_data = (void *)&lid_device_l57,
- + },
- + {
- + .ident = "Surface Laptop 3 (Intel 13\")",
- + .matches = {
- + /*
- + * We match for SKU here due to different vairants: The
- + * AMD (15") version does not rely on GPEs.
- + */
- + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
- + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Laptop_3_1867:1868"),
- + },
- + .driver_data = (void *)&lid_device_l4D,
- + },
- + { }
- +};
- +
- +
- +static int surface_lid_enable_wakeup(struct device *dev,
- + const struct surface_lid_device *lid,
- + bool enable)
- +{
- + int action = enable ? ACPI_GPE_ENABLE : ACPI_GPE_DISABLE;
- + acpi_status status;
- +
- + status = acpi_set_gpe_wake_mask(NULL, lid->gpe_number, action);
- + if (status) {
- + dev_err(dev, "failed to set GPE wake mask: %d\n", status);
- + return -EINVAL;
- + }
- +
- + return 0;
- +}
- +
- +
- +static int surface_gpe_suspend(struct device *dev)
- +{
- + const struct surface_lid_device *lid;
- +
- + lid = dev_get_platdata(dev);
- + return surface_lid_enable_wakeup(dev, lid, true);
- +}
- +
- +static int surface_gpe_resume(struct device *dev)
- +{
- + const struct surface_lid_device *lid;
- +
- + lid = dev_get_platdata(dev);
- + return surface_lid_enable_wakeup(dev, lid, false);
- +}
- +
- +static SIMPLE_DEV_PM_OPS(surface_gpe_pm, surface_gpe_suspend, surface_gpe_resume);
- +
- +
- +static int surface_gpe_probe(struct platform_device *pdev)
- +{
- + const struct surface_lid_device *lid;
- + int status;
- +
- + lid = dev_get_platdata(&pdev->dev);
- + if (!lid)
- + return -ENODEV;
- +
- + status = acpi_mark_gpe_for_wake(NULL, lid->gpe_number);
- + if (status) {
- + dev_err(&pdev->dev, "failed to mark GPE for wake: %d\n", status);
- + return -EINVAL;
- + }
- +
- + status = acpi_enable_gpe(NULL, lid->gpe_number);
- + if (status) {
- + dev_err(&pdev->dev, "failed to enable GPE: %d\n", status);
- + return -EINVAL;
- + }
- +
- + status = surface_lid_enable_wakeup(&pdev->dev, lid, false);
- + if (status) {
- + acpi_disable_gpe(NULL, lid->gpe_number);
- + return status;
- + }
- +
- + return 0;
- +}
- +
- +static int surface_gpe_remove(struct platform_device *pdev)
- +{
- + struct surface_lid_device *lid = dev_get_platdata(&pdev->dev);
- +
- + /* restore default behavior without this module */
- + surface_lid_enable_wakeup(&pdev->dev, lid, false);
- + acpi_disable_gpe(NULL, lid->gpe_number);
- +
- + return 0;
- +}
- +
- +static struct platform_driver surface_gpe_driver = {
- + .probe = surface_gpe_probe,
- + .remove = surface_gpe_remove,
- + .driver = {
- + .name = "surface_gpe",
- + .pm = &surface_gpe_pm,
- + .probe_type = PROBE_PREFER_ASYNCHRONOUS,
- + },
- +};
- +
- +
- +static struct platform_device *surface_gpe_device;
- +
- +static int __init surface_gpe_init(void)
- +{
- + const struct dmi_system_id *match;
- + const struct surface_lid_device *lid;
- +
- + struct platform_device *pdev;
- + int status;
- +
- + surface_gpe_device = NULL;
- +
- + match = dmi_first_match(dmi_lid_device_table);
- + if (!match) {
- + pr_info(KBUILD_MODNAME": no device detected, exiting\n");
- + return 0;
- + }
- +
- + lid = match->driver_data;
- +
- + status = platform_driver_register(&surface_gpe_driver);
- + if (status)
- + return status;
- +
- + pdev = platform_device_alloc("surface_gpe", PLATFORM_DEVID_NONE);
- + if (!pdev) {
- + platform_driver_unregister(&surface_gpe_driver);
- + return -ENOMEM;
- + }
- +
- + status = platform_device_add_data(pdev, lid, sizeof(*lid));
- + if (status) {
- + platform_device_put(pdev);
- + platform_driver_unregister(&surface_gpe_driver);
- + return status;
- + }
- +
- + status = platform_device_add(pdev);
- + if (status) {
- + platform_device_put(pdev);
- + platform_driver_unregister(&surface_gpe_driver);
- + return status;
- + }
- +
- + surface_gpe_device = pdev;
- + return 0;
- +}
- +
- +static void __exit surface_gpe_exit(void)
- +{
- + if (!surface_gpe_device)
- + return;
- +
- + platform_device_unregister(surface_gpe_device);
- + platform_driver_unregister(&surface_gpe_driver);
- +}
- +
- +module_init(surface_gpe_init);
- +module_exit(surface_gpe_exit);
- +
- +MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>");
- +MODULE_DESCRIPTION("Surface GPE/Lid Driver");
- +MODULE_LICENSE("GPL");
- +
- +MODULE_ALIAS("dmi:*:svnMicrosoftCorporation:pnSurfacePro:*");
- +MODULE_ALIAS("dmi:*:svnMicrosoftCorporation:pnSurfacePro4:*");
- +MODULE_ALIAS("dmi:*:svnMicrosoftCorporation:pnSurfacePro6:*");
- +MODULE_ALIAS("dmi:*:svnMicrosoftCorporation:pnSurfacePro7:*");
- +MODULE_ALIAS("dmi:*:svnMicrosoftCorporation:pnSurfaceBook:*");
- +MODULE_ALIAS("dmi:*:svnMicrosoftCorporation:pnSurfaceBook2:*");
- +MODULE_ALIAS("dmi:*:svnMicrosoftCorporation:pnSurfaceBook3:*");
- +MODULE_ALIAS("dmi:*:svnMicrosoftCorporation:pnSurfaceLaptop:*");
- +MODULE_ALIAS("dmi:*:svnMicrosoftCorporation:pnSurfaceLaptop2:*");
- +MODULE_ALIAS("dmi:*:svnMicrosoftCorporation:pnSurfaceLaptop3:*");
- --
- 2.28.0
|