From 01573e231f18eb2d99162747186f59511f56b64d Mon Sep 17 00:00:00 2001
From: hc <hc@nodka.com>
Date: Fri, 08 Dec 2023 10:40:48 +0000
Subject: [PATCH] 移去rt
---
kernel/drivers/input/misc/soc_button_array.c | 260 +++++++++++++++++++++++++++++++++++++++++++---------
1 files changed, 215 insertions(+), 45 deletions(-)
diff --git a/kernel/drivers/input/misc/soc_button_array.c b/kernel/drivers/input/misc/soc_button_array.c
index 55cd6e0..31c02c2 100644
--- a/kernel/drivers/input/misc/soc_button_array.c
+++ b/kernel/drivers/input/misc/soc_button_array.c
@@ -1,24 +1,26 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Supports for the button array on SoC tablets originally running
* Windows 8.
*
* (C) Copyright 2014 Intel Corporation
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; version 2
- * of the License.
*/
#include <linux/module.h>
#include <linux/input.h>
#include <linux/init.h>
+#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/acpi.h>
+#include <linux/dmi.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio_keys.h>
#include <linux/gpio.h>
#include <linux/platform_device.h>
+
+static bool use_low_level_irq;
+module_param(use_low_level_irq, bool, 0444);
+MODULE_PARM_DESC(use_low_level_irq, "Use low-level triggered IRQ instead of edge triggered");
struct soc_button_info {
const char *name;
@@ -27,6 +29,12 @@
unsigned int event_code;
bool autorepeat;
bool wakeup;
+ bool active_low;
+};
+
+struct soc_device_data {
+ const struct soc_button_info *button_info;
+ int (*check)(struct device *dev);
};
/*
@@ -41,22 +49,83 @@
};
/*
+ * Some 2-in-1s which use the soc_button_array driver have this ugly issue in
+ * their DSDT where the _LID method modifies the irq-type settings of the GPIOs
+ * used for the power and home buttons. The intend of this AML code is to
+ * disable these buttons when the lid is closed.
+ * The AML does this by directly poking the GPIO controllers registers. This is
+ * problematic because when re-enabling the irq, which happens whenever _LID
+ * gets called with the lid open (e.g. on boot and on resume), it sets the
+ * irq-type to IRQ_TYPE_LEVEL_LOW. Where as the gpio-keys driver programs the
+ * type to, and expects it to be, IRQ_TYPE_EDGE_BOTH.
+ * To work around this we don't set gpio_keys_button.gpio on these 2-in-1s,
+ * instead we get the irq for the GPIO ourselves, configure it as
+ * IRQ_TYPE_LEVEL_LOW (to match how the _LID AML code configures it) and pass
+ * the irq in gpio_keys_button.irq. Below is a list of affected devices.
+ */
+static const struct dmi_system_id dmi_use_low_level_irq[] = {
+ {
+ /*
+ * Acer Switch 10 SW5-012. _LID method messes with home- and
+ * power-button GPIO IRQ settings. When (re-)enabling the irq
+ * it ors in its own flags without clearing the previous set
+ * ones, leading to an irq-type of IRQ_TYPE_LEVEL_LOW |
+ * IRQ_TYPE_LEVEL_HIGH causing a continuous interrupt storm.
+ */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire SW5-012"),
+ },
+ },
+ {
+ /* Acer Switch V 10 SW5-017, same issue as Acer Switch 10 SW5-012. */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "SW5-017"),
+ },
+ },
+ {
+ /*
+ * Acer One S1003. _LID method messes with power-button GPIO
+ * IRQ settings, leading to a non working power-button.
+ */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "One S1003"),
+ },
+ },
+ {
+ /*
+ * Lenovo Yoga Tab2 1051F/1051L, something messes with the home-button
+ * IRQ settings, leading to a non working home-button.
+ */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "60073"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "1051"),
+ },
+ },
+ {} /* Terminating entry */
+};
+
+/*
* Get the Nth GPIO number from the ACPI object.
*/
-static int soc_button_lookup_gpio(struct device *dev, int acpi_index)
+static int soc_button_lookup_gpio(struct device *dev, int acpi_index,
+ int *gpio_ret, int *irq_ret)
{
struct gpio_desc *desc;
- int gpio;
desc = gpiod_get_index(dev, NULL, acpi_index, GPIOD_ASIS);
if (IS_ERR(desc))
return PTR_ERR(desc);
- gpio = desc_to_gpio(desc);
+ *gpio_ret = desc_to_gpio(desc);
+ *irq_ret = gpiod_to_irq(desc);
gpiod_put(desc);
- return gpio;
+ return 0;
}
static struct platform_device *
@@ -68,9 +137,8 @@
struct platform_device *pd;
struct gpio_keys_button *gpio_keys;
struct gpio_keys_platform_data *gpio_keys_pdata;
+ int error, gpio, irq;
int n_buttons = 0;
- int gpio;
- int error;
for (info = button_info; info->name; info++)
if (info->autorepeat == autorepeat)
@@ -90,14 +158,35 @@
if (info->autorepeat != autorepeat)
continue;
- gpio = soc_button_lookup_gpio(&pdev->dev, info->acpi_index);
- if (!gpio_is_valid(gpio))
+ error = soc_button_lookup_gpio(&pdev->dev, info->acpi_index, &gpio, &irq);
+ if (error || irq < 0) {
+ /*
+ * Skip GPIO if not present. Note we deliberately
+ * ignore -EPROBE_DEFER errors here. On some devices
+ * Intel is using so called virtual GPIOs which are not
+ * GPIOs at all but some way for AML code to check some
+ * random status bits without need a custom opregion.
+ * In some cases the resources table we parse points to
+ * such a virtual GPIO, since these are not real GPIOs
+ * we do not have a driver for these so they will never
+ * show up, therefore we ignore -EPROBE_DEFER.
+ */
continue;
+ }
+
+ /* See dmi_use_low_level_irq[] comment */
+ if (!autorepeat && (use_low_level_irq ||
+ dmi_check_system(dmi_use_low_level_irq))) {
+ irq_set_irq_type(irq, IRQ_TYPE_LEVEL_LOW);
+ gpio_keys[n_buttons].irq = irq;
+ gpio_keys[n_buttons].gpio = -ENOENT;
+ } else {
+ gpio_keys[n_buttons].gpio = gpio;
+ }
gpio_keys[n_buttons].type = info->event_type;
gpio_keys[n_buttons].code = info->event_code;
- gpio_keys[n_buttons].gpio = gpio;
- gpio_keys[n_buttons].active_low = 1;
+ gpio_keys[n_buttons].active_low = info->active_low;
gpio_keys[n_buttons].desc = info->name;
gpio_keys[n_buttons].wakeup = info->wakeup;
/* These devices often use cheap buttons, use 50 ms debounce */
@@ -114,25 +203,19 @@
gpio_keys_pdata->nbuttons = n_buttons;
gpio_keys_pdata->rep = autorepeat;
- pd = platform_device_alloc("gpio-keys", PLATFORM_DEVID_AUTO);
- if (!pd) {
- error = -ENOMEM;
+ pd = platform_device_register_resndata(&pdev->dev, "gpio-keys",
+ PLATFORM_DEVID_AUTO, NULL, 0,
+ gpio_keys_pdata,
+ sizeof(*gpio_keys_pdata));
+ error = PTR_ERR_OR_ZERO(pd);
+ if (error) {
+ dev_err(&pdev->dev,
+ "failed registering gpio-keys: %d\n", error);
goto err_free_mem;
}
- error = platform_device_add_data(pd, gpio_keys_pdata,
- sizeof(*gpio_keys_pdata));
- if (error)
- goto err_free_pdev;
-
- error = platform_device_add(pd);
- if (error)
- goto err_free_pdev;
-
return pd;
-err_free_pdev:
- platform_device_put(pd);
err_free_mem:
devm_kfree(&pdev->dev, gpio_keys_pdata);
return ERR_PTR(error);
@@ -166,6 +249,7 @@
}
info->event_type = EV_KEY;
+ info->active_low = true;
info->acpi_index =
soc_button_get_acpi_object_int(&desc->package.elements[1]);
upage = soc_button_get_acpi_object_int(&desc->package.elements[3]);
@@ -185,6 +269,10 @@
info->name = "power";
info->event_code = KEY_POWER;
info->wakeup = true;
+ } else if (upage == 0x01 && usage == 0xca) {
+ info->name = "rotation lock switch";
+ info->event_type = EV_SW;
+ info->event_code = SW_ROTATE_LOCK;
} else if (upage == 0x07 && usage == 0xe3) {
info->name = "home";
info->event_code = KEY_LEFTMETA;
@@ -309,23 +397,26 @@
static int soc_button_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- const struct acpi_device_id *id;
- struct soc_button_info *button_info;
+ const struct soc_device_data *device_data;
+ const struct soc_button_info *button_info;
struct soc_button_data *priv;
struct platform_device *pd;
int i;
int error;
- id = acpi_match_device(dev->driver->acpi_match_table, dev);
- if (!id)
- return -ENODEV;
+ device_data = acpi_device_get_match_data(dev);
+ if (device_data && device_data->check) {
+ error = device_data->check(dev);
+ if (error)
+ return error;
+ }
- if (!id->driver_data) {
+ if (device_data && device_data->button_info) {
+ button_info = device_data->button_info;
+ } else {
button_info = soc_button_get_button_info(dev);
if (IS_ERR(button_info))
return PTR_ERR(button_info);
- } else {
- button_info = (struct soc_button_info *)id->driver_data;
}
error = gpiod_count(dev, NULL);
@@ -357,7 +448,7 @@
if (!priv->children[0] && !priv->children[1])
return -ENODEV;
- if (!id->driver_data)
+ if (!device_data || !device_data->button_info)
devm_kfree(dev, button_info);
return 0;
@@ -368,18 +459,97 @@
* is defined in section 2.8.7.2 of "Windows ACPI Design Guide for SoC
* Platforms"
*/
-static struct soc_button_info soc_button_PNP0C40[] = {
- { "power", 0, EV_KEY, KEY_POWER, false, true },
- { "home", 1, EV_KEY, KEY_LEFTMETA, false, true },
- { "volume_up", 2, EV_KEY, KEY_VOLUMEUP, true, false },
- { "volume_down", 3, EV_KEY, KEY_VOLUMEDOWN, true, false },
- { "rotation_lock", 4, EV_KEY, KEY_ROTATE_LOCK_TOGGLE, false, false },
+static const struct soc_button_info soc_button_PNP0C40[] = {
+ { "power", 0, EV_KEY, KEY_POWER, false, true, true },
+ { "home", 1, EV_KEY, KEY_LEFTMETA, false, true, true },
+ { "volume_up", 2, EV_KEY, KEY_VOLUMEUP, true, false, true },
+ { "volume_down", 3, EV_KEY, KEY_VOLUMEDOWN, true, false, true },
+ { "rotation_lock", 4, EV_KEY, KEY_ROTATE_LOCK_TOGGLE, false, false, true },
{ }
};
+static const struct soc_device_data soc_device_PNP0C40 = {
+ .button_info = soc_button_PNP0C40,
+};
+
+static const struct soc_button_info soc_button_INT33D3[] = {
+ { "tablet_mode", 0, EV_SW, SW_TABLET_MODE, false, false, false },
+ { }
+};
+
+static const struct soc_device_data soc_device_INT33D3 = {
+ .button_info = soc_button_INT33D3,
+};
+
+/*
+ * Special device check for Surface Book 2 and Surface Pro (2017).
+ * Both, the Surface Pro 4 (surfacepro3_button.c) and the above mentioned
+ * devices use MSHW0040 for power and volume buttons, however the way they
+ * have to be addressed differs. Make sure that we only load this drivers
+ * for the correct devices by checking the OEM Platform Revision provided by
+ * the _DSM method.
+ */
+#define MSHW0040_DSM_REVISION 0x01
+#define MSHW0040_DSM_GET_OMPR 0x02 // get OEM Platform Revision
+static const guid_t MSHW0040_DSM_UUID =
+ GUID_INIT(0x6fd05c69, 0xcde3, 0x49f4, 0x95, 0xed, 0xab, 0x16, 0x65,
+ 0x49, 0x80, 0x35);
+
+static int soc_device_check_MSHW0040(struct device *dev)
+{
+ acpi_handle handle = ACPI_HANDLE(dev);
+ union acpi_object *result;
+ u64 oem_platform_rev = 0; // valid revisions are nonzero
+
+ // get OEM platform revision
+ result = acpi_evaluate_dsm_typed(handle, &MSHW0040_DSM_UUID,
+ MSHW0040_DSM_REVISION,
+ MSHW0040_DSM_GET_OMPR, NULL,
+ ACPI_TYPE_INTEGER);
+
+ if (result) {
+ oem_platform_rev = result->integer.value;
+ ACPI_FREE(result);
+ }
+
+ /*
+ * If the revision is zero here, the _DSM evaluation has failed. This
+ * indicates that we have a Pro 4 or Book 1 and this driver should not
+ * be used.
+ */
+ if (oem_platform_rev == 0)
+ return -ENODEV;
+
+ dev_dbg(dev, "OEM Platform Revision %llu\n", oem_platform_rev);
+
+ return 0;
+}
+
+/*
+ * Button infos for Microsoft Surface Book 2 and Surface Pro (2017).
+ * Obtained from DSDT/testing.
+ */
+static const struct soc_button_info soc_button_MSHW0040[] = {
+ { "power", 0, EV_KEY, KEY_POWER, false, true, true },
+ { "volume_up", 2, EV_KEY, KEY_VOLUMEUP, true, false, true },
+ { "volume_down", 4, EV_KEY, KEY_VOLUMEDOWN, true, false, true },
+ { }
+};
+
+static const struct soc_device_data soc_device_MSHW0040 = {
+ .button_info = soc_button_MSHW0040,
+ .check = soc_device_check_MSHW0040,
+};
+
static const struct acpi_device_id soc_button_acpi_match[] = {
- { "PNP0C40", (unsigned long)soc_button_PNP0C40 },
+ { "PNP0C40", (unsigned long)&soc_device_PNP0C40 },
+ { "INT33D3", (unsigned long)&soc_device_INT33D3 },
+ { "ID9001", (unsigned long)&soc_device_INT33D3 },
{ "ACPI0011", 0 },
+
+ /* Microsoft Surface Devices (5th and 6th generation) */
+ { "MSHW0040", (unsigned long)&soc_device_MSHW0040 },
+
{ }
};
--
Gitblit v1.6.2