18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ACPI helpers for GPIO API 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2012, Intel Corporation 68c2ecf20Sopenharmony_ci * Authors: Mathias Nyman <mathias.nyman@linux.intel.com> 78c2ecf20Sopenharmony_ci * Mika Westerberg <mika.westerberg@linux.intel.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/dmi.h> 118c2ecf20Sopenharmony_ci#include <linux/errno.h> 128c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 138c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h> 148c2ecf20Sopenharmony_ci#include <linux/gpio/machine.h> 158c2ecf20Sopenharmony_ci#include <linux/export.h> 168c2ecf20Sopenharmony_ci#include <linux/acpi.h> 178c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 188c2ecf20Sopenharmony_ci#include <linux/mutex.h> 198c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinctrl.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include "gpiolib.h" 228c2ecf20Sopenharmony_ci#include "gpiolib-acpi.h" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic int run_edge_events_on_boot = -1; 258c2ecf20Sopenharmony_cimodule_param(run_edge_events_on_boot, int, 0444); 268c2ecf20Sopenharmony_ciMODULE_PARM_DESC(run_edge_events_on_boot, 278c2ecf20Sopenharmony_ci "Run edge _AEI event-handlers at boot: 0=no, 1=yes, -1=auto"); 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic char *ignore_wake; 308c2ecf20Sopenharmony_cimodule_param(ignore_wake, charp, 0444); 318c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ignore_wake, 328c2ecf20Sopenharmony_ci "controller@pin combos on which to ignore the ACPI wake flag " 338c2ecf20Sopenharmony_ci "ignore_wake=controller@pin[,controller@pin[,...]]"); 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistruct acpi_gpiolib_dmi_quirk { 368c2ecf20Sopenharmony_ci bool no_edge_events_on_boot; 378c2ecf20Sopenharmony_ci char *ignore_wake; 388c2ecf20Sopenharmony_ci}; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci/** 418c2ecf20Sopenharmony_ci * struct acpi_gpio_event - ACPI GPIO event handler data 428c2ecf20Sopenharmony_ci * 438c2ecf20Sopenharmony_ci * @node: list-entry of the events list of the struct acpi_gpio_chip 448c2ecf20Sopenharmony_ci * @handle: handle of ACPI method to execute when the IRQ triggers 458c2ecf20Sopenharmony_ci * @handler: handler function to pass to request_irq() when requesting the IRQ 468c2ecf20Sopenharmony_ci * @pin: GPIO pin number on the struct gpio_chip 478c2ecf20Sopenharmony_ci * @irq: Linux IRQ number for the event, for request_irq() / free_irq() 488c2ecf20Sopenharmony_ci * @irqflags: flags to pass to request_irq() when requesting the IRQ 498c2ecf20Sopenharmony_ci * @irq_is_wake: If the ACPI flags indicate the IRQ is a wakeup source 508c2ecf20Sopenharmony_ci * @irq_requested:True if request_irq() has been done 518c2ecf20Sopenharmony_ci * @desc: struct gpio_desc for the GPIO pin for this event 528c2ecf20Sopenharmony_ci */ 538c2ecf20Sopenharmony_cistruct acpi_gpio_event { 548c2ecf20Sopenharmony_ci struct list_head node; 558c2ecf20Sopenharmony_ci acpi_handle handle; 568c2ecf20Sopenharmony_ci irq_handler_t handler; 578c2ecf20Sopenharmony_ci unsigned int pin; 588c2ecf20Sopenharmony_ci unsigned int irq; 598c2ecf20Sopenharmony_ci unsigned long irqflags; 608c2ecf20Sopenharmony_ci bool irq_is_wake; 618c2ecf20Sopenharmony_ci bool irq_requested; 628c2ecf20Sopenharmony_ci struct gpio_desc *desc; 638c2ecf20Sopenharmony_ci}; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistruct acpi_gpio_connection { 668c2ecf20Sopenharmony_ci struct list_head node; 678c2ecf20Sopenharmony_ci unsigned int pin; 688c2ecf20Sopenharmony_ci struct gpio_desc *desc; 698c2ecf20Sopenharmony_ci}; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistruct acpi_gpio_chip { 728c2ecf20Sopenharmony_ci /* 738c2ecf20Sopenharmony_ci * ACPICA requires that the first field of the context parameter 748c2ecf20Sopenharmony_ci * passed to acpi_install_address_space_handler() is large enough 758c2ecf20Sopenharmony_ci * to hold struct acpi_connection_info. 768c2ecf20Sopenharmony_ci */ 778c2ecf20Sopenharmony_ci struct acpi_connection_info conn_info; 788c2ecf20Sopenharmony_ci struct list_head conns; 798c2ecf20Sopenharmony_ci struct mutex conn_lock; 808c2ecf20Sopenharmony_ci struct gpio_chip *chip; 818c2ecf20Sopenharmony_ci struct list_head events; 828c2ecf20Sopenharmony_ci struct list_head deferred_req_irqs_list_entry; 838c2ecf20Sopenharmony_ci}; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci/* 868c2ecf20Sopenharmony_ci * For GPIO chips which call acpi_gpiochip_request_interrupts() before late_init 878c2ecf20Sopenharmony_ci * (so builtin drivers) we register the ACPI GpioInt IRQ handlers from a 888c2ecf20Sopenharmony_ci * late_initcall_sync() handler, so that other builtin drivers can register their 898c2ecf20Sopenharmony_ci * OpRegions before the event handlers can run. This list contains GPIO chips 908c2ecf20Sopenharmony_ci * for which the acpi_gpiochip_request_irqs() call has been deferred. 918c2ecf20Sopenharmony_ci */ 928c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(acpi_gpio_deferred_req_irqs_lock); 938c2ecf20Sopenharmony_cistatic LIST_HEAD(acpi_gpio_deferred_req_irqs_list); 948c2ecf20Sopenharmony_cistatic bool acpi_gpio_deferred_req_irqs_done; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic int acpi_gpiochip_find(struct gpio_chip *gc, void *data) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci if (!gc->parent) 998c2ecf20Sopenharmony_ci return false; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci return ACPI_HANDLE(gc->parent) == data; 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci/** 1058c2ecf20Sopenharmony_ci * acpi_get_gpiod() - Translate ACPI GPIO pin to GPIO descriptor usable with GPIO API 1068c2ecf20Sopenharmony_ci * @path: ACPI GPIO controller full path name, (e.g. "\\_SB.GPO1") 1078c2ecf20Sopenharmony_ci * @pin: ACPI GPIO pin number (0-based, controller-relative) 1088c2ecf20Sopenharmony_ci * 1098c2ecf20Sopenharmony_ci * Return: GPIO descriptor to use with Linux generic GPIO API, or ERR_PTR 1108c2ecf20Sopenharmony_ci * error value. Specifically returns %-EPROBE_DEFER if the referenced GPIO 1118c2ecf20Sopenharmony_ci * controller does not have GPIO chip registered at the moment. This is to 1128c2ecf20Sopenharmony_ci * support probe deferral. 1138c2ecf20Sopenharmony_ci */ 1148c2ecf20Sopenharmony_cistatic struct gpio_desc *acpi_get_gpiod(char *path, int pin) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci struct gpio_chip *chip; 1178c2ecf20Sopenharmony_ci acpi_handle handle; 1188c2ecf20Sopenharmony_ci acpi_status status; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci status = acpi_get_handle(NULL, path, &handle); 1218c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) 1228c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci chip = gpiochip_find(handle, acpi_gpiochip_find); 1258c2ecf20Sopenharmony_ci if (!chip) 1268c2ecf20Sopenharmony_ci return ERR_PTR(-EPROBE_DEFER); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci return gpiochip_get_desc(chip, pin); 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic irqreturn_t acpi_gpio_irq_handler(int irq, void *data) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci struct acpi_gpio_event *event = data; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci acpi_evaluate_object(event->handle, NULL, NULL, NULL); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic irqreturn_t acpi_gpio_irq_handler_evt(int irq, void *data) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci struct acpi_gpio_event *event = data; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci acpi_execute_simple_method(event->handle, NULL, event->pin); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic void acpi_gpio_chip_dh(acpi_handle handle, void *data) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci /* The address of this function is used as a key. */ 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cibool acpi_gpio_get_irq_resource(struct acpi_resource *ares, 1558c2ecf20Sopenharmony_ci struct acpi_resource_gpio **agpio) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci struct acpi_resource_gpio *gpio; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci if (ares->type != ACPI_RESOURCE_TYPE_GPIO) 1608c2ecf20Sopenharmony_ci return false; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci gpio = &ares->data.gpio; 1638c2ecf20Sopenharmony_ci if (gpio->connection_type != ACPI_RESOURCE_GPIO_TYPE_INT) 1648c2ecf20Sopenharmony_ci return false; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci *agpio = gpio; 1678c2ecf20Sopenharmony_ci return true; 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(acpi_gpio_get_irq_resource); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic void acpi_gpiochip_request_irq(struct acpi_gpio_chip *acpi_gpio, 1728c2ecf20Sopenharmony_ci struct acpi_gpio_event *event) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci int ret, value; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci ret = request_threaded_irq(event->irq, NULL, event->handler, 1778c2ecf20Sopenharmony_ci event->irqflags | IRQF_ONESHOT, "ACPI:Event", event); 1788c2ecf20Sopenharmony_ci if (ret) { 1798c2ecf20Sopenharmony_ci dev_err(acpi_gpio->chip->parent, 1808c2ecf20Sopenharmony_ci "Failed to setup interrupt handler for %d\n", 1818c2ecf20Sopenharmony_ci event->irq); 1828c2ecf20Sopenharmony_ci return; 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci if (event->irq_is_wake) 1868c2ecf20Sopenharmony_ci enable_irq_wake(event->irq); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci event->irq_requested = true; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci /* Make sure we trigger the initial state of edge-triggered IRQs */ 1918c2ecf20Sopenharmony_ci if (run_edge_events_on_boot && 1928c2ecf20Sopenharmony_ci (event->irqflags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING))) { 1938c2ecf20Sopenharmony_ci value = gpiod_get_raw_value_cansleep(event->desc); 1948c2ecf20Sopenharmony_ci if (((event->irqflags & IRQF_TRIGGER_RISING) && value == 1) || 1958c2ecf20Sopenharmony_ci ((event->irqflags & IRQF_TRIGGER_FALLING) && value == 0)) 1968c2ecf20Sopenharmony_ci event->handler(event->irq, event); 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic void acpi_gpiochip_request_irqs(struct acpi_gpio_chip *acpi_gpio) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci struct acpi_gpio_event *event; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci list_for_each_entry(event, &acpi_gpio->events, node) 2058c2ecf20Sopenharmony_ci acpi_gpiochip_request_irq(acpi_gpio, event); 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic bool acpi_gpio_in_ignore_list(const char *controller_in, int pin_in) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci const char *controller, *pin_str; 2118c2ecf20Sopenharmony_ci int len, pin; 2128c2ecf20Sopenharmony_ci char *endp; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci controller = ignore_wake; 2158c2ecf20Sopenharmony_ci while (controller) { 2168c2ecf20Sopenharmony_ci pin_str = strchr(controller, '@'); 2178c2ecf20Sopenharmony_ci if (!pin_str) 2188c2ecf20Sopenharmony_ci goto err; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci len = pin_str - controller; 2218c2ecf20Sopenharmony_ci if (len == strlen(controller_in) && 2228c2ecf20Sopenharmony_ci strncmp(controller, controller_in, len) == 0) { 2238c2ecf20Sopenharmony_ci pin = simple_strtoul(pin_str + 1, &endp, 10); 2248c2ecf20Sopenharmony_ci if (*endp != 0 && *endp != ',') 2258c2ecf20Sopenharmony_ci goto err; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci if (pin == pin_in) 2288c2ecf20Sopenharmony_ci return true; 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci controller = strchr(controller, ','); 2328c2ecf20Sopenharmony_ci if (controller) 2338c2ecf20Sopenharmony_ci controller++; 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci return false; 2378c2ecf20Sopenharmony_cierr: 2388c2ecf20Sopenharmony_ci pr_err_once("Error invalid value for gpiolib_acpi.ignore_wake: %s\n", 2398c2ecf20Sopenharmony_ci ignore_wake); 2408c2ecf20Sopenharmony_ci return false; 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_cistatic bool acpi_gpio_irq_is_wake(struct device *parent, 2448c2ecf20Sopenharmony_ci struct acpi_resource_gpio *agpio) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci int pin = agpio->pin_table[0]; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci if (agpio->wake_capable != ACPI_WAKE_CAPABLE) 2498c2ecf20Sopenharmony_ci return false; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci if (acpi_gpio_in_ignore_list(dev_name(parent), pin)) { 2528c2ecf20Sopenharmony_ci dev_info(parent, "Ignoring wakeup on pin %d\n", pin); 2538c2ecf20Sopenharmony_ci return false; 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci return true; 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci/* Always returns AE_OK so that we keep looping over the resources */ 2608c2ecf20Sopenharmony_cistatic acpi_status acpi_gpiochip_alloc_event(struct acpi_resource *ares, 2618c2ecf20Sopenharmony_ci void *context) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci struct acpi_gpio_chip *acpi_gpio = context; 2648c2ecf20Sopenharmony_ci struct gpio_chip *chip = acpi_gpio->chip; 2658c2ecf20Sopenharmony_ci struct acpi_resource_gpio *agpio; 2668c2ecf20Sopenharmony_ci acpi_handle handle, evt_handle; 2678c2ecf20Sopenharmony_ci struct acpi_gpio_event *event; 2688c2ecf20Sopenharmony_ci irq_handler_t handler = NULL; 2698c2ecf20Sopenharmony_ci struct gpio_desc *desc; 2708c2ecf20Sopenharmony_ci int ret, pin, irq; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci if (!acpi_gpio_get_irq_resource(ares, &agpio)) 2738c2ecf20Sopenharmony_ci return AE_OK; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci handle = ACPI_HANDLE(chip->parent); 2768c2ecf20Sopenharmony_ci pin = agpio->pin_table[0]; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci if (pin <= 255) { 2798c2ecf20Sopenharmony_ci char ev_name[8]; 2808c2ecf20Sopenharmony_ci sprintf(ev_name, "_%c%02X", 2818c2ecf20Sopenharmony_ci agpio->triggering == ACPI_EDGE_SENSITIVE ? 'E' : 'L', 2828c2ecf20Sopenharmony_ci pin); 2838c2ecf20Sopenharmony_ci if (ACPI_SUCCESS(acpi_get_handle(handle, ev_name, &evt_handle))) 2848c2ecf20Sopenharmony_ci handler = acpi_gpio_irq_handler; 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci if (!handler) { 2878c2ecf20Sopenharmony_ci if (ACPI_SUCCESS(acpi_get_handle(handle, "_EVT", &evt_handle))) 2888c2ecf20Sopenharmony_ci handler = acpi_gpio_irq_handler_evt; 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci if (!handler) 2918c2ecf20Sopenharmony_ci return AE_OK; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci desc = gpiochip_request_own_desc(chip, pin, "ACPI:Event", 2948c2ecf20Sopenharmony_ci GPIO_ACTIVE_HIGH, GPIOD_IN); 2958c2ecf20Sopenharmony_ci if (IS_ERR(desc)) { 2968c2ecf20Sopenharmony_ci dev_err(chip->parent, 2978c2ecf20Sopenharmony_ci "Failed to request GPIO for pin 0x%04X, err %ld\n", 2988c2ecf20Sopenharmony_ci pin, PTR_ERR(desc)); 2998c2ecf20Sopenharmony_ci return AE_OK; 3008c2ecf20Sopenharmony_ci } 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci ret = gpiochip_lock_as_irq(chip, pin); 3038c2ecf20Sopenharmony_ci if (ret) { 3048c2ecf20Sopenharmony_ci dev_err(chip->parent, 3058c2ecf20Sopenharmony_ci "Failed to lock GPIO pin 0x%04X as interrupt, err %d\n", 3068c2ecf20Sopenharmony_ci pin, ret); 3078c2ecf20Sopenharmony_ci goto fail_free_desc; 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci irq = gpiod_to_irq(desc); 3118c2ecf20Sopenharmony_ci if (irq < 0) { 3128c2ecf20Sopenharmony_ci dev_err(chip->parent, 3138c2ecf20Sopenharmony_ci "Failed to translate GPIO pin 0x%04X to IRQ, err %d\n", 3148c2ecf20Sopenharmony_ci pin, irq); 3158c2ecf20Sopenharmony_ci goto fail_unlock_irq; 3168c2ecf20Sopenharmony_ci } 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci event = kzalloc(sizeof(*event), GFP_KERNEL); 3198c2ecf20Sopenharmony_ci if (!event) 3208c2ecf20Sopenharmony_ci goto fail_unlock_irq; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci event->irqflags = IRQF_ONESHOT; 3238c2ecf20Sopenharmony_ci if (agpio->triggering == ACPI_LEVEL_SENSITIVE) { 3248c2ecf20Sopenharmony_ci if (agpio->polarity == ACPI_ACTIVE_HIGH) 3258c2ecf20Sopenharmony_ci event->irqflags |= IRQF_TRIGGER_HIGH; 3268c2ecf20Sopenharmony_ci else 3278c2ecf20Sopenharmony_ci event->irqflags |= IRQF_TRIGGER_LOW; 3288c2ecf20Sopenharmony_ci } else { 3298c2ecf20Sopenharmony_ci switch (agpio->polarity) { 3308c2ecf20Sopenharmony_ci case ACPI_ACTIVE_HIGH: 3318c2ecf20Sopenharmony_ci event->irqflags |= IRQF_TRIGGER_RISING; 3328c2ecf20Sopenharmony_ci break; 3338c2ecf20Sopenharmony_ci case ACPI_ACTIVE_LOW: 3348c2ecf20Sopenharmony_ci event->irqflags |= IRQF_TRIGGER_FALLING; 3358c2ecf20Sopenharmony_ci break; 3368c2ecf20Sopenharmony_ci default: 3378c2ecf20Sopenharmony_ci event->irqflags |= IRQF_TRIGGER_RISING | 3388c2ecf20Sopenharmony_ci IRQF_TRIGGER_FALLING; 3398c2ecf20Sopenharmony_ci break; 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci event->handle = evt_handle; 3448c2ecf20Sopenharmony_ci event->handler = handler; 3458c2ecf20Sopenharmony_ci event->irq = irq; 3468c2ecf20Sopenharmony_ci event->irq_is_wake = acpi_gpio_irq_is_wake(chip->parent, agpio); 3478c2ecf20Sopenharmony_ci event->pin = pin; 3488c2ecf20Sopenharmony_ci event->desc = desc; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci list_add_tail(&event->node, &acpi_gpio->events); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci return AE_OK; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_cifail_unlock_irq: 3558c2ecf20Sopenharmony_ci gpiochip_unlock_as_irq(chip, pin); 3568c2ecf20Sopenharmony_cifail_free_desc: 3578c2ecf20Sopenharmony_ci gpiochip_free_own_desc(desc); 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci return AE_OK; 3608c2ecf20Sopenharmony_ci} 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci/** 3638c2ecf20Sopenharmony_ci * acpi_gpiochip_request_interrupts() - Register isr for gpio chip ACPI events 3648c2ecf20Sopenharmony_ci * @chip: GPIO chip 3658c2ecf20Sopenharmony_ci * 3668c2ecf20Sopenharmony_ci * ACPI5 platforms can use GPIO signaled ACPI events. These GPIO interrupts are 3678c2ecf20Sopenharmony_ci * handled by ACPI event methods which need to be called from the GPIO 3688c2ecf20Sopenharmony_ci * chip's interrupt handler. acpi_gpiochip_request_interrupts() finds out which 3698c2ecf20Sopenharmony_ci * GPIO pins have ACPI event methods and assigns interrupt handlers that calls 3708c2ecf20Sopenharmony_ci * the ACPI event methods for those pins. 3718c2ecf20Sopenharmony_ci */ 3728c2ecf20Sopenharmony_civoid acpi_gpiochip_request_interrupts(struct gpio_chip *chip) 3738c2ecf20Sopenharmony_ci{ 3748c2ecf20Sopenharmony_ci struct acpi_gpio_chip *acpi_gpio; 3758c2ecf20Sopenharmony_ci acpi_handle handle; 3768c2ecf20Sopenharmony_ci acpi_status status; 3778c2ecf20Sopenharmony_ci bool defer; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci if (!chip->parent || !chip->to_irq) 3808c2ecf20Sopenharmony_ci return; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci handle = ACPI_HANDLE(chip->parent); 3838c2ecf20Sopenharmony_ci if (!handle) 3848c2ecf20Sopenharmony_ci return; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci status = acpi_get_data(handle, acpi_gpio_chip_dh, (void **)&acpi_gpio); 3878c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) 3888c2ecf20Sopenharmony_ci return; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci acpi_walk_resources(handle, "_AEI", 3918c2ecf20Sopenharmony_ci acpi_gpiochip_alloc_event, acpi_gpio); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci mutex_lock(&acpi_gpio_deferred_req_irqs_lock); 3948c2ecf20Sopenharmony_ci defer = !acpi_gpio_deferred_req_irqs_done; 3958c2ecf20Sopenharmony_ci if (defer) 3968c2ecf20Sopenharmony_ci list_add(&acpi_gpio->deferred_req_irqs_list_entry, 3978c2ecf20Sopenharmony_ci &acpi_gpio_deferred_req_irqs_list); 3988c2ecf20Sopenharmony_ci mutex_unlock(&acpi_gpio_deferred_req_irqs_lock); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci if (defer) 4018c2ecf20Sopenharmony_ci return; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci acpi_gpiochip_request_irqs(acpi_gpio); 4048c2ecf20Sopenharmony_ci} 4058c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(acpi_gpiochip_request_interrupts); 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci/** 4088c2ecf20Sopenharmony_ci * acpi_gpiochip_free_interrupts() - Free GPIO ACPI event interrupts. 4098c2ecf20Sopenharmony_ci * @chip: GPIO chip 4108c2ecf20Sopenharmony_ci * 4118c2ecf20Sopenharmony_ci * Free interrupts associated with GPIO ACPI event method for the given 4128c2ecf20Sopenharmony_ci * GPIO chip. 4138c2ecf20Sopenharmony_ci */ 4148c2ecf20Sopenharmony_civoid acpi_gpiochip_free_interrupts(struct gpio_chip *chip) 4158c2ecf20Sopenharmony_ci{ 4168c2ecf20Sopenharmony_ci struct acpi_gpio_chip *acpi_gpio; 4178c2ecf20Sopenharmony_ci struct acpi_gpio_event *event, *ep; 4188c2ecf20Sopenharmony_ci acpi_handle handle; 4198c2ecf20Sopenharmony_ci acpi_status status; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci if (!chip->parent || !chip->to_irq) 4228c2ecf20Sopenharmony_ci return; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci handle = ACPI_HANDLE(chip->parent); 4258c2ecf20Sopenharmony_ci if (!handle) 4268c2ecf20Sopenharmony_ci return; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci status = acpi_get_data(handle, acpi_gpio_chip_dh, (void **)&acpi_gpio); 4298c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) 4308c2ecf20Sopenharmony_ci return; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci mutex_lock(&acpi_gpio_deferred_req_irqs_lock); 4338c2ecf20Sopenharmony_ci if (!list_empty(&acpi_gpio->deferred_req_irqs_list_entry)) 4348c2ecf20Sopenharmony_ci list_del_init(&acpi_gpio->deferred_req_irqs_list_entry); 4358c2ecf20Sopenharmony_ci mutex_unlock(&acpi_gpio_deferred_req_irqs_lock); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci list_for_each_entry_safe_reverse(event, ep, &acpi_gpio->events, node) { 4388c2ecf20Sopenharmony_ci if (event->irq_requested) { 4398c2ecf20Sopenharmony_ci if (event->irq_is_wake) 4408c2ecf20Sopenharmony_ci disable_irq_wake(event->irq); 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci free_irq(event->irq, event); 4438c2ecf20Sopenharmony_ci } 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci gpiochip_unlock_as_irq(chip, event->pin); 4468c2ecf20Sopenharmony_ci gpiochip_free_own_desc(event->desc); 4478c2ecf20Sopenharmony_ci list_del(&event->node); 4488c2ecf20Sopenharmony_ci kfree(event); 4498c2ecf20Sopenharmony_ci } 4508c2ecf20Sopenharmony_ci} 4518c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(acpi_gpiochip_free_interrupts); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ciint acpi_dev_add_driver_gpios(struct acpi_device *adev, 4548c2ecf20Sopenharmony_ci const struct acpi_gpio_mapping *gpios) 4558c2ecf20Sopenharmony_ci{ 4568c2ecf20Sopenharmony_ci if (adev && gpios) { 4578c2ecf20Sopenharmony_ci adev->driver_gpios = gpios; 4588c2ecf20Sopenharmony_ci return 0; 4598c2ecf20Sopenharmony_ci } 4608c2ecf20Sopenharmony_ci return -EINVAL; 4618c2ecf20Sopenharmony_ci} 4628c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(acpi_dev_add_driver_gpios); 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_civoid acpi_dev_remove_driver_gpios(struct acpi_device *adev) 4658c2ecf20Sopenharmony_ci{ 4668c2ecf20Sopenharmony_ci if (adev) 4678c2ecf20Sopenharmony_ci adev->driver_gpios = NULL; 4688c2ecf20Sopenharmony_ci} 4698c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(acpi_dev_remove_driver_gpios); 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_cistatic void devm_acpi_dev_release_driver_gpios(struct device *dev, void *res) 4728c2ecf20Sopenharmony_ci{ 4738c2ecf20Sopenharmony_ci acpi_dev_remove_driver_gpios(ACPI_COMPANION(dev)); 4748c2ecf20Sopenharmony_ci} 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ciint devm_acpi_dev_add_driver_gpios(struct device *dev, 4778c2ecf20Sopenharmony_ci const struct acpi_gpio_mapping *gpios) 4788c2ecf20Sopenharmony_ci{ 4798c2ecf20Sopenharmony_ci void *res; 4808c2ecf20Sopenharmony_ci int ret; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci res = devres_alloc(devm_acpi_dev_release_driver_gpios, 0, GFP_KERNEL); 4838c2ecf20Sopenharmony_ci if (!res) 4848c2ecf20Sopenharmony_ci return -ENOMEM; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci ret = acpi_dev_add_driver_gpios(ACPI_COMPANION(dev), gpios); 4878c2ecf20Sopenharmony_ci if (ret) { 4888c2ecf20Sopenharmony_ci devres_free(res); 4898c2ecf20Sopenharmony_ci return ret; 4908c2ecf20Sopenharmony_ci } 4918c2ecf20Sopenharmony_ci devres_add(dev, res); 4928c2ecf20Sopenharmony_ci return 0; 4938c2ecf20Sopenharmony_ci} 4948c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_acpi_dev_add_driver_gpios); 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_civoid devm_acpi_dev_remove_driver_gpios(struct device *dev) 4978c2ecf20Sopenharmony_ci{ 4988c2ecf20Sopenharmony_ci WARN_ON(devres_release(dev, devm_acpi_dev_release_driver_gpios, NULL, NULL)); 4998c2ecf20Sopenharmony_ci} 5008c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_acpi_dev_remove_driver_gpios); 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_cistatic bool acpi_get_driver_gpio_data(struct acpi_device *adev, 5038c2ecf20Sopenharmony_ci const char *name, int index, 5048c2ecf20Sopenharmony_ci struct fwnode_reference_args *args, 5058c2ecf20Sopenharmony_ci unsigned int *quirks) 5068c2ecf20Sopenharmony_ci{ 5078c2ecf20Sopenharmony_ci const struct acpi_gpio_mapping *gm; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci if (!adev->driver_gpios) 5108c2ecf20Sopenharmony_ci return false; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci for (gm = adev->driver_gpios; gm->name; gm++) 5138c2ecf20Sopenharmony_ci if (!strcmp(name, gm->name) && gm->data && index < gm->size) { 5148c2ecf20Sopenharmony_ci const struct acpi_gpio_params *par = gm->data + index; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci args->fwnode = acpi_fwnode_handle(adev); 5178c2ecf20Sopenharmony_ci args->args[0] = par->crs_entry_index; 5188c2ecf20Sopenharmony_ci args->args[1] = par->line_index; 5198c2ecf20Sopenharmony_ci args->args[2] = par->active_low; 5208c2ecf20Sopenharmony_ci args->nargs = 3; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci *quirks = gm->quirks; 5238c2ecf20Sopenharmony_ci return true; 5248c2ecf20Sopenharmony_ci } 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci return false; 5278c2ecf20Sopenharmony_ci} 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_cistatic enum gpiod_flags 5308c2ecf20Sopenharmony_ciacpi_gpio_to_gpiod_flags(const struct acpi_resource_gpio *agpio) 5318c2ecf20Sopenharmony_ci{ 5328c2ecf20Sopenharmony_ci switch (agpio->io_restriction) { 5338c2ecf20Sopenharmony_ci case ACPI_IO_RESTRICT_INPUT: 5348c2ecf20Sopenharmony_ci return GPIOD_IN; 5358c2ecf20Sopenharmony_ci case ACPI_IO_RESTRICT_OUTPUT: 5368c2ecf20Sopenharmony_ci /* 5378c2ecf20Sopenharmony_ci * ACPI GPIO resources don't contain an initial value for the 5388c2ecf20Sopenharmony_ci * GPIO. Therefore we deduce that value from the pull field 5398c2ecf20Sopenharmony_ci * instead. If the pin is pulled up we assume default to be 5408c2ecf20Sopenharmony_ci * high, if it is pulled down we assume default to be low, 5418c2ecf20Sopenharmony_ci * otherwise we leave pin untouched. 5428c2ecf20Sopenharmony_ci */ 5438c2ecf20Sopenharmony_ci switch (agpio->pin_config) { 5448c2ecf20Sopenharmony_ci case ACPI_PIN_CONFIG_PULLUP: 5458c2ecf20Sopenharmony_ci return GPIOD_OUT_HIGH; 5468c2ecf20Sopenharmony_ci case ACPI_PIN_CONFIG_PULLDOWN: 5478c2ecf20Sopenharmony_ci return GPIOD_OUT_LOW; 5488c2ecf20Sopenharmony_ci default: 5498c2ecf20Sopenharmony_ci break; 5508c2ecf20Sopenharmony_ci } 5518c2ecf20Sopenharmony_ci default: 5528c2ecf20Sopenharmony_ci break; 5538c2ecf20Sopenharmony_ci } 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci /* 5568c2ecf20Sopenharmony_ci * Assume that the BIOS has configured the direction and pull 5578c2ecf20Sopenharmony_ci * accordingly. 5588c2ecf20Sopenharmony_ci */ 5598c2ecf20Sopenharmony_ci return GPIOD_ASIS; 5608c2ecf20Sopenharmony_ci} 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_cistatic int 5638c2ecf20Sopenharmony_ci__acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, enum gpiod_flags update) 5648c2ecf20Sopenharmony_ci{ 5658c2ecf20Sopenharmony_ci const enum gpiod_flags mask = 5668c2ecf20Sopenharmony_ci GPIOD_FLAGS_BIT_DIR_SET | GPIOD_FLAGS_BIT_DIR_OUT | 5678c2ecf20Sopenharmony_ci GPIOD_FLAGS_BIT_DIR_VAL; 5688c2ecf20Sopenharmony_ci int ret = 0; 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci /* 5718c2ecf20Sopenharmony_ci * Check if the BIOS has IoRestriction with explicitly set direction 5728c2ecf20Sopenharmony_ci * and update @flags accordingly. Otherwise use whatever caller asked 5738c2ecf20Sopenharmony_ci * for. 5748c2ecf20Sopenharmony_ci */ 5758c2ecf20Sopenharmony_ci if (update & GPIOD_FLAGS_BIT_DIR_SET) { 5768c2ecf20Sopenharmony_ci enum gpiod_flags diff = *flags ^ update; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci /* 5798c2ecf20Sopenharmony_ci * Check if caller supplied incompatible GPIO initialization 5808c2ecf20Sopenharmony_ci * flags. 5818c2ecf20Sopenharmony_ci * 5828c2ecf20Sopenharmony_ci * Return %-EINVAL to notify that firmware has different 5838c2ecf20Sopenharmony_ci * settings and we are going to use them. 5848c2ecf20Sopenharmony_ci */ 5858c2ecf20Sopenharmony_ci if (((*flags & GPIOD_FLAGS_BIT_DIR_SET) && (diff & GPIOD_FLAGS_BIT_DIR_OUT)) || 5868c2ecf20Sopenharmony_ci ((*flags & GPIOD_FLAGS_BIT_DIR_OUT) && (diff & GPIOD_FLAGS_BIT_DIR_VAL))) 5878c2ecf20Sopenharmony_ci ret = -EINVAL; 5888c2ecf20Sopenharmony_ci *flags = (*flags & ~mask) | (update & mask); 5898c2ecf20Sopenharmony_ci } 5908c2ecf20Sopenharmony_ci return ret; 5918c2ecf20Sopenharmony_ci} 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ciint 5948c2ecf20Sopenharmony_ciacpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, struct acpi_gpio_info *info) 5958c2ecf20Sopenharmony_ci{ 5968c2ecf20Sopenharmony_ci struct device *dev = &info->adev->dev; 5978c2ecf20Sopenharmony_ci enum gpiod_flags old = *flags; 5988c2ecf20Sopenharmony_ci int ret; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci ret = __acpi_gpio_update_gpiod_flags(&old, info->flags); 6018c2ecf20Sopenharmony_ci if (info->quirks & ACPI_GPIO_QUIRK_NO_IO_RESTRICTION) { 6028c2ecf20Sopenharmony_ci if (ret) 6038c2ecf20Sopenharmony_ci dev_warn(dev, FW_BUG "GPIO not in correct mode, fixing\n"); 6048c2ecf20Sopenharmony_ci } else { 6058c2ecf20Sopenharmony_ci if (ret) 6068c2ecf20Sopenharmony_ci dev_dbg(dev, "Override GPIO initialization flags\n"); 6078c2ecf20Sopenharmony_ci *flags = old; 6088c2ecf20Sopenharmony_ci } 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci return ret; 6118c2ecf20Sopenharmony_ci} 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ciint acpi_gpio_update_gpiod_lookup_flags(unsigned long *lookupflags, 6148c2ecf20Sopenharmony_ci struct acpi_gpio_info *info) 6158c2ecf20Sopenharmony_ci{ 6168c2ecf20Sopenharmony_ci switch (info->pin_config) { 6178c2ecf20Sopenharmony_ci case ACPI_PIN_CONFIG_PULLUP: 6188c2ecf20Sopenharmony_ci *lookupflags |= GPIO_PULL_UP; 6198c2ecf20Sopenharmony_ci break; 6208c2ecf20Sopenharmony_ci case ACPI_PIN_CONFIG_PULLDOWN: 6218c2ecf20Sopenharmony_ci *lookupflags |= GPIO_PULL_DOWN; 6228c2ecf20Sopenharmony_ci break; 6238c2ecf20Sopenharmony_ci default: 6248c2ecf20Sopenharmony_ci break; 6258c2ecf20Sopenharmony_ci } 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci if (info->polarity == GPIO_ACTIVE_LOW) 6288c2ecf20Sopenharmony_ci *lookupflags |= GPIO_ACTIVE_LOW; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci return 0; 6318c2ecf20Sopenharmony_ci} 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_cistruct acpi_gpio_lookup { 6348c2ecf20Sopenharmony_ci struct acpi_gpio_info info; 6358c2ecf20Sopenharmony_ci int index; 6368c2ecf20Sopenharmony_ci int pin_index; 6378c2ecf20Sopenharmony_ci bool active_low; 6388c2ecf20Sopenharmony_ci struct gpio_desc *desc; 6398c2ecf20Sopenharmony_ci int n; 6408c2ecf20Sopenharmony_ci}; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_cistatic int acpi_populate_gpio_lookup(struct acpi_resource *ares, void *data) 6438c2ecf20Sopenharmony_ci{ 6448c2ecf20Sopenharmony_ci struct acpi_gpio_lookup *lookup = data; 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci if (ares->type != ACPI_RESOURCE_TYPE_GPIO) 6478c2ecf20Sopenharmony_ci return 1; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci if (!lookup->desc) { 6508c2ecf20Sopenharmony_ci const struct acpi_resource_gpio *agpio = &ares->data.gpio; 6518c2ecf20Sopenharmony_ci bool gpioint = agpio->connection_type == ACPI_RESOURCE_GPIO_TYPE_INT; 6528c2ecf20Sopenharmony_ci struct gpio_desc *desc; 6538c2ecf20Sopenharmony_ci int pin_index; 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci if (lookup->info.quirks & ACPI_GPIO_QUIRK_ONLY_GPIOIO && gpioint) 6568c2ecf20Sopenharmony_ci lookup->index++; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci if (lookup->n++ != lookup->index) 6598c2ecf20Sopenharmony_ci return 1; 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci pin_index = lookup->pin_index; 6628c2ecf20Sopenharmony_ci if (pin_index >= agpio->pin_table_length) 6638c2ecf20Sopenharmony_ci return 1; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci if (lookup->info.quirks & ACPI_GPIO_QUIRK_ABSOLUTE_NUMBER) 6668c2ecf20Sopenharmony_ci desc = gpio_to_desc(agpio->pin_table[pin_index]); 6678c2ecf20Sopenharmony_ci else 6688c2ecf20Sopenharmony_ci desc = acpi_get_gpiod(agpio->resource_source.string_ptr, 6698c2ecf20Sopenharmony_ci agpio->pin_table[pin_index]); 6708c2ecf20Sopenharmony_ci lookup->desc = desc; 6718c2ecf20Sopenharmony_ci lookup->info.pin_config = agpio->pin_config; 6728c2ecf20Sopenharmony_ci lookup->info.gpioint = gpioint; 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci /* 6758c2ecf20Sopenharmony_ci * Polarity and triggering are only specified for GpioInt 6768c2ecf20Sopenharmony_ci * resource. 6778c2ecf20Sopenharmony_ci * Note: we expect here: 6788c2ecf20Sopenharmony_ci * - ACPI_ACTIVE_LOW == GPIO_ACTIVE_LOW 6798c2ecf20Sopenharmony_ci * - ACPI_ACTIVE_HIGH == GPIO_ACTIVE_HIGH 6808c2ecf20Sopenharmony_ci */ 6818c2ecf20Sopenharmony_ci if (lookup->info.gpioint) { 6828c2ecf20Sopenharmony_ci lookup->info.flags = GPIOD_IN; 6838c2ecf20Sopenharmony_ci lookup->info.polarity = agpio->polarity; 6848c2ecf20Sopenharmony_ci lookup->info.triggering = agpio->triggering; 6858c2ecf20Sopenharmony_ci } else { 6868c2ecf20Sopenharmony_ci lookup->info.flags = acpi_gpio_to_gpiod_flags(agpio); 6878c2ecf20Sopenharmony_ci lookup->info.polarity = lookup->active_low; 6888c2ecf20Sopenharmony_ci } 6898c2ecf20Sopenharmony_ci } 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci return 1; 6928c2ecf20Sopenharmony_ci} 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_cistatic int acpi_gpio_resource_lookup(struct acpi_gpio_lookup *lookup, 6958c2ecf20Sopenharmony_ci struct acpi_gpio_info *info) 6968c2ecf20Sopenharmony_ci{ 6978c2ecf20Sopenharmony_ci struct acpi_device *adev = lookup->info.adev; 6988c2ecf20Sopenharmony_ci struct list_head res_list; 6998c2ecf20Sopenharmony_ci int ret; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&res_list); 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci ret = acpi_dev_get_resources(adev, &res_list, 7048c2ecf20Sopenharmony_ci acpi_populate_gpio_lookup, 7058c2ecf20Sopenharmony_ci lookup); 7068c2ecf20Sopenharmony_ci if (ret < 0) 7078c2ecf20Sopenharmony_ci return ret; 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci acpi_dev_free_resource_list(&res_list); 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci if (!lookup->desc) 7128c2ecf20Sopenharmony_ci return -ENOENT; 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci if (info) 7158c2ecf20Sopenharmony_ci *info = lookup->info; 7168c2ecf20Sopenharmony_ci return 0; 7178c2ecf20Sopenharmony_ci} 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_cistatic int acpi_gpio_property_lookup(struct fwnode_handle *fwnode, 7208c2ecf20Sopenharmony_ci const char *propname, int index, 7218c2ecf20Sopenharmony_ci struct acpi_gpio_lookup *lookup) 7228c2ecf20Sopenharmony_ci{ 7238c2ecf20Sopenharmony_ci struct fwnode_reference_args args; 7248c2ecf20Sopenharmony_ci unsigned int quirks = 0; 7258c2ecf20Sopenharmony_ci int ret; 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci memset(&args, 0, sizeof(args)); 7288c2ecf20Sopenharmony_ci ret = __acpi_node_get_property_reference(fwnode, propname, index, 3, 7298c2ecf20Sopenharmony_ci &args); 7308c2ecf20Sopenharmony_ci if (ret) { 7318c2ecf20Sopenharmony_ci struct acpi_device *adev = to_acpi_device_node(fwnode); 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci if (!adev) 7348c2ecf20Sopenharmony_ci return ret; 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci if (!acpi_get_driver_gpio_data(adev, propname, index, &args, 7378c2ecf20Sopenharmony_ci &quirks)) 7388c2ecf20Sopenharmony_ci return ret; 7398c2ecf20Sopenharmony_ci } 7408c2ecf20Sopenharmony_ci /* 7418c2ecf20Sopenharmony_ci * The property was found and resolved, so need to lookup the GPIO based 7428c2ecf20Sopenharmony_ci * on returned args. 7438c2ecf20Sopenharmony_ci */ 7448c2ecf20Sopenharmony_ci if (!to_acpi_device_node(args.fwnode)) 7458c2ecf20Sopenharmony_ci return -EINVAL; 7468c2ecf20Sopenharmony_ci if (args.nargs != 3) 7478c2ecf20Sopenharmony_ci return -EPROTO; 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci lookup->index = args.args[0]; 7508c2ecf20Sopenharmony_ci lookup->pin_index = args.args[1]; 7518c2ecf20Sopenharmony_ci lookup->active_low = !!args.args[2]; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci lookup->info.adev = to_acpi_device_node(args.fwnode); 7548c2ecf20Sopenharmony_ci lookup->info.quirks = quirks; 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci return 0; 7578c2ecf20Sopenharmony_ci} 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci/** 7608c2ecf20Sopenharmony_ci * acpi_get_gpiod_by_index() - get a GPIO descriptor from device resources 7618c2ecf20Sopenharmony_ci * @adev: pointer to a ACPI device to get GPIO from 7628c2ecf20Sopenharmony_ci * @propname: Property name of the GPIO (optional) 7638c2ecf20Sopenharmony_ci * @index: index of GpioIo/GpioInt resource (starting from %0) 7648c2ecf20Sopenharmony_ci * @info: info pointer to fill in (optional) 7658c2ecf20Sopenharmony_ci * 7668c2ecf20Sopenharmony_ci * Function goes through ACPI resources for @adev and based on @index looks 7678c2ecf20Sopenharmony_ci * up a GpioIo/GpioInt resource, translates it to the Linux GPIO descriptor, 7688c2ecf20Sopenharmony_ci * and returns it. @index matches GpioIo/GpioInt resources only so if there 7698c2ecf20Sopenharmony_ci * are total %3 GPIO resources, the index goes from %0 to %2. 7708c2ecf20Sopenharmony_ci * 7718c2ecf20Sopenharmony_ci * If @propname is specified the GPIO is looked using device property. In 7728c2ecf20Sopenharmony_ci * that case @index is used to select the GPIO entry in the property value 7738c2ecf20Sopenharmony_ci * (in case of multiple). 7748c2ecf20Sopenharmony_ci * 7758c2ecf20Sopenharmony_ci * If the GPIO cannot be translated or there is an error, an ERR_PTR is 7768c2ecf20Sopenharmony_ci * returned. 7778c2ecf20Sopenharmony_ci * 7788c2ecf20Sopenharmony_ci * Note: if the GPIO resource has multiple entries in the pin list, this 7798c2ecf20Sopenharmony_ci * function only returns the first. 7808c2ecf20Sopenharmony_ci */ 7818c2ecf20Sopenharmony_cistatic struct gpio_desc *acpi_get_gpiod_by_index(struct acpi_device *adev, 7828c2ecf20Sopenharmony_ci const char *propname, int index, 7838c2ecf20Sopenharmony_ci struct acpi_gpio_info *info) 7848c2ecf20Sopenharmony_ci{ 7858c2ecf20Sopenharmony_ci struct acpi_gpio_lookup lookup; 7868c2ecf20Sopenharmony_ci int ret; 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci if (!adev) 7898c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci memset(&lookup, 0, sizeof(lookup)); 7928c2ecf20Sopenharmony_ci lookup.index = index; 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci if (propname) { 7958c2ecf20Sopenharmony_ci dev_dbg(&adev->dev, "GPIO: looking up %s\n", propname); 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci ret = acpi_gpio_property_lookup(acpi_fwnode_handle(adev), 7988c2ecf20Sopenharmony_ci propname, index, &lookup); 7998c2ecf20Sopenharmony_ci if (ret) 8008c2ecf20Sopenharmony_ci return ERR_PTR(ret); 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci dev_dbg(&adev->dev, "GPIO: _DSD returned %s %d %d %u\n", 8038c2ecf20Sopenharmony_ci dev_name(&lookup.info.adev->dev), lookup.index, 8048c2ecf20Sopenharmony_ci lookup.pin_index, lookup.active_low); 8058c2ecf20Sopenharmony_ci } else { 8068c2ecf20Sopenharmony_ci dev_dbg(&adev->dev, "GPIO: looking up %d in _CRS\n", index); 8078c2ecf20Sopenharmony_ci lookup.info.adev = adev; 8088c2ecf20Sopenharmony_ci } 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci ret = acpi_gpio_resource_lookup(&lookup, info); 8118c2ecf20Sopenharmony_ci return ret ? ERR_PTR(ret) : lookup.desc; 8128c2ecf20Sopenharmony_ci} 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_cistatic bool acpi_can_fallback_to_crs(struct acpi_device *adev, 8158c2ecf20Sopenharmony_ci const char *con_id) 8168c2ecf20Sopenharmony_ci{ 8178c2ecf20Sopenharmony_ci /* Never allow fallback if the device has properties */ 8188c2ecf20Sopenharmony_ci if (acpi_dev_has_props(adev) || adev->driver_gpios) 8198c2ecf20Sopenharmony_ci return false; 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci return con_id == NULL; 8228c2ecf20Sopenharmony_ci} 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_cistruct gpio_desc *acpi_find_gpio(struct device *dev, 8258c2ecf20Sopenharmony_ci const char *con_id, 8268c2ecf20Sopenharmony_ci unsigned int idx, 8278c2ecf20Sopenharmony_ci enum gpiod_flags *dflags, 8288c2ecf20Sopenharmony_ci unsigned long *lookupflags) 8298c2ecf20Sopenharmony_ci{ 8308c2ecf20Sopenharmony_ci struct acpi_device *adev = ACPI_COMPANION(dev); 8318c2ecf20Sopenharmony_ci struct acpi_gpio_info info; 8328c2ecf20Sopenharmony_ci struct gpio_desc *desc; 8338c2ecf20Sopenharmony_ci char propname[32]; 8348c2ecf20Sopenharmony_ci int i; 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci /* Try first from _DSD */ 8378c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) { 8388c2ecf20Sopenharmony_ci if (con_id) { 8398c2ecf20Sopenharmony_ci snprintf(propname, sizeof(propname), "%s-%s", 8408c2ecf20Sopenharmony_ci con_id, gpio_suffixes[i]); 8418c2ecf20Sopenharmony_ci } else { 8428c2ecf20Sopenharmony_ci snprintf(propname, sizeof(propname), "%s", 8438c2ecf20Sopenharmony_ci gpio_suffixes[i]); 8448c2ecf20Sopenharmony_ci } 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci desc = acpi_get_gpiod_by_index(adev, propname, idx, &info); 8478c2ecf20Sopenharmony_ci if (!IS_ERR(desc)) 8488c2ecf20Sopenharmony_ci break; 8498c2ecf20Sopenharmony_ci if (PTR_ERR(desc) == -EPROBE_DEFER) 8508c2ecf20Sopenharmony_ci return ERR_CAST(desc); 8518c2ecf20Sopenharmony_ci } 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci /* Then from plain _CRS GPIOs */ 8548c2ecf20Sopenharmony_ci if (IS_ERR(desc)) { 8558c2ecf20Sopenharmony_ci if (!acpi_can_fallback_to_crs(adev, con_id)) 8568c2ecf20Sopenharmony_ci return ERR_PTR(-ENOENT); 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci desc = acpi_get_gpiod_by_index(adev, NULL, idx, &info); 8598c2ecf20Sopenharmony_ci if (IS_ERR(desc)) 8608c2ecf20Sopenharmony_ci return desc; 8618c2ecf20Sopenharmony_ci } 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci if (info.gpioint && 8648c2ecf20Sopenharmony_ci (*dflags == GPIOD_OUT_LOW || *dflags == GPIOD_OUT_HIGH)) { 8658c2ecf20Sopenharmony_ci dev_dbg(dev, "refusing GpioInt() entry when doing GPIOD_OUT_* lookup\n"); 8668c2ecf20Sopenharmony_ci return ERR_PTR(-ENOENT); 8678c2ecf20Sopenharmony_ci } 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci acpi_gpio_update_gpiod_flags(dflags, &info); 8708c2ecf20Sopenharmony_ci acpi_gpio_update_gpiod_lookup_flags(lookupflags, &info); 8718c2ecf20Sopenharmony_ci return desc; 8728c2ecf20Sopenharmony_ci} 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci/** 8758c2ecf20Sopenharmony_ci * acpi_node_get_gpiod() - get a GPIO descriptor from ACPI resources 8768c2ecf20Sopenharmony_ci * @fwnode: pointer to an ACPI firmware node to get the GPIO information from 8778c2ecf20Sopenharmony_ci * @propname: Property name of the GPIO 8788c2ecf20Sopenharmony_ci * @index: index of GpioIo/GpioInt resource (starting from %0) 8798c2ecf20Sopenharmony_ci * @info: info pointer to fill in (optional) 8808c2ecf20Sopenharmony_ci * 8818c2ecf20Sopenharmony_ci * If @fwnode is an ACPI device object, call acpi_get_gpiod_by_index() for it. 8828c2ecf20Sopenharmony_ci * Otherwise (i.e. it is a data-only non-device object), use the property-based 8838c2ecf20Sopenharmony_ci * GPIO lookup to get to the GPIO resource with the relevant information and use 8848c2ecf20Sopenharmony_ci * that to obtain the GPIO descriptor to return. 8858c2ecf20Sopenharmony_ci * 8868c2ecf20Sopenharmony_ci * If the GPIO cannot be translated or there is an error an ERR_PTR is 8878c2ecf20Sopenharmony_ci * returned. 8888c2ecf20Sopenharmony_ci */ 8898c2ecf20Sopenharmony_cistruct gpio_desc *acpi_node_get_gpiod(struct fwnode_handle *fwnode, 8908c2ecf20Sopenharmony_ci const char *propname, int index, 8918c2ecf20Sopenharmony_ci struct acpi_gpio_info *info) 8928c2ecf20Sopenharmony_ci{ 8938c2ecf20Sopenharmony_ci struct acpi_gpio_lookup lookup; 8948c2ecf20Sopenharmony_ci struct acpi_device *adev; 8958c2ecf20Sopenharmony_ci int ret; 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci adev = to_acpi_device_node(fwnode); 8988c2ecf20Sopenharmony_ci if (adev) 8998c2ecf20Sopenharmony_ci return acpi_get_gpiod_by_index(adev, propname, index, info); 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci if (!is_acpi_data_node(fwnode)) 9028c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci if (!propname) 9058c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci memset(&lookup, 0, sizeof(lookup)); 9088c2ecf20Sopenharmony_ci lookup.index = index; 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci ret = acpi_gpio_property_lookup(fwnode, propname, index, &lookup); 9118c2ecf20Sopenharmony_ci if (ret) 9128c2ecf20Sopenharmony_ci return ERR_PTR(ret); 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci ret = acpi_gpio_resource_lookup(&lookup, info); 9158c2ecf20Sopenharmony_ci return ret ? ERR_PTR(ret) : lookup.desc; 9168c2ecf20Sopenharmony_ci} 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci/** 9198c2ecf20Sopenharmony_ci * acpi_dev_gpio_irq_get_by() - Find GpioInt and translate it to Linux IRQ number 9208c2ecf20Sopenharmony_ci * @adev: pointer to a ACPI device to get IRQ from 9218c2ecf20Sopenharmony_ci * @name: optional name of GpioInt resource 9228c2ecf20Sopenharmony_ci * @index: index of GpioInt resource (starting from %0) 9238c2ecf20Sopenharmony_ci * 9248c2ecf20Sopenharmony_ci * If the device has one or more GpioInt resources, this function can be 9258c2ecf20Sopenharmony_ci * used to translate from the GPIO offset in the resource to the Linux IRQ 9268c2ecf20Sopenharmony_ci * number. 9278c2ecf20Sopenharmony_ci * 9288c2ecf20Sopenharmony_ci * The function is idempotent, though each time it runs it will configure GPIO 9298c2ecf20Sopenharmony_ci * pin direction according to the flags in GpioInt resource. 9308c2ecf20Sopenharmony_ci * 9318c2ecf20Sopenharmony_ci * The function takes optional @name parameter. If the resource has a property 9328c2ecf20Sopenharmony_ci * name, then only those will be taken into account. 9338c2ecf20Sopenharmony_ci * 9348c2ecf20Sopenharmony_ci * Return: Linux IRQ number (> %0) on success, negative errno on failure. 9358c2ecf20Sopenharmony_ci */ 9368c2ecf20Sopenharmony_ciint acpi_dev_gpio_irq_get_by(struct acpi_device *adev, const char *name, int index) 9378c2ecf20Sopenharmony_ci{ 9388c2ecf20Sopenharmony_ci int idx, i; 9398c2ecf20Sopenharmony_ci unsigned int irq_flags; 9408c2ecf20Sopenharmony_ci int ret; 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci for (i = 0, idx = 0; idx <= index; i++) { 9438c2ecf20Sopenharmony_ci struct acpi_gpio_info info; 9448c2ecf20Sopenharmony_ci struct gpio_desc *desc; 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci desc = acpi_get_gpiod_by_index(adev, name, i, &info); 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci /* Ignore -EPROBE_DEFER, it only matters if idx matches */ 9498c2ecf20Sopenharmony_ci if (IS_ERR(desc) && PTR_ERR(desc) != -EPROBE_DEFER) 9508c2ecf20Sopenharmony_ci return PTR_ERR(desc); 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci if (info.gpioint && idx++ == index) { 9538c2ecf20Sopenharmony_ci unsigned long lflags = GPIO_LOOKUP_FLAGS_DEFAULT; 9548c2ecf20Sopenharmony_ci char label[32]; 9558c2ecf20Sopenharmony_ci int irq; 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci if (IS_ERR(desc)) 9588c2ecf20Sopenharmony_ci return PTR_ERR(desc); 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci irq = gpiod_to_irq(desc); 9618c2ecf20Sopenharmony_ci if (irq < 0) 9628c2ecf20Sopenharmony_ci return irq; 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci snprintf(label, sizeof(label), "GpioInt() %d", index); 9658c2ecf20Sopenharmony_ci ret = gpiod_configure_flags(desc, label, lflags, info.flags); 9668c2ecf20Sopenharmony_ci if (ret < 0) 9678c2ecf20Sopenharmony_ci return ret; 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci irq_flags = acpi_dev_get_irq_type(info.triggering, 9708c2ecf20Sopenharmony_ci info.polarity); 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci /* 9738c2ecf20Sopenharmony_ci * If the IRQ is not already in use then set type 9748c2ecf20Sopenharmony_ci * if specified and different than the current one. 9758c2ecf20Sopenharmony_ci */ 9768c2ecf20Sopenharmony_ci if (can_request_irq(irq, irq_flags)) { 9778c2ecf20Sopenharmony_ci if (irq_flags != IRQ_TYPE_NONE && 9788c2ecf20Sopenharmony_ci irq_flags != irq_get_trigger_type(irq)) 9798c2ecf20Sopenharmony_ci irq_set_irq_type(irq, irq_flags); 9808c2ecf20Sopenharmony_ci } else { 9818c2ecf20Sopenharmony_ci dev_dbg(&adev->dev, "IRQ %d already in use\n", irq); 9828c2ecf20Sopenharmony_ci } 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci return irq; 9858c2ecf20Sopenharmony_ci } 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci } 9888c2ecf20Sopenharmony_ci return -ENOENT; 9898c2ecf20Sopenharmony_ci} 9908c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(acpi_dev_gpio_irq_get_by); 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_cistatic acpi_status 9938c2ecf20Sopenharmony_ciacpi_gpio_adr_space_handler(u32 function, acpi_physical_address address, 9948c2ecf20Sopenharmony_ci u32 bits, u64 *value, void *handler_context, 9958c2ecf20Sopenharmony_ci void *region_context) 9968c2ecf20Sopenharmony_ci{ 9978c2ecf20Sopenharmony_ci struct acpi_gpio_chip *achip = region_context; 9988c2ecf20Sopenharmony_ci struct gpio_chip *chip = achip->chip; 9998c2ecf20Sopenharmony_ci struct acpi_resource_gpio *agpio; 10008c2ecf20Sopenharmony_ci struct acpi_resource *ares; 10018c2ecf20Sopenharmony_ci int pin_index = (int)address; 10028c2ecf20Sopenharmony_ci acpi_status status; 10038c2ecf20Sopenharmony_ci int length; 10048c2ecf20Sopenharmony_ci int i; 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci status = acpi_buffer_to_resource(achip->conn_info.connection, 10078c2ecf20Sopenharmony_ci achip->conn_info.length, &ares); 10088c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) 10098c2ecf20Sopenharmony_ci return status; 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci if (WARN_ON(ares->type != ACPI_RESOURCE_TYPE_GPIO)) { 10128c2ecf20Sopenharmony_ci ACPI_FREE(ares); 10138c2ecf20Sopenharmony_ci return AE_BAD_PARAMETER; 10148c2ecf20Sopenharmony_ci } 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci agpio = &ares->data.gpio; 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci if (WARN_ON(agpio->io_restriction == ACPI_IO_RESTRICT_INPUT && 10198c2ecf20Sopenharmony_ci function == ACPI_WRITE)) { 10208c2ecf20Sopenharmony_ci ACPI_FREE(ares); 10218c2ecf20Sopenharmony_ci return AE_BAD_PARAMETER; 10228c2ecf20Sopenharmony_ci } 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci length = min(agpio->pin_table_length, (u16)(pin_index + bits)); 10258c2ecf20Sopenharmony_ci for (i = pin_index; i < length; ++i) { 10268c2ecf20Sopenharmony_ci int pin = agpio->pin_table[i]; 10278c2ecf20Sopenharmony_ci struct acpi_gpio_connection *conn; 10288c2ecf20Sopenharmony_ci struct gpio_desc *desc; 10298c2ecf20Sopenharmony_ci bool found; 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci mutex_lock(&achip->conn_lock); 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci found = false; 10348c2ecf20Sopenharmony_ci list_for_each_entry(conn, &achip->conns, node) { 10358c2ecf20Sopenharmony_ci if (conn->pin == pin) { 10368c2ecf20Sopenharmony_ci found = true; 10378c2ecf20Sopenharmony_ci desc = conn->desc; 10388c2ecf20Sopenharmony_ci break; 10398c2ecf20Sopenharmony_ci } 10408c2ecf20Sopenharmony_ci } 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci /* 10438c2ecf20Sopenharmony_ci * The same GPIO can be shared between operation region and 10448c2ecf20Sopenharmony_ci * event but only if the access here is ACPI_READ. In that 10458c2ecf20Sopenharmony_ci * case we "borrow" the event GPIO instead. 10468c2ecf20Sopenharmony_ci */ 10478c2ecf20Sopenharmony_ci if (!found && agpio->shareable == ACPI_SHARED && 10488c2ecf20Sopenharmony_ci function == ACPI_READ) { 10498c2ecf20Sopenharmony_ci struct acpi_gpio_event *event; 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci list_for_each_entry(event, &achip->events, node) { 10528c2ecf20Sopenharmony_ci if (event->pin == pin) { 10538c2ecf20Sopenharmony_ci desc = event->desc; 10548c2ecf20Sopenharmony_ci found = true; 10558c2ecf20Sopenharmony_ci break; 10568c2ecf20Sopenharmony_ci } 10578c2ecf20Sopenharmony_ci } 10588c2ecf20Sopenharmony_ci } 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci if (!found) { 10618c2ecf20Sopenharmony_ci enum gpiod_flags flags = acpi_gpio_to_gpiod_flags(agpio); 10628c2ecf20Sopenharmony_ci const char *label = "ACPI:OpRegion"; 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci desc = gpiochip_request_own_desc(chip, pin, label, 10658c2ecf20Sopenharmony_ci GPIO_ACTIVE_HIGH, 10668c2ecf20Sopenharmony_ci flags); 10678c2ecf20Sopenharmony_ci if (IS_ERR(desc)) { 10688c2ecf20Sopenharmony_ci status = AE_ERROR; 10698c2ecf20Sopenharmony_ci mutex_unlock(&achip->conn_lock); 10708c2ecf20Sopenharmony_ci goto out; 10718c2ecf20Sopenharmony_ci } 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci conn = kzalloc(sizeof(*conn), GFP_KERNEL); 10748c2ecf20Sopenharmony_ci if (!conn) { 10758c2ecf20Sopenharmony_ci status = AE_NO_MEMORY; 10768c2ecf20Sopenharmony_ci gpiochip_free_own_desc(desc); 10778c2ecf20Sopenharmony_ci mutex_unlock(&achip->conn_lock); 10788c2ecf20Sopenharmony_ci goto out; 10798c2ecf20Sopenharmony_ci } 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci conn->pin = pin; 10828c2ecf20Sopenharmony_ci conn->desc = desc; 10838c2ecf20Sopenharmony_ci list_add_tail(&conn->node, &achip->conns); 10848c2ecf20Sopenharmony_ci } 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci mutex_unlock(&achip->conn_lock); 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci if (function == ACPI_WRITE) 10898c2ecf20Sopenharmony_ci gpiod_set_raw_value_cansleep(desc, 10908c2ecf20Sopenharmony_ci !!((1 << i) & *value)); 10918c2ecf20Sopenharmony_ci else 10928c2ecf20Sopenharmony_ci *value |= (u64)gpiod_get_raw_value_cansleep(desc) << i; 10938c2ecf20Sopenharmony_ci } 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ciout: 10968c2ecf20Sopenharmony_ci ACPI_FREE(ares); 10978c2ecf20Sopenharmony_ci return status; 10988c2ecf20Sopenharmony_ci} 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_cistatic void acpi_gpiochip_request_regions(struct acpi_gpio_chip *achip) 11018c2ecf20Sopenharmony_ci{ 11028c2ecf20Sopenharmony_ci struct gpio_chip *chip = achip->chip; 11038c2ecf20Sopenharmony_ci acpi_handle handle = ACPI_HANDLE(chip->parent); 11048c2ecf20Sopenharmony_ci acpi_status status; 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&achip->conns); 11078c2ecf20Sopenharmony_ci mutex_init(&achip->conn_lock); 11088c2ecf20Sopenharmony_ci status = acpi_install_address_space_handler(handle, ACPI_ADR_SPACE_GPIO, 11098c2ecf20Sopenharmony_ci acpi_gpio_adr_space_handler, 11108c2ecf20Sopenharmony_ci NULL, achip); 11118c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) 11128c2ecf20Sopenharmony_ci dev_err(chip->parent, 11138c2ecf20Sopenharmony_ci "Failed to install GPIO OpRegion handler\n"); 11148c2ecf20Sopenharmony_ci} 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_cistatic void acpi_gpiochip_free_regions(struct acpi_gpio_chip *achip) 11178c2ecf20Sopenharmony_ci{ 11188c2ecf20Sopenharmony_ci struct gpio_chip *chip = achip->chip; 11198c2ecf20Sopenharmony_ci acpi_handle handle = ACPI_HANDLE(chip->parent); 11208c2ecf20Sopenharmony_ci struct acpi_gpio_connection *conn, *tmp; 11218c2ecf20Sopenharmony_ci acpi_status status; 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci status = acpi_remove_address_space_handler(handle, ACPI_ADR_SPACE_GPIO, 11248c2ecf20Sopenharmony_ci acpi_gpio_adr_space_handler); 11258c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) { 11268c2ecf20Sopenharmony_ci dev_err(chip->parent, 11278c2ecf20Sopenharmony_ci "Failed to remove GPIO OpRegion handler\n"); 11288c2ecf20Sopenharmony_ci return; 11298c2ecf20Sopenharmony_ci } 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_ci list_for_each_entry_safe_reverse(conn, tmp, &achip->conns, node) { 11328c2ecf20Sopenharmony_ci gpiochip_free_own_desc(conn->desc); 11338c2ecf20Sopenharmony_ci list_del(&conn->node); 11348c2ecf20Sopenharmony_ci kfree(conn); 11358c2ecf20Sopenharmony_ci } 11368c2ecf20Sopenharmony_ci} 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_cistatic struct gpio_desc * 11398c2ecf20Sopenharmony_ciacpi_gpiochip_parse_own_gpio(struct acpi_gpio_chip *achip, 11408c2ecf20Sopenharmony_ci struct fwnode_handle *fwnode, 11418c2ecf20Sopenharmony_ci const char **name, 11428c2ecf20Sopenharmony_ci unsigned long *lflags, 11438c2ecf20Sopenharmony_ci enum gpiod_flags *dflags) 11448c2ecf20Sopenharmony_ci{ 11458c2ecf20Sopenharmony_ci struct gpio_chip *chip = achip->chip; 11468c2ecf20Sopenharmony_ci struct gpio_desc *desc; 11478c2ecf20Sopenharmony_ci u32 gpios[2]; 11488c2ecf20Sopenharmony_ci int ret; 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ci *lflags = GPIO_LOOKUP_FLAGS_DEFAULT; 11518c2ecf20Sopenharmony_ci *dflags = 0; 11528c2ecf20Sopenharmony_ci *name = NULL; 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci ret = fwnode_property_read_u32_array(fwnode, "gpios", gpios, 11558c2ecf20Sopenharmony_ci ARRAY_SIZE(gpios)); 11568c2ecf20Sopenharmony_ci if (ret < 0) 11578c2ecf20Sopenharmony_ci return ERR_PTR(ret); 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci desc = gpiochip_get_desc(chip, gpios[0]); 11608c2ecf20Sopenharmony_ci if (IS_ERR(desc)) 11618c2ecf20Sopenharmony_ci return desc; 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci if (gpios[1]) 11648c2ecf20Sopenharmony_ci *lflags |= GPIO_ACTIVE_LOW; 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci if (fwnode_property_present(fwnode, "input")) 11678c2ecf20Sopenharmony_ci *dflags |= GPIOD_IN; 11688c2ecf20Sopenharmony_ci else if (fwnode_property_present(fwnode, "output-low")) 11698c2ecf20Sopenharmony_ci *dflags |= GPIOD_OUT_LOW; 11708c2ecf20Sopenharmony_ci else if (fwnode_property_present(fwnode, "output-high")) 11718c2ecf20Sopenharmony_ci *dflags |= GPIOD_OUT_HIGH; 11728c2ecf20Sopenharmony_ci else 11738c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci fwnode_property_read_string(fwnode, "line-name", name); 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci return desc; 11788c2ecf20Sopenharmony_ci} 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_cistatic void acpi_gpiochip_scan_gpios(struct acpi_gpio_chip *achip) 11818c2ecf20Sopenharmony_ci{ 11828c2ecf20Sopenharmony_ci struct gpio_chip *chip = achip->chip; 11838c2ecf20Sopenharmony_ci struct fwnode_handle *fwnode; 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci device_for_each_child_node(chip->parent, fwnode) { 11868c2ecf20Sopenharmony_ci unsigned long lflags; 11878c2ecf20Sopenharmony_ci enum gpiod_flags dflags; 11888c2ecf20Sopenharmony_ci struct gpio_desc *desc; 11898c2ecf20Sopenharmony_ci const char *name; 11908c2ecf20Sopenharmony_ci int ret; 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci if (!fwnode_property_present(fwnode, "gpio-hog")) 11938c2ecf20Sopenharmony_ci continue; 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci desc = acpi_gpiochip_parse_own_gpio(achip, fwnode, &name, 11968c2ecf20Sopenharmony_ci &lflags, &dflags); 11978c2ecf20Sopenharmony_ci if (IS_ERR(desc)) 11988c2ecf20Sopenharmony_ci continue; 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_ci ret = gpiod_hog(desc, name, lflags, dflags); 12018c2ecf20Sopenharmony_ci if (ret) { 12028c2ecf20Sopenharmony_ci dev_err(chip->parent, "Failed to hog GPIO\n"); 12038c2ecf20Sopenharmony_ci fwnode_handle_put(fwnode); 12048c2ecf20Sopenharmony_ci return; 12058c2ecf20Sopenharmony_ci } 12068c2ecf20Sopenharmony_ci } 12078c2ecf20Sopenharmony_ci} 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_civoid acpi_gpiochip_add(struct gpio_chip *chip) 12108c2ecf20Sopenharmony_ci{ 12118c2ecf20Sopenharmony_ci struct acpi_gpio_chip *acpi_gpio; 12128c2ecf20Sopenharmony_ci acpi_handle handle; 12138c2ecf20Sopenharmony_ci acpi_status status; 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci if (!chip || !chip->parent) 12168c2ecf20Sopenharmony_ci return; 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci handle = ACPI_HANDLE(chip->parent); 12198c2ecf20Sopenharmony_ci if (!handle) 12208c2ecf20Sopenharmony_ci return; 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci acpi_gpio = kzalloc(sizeof(*acpi_gpio), GFP_KERNEL); 12238c2ecf20Sopenharmony_ci if (!acpi_gpio) { 12248c2ecf20Sopenharmony_ci dev_err(chip->parent, 12258c2ecf20Sopenharmony_ci "Failed to allocate memory for ACPI GPIO chip\n"); 12268c2ecf20Sopenharmony_ci return; 12278c2ecf20Sopenharmony_ci } 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci acpi_gpio->chip = chip; 12308c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&acpi_gpio->events); 12318c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&acpi_gpio->deferred_req_irqs_list_entry); 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci status = acpi_attach_data(handle, acpi_gpio_chip_dh, acpi_gpio); 12348c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) { 12358c2ecf20Sopenharmony_ci dev_err(chip->parent, "Failed to attach ACPI GPIO chip\n"); 12368c2ecf20Sopenharmony_ci kfree(acpi_gpio); 12378c2ecf20Sopenharmony_ci return; 12388c2ecf20Sopenharmony_ci } 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_ci acpi_gpiochip_request_regions(acpi_gpio); 12418c2ecf20Sopenharmony_ci acpi_gpiochip_scan_gpios(acpi_gpio); 12428c2ecf20Sopenharmony_ci acpi_walk_dep_device_list(handle); 12438c2ecf20Sopenharmony_ci} 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_civoid acpi_gpiochip_remove(struct gpio_chip *chip) 12468c2ecf20Sopenharmony_ci{ 12478c2ecf20Sopenharmony_ci struct acpi_gpio_chip *acpi_gpio; 12488c2ecf20Sopenharmony_ci acpi_handle handle; 12498c2ecf20Sopenharmony_ci acpi_status status; 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_ci if (!chip || !chip->parent) 12528c2ecf20Sopenharmony_ci return; 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci handle = ACPI_HANDLE(chip->parent); 12558c2ecf20Sopenharmony_ci if (!handle) 12568c2ecf20Sopenharmony_ci return; 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_ci status = acpi_get_data(handle, acpi_gpio_chip_dh, (void **)&acpi_gpio); 12598c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) { 12608c2ecf20Sopenharmony_ci dev_warn(chip->parent, "Failed to retrieve ACPI GPIO chip\n"); 12618c2ecf20Sopenharmony_ci return; 12628c2ecf20Sopenharmony_ci } 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci acpi_gpiochip_free_regions(acpi_gpio); 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci acpi_detach_data(handle, acpi_gpio_chip_dh); 12678c2ecf20Sopenharmony_ci kfree(acpi_gpio); 12688c2ecf20Sopenharmony_ci} 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_cistatic int acpi_gpio_package_count(const union acpi_object *obj) 12718c2ecf20Sopenharmony_ci{ 12728c2ecf20Sopenharmony_ci const union acpi_object *element = obj->package.elements; 12738c2ecf20Sopenharmony_ci const union acpi_object *end = element + obj->package.count; 12748c2ecf20Sopenharmony_ci unsigned int count = 0; 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_ci while (element < end) { 12778c2ecf20Sopenharmony_ci switch (element->type) { 12788c2ecf20Sopenharmony_ci case ACPI_TYPE_LOCAL_REFERENCE: 12798c2ecf20Sopenharmony_ci element += 3; 12808c2ecf20Sopenharmony_ci fallthrough; 12818c2ecf20Sopenharmony_ci case ACPI_TYPE_INTEGER: 12828c2ecf20Sopenharmony_ci element++; 12838c2ecf20Sopenharmony_ci count++; 12848c2ecf20Sopenharmony_ci break; 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci default: 12878c2ecf20Sopenharmony_ci return -EPROTO; 12888c2ecf20Sopenharmony_ci } 12898c2ecf20Sopenharmony_ci } 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci return count; 12928c2ecf20Sopenharmony_ci} 12938c2ecf20Sopenharmony_ci 12948c2ecf20Sopenharmony_cistatic int acpi_find_gpio_count(struct acpi_resource *ares, void *data) 12958c2ecf20Sopenharmony_ci{ 12968c2ecf20Sopenharmony_ci unsigned int *count = data; 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci if (ares->type == ACPI_RESOURCE_TYPE_GPIO) 12998c2ecf20Sopenharmony_ci *count += ares->data.gpio.pin_table_length; 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_ci return 1; 13028c2ecf20Sopenharmony_ci} 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci/** 13058c2ecf20Sopenharmony_ci * acpi_gpio_count - count the GPIOs associated with a device / function 13068c2ecf20Sopenharmony_ci * @dev: GPIO consumer, can be %NULL for system-global GPIOs 13078c2ecf20Sopenharmony_ci * @con_id: function within the GPIO consumer 13088c2ecf20Sopenharmony_ci * 13098c2ecf20Sopenharmony_ci * Return: 13108c2ecf20Sopenharmony_ci * The number of GPIOs associated with a device / function or %-ENOENT, 13118c2ecf20Sopenharmony_ci * if no GPIO has been assigned to the requested function. 13128c2ecf20Sopenharmony_ci */ 13138c2ecf20Sopenharmony_ciint acpi_gpio_count(struct device *dev, const char *con_id) 13148c2ecf20Sopenharmony_ci{ 13158c2ecf20Sopenharmony_ci struct acpi_device *adev = ACPI_COMPANION(dev); 13168c2ecf20Sopenharmony_ci const union acpi_object *obj; 13178c2ecf20Sopenharmony_ci const struct acpi_gpio_mapping *gm; 13188c2ecf20Sopenharmony_ci int count = -ENOENT; 13198c2ecf20Sopenharmony_ci int ret; 13208c2ecf20Sopenharmony_ci char propname[32]; 13218c2ecf20Sopenharmony_ci unsigned int i; 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci /* Try first from _DSD */ 13248c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) { 13258c2ecf20Sopenharmony_ci if (con_id) 13268c2ecf20Sopenharmony_ci snprintf(propname, sizeof(propname), "%s-%s", 13278c2ecf20Sopenharmony_ci con_id, gpio_suffixes[i]); 13288c2ecf20Sopenharmony_ci else 13298c2ecf20Sopenharmony_ci snprintf(propname, sizeof(propname), "%s", 13308c2ecf20Sopenharmony_ci gpio_suffixes[i]); 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_ci ret = acpi_dev_get_property(adev, propname, ACPI_TYPE_ANY, 13338c2ecf20Sopenharmony_ci &obj); 13348c2ecf20Sopenharmony_ci if (ret == 0) { 13358c2ecf20Sopenharmony_ci if (obj->type == ACPI_TYPE_LOCAL_REFERENCE) 13368c2ecf20Sopenharmony_ci count = 1; 13378c2ecf20Sopenharmony_ci else if (obj->type == ACPI_TYPE_PACKAGE) 13388c2ecf20Sopenharmony_ci count = acpi_gpio_package_count(obj); 13398c2ecf20Sopenharmony_ci } else if (adev->driver_gpios) { 13408c2ecf20Sopenharmony_ci for (gm = adev->driver_gpios; gm->name; gm++) 13418c2ecf20Sopenharmony_ci if (strcmp(propname, gm->name) == 0) { 13428c2ecf20Sopenharmony_ci count = gm->size; 13438c2ecf20Sopenharmony_ci break; 13448c2ecf20Sopenharmony_ci } 13458c2ecf20Sopenharmony_ci } 13468c2ecf20Sopenharmony_ci if (count > 0) 13478c2ecf20Sopenharmony_ci break; 13488c2ecf20Sopenharmony_ci } 13498c2ecf20Sopenharmony_ci 13508c2ecf20Sopenharmony_ci /* Then from plain _CRS GPIOs */ 13518c2ecf20Sopenharmony_ci if (count < 0) { 13528c2ecf20Sopenharmony_ci struct list_head resource_list; 13538c2ecf20Sopenharmony_ci unsigned int crs_count = 0; 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ci if (!acpi_can_fallback_to_crs(adev, con_id)) 13568c2ecf20Sopenharmony_ci return count; 13578c2ecf20Sopenharmony_ci 13588c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&resource_list); 13598c2ecf20Sopenharmony_ci acpi_dev_get_resources(adev, &resource_list, 13608c2ecf20Sopenharmony_ci acpi_find_gpio_count, &crs_count); 13618c2ecf20Sopenharmony_ci acpi_dev_free_resource_list(&resource_list); 13628c2ecf20Sopenharmony_ci if (crs_count > 0) 13638c2ecf20Sopenharmony_ci count = crs_count; 13648c2ecf20Sopenharmony_ci } 13658c2ecf20Sopenharmony_ci return count ? count : -ENOENT; 13668c2ecf20Sopenharmony_ci} 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_ci/* Run deferred acpi_gpiochip_request_irqs() */ 13698c2ecf20Sopenharmony_cistatic int __init acpi_gpio_handle_deferred_request_irqs(void) 13708c2ecf20Sopenharmony_ci{ 13718c2ecf20Sopenharmony_ci struct acpi_gpio_chip *acpi_gpio, *tmp; 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_ci mutex_lock(&acpi_gpio_deferred_req_irqs_lock); 13748c2ecf20Sopenharmony_ci list_for_each_entry_safe(acpi_gpio, tmp, 13758c2ecf20Sopenharmony_ci &acpi_gpio_deferred_req_irqs_list, 13768c2ecf20Sopenharmony_ci deferred_req_irqs_list_entry) 13778c2ecf20Sopenharmony_ci acpi_gpiochip_request_irqs(acpi_gpio); 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_ci acpi_gpio_deferred_req_irqs_done = true; 13808c2ecf20Sopenharmony_ci mutex_unlock(&acpi_gpio_deferred_req_irqs_lock); 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci return 0; 13838c2ecf20Sopenharmony_ci} 13848c2ecf20Sopenharmony_ci/* We must use _sync so that this runs after the first deferred_probe run */ 13858c2ecf20Sopenharmony_cilate_initcall_sync(acpi_gpio_handle_deferred_request_irqs); 13868c2ecf20Sopenharmony_ci 13878c2ecf20Sopenharmony_cistatic const struct dmi_system_id gpiolib_acpi_quirks[] __initconst = { 13888c2ecf20Sopenharmony_ci { 13898c2ecf20Sopenharmony_ci /* 13908c2ecf20Sopenharmony_ci * The Minix Neo Z83-4 has a micro-USB-B id-pin handler for 13918c2ecf20Sopenharmony_ci * a non existing micro-USB-B connector which puts the HDMI 13928c2ecf20Sopenharmony_ci * DDC pins in GPIO mode, breaking HDMI support. 13938c2ecf20Sopenharmony_ci */ 13948c2ecf20Sopenharmony_ci .matches = { 13958c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "MINIX"), 13968c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "Z83-4"), 13978c2ecf20Sopenharmony_ci }, 13988c2ecf20Sopenharmony_ci .driver_data = &(struct acpi_gpiolib_dmi_quirk) { 13998c2ecf20Sopenharmony_ci .no_edge_events_on_boot = true, 14008c2ecf20Sopenharmony_ci }, 14018c2ecf20Sopenharmony_ci }, 14028c2ecf20Sopenharmony_ci { 14038c2ecf20Sopenharmony_ci /* 14048c2ecf20Sopenharmony_ci * The Terra Pad 1061 has a micro-USB-B id-pin handler, which 14058c2ecf20Sopenharmony_ci * instead of controlling the actual micro-USB-B turns the 5V 14068c2ecf20Sopenharmony_ci * boost for its USB-A connector off. The actual micro-USB-B 14078c2ecf20Sopenharmony_ci * connector is wired for charging only. 14088c2ecf20Sopenharmony_ci */ 14098c2ecf20Sopenharmony_ci .matches = { 14108c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Wortmann_AG"), 14118c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "TERRA_PAD_1061"), 14128c2ecf20Sopenharmony_ci }, 14138c2ecf20Sopenharmony_ci .driver_data = &(struct acpi_gpiolib_dmi_quirk) { 14148c2ecf20Sopenharmony_ci .no_edge_events_on_boot = true, 14158c2ecf20Sopenharmony_ci }, 14168c2ecf20Sopenharmony_ci }, 14178c2ecf20Sopenharmony_ci { 14188c2ecf20Sopenharmony_ci /* 14198c2ecf20Sopenharmony_ci * The Dell Venue 10 Pro 5055, with Bay Trail SoC + TI PMIC uses an 14208c2ecf20Sopenharmony_ci * external embedded-controller connected via I2C + an ACPI GPIO 14218c2ecf20Sopenharmony_ci * event handler on INT33FFC:02 pin 12, causing spurious wakeups. 14228c2ecf20Sopenharmony_ci */ 14238c2ecf20Sopenharmony_ci .matches = { 14248c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 14258c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "Venue 10 Pro 5055"), 14268c2ecf20Sopenharmony_ci }, 14278c2ecf20Sopenharmony_ci .driver_data = &(struct acpi_gpiolib_dmi_quirk) { 14288c2ecf20Sopenharmony_ci .ignore_wake = "INT33FC:02@12", 14298c2ecf20Sopenharmony_ci }, 14308c2ecf20Sopenharmony_ci }, 14318c2ecf20Sopenharmony_ci { 14328c2ecf20Sopenharmony_ci /* 14338c2ecf20Sopenharmony_ci * HP X2 10 models with Cherry Trail SoC + TI PMIC use an 14348c2ecf20Sopenharmony_ci * external embedded-controller connected via I2C + an ACPI GPIO 14358c2ecf20Sopenharmony_ci * event handler on INT33FF:01 pin 0, causing spurious wakeups. 14368c2ecf20Sopenharmony_ci * When suspending by closing the LID, the power to the USB 14378c2ecf20Sopenharmony_ci * keyboard is turned off, causing INT0002 ACPI events to 14388c2ecf20Sopenharmony_ci * trigger once the XHCI controller notices the keyboard is 14398c2ecf20Sopenharmony_ci * gone. So INT0002 events cause spurious wakeups too. Ignoring 14408c2ecf20Sopenharmony_ci * EC wakes breaks wakeup when opening the lid, the user needs 14418c2ecf20Sopenharmony_ci * to press the power-button to wakeup the system. The 14428c2ecf20Sopenharmony_ci * alternative is suspend simply not working, which is worse. 14438c2ecf20Sopenharmony_ci */ 14448c2ecf20Sopenharmony_ci .matches = { 14458c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "HP"), 14468c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "HP x2 Detachable 10-p0XX"), 14478c2ecf20Sopenharmony_ci }, 14488c2ecf20Sopenharmony_ci .driver_data = &(struct acpi_gpiolib_dmi_quirk) { 14498c2ecf20Sopenharmony_ci .ignore_wake = "INT33FF:01@0,INT0002:00@2", 14508c2ecf20Sopenharmony_ci }, 14518c2ecf20Sopenharmony_ci }, 14528c2ecf20Sopenharmony_ci { 14538c2ecf20Sopenharmony_ci /* 14548c2ecf20Sopenharmony_ci * HP X2 10 models with Bay Trail SoC + AXP288 PMIC use an 14558c2ecf20Sopenharmony_ci * external embedded-controller connected via I2C + an ACPI GPIO 14568c2ecf20Sopenharmony_ci * event handler on INT33FC:02 pin 28, causing spurious wakeups. 14578c2ecf20Sopenharmony_ci */ 14588c2ecf20Sopenharmony_ci .matches = { 14598c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), 14608c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion x2 Detachable"), 14618c2ecf20Sopenharmony_ci DMI_MATCH(DMI_BOARD_NAME, "815D"), 14628c2ecf20Sopenharmony_ci }, 14638c2ecf20Sopenharmony_ci .driver_data = &(struct acpi_gpiolib_dmi_quirk) { 14648c2ecf20Sopenharmony_ci .ignore_wake = "INT33FC:02@28", 14658c2ecf20Sopenharmony_ci }, 14668c2ecf20Sopenharmony_ci }, 14678c2ecf20Sopenharmony_ci { 14688c2ecf20Sopenharmony_ci /* 14698c2ecf20Sopenharmony_ci * HP X2 10 models with Cherry Trail SoC + AXP288 PMIC use an 14708c2ecf20Sopenharmony_ci * external embedded-controller connected via I2C + an ACPI GPIO 14718c2ecf20Sopenharmony_ci * event handler on INT33FF:01 pin 0, causing spurious wakeups. 14728c2ecf20Sopenharmony_ci */ 14738c2ecf20Sopenharmony_ci .matches = { 14748c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "HP"), 14758c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion x2 Detachable"), 14768c2ecf20Sopenharmony_ci DMI_MATCH(DMI_BOARD_NAME, "813E"), 14778c2ecf20Sopenharmony_ci }, 14788c2ecf20Sopenharmony_ci .driver_data = &(struct acpi_gpiolib_dmi_quirk) { 14798c2ecf20Sopenharmony_ci .ignore_wake = "INT33FF:01@0", 14808c2ecf20Sopenharmony_ci }, 14818c2ecf20Sopenharmony_ci }, 14828c2ecf20Sopenharmony_ci { 14838c2ecf20Sopenharmony_ci /* 14848c2ecf20Sopenharmony_ci * Spurious wakeups from TP_ATTN# pin 14858c2ecf20Sopenharmony_ci * Found in BIOS 0.35 14868c2ecf20Sopenharmony_ci * https://gitlab.freedesktop.org/drm/amd/-/issues/3073 14878c2ecf20Sopenharmony_ci */ 14888c2ecf20Sopenharmony_ci .matches = { 14898c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "GPD"), 14908c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "G1619-04"), 14918c2ecf20Sopenharmony_ci }, 14928c2ecf20Sopenharmony_ci .driver_data = &(struct acpi_gpiolib_dmi_quirk) { 14938c2ecf20Sopenharmony_ci .ignore_wake = "PNP0C50:00@8", 14948c2ecf20Sopenharmony_ci }, 14958c2ecf20Sopenharmony_ci }, 14968c2ecf20Sopenharmony_ci {} /* Terminating entry */ 14978c2ecf20Sopenharmony_ci}; 14988c2ecf20Sopenharmony_ci 14998c2ecf20Sopenharmony_cistatic int __init acpi_gpio_setup_params(void) 15008c2ecf20Sopenharmony_ci{ 15018c2ecf20Sopenharmony_ci const struct acpi_gpiolib_dmi_quirk *quirk = NULL; 15028c2ecf20Sopenharmony_ci const struct dmi_system_id *id; 15038c2ecf20Sopenharmony_ci 15048c2ecf20Sopenharmony_ci id = dmi_first_match(gpiolib_acpi_quirks); 15058c2ecf20Sopenharmony_ci if (id) 15068c2ecf20Sopenharmony_ci quirk = id->driver_data; 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_ci if (run_edge_events_on_boot < 0) { 15098c2ecf20Sopenharmony_ci if (quirk && quirk->no_edge_events_on_boot) 15108c2ecf20Sopenharmony_ci run_edge_events_on_boot = 0; 15118c2ecf20Sopenharmony_ci else 15128c2ecf20Sopenharmony_ci run_edge_events_on_boot = 1; 15138c2ecf20Sopenharmony_ci } 15148c2ecf20Sopenharmony_ci 15158c2ecf20Sopenharmony_ci if (ignore_wake == NULL && quirk && quirk->ignore_wake) 15168c2ecf20Sopenharmony_ci ignore_wake = quirk->ignore_wake; 15178c2ecf20Sopenharmony_ci 15188c2ecf20Sopenharmony_ci return 0; 15198c2ecf20Sopenharmony_ci} 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_ci/* Directly after dmi_setup() which runs as core_initcall() */ 15228c2ecf20Sopenharmony_cipostcore_initcall(acpi_gpio_setup_params); 1523