#include <linux/init.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>
        struct platform_device *children[BUTTON_TYPES];
 };
 
+/*
+ * 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 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"),
+               },
+       },
+       {} /* 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 *
        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)
                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
                        continue;
                }
 
+               /* See dmi_use_low_level_irq[] comment */
+               if (!autorepeat && 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 = info->active_low;
                gpio_keys[n_buttons].desc = info->name;
                gpio_keys[n_buttons].wakeup = info->wakeup;