OSDN Git Service

platform/x86: x86-android-tablets: Add support for PMIC interrupts
authorHans de Goede <hdegoede@redhat.com>
Wed, 29 Dec 2021 23:14:24 +0000 (00:14 +0100)
committerHans de Goede <hdegoede@redhat.com>
Wed, 5 Jan 2022 10:31:18 +0000 (11:31 +0100)
The Crystal Cove PMIC has a pin which can be used to connect the IRQ of
an external charger IC. On some boards this is used so we need a way to
look this up.

Note that the Intel PMICs have 2 levels of interrupts and thus
2 levels of IRQ domains all tied to a single fwnode.

Level 1 is the irqchip which demultiplexes the actual PMIC interrupt into
interrupts for the various MFD cells. Level 2 are the irqchips used in the
cell drivers which themselves export IRQs, such as the crystal_cove_gpio
driver, which de-multiplexes the level 2 interrupts for the GPIOs into
individual per GPIO IRQs.

The crystal_cove_charger driver registers an irqchip with a single IRQ for
the charger driver to consume. Note the MFD cell IRQ cannot be consumed
directly because the level 2 interrupts must be explicitly acked.

To allow finding the right IRQ domain when looking up the IRQ for
the charger, the crystal_cove_charger driver sets a DOMAIN_BUS_WIRED token
on its IRQ domain.

Add support for looking up the IRQ from the crystal_cove_charger driver.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Link: https://lore.kernel.org/r/20211229231431.437982-6-hdegoede@redhat.com
drivers/platform/x86/x86-android-tablets.c

index ea033d7..4413888 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/gpio/machine.h>
 #include <linux/i2c.h>
 #include <linux/irq.h>
+#include <linux/irqdomain.h>
 #include <linux/module.h>
 #include <linux/mod_devicetable.h>
 #include <linux/string.h>
@@ -31,11 +32,13 @@ enum x86_acpi_irq_type {
        X86_ACPI_IRQ_TYPE_NONE,
        X86_ACPI_IRQ_TYPE_APIC,
        X86_ACPI_IRQ_TYPE_GPIOINT,
+       X86_ACPI_IRQ_TYPE_PMIC,
 };
 
 struct x86_acpi_irq_data {
-       char *chip;   /* GPIO chip label (GPIOINT) */
+       char *chip;   /* GPIO chip label (GPIOINT) or PMIC ACPI path (PMIC) */
        enum x86_acpi_irq_type type;
+       enum irq_domain_bus_token domain;
        int index;
        int trigger;  /* ACPI_EDGE_SENSITIVE / ACPI_LEVEL_SENSITIVE */
        int polarity; /* ACPI_ACTIVE_HIGH / ACPI_ACTIVE_LOW / ACPI_ACTIVE_BOTH */
@@ -48,9 +51,14 @@ static int x86_acpi_irq_helper_gpiochip_find(struct gpio_chip *gc, void *data)
 
 static int x86_acpi_irq_helper_get(const struct x86_acpi_irq_data *data)
 {
+       struct irq_fwspec fwspec = { };
+       struct irq_domain *domain;
+       struct acpi_device *adev;
        struct gpio_desc *gpiod;
        struct gpio_chip *chip;
        unsigned int irq_type;
+       acpi_handle handle;
+       acpi_status status;
        int irq, ret;
 
        switch (data->type) {
@@ -86,6 +94,27 @@ static int x86_acpi_irq_helper_get(const struct x86_acpi_irq_data *data)
                        irq_set_irq_type(irq, irq_type);
 
                return irq;
+       case X86_ACPI_IRQ_TYPE_PMIC:
+               status = acpi_get_handle(NULL, data->chip, &handle);
+               if (ACPI_FAILURE(status)) {
+                       pr_err("error could not get %s handle\n", data->chip);
+                       return -ENODEV;
+               }
+
+               acpi_bus_get_device(handle, &adev);
+               if (!adev) {
+                       pr_err("error could not get %s adev\n", data->chip);
+                       return -ENODEV;
+               }
+
+               fwspec.fwnode = acpi_fwnode_handle(adev);
+               domain = irq_find_matching_fwspec(&fwspec, data->domain);
+               if (!domain) {
+                       pr_err("error could not find IRQ domain for %s\n", data->chip);
+                       return -ENODEV;
+               }
+
+               return irq_create_mapping(domain, data->index);
        default:
                return 0;
        }