Re: [PATCH v5] platform/surface: Add Driver to set up lid GPEs on MS Surface device

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,

On 10/28/20 11:54 AM, Maximilian Luz wrote:
> Conventionally, wake-up events for a specific device, in our case the
> lid device, are managed via the ACPI _PRW field. While this does not
> seem strictly necessary based on ACPI spec, the kernel disables GPE
> wakeups to avoid non-wakeup interrupts preventing suspend by default and
> only enables GPEs associated via the _PRW field with a wake-up capable
> device. This behavior has been introduced in commit f941d3e41da7 ("ACPI:
> EC / PM: Disable non-wakeup GPEs for suspend-to-idle") and is described
> in more detail in its commit message.
> 
> Unfortunately, on MS Surface devices, there is no _PRW field present on
> the lid device, thus no GPE is associated with it, and therefore the GPE
> responsible for sending the status-change notification to the lid gets
> disabled during suspend, making it impossible to wake the device via the
> lid.
> 
> This patch introduces a pseudo-device and respective driver which, based
> on some DMI matching, marks the corresponding GPE of the lid device for
> wake and enables it during suspend. The behavior of this driver models
> the behavior of the ACPI/PM core for normal wakeup GPEs, properly
> declared via the _PRW field.
> 
> Signed-off-by: Maximilian Luz <luzmaximilian@xxxxxxxxx>

Thank you for your patch, I've applied this patch to my review-hans 
branch:
https://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86.git/log/?h=review-hans

Note it will show up in my review-hans branch once I've pushed my
local branch there, which might take a while.

Once I've run some tests on this branch the patches there will be
added to the platform-drivers-x86/for-next branch and eventually
will be included in the pdx86 pull-request to Linus for the next
merge-window.

Regards,

Hans



> ---
> 
> Note: This patch depends and is based on
> 
>   [PATCH v4] platform/surface: Create a platform subdirectory for
>              Microsoft Surface devices
> 
> which can be found at
> 
>   https://lore.kernel.org/platform-driver-x86/20201009141128.683254-1-luzmaximilian@xxxxxxxxx/
> 
> and is currently in platform-drivers-x86/for-next.
> 
> Changes in v2:
>  - Use software nodes and device properties instead of platform data.
>  - Simplify module alias.
>  - Add comment regarding origin of GPE numbers.
>  - Fix style issues.
> 
> Changes in v3:
>  - Rebase onto v5.9-rc5
>  - Fix and restructure error handling and module cleanup.
>  - Remove unnecessary platform_set_drvdata(..., NULL) calls.
>  - Add pr_fmt definition
>  - Return -ENODEV if no compatible device is found in module init.
> 
> Changes in v4:
>  - Rebase onto platform/surface patch-set
>  - Add copyright line
>  - Fix typo in comment
> 
> Changes in v5:
>  - Rebase onto current platform-drivers-x86/for-next
>  - Fix MAINTAINERS entry
> 
> ---
>  MAINTAINERS                            |   6 +
>  drivers/platform/surface/Kconfig       |  10 +
>  drivers/platform/surface/Makefile      |   1 +
>  drivers/platform/surface/surface_gpe.c | 309 +++++++++++++++++++++++++
>  4 files changed, 326 insertions(+)
>  create mode 100644 drivers/platform/surface/surface_gpe.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 38b70bd41d96..17e51a309a3a 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -11658,6 +11658,12 @@ F:	drivers/scsi/smartpqi/smartpqi*.[ch]
>  F:	include/linux/cciss*.h
>  F:	include/uapi/linux/cciss*.h
>  
> +MICROSOFT SURFACE GPE LID SUPPORT DRIVER
> +M:	Maximilian Luz <luzmaximilian@xxxxxxxxx>
> +L:	platform-driver-x86@xxxxxxxxxxxxxxx
> +S:	Maintained
> +F:	drivers/platform/surface/surface_gpe.c
> +
>  MICROSOFT SURFACE HARDWARE PLATFORM SUPPORT
>  M:	Hans de Goede <hdegoede@xxxxxxxxxx>
>  M:	Mark Gross <mgross@xxxxxxxxxxxxxxx>
> diff --git a/drivers/platform/surface/Kconfig b/drivers/platform/surface/Kconfig
> index 10902ea43861..33040b0b3b79 100644
> --- a/drivers/platform/surface/Kconfig
> +++ b/drivers/platform/surface/Kconfig
> @@ -40,6 +40,16 @@ config SURFACE_3_POWER_OPREGION
>  	  This driver provides support for ACPI operation
>  	  region of the Surface 3 battery platform driver.
>  
> +config SURFACE_GPE
> +	tristate "Surface GPE/Lid Support Driver"
> +	depends on ACPI
> +	depends on DMI
> +	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 SURFACE_PRO3_BUTTON
>  	tristate "Power/home/volume buttons driver for Microsoft Surface Pro 3/4 tablet"
>  	depends on ACPI && INPUT
> diff --git a/drivers/platform/surface/Makefile b/drivers/platform/surface/Makefile
> index dcb1df06d57a..cedfb027ded1 100644
> --- a/drivers/platform/surface/Makefile
> +++ b/drivers/platform/surface/Makefile
> @@ -7,4 +7,5 @@
>  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_GPE)		+= surface_gpe.o
>  obj-$(CONFIG_SURFACE_PRO3_BUTTON)	+= surfacepro3_button.o
> diff --git a/drivers/platform/surface/surface_gpe.c b/drivers/platform/surface/surface_gpe.c
> new file mode 100644
> index 000000000000..0f44a52d3a9b
> --- /dev/null
> +++ b/drivers/platform/surface/surface_gpe.c
> @@ -0,0 +1,309 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Surface GPE/Lid driver to enable wakeup from suspend via the lid by
> + * properly configuring the respective GPEs. Required for wakeup via lid on
> + * newer Intel-based Microsoft Surface devices.
> + *
> + * Copyright (C) 2020 Maximilian Luz <luzmaximilian@xxxxxxxxx>
> + */
> +
> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> +
> +#include <linux/acpi.h>
> +#include <linux/dmi.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +
> +/*
> + * Note: The GPE numbers for the lid devices found below have been obtained
> + *       from ACPI/the DSDT table, specifically from the GPE handler for the
> + *       lid.
> + */
> +
> +static const struct property_entry lid_device_props_l17[] = {
> +	PROPERTY_ENTRY_U32("gpe", 0x17),
> +	{},
> +};
> +
> +static const struct property_entry lid_device_props_l4D[] = {
> +	PROPERTY_ENTRY_U32("gpe", 0x4D),
> +	{},
> +};
> +
> +static const struct property_entry lid_device_props_l4F[] = {
> +	PROPERTY_ENTRY_U32("gpe", 0x4F),
> +	{},
> +};
> +
> +static const struct property_entry lid_device_props_l57[] = {
> +	PROPERTY_ENTRY_U32("gpe", 0x57),
> +	{},
> +};
> +
> +/*
> + * Note: When changing this, don't forget to check that the MODULE_ALIAS below
> + *       still fits.
> + */
> +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_props_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_props_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_props_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_props_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_props_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_props_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_props_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_props_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_props_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_props_l57,
> +	},
> +	{
> +		.ident = "Surface Laptop 3 (Intel 13\")",
> +		.matches = {
> +			/*
> +			 * We match for SKU here due to different variants: 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_props_l4D,
> +	},
> +	{ }
> +};
> +
> +struct surface_lid_device {
> +	u32 gpe_number;
> +};
> +
> +static int surface_lid_enable_wakeup(struct device *dev, bool enable)
> +{
> +	const struct surface_lid_device *lid = dev_get_drvdata(dev);
> +	int action = enable ? ACPI_GPE_ENABLE : ACPI_GPE_DISABLE;
> +	acpi_status status;
> +
> +	status = acpi_set_gpe_wake_mask(NULL, lid->gpe_number, action);
> +	if (ACPI_FAILURE(status)) {
> +		dev_err(dev, "failed to set GPE wake mask: %s\n",
> +			acpi_format_exception(status));
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int surface_gpe_suspend(struct device *dev)
> +{
> +	return surface_lid_enable_wakeup(dev, true);
> +}
> +
> +static int surface_gpe_resume(struct device *dev)
> +{
> +	return surface_lid_enable_wakeup(dev, false);
> +}
> +
> +static SIMPLE_DEV_PM_OPS(surface_gpe_pm, surface_gpe_suspend, surface_gpe_resume);
> +
> +static int surface_gpe_probe(struct platform_device *pdev)
> +{
> +	struct surface_lid_device *lid;
> +	u32 gpe_number;
> +	acpi_status status;
> +	int ret;
> +
> +	ret = device_property_read_u32(&pdev->dev, "gpe", &gpe_number);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to read 'gpe' property: %d\n", ret);
> +		return ret;
> +	}
> +
> +	lid = devm_kzalloc(&pdev->dev, sizeof(*lid), GFP_KERNEL);
> +	if (!lid)
> +		return -ENOMEM;
> +
> +	lid->gpe_number = gpe_number;
> +	platform_set_drvdata(pdev, lid);
> +
> +	status = acpi_mark_gpe_for_wake(NULL, gpe_number);
> +	if (ACPI_FAILURE(status)) {
> +		dev_err(&pdev->dev, "failed to mark GPE for wake: %s\n",
> +			acpi_format_exception(status));
> +		return -EINVAL;
> +	}
> +
> +	status = acpi_enable_gpe(NULL, gpe_number);
> +	if (ACPI_FAILURE(status)) {
> +		dev_err(&pdev->dev, "failed to enable GPE: %s\n",
> +			acpi_format_exception(status));
> +		return -EINVAL;
> +	}
> +
> +	ret = surface_lid_enable_wakeup(&pdev->dev, false);
> +	if (ret)
> +		acpi_disable_gpe(NULL, gpe_number);
> +
> +	return ret;
> +}
> +
> +static int surface_gpe_remove(struct platform_device *pdev)
> +{
> +	struct surface_lid_device *lid = dev_get_drvdata(&pdev->dev);
> +
> +	/* restore default behavior without this module */
> +	surface_lid_enable_wakeup(&pdev->dev, 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;
> +	struct platform_device *pdev;
> +	struct fwnode_handle *fwnode;
> +	int status;
> +
> +	match = dmi_first_match(dmi_lid_device_table);
> +	if (!match) {
> +		pr_info("no compatible Microsoft Surface device found, exiting\n");
> +		return -ENODEV;
> +	}
> +
> +	status = platform_driver_register(&surface_gpe_driver);
> +	if (status)
> +		return status;
> +
> +	fwnode = fwnode_create_software_node(match->driver_data, NULL);
> +	if (IS_ERR(fwnode)) {
> +		status = PTR_ERR(fwnode);
> +		goto err_node;
> +	}
> +
> +	pdev = platform_device_alloc("surface_gpe", PLATFORM_DEVID_NONE);
> +	if (!pdev) {
> +		status = -ENOMEM;
> +		goto err_alloc;
> +	}
> +
> +	pdev->dev.fwnode = fwnode;
> +
> +	status = platform_device_add(pdev);
> +	if (status)
> +		goto err_add;
> +
> +	surface_gpe_device = pdev;
> +	return 0;
> +
> +err_add:
> +	platform_device_put(pdev);
> +err_alloc:
> +	fwnode_remove_software_node(fwnode);
> +err_node:
> +	platform_driver_unregister(&surface_gpe_driver);
> +	return status;
> +}
> +module_init(surface_gpe_init);
> +
> +static void __exit surface_gpe_exit(void)
> +{
> +	struct fwnode_handle *fwnode = surface_gpe_device->dev.fwnode;
> +
> +	platform_device_unregister(surface_gpe_device);
> +	platform_driver_unregister(&surface_gpe_driver);
> +	fwnode_remove_software_node(fwnode);
> +}
> +module_exit(surface_gpe_exit);
> +
> +MODULE_AUTHOR("Maximilian Luz <luzmaximilian@xxxxxxxxx>");
> +MODULE_DESCRIPTION("Surface GPE/Lid Driver");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("dmi:*:svnMicrosoftCorporation:pnSurface*:*");
> 




[Index of Archives]     [Linux Kernel Development]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux