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