18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Supports for the button array on SoC tablets originally running 48c2ecf20Sopenharmony_ci * Windows 8. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * (C) Copyright 2014 Intel Corporation 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/input.h> 118c2ecf20Sopenharmony_ci#include <linux/init.h> 128c2ecf20Sopenharmony_ci#include <linux/irq.h> 138c2ecf20Sopenharmony_ci#include <linux/kernel.h> 148c2ecf20Sopenharmony_ci#include <linux/acpi.h> 158c2ecf20Sopenharmony_ci#include <linux/dmi.h> 168c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 178c2ecf20Sopenharmony_ci#include <linux/gpio_keys.h> 188c2ecf20Sopenharmony_ci#include <linux/gpio.h> 198c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic bool use_low_level_irq; 228c2ecf20Sopenharmony_cimodule_param(use_low_level_irq, bool, 0444); 238c2ecf20Sopenharmony_ciMODULE_PARM_DESC(use_low_level_irq, "Use low-level triggered IRQ instead of edge triggered"); 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistruct soc_button_info { 268c2ecf20Sopenharmony_ci const char *name; 278c2ecf20Sopenharmony_ci int acpi_index; 288c2ecf20Sopenharmony_ci unsigned int event_type; 298c2ecf20Sopenharmony_ci unsigned int event_code; 308c2ecf20Sopenharmony_ci bool autorepeat; 318c2ecf20Sopenharmony_ci bool wakeup; 328c2ecf20Sopenharmony_ci bool active_low; 338c2ecf20Sopenharmony_ci}; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistruct soc_device_data { 368c2ecf20Sopenharmony_ci const struct soc_button_info *button_info; 378c2ecf20Sopenharmony_ci int (*check)(struct device *dev); 388c2ecf20Sopenharmony_ci}; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci/* 418c2ecf20Sopenharmony_ci * Some of the buttons like volume up/down are auto repeat, while others 428c2ecf20Sopenharmony_ci * are not. To support both, we register two platform devices, and put 438c2ecf20Sopenharmony_ci * buttons into them based on whether the key should be auto repeat. 448c2ecf20Sopenharmony_ci */ 458c2ecf20Sopenharmony_ci#define BUTTON_TYPES 2 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistruct soc_button_data { 488c2ecf20Sopenharmony_ci struct platform_device *children[BUTTON_TYPES]; 498c2ecf20Sopenharmony_ci}; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* 528c2ecf20Sopenharmony_ci * Some 2-in-1s which use the soc_button_array driver have this ugly issue in 538c2ecf20Sopenharmony_ci * their DSDT where the _LID method modifies the irq-type settings of the GPIOs 548c2ecf20Sopenharmony_ci * used for the power and home buttons. The intend of this AML code is to 558c2ecf20Sopenharmony_ci * disable these buttons when the lid is closed. 568c2ecf20Sopenharmony_ci * The AML does this by directly poking the GPIO controllers registers. This is 578c2ecf20Sopenharmony_ci * problematic because when re-enabling the irq, which happens whenever _LID 588c2ecf20Sopenharmony_ci * gets called with the lid open (e.g. on boot and on resume), it sets the 598c2ecf20Sopenharmony_ci * irq-type to IRQ_TYPE_LEVEL_LOW. Where as the gpio-keys driver programs the 608c2ecf20Sopenharmony_ci * type to, and expects it to be, IRQ_TYPE_EDGE_BOTH. 618c2ecf20Sopenharmony_ci * To work around this we don't set gpio_keys_button.gpio on these 2-in-1s, 628c2ecf20Sopenharmony_ci * instead we get the irq for the GPIO ourselves, configure it as 638c2ecf20Sopenharmony_ci * IRQ_TYPE_LEVEL_LOW (to match how the _LID AML code configures it) and pass 648c2ecf20Sopenharmony_ci * the irq in gpio_keys_button.irq. Below is a list of affected devices. 658c2ecf20Sopenharmony_ci */ 668c2ecf20Sopenharmony_cistatic const struct dmi_system_id dmi_use_low_level_irq[] = { 678c2ecf20Sopenharmony_ci { 688c2ecf20Sopenharmony_ci /* 698c2ecf20Sopenharmony_ci * Acer Switch 10 SW5-012. _LID method messes with home- and 708c2ecf20Sopenharmony_ci * power-button GPIO IRQ settings. When (re-)enabling the irq 718c2ecf20Sopenharmony_ci * it ors in its own flags without clearing the previous set 728c2ecf20Sopenharmony_ci * ones, leading to an irq-type of IRQ_TYPE_LEVEL_LOW | 738c2ecf20Sopenharmony_ci * IRQ_TYPE_LEVEL_HIGH causing a continuous interrupt storm. 748c2ecf20Sopenharmony_ci */ 758c2ecf20Sopenharmony_ci .matches = { 768c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Acer"), 778c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "Aspire SW5-012"), 788c2ecf20Sopenharmony_ci }, 798c2ecf20Sopenharmony_ci }, 808c2ecf20Sopenharmony_ci { 818c2ecf20Sopenharmony_ci /* Acer Switch V 10 SW5-017, same issue as Acer Switch 10 SW5-012. */ 828c2ecf20Sopenharmony_ci .matches = { 838c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Acer"), 848c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "SW5-017"), 858c2ecf20Sopenharmony_ci }, 868c2ecf20Sopenharmony_ci }, 878c2ecf20Sopenharmony_ci { 888c2ecf20Sopenharmony_ci /* 898c2ecf20Sopenharmony_ci * Acer One S1003. _LID method messes with power-button GPIO 908c2ecf20Sopenharmony_ci * IRQ settings, leading to a non working power-button. 918c2ecf20Sopenharmony_ci */ 928c2ecf20Sopenharmony_ci .matches = { 938c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Acer"), 948c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "One S1003"), 958c2ecf20Sopenharmony_ci }, 968c2ecf20Sopenharmony_ci }, 978c2ecf20Sopenharmony_ci { 988c2ecf20Sopenharmony_ci /* 998c2ecf20Sopenharmony_ci * Lenovo Yoga Tab2 1051F/1051L, something messes with the home-button 1008c2ecf20Sopenharmony_ci * IRQ settings, leading to a non working home-button. 1018c2ecf20Sopenharmony_ci */ 1028c2ecf20Sopenharmony_ci .matches = { 1038c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), 1048c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "60073"), 1058c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_VERSION, "1051"), 1068c2ecf20Sopenharmony_ci }, 1078c2ecf20Sopenharmony_ci }, 1088c2ecf20Sopenharmony_ci {} /* Terminating entry */ 1098c2ecf20Sopenharmony_ci}; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci/* 1128c2ecf20Sopenharmony_ci * Some devices have a wrong entry which points to a GPIO which is 1138c2ecf20Sopenharmony_ci * required in another driver, so this driver must not claim it. 1148c2ecf20Sopenharmony_ci */ 1158c2ecf20Sopenharmony_cistatic const struct dmi_system_id dmi_invalid_acpi_index[] = { 1168c2ecf20Sopenharmony_ci { 1178c2ecf20Sopenharmony_ci /* 1188c2ecf20Sopenharmony_ci * Lenovo Yoga Book X90F / X90L, the PNP0C40 home button entry 1198c2ecf20Sopenharmony_ci * points to a GPIO which is not a home button and which is 1208c2ecf20Sopenharmony_ci * required by the lenovo-yogabook driver. 1218c2ecf20Sopenharmony_ci */ 1228c2ecf20Sopenharmony_ci .matches = { 1238c2ecf20Sopenharmony_ci DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), 1248c2ecf20Sopenharmony_ci DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "CHERRYVIEW D1 PLATFORM"), 1258c2ecf20Sopenharmony_ci DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "YETI-11"), 1268c2ecf20Sopenharmony_ci }, 1278c2ecf20Sopenharmony_ci .driver_data = (void *)1l, 1288c2ecf20Sopenharmony_ci }, 1298c2ecf20Sopenharmony_ci {} /* Terminating entry */ 1308c2ecf20Sopenharmony_ci}; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci/* 1338c2ecf20Sopenharmony_ci * Get the Nth GPIO number from the ACPI object. 1348c2ecf20Sopenharmony_ci */ 1358c2ecf20Sopenharmony_cistatic int soc_button_lookup_gpio(struct device *dev, int acpi_index, 1368c2ecf20Sopenharmony_ci int *gpio_ret, int *irq_ret) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci struct gpio_desc *desc; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci desc = gpiod_get_index(dev, NULL, acpi_index, GPIOD_ASIS); 1418c2ecf20Sopenharmony_ci if (IS_ERR(desc)) 1428c2ecf20Sopenharmony_ci return PTR_ERR(desc); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci *gpio_ret = desc_to_gpio(desc); 1458c2ecf20Sopenharmony_ci *irq_ret = gpiod_to_irq(desc); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci gpiod_put(desc); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci return 0; 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic struct platform_device * 1538c2ecf20Sopenharmony_cisoc_button_device_create(struct platform_device *pdev, 1548c2ecf20Sopenharmony_ci const struct soc_button_info *button_info, 1558c2ecf20Sopenharmony_ci bool autorepeat) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci const struct soc_button_info *info; 1588c2ecf20Sopenharmony_ci struct platform_device *pd; 1598c2ecf20Sopenharmony_ci struct gpio_keys_button *gpio_keys; 1608c2ecf20Sopenharmony_ci struct gpio_keys_platform_data *gpio_keys_pdata; 1618c2ecf20Sopenharmony_ci const struct dmi_system_id *dmi_id; 1628c2ecf20Sopenharmony_ci int invalid_acpi_index = -1; 1638c2ecf20Sopenharmony_ci int error, gpio, irq; 1648c2ecf20Sopenharmony_ci int n_buttons = 0; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci for (info = button_info; info->name; info++) 1678c2ecf20Sopenharmony_ci if (info->autorepeat == autorepeat) 1688c2ecf20Sopenharmony_ci n_buttons++; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci gpio_keys_pdata = devm_kzalloc(&pdev->dev, 1718c2ecf20Sopenharmony_ci sizeof(*gpio_keys_pdata) + 1728c2ecf20Sopenharmony_ci sizeof(*gpio_keys) * n_buttons, 1738c2ecf20Sopenharmony_ci GFP_KERNEL); 1748c2ecf20Sopenharmony_ci if (!gpio_keys_pdata) 1758c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci gpio_keys = (void *)(gpio_keys_pdata + 1); 1788c2ecf20Sopenharmony_ci n_buttons = 0; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci dmi_id = dmi_first_match(dmi_invalid_acpi_index); 1818c2ecf20Sopenharmony_ci if (dmi_id) 1828c2ecf20Sopenharmony_ci invalid_acpi_index = (long)dmi_id->driver_data; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci for (info = button_info; info->name; info++) { 1858c2ecf20Sopenharmony_ci if (info->autorepeat != autorepeat) 1868c2ecf20Sopenharmony_ci continue; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci if (info->acpi_index == invalid_acpi_index) 1898c2ecf20Sopenharmony_ci continue; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci error = soc_button_lookup_gpio(&pdev->dev, info->acpi_index, &gpio, &irq); 1928c2ecf20Sopenharmony_ci if (error || irq < 0) { 1938c2ecf20Sopenharmony_ci /* 1948c2ecf20Sopenharmony_ci * Skip GPIO if not present. Note we deliberately 1958c2ecf20Sopenharmony_ci * ignore -EPROBE_DEFER errors here. On some devices 1968c2ecf20Sopenharmony_ci * Intel is using so called virtual GPIOs which are not 1978c2ecf20Sopenharmony_ci * GPIOs at all but some way for AML code to check some 1988c2ecf20Sopenharmony_ci * random status bits without need a custom opregion. 1998c2ecf20Sopenharmony_ci * In some cases the resources table we parse points to 2008c2ecf20Sopenharmony_ci * such a virtual GPIO, since these are not real GPIOs 2018c2ecf20Sopenharmony_ci * we do not have a driver for these so they will never 2028c2ecf20Sopenharmony_ci * show up, therefore we ignore -EPROBE_DEFER. 2038c2ecf20Sopenharmony_ci */ 2048c2ecf20Sopenharmony_ci continue; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci /* See dmi_use_low_level_irq[] comment */ 2088c2ecf20Sopenharmony_ci if (!autorepeat && (use_low_level_irq || 2098c2ecf20Sopenharmony_ci dmi_check_system(dmi_use_low_level_irq))) { 2108c2ecf20Sopenharmony_ci irq_set_irq_type(irq, IRQ_TYPE_LEVEL_LOW); 2118c2ecf20Sopenharmony_ci gpio_keys[n_buttons].irq = irq; 2128c2ecf20Sopenharmony_ci gpio_keys[n_buttons].gpio = -ENOENT; 2138c2ecf20Sopenharmony_ci } else { 2148c2ecf20Sopenharmony_ci gpio_keys[n_buttons].gpio = gpio; 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci gpio_keys[n_buttons].type = info->event_type; 2188c2ecf20Sopenharmony_ci gpio_keys[n_buttons].code = info->event_code; 2198c2ecf20Sopenharmony_ci gpio_keys[n_buttons].active_low = info->active_low; 2208c2ecf20Sopenharmony_ci gpio_keys[n_buttons].desc = info->name; 2218c2ecf20Sopenharmony_ci gpio_keys[n_buttons].wakeup = info->wakeup; 2228c2ecf20Sopenharmony_ci /* These devices often use cheap buttons, use 50 ms debounce */ 2238c2ecf20Sopenharmony_ci gpio_keys[n_buttons].debounce_interval = 50; 2248c2ecf20Sopenharmony_ci n_buttons++; 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci if (n_buttons == 0) { 2288c2ecf20Sopenharmony_ci error = -ENODEV; 2298c2ecf20Sopenharmony_ci goto err_free_mem; 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci gpio_keys_pdata->buttons = gpio_keys; 2338c2ecf20Sopenharmony_ci gpio_keys_pdata->nbuttons = n_buttons; 2348c2ecf20Sopenharmony_ci gpio_keys_pdata->rep = autorepeat; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci pd = platform_device_register_resndata(&pdev->dev, "gpio-keys", 2378c2ecf20Sopenharmony_ci PLATFORM_DEVID_AUTO, NULL, 0, 2388c2ecf20Sopenharmony_ci gpio_keys_pdata, 2398c2ecf20Sopenharmony_ci sizeof(*gpio_keys_pdata)); 2408c2ecf20Sopenharmony_ci error = PTR_ERR_OR_ZERO(pd); 2418c2ecf20Sopenharmony_ci if (error) { 2428c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 2438c2ecf20Sopenharmony_ci "failed registering gpio-keys: %d\n", error); 2448c2ecf20Sopenharmony_ci goto err_free_mem; 2458c2ecf20Sopenharmony_ci } 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci return pd; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cierr_free_mem: 2508c2ecf20Sopenharmony_ci devm_kfree(&pdev->dev, gpio_keys_pdata); 2518c2ecf20Sopenharmony_ci return ERR_PTR(error); 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cistatic int soc_button_get_acpi_object_int(const union acpi_object *obj) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci if (obj->type != ACPI_TYPE_INTEGER) 2578c2ecf20Sopenharmony_ci return -1; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci return obj->integer.value; 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci/* Parse a single ACPI0011 _DSD button descriptor */ 2638c2ecf20Sopenharmony_cistatic int soc_button_parse_btn_desc(struct device *dev, 2648c2ecf20Sopenharmony_ci const union acpi_object *desc, 2658c2ecf20Sopenharmony_ci int collection_uid, 2668c2ecf20Sopenharmony_ci struct soc_button_info *info) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci int upage, usage; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci if (desc->type != ACPI_TYPE_PACKAGE || 2718c2ecf20Sopenharmony_ci desc->package.count != 5 || 2728c2ecf20Sopenharmony_ci /* First byte should be 1 (control) */ 2738c2ecf20Sopenharmony_ci soc_button_get_acpi_object_int(&desc->package.elements[0]) != 1 || 2748c2ecf20Sopenharmony_ci /* Third byte should be collection uid */ 2758c2ecf20Sopenharmony_ci soc_button_get_acpi_object_int(&desc->package.elements[2]) != 2768c2ecf20Sopenharmony_ci collection_uid) { 2778c2ecf20Sopenharmony_ci dev_err(dev, "Invalid ACPI Button Descriptor\n"); 2788c2ecf20Sopenharmony_ci return -ENODEV; 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci info->event_type = EV_KEY; 2828c2ecf20Sopenharmony_ci info->active_low = true; 2838c2ecf20Sopenharmony_ci info->acpi_index = 2848c2ecf20Sopenharmony_ci soc_button_get_acpi_object_int(&desc->package.elements[1]); 2858c2ecf20Sopenharmony_ci upage = soc_button_get_acpi_object_int(&desc->package.elements[3]); 2868c2ecf20Sopenharmony_ci usage = soc_button_get_acpi_object_int(&desc->package.elements[4]); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci /* 2898c2ecf20Sopenharmony_ci * The UUID: fa6bd625-9ce8-470d-a2c7-b3ca36c4282e descriptors use HID 2908c2ecf20Sopenharmony_ci * usage page and usage codes, but otherwise the device is not HID 2918c2ecf20Sopenharmony_ci * compliant: it uses one irq per button instead of generating HID 2928c2ecf20Sopenharmony_ci * input reports and some buttons should generate wakeups where as 2938c2ecf20Sopenharmony_ci * others should not, so we cannot use the HID subsystem. 2948c2ecf20Sopenharmony_ci * 2958c2ecf20Sopenharmony_ci * Luckily all devices only use a few usage page + usage combinations, 2968c2ecf20Sopenharmony_ci * so we can simply check for the known combinations here. 2978c2ecf20Sopenharmony_ci */ 2988c2ecf20Sopenharmony_ci if (upage == 0x01 && usage == 0x81) { 2998c2ecf20Sopenharmony_ci info->name = "power"; 3008c2ecf20Sopenharmony_ci info->event_code = KEY_POWER; 3018c2ecf20Sopenharmony_ci info->wakeup = true; 3028c2ecf20Sopenharmony_ci } else if (upage == 0x01 && usage == 0xc6) { 3038c2ecf20Sopenharmony_ci info->name = "airplane mode switch"; 3048c2ecf20Sopenharmony_ci info->event_type = EV_SW; 3058c2ecf20Sopenharmony_ci info->event_code = SW_RFKILL_ALL; 3068c2ecf20Sopenharmony_ci info->active_low = false; 3078c2ecf20Sopenharmony_ci } else if (upage == 0x01 && usage == 0xca) { 3088c2ecf20Sopenharmony_ci info->name = "rotation lock switch"; 3098c2ecf20Sopenharmony_ci info->event_type = EV_SW; 3108c2ecf20Sopenharmony_ci info->event_code = SW_ROTATE_LOCK; 3118c2ecf20Sopenharmony_ci } else if (upage == 0x07 && usage == 0xe3) { 3128c2ecf20Sopenharmony_ci info->name = "home"; 3138c2ecf20Sopenharmony_ci info->event_code = KEY_LEFTMETA; 3148c2ecf20Sopenharmony_ci info->wakeup = true; 3158c2ecf20Sopenharmony_ci } else if (upage == 0x0c && usage == 0xe9) { 3168c2ecf20Sopenharmony_ci info->name = "volume_up"; 3178c2ecf20Sopenharmony_ci info->event_code = KEY_VOLUMEUP; 3188c2ecf20Sopenharmony_ci info->autorepeat = true; 3198c2ecf20Sopenharmony_ci } else if (upage == 0x0c && usage == 0xea) { 3208c2ecf20Sopenharmony_ci info->name = "volume_down"; 3218c2ecf20Sopenharmony_ci info->event_code = KEY_VOLUMEDOWN; 3228c2ecf20Sopenharmony_ci info->autorepeat = true; 3238c2ecf20Sopenharmony_ci } else { 3248c2ecf20Sopenharmony_ci dev_warn(dev, "Unknown button index %d upage %02x usage %02x, ignoring\n", 3258c2ecf20Sopenharmony_ci info->acpi_index, upage, usage); 3268c2ecf20Sopenharmony_ci info->name = "unknown"; 3278c2ecf20Sopenharmony_ci info->event_code = KEY_RESERVED; 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci return 0; 3318c2ecf20Sopenharmony_ci} 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci/* ACPI0011 _DSD btns descriptors UUID: fa6bd625-9ce8-470d-a2c7-b3ca36c4282e */ 3348c2ecf20Sopenharmony_cistatic const u8 btns_desc_uuid[16] = { 3358c2ecf20Sopenharmony_ci 0x25, 0xd6, 0x6b, 0xfa, 0xe8, 0x9c, 0x0d, 0x47, 3368c2ecf20Sopenharmony_ci 0xa2, 0xc7, 0xb3, 0xca, 0x36, 0xc4, 0x28, 0x2e 3378c2ecf20Sopenharmony_ci}; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci/* Parse ACPI0011 _DSD button descriptors */ 3408c2ecf20Sopenharmony_cistatic struct soc_button_info *soc_button_get_button_info(struct device *dev) 3418c2ecf20Sopenharmony_ci{ 3428c2ecf20Sopenharmony_ci struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER }; 3438c2ecf20Sopenharmony_ci const union acpi_object *desc, *el0, *uuid, *btns_desc = NULL; 3448c2ecf20Sopenharmony_ci struct soc_button_info *button_info; 3458c2ecf20Sopenharmony_ci acpi_status status; 3468c2ecf20Sopenharmony_ci int i, btn, collection_uid = -1; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci status = acpi_evaluate_object_typed(ACPI_HANDLE(dev), "_DSD", NULL, 3498c2ecf20Sopenharmony_ci &buf, ACPI_TYPE_PACKAGE); 3508c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) { 3518c2ecf20Sopenharmony_ci dev_err(dev, "ACPI _DSD object not found\n"); 3528c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci /* Look for the Button Descriptors UUID */ 3568c2ecf20Sopenharmony_ci desc = buf.pointer; 3578c2ecf20Sopenharmony_ci for (i = 0; (i + 1) < desc->package.count; i += 2) { 3588c2ecf20Sopenharmony_ci uuid = &desc->package.elements[i]; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci if (uuid->type != ACPI_TYPE_BUFFER || 3618c2ecf20Sopenharmony_ci uuid->buffer.length != 16 || 3628c2ecf20Sopenharmony_ci desc->package.elements[i + 1].type != ACPI_TYPE_PACKAGE) { 3638c2ecf20Sopenharmony_ci break; 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci if (memcmp(uuid->buffer.pointer, btns_desc_uuid, 16) == 0) { 3678c2ecf20Sopenharmony_ci btns_desc = &desc->package.elements[i + 1]; 3688c2ecf20Sopenharmony_ci break; 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci if (!btns_desc) { 3738c2ecf20Sopenharmony_ci dev_err(dev, "ACPI Button Descriptors not found\n"); 3748c2ecf20Sopenharmony_ci button_info = ERR_PTR(-ENODEV); 3758c2ecf20Sopenharmony_ci goto out; 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci /* The first package describes the collection */ 3798c2ecf20Sopenharmony_ci el0 = &btns_desc->package.elements[0]; 3808c2ecf20Sopenharmony_ci if (el0->type == ACPI_TYPE_PACKAGE && 3818c2ecf20Sopenharmony_ci el0->package.count == 5 && 3828c2ecf20Sopenharmony_ci /* First byte should be 0 (collection) */ 3838c2ecf20Sopenharmony_ci soc_button_get_acpi_object_int(&el0->package.elements[0]) == 0 && 3848c2ecf20Sopenharmony_ci /* Third byte should be 0 (top level collection) */ 3858c2ecf20Sopenharmony_ci soc_button_get_acpi_object_int(&el0->package.elements[2]) == 0) { 3868c2ecf20Sopenharmony_ci collection_uid = soc_button_get_acpi_object_int( 3878c2ecf20Sopenharmony_ci &el0->package.elements[1]); 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci if (collection_uid == -1) { 3908c2ecf20Sopenharmony_ci dev_err(dev, "Invalid Button Collection Descriptor\n"); 3918c2ecf20Sopenharmony_ci button_info = ERR_PTR(-ENODEV); 3928c2ecf20Sopenharmony_ci goto out; 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci /* There are package.count - 1 buttons + 1 terminating empty entry */ 3968c2ecf20Sopenharmony_ci button_info = devm_kcalloc(dev, btns_desc->package.count, 3978c2ecf20Sopenharmony_ci sizeof(*button_info), GFP_KERNEL); 3988c2ecf20Sopenharmony_ci if (!button_info) { 3998c2ecf20Sopenharmony_ci button_info = ERR_PTR(-ENOMEM); 4008c2ecf20Sopenharmony_ci goto out; 4018c2ecf20Sopenharmony_ci } 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci /* Parse the button descriptors */ 4048c2ecf20Sopenharmony_ci for (i = 1, btn = 0; i < btns_desc->package.count; i++, btn++) { 4058c2ecf20Sopenharmony_ci if (soc_button_parse_btn_desc(dev, 4068c2ecf20Sopenharmony_ci &btns_desc->package.elements[i], 4078c2ecf20Sopenharmony_ci collection_uid, 4088c2ecf20Sopenharmony_ci &button_info[btn])) { 4098c2ecf20Sopenharmony_ci button_info = ERR_PTR(-ENODEV); 4108c2ecf20Sopenharmony_ci goto out; 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ciout: 4158c2ecf20Sopenharmony_ci kfree(buf.pointer); 4168c2ecf20Sopenharmony_ci return button_info; 4178c2ecf20Sopenharmony_ci} 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_cistatic int soc_button_remove(struct platform_device *pdev) 4208c2ecf20Sopenharmony_ci{ 4218c2ecf20Sopenharmony_ci struct soc_button_data *priv = platform_get_drvdata(pdev); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci int i; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci for (i = 0; i < BUTTON_TYPES; i++) 4268c2ecf20Sopenharmony_ci if (priv->children[i]) 4278c2ecf20Sopenharmony_ci platform_device_unregister(priv->children[i]); 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci return 0; 4308c2ecf20Sopenharmony_ci} 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_cistatic int soc_button_probe(struct platform_device *pdev) 4338c2ecf20Sopenharmony_ci{ 4348c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 4358c2ecf20Sopenharmony_ci const struct soc_device_data *device_data; 4368c2ecf20Sopenharmony_ci const struct soc_button_info *button_info; 4378c2ecf20Sopenharmony_ci struct soc_button_data *priv; 4388c2ecf20Sopenharmony_ci struct platform_device *pd; 4398c2ecf20Sopenharmony_ci int i; 4408c2ecf20Sopenharmony_ci int error; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci device_data = acpi_device_get_match_data(dev); 4438c2ecf20Sopenharmony_ci if (device_data && device_data->check) { 4448c2ecf20Sopenharmony_ci error = device_data->check(dev); 4458c2ecf20Sopenharmony_ci if (error) 4468c2ecf20Sopenharmony_ci return error; 4478c2ecf20Sopenharmony_ci } 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci if (device_data && device_data->button_info) { 4508c2ecf20Sopenharmony_ci button_info = device_data->button_info; 4518c2ecf20Sopenharmony_ci } else { 4528c2ecf20Sopenharmony_ci button_info = soc_button_get_button_info(dev); 4538c2ecf20Sopenharmony_ci if (IS_ERR(button_info)) 4548c2ecf20Sopenharmony_ci return PTR_ERR(button_info); 4558c2ecf20Sopenharmony_ci } 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci error = gpiod_count(dev, NULL); 4588c2ecf20Sopenharmony_ci if (error < 0) { 4598c2ecf20Sopenharmony_ci dev_dbg(dev, "no GPIO attached, ignoring...\n"); 4608c2ecf20Sopenharmony_ci return -ENODEV; 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 4648c2ecf20Sopenharmony_ci if (!priv) 4658c2ecf20Sopenharmony_ci return -ENOMEM; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, priv); 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci for (i = 0; i < BUTTON_TYPES; i++) { 4708c2ecf20Sopenharmony_ci pd = soc_button_device_create(pdev, button_info, i == 0); 4718c2ecf20Sopenharmony_ci if (IS_ERR(pd)) { 4728c2ecf20Sopenharmony_ci error = PTR_ERR(pd); 4738c2ecf20Sopenharmony_ci if (error != -ENODEV) { 4748c2ecf20Sopenharmony_ci soc_button_remove(pdev); 4758c2ecf20Sopenharmony_ci return error; 4768c2ecf20Sopenharmony_ci } 4778c2ecf20Sopenharmony_ci continue; 4788c2ecf20Sopenharmony_ci } 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci priv->children[i] = pd; 4818c2ecf20Sopenharmony_ci } 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci if (!priv->children[0] && !priv->children[1]) 4848c2ecf20Sopenharmony_ci return -ENODEV; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci if (!device_data || !device_data->button_info) 4878c2ecf20Sopenharmony_ci devm_kfree(dev, button_info); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci return 0; 4908c2ecf20Sopenharmony_ci} 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci/* 4938c2ecf20Sopenharmony_ci * Definition of buttons on the tablet. The ACPI index of each button 4948c2ecf20Sopenharmony_ci * is defined in section 2.8.7.2 of "Windows ACPI Design Guide for SoC 4958c2ecf20Sopenharmony_ci * Platforms" 4968c2ecf20Sopenharmony_ci */ 4978c2ecf20Sopenharmony_cistatic const struct soc_button_info soc_button_PNP0C40[] = { 4988c2ecf20Sopenharmony_ci { "power", 0, EV_KEY, KEY_POWER, false, true, true }, 4998c2ecf20Sopenharmony_ci { "home", 1, EV_KEY, KEY_LEFTMETA, false, true, true }, 5008c2ecf20Sopenharmony_ci { "volume_up", 2, EV_KEY, KEY_VOLUMEUP, true, false, true }, 5018c2ecf20Sopenharmony_ci { "volume_down", 3, EV_KEY, KEY_VOLUMEDOWN, true, false, true }, 5028c2ecf20Sopenharmony_ci { "rotation_lock", 4, EV_KEY, KEY_ROTATE_LOCK_TOGGLE, false, false, true }, 5038c2ecf20Sopenharmony_ci { } 5048c2ecf20Sopenharmony_ci}; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_cistatic const struct soc_device_data soc_device_PNP0C40 = { 5078c2ecf20Sopenharmony_ci .button_info = soc_button_PNP0C40, 5088c2ecf20Sopenharmony_ci}; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_cistatic const struct soc_button_info soc_button_INT33D3[] = { 5118c2ecf20Sopenharmony_ci { "tablet_mode", 0, EV_SW, SW_TABLET_MODE, false, false, false }, 5128c2ecf20Sopenharmony_ci { } 5138c2ecf20Sopenharmony_ci}; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_cistatic const struct soc_device_data soc_device_INT33D3 = { 5168c2ecf20Sopenharmony_ci .button_info = soc_button_INT33D3, 5178c2ecf20Sopenharmony_ci}; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci/* 5208c2ecf20Sopenharmony_ci * Special device check for Surface Book 2 and Surface Pro (2017). 5218c2ecf20Sopenharmony_ci * Both, the Surface Pro 4 (surfacepro3_button.c) and the above mentioned 5228c2ecf20Sopenharmony_ci * devices use MSHW0040 for power and volume buttons, however the way they 5238c2ecf20Sopenharmony_ci * have to be addressed differs. Make sure that we only load this drivers 5248c2ecf20Sopenharmony_ci * for the correct devices by checking the OEM Platform Revision provided by 5258c2ecf20Sopenharmony_ci * the _DSM method. 5268c2ecf20Sopenharmony_ci */ 5278c2ecf20Sopenharmony_ci#define MSHW0040_DSM_REVISION 0x01 5288c2ecf20Sopenharmony_ci#define MSHW0040_DSM_GET_OMPR 0x02 // get OEM Platform Revision 5298c2ecf20Sopenharmony_cistatic const guid_t MSHW0040_DSM_UUID = 5308c2ecf20Sopenharmony_ci GUID_INIT(0x6fd05c69, 0xcde3, 0x49f4, 0x95, 0xed, 0xab, 0x16, 0x65, 5318c2ecf20Sopenharmony_ci 0x49, 0x80, 0x35); 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_cistatic int soc_device_check_MSHW0040(struct device *dev) 5348c2ecf20Sopenharmony_ci{ 5358c2ecf20Sopenharmony_ci acpi_handle handle = ACPI_HANDLE(dev); 5368c2ecf20Sopenharmony_ci union acpi_object *result; 5378c2ecf20Sopenharmony_ci u64 oem_platform_rev = 0; // valid revisions are nonzero 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci // get OEM platform revision 5408c2ecf20Sopenharmony_ci result = acpi_evaluate_dsm_typed(handle, &MSHW0040_DSM_UUID, 5418c2ecf20Sopenharmony_ci MSHW0040_DSM_REVISION, 5428c2ecf20Sopenharmony_ci MSHW0040_DSM_GET_OMPR, NULL, 5438c2ecf20Sopenharmony_ci ACPI_TYPE_INTEGER); 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci if (result) { 5468c2ecf20Sopenharmony_ci oem_platform_rev = result->integer.value; 5478c2ecf20Sopenharmony_ci ACPI_FREE(result); 5488c2ecf20Sopenharmony_ci } 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci /* 5518c2ecf20Sopenharmony_ci * If the revision is zero here, the _DSM evaluation has failed. This 5528c2ecf20Sopenharmony_ci * indicates that we have a Pro 4 or Book 1 and this driver should not 5538c2ecf20Sopenharmony_ci * be used. 5548c2ecf20Sopenharmony_ci */ 5558c2ecf20Sopenharmony_ci if (oem_platform_rev == 0) 5568c2ecf20Sopenharmony_ci return -ENODEV; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci dev_dbg(dev, "OEM Platform Revision %llu\n", oem_platform_rev); 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci return 0; 5618c2ecf20Sopenharmony_ci} 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci/* 5648c2ecf20Sopenharmony_ci * Button infos for Microsoft Surface Book 2 and Surface Pro (2017). 5658c2ecf20Sopenharmony_ci * Obtained from DSDT/testing. 5668c2ecf20Sopenharmony_ci */ 5678c2ecf20Sopenharmony_cistatic const struct soc_button_info soc_button_MSHW0040[] = { 5688c2ecf20Sopenharmony_ci { "power", 0, EV_KEY, KEY_POWER, false, true, true }, 5698c2ecf20Sopenharmony_ci { "volume_up", 2, EV_KEY, KEY_VOLUMEUP, true, false, true }, 5708c2ecf20Sopenharmony_ci { "volume_down", 4, EV_KEY, KEY_VOLUMEDOWN, true, false, true }, 5718c2ecf20Sopenharmony_ci { } 5728c2ecf20Sopenharmony_ci}; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_cistatic const struct soc_device_data soc_device_MSHW0040 = { 5758c2ecf20Sopenharmony_ci .button_info = soc_button_MSHW0040, 5768c2ecf20Sopenharmony_ci .check = soc_device_check_MSHW0040, 5778c2ecf20Sopenharmony_ci}; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_cistatic const struct acpi_device_id soc_button_acpi_match[] = { 5808c2ecf20Sopenharmony_ci { "PNP0C40", (unsigned long)&soc_device_PNP0C40 }, 5818c2ecf20Sopenharmony_ci { "INT33D3", (unsigned long)&soc_device_INT33D3 }, 5828c2ecf20Sopenharmony_ci { "ID9001", (unsigned long)&soc_device_INT33D3 }, 5838c2ecf20Sopenharmony_ci { "ACPI0011", 0 }, 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci /* Microsoft Surface Devices (5th and 6th generation) */ 5868c2ecf20Sopenharmony_ci { "MSHW0040", (unsigned long)&soc_device_MSHW0040 }, 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci { } 5898c2ecf20Sopenharmony_ci}; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, soc_button_acpi_match); 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_cistatic struct platform_driver soc_button_driver = { 5948c2ecf20Sopenharmony_ci .probe = soc_button_probe, 5958c2ecf20Sopenharmony_ci .remove = soc_button_remove, 5968c2ecf20Sopenharmony_ci .driver = { 5978c2ecf20Sopenharmony_ci .name = KBUILD_MODNAME, 5988c2ecf20Sopenharmony_ci .acpi_match_table = ACPI_PTR(soc_button_acpi_match), 5998c2ecf20Sopenharmony_ci }, 6008c2ecf20Sopenharmony_ci}; 6018c2ecf20Sopenharmony_cimodule_platform_driver(soc_button_driver); 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 604