Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Surface GPE/Lid driver to enable wakeup from suspend via the lid by
0004  * properly configuring the respective GPEs. Required for wakeup via lid on
0005  * newer Intel-based Microsoft Surface devices.
0006  *
0007  * Copyright (C) 2020-2022 Maximilian Luz <luzmaximilian@gmail.com>
0008  */
0009 
0010 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0011 
0012 #include <linux/acpi.h>
0013 #include <linux/dmi.h>
0014 #include <linux/kernel.h>
0015 #include <linux/module.h>
0016 #include <linux/platform_device.h>
0017 
0018 /*
0019  * Note: The GPE numbers for the lid devices found below have been obtained
0020  *       from ACPI/the DSDT table, specifically from the GPE handler for the
0021  *       lid.
0022  */
0023 
0024 static const struct property_entry lid_device_props_l17[] = {
0025     PROPERTY_ENTRY_U32("gpe", 0x17),
0026     {},
0027 };
0028 
0029 static const struct property_entry lid_device_props_l4B[] = {
0030     PROPERTY_ENTRY_U32("gpe", 0x4B),
0031     {},
0032 };
0033 
0034 static const struct property_entry lid_device_props_l4D[] = {
0035     PROPERTY_ENTRY_U32("gpe", 0x4D),
0036     {},
0037 };
0038 
0039 static const struct property_entry lid_device_props_l4F[] = {
0040     PROPERTY_ENTRY_U32("gpe", 0x4F),
0041     {},
0042 };
0043 
0044 static const struct property_entry lid_device_props_l57[] = {
0045     PROPERTY_ENTRY_U32("gpe", 0x57),
0046     {},
0047 };
0048 
0049 /*
0050  * Note: When changing this, don't forget to check that the MODULE_ALIAS below
0051  *       still fits.
0052  */
0053 static const struct dmi_system_id dmi_lid_device_table[] = {
0054     {
0055         .ident = "Surface Pro 4",
0056         .matches = {
0057             DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
0058             DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Pro 4"),
0059         },
0060         .driver_data = (void *)lid_device_props_l17,
0061     },
0062     {
0063         .ident = "Surface Pro 5",
0064         .matches = {
0065             /*
0066              * We match for SKU here due to generic product name
0067              * "Surface Pro".
0068              */
0069             DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
0070             DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Pro_1796"),
0071         },
0072         .driver_data = (void *)lid_device_props_l4F,
0073     },
0074     {
0075         .ident = "Surface Pro 5 (LTE)",
0076         .matches = {
0077             /*
0078              * We match for SKU here due to generic product name
0079              * "Surface Pro"
0080              */
0081             DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
0082             DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Pro_1807"),
0083         },
0084         .driver_data = (void *)lid_device_props_l4F,
0085     },
0086     {
0087         .ident = "Surface Pro 6",
0088         .matches = {
0089             DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
0090             DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Pro 6"),
0091         },
0092         .driver_data = (void *)lid_device_props_l4F,
0093     },
0094     {
0095         .ident = "Surface Pro 7",
0096         .matches = {
0097             DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
0098             DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Pro 7"),
0099         },
0100         .driver_data = (void *)lid_device_props_l4D,
0101     },
0102     {
0103         .ident = "Surface Pro 8",
0104         .matches = {
0105             DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
0106             DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Pro 8"),
0107         },
0108         .driver_data = (void *)lid_device_props_l4B,
0109     },
0110     {
0111         .ident = "Surface Book 1",
0112         .matches = {
0113             DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
0114             DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Book"),
0115         },
0116         .driver_data = (void *)lid_device_props_l17,
0117     },
0118     {
0119         .ident = "Surface Book 2",
0120         .matches = {
0121             DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
0122             DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Book 2"),
0123         },
0124         .driver_data = (void *)lid_device_props_l17,
0125     },
0126     {
0127         .ident = "Surface Book 3",
0128         .matches = {
0129             DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
0130             DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Book 3"),
0131         },
0132         .driver_data = (void *)lid_device_props_l4D,
0133     },
0134     {
0135         .ident = "Surface Laptop 1",
0136         .matches = {
0137             DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
0138             DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Laptop"),
0139         },
0140         .driver_data = (void *)lid_device_props_l57,
0141     },
0142     {
0143         .ident = "Surface Laptop 2",
0144         .matches = {
0145             DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
0146             DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Laptop 2"),
0147         },
0148         .driver_data = (void *)lid_device_props_l57,
0149     },
0150     {
0151         .ident = "Surface Laptop 3 (Intel 13\")",
0152         .matches = {
0153             /*
0154              * We match for SKU here due to different variants: The
0155              * AMD (15") version does not rely on GPEs.
0156              */
0157             DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
0158             DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Laptop_3_1867:1868"),
0159         },
0160         .driver_data = (void *)lid_device_props_l4D,
0161     },
0162     {
0163         .ident = "Surface Laptop 3 (Intel 15\")",
0164         .matches = {
0165             /*
0166              * We match for SKU here due to different variants: The
0167              * AMD (15") version does not rely on GPEs.
0168              */
0169             DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
0170             DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Laptop_3_1872"),
0171         },
0172         .driver_data = (void *)lid_device_props_l4D,
0173     },
0174     {
0175         .ident = "Surface Laptop 4 (Intel 13\")",
0176         .matches = {
0177             /*
0178              * We match for SKU here due to different variants: The
0179              * AMD (15") version does not rely on GPEs.
0180              */
0181             DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
0182             DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Laptop_4_1950:1951"),
0183         },
0184         .driver_data = (void *)lid_device_props_l4B,
0185     },
0186     {
0187         .ident = "Surface Laptop Studio",
0188         .matches = {
0189             DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
0190             DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Laptop Studio"),
0191         },
0192         .driver_data = (void *)lid_device_props_l4B,
0193     },
0194     { }
0195 };
0196 
0197 struct surface_lid_device {
0198     u32 gpe_number;
0199 };
0200 
0201 static int surface_lid_enable_wakeup(struct device *dev, bool enable)
0202 {
0203     const struct surface_lid_device *lid = dev_get_drvdata(dev);
0204     int action = enable ? ACPI_GPE_ENABLE : ACPI_GPE_DISABLE;
0205     acpi_status status;
0206 
0207     status = acpi_set_gpe_wake_mask(NULL, lid->gpe_number, action);
0208     if (ACPI_FAILURE(status)) {
0209         dev_err(dev, "failed to set GPE wake mask: %s\n",
0210             acpi_format_exception(status));
0211         return -EINVAL;
0212     }
0213 
0214     return 0;
0215 }
0216 
0217 static int __maybe_unused surface_gpe_suspend(struct device *dev)
0218 {
0219     return surface_lid_enable_wakeup(dev, true);
0220 }
0221 
0222 static int __maybe_unused surface_gpe_resume(struct device *dev)
0223 {
0224     return surface_lid_enable_wakeup(dev, false);
0225 }
0226 
0227 static SIMPLE_DEV_PM_OPS(surface_gpe_pm, surface_gpe_suspend, surface_gpe_resume);
0228 
0229 static int surface_gpe_probe(struct platform_device *pdev)
0230 {
0231     struct surface_lid_device *lid;
0232     u32 gpe_number;
0233     acpi_status status;
0234     int ret;
0235 
0236     ret = device_property_read_u32(&pdev->dev, "gpe", &gpe_number);
0237     if (ret) {
0238         dev_err(&pdev->dev, "failed to read 'gpe' property: %d\n", ret);
0239         return ret;
0240     }
0241 
0242     lid = devm_kzalloc(&pdev->dev, sizeof(*lid), GFP_KERNEL);
0243     if (!lid)
0244         return -ENOMEM;
0245 
0246     lid->gpe_number = gpe_number;
0247     platform_set_drvdata(pdev, lid);
0248 
0249     status = acpi_mark_gpe_for_wake(NULL, gpe_number);
0250     if (ACPI_FAILURE(status)) {
0251         dev_err(&pdev->dev, "failed to mark GPE for wake: %s\n",
0252             acpi_format_exception(status));
0253         return -EINVAL;
0254     }
0255 
0256     status = acpi_enable_gpe(NULL, gpe_number);
0257     if (ACPI_FAILURE(status)) {
0258         dev_err(&pdev->dev, "failed to enable GPE: %s\n",
0259             acpi_format_exception(status));
0260         return -EINVAL;
0261     }
0262 
0263     ret = surface_lid_enable_wakeup(&pdev->dev, false);
0264     if (ret)
0265         acpi_disable_gpe(NULL, gpe_number);
0266 
0267     return ret;
0268 }
0269 
0270 static int surface_gpe_remove(struct platform_device *pdev)
0271 {
0272     struct surface_lid_device *lid = dev_get_drvdata(&pdev->dev);
0273 
0274     /* restore default behavior without this module */
0275     surface_lid_enable_wakeup(&pdev->dev, false);
0276     acpi_disable_gpe(NULL, lid->gpe_number);
0277 
0278     return 0;
0279 }
0280 
0281 static struct platform_driver surface_gpe_driver = {
0282     .probe = surface_gpe_probe,
0283     .remove = surface_gpe_remove,
0284     .driver = {
0285         .name = "surface_gpe",
0286         .pm = &surface_gpe_pm,
0287         .probe_type = PROBE_PREFER_ASYNCHRONOUS,
0288     },
0289 };
0290 
0291 static struct platform_device *surface_gpe_device;
0292 
0293 static int __init surface_gpe_init(void)
0294 {
0295     const struct dmi_system_id *match;
0296     struct platform_device *pdev;
0297     struct fwnode_handle *fwnode;
0298     int status;
0299 
0300     match = dmi_first_match(dmi_lid_device_table);
0301     if (!match) {
0302         pr_info("no compatible Microsoft Surface device found, exiting\n");
0303         return -ENODEV;
0304     }
0305 
0306     status = platform_driver_register(&surface_gpe_driver);
0307     if (status)
0308         return status;
0309 
0310     fwnode = fwnode_create_software_node(match->driver_data, NULL);
0311     if (IS_ERR(fwnode)) {
0312         status = PTR_ERR(fwnode);
0313         goto err_node;
0314     }
0315 
0316     pdev = platform_device_alloc("surface_gpe", PLATFORM_DEVID_NONE);
0317     if (!pdev) {
0318         status = -ENOMEM;
0319         goto err_alloc;
0320     }
0321 
0322     pdev->dev.fwnode = fwnode;
0323 
0324     status = platform_device_add(pdev);
0325     if (status)
0326         goto err_add;
0327 
0328     surface_gpe_device = pdev;
0329     return 0;
0330 
0331 err_add:
0332     platform_device_put(pdev);
0333 err_alloc:
0334     fwnode_remove_software_node(fwnode);
0335 err_node:
0336     platform_driver_unregister(&surface_gpe_driver);
0337     return status;
0338 }
0339 module_init(surface_gpe_init);
0340 
0341 static void __exit surface_gpe_exit(void)
0342 {
0343     struct fwnode_handle *fwnode = surface_gpe_device->dev.fwnode;
0344 
0345     platform_device_unregister(surface_gpe_device);
0346     platform_driver_unregister(&surface_gpe_driver);
0347     fwnode_remove_software_node(fwnode);
0348 }
0349 module_exit(surface_gpe_exit);
0350 
0351 MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>");
0352 MODULE_DESCRIPTION("Surface GPE/Lid Driver");
0353 MODULE_LICENSE("GPL");
0354 MODULE_ALIAS("dmi:*:svnMicrosoftCorporation:pnSurface*:*");