162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * ACPI helpers for GPIO API 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2012, Intel Corporation 662306a36Sopenharmony_ci * Authors: Mathias Nyman <mathias.nyman@linux.intel.com> 762306a36Sopenharmony_ci * Mika Westerberg <mika.westerberg@linux.intel.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/acpi.h> 1162306a36Sopenharmony_ci#include <linux/dmi.h> 1262306a36Sopenharmony_ci#include <linux/errno.h> 1362306a36Sopenharmony_ci#include <linux/export.h> 1462306a36Sopenharmony_ci#include <linux/interrupt.h> 1562306a36Sopenharmony_ci#include <linux/irq.h> 1662306a36Sopenharmony_ci#include <linux/mutex.h> 1762306a36Sopenharmony_ci#include <linux/pinctrl/pinctrl.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 2062306a36Sopenharmony_ci#include <linux/gpio/driver.h> 2162306a36Sopenharmony_ci#include <linux/gpio/machine.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include "gpiolib.h" 2462306a36Sopenharmony_ci#include "gpiolib-acpi.h" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic int run_edge_events_on_boot = -1; 2762306a36Sopenharmony_cimodule_param(run_edge_events_on_boot, int, 0444); 2862306a36Sopenharmony_ciMODULE_PARM_DESC(run_edge_events_on_boot, 2962306a36Sopenharmony_ci "Run edge _AEI event-handlers at boot: 0=no, 1=yes, -1=auto"); 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic char *ignore_wake; 3262306a36Sopenharmony_cimodule_param(ignore_wake, charp, 0444); 3362306a36Sopenharmony_ciMODULE_PARM_DESC(ignore_wake, 3462306a36Sopenharmony_ci "controller@pin combos on which to ignore the ACPI wake flag " 3562306a36Sopenharmony_ci "ignore_wake=controller@pin[,controller@pin[,...]]"); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic char *ignore_interrupt; 3862306a36Sopenharmony_cimodule_param(ignore_interrupt, charp, 0444); 3962306a36Sopenharmony_ciMODULE_PARM_DESC(ignore_interrupt, 4062306a36Sopenharmony_ci "controller@pin combos on which to ignore interrupt " 4162306a36Sopenharmony_ci "ignore_interrupt=controller@pin[,controller@pin[,...]]"); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistruct acpi_gpiolib_dmi_quirk { 4462306a36Sopenharmony_ci bool no_edge_events_on_boot; 4562306a36Sopenharmony_ci char *ignore_wake; 4662306a36Sopenharmony_ci char *ignore_interrupt; 4762306a36Sopenharmony_ci}; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci/** 5062306a36Sopenharmony_ci * struct acpi_gpio_event - ACPI GPIO event handler data 5162306a36Sopenharmony_ci * 5262306a36Sopenharmony_ci * @node: list-entry of the events list of the struct acpi_gpio_chip 5362306a36Sopenharmony_ci * @handle: handle of ACPI method to execute when the IRQ triggers 5462306a36Sopenharmony_ci * @handler: handler function to pass to request_irq() when requesting the IRQ 5562306a36Sopenharmony_ci * @pin: GPIO pin number on the struct gpio_chip 5662306a36Sopenharmony_ci * @irq: Linux IRQ number for the event, for request_irq() / free_irq() 5762306a36Sopenharmony_ci * @irqflags: flags to pass to request_irq() when requesting the IRQ 5862306a36Sopenharmony_ci * @irq_is_wake: If the ACPI flags indicate the IRQ is a wakeup source 5962306a36Sopenharmony_ci * @irq_requested:True if request_irq() has been done 6062306a36Sopenharmony_ci * @desc: struct gpio_desc for the GPIO pin for this event 6162306a36Sopenharmony_ci */ 6262306a36Sopenharmony_cistruct acpi_gpio_event { 6362306a36Sopenharmony_ci struct list_head node; 6462306a36Sopenharmony_ci acpi_handle handle; 6562306a36Sopenharmony_ci irq_handler_t handler; 6662306a36Sopenharmony_ci unsigned int pin; 6762306a36Sopenharmony_ci unsigned int irq; 6862306a36Sopenharmony_ci unsigned long irqflags; 6962306a36Sopenharmony_ci bool irq_is_wake; 7062306a36Sopenharmony_ci bool irq_requested; 7162306a36Sopenharmony_ci struct gpio_desc *desc; 7262306a36Sopenharmony_ci}; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistruct acpi_gpio_connection { 7562306a36Sopenharmony_ci struct list_head node; 7662306a36Sopenharmony_ci unsigned int pin; 7762306a36Sopenharmony_ci struct gpio_desc *desc; 7862306a36Sopenharmony_ci}; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistruct acpi_gpio_chip { 8162306a36Sopenharmony_ci /* 8262306a36Sopenharmony_ci * ACPICA requires that the first field of the context parameter 8362306a36Sopenharmony_ci * passed to acpi_install_address_space_handler() is large enough 8462306a36Sopenharmony_ci * to hold struct acpi_connection_info. 8562306a36Sopenharmony_ci */ 8662306a36Sopenharmony_ci struct acpi_connection_info conn_info; 8762306a36Sopenharmony_ci struct list_head conns; 8862306a36Sopenharmony_ci struct mutex conn_lock; 8962306a36Sopenharmony_ci struct gpio_chip *chip; 9062306a36Sopenharmony_ci struct list_head events; 9162306a36Sopenharmony_ci struct list_head deferred_req_irqs_list_entry; 9262306a36Sopenharmony_ci}; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci/** 9562306a36Sopenharmony_ci * struct acpi_gpio_info - ACPI GPIO specific information 9662306a36Sopenharmony_ci * @adev: reference to ACPI device which consumes GPIO resource 9762306a36Sopenharmony_ci * @flags: GPIO initialization flags 9862306a36Sopenharmony_ci * @gpioint: if %true this GPIO is of type GpioInt otherwise type is GpioIo 9962306a36Sopenharmony_ci * @pin_config: pin bias as provided by ACPI 10062306a36Sopenharmony_ci * @polarity: interrupt polarity as provided by ACPI 10162306a36Sopenharmony_ci * @triggering: triggering type as provided by ACPI 10262306a36Sopenharmony_ci * @wake_capable: wake capability as provided by ACPI 10362306a36Sopenharmony_ci * @debounce: debounce timeout as provided by ACPI 10462306a36Sopenharmony_ci * @quirks: Linux specific quirks as provided by struct acpi_gpio_mapping 10562306a36Sopenharmony_ci */ 10662306a36Sopenharmony_cistruct acpi_gpio_info { 10762306a36Sopenharmony_ci struct acpi_device *adev; 10862306a36Sopenharmony_ci enum gpiod_flags flags; 10962306a36Sopenharmony_ci bool gpioint; 11062306a36Sopenharmony_ci int pin_config; 11162306a36Sopenharmony_ci int polarity; 11262306a36Sopenharmony_ci int triggering; 11362306a36Sopenharmony_ci bool wake_capable; 11462306a36Sopenharmony_ci unsigned int debounce; 11562306a36Sopenharmony_ci unsigned int quirks; 11662306a36Sopenharmony_ci}; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci/* 11962306a36Sopenharmony_ci * For GPIO chips which call acpi_gpiochip_request_interrupts() before late_init 12062306a36Sopenharmony_ci * (so builtin drivers) we register the ACPI GpioInt IRQ handlers from a 12162306a36Sopenharmony_ci * late_initcall_sync() handler, so that other builtin drivers can register their 12262306a36Sopenharmony_ci * OpRegions before the event handlers can run. This list contains GPIO chips 12362306a36Sopenharmony_ci * for which the acpi_gpiochip_request_irqs() call has been deferred. 12462306a36Sopenharmony_ci */ 12562306a36Sopenharmony_cistatic DEFINE_MUTEX(acpi_gpio_deferred_req_irqs_lock); 12662306a36Sopenharmony_cistatic LIST_HEAD(acpi_gpio_deferred_req_irqs_list); 12762306a36Sopenharmony_cistatic bool acpi_gpio_deferred_req_irqs_done; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic int acpi_gpiochip_find(struct gpio_chip *gc, void *data) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci return device_match_acpi_handle(&gc->gpiodev->dev, data); 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci/** 13562306a36Sopenharmony_ci * acpi_get_gpiod() - Translate ACPI GPIO pin to GPIO descriptor usable with GPIO API 13662306a36Sopenharmony_ci * @path: ACPI GPIO controller full path name, (e.g. "\\_SB.GPO1") 13762306a36Sopenharmony_ci * @pin: ACPI GPIO pin number (0-based, controller-relative) 13862306a36Sopenharmony_ci * 13962306a36Sopenharmony_ci * Return: GPIO descriptor to use with Linux generic GPIO API, or ERR_PTR 14062306a36Sopenharmony_ci * error value. Specifically returns %-EPROBE_DEFER if the referenced GPIO 14162306a36Sopenharmony_ci * controller does not have GPIO chip registered at the moment. This is to 14262306a36Sopenharmony_ci * support probe deferral. 14362306a36Sopenharmony_ci */ 14462306a36Sopenharmony_cistatic struct gpio_desc *acpi_get_gpiod(char *path, unsigned int pin) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci struct gpio_chip *chip; 14762306a36Sopenharmony_ci acpi_handle handle; 14862306a36Sopenharmony_ci acpi_status status; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci status = acpi_get_handle(NULL, path, &handle); 15162306a36Sopenharmony_ci if (ACPI_FAILURE(status)) 15262306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci chip = gpiochip_find(handle, acpi_gpiochip_find); 15562306a36Sopenharmony_ci if (!chip) 15662306a36Sopenharmony_ci return ERR_PTR(-EPROBE_DEFER); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci return gpiochip_get_desc(chip, pin); 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci/** 16262306a36Sopenharmony_ci * acpi_get_and_request_gpiod - Translate ACPI GPIO pin to GPIO descriptor and 16362306a36Sopenharmony_ci * hold a refcount to the GPIO device. 16462306a36Sopenharmony_ci * @path: ACPI GPIO controller full path name, (e.g. "\\_SB.GPO1") 16562306a36Sopenharmony_ci * @pin: ACPI GPIO pin number (0-based, controller-relative) 16662306a36Sopenharmony_ci * @label: Label to pass to gpiod_request() 16762306a36Sopenharmony_ci * 16862306a36Sopenharmony_ci * This function is a simple pass-through to acpi_get_gpiod(), except that 16962306a36Sopenharmony_ci * as it is intended for use outside of the GPIO layer (in a similar fashion to 17062306a36Sopenharmony_ci * gpiod_get_index() for example) it also holds a reference to the GPIO device. 17162306a36Sopenharmony_ci */ 17262306a36Sopenharmony_cistruct gpio_desc *acpi_get_and_request_gpiod(char *path, unsigned int pin, char *label) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci struct gpio_desc *gpio; 17562306a36Sopenharmony_ci int ret; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci gpio = acpi_get_gpiod(path, pin); 17862306a36Sopenharmony_ci if (IS_ERR(gpio)) 17962306a36Sopenharmony_ci return gpio; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci ret = gpiod_request(gpio, label); 18262306a36Sopenharmony_ci if (ret) 18362306a36Sopenharmony_ci return ERR_PTR(ret); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci return gpio; 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(acpi_get_and_request_gpiod); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic irqreturn_t acpi_gpio_irq_handler(int irq, void *data) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci struct acpi_gpio_event *event = data; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci acpi_evaluate_object(event->handle, NULL, NULL, NULL); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci return IRQ_HANDLED; 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_cistatic irqreturn_t acpi_gpio_irq_handler_evt(int irq, void *data) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci struct acpi_gpio_event *event = data; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci acpi_execute_simple_method(event->handle, NULL, event->pin); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci return IRQ_HANDLED; 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistatic void acpi_gpio_chip_dh(acpi_handle handle, void *data) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci /* The address of this function is used as a key. */ 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cibool acpi_gpio_get_irq_resource(struct acpi_resource *ares, 21362306a36Sopenharmony_ci struct acpi_resource_gpio **agpio) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci struct acpi_resource_gpio *gpio; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci if (ares->type != ACPI_RESOURCE_TYPE_GPIO) 21862306a36Sopenharmony_ci return false; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci gpio = &ares->data.gpio; 22162306a36Sopenharmony_ci if (gpio->connection_type != ACPI_RESOURCE_GPIO_TYPE_INT) 22262306a36Sopenharmony_ci return false; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci *agpio = gpio; 22562306a36Sopenharmony_ci return true; 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(acpi_gpio_get_irq_resource); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci/** 23062306a36Sopenharmony_ci * acpi_gpio_get_io_resource - Fetch details of an ACPI resource if it is a GPIO 23162306a36Sopenharmony_ci * I/O resource or return False if not. 23262306a36Sopenharmony_ci * @ares: Pointer to the ACPI resource to fetch 23362306a36Sopenharmony_ci * @agpio: Pointer to a &struct acpi_resource_gpio to store the output pointer 23462306a36Sopenharmony_ci */ 23562306a36Sopenharmony_cibool acpi_gpio_get_io_resource(struct acpi_resource *ares, 23662306a36Sopenharmony_ci struct acpi_resource_gpio **agpio) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci struct acpi_resource_gpio *gpio; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci if (ares->type != ACPI_RESOURCE_TYPE_GPIO) 24162306a36Sopenharmony_ci return false; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci gpio = &ares->data.gpio; 24462306a36Sopenharmony_ci if (gpio->connection_type != ACPI_RESOURCE_GPIO_TYPE_IO) 24562306a36Sopenharmony_ci return false; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci *agpio = gpio; 24862306a36Sopenharmony_ci return true; 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(acpi_gpio_get_io_resource); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cistatic void acpi_gpiochip_request_irq(struct acpi_gpio_chip *acpi_gpio, 25362306a36Sopenharmony_ci struct acpi_gpio_event *event) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci struct device *parent = acpi_gpio->chip->parent; 25662306a36Sopenharmony_ci int ret, value; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci ret = request_threaded_irq(event->irq, NULL, event->handler, 25962306a36Sopenharmony_ci event->irqflags | IRQF_ONESHOT, "ACPI:Event", event); 26062306a36Sopenharmony_ci if (ret) { 26162306a36Sopenharmony_ci dev_err(parent, "Failed to setup interrupt handler for %d\n", event->irq); 26262306a36Sopenharmony_ci return; 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci if (event->irq_is_wake) 26662306a36Sopenharmony_ci enable_irq_wake(event->irq); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci event->irq_requested = true; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci /* Make sure we trigger the initial state of edge-triggered IRQs */ 27162306a36Sopenharmony_ci if (run_edge_events_on_boot && 27262306a36Sopenharmony_ci (event->irqflags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING))) { 27362306a36Sopenharmony_ci value = gpiod_get_raw_value_cansleep(event->desc); 27462306a36Sopenharmony_ci if (((event->irqflags & IRQF_TRIGGER_RISING) && value == 1) || 27562306a36Sopenharmony_ci ((event->irqflags & IRQF_TRIGGER_FALLING) && value == 0)) 27662306a36Sopenharmony_ci event->handler(event->irq, event); 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cistatic void acpi_gpiochip_request_irqs(struct acpi_gpio_chip *acpi_gpio) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci struct acpi_gpio_event *event; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci list_for_each_entry(event, &acpi_gpio->events, node) 28562306a36Sopenharmony_ci acpi_gpiochip_request_irq(acpi_gpio, event); 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_cistatic enum gpiod_flags 28962306a36Sopenharmony_ciacpi_gpio_to_gpiod_flags(const struct acpi_resource_gpio *agpio, int polarity) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci /* GpioInt() implies input configuration */ 29262306a36Sopenharmony_ci if (agpio->connection_type == ACPI_RESOURCE_GPIO_TYPE_INT) 29362306a36Sopenharmony_ci return GPIOD_IN; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci switch (agpio->io_restriction) { 29662306a36Sopenharmony_ci case ACPI_IO_RESTRICT_INPUT: 29762306a36Sopenharmony_ci return GPIOD_IN; 29862306a36Sopenharmony_ci case ACPI_IO_RESTRICT_OUTPUT: 29962306a36Sopenharmony_ci /* 30062306a36Sopenharmony_ci * ACPI GPIO resources don't contain an initial value for the 30162306a36Sopenharmony_ci * GPIO. Therefore we deduce that value from the pull field 30262306a36Sopenharmony_ci * and the polarity instead. If the pin is pulled up we assume 30362306a36Sopenharmony_ci * default to be high, if it is pulled down we assume default 30462306a36Sopenharmony_ci * to be low, otherwise we leave pin untouched. For active low 30562306a36Sopenharmony_ci * polarity values will be switched. See also 30662306a36Sopenharmony_ci * Documentation/firmware-guide/acpi/gpio-properties.rst. 30762306a36Sopenharmony_ci */ 30862306a36Sopenharmony_ci switch (agpio->pin_config) { 30962306a36Sopenharmony_ci case ACPI_PIN_CONFIG_PULLUP: 31062306a36Sopenharmony_ci return polarity == GPIO_ACTIVE_LOW ? GPIOD_OUT_LOW : GPIOD_OUT_HIGH; 31162306a36Sopenharmony_ci case ACPI_PIN_CONFIG_PULLDOWN: 31262306a36Sopenharmony_ci return polarity == GPIO_ACTIVE_LOW ? GPIOD_OUT_HIGH : GPIOD_OUT_LOW; 31362306a36Sopenharmony_ci default: 31462306a36Sopenharmony_ci break; 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci break; 31762306a36Sopenharmony_ci default: 31862306a36Sopenharmony_ci break; 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci /* 32262306a36Sopenharmony_ci * Assume that the BIOS has configured the direction and pull 32362306a36Sopenharmony_ci * accordingly. 32462306a36Sopenharmony_ci */ 32562306a36Sopenharmony_ci return GPIOD_ASIS; 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cistatic struct gpio_desc *acpi_request_own_gpiod(struct gpio_chip *chip, 32962306a36Sopenharmony_ci struct acpi_resource_gpio *agpio, 33062306a36Sopenharmony_ci unsigned int index, 33162306a36Sopenharmony_ci const char *label) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci int polarity = GPIO_ACTIVE_HIGH; 33462306a36Sopenharmony_ci enum gpiod_flags flags = acpi_gpio_to_gpiod_flags(agpio, polarity); 33562306a36Sopenharmony_ci unsigned int pin = agpio->pin_table[index]; 33662306a36Sopenharmony_ci struct gpio_desc *desc; 33762306a36Sopenharmony_ci int ret; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci desc = gpiochip_request_own_desc(chip, pin, label, polarity, flags); 34062306a36Sopenharmony_ci if (IS_ERR(desc)) 34162306a36Sopenharmony_ci return desc; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci /* ACPI uses hundredths of milliseconds units */ 34462306a36Sopenharmony_ci ret = gpio_set_debounce_timeout(desc, agpio->debounce_timeout * 10); 34562306a36Sopenharmony_ci if (ret) 34662306a36Sopenharmony_ci dev_warn(chip->parent, 34762306a36Sopenharmony_ci "Failed to set debounce-timeout for pin 0x%04X, err %d\n", 34862306a36Sopenharmony_ci pin, ret); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci return desc; 35162306a36Sopenharmony_ci} 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_cistatic bool acpi_gpio_in_ignore_list(const char *ignore_list, const char *controller_in, 35462306a36Sopenharmony_ci unsigned int pin_in) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci const char *controller, *pin_str; 35762306a36Sopenharmony_ci unsigned int pin; 35862306a36Sopenharmony_ci char *endp; 35962306a36Sopenharmony_ci int len; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci controller = ignore_list; 36262306a36Sopenharmony_ci while (controller) { 36362306a36Sopenharmony_ci pin_str = strchr(controller, '@'); 36462306a36Sopenharmony_ci if (!pin_str) 36562306a36Sopenharmony_ci goto err; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci len = pin_str - controller; 36862306a36Sopenharmony_ci if (len == strlen(controller_in) && 36962306a36Sopenharmony_ci strncmp(controller, controller_in, len) == 0) { 37062306a36Sopenharmony_ci pin = simple_strtoul(pin_str + 1, &endp, 10); 37162306a36Sopenharmony_ci if (*endp != 0 && *endp != ',') 37262306a36Sopenharmony_ci goto err; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci if (pin == pin_in) 37562306a36Sopenharmony_ci return true; 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci controller = strchr(controller, ','); 37962306a36Sopenharmony_ci if (controller) 38062306a36Sopenharmony_ci controller++; 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci return false; 38462306a36Sopenharmony_cierr: 38562306a36Sopenharmony_ci pr_err_once("Error: Invalid value for gpiolib_acpi.ignore_...: %s\n", ignore_list); 38662306a36Sopenharmony_ci return false; 38762306a36Sopenharmony_ci} 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_cistatic bool acpi_gpio_irq_is_wake(struct device *parent, 39062306a36Sopenharmony_ci const struct acpi_resource_gpio *agpio) 39162306a36Sopenharmony_ci{ 39262306a36Sopenharmony_ci unsigned int pin = agpio->pin_table[0]; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci if (agpio->wake_capable != ACPI_WAKE_CAPABLE) 39562306a36Sopenharmony_ci return false; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci if (acpi_gpio_in_ignore_list(ignore_wake, dev_name(parent), pin)) { 39862306a36Sopenharmony_ci dev_info(parent, "Ignoring wakeup on pin %u\n", pin); 39962306a36Sopenharmony_ci return false; 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci return true; 40362306a36Sopenharmony_ci} 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci/* Always returns AE_OK so that we keep looping over the resources */ 40662306a36Sopenharmony_cistatic acpi_status acpi_gpiochip_alloc_event(struct acpi_resource *ares, 40762306a36Sopenharmony_ci void *context) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci struct acpi_gpio_chip *acpi_gpio = context; 41062306a36Sopenharmony_ci struct gpio_chip *chip = acpi_gpio->chip; 41162306a36Sopenharmony_ci struct acpi_resource_gpio *agpio; 41262306a36Sopenharmony_ci acpi_handle handle, evt_handle; 41362306a36Sopenharmony_ci struct acpi_gpio_event *event; 41462306a36Sopenharmony_ci irq_handler_t handler = NULL; 41562306a36Sopenharmony_ci struct gpio_desc *desc; 41662306a36Sopenharmony_ci unsigned int pin; 41762306a36Sopenharmony_ci int ret, irq; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci if (!acpi_gpio_get_irq_resource(ares, &agpio)) 42062306a36Sopenharmony_ci return AE_OK; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci handle = ACPI_HANDLE(chip->parent); 42362306a36Sopenharmony_ci pin = agpio->pin_table[0]; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci if (pin <= 255) { 42662306a36Sopenharmony_ci char ev_name[8]; 42762306a36Sopenharmony_ci sprintf(ev_name, "_%c%02X", 42862306a36Sopenharmony_ci agpio->triggering == ACPI_EDGE_SENSITIVE ? 'E' : 'L', 42962306a36Sopenharmony_ci pin); 43062306a36Sopenharmony_ci if (ACPI_SUCCESS(acpi_get_handle(handle, ev_name, &evt_handle))) 43162306a36Sopenharmony_ci handler = acpi_gpio_irq_handler; 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci if (!handler) { 43462306a36Sopenharmony_ci if (ACPI_SUCCESS(acpi_get_handle(handle, "_EVT", &evt_handle))) 43562306a36Sopenharmony_ci handler = acpi_gpio_irq_handler_evt; 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci if (!handler) 43862306a36Sopenharmony_ci return AE_OK; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci desc = acpi_request_own_gpiod(chip, agpio, 0, "ACPI:Event"); 44162306a36Sopenharmony_ci if (IS_ERR(desc)) { 44262306a36Sopenharmony_ci dev_err(chip->parent, 44362306a36Sopenharmony_ci "Failed to request GPIO for pin 0x%04X, err %ld\n", 44462306a36Sopenharmony_ci pin, PTR_ERR(desc)); 44562306a36Sopenharmony_ci return AE_OK; 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci ret = gpiochip_lock_as_irq(chip, pin); 44962306a36Sopenharmony_ci if (ret) { 45062306a36Sopenharmony_ci dev_err(chip->parent, 45162306a36Sopenharmony_ci "Failed to lock GPIO pin 0x%04X as interrupt, err %d\n", 45262306a36Sopenharmony_ci pin, ret); 45362306a36Sopenharmony_ci goto fail_free_desc; 45462306a36Sopenharmony_ci } 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci irq = gpiod_to_irq(desc); 45762306a36Sopenharmony_ci if (irq < 0) { 45862306a36Sopenharmony_ci dev_err(chip->parent, 45962306a36Sopenharmony_ci "Failed to translate GPIO pin 0x%04X to IRQ, err %d\n", 46062306a36Sopenharmony_ci pin, irq); 46162306a36Sopenharmony_ci goto fail_unlock_irq; 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci if (acpi_gpio_in_ignore_list(ignore_interrupt, dev_name(chip->parent), pin)) { 46562306a36Sopenharmony_ci dev_info(chip->parent, "Ignoring interrupt on pin %u\n", pin); 46662306a36Sopenharmony_ci return AE_OK; 46762306a36Sopenharmony_ci } 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci event = kzalloc(sizeof(*event), GFP_KERNEL); 47062306a36Sopenharmony_ci if (!event) 47162306a36Sopenharmony_ci goto fail_unlock_irq; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci event->irqflags = IRQF_ONESHOT; 47462306a36Sopenharmony_ci if (agpio->triggering == ACPI_LEVEL_SENSITIVE) { 47562306a36Sopenharmony_ci if (agpio->polarity == ACPI_ACTIVE_HIGH) 47662306a36Sopenharmony_ci event->irqflags |= IRQF_TRIGGER_HIGH; 47762306a36Sopenharmony_ci else 47862306a36Sopenharmony_ci event->irqflags |= IRQF_TRIGGER_LOW; 47962306a36Sopenharmony_ci } else { 48062306a36Sopenharmony_ci switch (agpio->polarity) { 48162306a36Sopenharmony_ci case ACPI_ACTIVE_HIGH: 48262306a36Sopenharmony_ci event->irqflags |= IRQF_TRIGGER_RISING; 48362306a36Sopenharmony_ci break; 48462306a36Sopenharmony_ci case ACPI_ACTIVE_LOW: 48562306a36Sopenharmony_ci event->irqflags |= IRQF_TRIGGER_FALLING; 48662306a36Sopenharmony_ci break; 48762306a36Sopenharmony_ci default: 48862306a36Sopenharmony_ci event->irqflags |= IRQF_TRIGGER_RISING | 48962306a36Sopenharmony_ci IRQF_TRIGGER_FALLING; 49062306a36Sopenharmony_ci break; 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci } 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci event->handle = evt_handle; 49562306a36Sopenharmony_ci event->handler = handler; 49662306a36Sopenharmony_ci event->irq = irq; 49762306a36Sopenharmony_ci event->irq_is_wake = acpi_gpio_irq_is_wake(chip->parent, agpio); 49862306a36Sopenharmony_ci event->pin = pin; 49962306a36Sopenharmony_ci event->desc = desc; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci list_add_tail(&event->node, &acpi_gpio->events); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci return AE_OK; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_cifail_unlock_irq: 50662306a36Sopenharmony_ci gpiochip_unlock_as_irq(chip, pin); 50762306a36Sopenharmony_cifail_free_desc: 50862306a36Sopenharmony_ci gpiochip_free_own_desc(desc); 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci return AE_OK; 51162306a36Sopenharmony_ci} 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci/** 51462306a36Sopenharmony_ci * acpi_gpiochip_request_interrupts() - Register isr for gpio chip ACPI events 51562306a36Sopenharmony_ci * @chip: GPIO chip 51662306a36Sopenharmony_ci * 51762306a36Sopenharmony_ci * ACPI5 platforms can use GPIO signaled ACPI events. These GPIO interrupts are 51862306a36Sopenharmony_ci * handled by ACPI event methods which need to be called from the GPIO 51962306a36Sopenharmony_ci * chip's interrupt handler. acpi_gpiochip_request_interrupts() finds out which 52062306a36Sopenharmony_ci * GPIO pins have ACPI event methods and assigns interrupt handlers that calls 52162306a36Sopenharmony_ci * the ACPI event methods for those pins. 52262306a36Sopenharmony_ci */ 52362306a36Sopenharmony_civoid acpi_gpiochip_request_interrupts(struct gpio_chip *chip) 52462306a36Sopenharmony_ci{ 52562306a36Sopenharmony_ci struct acpi_gpio_chip *acpi_gpio; 52662306a36Sopenharmony_ci acpi_handle handle; 52762306a36Sopenharmony_ci acpi_status status; 52862306a36Sopenharmony_ci bool defer; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci if (!chip->parent || !chip->to_irq) 53162306a36Sopenharmony_ci return; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci handle = ACPI_HANDLE(chip->parent); 53462306a36Sopenharmony_ci if (!handle) 53562306a36Sopenharmony_ci return; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci status = acpi_get_data(handle, acpi_gpio_chip_dh, (void **)&acpi_gpio); 53862306a36Sopenharmony_ci if (ACPI_FAILURE(status)) 53962306a36Sopenharmony_ci return; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci if (acpi_quirk_skip_gpio_event_handlers()) 54262306a36Sopenharmony_ci return; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci acpi_walk_resources(handle, METHOD_NAME__AEI, 54562306a36Sopenharmony_ci acpi_gpiochip_alloc_event, acpi_gpio); 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci mutex_lock(&acpi_gpio_deferred_req_irqs_lock); 54862306a36Sopenharmony_ci defer = !acpi_gpio_deferred_req_irqs_done; 54962306a36Sopenharmony_ci if (defer) 55062306a36Sopenharmony_ci list_add(&acpi_gpio->deferred_req_irqs_list_entry, 55162306a36Sopenharmony_ci &acpi_gpio_deferred_req_irqs_list); 55262306a36Sopenharmony_ci mutex_unlock(&acpi_gpio_deferred_req_irqs_lock); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci if (defer) 55562306a36Sopenharmony_ci return; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci acpi_gpiochip_request_irqs(acpi_gpio); 55862306a36Sopenharmony_ci} 55962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(acpi_gpiochip_request_interrupts); 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci/** 56262306a36Sopenharmony_ci * acpi_gpiochip_free_interrupts() - Free GPIO ACPI event interrupts. 56362306a36Sopenharmony_ci * @chip: GPIO chip 56462306a36Sopenharmony_ci * 56562306a36Sopenharmony_ci * Free interrupts associated with GPIO ACPI event method for the given 56662306a36Sopenharmony_ci * GPIO chip. 56762306a36Sopenharmony_ci */ 56862306a36Sopenharmony_civoid acpi_gpiochip_free_interrupts(struct gpio_chip *chip) 56962306a36Sopenharmony_ci{ 57062306a36Sopenharmony_ci struct acpi_gpio_chip *acpi_gpio; 57162306a36Sopenharmony_ci struct acpi_gpio_event *event, *ep; 57262306a36Sopenharmony_ci acpi_handle handle; 57362306a36Sopenharmony_ci acpi_status status; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci if (!chip->parent || !chip->to_irq) 57662306a36Sopenharmony_ci return; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci handle = ACPI_HANDLE(chip->parent); 57962306a36Sopenharmony_ci if (!handle) 58062306a36Sopenharmony_ci return; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci status = acpi_get_data(handle, acpi_gpio_chip_dh, (void **)&acpi_gpio); 58362306a36Sopenharmony_ci if (ACPI_FAILURE(status)) 58462306a36Sopenharmony_ci return; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci mutex_lock(&acpi_gpio_deferred_req_irqs_lock); 58762306a36Sopenharmony_ci if (!list_empty(&acpi_gpio->deferred_req_irqs_list_entry)) 58862306a36Sopenharmony_ci list_del_init(&acpi_gpio->deferred_req_irqs_list_entry); 58962306a36Sopenharmony_ci mutex_unlock(&acpi_gpio_deferred_req_irqs_lock); 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci list_for_each_entry_safe_reverse(event, ep, &acpi_gpio->events, node) { 59262306a36Sopenharmony_ci if (event->irq_requested) { 59362306a36Sopenharmony_ci if (event->irq_is_wake) 59462306a36Sopenharmony_ci disable_irq_wake(event->irq); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci free_irq(event->irq, event); 59762306a36Sopenharmony_ci } 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci gpiochip_unlock_as_irq(chip, event->pin); 60062306a36Sopenharmony_ci gpiochip_free_own_desc(event->desc); 60162306a36Sopenharmony_ci list_del(&event->node); 60262306a36Sopenharmony_ci kfree(event); 60362306a36Sopenharmony_ci } 60462306a36Sopenharmony_ci} 60562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(acpi_gpiochip_free_interrupts); 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ciint acpi_dev_add_driver_gpios(struct acpi_device *adev, 60862306a36Sopenharmony_ci const struct acpi_gpio_mapping *gpios) 60962306a36Sopenharmony_ci{ 61062306a36Sopenharmony_ci if (adev && gpios) { 61162306a36Sopenharmony_ci adev->driver_gpios = gpios; 61262306a36Sopenharmony_ci return 0; 61362306a36Sopenharmony_ci } 61462306a36Sopenharmony_ci return -EINVAL; 61562306a36Sopenharmony_ci} 61662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(acpi_dev_add_driver_gpios); 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_civoid acpi_dev_remove_driver_gpios(struct acpi_device *adev) 61962306a36Sopenharmony_ci{ 62062306a36Sopenharmony_ci if (adev) 62162306a36Sopenharmony_ci adev->driver_gpios = NULL; 62262306a36Sopenharmony_ci} 62362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(acpi_dev_remove_driver_gpios); 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_cistatic void acpi_dev_release_driver_gpios(void *adev) 62662306a36Sopenharmony_ci{ 62762306a36Sopenharmony_ci acpi_dev_remove_driver_gpios(adev); 62862306a36Sopenharmony_ci} 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ciint devm_acpi_dev_add_driver_gpios(struct device *dev, 63162306a36Sopenharmony_ci const struct acpi_gpio_mapping *gpios) 63262306a36Sopenharmony_ci{ 63362306a36Sopenharmony_ci struct acpi_device *adev = ACPI_COMPANION(dev); 63462306a36Sopenharmony_ci int ret; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci ret = acpi_dev_add_driver_gpios(adev, gpios); 63762306a36Sopenharmony_ci if (ret) 63862306a36Sopenharmony_ci return ret; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci return devm_add_action_or_reset(dev, acpi_dev_release_driver_gpios, adev); 64162306a36Sopenharmony_ci} 64262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_acpi_dev_add_driver_gpios); 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_cistatic bool acpi_get_driver_gpio_data(struct acpi_device *adev, 64562306a36Sopenharmony_ci const char *name, int index, 64662306a36Sopenharmony_ci struct fwnode_reference_args *args, 64762306a36Sopenharmony_ci unsigned int *quirks) 64862306a36Sopenharmony_ci{ 64962306a36Sopenharmony_ci const struct acpi_gpio_mapping *gm; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci if (!adev || !adev->driver_gpios) 65262306a36Sopenharmony_ci return false; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci for (gm = adev->driver_gpios; gm->name; gm++) 65562306a36Sopenharmony_ci if (!strcmp(name, gm->name) && gm->data && index < gm->size) { 65662306a36Sopenharmony_ci const struct acpi_gpio_params *par = gm->data + index; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci args->fwnode = acpi_fwnode_handle(adev); 65962306a36Sopenharmony_ci args->args[0] = par->crs_entry_index; 66062306a36Sopenharmony_ci args->args[1] = par->line_index; 66162306a36Sopenharmony_ci args->args[2] = par->active_low; 66262306a36Sopenharmony_ci args->nargs = 3; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci *quirks = gm->quirks; 66562306a36Sopenharmony_ci return true; 66662306a36Sopenharmony_ci } 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci return false; 66962306a36Sopenharmony_ci} 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_cistatic int 67262306a36Sopenharmony_ci__acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, enum gpiod_flags update) 67362306a36Sopenharmony_ci{ 67462306a36Sopenharmony_ci const enum gpiod_flags mask = 67562306a36Sopenharmony_ci GPIOD_FLAGS_BIT_DIR_SET | GPIOD_FLAGS_BIT_DIR_OUT | 67662306a36Sopenharmony_ci GPIOD_FLAGS_BIT_DIR_VAL; 67762306a36Sopenharmony_ci int ret = 0; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci /* 68062306a36Sopenharmony_ci * Check if the BIOS has IoRestriction with explicitly set direction 68162306a36Sopenharmony_ci * and update @flags accordingly. Otherwise use whatever caller asked 68262306a36Sopenharmony_ci * for. 68362306a36Sopenharmony_ci */ 68462306a36Sopenharmony_ci if (update & GPIOD_FLAGS_BIT_DIR_SET) { 68562306a36Sopenharmony_ci enum gpiod_flags diff = *flags ^ update; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci /* 68862306a36Sopenharmony_ci * Check if caller supplied incompatible GPIO initialization 68962306a36Sopenharmony_ci * flags. 69062306a36Sopenharmony_ci * 69162306a36Sopenharmony_ci * Return %-EINVAL to notify that firmware has different 69262306a36Sopenharmony_ci * settings and we are going to use them. 69362306a36Sopenharmony_ci */ 69462306a36Sopenharmony_ci if (((*flags & GPIOD_FLAGS_BIT_DIR_SET) && (diff & GPIOD_FLAGS_BIT_DIR_OUT)) || 69562306a36Sopenharmony_ci ((*flags & GPIOD_FLAGS_BIT_DIR_OUT) && (diff & GPIOD_FLAGS_BIT_DIR_VAL))) 69662306a36Sopenharmony_ci ret = -EINVAL; 69762306a36Sopenharmony_ci *flags = (*flags & ~mask) | (update & mask); 69862306a36Sopenharmony_ci } 69962306a36Sopenharmony_ci return ret; 70062306a36Sopenharmony_ci} 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_cistatic int acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, 70362306a36Sopenharmony_ci struct acpi_gpio_info *info) 70462306a36Sopenharmony_ci{ 70562306a36Sopenharmony_ci struct device *dev = &info->adev->dev; 70662306a36Sopenharmony_ci enum gpiod_flags old = *flags; 70762306a36Sopenharmony_ci int ret; 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci ret = __acpi_gpio_update_gpiod_flags(&old, info->flags); 71062306a36Sopenharmony_ci if (info->quirks & ACPI_GPIO_QUIRK_NO_IO_RESTRICTION) { 71162306a36Sopenharmony_ci if (ret) 71262306a36Sopenharmony_ci dev_warn(dev, FW_BUG "GPIO not in correct mode, fixing\n"); 71362306a36Sopenharmony_ci } else { 71462306a36Sopenharmony_ci if (ret) 71562306a36Sopenharmony_ci dev_dbg(dev, "Override GPIO initialization flags\n"); 71662306a36Sopenharmony_ci *flags = old; 71762306a36Sopenharmony_ci } 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci return ret; 72062306a36Sopenharmony_ci} 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_cistatic int acpi_gpio_update_gpiod_lookup_flags(unsigned long *lookupflags, 72362306a36Sopenharmony_ci struct acpi_gpio_info *info) 72462306a36Sopenharmony_ci{ 72562306a36Sopenharmony_ci switch (info->pin_config) { 72662306a36Sopenharmony_ci case ACPI_PIN_CONFIG_PULLUP: 72762306a36Sopenharmony_ci *lookupflags |= GPIO_PULL_UP; 72862306a36Sopenharmony_ci break; 72962306a36Sopenharmony_ci case ACPI_PIN_CONFIG_PULLDOWN: 73062306a36Sopenharmony_ci *lookupflags |= GPIO_PULL_DOWN; 73162306a36Sopenharmony_ci break; 73262306a36Sopenharmony_ci case ACPI_PIN_CONFIG_NOPULL: 73362306a36Sopenharmony_ci *lookupflags |= GPIO_PULL_DISABLE; 73462306a36Sopenharmony_ci break; 73562306a36Sopenharmony_ci default: 73662306a36Sopenharmony_ci break; 73762306a36Sopenharmony_ci } 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci if (info->polarity == GPIO_ACTIVE_LOW) 74062306a36Sopenharmony_ci *lookupflags |= GPIO_ACTIVE_LOW; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci return 0; 74362306a36Sopenharmony_ci} 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_cistruct acpi_gpio_lookup { 74662306a36Sopenharmony_ci struct acpi_gpio_info info; 74762306a36Sopenharmony_ci int index; 74862306a36Sopenharmony_ci u16 pin_index; 74962306a36Sopenharmony_ci bool active_low; 75062306a36Sopenharmony_ci struct gpio_desc *desc; 75162306a36Sopenharmony_ci int n; 75262306a36Sopenharmony_ci}; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_cistatic int acpi_populate_gpio_lookup(struct acpi_resource *ares, void *data) 75562306a36Sopenharmony_ci{ 75662306a36Sopenharmony_ci struct acpi_gpio_lookup *lookup = data; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci if (ares->type != ACPI_RESOURCE_TYPE_GPIO) 75962306a36Sopenharmony_ci return 1; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci if (!lookup->desc) { 76262306a36Sopenharmony_ci const struct acpi_resource_gpio *agpio = &ares->data.gpio; 76362306a36Sopenharmony_ci bool gpioint = agpio->connection_type == ACPI_RESOURCE_GPIO_TYPE_INT; 76462306a36Sopenharmony_ci struct gpio_desc *desc; 76562306a36Sopenharmony_ci u16 pin_index; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci if (lookup->info.quirks & ACPI_GPIO_QUIRK_ONLY_GPIOIO && gpioint) 76862306a36Sopenharmony_ci lookup->index++; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci if (lookup->n++ != lookup->index) 77162306a36Sopenharmony_ci return 1; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci pin_index = lookup->pin_index; 77462306a36Sopenharmony_ci if (pin_index >= agpio->pin_table_length) 77562306a36Sopenharmony_ci return 1; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci if (lookup->info.quirks & ACPI_GPIO_QUIRK_ABSOLUTE_NUMBER) 77862306a36Sopenharmony_ci desc = gpio_to_desc(agpio->pin_table[pin_index]); 77962306a36Sopenharmony_ci else 78062306a36Sopenharmony_ci desc = acpi_get_gpiod(agpio->resource_source.string_ptr, 78162306a36Sopenharmony_ci agpio->pin_table[pin_index]); 78262306a36Sopenharmony_ci lookup->desc = desc; 78362306a36Sopenharmony_ci lookup->info.pin_config = agpio->pin_config; 78462306a36Sopenharmony_ci lookup->info.debounce = agpio->debounce_timeout; 78562306a36Sopenharmony_ci lookup->info.gpioint = gpioint; 78662306a36Sopenharmony_ci lookup->info.wake_capable = acpi_gpio_irq_is_wake(&lookup->info.adev->dev, agpio); 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci /* 78962306a36Sopenharmony_ci * Polarity and triggering are only specified for GpioInt 79062306a36Sopenharmony_ci * resource. 79162306a36Sopenharmony_ci * Note: we expect here: 79262306a36Sopenharmony_ci * - ACPI_ACTIVE_LOW == GPIO_ACTIVE_LOW 79362306a36Sopenharmony_ci * - ACPI_ACTIVE_HIGH == GPIO_ACTIVE_HIGH 79462306a36Sopenharmony_ci */ 79562306a36Sopenharmony_ci if (lookup->info.gpioint) { 79662306a36Sopenharmony_ci lookup->info.polarity = agpio->polarity; 79762306a36Sopenharmony_ci lookup->info.triggering = agpio->triggering; 79862306a36Sopenharmony_ci } else { 79962306a36Sopenharmony_ci lookup->info.polarity = lookup->active_low; 80062306a36Sopenharmony_ci } 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci lookup->info.flags = acpi_gpio_to_gpiod_flags(agpio, lookup->info.polarity); 80362306a36Sopenharmony_ci } 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci return 1; 80662306a36Sopenharmony_ci} 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_cistatic int acpi_gpio_resource_lookup(struct acpi_gpio_lookup *lookup, 80962306a36Sopenharmony_ci struct acpi_gpio_info *info) 81062306a36Sopenharmony_ci{ 81162306a36Sopenharmony_ci struct acpi_device *adev = lookup->info.adev; 81262306a36Sopenharmony_ci struct list_head res_list; 81362306a36Sopenharmony_ci int ret; 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci INIT_LIST_HEAD(&res_list); 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci ret = acpi_dev_get_resources(adev, &res_list, 81862306a36Sopenharmony_ci acpi_populate_gpio_lookup, 81962306a36Sopenharmony_ci lookup); 82062306a36Sopenharmony_ci if (ret < 0) 82162306a36Sopenharmony_ci return ret; 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci acpi_dev_free_resource_list(&res_list); 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci if (!lookup->desc) 82662306a36Sopenharmony_ci return -ENOENT; 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci if (info) 82962306a36Sopenharmony_ci *info = lookup->info; 83062306a36Sopenharmony_ci return 0; 83162306a36Sopenharmony_ci} 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_cistatic int acpi_gpio_property_lookup(struct fwnode_handle *fwnode, 83462306a36Sopenharmony_ci const char *propname, int index, 83562306a36Sopenharmony_ci struct acpi_gpio_lookup *lookup) 83662306a36Sopenharmony_ci{ 83762306a36Sopenharmony_ci struct fwnode_reference_args args; 83862306a36Sopenharmony_ci unsigned int quirks = 0; 83962306a36Sopenharmony_ci int ret; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci memset(&args, 0, sizeof(args)); 84262306a36Sopenharmony_ci ret = __acpi_node_get_property_reference(fwnode, propname, index, 3, 84362306a36Sopenharmony_ci &args); 84462306a36Sopenharmony_ci if (ret) { 84562306a36Sopenharmony_ci struct acpi_device *adev; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci adev = to_acpi_device_node(fwnode); 84862306a36Sopenharmony_ci if (!acpi_get_driver_gpio_data(adev, propname, index, &args, &quirks)) 84962306a36Sopenharmony_ci return ret; 85062306a36Sopenharmony_ci } 85162306a36Sopenharmony_ci /* 85262306a36Sopenharmony_ci * The property was found and resolved, so need to lookup the GPIO based 85362306a36Sopenharmony_ci * on returned args. 85462306a36Sopenharmony_ci */ 85562306a36Sopenharmony_ci if (!to_acpi_device_node(args.fwnode)) 85662306a36Sopenharmony_ci return -EINVAL; 85762306a36Sopenharmony_ci if (args.nargs != 3) 85862306a36Sopenharmony_ci return -EPROTO; 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci lookup->index = args.args[0]; 86162306a36Sopenharmony_ci lookup->pin_index = args.args[1]; 86262306a36Sopenharmony_ci lookup->active_low = !!args.args[2]; 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci lookup->info.adev = to_acpi_device_node(args.fwnode); 86562306a36Sopenharmony_ci lookup->info.quirks = quirks; 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci return 0; 86862306a36Sopenharmony_ci} 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci/** 87162306a36Sopenharmony_ci * acpi_get_gpiod_by_index() - get a GPIO descriptor from device resources 87262306a36Sopenharmony_ci * @adev: pointer to a ACPI device to get GPIO from 87362306a36Sopenharmony_ci * @propname: Property name of the GPIO (optional) 87462306a36Sopenharmony_ci * @index: index of GpioIo/GpioInt resource (starting from %0) 87562306a36Sopenharmony_ci * @info: info pointer to fill in (optional) 87662306a36Sopenharmony_ci * 87762306a36Sopenharmony_ci * Function goes through ACPI resources for @adev and based on @index looks 87862306a36Sopenharmony_ci * up a GpioIo/GpioInt resource, translates it to the Linux GPIO descriptor, 87962306a36Sopenharmony_ci * and returns it. @index matches GpioIo/GpioInt resources only so if there 88062306a36Sopenharmony_ci * are total %3 GPIO resources, the index goes from %0 to %2. 88162306a36Sopenharmony_ci * 88262306a36Sopenharmony_ci * If @propname is specified the GPIO is looked using device property. In 88362306a36Sopenharmony_ci * that case @index is used to select the GPIO entry in the property value 88462306a36Sopenharmony_ci * (in case of multiple). 88562306a36Sopenharmony_ci * 88662306a36Sopenharmony_ci * If the GPIO cannot be translated or there is an error, an ERR_PTR is 88762306a36Sopenharmony_ci * returned. 88862306a36Sopenharmony_ci * 88962306a36Sopenharmony_ci * Note: if the GPIO resource has multiple entries in the pin list, this 89062306a36Sopenharmony_ci * function only returns the first. 89162306a36Sopenharmony_ci */ 89262306a36Sopenharmony_cistatic struct gpio_desc *acpi_get_gpiod_by_index(struct acpi_device *adev, 89362306a36Sopenharmony_ci const char *propname, 89462306a36Sopenharmony_ci int index, 89562306a36Sopenharmony_ci struct acpi_gpio_info *info) 89662306a36Sopenharmony_ci{ 89762306a36Sopenharmony_ci struct acpi_gpio_lookup lookup; 89862306a36Sopenharmony_ci int ret; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci if (!adev) 90162306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci memset(&lookup, 0, sizeof(lookup)); 90462306a36Sopenharmony_ci lookup.index = index; 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci if (propname) { 90762306a36Sopenharmony_ci dev_dbg(&adev->dev, "GPIO: looking up %s\n", propname); 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci ret = acpi_gpio_property_lookup(acpi_fwnode_handle(adev), 91062306a36Sopenharmony_ci propname, index, &lookup); 91162306a36Sopenharmony_ci if (ret) 91262306a36Sopenharmony_ci return ERR_PTR(ret); 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci dev_dbg(&adev->dev, "GPIO: _DSD returned %s %d %u %u\n", 91562306a36Sopenharmony_ci dev_name(&lookup.info.adev->dev), lookup.index, 91662306a36Sopenharmony_ci lookup.pin_index, lookup.active_low); 91762306a36Sopenharmony_ci } else { 91862306a36Sopenharmony_ci dev_dbg(&adev->dev, "GPIO: looking up %d in _CRS\n", index); 91962306a36Sopenharmony_ci lookup.info.adev = adev; 92062306a36Sopenharmony_ci } 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci ret = acpi_gpio_resource_lookup(&lookup, info); 92362306a36Sopenharmony_ci return ret ? ERR_PTR(ret) : lookup.desc; 92462306a36Sopenharmony_ci} 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci/** 92762306a36Sopenharmony_ci * acpi_get_gpiod_from_data() - get a GPIO descriptor from ACPI data node 92862306a36Sopenharmony_ci * @fwnode: pointer to an ACPI firmware node to get the GPIO information from 92962306a36Sopenharmony_ci * @propname: Property name of the GPIO 93062306a36Sopenharmony_ci * @index: index of GpioIo/GpioInt resource (starting from %0) 93162306a36Sopenharmony_ci * @info: info pointer to fill in (optional) 93262306a36Sopenharmony_ci * 93362306a36Sopenharmony_ci * This function uses the property-based GPIO lookup to get to the GPIO 93462306a36Sopenharmony_ci * resource with the relevant information from a data-only ACPI firmware node 93562306a36Sopenharmony_ci * and uses that to obtain the GPIO descriptor to return. 93662306a36Sopenharmony_ci * 93762306a36Sopenharmony_ci * If the GPIO cannot be translated or there is an error an ERR_PTR is 93862306a36Sopenharmony_ci * returned. 93962306a36Sopenharmony_ci */ 94062306a36Sopenharmony_cistatic struct gpio_desc *acpi_get_gpiod_from_data(struct fwnode_handle *fwnode, 94162306a36Sopenharmony_ci const char *propname, 94262306a36Sopenharmony_ci int index, 94362306a36Sopenharmony_ci struct acpi_gpio_info *info) 94462306a36Sopenharmony_ci{ 94562306a36Sopenharmony_ci struct acpi_gpio_lookup lookup; 94662306a36Sopenharmony_ci int ret; 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci if (!is_acpi_data_node(fwnode)) 94962306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci if (!propname) 95262306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci memset(&lookup, 0, sizeof(lookup)); 95562306a36Sopenharmony_ci lookup.index = index; 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci ret = acpi_gpio_property_lookup(fwnode, propname, index, &lookup); 95862306a36Sopenharmony_ci if (ret) 95962306a36Sopenharmony_ci return ERR_PTR(ret); 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci ret = acpi_gpio_resource_lookup(&lookup, info); 96262306a36Sopenharmony_ci return ret ? ERR_PTR(ret) : lookup.desc; 96362306a36Sopenharmony_ci} 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_cistatic bool acpi_can_fallback_to_crs(struct acpi_device *adev, 96662306a36Sopenharmony_ci const char *con_id) 96762306a36Sopenharmony_ci{ 96862306a36Sopenharmony_ci /* Never allow fallback if the device has properties */ 96962306a36Sopenharmony_ci if (acpi_dev_has_props(adev) || adev->driver_gpios) 97062306a36Sopenharmony_ci return false; 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci return con_id == NULL; 97362306a36Sopenharmony_ci} 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_cistruct gpio_desc *acpi_find_gpio(struct fwnode_handle *fwnode, 97662306a36Sopenharmony_ci const char *con_id, 97762306a36Sopenharmony_ci unsigned int idx, 97862306a36Sopenharmony_ci enum gpiod_flags *dflags, 97962306a36Sopenharmony_ci unsigned long *lookupflags) 98062306a36Sopenharmony_ci{ 98162306a36Sopenharmony_ci struct acpi_device *adev = to_acpi_device_node(fwnode); 98262306a36Sopenharmony_ci struct acpi_gpio_info info; 98362306a36Sopenharmony_ci struct gpio_desc *desc; 98462306a36Sopenharmony_ci char propname[32]; 98562306a36Sopenharmony_ci int i; 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci /* Try first from _DSD */ 98862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) { 98962306a36Sopenharmony_ci if (con_id) { 99062306a36Sopenharmony_ci snprintf(propname, sizeof(propname), "%s-%s", 99162306a36Sopenharmony_ci con_id, gpio_suffixes[i]); 99262306a36Sopenharmony_ci } else { 99362306a36Sopenharmony_ci snprintf(propname, sizeof(propname), "%s", 99462306a36Sopenharmony_ci gpio_suffixes[i]); 99562306a36Sopenharmony_ci } 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci if (adev) 99862306a36Sopenharmony_ci desc = acpi_get_gpiod_by_index(adev, 99962306a36Sopenharmony_ci propname, idx, &info); 100062306a36Sopenharmony_ci else 100162306a36Sopenharmony_ci desc = acpi_get_gpiod_from_data(fwnode, 100262306a36Sopenharmony_ci propname, idx, &info); 100362306a36Sopenharmony_ci if (!IS_ERR(desc)) 100462306a36Sopenharmony_ci break; 100562306a36Sopenharmony_ci if (PTR_ERR(desc) == -EPROBE_DEFER) 100662306a36Sopenharmony_ci return ERR_CAST(desc); 100762306a36Sopenharmony_ci } 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci /* Then from plain _CRS GPIOs */ 101062306a36Sopenharmony_ci if (IS_ERR(desc)) { 101162306a36Sopenharmony_ci if (!adev || !acpi_can_fallback_to_crs(adev, con_id)) 101262306a36Sopenharmony_ci return ERR_PTR(-ENOENT); 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci desc = acpi_get_gpiod_by_index(adev, NULL, idx, &info); 101562306a36Sopenharmony_ci if (IS_ERR(desc)) 101662306a36Sopenharmony_ci return desc; 101762306a36Sopenharmony_ci } 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci if (info.gpioint && 102062306a36Sopenharmony_ci (*dflags == GPIOD_OUT_LOW || *dflags == GPIOD_OUT_HIGH)) { 102162306a36Sopenharmony_ci dev_dbg(&adev->dev, "refusing GpioInt() entry when doing GPIOD_OUT_* lookup\n"); 102262306a36Sopenharmony_ci return ERR_PTR(-ENOENT); 102362306a36Sopenharmony_ci } 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci acpi_gpio_update_gpiod_flags(dflags, &info); 102662306a36Sopenharmony_ci acpi_gpio_update_gpiod_lookup_flags(lookupflags, &info); 102762306a36Sopenharmony_ci return desc; 102862306a36Sopenharmony_ci} 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci/** 103162306a36Sopenharmony_ci * acpi_dev_gpio_irq_wake_get_by() - Find GpioInt and translate it to Linux IRQ number 103262306a36Sopenharmony_ci * @adev: pointer to a ACPI device to get IRQ from 103362306a36Sopenharmony_ci * @name: optional name of GpioInt resource 103462306a36Sopenharmony_ci * @index: index of GpioInt resource (starting from %0) 103562306a36Sopenharmony_ci * @wake_capable: Set to true if the IRQ is wake capable 103662306a36Sopenharmony_ci * 103762306a36Sopenharmony_ci * If the device has one or more GpioInt resources, this function can be 103862306a36Sopenharmony_ci * used to translate from the GPIO offset in the resource to the Linux IRQ 103962306a36Sopenharmony_ci * number. 104062306a36Sopenharmony_ci * 104162306a36Sopenharmony_ci * The function is idempotent, though each time it runs it will configure GPIO 104262306a36Sopenharmony_ci * pin direction according to the flags in GpioInt resource. 104362306a36Sopenharmony_ci * 104462306a36Sopenharmony_ci * The function takes optional @name parameter. If the resource has a property 104562306a36Sopenharmony_ci * name, then only those will be taken into account. 104662306a36Sopenharmony_ci * 104762306a36Sopenharmony_ci * The GPIO is considered wake capable if the GpioInt resource specifies 104862306a36Sopenharmony_ci * SharedAndWake or ExclusiveAndWake. 104962306a36Sopenharmony_ci * 105062306a36Sopenharmony_ci * Return: Linux IRQ number (> %0) on success, negative errno on failure. 105162306a36Sopenharmony_ci */ 105262306a36Sopenharmony_ciint acpi_dev_gpio_irq_wake_get_by(struct acpi_device *adev, const char *name, int index, 105362306a36Sopenharmony_ci bool *wake_capable) 105462306a36Sopenharmony_ci{ 105562306a36Sopenharmony_ci int idx, i; 105662306a36Sopenharmony_ci unsigned int irq_flags; 105762306a36Sopenharmony_ci int ret; 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci for (i = 0, idx = 0; idx <= index; i++) { 106062306a36Sopenharmony_ci struct acpi_gpio_info info; 106162306a36Sopenharmony_ci struct gpio_desc *desc; 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci desc = acpi_get_gpiod_by_index(adev, name, i, &info); 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci /* Ignore -EPROBE_DEFER, it only matters if idx matches */ 106662306a36Sopenharmony_ci if (IS_ERR(desc) && PTR_ERR(desc) != -EPROBE_DEFER) 106762306a36Sopenharmony_ci return PTR_ERR(desc); 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci if (info.gpioint && idx++ == index) { 107062306a36Sopenharmony_ci unsigned long lflags = GPIO_LOOKUP_FLAGS_DEFAULT; 107162306a36Sopenharmony_ci enum gpiod_flags dflags = GPIOD_ASIS; 107262306a36Sopenharmony_ci char label[32]; 107362306a36Sopenharmony_ci int irq; 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci if (IS_ERR(desc)) 107662306a36Sopenharmony_ci return PTR_ERR(desc); 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci irq = gpiod_to_irq(desc); 107962306a36Sopenharmony_ci if (irq < 0) 108062306a36Sopenharmony_ci return irq; 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci acpi_gpio_update_gpiod_flags(&dflags, &info); 108362306a36Sopenharmony_ci acpi_gpio_update_gpiod_lookup_flags(&lflags, &info); 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci snprintf(label, sizeof(label), "GpioInt() %d", index); 108662306a36Sopenharmony_ci ret = gpiod_configure_flags(desc, label, lflags, dflags); 108762306a36Sopenharmony_ci if (ret < 0) 108862306a36Sopenharmony_ci return ret; 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci /* ACPI uses hundredths of milliseconds units */ 109162306a36Sopenharmony_ci ret = gpio_set_debounce_timeout(desc, info.debounce * 10); 109262306a36Sopenharmony_ci if (ret) 109362306a36Sopenharmony_ci return ret; 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci irq_flags = acpi_dev_get_irq_type(info.triggering, 109662306a36Sopenharmony_ci info.polarity); 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci /* 109962306a36Sopenharmony_ci * If the IRQ is not already in use then set type 110062306a36Sopenharmony_ci * if specified and different than the current one. 110162306a36Sopenharmony_ci */ 110262306a36Sopenharmony_ci if (can_request_irq(irq, irq_flags)) { 110362306a36Sopenharmony_ci if (irq_flags != IRQ_TYPE_NONE && 110462306a36Sopenharmony_ci irq_flags != irq_get_trigger_type(irq)) 110562306a36Sopenharmony_ci irq_set_irq_type(irq, irq_flags); 110662306a36Sopenharmony_ci } else { 110762306a36Sopenharmony_ci dev_dbg(&adev->dev, "IRQ %d already in use\n", irq); 110862306a36Sopenharmony_ci } 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci /* avoid suspend issues with GPIOs when systems are using S3 */ 111162306a36Sopenharmony_ci if (wake_capable && acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0) 111262306a36Sopenharmony_ci *wake_capable = info.wake_capable; 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci return irq; 111562306a36Sopenharmony_ci } 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci } 111862306a36Sopenharmony_ci return -ENOENT; 111962306a36Sopenharmony_ci} 112062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(acpi_dev_gpio_irq_wake_get_by); 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_cistatic acpi_status 112362306a36Sopenharmony_ciacpi_gpio_adr_space_handler(u32 function, acpi_physical_address address, 112462306a36Sopenharmony_ci u32 bits, u64 *value, void *handler_context, 112562306a36Sopenharmony_ci void *region_context) 112662306a36Sopenharmony_ci{ 112762306a36Sopenharmony_ci struct acpi_gpio_chip *achip = region_context; 112862306a36Sopenharmony_ci struct gpio_chip *chip = achip->chip; 112962306a36Sopenharmony_ci struct acpi_resource_gpio *agpio; 113062306a36Sopenharmony_ci struct acpi_resource *ares; 113162306a36Sopenharmony_ci u16 pin_index = address; 113262306a36Sopenharmony_ci acpi_status status; 113362306a36Sopenharmony_ci int length; 113462306a36Sopenharmony_ci int i; 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci status = acpi_buffer_to_resource(achip->conn_info.connection, 113762306a36Sopenharmony_ci achip->conn_info.length, &ares); 113862306a36Sopenharmony_ci if (ACPI_FAILURE(status)) 113962306a36Sopenharmony_ci return status; 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci if (WARN_ON(ares->type != ACPI_RESOURCE_TYPE_GPIO)) { 114262306a36Sopenharmony_ci ACPI_FREE(ares); 114362306a36Sopenharmony_ci return AE_BAD_PARAMETER; 114462306a36Sopenharmony_ci } 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci agpio = &ares->data.gpio; 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_ci if (WARN_ON(agpio->io_restriction == ACPI_IO_RESTRICT_INPUT && 114962306a36Sopenharmony_ci function == ACPI_WRITE)) { 115062306a36Sopenharmony_ci ACPI_FREE(ares); 115162306a36Sopenharmony_ci return AE_BAD_PARAMETER; 115262306a36Sopenharmony_ci } 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci length = min_t(u16, agpio->pin_table_length, pin_index + bits); 115562306a36Sopenharmony_ci for (i = pin_index; i < length; ++i) { 115662306a36Sopenharmony_ci unsigned int pin = agpio->pin_table[i]; 115762306a36Sopenharmony_ci struct acpi_gpio_connection *conn; 115862306a36Sopenharmony_ci struct gpio_desc *desc; 115962306a36Sopenharmony_ci bool found; 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci mutex_lock(&achip->conn_lock); 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci found = false; 116462306a36Sopenharmony_ci list_for_each_entry(conn, &achip->conns, node) { 116562306a36Sopenharmony_ci if (conn->pin == pin) { 116662306a36Sopenharmony_ci found = true; 116762306a36Sopenharmony_ci desc = conn->desc; 116862306a36Sopenharmony_ci break; 116962306a36Sopenharmony_ci } 117062306a36Sopenharmony_ci } 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci /* 117362306a36Sopenharmony_ci * The same GPIO can be shared between operation region and 117462306a36Sopenharmony_ci * event but only if the access here is ACPI_READ. In that 117562306a36Sopenharmony_ci * case we "borrow" the event GPIO instead. 117662306a36Sopenharmony_ci */ 117762306a36Sopenharmony_ci if (!found && agpio->shareable == ACPI_SHARED && 117862306a36Sopenharmony_ci function == ACPI_READ) { 117962306a36Sopenharmony_ci struct acpi_gpio_event *event; 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci list_for_each_entry(event, &achip->events, node) { 118262306a36Sopenharmony_ci if (event->pin == pin) { 118362306a36Sopenharmony_ci desc = event->desc; 118462306a36Sopenharmony_ci found = true; 118562306a36Sopenharmony_ci break; 118662306a36Sopenharmony_ci } 118762306a36Sopenharmony_ci } 118862306a36Sopenharmony_ci } 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci if (!found) { 119162306a36Sopenharmony_ci desc = acpi_request_own_gpiod(chip, agpio, i, "ACPI:OpRegion"); 119262306a36Sopenharmony_ci if (IS_ERR(desc)) { 119362306a36Sopenharmony_ci mutex_unlock(&achip->conn_lock); 119462306a36Sopenharmony_ci status = AE_ERROR; 119562306a36Sopenharmony_ci goto out; 119662306a36Sopenharmony_ci } 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ci conn = kzalloc(sizeof(*conn), GFP_KERNEL); 119962306a36Sopenharmony_ci if (!conn) { 120062306a36Sopenharmony_ci gpiochip_free_own_desc(desc); 120162306a36Sopenharmony_ci mutex_unlock(&achip->conn_lock); 120262306a36Sopenharmony_ci status = AE_NO_MEMORY; 120362306a36Sopenharmony_ci goto out; 120462306a36Sopenharmony_ci } 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci conn->pin = pin; 120762306a36Sopenharmony_ci conn->desc = desc; 120862306a36Sopenharmony_ci list_add_tail(&conn->node, &achip->conns); 120962306a36Sopenharmony_ci } 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci mutex_unlock(&achip->conn_lock); 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci if (function == ACPI_WRITE) 121462306a36Sopenharmony_ci gpiod_set_raw_value_cansleep(desc, !!(*value & BIT(i))); 121562306a36Sopenharmony_ci else 121662306a36Sopenharmony_ci *value |= (u64)gpiod_get_raw_value_cansleep(desc) << i; 121762306a36Sopenharmony_ci } 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ciout: 122062306a36Sopenharmony_ci ACPI_FREE(ares); 122162306a36Sopenharmony_ci return status; 122262306a36Sopenharmony_ci} 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_cistatic void acpi_gpiochip_request_regions(struct acpi_gpio_chip *achip) 122562306a36Sopenharmony_ci{ 122662306a36Sopenharmony_ci struct gpio_chip *chip = achip->chip; 122762306a36Sopenharmony_ci acpi_handle handle = ACPI_HANDLE(chip->parent); 122862306a36Sopenharmony_ci acpi_status status; 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci INIT_LIST_HEAD(&achip->conns); 123162306a36Sopenharmony_ci mutex_init(&achip->conn_lock); 123262306a36Sopenharmony_ci status = acpi_install_address_space_handler(handle, ACPI_ADR_SPACE_GPIO, 123362306a36Sopenharmony_ci acpi_gpio_adr_space_handler, 123462306a36Sopenharmony_ci NULL, achip); 123562306a36Sopenharmony_ci if (ACPI_FAILURE(status)) 123662306a36Sopenharmony_ci dev_err(chip->parent, 123762306a36Sopenharmony_ci "Failed to install GPIO OpRegion handler\n"); 123862306a36Sopenharmony_ci} 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_cistatic void acpi_gpiochip_free_regions(struct acpi_gpio_chip *achip) 124162306a36Sopenharmony_ci{ 124262306a36Sopenharmony_ci struct gpio_chip *chip = achip->chip; 124362306a36Sopenharmony_ci acpi_handle handle = ACPI_HANDLE(chip->parent); 124462306a36Sopenharmony_ci struct acpi_gpio_connection *conn, *tmp; 124562306a36Sopenharmony_ci acpi_status status; 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_ci status = acpi_remove_address_space_handler(handle, ACPI_ADR_SPACE_GPIO, 124862306a36Sopenharmony_ci acpi_gpio_adr_space_handler); 124962306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 125062306a36Sopenharmony_ci dev_err(chip->parent, 125162306a36Sopenharmony_ci "Failed to remove GPIO OpRegion handler\n"); 125262306a36Sopenharmony_ci return; 125362306a36Sopenharmony_ci } 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci list_for_each_entry_safe_reverse(conn, tmp, &achip->conns, node) { 125662306a36Sopenharmony_ci gpiochip_free_own_desc(conn->desc); 125762306a36Sopenharmony_ci list_del(&conn->node); 125862306a36Sopenharmony_ci kfree(conn); 125962306a36Sopenharmony_ci } 126062306a36Sopenharmony_ci} 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_cistatic struct gpio_desc * 126362306a36Sopenharmony_ciacpi_gpiochip_parse_own_gpio(struct acpi_gpio_chip *achip, 126462306a36Sopenharmony_ci struct fwnode_handle *fwnode, 126562306a36Sopenharmony_ci const char **name, 126662306a36Sopenharmony_ci unsigned long *lflags, 126762306a36Sopenharmony_ci enum gpiod_flags *dflags) 126862306a36Sopenharmony_ci{ 126962306a36Sopenharmony_ci struct gpio_chip *chip = achip->chip; 127062306a36Sopenharmony_ci struct gpio_desc *desc; 127162306a36Sopenharmony_ci u32 gpios[2]; 127262306a36Sopenharmony_ci int ret; 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_ci *lflags = GPIO_LOOKUP_FLAGS_DEFAULT; 127562306a36Sopenharmony_ci *dflags = GPIOD_ASIS; 127662306a36Sopenharmony_ci *name = NULL; 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci ret = fwnode_property_read_u32_array(fwnode, "gpios", gpios, 127962306a36Sopenharmony_ci ARRAY_SIZE(gpios)); 128062306a36Sopenharmony_ci if (ret < 0) 128162306a36Sopenharmony_ci return ERR_PTR(ret); 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_ci desc = gpiochip_get_desc(chip, gpios[0]); 128462306a36Sopenharmony_ci if (IS_ERR(desc)) 128562306a36Sopenharmony_ci return desc; 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci if (gpios[1]) 128862306a36Sopenharmony_ci *lflags |= GPIO_ACTIVE_LOW; 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci if (fwnode_property_present(fwnode, "input")) 129162306a36Sopenharmony_ci *dflags |= GPIOD_IN; 129262306a36Sopenharmony_ci else if (fwnode_property_present(fwnode, "output-low")) 129362306a36Sopenharmony_ci *dflags |= GPIOD_OUT_LOW; 129462306a36Sopenharmony_ci else if (fwnode_property_present(fwnode, "output-high")) 129562306a36Sopenharmony_ci *dflags |= GPIOD_OUT_HIGH; 129662306a36Sopenharmony_ci else 129762306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci fwnode_property_read_string(fwnode, "line-name", name); 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_ci return desc; 130262306a36Sopenharmony_ci} 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_cistatic void acpi_gpiochip_scan_gpios(struct acpi_gpio_chip *achip) 130562306a36Sopenharmony_ci{ 130662306a36Sopenharmony_ci struct gpio_chip *chip = achip->chip; 130762306a36Sopenharmony_ci struct fwnode_handle *fwnode; 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci device_for_each_child_node(chip->parent, fwnode) { 131062306a36Sopenharmony_ci unsigned long lflags; 131162306a36Sopenharmony_ci enum gpiod_flags dflags; 131262306a36Sopenharmony_ci struct gpio_desc *desc; 131362306a36Sopenharmony_ci const char *name; 131462306a36Sopenharmony_ci int ret; 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_ci if (!fwnode_property_present(fwnode, "gpio-hog")) 131762306a36Sopenharmony_ci continue; 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci desc = acpi_gpiochip_parse_own_gpio(achip, fwnode, &name, 132062306a36Sopenharmony_ci &lflags, &dflags); 132162306a36Sopenharmony_ci if (IS_ERR(desc)) 132262306a36Sopenharmony_ci continue; 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci ret = gpiod_hog(desc, name, lflags, dflags); 132562306a36Sopenharmony_ci if (ret) { 132662306a36Sopenharmony_ci dev_err(chip->parent, "Failed to hog GPIO\n"); 132762306a36Sopenharmony_ci fwnode_handle_put(fwnode); 132862306a36Sopenharmony_ci return; 132962306a36Sopenharmony_ci } 133062306a36Sopenharmony_ci } 133162306a36Sopenharmony_ci} 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_civoid acpi_gpiochip_add(struct gpio_chip *chip) 133462306a36Sopenharmony_ci{ 133562306a36Sopenharmony_ci struct acpi_gpio_chip *acpi_gpio; 133662306a36Sopenharmony_ci struct acpi_device *adev; 133762306a36Sopenharmony_ci acpi_status status; 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_ci if (!chip || !chip->parent) 134062306a36Sopenharmony_ci return; 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_ci adev = ACPI_COMPANION(chip->parent); 134362306a36Sopenharmony_ci if (!adev) 134462306a36Sopenharmony_ci return; 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_ci acpi_gpio = kzalloc(sizeof(*acpi_gpio), GFP_KERNEL); 134762306a36Sopenharmony_ci if (!acpi_gpio) { 134862306a36Sopenharmony_ci dev_err(chip->parent, 134962306a36Sopenharmony_ci "Failed to allocate memory for ACPI GPIO chip\n"); 135062306a36Sopenharmony_ci return; 135162306a36Sopenharmony_ci } 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci acpi_gpio->chip = chip; 135462306a36Sopenharmony_ci INIT_LIST_HEAD(&acpi_gpio->events); 135562306a36Sopenharmony_ci INIT_LIST_HEAD(&acpi_gpio->deferred_req_irqs_list_entry); 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_ci status = acpi_attach_data(adev->handle, acpi_gpio_chip_dh, acpi_gpio); 135862306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 135962306a36Sopenharmony_ci dev_err(chip->parent, "Failed to attach ACPI GPIO chip\n"); 136062306a36Sopenharmony_ci kfree(acpi_gpio); 136162306a36Sopenharmony_ci return; 136262306a36Sopenharmony_ci } 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci acpi_gpiochip_request_regions(acpi_gpio); 136562306a36Sopenharmony_ci acpi_gpiochip_scan_gpios(acpi_gpio); 136662306a36Sopenharmony_ci acpi_dev_clear_dependencies(adev); 136762306a36Sopenharmony_ci} 136862306a36Sopenharmony_ci 136962306a36Sopenharmony_civoid acpi_gpiochip_remove(struct gpio_chip *chip) 137062306a36Sopenharmony_ci{ 137162306a36Sopenharmony_ci struct acpi_gpio_chip *acpi_gpio; 137262306a36Sopenharmony_ci acpi_handle handle; 137362306a36Sopenharmony_ci acpi_status status; 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ci if (!chip || !chip->parent) 137662306a36Sopenharmony_ci return; 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_ci handle = ACPI_HANDLE(chip->parent); 137962306a36Sopenharmony_ci if (!handle) 138062306a36Sopenharmony_ci return; 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_ci status = acpi_get_data(handle, acpi_gpio_chip_dh, (void **)&acpi_gpio); 138362306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 138462306a36Sopenharmony_ci dev_warn(chip->parent, "Failed to retrieve ACPI GPIO chip\n"); 138562306a36Sopenharmony_ci return; 138662306a36Sopenharmony_ci } 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_ci acpi_gpiochip_free_regions(acpi_gpio); 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci acpi_detach_data(handle, acpi_gpio_chip_dh); 139162306a36Sopenharmony_ci kfree(acpi_gpio); 139262306a36Sopenharmony_ci} 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_cistatic int acpi_gpio_package_count(const union acpi_object *obj) 139562306a36Sopenharmony_ci{ 139662306a36Sopenharmony_ci const union acpi_object *element = obj->package.elements; 139762306a36Sopenharmony_ci const union acpi_object *end = element + obj->package.count; 139862306a36Sopenharmony_ci unsigned int count = 0; 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_ci while (element < end) { 140162306a36Sopenharmony_ci switch (element->type) { 140262306a36Sopenharmony_ci case ACPI_TYPE_LOCAL_REFERENCE: 140362306a36Sopenharmony_ci element += 3; 140462306a36Sopenharmony_ci fallthrough; 140562306a36Sopenharmony_ci case ACPI_TYPE_INTEGER: 140662306a36Sopenharmony_ci element++; 140762306a36Sopenharmony_ci count++; 140862306a36Sopenharmony_ci break; 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_ci default: 141162306a36Sopenharmony_ci return -EPROTO; 141262306a36Sopenharmony_ci } 141362306a36Sopenharmony_ci } 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_ci return count; 141662306a36Sopenharmony_ci} 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_cistatic int acpi_find_gpio_count(struct acpi_resource *ares, void *data) 141962306a36Sopenharmony_ci{ 142062306a36Sopenharmony_ci unsigned int *count = data; 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_ci if (ares->type == ACPI_RESOURCE_TYPE_GPIO) 142362306a36Sopenharmony_ci *count += ares->data.gpio.pin_table_length; 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_ci return 1; 142662306a36Sopenharmony_ci} 142762306a36Sopenharmony_ci 142862306a36Sopenharmony_ci/** 142962306a36Sopenharmony_ci * acpi_gpio_count - count the GPIOs associated with a device / function 143062306a36Sopenharmony_ci * @dev: GPIO consumer, can be %NULL for system-global GPIOs 143162306a36Sopenharmony_ci * @con_id: function within the GPIO consumer 143262306a36Sopenharmony_ci * 143362306a36Sopenharmony_ci * Return: 143462306a36Sopenharmony_ci * The number of GPIOs associated with a device / function or %-ENOENT, 143562306a36Sopenharmony_ci * if no GPIO has been assigned to the requested function. 143662306a36Sopenharmony_ci */ 143762306a36Sopenharmony_ciint acpi_gpio_count(struct device *dev, const char *con_id) 143862306a36Sopenharmony_ci{ 143962306a36Sopenharmony_ci struct acpi_device *adev = ACPI_COMPANION(dev); 144062306a36Sopenharmony_ci const union acpi_object *obj; 144162306a36Sopenharmony_ci const struct acpi_gpio_mapping *gm; 144262306a36Sopenharmony_ci int count = -ENOENT; 144362306a36Sopenharmony_ci int ret; 144462306a36Sopenharmony_ci char propname[32]; 144562306a36Sopenharmony_ci unsigned int i; 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_ci /* Try first from _DSD */ 144862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) { 144962306a36Sopenharmony_ci if (con_id) 145062306a36Sopenharmony_ci snprintf(propname, sizeof(propname), "%s-%s", 145162306a36Sopenharmony_ci con_id, gpio_suffixes[i]); 145262306a36Sopenharmony_ci else 145362306a36Sopenharmony_ci snprintf(propname, sizeof(propname), "%s", 145462306a36Sopenharmony_ci gpio_suffixes[i]); 145562306a36Sopenharmony_ci 145662306a36Sopenharmony_ci ret = acpi_dev_get_property(adev, propname, ACPI_TYPE_ANY, 145762306a36Sopenharmony_ci &obj); 145862306a36Sopenharmony_ci if (ret == 0) { 145962306a36Sopenharmony_ci if (obj->type == ACPI_TYPE_LOCAL_REFERENCE) 146062306a36Sopenharmony_ci count = 1; 146162306a36Sopenharmony_ci else if (obj->type == ACPI_TYPE_PACKAGE) 146262306a36Sopenharmony_ci count = acpi_gpio_package_count(obj); 146362306a36Sopenharmony_ci } else if (adev->driver_gpios) { 146462306a36Sopenharmony_ci for (gm = adev->driver_gpios; gm->name; gm++) 146562306a36Sopenharmony_ci if (strcmp(propname, gm->name) == 0) { 146662306a36Sopenharmony_ci count = gm->size; 146762306a36Sopenharmony_ci break; 146862306a36Sopenharmony_ci } 146962306a36Sopenharmony_ci } 147062306a36Sopenharmony_ci if (count > 0) 147162306a36Sopenharmony_ci break; 147262306a36Sopenharmony_ci } 147362306a36Sopenharmony_ci 147462306a36Sopenharmony_ci /* Then from plain _CRS GPIOs */ 147562306a36Sopenharmony_ci if (count < 0) { 147662306a36Sopenharmony_ci struct list_head resource_list; 147762306a36Sopenharmony_ci unsigned int crs_count = 0; 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_ci if (!acpi_can_fallback_to_crs(adev, con_id)) 148062306a36Sopenharmony_ci return count; 148162306a36Sopenharmony_ci 148262306a36Sopenharmony_ci INIT_LIST_HEAD(&resource_list); 148362306a36Sopenharmony_ci acpi_dev_get_resources(adev, &resource_list, 148462306a36Sopenharmony_ci acpi_find_gpio_count, &crs_count); 148562306a36Sopenharmony_ci acpi_dev_free_resource_list(&resource_list); 148662306a36Sopenharmony_ci if (crs_count > 0) 148762306a36Sopenharmony_ci count = crs_count; 148862306a36Sopenharmony_ci } 148962306a36Sopenharmony_ci return count ? count : -ENOENT; 149062306a36Sopenharmony_ci} 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_ci/* Run deferred acpi_gpiochip_request_irqs() */ 149362306a36Sopenharmony_cistatic int __init acpi_gpio_handle_deferred_request_irqs(void) 149462306a36Sopenharmony_ci{ 149562306a36Sopenharmony_ci struct acpi_gpio_chip *acpi_gpio, *tmp; 149662306a36Sopenharmony_ci 149762306a36Sopenharmony_ci mutex_lock(&acpi_gpio_deferred_req_irqs_lock); 149862306a36Sopenharmony_ci list_for_each_entry_safe(acpi_gpio, tmp, 149962306a36Sopenharmony_ci &acpi_gpio_deferred_req_irqs_list, 150062306a36Sopenharmony_ci deferred_req_irqs_list_entry) 150162306a36Sopenharmony_ci acpi_gpiochip_request_irqs(acpi_gpio); 150262306a36Sopenharmony_ci 150362306a36Sopenharmony_ci acpi_gpio_deferred_req_irqs_done = true; 150462306a36Sopenharmony_ci mutex_unlock(&acpi_gpio_deferred_req_irqs_lock); 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_ci return 0; 150762306a36Sopenharmony_ci} 150862306a36Sopenharmony_ci/* We must use _sync so that this runs after the first deferred_probe run */ 150962306a36Sopenharmony_cilate_initcall_sync(acpi_gpio_handle_deferred_request_irqs); 151062306a36Sopenharmony_ci 151162306a36Sopenharmony_cistatic const struct dmi_system_id gpiolib_acpi_quirks[] __initconst = { 151262306a36Sopenharmony_ci { 151362306a36Sopenharmony_ci /* 151462306a36Sopenharmony_ci * The Minix Neo Z83-4 has a micro-USB-B id-pin handler for 151562306a36Sopenharmony_ci * a non existing micro-USB-B connector which puts the HDMI 151662306a36Sopenharmony_ci * DDC pins in GPIO mode, breaking HDMI support. 151762306a36Sopenharmony_ci */ 151862306a36Sopenharmony_ci .matches = { 151962306a36Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "MINIX"), 152062306a36Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "Z83-4"), 152162306a36Sopenharmony_ci }, 152262306a36Sopenharmony_ci .driver_data = &(struct acpi_gpiolib_dmi_quirk) { 152362306a36Sopenharmony_ci .no_edge_events_on_boot = true, 152462306a36Sopenharmony_ci }, 152562306a36Sopenharmony_ci }, 152662306a36Sopenharmony_ci { 152762306a36Sopenharmony_ci /* 152862306a36Sopenharmony_ci * The Terra Pad 1061 has a micro-USB-B id-pin handler, which 152962306a36Sopenharmony_ci * instead of controlling the actual micro-USB-B turns the 5V 153062306a36Sopenharmony_ci * boost for its USB-A connector off. The actual micro-USB-B 153162306a36Sopenharmony_ci * connector is wired for charging only. 153262306a36Sopenharmony_ci */ 153362306a36Sopenharmony_ci .matches = { 153462306a36Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Wortmann_AG"), 153562306a36Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "TERRA_PAD_1061"), 153662306a36Sopenharmony_ci }, 153762306a36Sopenharmony_ci .driver_data = &(struct acpi_gpiolib_dmi_quirk) { 153862306a36Sopenharmony_ci .no_edge_events_on_boot = true, 153962306a36Sopenharmony_ci }, 154062306a36Sopenharmony_ci }, 154162306a36Sopenharmony_ci { 154262306a36Sopenharmony_ci /* 154362306a36Sopenharmony_ci * The Dell Venue 10 Pro 5055, with Bay Trail SoC + TI PMIC uses an 154462306a36Sopenharmony_ci * external embedded-controller connected via I2C + an ACPI GPIO 154562306a36Sopenharmony_ci * event handler on INT33FFC:02 pin 12, causing spurious wakeups. 154662306a36Sopenharmony_ci */ 154762306a36Sopenharmony_ci .matches = { 154862306a36Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 154962306a36Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "Venue 10 Pro 5055"), 155062306a36Sopenharmony_ci }, 155162306a36Sopenharmony_ci .driver_data = &(struct acpi_gpiolib_dmi_quirk) { 155262306a36Sopenharmony_ci .ignore_wake = "INT33FC:02@12", 155362306a36Sopenharmony_ci }, 155462306a36Sopenharmony_ci }, 155562306a36Sopenharmony_ci { 155662306a36Sopenharmony_ci /* 155762306a36Sopenharmony_ci * HP X2 10 models with Cherry Trail SoC + TI PMIC use an 155862306a36Sopenharmony_ci * external embedded-controller connected via I2C + an ACPI GPIO 155962306a36Sopenharmony_ci * event handler on INT33FF:01 pin 0, causing spurious wakeups. 156062306a36Sopenharmony_ci * When suspending by closing the LID, the power to the USB 156162306a36Sopenharmony_ci * keyboard is turned off, causing INT0002 ACPI events to 156262306a36Sopenharmony_ci * trigger once the XHCI controller notices the keyboard is 156362306a36Sopenharmony_ci * gone. So INT0002 events cause spurious wakeups too. Ignoring 156462306a36Sopenharmony_ci * EC wakes breaks wakeup when opening the lid, the user needs 156562306a36Sopenharmony_ci * to press the power-button to wakeup the system. The 156662306a36Sopenharmony_ci * alternative is suspend simply not working, which is worse. 156762306a36Sopenharmony_ci */ 156862306a36Sopenharmony_ci .matches = { 156962306a36Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "HP"), 157062306a36Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "HP x2 Detachable 10-p0XX"), 157162306a36Sopenharmony_ci }, 157262306a36Sopenharmony_ci .driver_data = &(struct acpi_gpiolib_dmi_quirk) { 157362306a36Sopenharmony_ci .ignore_wake = "INT33FF:01@0,INT0002:00@2", 157462306a36Sopenharmony_ci }, 157562306a36Sopenharmony_ci }, 157662306a36Sopenharmony_ci { 157762306a36Sopenharmony_ci /* 157862306a36Sopenharmony_ci * HP X2 10 models with Bay Trail SoC + AXP288 PMIC use an 157962306a36Sopenharmony_ci * external embedded-controller connected via I2C + an ACPI GPIO 158062306a36Sopenharmony_ci * event handler on INT33FC:02 pin 28, causing spurious wakeups. 158162306a36Sopenharmony_ci */ 158262306a36Sopenharmony_ci .matches = { 158362306a36Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), 158462306a36Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion x2 Detachable"), 158562306a36Sopenharmony_ci DMI_MATCH(DMI_BOARD_NAME, "815D"), 158662306a36Sopenharmony_ci }, 158762306a36Sopenharmony_ci .driver_data = &(struct acpi_gpiolib_dmi_quirk) { 158862306a36Sopenharmony_ci .ignore_wake = "INT33FC:02@28", 158962306a36Sopenharmony_ci }, 159062306a36Sopenharmony_ci }, 159162306a36Sopenharmony_ci { 159262306a36Sopenharmony_ci /* 159362306a36Sopenharmony_ci * HP X2 10 models with Cherry Trail SoC + AXP288 PMIC use an 159462306a36Sopenharmony_ci * external embedded-controller connected via I2C + an ACPI GPIO 159562306a36Sopenharmony_ci * event handler on INT33FF:01 pin 0, causing spurious wakeups. 159662306a36Sopenharmony_ci */ 159762306a36Sopenharmony_ci .matches = { 159862306a36Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "HP"), 159962306a36Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion x2 Detachable"), 160062306a36Sopenharmony_ci DMI_MATCH(DMI_BOARD_NAME, "813E"), 160162306a36Sopenharmony_ci }, 160262306a36Sopenharmony_ci .driver_data = &(struct acpi_gpiolib_dmi_quirk) { 160362306a36Sopenharmony_ci .ignore_wake = "INT33FF:01@0", 160462306a36Sopenharmony_ci }, 160562306a36Sopenharmony_ci }, 160662306a36Sopenharmony_ci { 160762306a36Sopenharmony_ci /* 160862306a36Sopenharmony_ci * Interrupt storm caused from edge triggered floating pin 160962306a36Sopenharmony_ci * Found in BIOS UX325UAZ.300 161062306a36Sopenharmony_ci * https://bugzilla.kernel.org/show_bug.cgi?id=216208 161162306a36Sopenharmony_ci */ 161262306a36Sopenharmony_ci .matches = { 161362306a36Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 161462306a36Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "ZenBook UX325UAZ_UM325UAZ"), 161562306a36Sopenharmony_ci }, 161662306a36Sopenharmony_ci .driver_data = &(struct acpi_gpiolib_dmi_quirk) { 161762306a36Sopenharmony_ci .ignore_interrupt = "AMDI0030:00@18", 161862306a36Sopenharmony_ci }, 161962306a36Sopenharmony_ci }, 162062306a36Sopenharmony_ci { 162162306a36Sopenharmony_ci /* 162262306a36Sopenharmony_ci * Spurious wakeups from TP_ATTN# pin 162362306a36Sopenharmony_ci * Found in BIOS 1.7.8 162462306a36Sopenharmony_ci * https://gitlab.freedesktop.org/drm/amd/-/issues/1722#note_1720627 162562306a36Sopenharmony_ci */ 162662306a36Sopenharmony_ci .matches = { 162762306a36Sopenharmony_ci DMI_MATCH(DMI_BOARD_NAME, "NL5xNU"), 162862306a36Sopenharmony_ci }, 162962306a36Sopenharmony_ci .driver_data = &(struct acpi_gpiolib_dmi_quirk) { 163062306a36Sopenharmony_ci .ignore_wake = "ELAN0415:00@9", 163162306a36Sopenharmony_ci }, 163262306a36Sopenharmony_ci }, 163362306a36Sopenharmony_ci { 163462306a36Sopenharmony_ci /* 163562306a36Sopenharmony_ci * Spurious wakeups from TP_ATTN# pin 163662306a36Sopenharmony_ci * Found in BIOS 1.7.8 163762306a36Sopenharmony_ci * https://gitlab.freedesktop.org/drm/amd/-/issues/1722#note_1720627 163862306a36Sopenharmony_ci */ 163962306a36Sopenharmony_ci .matches = { 164062306a36Sopenharmony_ci DMI_MATCH(DMI_BOARD_NAME, "NL5xRU"), 164162306a36Sopenharmony_ci }, 164262306a36Sopenharmony_ci .driver_data = &(struct acpi_gpiolib_dmi_quirk) { 164362306a36Sopenharmony_ci .ignore_wake = "ELAN0415:00@9", 164462306a36Sopenharmony_ci }, 164562306a36Sopenharmony_ci }, 164662306a36Sopenharmony_ci { 164762306a36Sopenharmony_ci /* 164862306a36Sopenharmony_ci * Spurious wakeups from TP_ATTN# pin 164962306a36Sopenharmony_ci * Found in BIOS 1.7.7 165062306a36Sopenharmony_ci */ 165162306a36Sopenharmony_ci .matches = { 165262306a36Sopenharmony_ci DMI_MATCH(DMI_BOARD_NAME, "NH5xAx"), 165362306a36Sopenharmony_ci }, 165462306a36Sopenharmony_ci .driver_data = &(struct acpi_gpiolib_dmi_quirk) { 165562306a36Sopenharmony_ci .ignore_wake = "SYNA1202:00@16", 165662306a36Sopenharmony_ci }, 165762306a36Sopenharmony_ci }, 165862306a36Sopenharmony_ci { 165962306a36Sopenharmony_ci /* 166062306a36Sopenharmony_ci * On the Peaq C1010 2-in-1 INT33FC:00 pin 3 is connected to 166162306a36Sopenharmony_ci * a "dolby" button. At the ACPI level an _AEI event-handler 166262306a36Sopenharmony_ci * is connected which sets an ACPI variable to 1 on both 166362306a36Sopenharmony_ci * edges. This variable can be polled + cleared to 0 using 166462306a36Sopenharmony_ci * WMI. But since the variable is set on both edges the WMI 166562306a36Sopenharmony_ci * interface is pretty useless even when polling. 166662306a36Sopenharmony_ci * So instead the x86-android-tablets code instantiates 166762306a36Sopenharmony_ci * a gpio-keys platform device for it. 166862306a36Sopenharmony_ci * Ignore the _AEI handler for the pin, so that it is not busy. 166962306a36Sopenharmony_ci */ 167062306a36Sopenharmony_ci .matches = { 167162306a36Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "PEAQ"), 167262306a36Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "PEAQ PMM C1010 MD99187"), 167362306a36Sopenharmony_ci }, 167462306a36Sopenharmony_ci .driver_data = &(struct acpi_gpiolib_dmi_quirk) { 167562306a36Sopenharmony_ci .ignore_interrupt = "INT33FC:00@3", 167662306a36Sopenharmony_ci }, 167762306a36Sopenharmony_ci }, 167862306a36Sopenharmony_ci { 167962306a36Sopenharmony_ci /* 168062306a36Sopenharmony_ci * Spurious wakeups from TP_ATTN# pin 168162306a36Sopenharmony_ci * Found in BIOS 0.35 168262306a36Sopenharmony_ci * https://gitlab.freedesktop.org/drm/amd/-/issues/3073 168362306a36Sopenharmony_ci */ 168462306a36Sopenharmony_ci .matches = { 168562306a36Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "GPD"), 168662306a36Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "G1619-04"), 168762306a36Sopenharmony_ci }, 168862306a36Sopenharmony_ci .driver_data = &(struct acpi_gpiolib_dmi_quirk) { 168962306a36Sopenharmony_ci .ignore_wake = "PNP0C50:00@8", 169062306a36Sopenharmony_ci }, 169162306a36Sopenharmony_ci }, 169262306a36Sopenharmony_ci {} /* Terminating entry */ 169362306a36Sopenharmony_ci}; 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_cistatic int __init acpi_gpio_setup_params(void) 169662306a36Sopenharmony_ci{ 169762306a36Sopenharmony_ci const struct acpi_gpiolib_dmi_quirk *quirk = NULL; 169862306a36Sopenharmony_ci const struct dmi_system_id *id; 169962306a36Sopenharmony_ci 170062306a36Sopenharmony_ci id = dmi_first_match(gpiolib_acpi_quirks); 170162306a36Sopenharmony_ci if (id) 170262306a36Sopenharmony_ci quirk = id->driver_data; 170362306a36Sopenharmony_ci 170462306a36Sopenharmony_ci if (run_edge_events_on_boot < 0) { 170562306a36Sopenharmony_ci if (quirk && quirk->no_edge_events_on_boot) 170662306a36Sopenharmony_ci run_edge_events_on_boot = 0; 170762306a36Sopenharmony_ci else 170862306a36Sopenharmony_ci run_edge_events_on_boot = 1; 170962306a36Sopenharmony_ci } 171062306a36Sopenharmony_ci 171162306a36Sopenharmony_ci if (ignore_wake == NULL && quirk && quirk->ignore_wake) 171262306a36Sopenharmony_ci ignore_wake = quirk->ignore_wake; 171362306a36Sopenharmony_ci 171462306a36Sopenharmony_ci if (ignore_interrupt == NULL && quirk && quirk->ignore_interrupt) 171562306a36Sopenharmony_ci ignore_interrupt = quirk->ignore_interrupt; 171662306a36Sopenharmony_ci 171762306a36Sopenharmony_ci return 0; 171862306a36Sopenharmony_ci} 171962306a36Sopenharmony_ci 172062306a36Sopenharmony_ci/* Directly after dmi_setup() which runs as core_initcall() */ 172162306a36Sopenharmony_cipostcore_initcall(acpi_gpio_setup_params); 1722