cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

surface_gpe.c (8707B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Surface GPE/Lid driver to enable wakeup from suspend via the lid by
      4 * properly configuring the respective GPEs. Required for wakeup via lid on
      5 * newer Intel-based Microsoft Surface devices.
      6 *
      7 * Copyright (C) 2020 Maximilian Luz <luzmaximilian@gmail.com>
      8 */
      9
     10#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
     11
     12#include <linux/acpi.h>
     13#include <linux/dmi.h>
     14#include <linux/kernel.h>
     15#include <linux/module.h>
     16#include <linux/platform_device.h>
     17
     18/*
     19 * Note: The GPE numbers for the lid devices found below have been obtained
     20 *       from ACPI/the DSDT table, specifically from the GPE handler for the
     21 *       lid.
     22 */
     23
     24static const struct property_entry lid_device_props_l17[] = {
     25	PROPERTY_ENTRY_U32("gpe", 0x17),
     26	{},
     27};
     28
     29static const struct property_entry lid_device_props_l4B[] = {
     30	PROPERTY_ENTRY_U32("gpe", 0x4B),
     31	{},
     32};
     33
     34static const struct property_entry lid_device_props_l4D[] = {
     35	PROPERTY_ENTRY_U32("gpe", 0x4D),
     36	{},
     37};
     38
     39static const struct property_entry lid_device_props_l4F[] = {
     40	PROPERTY_ENTRY_U32("gpe", 0x4F),
     41	{},
     42};
     43
     44static const struct property_entry lid_device_props_l57[] = {
     45	PROPERTY_ENTRY_U32("gpe", 0x57),
     46	{},
     47};
     48
     49/*
     50 * Note: When changing this, don't forget to check that the MODULE_ALIAS below
     51 *       still fits.
     52 */
     53static const struct dmi_system_id dmi_lid_device_table[] = {
     54	{
     55		.ident = "Surface Pro 4",
     56		.matches = {
     57			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
     58			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Pro 4"),
     59		},
     60		.driver_data = (void *)lid_device_props_l17,
     61	},
     62	{
     63		.ident = "Surface Pro 5",
     64		.matches = {
     65			/*
     66			 * We match for SKU here due to generic product name
     67			 * "Surface Pro".
     68			 */
     69			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
     70			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Pro_1796"),
     71		},
     72		.driver_data = (void *)lid_device_props_l4F,
     73	},
     74	{
     75		.ident = "Surface Pro 5 (LTE)",
     76		.matches = {
     77			/*
     78			 * We match for SKU here due to generic product name
     79			 * "Surface Pro"
     80			 */
     81			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
     82			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Pro_1807"),
     83		},
     84		.driver_data = (void *)lid_device_props_l4F,
     85	},
     86	{
     87		.ident = "Surface Pro 6",
     88		.matches = {
     89			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
     90			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Pro 6"),
     91		},
     92		.driver_data = (void *)lid_device_props_l4F,
     93	},
     94	{
     95		.ident = "Surface Pro 7",
     96		.matches = {
     97			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
     98			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Pro 7"),
     99		},
    100		.driver_data = (void *)lid_device_props_l4D,
    101	},
    102	{
    103		.ident = "Surface Pro 8",
    104		.matches = {
    105			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
    106			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Pro 8"),
    107		},
    108		.driver_data = (void *)lid_device_props_l4B,
    109	},
    110	{
    111		.ident = "Surface Book 1",
    112		.matches = {
    113			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
    114			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Book"),
    115		},
    116		.driver_data = (void *)lid_device_props_l17,
    117	},
    118	{
    119		.ident = "Surface Book 2",
    120		.matches = {
    121			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
    122			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Book 2"),
    123		},
    124		.driver_data = (void *)lid_device_props_l17,
    125	},
    126	{
    127		.ident = "Surface Book 3",
    128		.matches = {
    129			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
    130			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Book 3"),
    131		},
    132		.driver_data = (void *)lid_device_props_l4D,
    133	},
    134	{
    135		.ident = "Surface Laptop 1",
    136		.matches = {
    137			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
    138			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Laptop"),
    139		},
    140		.driver_data = (void *)lid_device_props_l57,
    141	},
    142	{
    143		.ident = "Surface Laptop 2",
    144		.matches = {
    145			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
    146			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Laptop 2"),
    147		},
    148		.driver_data = (void *)lid_device_props_l57,
    149	},
    150	{
    151		.ident = "Surface Laptop 3 (Intel 13\")",
    152		.matches = {
    153			/*
    154			 * We match for SKU here due to different variants: The
    155			 * AMD (15") version does not rely on GPEs.
    156			 */
    157			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
    158			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Laptop_3_1867:1868"),
    159		},
    160		.driver_data = (void *)lid_device_props_l4D,
    161	},
    162	{
    163		.ident = "Surface Laptop 3 (Intel 15\")",
    164		.matches = {
    165			/*
    166			 * We match for SKU here due to different variants: The
    167			 * AMD (15") version does not rely on GPEs.
    168			 */
    169			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
    170			DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Laptop_3_1872"),
    171		},
    172		.driver_data = (void *)lid_device_props_l4D,
    173	},
    174	{
    175		.ident = "Surface Laptop Studio",
    176		.matches = {
    177			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
    178			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Laptop Studio"),
    179		},
    180		.driver_data = (void *)lid_device_props_l4B,
    181	},
    182	{ }
    183};
    184
    185struct surface_lid_device {
    186	u32 gpe_number;
    187};
    188
    189static int surface_lid_enable_wakeup(struct device *dev, bool enable)
    190{
    191	const struct surface_lid_device *lid = dev_get_drvdata(dev);
    192	int action = enable ? ACPI_GPE_ENABLE : ACPI_GPE_DISABLE;
    193	acpi_status status;
    194
    195	status = acpi_set_gpe_wake_mask(NULL, lid->gpe_number, action);
    196	if (ACPI_FAILURE(status)) {
    197		dev_err(dev, "failed to set GPE wake mask: %s\n",
    198			acpi_format_exception(status));
    199		return -EINVAL;
    200	}
    201
    202	return 0;
    203}
    204
    205static int __maybe_unused surface_gpe_suspend(struct device *dev)
    206{
    207	return surface_lid_enable_wakeup(dev, true);
    208}
    209
    210static int __maybe_unused surface_gpe_resume(struct device *dev)
    211{
    212	return surface_lid_enable_wakeup(dev, false);
    213}
    214
    215static SIMPLE_DEV_PM_OPS(surface_gpe_pm, surface_gpe_suspend, surface_gpe_resume);
    216
    217static int surface_gpe_probe(struct platform_device *pdev)
    218{
    219	struct surface_lid_device *lid;
    220	u32 gpe_number;
    221	acpi_status status;
    222	int ret;
    223
    224	ret = device_property_read_u32(&pdev->dev, "gpe", &gpe_number);
    225	if (ret) {
    226		dev_err(&pdev->dev, "failed to read 'gpe' property: %d\n", ret);
    227		return ret;
    228	}
    229
    230	lid = devm_kzalloc(&pdev->dev, sizeof(*lid), GFP_KERNEL);
    231	if (!lid)
    232		return -ENOMEM;
    233
    234	lid->gpe_number = gpe_number;
    235	platform_set_drvdata(pdev, lid);
    236
    237	status = acpi_mark_gpe_for_wake(NULL, gpe_number);
    238	if (ACPI_FAILURE(status)) {
    239		dev_err(&pdev->dev, "failed to mark GPE for wake: %s\n",
    240			acpi_format_exception(status));
    241		return -EINVAL;
    242	}
    243
    244	status = acpi_enable_gpe(NULL, gpe_number);
    245	if (ACPI_FAILURE(status)) {
    246		dev_err(&pdev->dev, "failed to enable GPE: %s\n",
    247			acpi_format_exception(status));
    248		return -EINVAL;
    249	}
    250
    251	ret = surface_lid_enable_wakeup(&pdev->dev, false);
    252	if (ret)
    253		acpi_disable_gpe(NULL, gpe_number);
    254
    255	return ret;
    256}
    257
    258static int surface_gpe_remove(struct platform_device *pdev)
    259{
    260	struct surface_lid_device *lid = dev_get_drvdata(&pdev->dev);
    261
    262	/* restore default behavior without this module */
    263	surface_lid_enable_wakeup(&pdev->dev, false);
    264	acpi_disable_gpe(NULL, lid->gpe_number);
    265
    266	return 0;
    267}
    268
    269static struct platform_driver surface_gpe_driver = {
    270	.probe = surface_gpe_probe,
    271	.remove = surface_gpe_remove,
    272	.driver = {
    273		.name = "surface_gpe",
    274		.pm = &surface_gpe_pm,
    275		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
    276	},
    277};
    278
    279static struct platform_device *surface_gpe_device;
    280
    281static int __init surface_gpe_init(void)
    282{
    283	const struct dmi_system_id *match;
    284	struct platform_device *pdev;
    285	struct fwnode_handle *fwnode;
    286	int status;
    287
    288	match = dmi_first_match(dmi_lid_device_table);
    289	if (!match) {
    290		pr_info("no compatible Microsoft Surface device found, exiting\n");
    291		return -ENODEV;
    292	}
    293
    294	status = platform_driver_register(&surface_gpe_driver);
    295	if (status)
    296		return status;
    297
    298	fwnode = fwnode_create_software_node(match->driver_data, NULL);
    299	if (IS_ERR(fwnode)) {
    300		status = PTR_ERR(fwnode);
    301		goto err_node;
    302	}
    303
    304	pdev = platform_device_alloc("surface_gpe", PLATFORM_DEVID_NONE);
    305	if (!pdev) {
    306		status = -ENOMEM;
    307		goto err_alloc;
    308	}
    309
    310	pdev->dev.fwnode = fwnode;
    311
    312	status = platform_device_add(pdev);
    313	if (status)
    314		goto err_add;
    315
    316	surface_gpe_device = pdev;
    317	return 0;
    318
    319err_add:
    320	platform_device_put(pdev);
    321err_alloc:
    322	fwnode_remove_software_node(fwnode);
    323err_node:
    324	platform_driver_unregister(&surface_gpe_driver);
    325	return status;
    326}
    327module_init(surface_gpe_init);
    328
    329static void __exit surface_gpe_exit(void)
    330{
    331	struct fwnode_handle *fwnode = surface_gpe_device->dev.fwnode;
    332
    333	platform_device_unregister(surface_gpe_device);
    334	platform_driver_unregister(&surface_gpe_driver);
    335	fwnode_remove_software_node(fwnode);
    336}
    337module_exit(surface_gpe_exit);
    338
    339MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>");
    340MODULE_DESCRIPTION("Surface GPE/Lid Driver");
    341MODULE_LICENSE("GPL");
    342MODULE_ALIAS("dmi:*:svnMicrosoftCorporation:pnSurface*:*");