162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Supports for the button array on SoC tablets originally running 462306a36Sopenharmony_ci * Windows 8. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * (C) Copyright 2014 Intel Corporation 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/input.h> 1162306a36Sopenharmony_ci#include <linux/init.h> 1262306a36Sopenharmony_ci#include <linux/irq.h> 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/acpi.h> 1562306a36Sopenharmony_ci#include <linux/dmi.h> 1662306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 1762306a36Sopenharmony_ci#include <linux/gpio_keys.h> 1862306a36Sopenharmony_ci#include <linux/gpio.h> 1962306a36Sopenharmony_ci#include <linux/platform_device.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic bool use_low_level_irq; 2262306a36Sopenharmony_cimodule_param(use_low_level_irq, bool, 0444); 2362306a36Sopenharmony_ciMODULE_PARM_DESC(use_low_level_irq, "Use low-level triggered IRQ instead of edge triggered"); 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistruct soc_button_info { 2662306a36Sopenharmony_ci const char *name; 2762306a36Sopenharmony_ci int acpi_index; 2862306a36Sopenharmony_ci unsigned int event_type; 2962306a36Sopenharmony_ci unsigned int event_code; 3062306a36Sopenharmony_ci bool autorepeat; 3162306a36Sopenharmony_ci bool wakeup; 3262306a36Sopenharmony_ci bool active_low; 3362306a36Sopenharmony_ci}; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistruct soc_device_data { 3662306a36Sopenharmony_ci const struct soc_button_info *button_info; 3762306a36Sopenharmony_ci int (*check)(struct device *dev); 3862306a36Sopenharmony_ci}; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci/* 4162306a36Sopenharmony_ci * Some of the buttons like volume up/down are auto repeat, while others 4262306a36Sopenharmony_ci * are not. To support both, we register two platform devices, and put 4362306a36Sopenharmony_ci * buttons into them based on whether the key should be auto repeat. 4462306a36Sopenharmony_ci */ 4562306a36Sopenharmony_ci#define BUTTON_TYPES 2 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistruct soc_button_data { 4862306a36Sopenharmony_ci struct platform_device *children[BUTTON_TYPES]; 4962306a36Sopenharmony_ci}; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci/* 5262306a36Sopenharmony_ci * Some 2-in-1s which use the soc_button_array driver have this ugly issue in 5362306a36Sopenharmony_ci * their DSDT where the _LID method modifies the irq-type settings of the GPIOs 5462306a36Sopenharmony_ci * used for the power and home buttons. The intend of this AML code is to 5562306a36Sopenharmony_ci * disable these buttons when the lid is closed. 5662306a36Sopenharmony_ci * The AML does this by directly poking the GPIO controllers registers. This is 5762306a36Sopenharmony_ci * problematic because when re-enabling the irq, which happens whenever _LID 5862306a36Sopenharmony_ci * gets called with the lid open (e.g. on boot and on resume), it sets the 5962306a36Sopenharmony_ci * irq-type to IRQ_TYPE_LEVEL_LOW. Where as the gpio-keys driver programs the 6062306a36Sopenharmony_ci * type to, and expects it to be, IRQ_TYPE_EDGE_BOTH. 6162306a36Sopenharmony_ci * To work around this we don't set gpio_keys_button.gpio on these 2-in-1s, 6262306a36Sopenharmony_ci * instead we get the irq for the GPIO ourselves, configure it as 6362306a36Sopenharmony_ci * IRQ_TYPE_LEVEL_LOW (to match how the _LID AML code configures it) and pass 6462306a36Sopenharmony_ci * the irq in gpio_keys_button.irq. Below is a list of affected devices. 6562306a36Sopenharmony_ci */ 6662306a36Sopenharmony_cistatic const struct dmi_system_id dmi_use_low_level_irq[] = { 6762306a36Sopenharmony_ci { 6862306a36Sopenharmony_ci /* 6962306a36Sopenharmony_ci * Acer Switch 10 SW5-012. _LID method messes with home- and 7062306a36Sopenharmony_ci * power-button GPIO IRQ settings. When (re-)enabling the irq 7162306a36Sopenharmony_ci * it ors in its own flags without clearing the previous set 7262306a36Sopenharmony_ci * ones, leading to an irq-type of IRQ_TYPE_LEVEL_LOW | 7362306a36Sopenharmony_ci * IRQ_TYPE_LEVEL_HIGH causing a continuous interrupt storm. 7462306a36Sopenharmony_ci */ 7562306a36Sopenharmony_ci .matches = { 7662306a36Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Acer"), 7762306a36Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "Aspire SW5-012"), 7862306a36Sopenharmony_ci }, 7962306a36Sopenharmony_ci }, 8062306a36Sopenharmony_ci { 8162306a36Sopenharmony_ci /* Acer Switch V 10 SW5-017, same issue as Acer Switch 10 SW5-012. */ 8262306a36Sopenharmony_ci .matches = { 8362306a36Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Acer"), 8462306a36Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "SW5-017"), 8562306a36Sopenharmony_ci }, 8662306a36Sopenharmony_ci }, 8762306a36Sopenharmony_ci { 8862306a36Sopenharmony_ci /* 8962306a36Sopenharmony_ci * Acer One S1003. _LID method messes with power-button GPIO 9062306a36Sopenharmony_ci * IRQ settings, leading to a non working power-button. 9162306a36Sopenharmony_ci */ 9262306a36Sopenharmony_ci .matches = { 9362306a36Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Acer"), 9462306a36Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "One S1003"), 9562306a36Sopenharmony_ci }, 9662306a36Sopenharmony_ci }, 9762306a36Sopenharmony_ci { 9862306a36Sopenharmony_ci /* 9962306a36Sopenharmony_ci * Lenovo Yoga Tab2 1051F/1051L, something messes with the home-button 10062306a36Sopenharmony_ci * IRQ settings, leading to a non working home-button. 10162306a36Sopenharmony_ci */ 10262306a36Sopenharmony_ci .matches = { 10362306a36Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), 10462306a36Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "60073"), 10562306a36Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_VERSION, "1051"), 10662306a36Sopenharmony_ci }, 10762306a36Sopenharmony_ci }, 10862306a36Sopenharmony_ci {} /* Terminating entry */ 10962306a36Sopenharmony_ci}; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci/* 11262306a36Sopenharmony_ci * Some devices have a wrong entry which points to a GPIO which is 11362306a36Sopenharmony_ci * required in another driver, so this driver must not claim it. 11462306a36Sopenharmony_ci */ 11562306a36Sopenharmony_cistatic const struct dmi_system_id dmi_invalid_acpi_index[] = { 11662306a36Sopenharmony_ci { 11762306a36Sopenharmony_ci /* 11862306a36Sopenharmony_ci * Lenovo Yoga Book X90F / X90L, the PNP0C40 home button entry 11962306a36Sopenharmony_ci * points to a GPIO which is not a home button and which is 12062306a36Sopenharmony_ci * required by the lenovo-yogabook driver. 12162306a36Sopenharmony_ci */ 12262306a36Sopenharmony_ci .matches = { 12362306a36Sopenharmony_ci DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), 12462306a36Sopenharmony_ci DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "CHERRYVIEW D1 PLATFORM"), 12562306a36Sopenharmony_ci DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "YETI-11"), 12662306a36Sopenharmony_ci }, 12762306a36Sopenharmony_ci .driver_data = (void *)1l, 12862306a36Sopenharmony_ci }, 12962306a36Sopenharmony_ci {} /* Terminating entry */ 13062306a36Sopenharmony_ci}; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci/* 13362306a36Sopenharmony_ci * Get the Nth GPIO number from the ACPI object. 13462306a36Sopenharmony_ci */ 13562306a36Sopenharmony_cistatic int soc_button_lookup_gpio(struct device *dev, int acpi_index, 13662306a36Sopenharmony_ci int *gpio_ret, int *irq_ret) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci struct gpio_desc *desc; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci desc = gpiod_get_index(dev, NULL, acpi_index, GPIOD_ASIS); 14162306a36Sopenharmony_ci if (IS_ERR(desc)) 14262306a36Sopenharmony_ci return PTR_ERR(desc); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci *gpio_ret = desc_to_gpio(desc); 14562306a36Sopenharmony_ci *irq_ret = gpiod_to_irq(desc); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci gpiod_put(desc); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci return 0; 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic struct platform_device * 15362306a36Sopenharmony_cisoc_button_device_create(struct platform_device *pdev, 15462306a36Sopenharmony_ci const struct soc_button_info *button_info, 15562306a36Sopenharmony_ci bool autorepeat) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci const struct soc_button_info *info; 15862306a36Sopenharmony_ci struct platform_device *pd; 15962306a36Sopenharmony_ci struct gpio_keys_button *gpio_keys; 16062306a36Sopenharmony_ci struct gpio_keys_platform_data *gpio_keys_pdata; 16162306a36Sopenharmony_ci const struct dmi_system_id *dmi_id; 16262306a36Sopenharmony_ci int invalid_acpi_index = -1; 16362306a36Sopenharmony_ci int error, gpio, irq; 16462306a36Sopenharmony_ci int n_buttons = 0; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci for (info = button_info; info->name; info++) 16762306a36Sopenharmony_ci if (info->autorepeat == autorepeat) 16862306a36Sopenharmony_ci n_buttons++; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci gpio_keys_pdata = devm_kzalloc(&pdev->dev, 17162306a36Sopenharmony_ci sizeof(*gpio_keys_pdata) + 17262306a36Sopenharmony_ci sizeof(*gpio_keys) * n_buttons, 17362306a36Sopenharmony_ci GFP_KERNEL); 17462306a36Sopenharmony_ci if (!gpio_keys_pdata) 17562306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci gpio_keys = (void *)(gpio_keys_pdata + 1); 17862306a36Sopenharmony_ci n_buttons = 0; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci dmi_id = dmi_first_match(dmi_invalid_acpi_index); 18162306a36Sopenharmony_ci if (dmi_id) 18262306a36Sopenharmony_ci invalid_acpi_index = (long)dmi_id->driver_data; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci for (info = button_info; info->name; info++) { 18562306a36Sopenharmony_ci if (info->autorepeat != autorepeat) 18662306a36Sopenharmony_ci continue; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci if (info->acpi_index == invalid_acpi_index) 18962306a36Sopenharmony_ci continue; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci error = soc_button_lookup_gpio(&pdev->dev, info->acpi_index, &gpio, &irq); 19262306a36Sopenharmony_ci if (error || irq < 0) { 19362306a36Sopenharmony_ci /* 19462306a36Sopenharmony_ci * Skip GPIO if not present. Note we deliberately 19562306a36Sopenharmony_ci * ignore -EPROBE_DEFER errors here. On some devices 19662306a36Sopenharmony_ci * Intel is using so called virtual GPIOs which are not 19762306a36Sopenharmony_ci * GPIOs at all but some way for AML code to check some 19862306a36Sopenharmony_ci * random status bits without need a custom opregion. 19962306a36Sopenharmony_ci * In some cases the resources table we parse points to 20062306a36Sopenharmony_ci * such a virtual GPIO, since these are not real GPIOs 20162306a36Sopenharmony_ci * we do not have a driver for these so they will never 20262306a36Sopenharmony_ci * show up, therefore we ignore -EPROBE_DEFER. 20362306a36Sopenharmony_ci */ 20462306a36Sopenharmony_ci continue; 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci /* See dmi_use_low_level_irq[] comment */ 20862306a36Sopenharmony_ci if (!autorepeat && (use_low_level_irq || 20962306a36Sopenharmony_ci dmi_check_system(dmi_use_low_level_irq))) { 21062306a36Sopenharmony_ci irq_set_irq_type(irq, IRQ_TYPE_LEVEL_LOW); 21162306a36Sopenharmony_ci gpio_keys[n_buttons].irq = irq; 21262306a36Sopenharmony_ci gpio_keys[n_buttons].gpio = -ENOENT; 21362306a36Sopenharmony_ci } else { 21462306a36Sopenharmony_ci gpio_keys[n_buttons].gpio = gpio; 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci gpio_keys[n_buttons].type = info->event_type; 21862306a36Sopenharmony_ci gpio_keys[n_buttons].code = info->event_code; 21962306a36Sopenharmony_ci gpio_keys[n_buttons].active_low = info->active_low; 22062306a36Sopenharmony_ci gpio_keys[n_buttons].desc = info->name; 22162306a36Sopenharmony_ci gpio_keys[n_buttons].wakeup = info->wakeup; 22262306a36Sopenharmony_ci /* These devices often use cheap buttons, use 50 ms debounce */ 22362306a36Sopenharmony_ci gpio_keys[n_buttons].debounce_interval = 50; 22462306a36Sopenharmony_ci n_buttons++; 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci if (n_buttons == 0) { 22862306a36Sopenharmony_ci error = -ENODEV; 22962306a36Sopenharmony_ci goto err_free_mem; 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci gpio_keys_pdata->buttons = gpio_keys; 23362306a36Sopenharmony_ci gpio_keys_pdata->nbuttons = n_buttons; 23462306a36Sopenharmony_ci gpio_keys_pdata->rep = autorepeat; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci pd = platform_device_register_resndata(&pdev->dev, "gpio-keys", 23762306a36Sopenharmony_ci PLATFORM_DEVID_AUTO, NULL, 0, 23862306a36Sopenharmony_ci gpio_keys_pdata, 23962306a36Sopenharmony_ci sizeof(*gpio_keys_pdata)); 24062306a36Sopenharmony_ci error = PTR_ERR_OR_ZERO(pd); 24162306a36Sopenharmony_ci if (error) { 24262306a36Sopenharmony_ci dev_err(&pdev->dev, 24362306a36Sopenharmony_ci "failed registering gpio-keys: %d\n", error); 24462306a36Sopenharmony_ci goto err_free_mem; 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci return pd; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_cierr_free_mem: 25062306a36Sopenharmony_ci devm_kfree(&pdev->dev, gpio_keys_pdata); 25162306a36Sopenharmony_ci return ERR_PTR(error); 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic int soc_button_get_acpi_object_int(const union acpi_object *obj) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci if (obj->type != ACPI_TYPE_INTEGER) 25762306a36Sopenharmony_ci return -1; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci return obj->integer.value; 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci/* Parse a single ACPI0011 _DSD button descriptor */ 26362306a36Sopenharmony_cistatic int soc_button_parse_btn_desc(struct device *dev, 26462306a36Sopenharmony_ci const union acpi_object *desc, 26562306a36Sopenharmony_ci int collection_uid, 26662306a36Sopenharmony_ci struct soc_button_info *info) 26762306a36Sopenharmony_ci{ 26862306a36Sopenharmony_ci int upage, usage; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci if (desc->type != ACPI_TYPE_PACKAGE || 27162306a36Sopenharmony_ci desc->package.count != 5 || 27262306a36Sopenharmony_ci /* First byte should be 1 (control) */ 27362306a36Sopenharmony_ci soc_button_get_acpi_object_int(&desc->package.elements[0]) != 1 || 27462306a36Sopenharmony_ci /* Third byte should be collection uid */ 27562306a36Sopenharmony_ci soc_button_get_acpi_object_int(&desc->package.elements[2]) != 27662306a36Sopenharmony_ci collection_uid) { 27762306a36Sopenharmony_ci dev_err(dev, "Invalid ACPI Button Descriptor\n"); 27862306a36Sopenharmony_ci return -ENODEV; 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci info->event_type = EV_KEY; 28262306a36Sopenharmony_ci info->active_low = true; 28362306a36Sopenharmony_ci info->acpi_index = 28462306a36Sopenharmony_ci soc_button_get_acpi_object_int(&desc->package.elements[1]); 28562306a36Sopenharmony_ci upage = soc_button_get_acpi_object_int(&desc->package.elements[3]); 28662306a36Sopenharmony_ci usage = soc_button_get_acpi_object_int(&desc->package.elements[4]); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci /* 28962306a36Sopenharmony_ci * The UUID: fa6bd625-9ce8-470d-a2c7-b3ca36c4282e descriptors use HID 29062306a36Sopenharmony_ci * usage page and usage codes, but otherwise the device is not HID 29162306a36Sopenharmony_ci * compliant: it uses one irq per button instead of generating HID 29262306a36Sopenharmony_ci * input reports and some buttons should generate wakeups where as 29362306a36Sopenharmony_ci * others should not, so we cannot use the HID subsystem. 29462306a36Sopenharmony_ci * 29562306a36Sopenharmony_ci * Luckily all devices only use a few usage page + usage combinations, 29662306a36Sopenharmony_ci * so we can simply check for the known combinations here. 29762306a36Sopenharmony_ci */ 29862306a36Sopenharmony_ci if (upage == 0x01 && usage == 0x81) { 29962306a36Sopenharmony_ci info->name = "power"; 30062306a36Sopenharmony_ci info->event_code = KEY_POWER; 30162306a36Sopenharmony_ci info->wakeup = true; 30262306a36Sopenharmony_ci } else if (upage == 0x01 && usage == 0xc6) { 30362306a36Sopenharmony_ci info->name = "airplane mode switch"; 30462306a36Sopenharmony_ci info->event_type = EV_SW; 30562306a36Sopenharmony_ci info->event_code = SW_RFKILL_ALL; 30662306a36Sopenharmony_ci info->active_low = false; 30762306a36Sopenharmony_ci } else if (upage == 0x01 && usage == 0xca) { 30862306a36Sopenharmony_ci info->name = "rotation lock switch"; 30962306a36Sopenharmony_ci info->event_type = EV_SW; 31062306a36Sopenharmony_ci info->event_code = SW_ROTATE_LOCK; 31162306a36Sopenharmony_ci } else if (upage == 0x07 && usage == 0xe3) { 31262306a36Sopenharmony_ci info->name = "home"; 31362306a36Sopenharmony_ci info->event_code = KEY_LEFTMETA; 31462306a36Sopenharmony_ci info->wakeup = true; 31562306a36Sopenharmony_ci } else if (upage == 0x0c && usage == 0xe9) { 31662306a36Sopenharmony_ci info->name = "volume_up"; 31762306a36Sopenharmony_ci info->event_code = KEY_VOLUMEUP; 31862306a36Sopenharmony_ci info->autorepeat = true; 31962306a36Sopenharmony_ci } else if (upage == 0x0c && usage == 0xea) { 32062306a36Sopenharmony_ci info->name = "volume_down"; 32162306a36Sopenharmony_ci info->event_code = KEY_VOLUMEDOWN; 32262306a36Sopenharmony_ci info->autorepeat = true; 32362306a36Sopenharmony_ci } else { 32462306a36Sopenharmony_ci dev_warn(dev, "Unknown button index %d upage %02x usage %02x, ignoring\n", 32562306a36Sopenharmony_ci info->acpi_index, upage, usage); 32662306a36Sopenharmony_ci info->name = "unknown"; 32762306a36Sopenharmony_ci info->event_code = KEY_RESERVED; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci return 0; 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci/* ACPI0011 _DSD btns descriptors UUID: fa6bd625-9ce8-470d-a2c7-b3ca36c4282e */ 33462306a36Sopenharmony_cistatic const u8 btns_desc_uuid[16] = { 33562306a36Sopenharmony_ci 0x25, 0xd6, 0x6b, 0xfa, 0xe8, 0x9c, 0x0d, 0x47, 33662306a36Sopenharmony_ci 0xa2, 0xc7, 0xb3, 0xca, 0x36, 0xc4, 0x28, 0x2e 33762306a36Sopenharmony_ci}; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci/* Parse ACPI0011 _DSD button descriptors */ 34062306a36Sopenharmony_cistatic struct soc_button_info *soc_button_get_button_info(struct device *dev) 34162306a36Sopenharmony_ci{ 34262306a36Sopenharmony_ci struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER }; 34362306a36Sopenharmony_ci const union acpi_object *desc, *el0, *uuid, *btns_desc = NULL; 34462306a36Sopenharmony_ci struct soc_button_info *button_info; 34562306a36Sopenharmony_ci acpi_status status; 34662306a36Sopenharmony_ci int i, btn, collection_uid = -1; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci status = acpi_evaluate_object_typed(ACPI_HANDLE(dev), "_DSD", NULL, 34962306a36Sopenharmony_ci &buf, ACPI_TYPE_PACKAGE); 35062306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 35162306a36Sopenharmony_ci dev_err(dev, "ACPI _DSD object not found\n"); 35262306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci /* Look for the Button Descriptors UUID */ 35662306a36Sopenharmony_ci desc = buf.pointer; 35762306a36Sopenharmony_ci for (i = 0; (i + 1) < desc->package.count; i += 2) { 35862306a36Sopenharmony_ci uuid = &desc->package.elements[i]; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci if (uuid->type != ACPI_TYPE_BUFFER || 36162306a36Sopenharmony_ci uuid->buffer.length != 16 || 36262306a36Sopenharmony_ci desc->package.elements[i + 1].type != ACPI_TYPE_PACKAGE) { 36362306a36Sopenharmony_ci break; 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci if (memcmp(uuid->buffer.pointer, btns_desc_uuid, 16) == 0) { 36762306a36Sopenharmony_ci btns_desc = &desc->package.elements[i + 1]; 36862306a36Sopenharmony_ci break; 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci } 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci if (!btns_desc) { 37362306a36Sopenharmony_ci dev_err(dev, "ACPI Button Descriptors not found\n"); 37462306a36Sopenharmony_ci button_info = ERR_PTR(-ENODEV); 37562306a36Sopenharmony_ci goto out; 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci /* The first package describes the collection */ 37962306a36Sopenharmony_ci el0 = &btns_desc->package.elements[0]; 38062306a36Sopenharmony_ci if (el0->type == ACPI_TYPE_PACKAGE && 38162306a36Sopenharmony_ci el0->package.count == 5 && 38262306a36Sopenharmony_ci /* First byte should be 0 (collection) */ 38362306a36Sopenharmony_ci soc_button_get_acpi_object_int(&el0->package.elements[0]) == 0 && 38462306a36Sopenharmony_ci /* Third byte should be 0 (top level collection) */ 38562306a36Sopenharmony_ci soc_button_get_acpi_object_int(&el0->package.elements[2]) == 0) { 38662306a36Sopenharmony_ci collection_uid = soc_button_get_acpi_object_int( 38762306a36Sopenharmony_ci &el0->package.elements[1]); 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci if (collection_uid == -1) { 39062306a36Sopenharmony_ci dev_err(dev, "Invalid Button Collection Descriptor\n"); 39162306a36Sopenharmony_ci button_info = ERR_PTR(-ENODEV); 39262306a36Sopenharmony_ci goto out; 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci /* There are package.count - 1 buttons + 1 terminating empty entry */ 39662306a36Sopenharmony_ci button_info = devm_kcalloc(dev, btns_desc->package.count, 39762306a36Sopenharmony_ci sizeof(*button_info), GFP_KERNEL); 39862306a36Sopenharmony_ci if (!button_info) { 39962306a36Sopenharmony_ci button_info = ERR_PTR(-ENOMEM); 40062306a36Sopenharmony_ci goto out; 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci /* Parse the button descriptors */ 40462306a36Sopenharmony_ci for (i = 1, btn = 0; i < btns_desc->package.count; i++, btn++) { 40562306a36Sopenharmony_ci if (soc_button_parse_btn_desc(dev, 40662306a36Sopenharmony_ci &btns_desc->package.elements[i], 40762306a36Sopenharmony_ci collection_uid, 40862306a36Sopenharmony_ci &button_info[btn])) { 40962306a36Sopenharmony_ci button_info = ERR_PTR(-ENODEV); 41062306a36Sopenharmony_ci goto out; 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ciout: 41562306a36Sopenharmony_ci kfree(buf.pointer); 41662306a36Sopenharmony_ci return button_info; 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_cistatic int soc_button_remove(struct platform_device *pdev) 42062306a36Sopenharmony_ci{ 42162306a36Sopenharmony_ci struct soc_button_data *priv = platform_get_drvdata(pdev); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci int i; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci for (i = 0; i < BUTTON_TYPES; i++) 42662306a36Sopenharmony_ci if (priv->children[i]) 42762306a36Sopenharmony_ci platform_device_unregister(priv->children[i]); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci return 0; 43062306a36Sopenharmony_ci} 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_cistatic int soc_button_probe(struct platform_device *pdev) 43362306a36Sopenharmony_ci{ 43462306a36Sopenharmony_ci struct device *dev = &pdev->dev; 43562306a36Sopenharmony_ci const struct soc_device_data *device_data; 43662306a36Sopenharmony_ci const struct soc_button_info *button_info; 43762306a36Sopenharmony_ci struct soc_button_data *priv; 43862306a36Sopenharmony_ci struct platform_device *pd; 43962306a36Sopenharmony_ci int i; 44062306a36Sopenharmony_ci int error; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci device_data = acpi_device_get_match_data(dev); 44362306a36Sopenharmony_ci if (device_data && device_data->check) { 44462306a36Sopenharmony_ci error = device_data->check(dev); 44562306a36Sopenharmony_ci if (error) 44662306a36Sopenharmony_ci return error; 44762306a36Sopenharmony_ci } 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci if (device_data && device_data->button_info) { 45062306a36Sopenharmony_ci button_info = device_data->button_info; 45162306a36Sopenharmony_ci } else { 45262306a36Sopenharmony_ci button_info = soc_button_get_button_info(dev); 45362306a36Sopenharmony_ci if (IS_ERR(button_info)) 45462306a36Sopenharmony_ci return PTR_ERR(button_info); 45562306a36Sopenharmony_ci } 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci error = gpiod_count(dev, NULL); 45862306a36Sopenharmony_ci if (error < 0) { 45962306a36Sopenharmony_ci dev_dbg(dev, "no GPIO attached, ignoring...\n"); 46062306a36Sopenharmony_ci return -ENODEV; 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 46462306a36Sopenharmony_ci if (!priv) 46562306a36Sopenharmony_ci return -ENOMEM; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci platform_set_drvdata(pdev, priv); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci for (i = 0; i < BUTTON_TYPES; i++) { 47062306a36Sopenharmony_ci pd = soc_button_device_create(pdev, button_info, i == 0); 47162306a36Sopenharmony_ci if (IS_ERR(pd)) { 47262306a36Sopenharmony_ci error = PTR_ERR(pd); 47362306a36Sopenharmony_ci if (error != -ENODEV) { 47462306a36Sopenharmony_ci soc_button_remove(pdev); 47562306a36Sopenharmony_ci return error; 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci continue; 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci priv->children[i] = pd; 48162306a36Sopenharmony_ci } 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci if (!priv->children[0] && !priv->children[1]) 48462306a36Sopenharmony_ci return -ENODEV; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci if (!device_data || !device_data->button_info) 48762306a36Sopenharmony_ci devm_kfree(dev, button_info); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci return 0; 49062306a36Sopenharmony_ci} 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci/* 49362306a36Sopenharmony_ci * Definition of buttons on the tablet. The ACPI index of each button 49462306a36Sopenharmony_ci * is defined in section 2.8.7.2 of "Windows ACPI Design Guide for SoC 49562306a36Sopenharmony_ci * Platforms" 49662306a36Sopenharmony_ci */ 49762306a36Sopenharmony_cistatic const struct soc_button_info soc_button_PNP0C40[] = { 49862306a36Sopenharmony_ci { "power", 0, EV_KEY, KEY_POWER, false, true, true }, 49962306a36Sopenharmony_ci { "home", 1, EV_KEY, KEY_LEFTMETA, false, true, true }, 50062306a36Sopenharmony_ci { "volume_up", 2, EV_KEY, KEY_VOLUMEUP, true, false, true }, 50162306a36Sopenharmony_ci { "volume_down", 3, EV_KEY, KEY_VOLUMEDOWN, true, false, true }, 50262306a36Sopenharmony_ci { "rotation_lock", 4, EV_KEY, KEY_ROTATE_LOCK_TOGGLE, false, false, true }, 50362306a36Sopenharmony_ci { } 50462306a36Sopenharmony_ci}; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_cistatic const struct soc_device_data soc_device_PNP0C40 = { 50762306a36Sopenharmony_ci .button_info = soc_button_PNP0C40, 50862306a36Sopenharmony_ci}; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_cistatic const struct soc_button_info soc_button_INT33D3[] = { 51162306a36Sopenharmony_ci { "tablet_mode", 0, EV_SW, SW_TABLET_MODE, false, false, false }, 51262306a36Sopenharmony_ci { } 51362306a36Sopenharmony_ci}; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_cistatic const struct soc_device_data soc_device_INT33D3 = { 51662306a36Sopenharmony_ci .button_info = soc_button_INT33D3, 51762306a36Sopenharmony_ci}; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci/* 52062306a36Sopenharmony_ci * Button info for Microsoft Surface 3 (non pro), this is indentical to 52162306a36Sopenharmony_ci * the PNP0C40 info except that the home button is active-high. 52262306a36Sopenharmony_ci * 52362306a36Sopenharmony_ci * The Surface 3 Pro also has a MSHW0028 ACPI device, but that uses a custom 52462306a36Sopenharmony_ci * version of the drivers/platform/x86/intel/hid.c 5 button array ACPI API 52562306a36Sopenharmony_ci * instead. A check() callback is not necessary though as the Surface 3 Pro 52662306a36Sopenharmony_ci * MSHW0028 ACPI device's resource table does not contain any GPIOs. 52762306a36Sopenharmony_ci */ 52862306a36Sopenharmony_cistatic const struct soc_button_info soc_button_MSHW0028[] = { 52962306a36Sopenharmony_ci { "power", 0, EV_KEY, KEY_POWER, false, true, true }, 53062306a36Sopenharmony_ci { "home", 1, EV_KEY, KEY_LEFTMETA, false, true, false }, 53162306a36Sopenharmony_ci { "volume_up", 2, EV_KEY, KEY_VOLUMEUP, true, false, true }, 53262306a36Sopenharmony_ci { "volume_down", 3, EV_KEY, KEY_VOLUMEDOWN, true, false, true }, 53362306a36Sopenharmony_ci { } 53462306a36Sopenharmony_ci}; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_cistatic const struct soc_device_data soc_device_MSHW0028 = { 53762306a36Sopenharmony_ci .button_info = soc_button_MSHW0028, 53862306a36Sopenharmony_ci}; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci/* 54162306a36Sopenharmony_ci * Special device check for Surface Book 2 and Surface Pro (2017). 54262306a36Sopenharmony_ci * Both, the Surface Pro 4 (surfacepro3_button.c) and the above mentioned 54362306a36Sopenharmony_ci * devices use MSHW0040 for power and volume buttons, however the way they 54462306a36Sopenharmony_ci * have to be addressed differs. Make sure that we only load this drivers 54562306a36Sopenharmony_ci * for the correct devices by checking the OEM Platform Revision provided by 54662306a36Sopenharmony_ci * the _DSM method. 54762306a36Sopenharmony_ci */ 54862306a36Sopenharmony_ci#define MSHW0040_DSM_REVISION 0x01 54962306a36Sopenharmony_ci#define MSHW0040_DSM_GET_OMPR 0x02 // get OEM Platform Revision 55062306a36Sopenharmony_cistatic const guid_t MSHW0040_DSM_UUID = 55162306a36Sopenharmony_ci GUID_INIT(0x6fd05c69, 0xcde3, 0x49f4, 0x95, 0xed, 0xab, 0x16, 0x65, 55262306a36Sopenharmony_ci 0x49, 0x80, 0x35); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_cistatic int soc_device_check_MSHW0040(struct device *dev) 55562306a36Sopenharmony_ci{ 55662306a36Sopenharmony_ci acpi_handle handle = ACPI_HANDLE(dev); 55762306a36Sopenharmony_ci union acpi_object *result; 55862306a36Sopenharmony_ci u64 oem_platform_rev = 0; // valid revisions are nonzero 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci // get OEM platform revision 56162306a36Sopenharmony_ci result = acpi_evaluate_dsm_typed(handle, &MSHW0040_DSM_UUID, 56262306a36Sopenharmony_ci MSHW0040_DSM_REVISION, 56362306a36Sopenharmony_ci MSHW0040_DSM_GET_OMPR, NULL, 56462306a36Sopenharmony_ci ACPI_TYPE_INTEGER); 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci if (result) { 56762306a36Sopenharmony_ci oem_platform_rev = result->integer.value; 56862306a36Sopenharmony_ci ACPI_FREE(result); 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci /* 57262306a36Sopenharmony_ci * If the revision is zero here, the _DSM evaluation has failed. This 57362306a36Sopenharmony_ci * indicates that we have a Pro 4 or Book 1 and this driver should not 57462306a36Sopenharmony_ci * be used. 57562306a36Sopenharmony_ci */ 57662306a36Sopenharmony_ci if (oem_platform_rev == 0) 57762306a36Sopenharmony_ci return -ENODEV; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci dev_dbg(dev, "OEM Platform Revision %llu\n", oem_platform_rev); 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci return 0; 58262306a36Sopenharmony_ci} 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci/* 58562306a36Sopenharmony_ci * Button infos for Microsoft Surface Book 2 and Surface Pro (2017). 58662306a36Sopenharmony_ci * Obtained from DSDT/testing. 58762306a36Sopenharmony_ci */ 58862306a36Sopenharmony_cistatic const struct soc_button_info soc_button_MSHW0040[] = { 58962306a36Sopenharmony_ci { "power", 0, EV_KEY, KEY_POWER, false, true, true }, 59062306a36Sopenharmony_ci { "volume_up", 2, EV_KEY, KEY_VOLUMEUP, true, false, true }, 59162306a36Sopenharmony_ci { "volume_down", 4, EV_KEY, KEY_VOLUMEDOWN, true, false, true }, 59262306a36Sopenharmony_ci { } 59362306a36Sopenharmony_ci}; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_cistatic const struct soc_device_data soc_device_MSHW0040 = { 59662306a36Sopenharmony_ci .button_info = soc_button_MSHW0040, 59762306a36Sopenharmony_ci .check = soc_device_check_MSHW0040, 59862306a36Sopenharmony_ci}; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_cistatic const struct acpi_device_id soc_button_acpi_match[] = { 60162306a36Sopenharmony_ci { "PNP0C40", (unsigned long)&soc_device_PNP0C40 }, 60262306a36Sopenharmony_ci { "INT33D3", (unsigned long)&soc_device_INT33D3 }, 60362306a36Sopenharmony_ci { "ID9001", (unsigned long)&soc_device_INT33D3 }, 60462306a36Sopenharmony_ci { "ACPI0011", 0 }, 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci /* Microsoft Surface Devices (3th, 5th and 6th generation) */ 60762306a36Sopenharmony_ci { "MSHW0028", (unsigned long)&soc_device_MSHW0028 }, 60862306a36Sopenharmony_ci { "MSHW0040", (unsigned long)&soc_device_MSHW0040 }, 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci { } 61162306a36Sopenharmony_ci}; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, soc_button_acpi_match); 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_cistatic struct platform_driver soc_button_driver = { 61662306a36Sopenharmony_ci .probe = soc_button_probe, 61762306a36Sopenharmony_ci .remove = soc_button_remove, 61862306a36Sopenharmony_ci .driver = { 61962306a36Sopenharmony_ci .name = KBUILD_MODNAME, 62062306a36Sopenharmony_ci .acpi_match_table = ACPI_PTR(soc_button_acpi_match), 62162306a36Sopenharmony_ci }, 62262306a36Sopenharmony_ci}; 62362306a36Sopenharmony_cimodule_platform_driver(soc_button_driver); 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 626