6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Andy Shevchenko andriy.shevchenko@linux.intel.com
[ Upstream commit 92dc572852ddcae687590cb159189004d58e382e ]
The gpiolib-acpi.c is huge enough even without DMI quirks. Move them to a separate file for a better maintenance.
No functional change intended.
Reviewed-by: Hans de Goede hdegoede@redhat.com Acked-by: Mika Westerberg mika.westerberg@linux.intel.com Signed-off-by: Andy Shevchenko andriy.shevchenko@linux.intel.com Stable-dep-of: 2d967310c49e ("gpiolib: acpi: Add quirk for Dell Precision 7780") Signed-off-by: Sasha Levin sashal@kernel.org Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org --- drivers/gpio/Makefile | 1 drivers/gpio/gpiolib-acpi-core.c | 1419 +++++++++++++++++++++++++++++ drivers/gpio/gpiolib-acpi-quirks.c | 363 +++++++ drivers/gpio/gpiolib-acpi.c | 1765 ------------------------------------- 4 files changed, 1783 insertions(+), 1765 deletions(-) rename drivers/gpio/{gpiolib-acpi.c => gpiolib-acpi-core.c} (79%) create mode 100644 drivers/gpio/gpiolib-acpi-quirks.c
--- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_OF_GPIO) += gpiolib-of.o obj-$(CONFIG_GPIO_CDEV) += gpiolib-cdev.o obj-$(CONFIG_GPIO_SYSFS) += gpiolib-sysfs.o obj-$(CONFIG_GPIO_ACPI) += gpiolib-acpi.o +gpiolib-acpi-y := gpiolib-acpi-core.o gpiolib-acpi-quirks.o obj-$(CONFIG_GPIOLIB) += gpiolib-swnode.o
# Device drivers. Generally keep list sorted alphabetically --- /dev/null +++ b/drivers/gpio/gpiolib-acpi-core.c @@ -0,0 +1,1419 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ACPI helpers for GPIO API + * + * Copyright (C) 2012, Intel Corporation + * Authors: Mathias Nyman mathias.nyman@linux.intel.com + * Mika Westerberg mika.westerberg@linux.intel.com + */ + +#include <linux/acpi.h> +#include <linux/dmi.h> +#include <linux/errno.h> +#include <linux/export.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/mutex.h> +#include <linux/pinctrl/pinctrl.h> + +#include <linux/gpio/consumer.h> +#include <linux/gpio/driver.h> +#include <linux/gpio/machine.h> + +#include "gpiolib.h" +#include "gpiolib-acpi.h" + +/** + * struct acpi_gpio_event - ACPI GPIO event handler data + * + * @node: list-entry of the events list of the struct acpi_gpio_chip + * @handle: handle of ACPI method to execute when the IRQ triggers + * @handler: handler function to pass to request_irq() when requesting the IRQ + * @pin: GPIO pin number on the struct gpio_chip + * @irq: Linux IRQ number for the event, for request_irq() / free_irq() + * @irqflags: flags to pass to request_irq() when requesting the IRQ + * @irq_is_wake: If the ACPI flags indicate the IRQ is a wakeup source + * @irq_requested:True if request_irq() has been done + * @desc: struct gpio_desc for the GPIO pin for this event + */ +struct acpi_gpio_event { + struct list_head node; + acpi_handle handle; + irq_handler_t handler; + unsigned int pin; + unsigned int irq; + unsigned long irqflags; + bool irq_is_wake; + bool irq_requested; + struct gpio_desc *desc; +}; + +struct acpi_gpio_connection { + struct list_head node; + unsigned int pin; + struct gpio_desc *desc; +}; + +struct acpi_gpio_chip { + /* + * ACPICA requires that the first field of the context parameter + * passed to acpi_install_address_space_handler() is large enough + * to hold struct acpi_connection_info. + */ + struct acpi_connection_info conn_info; + struct list_head conns; + struct mutex conn_lock; + struct gpio_chip *chip; + struct list_head events; + struct list_head deferred_req_irqs_list_entry; +}; + +/** + * struct acpi_gpio_info - ACPI GPIO specific information + * @adev: reference to ACPI device which consumes GPIO resource + * @flags: GPIO initialization flags + * @gpioint: if %true this GPIO is of type GpioInt otherwise type is GpioIo + * @pin_config: pin bias as provided by ACPI + * @polarity: interrupt polarity as provided by ACPI + * @triggering: triggering type as provided by ACPI + * @wake_capable: wake capability as provided by ACPI + * @debounce: debounce timeout as provided by ACPI + * @quirks: Linux specific quirks as provided by struct acpi_gpio_mapping + */ +struct acpi_gpio_info { + struct acpi_device *adev; + enum gpiod_flags flags; + bool gpioint; + int pin_config; + int polarity; + int triggering; + bool wake_capable; + unsigned int debounce; + unsigned int quirks; +}; + +static int acpi_gpiochip_find(struct gpio_chip *gc, const void *data) +{ + /* First check the actual GPIO device */ + if (device_match_acpi_handle(&gc->gpiodev->dev, data)) + return true; + + /* + * When the ACPI device is artificially split to the banks of GPIOs, + * where each of them is represented by a separate GPIO device, + * the firmware node of the physical device may not be shared among + * the banks as they may require different values for the same property, + * e.g., number of GPIOs in a certain bank. In such case the ACPI handle + * of a GPIO device is NULL and can not be used. Hence we have to check + * the parent device to be sure that there is no match before bailing + * out. + */ + if (gc->parent) + return device_match_acpi_handle(gc->parent, data); + + return false; +} + +/** + * acpi_get_gpiod() - Translate ACPI GPIO pin to GPIO descriptor usable with GPIO API + * @path: ACPI GPIO controller full path name, (e.g. "\_SB.GPO1") + * @pin: ACPI GPIO pin number (0-based, controller-relative) + * + * Returns: + * GPIO descriptor to use with Linux generic GPIO API. + * If the GPIO cannot be translated or there is an error an ERR_PTR is + * returned. + * + * Specifically returns %-EPROBE_DEFER if the referenced GPIO + * controller does not have GPIO chip registered at the moment. This is to + * support probe deferral. + */ +static struct gpio_desc *acpi_get_gpiod(char *path, unsigned int pin) +{ + acpi_handle handle; + acpi_status status; + + status = acpi_get_handle(NULL, path, &handle); + if (ACPI_FAILURE(status)) + return ERR_PTR(-ENODEV); + + struct gpio_device *gdev __free(gpio_device_put) = + gpio_device_find(handle, acpi_gpiochip_find); + if (!gdev) + return ERR_PTR(-EPROBE_DEFER); + + /* + * FIXME: keep track of the reference to the GPIO device somehow + * instead of putting it here. + */ + return gpio_device_get_desc(gdev, pin); +} + +static irqreturn_t acpi_gpio_irq_handler(int irq, void *data) +{ + struct acpi_gpio_event *event = data; + + acpi_evaluate_object(event->handle, NULL, NULL, NULL); + + return IRQ_HANDLED; +} + +static irqreturn_t acpi_gpio_irq_handler_evt(int irq, void *data) +{ + struct acpi_gpio_event *event = data; + + acpi_execute_simple_method(event->handle, NULL, event->pin); + + return IRQ_HANDLED; +} + +static void acpi_gpio_chip_dh(acpi_handle handle, void *data) +{ + /* The address of this function is used as a key. */ +} + +bool acpi_gpio_get_irq_resource(struct acpi_resource *ares, + struct acpi_resource_gpio **agpio) +{ + struct acpi_resource_gpio *gpio; + + if (ares->type != ACPI_RESOURCE_TYPE_GPIO) + return false; + + gpio = &ares->data.gpio; + if (gpio->connection_type != ACPI_RESOURCE_GPIO_TYPE_INT) + return false; + + *agpio = gpio; + return true; +} +EXPORT_SYMBOL_GPL(acpi_gpio_get_irq_resource); + +/** + * acpi_gpio_get_io_resource - Fetch details of an ACPI resource if it is a GPIO + * I/O resource or return False if not. + * @ares: Pointer to the ACPI resource to fetch + * @agpio: Pointer to a &struct acpi_resource_gpio to store the output pointer + * + * Returns: + * %true if GpioIo resource is found, %false otherwise. + */ +bool acpi_gpio_get_io_resource(struct acpi_resource *ares, + struct acpi_resource_gpio **agpio) +{ + struct acpi_resource_gpio *gpio; + + if (ares->type != ACPI_RESOURCE_TYPE_GPIO) + return false; + + gpio = &ares->data.gpio; + if (gpio->connection_type != ACPI_RESOURCE_GPIO_TYPE_IO) + return false; + + *agpio = gpio; + return true; +} +EXPORT_SYMBOL_GPL(acpi_gpio_get_io_resource); + +static void acpi_gpiochip_request_irq(struct acpi_gpio_chip *acpi_gpio, + struct acpi_gpio_event *event) +{ + struct device *parent = acpi_gpio->chip->parent; + int ret, value; + + ret = request_threaded_irq(event->irq, NULL, event->handler, + event->irqflags | IRQF_ONESHOT, "ACPI:Event", event); + if (ret) { + dev_err(parent, "Failed to setup interrupt handler for %d\n", event->irq); + return; + } + + if (event->irq_is_wake) + enable_irq_wake(event->irq); + + event->irq_requested = true; + + /* Make sure we trigger the initial state of edge-triggered IRQs */ + if (acpi_gpio_need_run_edge_events_on_boot() && + (event->irqflags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING))) { + value = gpiod_get_raw_value_cansleep(event->desc); + if (((event->irqflags & IRQF_TRIGGER_RISING) && value == 1) || + ((event->irqflags & IRQF_TRIGGER_FALLING) && value == 0)) + event->handler(event->irq, event); + } +} + +static void acpi_gpiochip_request_irqs(struct acpi_gpio_chip *acpi_gpio) +{ + struct acpi_gpio_event *event; + + list_for_each_entry(event, &acpi_gpio->events, node) + acpi_gpiochip_request_irq(acpi_gpio, event); +} + +static enum gpiod_flags +acpi_gpio_to_gpiod_flags(const struct acpi_resource_gpio *agpio, int polarity) +{ + /* GpioInt() implies input configuration */ + if (agpio->connection_type == ACPI_RESOURCE_GPIO_TYPE_INT) + return GPIOD_IN; + + switch (agpio->io_restriction) { + case ACPI_IO_RESTRICT_INPUT: + return GPIOD_IN; + case ACPI_IO_RESTRICT_OUTPUT: + /* + * ACPI GPIO resources don't contain an initial value for the + * GPIO. Therefore we deduce that value from the pull field + * and the polarity instead. If the pin is pulled up we assume + * default to be high, if it is pulled down we assume default + * to be low, otherwise we leave pin untouched. For active low + * polarity values will be switched. See also + * Documentation/firmware-guide/acpi/gpio-properties.rst. + */ + switch (agpio->pin_config) { + case ACPI_PIN_CONFIG_PULLUP: + return polarity == GPIO_ACTIVE_LOW ? GPIOD_OUT_LOW : GPIOD_OUT_HIGH; + case ACPI_PIN_CONFIG_PULLDOWN: + return polarity == GPIO_ACTIVE_LOW ? GPIOD_OUT_HIGH : GPIOD_OUT_LOW; + default: + break; + } + break; + default: + break; + } + + /* + * Assume that the BIOS has configured the direction and pull + * accordingly. + */ + return GPIOD_ASIS; +} + +static struct gpio_desc *acpi_request_own_gpiod(struct gpio_chip *chip, + struct acpi_resource_gpio *agpio, + unsigned int index, + const char *label) +{ + int polarity = GPIO_ACTIVE_HIGH; + enum gpiod_flags flags = acpi_gpio_to_gpiod_flags(agpio, polarity); + unsigned int pin = agpio->pin_table[index]; + struct gpio_desc *desc; + int ret; + + desc = gpiochip_request_own_desc(chip, pin, label, polarity, flags); + if (IS_ERR(desc)) + return desc; + + /* ACPI uses hundredths of milliseconds units */ + ret = gpio_set_debounce_timeout(desc, agpio->debounce_timeout * 10); + if (ret) + dev_warn(chip->parent, + "Failed to set debounce-timeout for pin 0x%04X, err %d\n", + pin, ret); + + return desc; +} + +static bool acpi_gpio_irq_is_wake(struct device *parent, + const struct acpi_resource_gpio *agpio) +{ + unsigned int pin = agpio->pin_table[0]; + + if (agpio->wake_capable != ACPI_WAKE_CAPABLE) + return false; + + if (acpi_gpio_in_ignore_list(ACPI_GPIO_IGNORE_WAKE, dev_name(parent), pin)) { + dev_info(parent, "Ignoring wakeup on pin %u\n", pin); + return false; + } + + return true; +} + +/* Always returns AE_OK so that we keep looping over the resources */ +static acpi_status acpi_gpiochip_alloc_event(struct acpi_resource *ares, + void *context) +{ + struct acpi_gpio_chip *acpi_gpio = context; + struct gpio_chip *chip = acpi_gpio->chip; + struct acpi_resource_gpio *agpio; + acpi_handle handle, evt_handle; + struct acpi_gpio_event *event; + irq_handler_t handler = NULL; + struct gpio_desc *desc; + unsigned int pin; + int ret, irq; + + if (!acpi_gpio_get_irq_resource(ares, &agpio)) + return AE_OK; + + handle = ACPI_HANDLE(chip->parent); + pin = agpio->pin_table[0]; + + if (pin <= 255) { + char ev_name[8]; + sprintf(ev_name, "_%c%02X", + agpio->triggering == ACPI_EDGE_SENSITIVE ? 'E' : 'L', + pin); + if (ACPI_SUCCESS(acpi_get_handle(handle, ev_name, &evt_handle))) + handler = acpi_gpio_irq_handler; + } + if (!handler) { + if (ACPI_SUCCESS(acpi_get_handle(handle, "_EVT", &evt_handle))) + handler = acpi_gpio_irq_handler_evt; + } + if (!handler) + return AE_OK; + + if (acpi_gpio_in_ignore_list(ACPI_GPIO_IGNORE_INTERRUPT, dev_name(chip->parent), pin)) { + dev_info(chip->parent, "Ignoring interrupt on pin %u\n", pin); + return AE_OK; + } + + desc = acpi_request_own_gpiod(chip, agpio, 0, "ACPI:Event"); + if (IS_ERR(desc)) { + dev_err(chip->parent, + "Failed to request GPIO for pin 0x%04X, err %ld\n", + pin, PTR_ERR(desc)); + return AE_OK; + } + + ret = gpiochip_lock_as_irq(chip, pin); + if (ret) { + dev_err(chip->parent, + "Failed to lock GPIO pin 0x%04X as interrupt, err %d\n", + pin, ret); + goto fail_free_desc; + } + + irq = gpiod_to_irq(desc); + if (irq < 0) { + dev_err(chip->parent, + "Failed to translate GPIO pin 0x%04X to IRQ, err %d\n", + pin, irq); + goto fail_unlock_irq; + } + + event = kzalloc(sizeof(*event), GFP_KERNEL); + if (!event) + goto fail_unlock_irq; + + event->irqflags = IRQF_ONESHOT; + if (agpio->triggering == ACPI_LEVEL_SENSITIVE) { + if (agpio->polarity == ACPI_ACTIVE_HIGH) + event->irqflags |= IRQF_TRIGGER_HIGH; + else + event->irqflags |= IRQF_TRIGGER_LOW; + } else { + switch (agpio->polarity) { + case ACPI_ACTIVE_HIGH: + event->irqflags |= IRQF_TRIGGER_RISING; + break; + case ACPI_ACTIVE_LOW: + event->irqflags |= IRQF_TRIGGER_FALLING; + break; + default: + event->irqflags |= IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING; + break; + } + } + + event->handle = evt_handle; + event->handler = handler; + event->irq = irq; + event->irq_is_wake = acpi_gpio_irq_is_wake(chip->parent, agpio); + event->pin = pin; + event->desc = desc; + + list_add_tail(&event->node, &acpi_gpio->events); + + return AE_OK; + +fail_unlock_irq: + gpiochip_unlock_as_irq(chip, pin); +fail_free_desc: + gpiochip_free_own_desc(desc); + + return AE_OK; +} + +/** + * acpi_gpiochip_request_interrupts() - Register isr for gpio chip ACPI events + * @chip: GPIO chip + * + * ACPI5 platforms can use GPIO signaled ACPI events. These GPIO interrupts are + * handled by ACPI event methods which need to be called from the GPIO + * chip's interrupt handler. acpi_gpiochip_request_interrupts() finds out which + * GPIO pins have ACPI event methods and assigns interrupt handlers that calls + * the ACPI event methods for those pins. + */ +void acpi_gpiochip_request_interrupts(struct gpio_chip *chip) +{ + struct acpi_gpio_chip *acpi_gpio; + acpi_handle handle; + acpi_status status; + + if (!chip->parent || !chip->to_irq) + return; + + handle = ACPI_HANDLE(chip->parent); + if (!handle) + return; + + status = acpi_get_data(handle, acpi_gpio_chip_dh, (void **)&acpi_gpio); + if (ACPI_FAILURE(status)) + return; + + if (acpi_quirk_skip_gpio_event_handlers()) + return; + + acpi_walk_resources(handle, METHOD_NAME__AEI, + acpi_gpiochip_alloc_event, acpi_gpio); + + if (acpi_gpio_add_to_deferred_list(&acpi_gpio->deferred_req_irqs_list_entry)) + return; + + acpi_gpiochip_request_irqs(acpi_gpio); +} +EXPORT_SYMBOL_GPL(acpi_gpiochip_request_interrupts); + +/** + * acpi_gpiochip_free_interrupts() - Free GPIO ACPI event interrupts. + * @chip: GPIO chip + * + * Free interrupts associated with GPIO ACPI event method for the given + * GPIO chip. + */ +void acpi_gpiochip_free_interrupts(struct gpio_chip *chip) +{ + struct acpi_gpio_chip *acpi_gpio; + struct acpi_gpio_event *event, *ep; + acpi_handle handle; + acpi_status status; + + if (!chip->parent || !chip->to_irq) + return; + + handle = ACPI_HANDLE(chip->parent); + if (!handle) + return; + + status = acpi_get_data(handle, acpi_gpio_chip_dh, (void **)&acpi_gpio); + if (ACPI_FAILURE(status)) + return; + + acpi_gpio_remove_from_deferred_list(&acpi_gpio->deferred_req_irqs_list_entry); + + list_for_each_entry_safe_reverse(event, ep, &acpi_gpio->events, node) { + if (event->irq_requested) { + if (event->irq_is_wake) + disable_irq_wake(event->irq); + + free_irq(event->irq, event); + } + + gpiochip_unlock_as_irq(chip, event->pin); + gpiochip_free_own_desc(event->desc); + list_del(&event->node); + kfree(event); + } +} +EXPORT_SYMBOL_GPL(acpi_gpiochip_free_interrupts); + +void __init acpi_gpio_process_deferred_list(struct list_head *list) +{ + struct acpi_gpio_chip *acpi_gpio, *tmp; + + list_for_each_entry_safe(acpi_gpio, tmp, list, deferred_req_irqs_list_entry) + acpi_gpiochip_request_irqs(acpi_gpio); +} + +int acpi_dev_add_driver_gpios(struct acpi_device *adev, + const struct acpi_gpio_mapping *gpios) +{ + if (adev && gpios) { + adev->driver_gpios = gpios; + return 0; + } + return -EINVAL; +} +EXPORT_SYMBOL_GPL(acpi_dev_add_driver_gpios); + +void acpi_dev_remove_driver_gpios(struct acpi_device *adev) +{ + if (adev) + adev->driver_gpios = NULL; +} +EXPORT_SYMBOL_GPL(acpi_dev_remove_driver_gpios); + +static void acpi_dev_release_driver_gpios(void *adev) +{ + acpi_dev_remove_driver_gpios(adev); +} + +int devm_acpi_dev_add_driver_gpios(struct device *dev, + const struct acpi_gpio_mapping *gpios) +{ + struct acpi_device *adev = ACPI_COMPANION(dev); + int ret; + + ret = acpi_dev_add_driver_gpios(adev, gpios); + if (ret) + return ret; + + return devm_add_action_or_reset(dev, acpi_dev_release_driver_gpios, adev); +} +EXPORT_SYMBOL_GPL(devm_acpi_dev_add_driver_gpios); + +static bool acpi_get_driver_gpio_data(struct acpi_device *adev, + const char *name, int index, + struct fwnode_reference_args *args, + unsigned int *quirks) +{ + const struct acpi_gpio_mapping *gm; + + if (!adev || !adev->driver_gpios) + return false; + + for (gm = adev->driver_gpios; gm->name; gm++) + if (!strcmp(name, gm->name) && gm->data && index < gm->size) { + const struct acpi_gpio_params *par = gm->data + index; + + args->fwnode = acpi_fwnode_handle(adev); + args->args[0] = par->crs_entry_index; + args->args[1] = par->line_index; + args->args[2] = par->active_low; + args->nargs = 3; + + *quirks = gm->quirks; + return true; + } + + return false; +} + +static int +__acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, enum gpiod_flags update) +{ + const enum gpiod_flags mask = + GPIOD_FLAGS_BIT_DIR_SET | GPIOD_FLAGS_BIT_DIR_OUT | + GPIOD_FLAGS_BIT_DIR_VAL; + int ret = 0; + + /* + * Check if the BIOS has IoRestriction with explicitly set direction + * and update @flags accordingly. Otherwise use whatever caller asked + * for. + */ + if (update & GPIOD_FLAGS_BIT_DIR_SET) { + enum gpiod_flags diff = *flags ^ update; + + /* + * Check if caller supplied incompatible GPIO initialization + * flags. + * + * Return %-EINVAL to notify that firmware has different + * settings and we are going to use them. + */ + if (((*flags & GPIOD_FLAGS_BIT_DIR_SET) && (diff & GPIOD_FLAGS_BIT_DIR_OUT)) || + ((*flags & GPIOD_FLAGS_BIT_DIR_OUT) && (diff & GPIOD_FLAGS_BIT_DIR_VAL))) + ret = -EINVAL; + *flags = (*flags & ~mask) | (update & mask); + } + return ret; +} + +static int acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, + struct acpi_gpio_info *info) +{ + struct device *dev = &info->adev->dev; + enum gpiod_flags old = *flags; + int ret; + + ret = __acpi_gpio_update_gpiod_flags(&old, info->flags); + if (info->quirks & ACPI_GPIO_QUIRK_NO_IO_RESTRICTION) { + if (ret) + dev_warn(dev, FW_BUG "GPIO not in correct mode, fixing\n"); + } else { + if (ret) + dev_dbg(dev, "Override GPIO initialization flags\n"); + *flags = old; + } + + return ret; +} + +static int acpi_gpio_update_gpiod_lookup_flags(unsigned long *lookupflags, + struct acpi_gpio_info *info) +{ + switch (info->pin_config) { + case ACPI_PIN_CONFIG_PULLUP: + *lookupflags |= GPIO_PULL_UP; + break; + case ACPI_PIN_CONFIG_PULLDOWN: + *lookupflags |= GPIO_PULL_DOWN; + break; + case ACPI_PIN_CONFIG_NOPULL: + *lookupflags |= GPIO_PULL_DISABLE; + break; + default: + break; + } + + if (info->polarity == GPIO_ACTIVE_LOW) + *lookupflags |= GPIO_ACTIVE_LOW; + + return 0; +} + +struct acpi_gpio_lookup { + struct acpi_gpio_info info; + int index; + u16 pin_index; + bool active_low; + struct gpio_desc *desc; + int n; +}; + +static int acpi_populate_gpio_lookup(struct acpi_resource *ares, void *data) +{ + struct acpi_gpio_lookup *lookup = data; + + if (ares->type != ACPI_RESOURCE_TYPE_GPIO) + return 1; + + if (!lookup->desc) { + const struct acpi_resource_gpio *agpio = &ares->data.gpio; + bool gpioint = agpio->connection_type == ACPI_RESOURCE_GPIO_TYPE_INT; + struct gpio_desc *desc; + u16 pin_index; + + if (lookup->info.quirks & ACPI_GPIO_QUIRK_ONLY_GPIOIO && gpioint) + lookup->index++; + + if (lookup->n++ != lookup->index) + return 1; + + pin_index = lookup->pin_index; + if (pin_index >= agpio->pin_table_length) + return 1; + + if (lookup->info.quirks & ACPI_GPIO_QUIRK_ABSOLUTE_NUMBER) + desc = gpio_to_desc(agpio->pin_table[pin_index]); + else + desc = acpi_get_gpiod(agpio->resource_source.string_ptr, + agpio->pin_table[pin_index]); + lookup->desc = desc; + lookup->info.pin_config = agpio->pin_config; + lookup->info.debounce = agpio->debounce_timeout; + lookup->info.gpioint = gpioint; + lookup->info.wake_capable = acpi_gpio_irq_is_wake(&lookup->info.adev->dev, agpio); + + /* + * Polarity and triggering are only specified for GpioInt + * resource. + * Note: we expect here: + * - ACPI_ACTIVE_LOW == GPIO_ACTIVE_LOW + * - ACPI_ACTIVE_HIGH == GPIO_ACTIVE_HIGH + */ + if (lookup->info.gpioint) { + lookup->info.polarity = agpio->polarity; + lookup->info.triggering = agpio->triggering; + } else { + lookup->info.polarity = lookup->active_low; + } + + lookup->info.flags = acpi_gpio_to_gpiod_flags(agpio, lookup->info.polarity); + } + + return 1; +} + +static int acpi_gpio_resource_lookup(struct acpi_gpio_lookup *lookup, + struct acpi_gpio_info *info) +{ + struct acpi_device *adev = lookup->info.adev; + struct list_head res_list; + int ret; + + INIT_LIST_HEAD(&res_list); + + ret = acpi_dev_get_resources(adev, &res_list, + acpi_populate_gpio_lookup, + lookup); + if (ret < 0) + return ret; + + acpi_dev_free_resource_list(&res_list); + + if (!lookup->desc) + return -ENOENT; + + if (info) + *info = lookup->info; + return 0; +} + +static int acpi_gpio_property_lookup(struct fwnode_handle *fwnode, + const char *propname, int index, + struct acpi_gpio_lookup *lookup) +{ + struct fwnode_reference_args args; + unsigned int quirks = 0; + int ret; + + memset(&args, 0, sizeof(args)); + ret = __acpi_node_get_property_reference(fwnode, propname, index, 3, + &args); + if (ret) { + struct acpi_device *adev; + + adev = to_acpi_device_node(fwnode); + if (!acpi_get_driver_gpio_data(adev, propname, index, &args, &quirks)) + return ret; + } + /* + * The property was found and resolved, so need to lookup the GPIO based + * on returned args. + */ + if (!to_acpi_device_node(args.fwnode)) + return -EINVAL; + if (args.nargs != 3) + return -EPROTO; + + lookup->index = args.args[0]; + lookup->pin_index = args.args[1]; + lookup->active_low = !!args.args[2]; + + lookup->info.adev = to_acpi_device_node(args.fwnode); + lookup->info.quirks = quirks; + + return 0; +} + +/** + * acpi_get_gpiod_by_index() - get a GPIO descriptor from device resources + * @adev: pointer to a ACPI device to get GPIO from + * @propname: Property name of the GPIO (optional) + * @index: index of GpioIo/GpioInt resource (starting from %0) + * @info: info pointer to fill in (optional) + * + * Function goes through ACPI resources for @adev and based on @index looks + * up a GpioIo/GpioInt resource, translates it to the Linux GPIO descriptor, + * and returns it. @index matches GpioIo/GpioInt resources only so if there + * are total %3 GPIO resources, the index goes from %0 to %2. + * + * If @propname is specified the GPIO is looked using device property. In + * that case @index is used to select the GPIO entry in the property value + * (in case of multiple). + * + * Returns: + * GPIO descriptor to use with Linux generic GPIO API. + * If the GPIO cannot be translated or there is an error an ERR_PTR is + * returned. + * + * Note: if the GPIO resource has multiple entries in the pin list, this + * function only returns the first. + */ +static struct gpio_desc *acpi_get_gpiod_by_index(struct acpi_device *adev, + const char *propname, + int index, + struct acpi_gpio_info *info) +{ + struct acpi_gpio_lookup lookup; + int ret; + + memset(&lookup, 0, sizeof(lookup)); + lookup.index = index; + + if (propname) { + dev_dbg(&adev->dev, "GPIO: looking up %s\n", propname); + + ret = acpi_gpio_property_lookup(acpi_fwnode_handle(adev), + propname, index, &lookup); + if (ret) + return ERR_PTR(ret); + + dev_dbg(&adev->dev, "GPIO: _DSD returned %s %d %u %u\n", + dev_name(&lookup.info.adev->dev), lookup.index, + lookup.pin_index, lookup.active_low); + } else { + dev_dbg(&adev->dev, "GPIO: looking up %d in _CRS\n", index); + lookup.info.adev = adev; + } + + ret = acpi_gpio_resource_lookup(&lookup, info); + return ret ? ERR_PTR(ret) : lookup.desc; +} + +/** + * acpi_get_gpiod_from_data() - get a GPIO descriptor from ACPI data node + * @fwnode: pointer to an ACPI firmware node to get the GPIO information from + * @propname: Property name of the GPIO + * @index: index of GpioIo/GpioInt resource (starting from %0) + * @info: info pointer to fill in (optional) + * + * This function uses the property-based GPIO lookup to get to the GPIO + * resource with the relevant information from a data-only ACPI firmware node + * and uses that to obtain the GPIO descriptor to return. + * + * Returns: + * GPIO descriptor to use with Linux generic GPIO API. + * If the GPIO cannot be translated or there is an error an ERR_PTR is + * returned. + */ +static struct gpio_desc *acpi_get_gpiod_from_data(struct fwnode_handle *fwnode, + const char *propname, + int index, + struct acpi_gpio_info *info) +{ + struct acpi_gpio_lookup lookup; + int ret; + + if (!is_acpi_data_node(fwnode)) + return ERR_PTR(-ENODEV); + + if (!propname) + return ERR_PTR(-EINVAL); + + memset(&lookup, 0, sizeof(lookup)); + lookup.index = index; + + ret = acpi_gpio_property_lookup(fwnode, propname, index, &lookup); + if (ret) + return ERR_PTR(ret); + + ret = acpi_gpio_resource_lookup(&lookup, info); + return ret ? ERR_PTR(ret) : lookup.desc; +} + +static bool acpi_can_fallback_to_crs(struct acpi_device *adev, + const char *con_id) +{ + /* If there is no ACPI device, there is no _CRS to fall back to */ + if (!adev) + return false; + + /* Never allow fallback if the device has properties */ + if (acpi_dev_has_props(adev) || adev->driver_gpios) + return false; + + return con_id == NULL; +} + +static struct gpio_desc * +__acpi_find_gpio(struct fwnode_handle *fwnode, const char *con_id, unsigned int idx, + bool can_fallback, struct acpi_gpio_info *info) +{ + struct acpi_device *adev = to_acpi_device_node(fwnode); + struct gpio_desc *desc; + char propname[32]; + + /* Try first from _DSD */ + for_each_gpio_property_name(propname, con_id) { + if (adev) + desc = acpi_get_gpiod_by_index(adev, + propname, idx, info); + else + desc = acpi_get_gpiod_from_data(fwnode, + propname, idx, info); + if (PTR_ERR(desc) == -EPROBE_DEFER) + return ERR_CAST(desc); + + if (!IS_ERR(desc)) + return desc; + } + + /* Then from plain _CRS GPIOs */ + if (can_fallback) + return acpi_get_gpiod_by_index(adev, NULL, idx, info); + + return ERR_PTR(-ENOENT); +} + +struct gpio_desc *acpi_find_gpio(struct fwnode_handle *fwnode, + const char *con_id, + unsigned int idx, + enum gpiod_flags *dflags, + unsigned long *lookupflags) +{ + struct acpi_device *adev = to_acpi_device_node(fwnode); + bool can_fallback = acpi_can_fallback_to_crs(adev, con_id); + struct acpi_gpio_info info; + struct gpio_desc *desc; + + desc = __acpi_find_gpio(fwnode, con_id, idx, can_fallback, &info); + if (IS_ERR(desc)) + return desc; + + if (info.gpioint && + (*dflags == GPIOD_OUT_LOW || *dflags == GPIOD_OUT_HIGH)) { + dev_dbg(&adev->dev, "refusing GpioInt() entry when doing GPIOD_OUT_* lookup\n"); + return ERR_PTR(-ENOENT); + } + + acpi_gpio_update_gpiod_flags(dflags, &info); + acpi_gpio_update_gpiod_lookup_flags(lookupflags, &info); + return desc; +} + +/** + * acpi_dev_gpio_irq_wake_get_by() - Find GpioInt and translate it to Linux IRQ number + * @adev: pointer to a ACPI device to get IRQ from + * @con_id: optional name of GpioInt resource + * @index: index of GpioInt resource (starting from %0) + * @wake_capable: Set to true if the IRQ is wake capable + * + * If the device has one or more GpioInt resources, this function can be + * used to translate from the GPIO offset in the resource to the Linux IRQ + * number. + * + * The function is idempotent, though each time it runs it will configure GPIO + * pin direction according to the flags in GpioInt resource. + * + * The function takes optional @con_id parameter. If the resource has + * a @con_id in a property, then only those will be taken into account. + * + * The GPIO is considered wake capable if the GpioInt resource specifies + * SharedAndWake or ExclusiveAndWake. + * + * Returns: + * Linux IRQ number (> 0) on success, negative errno on failure. + */ +int acpi_dev_gpio_irq_wake_get_by(struct acpi_device *adev, const char *con_id, int index, + bool *wake_capable) +{ + struct fwnode_handle *fwnode = acpi_fwnode_handle(adev); + int idx, i; + unsigned int irq_flags; + int ret; + + for (i = 0, idx = 0; idx <= index; i++) { + struct acpi_gpio_info info; + struct gpio_desc *desc; + + /* Ignore -EPROBE_DEFER, it only matters if idx matches */ + desc = __acpi_find_gpio(fwnode, con_id, i, true, &info); + if (IS_ERR(desc) && PTR_ERR(desc) != -EPROBE_DEFER) + return PTR_ERR(desc); + + if (info.gpioint && idx++ == index) { + unsigned long lflags = GPIO_LOOKUP_FLAGS_DEFAULT; + enum gpiod_flags dflags = GPIOD_ASIS; + char label[32]; + int irq; + + if (IS_ERR(desc)) + return PTR_ERR(desc); + + irq = gpiod_to_irq(desc); + if (irq < 0) + return irq; + + acpi_gpio_update_gpiod_flags(&dflags, &info); + acpi_gpio_update_gpiod_lookup_flags(&lflags, &info); + + snprintf(label, sizeof(label), "%pfwP GpioInt(%d)", fwnode, index); + ret = gpiod_set_consumer_name(desc, con_id ?: label); + if (ret) + return ret; + + ret = gpiod_configure_flags(desc, label, lflags, dflags); + if (ret < 0) + return ret; + + /* ACPI uses hundredths of milliseconds units */ + ret = gpio_set_debounce_timeout(desc, info.debounce * 10); + if (ret) + return ret; + + irq_flags = acpi_dev_get_irq_type(info.triggering, + info.polarity); + + /* + * If the IRQ is not already in use then set type + * if specified and different than the current one. + */ + if (can_request_irq(irq, irq_flags)) { + if (irq_flags != IRQ_TYPE_NONE && + irq_flags != irq_get_trigger_type(irq)) + irq_set_irq_type(irq, irq_flags); + } else { + dev_dbg(&adev->dev, "IRQ %d already in use\n", irq); + } + + /* avoid suspend issues with GPIOs when systems are using S3 */ + if (wake_capable && acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0) + *wake_capable = info.wake_capable; + + return irq; + } + + } + return -ENOENT; +} +EXPORT_SYMBOL_GPL(acpi_dev_gpio_irq_wake_get_by); + +static acpi_status +acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address, + u32 bits, u64 *value, void *handler_context, + void *region_context) +{ + struct acpi_gpio_chip *achip = region_context; + struct gpio_chip *chip = achip->chip; + struct acpi_resource_gpio *agpio; + struct acpi_resource *ares; + u16 pin_index = address; + acpi_status status; + int length; + int i; + + status = acpi_buffer_to_resource(achip->conn_info.connection, + achip->conn_info.length, &ares); + if (ACPI_FAILURE(status)) + return status; + + if (WARN_ON(ares->type != ACPI_RESOURCE_TYPE_GPIO)) { + ACPI_FREE(ares); + return AE_BAD_PARAMETER; + } + + agpio = &ares->data.gpio; + + if (WARN_ON(agpio->io_restriction == ACPI_IO_RESTRICT_INPUT && + function == ACPI_WRITE)) { + ACPI_FREE(ares); + return AE_BAD_PARAMETER; + } + + length = min_t(u16, agpio->pin_table_length, pin_index + bits); + for (i = pin_index; i < length; ++i) { + unsigned int pin = agpio->pin_table[i]; + struct acpi_gpio_connection *conn; + struct gpio_desc *desc; + bool found; + + mutex_lock(&achip->conn_lock); + + found = false; + list_for_each_entry(conn, &achip->conns, node) { + if (conn->pin == pin) { + found = true; + desc = conn->desc; + break; + } + } + + /* + * The same GPIO can be shared between operation region and + * event but only if the access here is ACPI_READ. In that + * case we "borrow" the event GPIO instead. + */ + if (!found && agpio->shareable == ACPI_SHARED && + function == ACPI_READ) { + struct acpi_gpio_event *event; + + list_for_each_entry(event, &achip->events, node) { + if (event->pin == pin) { + desc = event->desc; + found = true; + break; + } + } + } + + if (!found) { + desc = acpi_request_own_gpiod(chip, agpio, i, "ACPI:OpRegion"); + if (IS_ERR(desc)) { + mutex_unlock(&achip->conn_lock); + status = AE_ERROR; + goto out; + } + + conn = kzalloc(sizeof(*conn), GFP_KERNEL); + if (!conn) { + gpiochip_free_own_desc(desc); + mutex_unlock(&achip->conn_lock); + status = AE_NO_MEMORY; + goto out; + } + + conn->pin = pin; + conn->desc = desc; + list_add_tail(&conn->node, &achip->conns); + } + + mutex_unlock(&achip->conn_lock); + + if (function == ACPI_WRITE) + gpiod_set_raw_value_cansleep(desc, !!(*value & BIT(i))); + else + *value |= (u64)gpiod_get_raw_value_cansleep(desc) << i; + } + +out: + ACPI_FREE(ares); + return status; +} + +static void acpi_gpiochip_request_regions(struct acpi_gpio_chip *achip) +{ + struct gpio_chip *chip = achip->chip; + acpi_handle handle = ACPI_HANDLE(chip->parent); + acpi_status status; + + INIT_LIST_HEAD(&achip->conns); + mutex_init(&achip->conn_lock); + status = acpi_install_address_space_handler(handle, ACPI_ADR_SPACE_GPIO, + acpi_gpio_adr_space_handler, + NULL, achip); + if (ACPI_FAILURE(status)) + dev_err(chip->parent, + "Failed to install GPIO OpRegion handler\n"); +} + +static void acpi_gpiochip_free_regions(struct acpi_gpio_chip *achip) +{ + struct gpio_chip *chip = achip->chip; + acpi_handle handle = ACPI_HANDLE(chip->parent); + struct acpi_gpio_connection *conn, *tmp; + acpi_status status; + + status = acpi_remove_address_space_handler(handle, ACPI_ADR_SPACE_GPIO, + acpi_gpio_adr_space_handler); + if (ACPI_FAILURE(status)) { + dev_err(chip->parent, + "Failed to remove GPIO OpRegion handler\n"); + return; + } + + list_for_each_entry_safe_reverse(conn, tmp, &achip->conns, node) { + gpiochip_free_own_desc(conn->desc); + list_del(&conn->node); + kfree(conn); + } +} + +static struct gpio_desc * +acpi_gpiochip_parse_own_gpio(struct acpi_gpio_chip *achip, + struct fwnode_handle *fwnode, + const char **name, + unsigned long *lflags, + enum gpiod_flags *dflags) +{ + struct gpio_chip *chip = achip->chip; + struct gpio_desc *desc; + u32 gpios[2]; + int ret; + + *lflags = GPIO_LOOKUP_FLAGS_DEFAULT; + *dflags = GPIOD_ASIS; + *name = NULL; + + ret = fwnode_property_read_u32_array(fwnode, "gpios", gpios, + ARRAY_SIZE(gpios)); + if (ret < 0) + return ERR_PTR(ret); + + desc = gpiochip_get_desc(chip, gpios[0]); + if (IS_ERR(desc)) + return desc; + + if (gpios[1]) + *lflags |= GPIO_ACTIVE_LOW; + + if (fwnode_property_present(fwnode, "input")) + *dflags |= GPIOD_IN; + else if (fwnode_property_present(fwnode, "output-low")) + *dflags |= GPIOD_OUT_LOW; + else if (fwnode_property_present(fwnode, "output-high")) + *dflags |= GPIOD_OUT_HIGH; + else + return ERR_PTR(-EINVAL); + + fwnode_property_read_string(fwnode, "line-name", name); + + return desc; +} + +static void acpi_gpiochip_scan_gpios(struct acpi_gpio_chip *achip) +{ + struct gpio_chip *chip = achip->chip; + struct fwnode_handle *fwnode; + + device_for_each_child_node(chip->parent, fwnode) { + unsigned long lflags; + enum gpiod_flags dflags; + struct gpio_desc *desc; + const char *name; + int ret; + + if (!fwnode_property_present(fwnode, "gpio-hog")) + continue; + + desc = acpi_gpiochip_parse_own_gpio(achip, fwnode, &name, + &lflags, &dflags); + if (IS_ERR(desc)) + continue; + + ret = gpiod_hog(desc, name, lflags, dflags); + if (ret) { + dev_err(chip->parent, "Failed to hog GPIO\n"); + fwnode_handle_put(fwnode); + return; + } + } +} + +void acpi_gpiochip_add(struct gpio_chip *chip) +{ + struct acpi_gpio_chip *acpi_gpio; + struct acpi_device *adev; + acpi_status status; + + if (!chip || !chip->parent) + return; + + adev = ACPI_COMPANION(chip->parent); + if (!adev) + return; + + acpi_gpio = kzalloc(sizeof(*acpi_gpio), GFP_KERNEL); + if (!acpi_gpio) { + dev_err(chip->parent, + "Failed to allocate memory for ACPI GPIO chip\n"); + return; + } + + acpi_gpio->chip = chip; + INIT_LIST_HEAD(&acpi_gpio->events); + INIT_LIST_HEAD(&acpi_gpio->deferred_req_irqs_list_entry); + + status = acpi_attach_data(adev->handle, acpi_gpio_chip_dh, acpi_gpio); + if (ACPI_FAILURE(status)) { + dev_err(chip->parent, "Failed to attach ACPI GPIO chip\n"); + kfree(acpi_gpio); + return; + } + + acpi_gpiochip_request_regions(acpi_gpio); + acpi_gpiochip_scan_gpios(acpi_gpio); + acpi_dev_clear_dependencies(adev); +} + +void acpi_gpiochip_remove(struct gpio_chip *chip) +{ + struct acpi_gpio_chip *acpi_gpio; + acpi_handle handle; + acpi_status status; + + if (!chip || !chip->parent) + return; + + handle = ACPI_HANDLE(chip->parent); + if (!handle) + return; + + status = acpi_get_data(handle, acpi_gpio_chip_dh, (void **)&acpi_gpio); + if (ACPI_FAILURE(status)) { + dev_warn(chip->parent, "Failed to retrieve ACPI GPIO chip\n"); + return; + } + + acpi_gpiochip_free_regions(acpi_gpio); + + acpi_detach_data(handle, acpi_gpio_chip_dh); + kfree(acpi_gpio); +} + +static int acpi_gpio_package_count(const union acpi_object *obj) +{ + const union acpi_object *element = obj->package.elements; + const union acpi_object *end = element + obj->package.count; + unsigned int count = 0; + + while (element < end) { + switch (element->type) { + case ACPI_TYPE_LOCAL_REFERENCE: + element += 3; + fallthrough; + case ACPI_TYPE_INTEGER: + element++; + count++; + break; + + default: + return -EPROTO; + } + } + + return count; +} + +static int acpi_find_gpio_count(struct acpi_resource *ares, void *data) +{ + unsigned int *count = data; + + if (ares->type == ACPI_RESOURCE_TYPE_GPIO) + *count += ares->data.gpio.pin_table_length; + + return 1; +} + +/** + * acpi_gpio_count - count the GPIOs associated with a firmware node / function + * @fwnode: firmware node of the GPIO consumer + * @con_id: function within the GPIO consumer + * + * Returns: + * The number of GPIOs associated with a firmware node / function or %-ENOENT, + * if no GPIO has been assigned to the requested function. + */ +int acpi_gpio_count(const struct fwnode_handle *fwnode, const char *con_id) +{ + struct acpi_device *adev = to_acpi_device_node(fwnode); + const union acpi_object *obj; + const struct acpi_gpio_mapping *gm; + int count = -ENOENT; + int ret; + char propname[32]; + + /* Try first from _DSD */ + for_each_gpio_property_name(propname, con_id) { + ret = acpi_dev_get_property(adev, propname, ACPI_TYPE_ANY, &obj); + if (ret == 0) { + if (obj->type == ACPI_TYPE_LOCAL_REFERENCE) + count = 1; + else if (obj->type == ACPI_TYPE_PACKAGE) + count = acpi_gpio_package_count(obj); + } else if (adev->driver_gpios) { + for (gm = adev->driver_gpios; gm->name; gm++) + if (strcmp(propname, gm->name) == 0) { + count = gm->size; + break; + } + } + if (count > 0) + break; + } + + /* Then from plain _CRS GPIOs */ + if (count < 0) { + struct list_head resource_list; + unsigned int crs_count = 0; + + if (!acpi_can_fallback_to_crs(adev, con_id)) + return count; + + INIT_LIST_HEAD(&resource_list); + acpi_dev_get_resources(adev, &resource_list, + acpi_find_gpio_count, &crs_count); + acpi_dev_free_resource_list(&resource_list); + if (crs_count > 0) + count = crs_count; + } + return count ? count : -ENOENT; +} --- /dev/null +++ b/drivers/gpio/gpiolib-acpi-quirks.c @@ -0,0 +1,363 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ACPI quirks for GPIO ACPI helpers + * + * Author: Hans de Goede hdegoede@redhat.com + */ + +#include <linux/dmi.h> +#include <linux/kstrtox.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/printk.h> +#include <linux/string.h> +#include <linux/types.h> + +#include "gpiolib-acpi.h" + +static int run_edge_events_on_boot = -1; +module_param(run_edge_events_on_boot, int, 0444); +MODULE_PARM_DESC(run_edge_events_on_boot, + "Run edge _AEI event-handlers at boot: 0=no, 1=yes, -1=auto"); + +static char *ignore_wake; +module_param(ignore_wake, charp, 0444); +MODULE_PARM_DESC(ignore_wake, + "controller@pin combos on which to ignore the ACPI wake flag " + "ignore_wake=controller@pin[,controller@pin[,...]]"); + +static char *ignore_interrupt; +module_param(ignore_interrupt, charp, 0444); +MODULE_PARM_DESC(ignore_interrupt, + "controller@pin combos on which to ignore interrupt " + "ignore_interrupt=controller@pin[,controller@pin[,...]]"); + +/* + * For GPIO chips which call acpi_gpiochip_request_interrupts() before late_init + * (so builtin drivers) we register the ACPI GpioInt IRQ handlers from a + * late_initcall_sync() handler, so that other builtin drivers can register their + * OpRegions before the event handlers can run. This list contains GPIO chips + * for which the acpi_gpiochip_request_irqs() call has been deferred. + */ +static DEFINE_MUTEX(acpi_gpio_deferred_req_irqs_lock); +static LIST_HEAD(acpi_gpio_deferred_req_irqs_list); +static bool acpi_gpio_deferred_req_irqs_done; + +bool acpi_gpio_add_to_deferred_list(struct list_head *list) +{ + bool defer; + + mutex_lock(&acpi_gpio_deferred_req_irqs_lock); + defer = !acpi_gpio_deferred_req_irqs_done; + if (defer) + list_add(list, &acpi_gpio_deferred_req_irqs_list); + mutex_unlock(&acpi_gpio_deferred_req_irqs_lock); + + return defer; +} + +void acpi_gpio_remove_from_deferred_list(struct list_head *list) +{ + mutex_lock(&acpi_gpio_deferred_req_irqs_lock); + if (!list_empty(list)) + list_del_init(list); + mutex_unlock(&acpi_gpio_deferred_req_irqs_lock); +} + +int acpi_gpio_need_run_edge_events_on_boot(void) +{ + return run_edge_events_on_boot; +} + +bool acpi_gpio_in_ignore_list(enum acpi_gpio_ignore_list list, + const char *controller_in, unsigned int pin_in) +{ + const char *ignore_list, *controller, *pin_str; + unsigned int pin; + char *endp; + int len; + + switch (list) { + case ACPI_GPIO_IGNORE_WAKE: + ignore_list = ignore_wake; + break; + case ACPI_GPIO_IGNORE_INTERRUPT: + ignore_list = ignore_interrupt; + break; + default: + return false; + } + + controller = ignore_list; + while (controller) { + pin_str = strchr(controller, '@'); + if (!pin_str) + goto err; + + len = pin_str - controller; + if (len == strlen(controller_in) && + strncmp(controller, controller_in, len) == 0) { + pin = simple_strtoul(pin_str + 1, &endp, 10); + if (*endp != 0 && *endp != ',') + goto err; + + if (pin == pin_in) + return true; + } + + controller = strchr(controller, ','); + if (controller) + controller++; + } + + return false; +err: + pr_err_once("Error: Invalid value for gpiolib_acpi.ignore_...: %s\n", ignore_list); + return false; +} + +/* Run deferred acpi_gpiochip_request_irqs() */ +static int __init acpi_gpio_handle_deferred_request_irqs(void) +{ + mutex_lock(&acpi_gpio_deferred_req_irqs_lock); + acpi_gpio_process_deferred_list(&acpi_gpio_deferred_req_irqs_list); + acpi_gpio_deferred_req_irqs_done = true; + mutex_unlock(&acpi_gpio_deferred_req_irqs_lock); + + return 0; +} +/* We must use _sync so that this runs after the first deferred_probe run */ +late_initcall_sync(acpi_gpio_handle_deferred_request_irqs); + +struct acpi_gpiolib_dmi_quirk { + bool no_edge_events_on_boot; + char *ignore_wake; + char *ignore_interrupt; +}; + +static const struct dmi_system_id gpiolib_acpi_quirks[] __initconst = { + { + /* + * The Minix Neo Z83-4 has a micro-USB-B id-pin handler for + * a non existing micro-USB-B connector which puts the HDMI + * DDC pins in GPIO mode, breaking HDMI support. + */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "MINIX"), + DMI_MATCH(DMI_PRODUCT_NAME, "Z83-4"), + }, + .driver_data = &(struct acpi_gpiolib_dmi_quirk) { + .no_edge_events_on_boot = true, + }, + }, + { + /* + * The Terra Pad 1061 has a micro-USB-B id-pin handler, which + * instead of controlling the actual micro-USB-B turns the 5V + * boost for its USB-A connector off. The actual micro-USB-B + * connector is wired for charging only. + */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Wortmann_AG"), + DMI_MATCH(DMI_PRODUCT_NAME, "TERRA_PAD_1061"), + }, + .driver_data = &(struct acpi_gpiolib_dmi_quirk) { + .no_edge_events_on_boot = true, + }, + }, + { + /* + * The Dell Venue 10 Pro 5055, with Bay Trail SoC + TI PMIC uses an + * external embedded-controller connected via I2C + an ACPI GPIO + * event handler on INT33FFC:02 pin 12, causing spurious wakeups. + */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Venue 10 Pro 5055"), + }, + .driver_data = &(struct acpi_gpiolib_dmi_quirk) { + .ignore_wake = "INT33FC:02@12", + }, + }, + { + /* + * HP X2 10 models with Cherry Trail SoC + TI PMIC use an + * external embedded-controller connected via I2C + an ACPI GPIO + * event handler on INT33FF:01 pin 0, causing spurious wakeups. + * When suspending by closing the LID, the power to the USB + * keyboard is turned off, causing INT0002 ACPI events to + * trigger once the XHCI controller notices the keyboard is + * gone. So INT0002 events cause spurious wakeups too. Ignoring + * EC wakes breaks wakeup when opening the lid, the user needs + * to press the power-button to wakeup the system. The + * alternative is suspend simply not working, which is worse. + */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "HP"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP x2 Detachable 10-p0XX"), + }, + .driver_data = &(struct acpi_gpiolib_dmi_quirk) { + .ignore_wake = "INT33FF:01@0,INT0002:00@2", + }, + }, + { + /* + * HP X2 10 models with Bay Trail SoC + AXP288 PMIC use an + * external embedded-controller connected via I2C + an ACPI GPIO + * event handler on INT33FC:02 pin 28, causing spurious wakeups. + */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion x2 Detachable"), + DMI_MATCH(DMI_BOARD_NAME, "815D"), + }, + .driver_data = &(struct acpi_gpiolib_dmi_quirk) { + .ignore_wake = "INT33FC:02@28", + }, + }, + { + /* + * HP X2 10 models with Cherry Trail SoC + AXP288 PMIC use an + * external embedded-controller connected via I2C + an ACPI GPIO + * event handler on INT33FF:01 pin 0, causing spurious wakeups. + */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "HP"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion x2 Detachable"), + DMI_MATCH(DMI_BOARD_NAME, "813E"), + }, + .driver_data = &(struct acpi_gpiolib_dmi_quirk) { + .ignore_wake = "INT33FF:01@0", + }, + }, + { + /* + * Interrupt storm caused from edge triggered floating pin + * Found in BIOS UX325UAZ.300 + * https://bugzilla.kernel.org/show_bug.cgi?id=216208 + */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "ZenBook UX325UAZ_UM325UAZ"), + }, + .driver_data = &(struct acpi_gpiolib_dmi_quirk) { + .ignore_interrupt = "AMDI0030:00@18", + }, + }, + { + /* + * Spurious wakeups from TP_ATTN# pin + * Found in BIOS 1.7.8 + * https://gitlab.freedesktop.org/drm/amd/-/issues/1722#note_1720627 + */ + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "NL5xNU"), + }, + .driver_data = &(struct acpi_gpiolib_dmi_quirk) { + .ignore_wake = "ELAN0415:00@9", + }, + }, + { + /* + * Spurious wakeups from TP_ATTN# pin + * Found in BIOS 1.7.8 + * https://gitlab.freedesktop.org/drm/amd/-/issues/1722#note_1720627 + */ + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "NL5xRU"), + }, + .driver_data = &(struct acpi_gpiolib_dmi_quirk) { + .ignore_wake = "ELAN0415:00@9", + }, + }, + { + /* + * Spurious wakeups from TP_ATTN# pin + * Found in BIOS 1.7.7 + */ + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "NH5xAx"), + }, + .driver_data = &(struct acpi_gpiolib_dmi_quirk) { + .ignore_wake = "SYNA1202:00@16", + }, + }, + { + /* + * On the Peaq C1010 2-in-1 INT33FC:00 pin 3 is connected to + * a "dolby" button. At the ACPI level an _AEI event-handler + * is connected which sets an ACPI variable to 1 on both + * edges. This variable can be polled + cleared to 0 using + * WMI. But since the variable is set on both edges the WMI + * interface is pretty useless even when polling. + * So instead the x86-android-tablets code instantiates + * a gpio-keys platform device for it. + * Ignore the _AEI handler for the pin, so that it is not busy. + */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "PEAQ"), + DMI_MATCH(DMI_PRODUCT_NAME, "PEAQ PMM C1010 MD99187"), + }, + .driver_data = &(struct acpi_gpiolib_dmi_quirk) { + .ignore_interrupt = "INT33FC:00@3", + }, + }, + { + /* + * Spurious wakeups from TP_ATTN# pin + * Found in BIOS 0.35 + * https://gitlab.freedesktop.org/drm/amd/-/issues/3073 + */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "GPD"), + DMI_MATCH(DMI_PRODUCT_NAME, "G1619-04"), + }, + .driver_data = &(struct acpi_gpiolib_dmi_quirk) { + .ignore_wake = "PNP0C50:00@8", + }, + }, + { + /* + * Spurious wakeups from GPIO 11 + * Found in BIOS 1.04 + * https://gitlab.freedesktop.org/drm/amd/-/issues/3954 + */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_FAMILY, "Acer Nitro V 14"), + }, + .driver_data = &(struct acpi_gpiolib_dmi_quirk) { + .ignore_interrupt = "AMDI0030:00@11", + }, + }, + {} /* Terminating entry */ +}; + +static int __init acpi_gpio_setup_params(void) +{ + const struct acpi_gpiolib_dmi_quirk *quirk = NULL; + const struct dmi_system_id *id; + + id = dmi_first_match(gpiolib_acpi_quirks); + if (id) + quirk = id->driver_data; + + if (run_edge_events_on_boot < 0) { + if (quirk && quirk->no_edge_events_on_boot) + run_edge_events_on_boot = 0; + else + run_edge_events_on_boot = 1; + } + + if (ignore_wake == NULL && quirk && quirk->ignore_wake) + ignore_wake = quirk->ignore_wake; + + if (ignore_interrupt == NULL && quirk && quirk->ignore_interrupt) + ignore_interrupt = quirk->ignore_interrupt; + + return 0; +} + +/* Directly after dmi_setup() which runs as core_initcall() */ +postcore_initcall(acpi_gpio_setup_params); --- a/drivers/gpio/gpiolib-acpi.c +++ /dev/null @@ -1,1765 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * ACPI helpers for GPIO API - * - * Copyright (C) 2012, Intel Corporation - * Authors: Mathias Nyman mathias.nyman@linux.intel.com - * Mika Westerberg mika.westerberg@linux.intel.com - */ - -#include <linux/acpi.h> -#include <linux/dmi.h> -#include <linux/errno.h> -#include <linux/export.h> -#include <linux/interrupt.h> -#include <linux/irq.h> -#include <linux/mutex.h> -#include <linux/pinctrl/pinctrl.h> - -#include <linux/gpio/consumer.h> -#include <linux/gpio/driver.h> -#include <linux/gpio/machine.h> - -#include "gpiolib.h" -#include "gpiolib-acpi.h" - -static int run_edge_events_on_boot = -1; -module_param(run_edge_events_on_boot, int, 0444); -MODULE_PARM_DESC(run_edge_events_on_boot, - "Run edge _AEI event-handlers at boot: 0=no, 1=yes, -1=auto"); - -static char *ignore_wake; -module_param(ignore_wake, charp, 0444); -MODULE_PARM_DESC(ignore_wake, - "controller@pin combos on which to ignore the ACPI wake flag " - "ignore_wake=controller@pin[,controller@pin[,...]]"); - -static char *ignore_interrupt; -module_param(ignore_interrupt, charp, 0444); -MODULE_PARM_DESC(ignore_interrupt, - "controller@pin combos on which to ignore interrupt " - "ignore_interrupt=controller@pin[,controller@pin[,...]]"); - -struct acpi_gpiolib_dmi_quirk { - bool no_edge_events_on_boot; - char *ignore_wake; - char *ignore_interrupt; -}; - -/** - * struct acpi_gpio_event - ACPI GPIO event handler data - * - * @node: list-entry of the events list of the struct acpi_gpio_chip - * @handle: handle of ACPI method to execute when the IRQ triggers - * @handler: handler function to pass to request_irq() when requesting the IRQ - * @pin: GPIO pin number on the struct gpio_chip - * @irq: Linux IRQ number for the event, for request_irq() / free_irq() - * @irqflags: flags to pass to request_irq() when requesting the IRQ - * @irq_is_wake: If the ACPI flags indicate the IRQ is a wakeup source - * @irq_requested:True if request_irq() has been done - * @desc: struct gpio_desc for the GPIO pin for this event - */ -struct acpi_gpio_event { - struct list_head node; - acpi_handle handle; - irq_handler_t handler; - unsigned int pin; - unsigned int irq; - unsigned long irqflags; - bool irq_is_wake; - bool irq_requested; - struct gpio_desc *desc; -}; - -struct acpi_gpio_connection { - struct list_head node; - unsigned int pin; - struct gpio_desc *desc; -}; - -struct acpi_gpio_chip { - /* - * ACPICA requires that the first field of the context parameter - * passed to acpi_install_address_space_handler() is large enough - * to hold struct acpi_connection_info. - */ - struct acpi_connection_info conn_info; - struct list_head conns; - struct mutex conn_lock; - struct gpio_chip *chip; - struct list_head events; - struct list_head deferred_req_irqs_list_entry; -}; - -/** - * struct acpi_gpio_info - ACPI GPIO specific information - * @adev: reference to ACPI device which consumes GPIO resource - * @flags: GPIO initialization flags - * @gpioint: if %true this GPIO is of type GpioInt otherwise type is GpioIo - * @pin_config: pin bias as provided by ACPI - * @polarity: interrupt polarity as provided by ACPI - * @triggering: triggering type as provided by ACPI - * @wake_capable: wake capability as provided by ACPI - * @debounce: debounce timeout as provided by ACPI - * @quirks: Linux specific quirks as provided by struct acpi_gpio_mapping - */ -struct acpi_gpio_info { - struct acpi_device *adev; - enum gpiod_flags flags; - bool gpioint; - int pin_config; - int polarity; - int triggering; - bool wake_capable; - unsigned int debounce; - unsigned int quirks; -}; - -/* - * For GPIO chips which call acpi_gpiochip_request_interrupts() before late_init - * (so builtin drivers) we register the ACPI GpioInt IRQ handlers from a - * late_initcall_sync() handler, so that other builtin drivers can register their - * OpRegions before the event handlers can run. This list contains GPIO chips - * for which the acpi_gpiochip_request_irqs() call has been deferred. - */ -static DEFINE_MUTEX(acpi_gpio_deferred_req_irqs_lock); -static LIST_HEAD(acpi_gpio_deferred_req_irqs_list); -static bool acpi_gpio_deferred_req_irqs_done; - -static int acpi_gpiochip_find(struct gpio_chip *gc, const void *data) -{ - /* First check the actual GPIO device */ - if (device_match_acpi_handle(&gc->gpiodev->dev, data)) - return true; - - /* - * When the ACPI device is artificially split to the banks of GPIOs, - * where each of them is represented by a separate GPIO device, - * the firmware node of the physical device may not be shared among - * the banks as they may require different values for the same property, - * e.g., number of GPIOs in a certain bank. In such case the ACPI handle - * of a GPIO device is NULL and can not be used. Hence we have to check - * the parent device to be sure that there is no match before bailing - * out. - */ - if (gc->parent) - return device_match_acpi_handle(gc->parent, data); - - return false; -} - -/** - * acpi_get_gpiod() - Translate ACPI GPIO pin to GPIO descriptor usable with GPIO API - * @path: ACPI GPIO controller full path name, (e.g. "\_SB.GPO1") - * @pin: ACPI GPIO pin number (0-based, controller-relative) - * - * Returns: - * GPIO descriptor to use with Linux generic GPIO API. - * If the GPIO cannot be translated or there is an error an ERR_PTR is - * returned. - * - * Specifically returns %-EPROBE_DEFER if the referenced GPIO - * controller does not have GPIO chip registered at the moment. This is to - * support probe deferral. - */ -static struct gpio_desc *acpi_get_gpiod(char *path, unsigned int pin) -{ - acpi_handle handle; - acpi_status status; - - status = acpi_get_handle(NULL, path, &handle); - if (ACPI_FAILURE(status)) - return ERR_PTR(-ENODEV); - - struct gpio_device *gdev __free(gpio_device_put) = - gpio_device_find(handle, acpi_gpiochip_find); - if (!gdev) - return ERR_PTR(-EPROBE_DEFER); - - /* - * FIXME: keep track of the reference to the GPIO device somehow - * instead of putting it here. - */ - return gpio_device_get_desc(gdev, pin); -} - -static irqreturn_t acpi_gpio_irq_handler(int irq, void *data) -{ - struct acpi_gpio_event *event = data; - - acpi_evaluate_object(event->handle, NULL, NULL, NULL); - - return IRQ_HANDLED; -} - -static irqreturn_t acpi_gpio_irq_handler_evt(int irq, void *data) -{ - struct acpi_gpio_event *event = data; - - acpi_execute_simple_method(event->handle, NULL, event->pin); - - return IRQ_HANDLED; -} - -static void acpi_gpio_chip_dh(acpi_handle handle, void *data) -{ - /* The address of this function is used as a key. */ -} - -bool acpi_gpio_get_irq_resource(struct acpi_resource *ares, - struct acpi_resource_gpio **agpio) -{ - struct acpi_resource_gpio *gpio; - - if (ares->type != ACPI_RESOURCE_TYPE_GPIO) - return false; - - gpio = &ares->data.gpio; - if (gpio->connection_type != ACPI_RESOURCE_GPIO_TYPE_INT) - return false; - - *agpio = gpio; - return true; -} -EXPORT_SYMBOL_GPL(acpi_gpio_get_irq_resource); - -/** - * acpi_gpio_get_io_resource - Fetch details of an ACPI resource if it is a GPIO - * I/O resource or return False if not. - * @ares: Pointer to the ACPI resource to fetch - * @agpio: Pointer to a &struct acpi_resource_gpio to store the output pointer - * - * Returns: - * %true if GpioIo resource is found, %false otherwise. - */ -bool acpi_gpio_get_io_resource(struct acpi_resource *ares, - struct acpi_resource_gpio **agpio) -{ - struct acpi_resource_gpio *gpio; - - if (ares->type != ACPI_RESOURCE_TYPE_GPIO) - return false; - - gpio = &ares->data.gpio; - if (gpio->connection_type != ACPI_RESOURCE_GPIO_TYPE_IO) - return false; - - *agpio = gpio; - return true; -} -EXPORT_SYMBOL_GPL(acpi_gpio_get_io_resource); - -static void acpi_gpiochip_request_irq(struct acpi_gpio_chip *acpi_gpio, - struct acpi_gpio_event *event) -{ - struct device *parent = acpi_gpio->chip->parent; - int ret, value; - - ret = request_threaded_irq(event->irq, NULL, event->handler, - event->irqflags | IRQF_ONESHOT, "ACPI:Event", event); - if (ret) { - dev_err(parent, "Failed to setup interrupt handler for %d\n", event->irq); - return; - } - - if (event->irq_is_wake) - enable_irq_wake(event->irq); - - event->irq_requested = true; - - /* Make sure we trigger the initial state of edge-triggered IRQs */ - if (acpi_gpio_need_run_edge_events_on_boot() && - (event->irqflags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING))) { - value = gpiod_get_raw_value_cansleep(event->desc); - if (((event->irqflags & IRQF_TRIGGER_RISING) && value == 1) || - ((event->irqflags & IRQF_TRIGGER_FALLING) && value == 0)) - event->handler(event->irq, event); - } -} - -static void acpi_gpiochip_request_irqs(struct acpi_gpio_chip *acpi_gpio) -{ - struct acpi_gpio_event *event; - - list_for_each_entry(event, &acpi_gpio->events, node) - acpi_gpiochip_request_irq(acpi_gpio, event); -} - -static enum gpiod_flags -acpi_gpio_to_gpiod_flags(const struct acpi_resource_gpio *agpio, int polarity) -{ - /* GpioInt() implies input configuration */ - if (agpio->connection_type == ACPI_RESOURCE_GPIO_TYPE_INT) - return GPIOD_IN; - - switch (agpio->io_restriction) { - case ACPI_IO_RESTRICT_INPUT: - return GPIOD_IN; - case ACPI_IO_RESTRICT_OUTPUT: - /* - * ACPI GPIO resources don't contain an initial value for the - * GPIO. Therefore we deduce that value from the pull field - * and the polarity instead. If the pin is pulled up we assume - * default to be high, if it is pulled down we assume default - * to be low, otherwise we leave pin untouched. For active low - * polarity values will be switched. See also - * Documentation/firmware-guide/acpi/gpio-properties.rst. - */ - switch (agpio->pin_config) { - case ACPI_PIN_CONFIG_PULLUP: - return polarity == GPIO_ACTIVE_LOW ? GPIOD_OUT_LOW : GPIOD_OUT_HIGH; - case ACPI_PIN_CONFIG_PULLDOWN: - return polarity == GPIO_ACTIVE_LOW ? GPIOD_OUT_HIGH : GPIOD_OUT_LOW; - default: - break; - } - break; - default: - break; - } - - /* - * Assume that the BIOS has configured the direction and pull - * accordingly. - */ - return GPIOD_ASIS; -} - -static struct gpio_desc *acpi_request_own_gpiod(struct gpio_chip *chip, - struct acpi_resource_gpio *agpio, - unsigned int index, - const char *label) -{ - int polarity = GPIO_ACTIVE_HIGH; - enum gpiod_flags flags = acpi_gpio_to_gpiod_flags(agpio, polarity); - unsigned int pin = agpio->pin_table[index]; - struct gpio_desc *desc; - int ret; - - desc = gpiochip_request_own_desc(chip, pin, label, polarity, flags); - if (IS_ERR(desc)) - return desc; - - /* ACPI uses hundredths of milliseconds units */ - ret = gpio_set_debounce_timeout(desc, agpio->debounce_timeout * 10); - if (ret) - dev_warn(chip->parent, - "Failed to set debounce-timeout for pin 0x%04X, err %d\n", - pin, ret); - - return desc; -} - -bool acpi_gpio_add_to_deferred_list(struct list_head *list) -{ - bool defer; - - mutex_lock(&acpi_gpio_deferred_req_irqs_lock); - defer = !acpi_gpio_deferred_req_irqs_done; - if (defer) - list_add(list, &acpi_gpio_deferred_req_irqs_list); - mutex_unlock(&acpi_gpio_deferred_req_irqs_lock); - - return defer; -} - -void acpi_gpio_remove_from_deferred_list(struct list_head *list) -{ - mutex_lock(&acpi_gpio_deferred_req_irqs_lock); - if (!list_empty(list)) - list_del_init(list); - mutex_unlock(&acpi_gpio_deferred_req_irqs_lock); -} - -int acpi_gpio_need_run_edge_events_on_boot(void) -{ - return run_edge_events_on_boot; -} - -bool acpi_gpio_in_ignore_list(enum acpi_gpio_ignore_list list, const char *controller_in, - unsigned int pin_in) -{ - const char *ignore_list, *controller, *pin_str; - unsigned int pin; - char *endp; - int len; - - switch (list) { - case ACPI_GPIO_IGNORE_WAKE: - ignore_list = ignore_wake; - break; - case ACPI_GPIO_IGNORE_INTERRUPT: - ignore_list = ignore_interrupt; - break; - default: - return false; - } - - controller = ignore_list; - while (controller) { - pin_str = strchr(controller, '@'); - if (!pin_str) - goto err; - - len = pin_str - controller; - if (len == strlen(controller_in) && - strncmp(controller, controller_in, len) == 0) { - pin = simple_strtoul(pin_str + 1, &endp, 10); - if (*endp != 0 && *endp != ',') - goto err; - - if (pin == pin_in) - return true; - } - - controller = strchr(controller, ','); - if (controller) - controller++; - } - - return false; -err: - pr_err_once("Error: Invalid value for gpiolib_acpi.ignore_...: %s\n", ignore_list); - return false; -} - -static bool acpi_gpio_irq_is_wake(struct device *parent, - const struct acpi_resource_gpio *agpio) -{ - unsigned int pin = agpio->pin_table[0]; - - if (agpio->wake_capable != ACPI_WAKE_CAPABLE) - return false; - - if (acpi_gpio_in_ignore_list(ACPI_GPIO_IGNORE_WAKE, dev_name(parent), pin)) { - dev_info(parent, "Ignoring wakeup on pin %u\n", pin); - return false; - } - - return true; -} - -/* Always returns AE_OK so that we keep looping over the resources */ -static acpi_status acpi_gpiochip_alloc_event(struct acpi_resource *ares, - void *context) -{ - struct acpi_gpio_chip *acpi_gpio = context; - struct gpio_chip *chip = acpi_gpio->chip; - struct acpi_resource_gpio *agpio; - acpi_handle handle, evt_handle; - struct acpi_gpio_event *event; - irq_handler_t handler = NULL; - struct gpio_desc *desc; - unsigned int pin; - int ret, irq; - - if (!acpi_gpio_get_irq_resource(ares, &agpio)) - return AE_OK; - - handle = ACPI_HANDLE(chip->parent); - pin = agpio->pin_table[0]; - - if (pin <= 255) { - char ev_name[8]; - sprintf(ev_name, "_%c%02X", - agpio->triggering == ACPI_EDGE_SENSITIVE ? 'E' : 'L', - pin); - if (ACPI_SUCCESS(acpi_get_handle(handle, ev_name, &evt_handle))) - handler = acpi_gpio_irq_handler; - } - if (!handler) { - if (ACPI_SUCCESS(acpi_get_handle(handle, "_EVT", &evt_handle))) - handler = acpi_gpio_irq_handler_evt; - } - if (!handler) - return AE_OK; - - if (acpi_gpio_in_ignore_list(ACPI_GPIO_IGNORE_INTERRUPT, dev_name(chip->parent), pin)) { - dev_info(chip->parent, "Ignoring interrupt on pin %u\n", pin); - return AE_OK; - } - - desc = acpi_request_own_gpiod(chip, agpio, 0, "ACPI:Event"); - if (IS_ERR(desc)) { - dev_err(chip->parent, - "Failed to request GPIO for pin 0x%04X, err %ld\n", - pin, PTR_ERR(desc)); - return AE_OK; - } - - ret = gpiochip_lock_as_irq(chip, pin); - if (ret) { - dev_err(chip->parent, - "Failed to lock GPIO pin 0x%04X as interrupt, err %d\n", - pin, ret); - goto fail_free_desc; - } - - irq = gpiod_to_irq(desc); - if (irq < 0) { - dev_err(chip->parent, - "Failed to translate GPIO pin 0x%04X to IRQ, err %d\n", - pin, irq); - goto fail_unlock_irq; - } - - event = kzalloc(sizeof(*event), GFP_KERNEL); - if (!event) - goto fail_unlock_irq; - - event->irqflags = IRQF_ONESHOT; - if (agpio->triggering == ACPI_LEVEL_SENSITIVE) { - if (agpio->polarity == ACPI_ACTIVE_HIGH) - event->irqflags |= IRQF_TRIGGER_HIGH; - else - event->irqflags |= IRQF_TRIGGER_LOW; - } else { - switch (agpio->polarity) { - case ACPI_ACTIVE_HIGH: - event->irqflags |= IRQF_TRIGGER_RISING; - break; - case ACPI_ACTIVE_LOW: - event->irqflags |= IRQF_TRIGGER_FALLING; - break; - default: - event->irqflags |= IRQF_TRIGGER_RISING | - IRQF_TRIGGER_FALLING; - break; - } - } - - event->handle = evt_handle; - event->handler = handler; - event->irq = irq; - event->irq_is_wake = acpi_gpio_irq_is_wake(chip->parent, agpio); - event->pin = pin; - event->desc = desc; - - list_add_tail(&event->node, &acpi_gpio->events); - - return AE_OK; - -fail_unlock_irq: - gpiochip_unlock_as_irq(chip, pin); -fail_free_desc: - gpiochip_free_own_desc(desc); - - return AE_OK; -} - -/** - * acpi_gpiochip_request_interrupts() - Register isr for gpio chip ACPI events - * @chip: GPIO chip - * - * ACPI5 platforms can use GPIO signaled ACPI events. These GPIO interrupts are - * handled by ACPI event methods which need to be called from the GPIO - * chip's interrupt handler. acpi_gpiochip_request_interrupts() finds out which - * GPIO pins have ACPI event methods and assigns interrupt handlers that calls - * the ACPI event methods for those pins. - */ -void acpi_gpiochip_request_interrupts(struct gpio_chip *chip) -{ - struct acpi_gpio_chip *acpi_gpio; - acpi_handle handle; - acpi_status status; - - if (!chip->parent || !chip->to_irq) - return; - - handle = ACPI_HANDLE(chip->parent); - if (!handle) - return; - - status = acpi_get_data(handle, acpi_gpio_chip_dh, (void **)&acpi_gpio); - if (ACPI_FAILURE(status)) - return; - - if (acpi_quirk_skip_gpio_event_handlers()) - return; - - acpi_walk_resources(handle, METHOD_NAME__AEI, - acpi_gpiochip_alloc_event, acpi_gpio); - - if (acpi_gpio_add_to_deferred_list(&acpi_gpio->deferred_req_irqs_list_entry)) - return; - - acpi_gpiochip_request_irqs(acpi_gpio); -} -EXPORT_SYMBOL_GPL(acpi_gpiochip_request_interrupts); - -/** - * acpi_gpiochip_free_interrupts() - Free GPIO ACPI event interrupts. - * @chip: GPIO chip - * - * Free interrupts associated with GPIO ACPI event method for the given - * GPIO chip. - */ -void acpi_gpiochip_free_interrupts(struct gpio_chip *chip) -{ - struct acpi_gpio_chip *acpi_gpio; - struct acpi_gpio_event *event, *ep; - acpi_handle handle; - acpi_status status; - - if (!chip->parent || !chip->to_irq) - return; - - handle = ACPI_HANDLE(chip->parent); - if (!handle) - return; - - status = acpi_get_data(handle, acpi_gpio_chip_dh, (void **)&acpi_gpio); - if (ACPI_FAILURE(status)) - return; - - acpi_gpio_remove_from_deferred_list(&acpi_gpio->deferred_req_irqs_list_entry); - - list_for_each_entry_safe_reverse(event, ep, &acpi_gpio->events, node) { - if (event->irq_requested) { - if (event->irq_is_wake) - disable_irq_wake(event->irq); - - free_irq(event->irq, event); - } - - gpiochip_unlock_as_irq(chip, event->pin); - gpiochip_free_own_desc(event->desc); - list_del(&event->node); - kfree(event); - } -} -EXPORT_SYMBOL_GPL(acpi_gpiochip_free_interrupts); - -void __init acpi_gpio_process_deferred_list(struct list_head *list) -{ - struct acpi_gpio_chip *acpi_gpio, *tmp; - - list_for_each_entry_safe(acpi_gpio, tmp, list, deferred_req_irqs_list_entry) - acpi_gpiochip_request_irqs(acpi_gpio); -} - -int acpi_dev_add_driver_gpios(struct acpi_device *adev, - const struct acpi_gpio_mapping *gpios) -{ - if (adev && gpios) { - adev->driver_gpios = gpios; - return 0; - } - return -EINVAL; -} -EXPORT_SYMBOL_GPL(acpi_dev_add_driver_gpios); - -void acpi_dev_remove_driver_gpios(struct acpi_device *adev) -{ - if (adev) - adev->driver_gpios = NULL; -} -EXPORT_SYMBOL_GPL(acpi_dev_remove_driver_gpios); - -static void acpi_dev_release_driver_gpios(void *adev) -{ - acpi_dev_remove_driver_gpios(adev); -} - -int devm_acpi_dev_add_driver_gpios(struct device *dev, - const struct acpi_gpio_mapping *gpios) -{ - struct acpi_device *adev = ACPI_COMPANION(dev); - int ret; - - ret = acpi_dev_add_driver_gpios(adev, gpios); - if (ret) - return ret; - - return devm_add_action_or_reset(dev, acpi_dev_release_driver_gpios, adev); -} -EXPORT_SYMBOL_GPL(devm_acpi_dev_add_driver_gpios); - -static bool acpi_get_driver_gpio_data(struct acpi_device *adev, - const char *name, int index, - struct fwnode_reference_args *args, - unsigned int *quirks) -{ - const struct acpi_gpio_mapping *gm; - - if (!adev || !adev->driver_gpios) - return false; - - for (gm = adev->driver_gpios; gm->name; gm++) - if (!strcmp(name, gm->name) && gm->data && index < gm->size) { - const struct acpi_gpio_params *par = gm->data + index; - - args->fwnode = acpi_fwnode_handle(adev); - args->args[0] = par->crs_entry_index; - args->args[1] = par->line_index; - args->args[2] = par->active_low; - args->nargs = 3; - - *quirks = gm->quirks; - return true; - } - - return false; -} - -static int -__acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, enum gpiod_flags update) -{ - const enum gpiod_flags mask = - GPIOD_FLAGS_BIT_DIR_SET | GPIOD_FLAGS_BIT_DIR_OUT | - GPIOD_FLAGS_BIT_DIR_VAL; - int ret = 0; - - /* - * Check if the BIOS has IoRestriction with explicitly set direction - * and update @flags accordingly. Otherwise use whatever caller asked - * for. - */ - if (update & GPIOD_FLAGS_BIT_DIR_SET) { - enum gpiod_flags diff = *flags ^ update; - - /* - * Check if caller supplied incompatible GPIO initialization - * flags. - * - * Return %-EINVAL to notify that firmware has different - * settings and we are going to use them. - */ - if (((*flags & GPIOD_FLAGS_BIT_DIR_SET) && (diff & GPIOD_FLAGS_BIT_DIR_OUT)) || - ((*flags & GPIOD_FLAGS_BIT_DIR_OUT) && (diff & GPIOD_FLAGS_BIT_DIR_VAL))) - ret = -EINVAL; - *flags = (*flags & ~mask) | (update & mask); - } - return ret; -} - -static int acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, - struct acpi_gpio_info *info) -{ - struct device *dev = &info->adev->dev; - enum gpiod_flags old = *flags; - int ret; - - ret = __acpi_gpio_update_gpiod_flags(&old, info->flags); - if (info->quirks & ACPI_GPIO_QUIRK_NO_IO_RESTRICTION) { - if (ret) - dev_warn(dev, FW_BUG "GPIO not in correct mode, fixing\n"); - } else { - if (ret) - dev_dbg(dev, "Override GPIO initialization flags\n"); - *flags = old; - } - - return ret; -} - -static int acpi_gpio_update_gpiod_lookup_flags(unsigned long *lookupflags, - struct acpi_gpio_info *info) -{ - switch (info->pin_config) { - case ACPI_PIN_CONFIG_PULLUP: - *lookupflags |= GPIO_PULL_UP; - break; - case ACPI_PIN_CONFIG_PULLDOWN: - *lookupflags |= GPIO_PULL_DOWN; - break; - case ACPI_PIN_CONFIG_NOPULL: - *lookupflags |= GPIO_PULL_DISABLE; - break; - default: - break; - } - - if (info->polarity == GPIO_ACTIVE_LOW) - *lookupflags |= GPIO_ACTIVE_LOW; - - return 0; -} - -struct acpi_gpio_lookup { - struct acpi_gpio_info info; - int index; - u16 pin_index; - bool active_low; - struct gpio_desc *desc; - int n; -}; - -static int acpi_populate_gpio_lookup(struct acpi_resource *ares, void *data) -{ - struct acpi_gpio_lookup *lookup = data; - - if (ares->type != ACPI_RESOURCE_TYPE_GPIO) - return 1; - - if (!lookup->desc) { - const struct acpi_resource_gpio *agpio = &ares->data.gpio; - bool gpioint = agpio->connection_type == ACPI_RESOURCE_GPIO_TYPE_INT; - struct gpio_desc *desc; - u16 pin_index; - - if (lookup->info.quirks & ACPI_GPIO_QUIRK_ONLY_GPIOIO && gpioint) - lookup->index++; - - if (lookup->n++ != lookup->index) - return 1; - - pin_index = lookup->pin_index; - if (pin_index >= agpio->pin_table_length) - return 1; - - if (lookup->info.quirks & ACPI_GPIO_QUIRK_ABSOLUTE_NUMBER) - desc = gpio_to_desc(agpio->pin_table[pin_index]); - else - desc = acpi_get_gpiod(agpio->resource_source.string_ptr, - agpio->pin_table[pin_index]); - lookup->desc = desc; - lookup->info.pin_config = agpio->pin_config; - lookup->info.debounce = agpio->debounce_timeout; - lookup->info.gpioint = gpioint; - lookup->info.wake_capable = acpi_gpio_irq_is_wake(&lookup->info.adev->dev, agpio); - - /* - * Polarity and triggering are only specified for GpioInt - * resource. - * Note: we expect here: - * - ACPI_ACTIVE_LOW == GPIO_ACTIVE_LOW - * - ACPI_ACTIVE_HIGH == GPIO_ACTIVE_HIGH - */ - if (lookup->info.gpioint) { - lookup->info.polarity = agpio->polarity; - lookup->info.triggering = agpio->triggering; - } else { - lookup->info.polarity = lookup->active_low; - } - - lookup->info.flags = acpi_gpio_to_gpiod_flags(agpio, lookup->info.polarity); - } - - return 1; -} - -static int acpi_gpio_resource_lookup(struct acpi_gpio_lookup *lookup, - struct acpi_gpio_info *info) -{ - struct acpi_device *adev = lookup->info.adev; - struct list_head res_list; - int ret; - - INIT_LIST_HEAD(&res_list); - - ret = acpi_dev_get_resources(adev, &res_list, - acpi_populate_gpio_lookup, - lookup); - if (ret < 0) - return ret; - - acpi_dev_free_resource_list(&res_list); - - if (!lookup->desc) - return -ENOENT; - - if (info) - *info = lookup->info; - return 0; -} - -static int acpi_gpio_property_lookup(struct fwnode_handle *fwnode, - const char *propname, int index, - struct acpi_gpio_lookup *lookup) -{ - struct fwnode_reference_args args; - unsigned int quirks = 0; - int ret; - - memset(&args, 0, sizeof(args)); - ret = __acpi_node_get_property_reference(fwnode, propname, index, 3, - &args); - if (ret) { - struct acpi_device *adev; - - adev = to_acpi_device_node(fwnode); - if (!acpi_get_driver_gpio_data(adev, propname, index, &args, &quirks)) - return ret; - } - /* - * The property was found and resolved, so need to lookup the GPIO based - * on returned args. - */ - if (!to_acpi_device_node(args.fwnode)) - return -EINVAL; - if (args.nargs != 3) - return -EPROTO; - - lookup->index = args.args[0]; - lookup->pin_index = args.args[1]; - lookup->active_low = !!args.args[2]; - - lookup->info.adev = to_acpi_device_node(args.fwnode); - lookup->info.quirks = quirks; - - return 0; -} - -/** - * acpi_get_gpiod_by_index() - get a GPIO descriptor from device resources - * @adev: pointer to a ACPI device to get GPIO from - * @propname: Property name of the GPIO (optional) - * @index: index of GpioIo/GpioInt resource (starting from %0) - * @info: info pointer to fill in (optional) - * - * Function goes through ACPI resources for @adev and based on @index looks - * up a GpioIo/GpioInt resource, translates it to the Linux GPIO descriptor, - * and returns it. @index matches GpioIo/GpioInt resources only so if there - * are total %3 GPIO resources, the index goes from %0 to %2. - * - * If @propname is specified the GPIO is looked using device property. In - * that case @index is used to select the GPIO entry in the property value - * (in case of multiple). - * - * Returns: - * GPIO descriptor to use with Linux generic GPIO API. - * If the GPIO cannot be translated or there is an error an ERR_PTR is - * returned. - * - * Note: if the GPIO resource has multiple entries in the pin list, this - * function only returns the first. - */ -static struct gpio_desc *acpi_get_gpiod_by_index(struct acpi_device *adev, - const char *propname, - int index, - struct acpi_gpio_info *info) -{ - struct acpi_gpio_lookup lookup; - int ret; - - memset(&lookup, 0, sizeof(lookup)); - lookup.index = index; - - if (propname) { - dev_dbg(&adev->dev, "GPIO: looking up %s\n", propname); - - ret = acpi_gpio_property_lookup(acpi_fwnode_handle(adev), - propname, index, &lookup); - if (ret) - return ERR_PTR(ret); - - dev_dbg(&adev->dev, "GPIO: _DSD returned %s %d %u %u\n", - dev_name(&lookup.info.adev->dev), lookup.index, - lookup.pin_index, lookup.active_low); - } else { - dev_dbg(&adev->dev, "GPIO: looking up %d in _CRS\n", index); - lookup.info.adev = adev; - } - - ret = acpi_gpio_resource_lookup(&lookup, info); - return ret ? ERR_PTR(ret) : lookup.desc; -} - -/** - * acpi_get_gpiod_from_data() - get a GPIO descriptor from ACPI data node - * @fwnode: pointer to an ACPI firmware node to get the GPIO information from - * @propname: Property name of the GPIO - * @index: index of GpioIo/GpioInt resource (starting from %0) - * @info: info pointer to fill in (optional) - * - * This function uses the property-based GPIO lookup to get to the GPIO - * resource with the relevant information from a data-only ACPI firmware node - * and uses that to obtain the GPIO descriptor to return. - * - * Returns: - * GPIO descriptor to use with Linux generic GPIO API. - * If the GPIO cannot be translated or there is an error an ERR_PTR is - * returned. - */ -static struct gpio_desc *acpi_get_gpiod_from_data(struct fwnode_handle *fwnode, - const char *propname, - int index, - struct acpi_gpio_info *info) -{ - struct acpi_gpio_lookup lookup; - int ret; - - if (!is_acpi_data_node(fwnode)) - return ERR_PTR(-ENODEV); - - if (!propname) - return ERR_PTR(-EINVAL); - - memset(&lookup, 0, sizeof(lookup)); - lookup.index = index; - - ret = acpi_gpio_property_lookup(fwnode, propname, index, &lookup); - if (ret) - return ERR_PTR(ret); - - ret = acpi_gpio_resource_lookup(&lookup, info); - return ret ? ERR_PTR(ret) : lookup.desc; -} - -static bool acpi_can_fallback_to_crs(struct acpi_device *adev, - const char *con_id) -{ - /* If there is no ACPI device, there is no _CRS to fall back to */ - if (!adev) - return false; - - /* Never allow fallback if the device has properties */ - if (acpi_dev_has_props(adev) || adev->driver_gpios) - return false; - - return con_id == NULL; -} - -static struct gpio_desc * -__acpi_find_gpio(struct fwnode_handle *fwnode, const char *con_id, unsigned int idx, - bool can_fallback, struct acpi_gpio_info *info) -{ - struct acpi_device *adev = to_acpi_device_node(fwnode); - struct gpio_desc *desc; - char propname[32]; - - /* Try first from _DSD */ - for_each_gpio_property_name(propname, con_id) { - if (adev) - desc = acpi_get_gpiod_by_index(adev, - propname, idx, info); - else - desc = acpi_get_gpiod_from_data(fwnode, - propname, idx, info); - if (PTR_ERR(desc) == -EPROBE_DEFER) - return ERR_CAST(desc); - - if (!IS_ERR(desc)) - return desc; - } - - /* Then from plain _CRS GPIOs */ - if (can_fallback) - return acpi_get_gpiod_by_index(adev, NULL, idx, info); - - return ERR_PTR(-ENOENT); -} - -struct gpio_desc *acpi_find_gpio(struct fwnode_handle *fwnode, - const char *con_id, - unsigned int idx, - enum gpiod_flags *dflags, - unsigned long *lookupflags) -{ - struct acpi_device *adev = to_acpi_device_node(fwnode); - bool can_fallback = acpi_can_fallback_to_crs(adev, con_id); - struct acpi_gpio_info info; - struct gpio_desc *desc; - - desc = __acpi_find_gpio(fwnode, con_id, idx, can_fallback, &info); - if (IS_ERR(desc)) - return desc; - - if (info.gpioint && - (*dflags == GPIOD_OUT_LOW || *dflags == GPIOD_OUT_HIGH)) { - dev_dbg(&adev->dev, "refusing GpioInt() entry when doing GPIOD_OUT_* lookup\n"); - return ERR_PTR(-ENOENT); - } - - acpi_gpio_update_gpiod_flags(dflags, &info); - acpi_gpio_update_gpiod_lookup_flags(lookupflags, &info); - return desc; -} - -/** - * acpi_dev_gpio_irq_wake_get_by() - Find GpioInt and translate it to Linux IRQ number - * @adev: pointer to a ACPI device to get IRQ from - * @con_id: optional name of GpioInt resource - * @index: index of GpioInt resource (starting from %0) - * @wake_capable: Set to true if the IRQ is wake capable - * - * If the device has one or more GpioInt resources, this function can be - * used to translate from the GPIO offset in the resource to the Linux IRQ - * number. - * - * The function is idempotent, though each time it runs it will configure GPIO - * pin direction according to the flags in GpioInt resource. - * - * The function takes optional @con_id parameter. If the resource has - * a @con_id in a property, then only those will be taken into account. - * - * The GPIO is considered wake capable if the GpioInt resource specifies - * SharedAndWake or ExclusiveAndWake. - * - * Returns: - * Linux IRQ number (> 0) on success, negative errno on failure. - */ -int acpi_dev_gpio_irq_wake_get_by(struct acpi_device *adev, const char *con_id, int index, - bool *wake_capable) -{ - struct fwnode_handle *fwnode = acpi_fwnode_handle(adev); - int idx, i; - unsigned int irq_flags; - int ret; - - for (i = 0, idx = 0; idx <= index; i++) { - struct acpi_gpio_info info; - struct gpio_desc *desc; - - /* Ignore -EPROBE_DEFER, it only matters if idx matches */ - desc = __acpi_find_gpio(fwnode, con_id, i, true, &info); - if (IS_ERR(desc) && PTR_ERR(desc) != -EPROBE_DEFER) - return PTR_ERR(desc); - - if (info.gpioint && idx++ == index) { - unsigned long lflags = GPIO_LOOKUP_FLAGS_DEFAULT; - enum gpiod_flags dflags = GPIOD_ASIS; - char label[32]; - int irq; - - if (IS_ERR(desc)) - return PTR_ERR(desc); - - irq = gpiod_to_irq(desc); - if (irq < 0) - return irq; - - acpi_gpio_update_gpiod_flags(&dflags, &info); - acpi_gpio_update_gpiod_lookup_flags(&lflags, &info); - - snprintf(label, sizeof(label), "%pfwP GpioInt(%d)", fwnode, index); - ret = gpiod_set_consumer_name(desc, con_id ?: label); - if (ret) - return ret; - - ret = gpiod_configure_flags(desc, label, lflags, dflags); - if (ret < 0) - return ret; - - /* ACPI uses hundredths of milliseconds units */ - ret = gpio_set_debounce_timeout(desc, info.debounce * 10); - if (ret) - return ret; - - irq_flags = acpi_dev_get_irq_type(info.triggering, - info.polarity); - - /* - * If the IRQ is not already in use then set type - * if specified and different than the current one. - */ - if (can_request_irq(irq, irq_flags)) { - if (irq_flags != IRQ_TYPE_NONE && - irq_flags != irq_get_trigger_type(irq)) - irq_set_irq_type(irq, irq_flags); - } else { - dev_dbg(&adev->dev, "IRQ %d already in use\n", irq); - } - - /* avoid suspend issues with GPIOs when systems are using S3 */ - if (wake_capable && acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0) - *wake_capable = info.wake_capable; - - return irq; - } - - } - return -ENOENT; -} -EXPORT_SYMBOL_GPL(acpi_dev_gpio_irq_wake_get_by); - -static acpi_status -acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address, - u32 bits, u64 *value, void *handler_context, - void *region_context) -{ - struct acpi_gpio_chip *achip = region_context; - struct gpio_chip *chip = achip->chip; - struct acpi_resource_gpio *agpio; - struct acpi_resource *ares; - u16 pin_index = address; - acpi_status status; - int length; - int i; - - status = acpi_buffer_to_resource(achip->conn_info.connection, - achip->conn_info.length, &ares); - if (ACPI_FAILURE(status)) - return status; - - if (WARN_ON(ares->type != ACPI_RESOURCE_TYPE_GPIO)) { - ACPI_FREE(ares); - return AE_BAD_PARAMETER; - } - - agpio = &ares->data.gpio; - - if (WARN_ON(agpio->io_restriction == ACPI_IO_RESTRICT_INPUT && - function == ACPI_WRITE)) { - ACPI_FREE(ares); - return AE_BAD_PARAMETER; - } - - length = min_t(u16, agpio->pin_table_length, pin_index + bits); - for (i = pin_index; i < length; ++i) { - unsigned int pin = agpio->pin_table[i]; - struct acpi_gpio_connection *conn; - struct gpio_desc *desc; - bool found; - - mutex_lock(&achip->conn_lock); - - found = false; - list_for_each_entry(conn, &achip->conns, node) { - if (conn->pin == pin) { - found = true; - desc = conn->desc; - break; - } - } - - /* - * The same GPIO can be shared between operation region and - * event but only if the access here is ACPI_READ. In that - * case we "borrow" the event GPIO instead. - */ - if (!found && agpio->shareable == ACPI_SHARED && - function == ACPI_READ) { - struct acpi_gpio_event *event; - - list_for_each_entry(event, &achip->events, node) { - if (event->pin == pin) { - desc = event->desc; - found = true; - break; - } - } - } - - if (!found) { - desc = acpi_request_own_gpiod(chip, agpio, i, "ACPI:OpRegion"); - if (IS_ERR(desc)) { - mutex_unlock(&achip->conn_lock); - status = AE_ERROR; - goto out; - } - - conn = kzalloc(sizeof(*conn), GFP_KERNEL); - if (!conn) { - gpiochip_free_own_desc(desc); - mutex_unlock(&achip->conn_lock); - status = AE_NO_MEMORY; - goto out; - } - - conn->pin = pin; - conn->desc = desc; - list_add_tail(&conn->node, &achip->conns); - } - - mutex_unlock(&achip->conn_lock); - - if (function == ACPI_WRITE) - gpiod_set_raw_value_cansleep(desc, !!(*value & BIT(i))); - else - *value |= (u64)gpiod_get_raw_value_cansleep(desc) << i; - } - -out: - ACPI_FREE(ares); - return status; -} - -static void acpi_gpiochip_request_regions(struct acpi_gpio_chip *achip) -{ - struct gpio_chip *chip = achip->chip; - acpi_handle handle = ACPI_HANDLE(chip->parent); - acpi_status status; - - INIT_LIST_HEAD(&achip->conns); - mutex_init(&achip->conn_lock); - status = acpi_install_address_space_handler(handle, ACPI_ADR_SPACE_GPIO, - acpi_gpio_adr_space_handler, - NULL, achip); - if (ACPI_FAILURE(status)) - dev_err(chip->parent, - "Failed to install GPIO OpRegion handler\n"); -} - -static void acpi_gpiochip_free_regions(struct acpi_gpio_chip *achip) -{ - struct gpio_chip *chip = achip->chip; - acpi_handle handle = ACPI_HANDLE(chip->parent); - struct acpi_gpio_connection *conn, *tmp; - acpi_status status; - - status = acpi_remove_address_space_handler(handle, ACPI_ADR_SPACE_GPIO, - acpi_gpio_adr_space_handler); - if (ACPI_FAILURE(status)) { - dev_err(chip->parent, - "Failed to remove GPIO OpRegion handler\n"); - return; - } - - list_for_each_entry_safe_reverse(conn, tmp, &achip->conns, node) { - gpiochip_free_own_desc(conn->desc); - list_del(&conn->node); - kfree(conn); - } -} - -static struct gpio_desc * -acpi_gpiochip_parse_own_gpio(struct acpi_gpio_chip *achip, - struct fwnode_handle *fwnode, - const char **name, - unsigned long *lflags, - enum gpiod_flags *dflags) -{ - struct gpio_chip *chip = achip->chip; - struct gpio_desc *desc; - u32 gpios[2]; - int ret; - - *lflags = GPIO_LOOKUP_FLAGS_DEFAULT; - *dflags = GPIOD_ASIS; - *name = NULL; - - ret = fwnode_property_read_u32_array(fwnode, "gpios", gpios, - ARRAY_SIZE(gpios)); - if (ret < 0) - return ERR_PTR(ret); - - desc = gpiochip_get_desc(chip, gpios[0]); - if (IS_ERR(desc)) - return desc; - - if (gpios[1]) - *lflags |= GPIO_ACTIVE_LOW; - - if (fwnode_property_present(fwnode, "input")) - *dflags |= GPIOD_IN; - else if (fwnode_property_present(fwnode, "output-low")) - *dflags |= GPIOD_OUT_LOW; - else if (fwnode_property_present(fwnode, "output-high")) - *dflags |= GPIOD_OUT_HIGH; - else - return ERR_PTR(-EINVAL); - - fwnode_property_read_string(fwnode, "line-name", name); - - return desc; -} - -static void acpi_gpiochip_scan_gpios(struct acpi_gpio_chip *achip) -{ - struct gpio_chip *chip = achip->chip; - struct fwnode_handle *fwnode; - - device_for_each_child_node(chip->parent, fwnode) { - unsigned long lflags; - enum gpiod_flags dflags; - struct gpio_desc *desc; - const char *name; - int ret; - - if (!fwnode_property_present(fwnode, "gpio-hog")) - continue; - - desc = acpi_gpiochip_parse_own_gpio(achip, fwnode, &name, - &lflags, &dflags); - if (IS_ERR(desc)) - continue; - - ret = gpiod_hog(desc, name, lflags, dflags); - if (ret) { - dev_err(chip->parent, "Failed to hog GPIO\n"); - fwnode_handle_put(fwnode); - return; - } - } -} - -void acpi_gpiochip_add(struct gpio_chip *chip) -{ - struct acpi_gpio_chip *acpi_gpio; - struct acpi_device *adev; - acpi_status status; - - if (!chip || !chip->parent) - return; - - adev = ACPI_COMPANION(chip->parent); - if (!adev) - return; - - acpi_gpio = kzalloc(sizeof(*acpi_gpio), GFP_KERNEL); - if (!acpi_gpio) { - dev_err(chip->parent, - "Failed to allocate memory for ACPI GPIO chip\n"); - return; - } - - acpi_gpio->chip = chip; - INIT_LIST_HEAD(&acpi_gpio->events); - INIT_LIST_HEAD(&acpi_gpio->deferred_req_irqs_list_entry); - - status = acpi_attach_data(adev->handle, acpi_gpio_chip_dh, acpi_gpio); - if (ACPI_FAILURE(status)) { - dev_err(chip->parent, "Failed to attach ACPI GPIO chip\n"); - kfree(acpi_gpio); - return; - } - - acpi_gpiochip_request_regions(acpi_gpio); - acpi_gpiochip_scan_gpios(acpi_gpio); - acpi_dev_clear_dependencies(adev); -} - -void acpi_gpiochip_remove(struct gpio_chip *chip) -{ - struct acpi_gpio_chip *acpi_gpio; - acpi_handle handle; - acpi_status status; - - if (!chip || !chip->parent) - return; - - handle = ACPI_HANDLE(chip->parent); - if (!handle) - return; - - status = acpi_get_data(handle, acpi_gpio_chip_dh, (void **)&acpi_gpio); - if (ACPI_FAILURE(status)) { - dev_warn(chip->parent, "Failed to retrieve ACPI GPIO chip\n"); - return; - } - - acpi_gpiochip_free_regions(acpi_gpio); - - acpi_detach_data(handle, acpi_gpio_chip_dh); - kfree(acpi_gpio); -} - -static int acpi_gpio_package_count(const union acpi_object *obj) -{ - const union acpi_object *element = obj->package.elements; - const union acpi_object *end = element + obj->package.count; - unsigned int count = 0; - - while (element < end) { - switch (element->type) { - case ACPI_TYPE_LOCAL_REFERENCE: - element += 3; - fallthrough; - case ACPI_TYPE_INTEGER: - element++; - count++; - break; - - default: - return -EPROTO; - } - } - - return count; -} - -static int acpi_find_gpio_count(struct acpi_resource *ares, void *data) -{ - unsigned int *count = data; - - if (ares->type == ACPI_RESOURCE_TYPE_GPIO) - *count += ares->data.gpio.pin_table_length; - - return 1; -} - -/** - * acpi_gpio_count - count the GPIOs associated with a firmware node / function - * @fwnode: firmware node of the GPIO consumer - * @con_id: function within the GPIO consumer - * - * Returns: - * The number of GPIOs associated with a firmware node / function or %-ENOENT, - * if no GPIO has been assigned to the requested function. - */ -int acpi_gpio_count(const struct fwnode_handle *fwnode, const char *con_id) -{ - struct acpi_device *adev = to_acpi_device_node(fwnode); - const union acpi_object *obj; - const struct acpi_gpio_mapping *gm; - int count = -ENOENT; - int ret; - char propname[32]; - - /* Try first from _DSD */ - for_each_gpio_property_name(propname, con_id) { - ret = acpi_dev_get_property(adev, propname, ACPI_TYPE_ANY, &obj); - if (ret == 0) { - if (obj->type == ACPI_TYPE_LOCAL_REFERENCE) - count = 1; - else if (obj->type == ACPI_TYPE_PACKAGE) - count = acpi_gpio_package_count(obj); - } else if (adev->driver_gpios) { - for (gm = adev->driver_gpios; gm->name; gm++) - if (strcmp(propname, gm->name) == 0) { - count = gm->size; - break; - } - } - if (count > 0) - break; - } - - /* Then from plain _CRS GPIOs */ - if (count < 0) { - struct list_head resource_list; - unsigned int crs_count = 0; - - if (!acpi_can_fallback_to_crs(adev, con_id)) - return count; - - INIT_LIST_HEAD(&resource_list); - acpi_dev_get_resources(adev, &resource_list, - acpi_find_gpio_count, &crs_count); - acpi_dev_free_resource_list(&resource_list); - if (crs_count > 0) - count = crs_count; - } - return count ? count : -ENOENT; -} - -/* Run deferred acpi_gpiochip_request_irqs() */ -static int __init acpi_gpio_handle_deferred_request_irqs(void) -{ - mutex_lock(&acpi_gpio_deferred_req_irqs_lock); - acpi_gpio_process_deferred_list(&acpi_gpio_deferred_req_irqs_list); - acpi_gpio_deferred_req_irqs_done = true; - mutex_unlock(&acpi_gpio_deferred_req_irqs_lock); - - return 0; -} -/* We must use _sync so that this runs after the first deferred_probe run */ -late_initcall_sync(acpi_gpio_handle_deferred_request_irqs); - -static const struct dmi_system_id gpiolib_acpi_quirks[] __initconst = { - { - /* - * The Minix Neo Z83-4 has a micro-USB-B id-pin handler for - * a non existing micro-USB-B connector which puts the HDMI - * DDC pins in GPIO mode, breaking HDMI support. - */ - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "MINIX"), - DMI_MATCH(DMI_PRODUCT_NAME, "Z83-4"), - }, - .driver_data = &(struct acpi_gpiolib_dmi_quirk) { - .no_edge_events_on_boot = true, - }, - }, - { - /* - * The Terra Pad 1061 has a micro-USB-B id-pin handler, which - * instead of controlling the actual micro-USB-B turns the 5V - * boost for its USB-A connector off. The actual micro-USB-B - * connector is wired for charging only. - */ - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Wortmann_AG"), - DMI_MATCH(DMI_PRODUCT_NAME, "TERRA_PAD_1061"), - }, - .driver_data = &(struct acpi_gpiolib_dmi_quirk) { - .no_edge_events_on_boot = true, - }, - }, - { - /* - * The Dell Venue 10 Pro 5055, with Bay Trail SoC + TI PMIC uses an - * external embedded-controller connected via I2C + an ACPI GPIO - * event handler on INT33FFC:02 pin 12, causing spurious wakeups. - */ - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Venue 10 Pro 5055"), - }, - .driver_data = &(struct acpi_gpiolib_dmi_quirk) { - .ignore_wake = "INT33FC:02@12", - }, - }, - { - /* - * HP X2 10 models with Cherry Trail SoC + TI PMIC use an - * external embedded-controller connected via I2C + an ACPI GPIO - * event handler on INT33FF:01 pin 0, causing spurious wakeups. - * When suspending by closing the LID, the power to the USB - * keyboard is turned off, causing INT0002 ACPI events to - * trigger once the XHCI controller notices the keyboard is - * gone. So INT0002 events cause spurious wakeups too. Ignoring - * EC wakes breaks wakeup when opening the lid, the user needs - * to press the power-button to wakeup the system. The - * alternative is suspend simply not working, which is worse. - */ - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "HP"), - DMI_MATCH(DMI_PRODUCT_NAME, "HP x2 Detachable 10-p0XX"), - }, - .driver_data = &(struct acpi_gpiolib_dmi_quirk) { - .ignore_wake = "INT33FF:01@0,INT0002:00@2", - }, - }, - { - /* - * HP X2 10 models with Bay Trail SoC + AXP288 PMIC use an - * external embedded-controller connected via I2C + an ACPI GPIO - * event handler on INT33FC:02 pin 28, causing spurious wakeups. - */ - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), - DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion x2 Detachable"), - DMI_MATCH(DMI_BOARD_NAME, "815D"), - }, - .driver_data = &(struct acpi_gpiolib_dmi_quirk) { - .ignore_wake = "INT33FC:02@28", - }, - }, - { - /* - * HP X2 10 models with Cherry Trail SoC + AXP288 PMIC use an - * external embedded-controller connected via I2C + an ACPI GPIO - * event handler on INT33FF:01 pin 0, causing spurious wakeups. - */ - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "HP"), - DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion x2 Detachable"), - DMI_MATCH(DMI_BOARD_NAME, "813E"), - }, - .driver_data = &(struct acpi_gpiolib_dmi_quirk) { - .ignore_wake = "INT33FF:01@0", - }, - }, - { - /* - * Interrupt storm caused from edge triggered floating pin - * Found in BIOS UX325UAZ.300 - * https://bugzilla.kernel.org/show_bug.cgi?id=216208 - */ - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_MATCH(DMI_PRODUCT_NAME, "ZenBook UX325UAZ_UM325UAZ"), - }, - .driver_data = &(struct acpi_gpiolib_dmi_quirk) { - .ignore_interrupt = "AMDI0030:00@18", - }, - }, - { - /* - * Spurious wakeups from TP_ATTN# pin - * Found in BIOS 1.7.8 - * https://gitlab.freedesktop.org/drm/amd/-/issues/1722#note_1720627 - */ - .matches = { - DMI_MATCH(DMI_BOARD_NAME, "NL5xNU"), - }, - .driver_data = &(struct acpi_gpiolib_dmi_quirk) { - .ignore_wake = "ELAN0415:00@9", - }, - }, - { - /* - * Spurious wakeups from TP_ATTN# pin - * Found in BIOS 1.7.8 - * https://gitlab.freedesktop.org/drm/amd/-/issues/1722#note_1720627 - */ - .matches = { - DMI_MATCH(DMI_BOARD_NAME, "NL5xRU"), - }, - .driver_data = &(struct acpi_gpiolib_dmi_quirk) { - .ignore_wake = "ELAN0415:00@9", - }, - }, - { - /* - * Spurious wakeups from TP_ATTN# pin - * Found in BIOS 1.7.7 - */ - .matches = { - DMI_MATCH(DMI_BOARD_NAME, "NH5xAx"), - }, - .driver_data = &(struct acpi_gpiolib_dmi_quirk) { - .ignore_wake = "SYNA1202:00@16", - }, - }, - { - /* - * On the Peaq C1010 2-in-1 INT33FC:00 pin 3 is connected to - * a "dolby" button. At the ACPI level an _AEI event-handler - * is connected which sets an ACPI variable to 1 on both - * edges. This variable can be polled + cleared to 0 using - * WMI. But since the variable is set on both edges the WMI - * interface is pretty useless even when polling. - * So instead the x86-android-tablets code instantiates - * a gpio-keys platform device for it. - * Ignore the _AEI handler for the pin, so that it is not busy. - */ - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "PEAQ"), - DMI_MATCH(DMI_PRODUCT_NAME, "PEAQ PMM C1010 MD99187"), - }, - .driver_data = &(struct acpi_gpiolib_dmi_quirk) { - .ignore_interrupt = "INT33FC:00@3", - }, - }, - { - /* - * Spurious wakeups from TP_ATTN# pin - * Found in BIOS 0.35 - * https://gitlab.freedesktop.org/drm/amd/-/issues/3073 - */ - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "GPD"), - DMI_MATCH(DMI_PRODUCT_NAME, "G1619-04"), - }, - .driver_data = &(struct acpi_gpiolib_dmi_quirk) { - .ignore_wake = "PNP0C50:00@8", - }, - }, - { - /* - * Spurious wakeups from GPIO 11 - * Found in BIOS 1.04 - * https://gitlab.freedesktop.org/drm/amd/-/issues/3954 - */ - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_FAMILY, "Acer Nitro V 14"), - }, - .driver_data = &(struct acpi_gpiolib_dmi_quirk) { - .ignore_interrupt = "AMDI0030:00@11", - }, - }, - {} /* Terminating entry */ -}; - -static int __init acpi_gpio_setup_params(void) -{ - const struct acpi_gpiolib_dmi_quirk *quirk = NULL; - const struct dmi_system_id *id; - - id = dmi_first_match(gpiolib_acpi_quirks); - if (id) - quirk = id->driver_data; - - if (run_edge_events_on_boot < 0) { - if (quirk && quirk->no_edge_events_on_boot) - run_edge_events_on_boot = 0; - else - run_edge_events_on_boot = 1; - } - - if (ignore_wake == NULL && quirk && quirk->ignore_wake) - ignore_wake = quirk->ignore_wake; - - if (ignore_interrupt == NULL && quirk && quirk->ignore_interrupt) - ignore_interrupt = quirk->ignore_interrupt; - - return 0; -} - -/* Directly after dmi_setup() which runs as core_initcall() */ -postcore_initcall(acpi_gpio_setup_params);