mirror of
git://git.yoctoproject.org/linux-yocto.git
synced 2025-10-22 23:13:01 +02:00
intel-gpio for v6.16-1
* Split GPIO ACPI quirks to its own file * Refactored GPIO ACPI library to shrink the code The following is an automated git shortlog grouped by driver: gpiolib: - acpi: Update file references in the Documentation and MAINTAINERS - acpi: Move quirks to a separate file - acpi: Add acpi_gpio_need_run_edge_events_on_boot() getter - acpi: Handle deferred list via new API - acpi: Make sure we fill struct acpi_gpio_info - acpi: Switch to use enum in acpi_gpio_in_ignore_list() - acpi: Use temporary variable for struct acpi_gpio_info - acpi: Deduplicate some code in __acpi_find_gpio() - acpi: Reuse struct acpi_gpio_params in struct acpi_gpio_lookup - acpi: Rename par to params for better readability - acpi: Reduce memory footprint for struct acpi_gpio_params - acpi: Remove index parameter from acpi_gpio_property_lookup() - acpi: Improve struct acpi_gpio_info memory footprint -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEqaflIX74DDDzMJJtb7wzTHR8rCgFAmgtn20ACgkQb7wzTHR8 rChZig//SRA4VGnOF6nZfP985YZ9YjwdPibnxmkWBvyOodpJl3I1iiex1XrbX1MI DxfQGDe8OhXWM6LL/569BCdSy5jHVpFGxISueIzz7gQ7WhzeWpC4/zO8wlW4InTy xI/GScxqp65Rs0rLy+7OL3OEMSGJZbDLLZuadQnEAVfxRP5jCPDhBQyQBRdrQJyR T+exFet5J6eMFvigpVcjCR3/Mlyp8Af/tgagtbL638/kOkfa2cgyMPTVO7Nmunrt lLyAZVXytHN7DyhrIxh+H90U38oObix8rNOEXC2wdZMFjan75wOsq/+FxcrJPhzR k2FsyVzM5ggcvWcR9gHXxwczSNu6g8wAzn20ibPfbdsVyNiKIyw+0mX8/JUN4mCS Nc3xLxPM7cuve09Z6aAm8M1tPpOqrb0ATgv2kjV+yid7SLIEBgeJLru8Js+vjC0V Cj+0jtlgUKiaNAo0g0Sp5JrARnJz95bNVTqVYqkcK5fqaFHR9OfI1Zd56Cgi1PBL N2xqjlYHwm5QuKaXsq/5/81AmBjlHLkTLSlGo2w3ZRjHyFLVINRBGypZuKYFvZJ1 tZAgvK4r3GoBC6iJ+aXPy0ggr/oRbL0lIBTa/Za+rDij+rDhRCn1XwLcZ63tEry5 eJaTazj7lt1v1TXTgJRqqD2W0frSc5Ir7xipG5jcAjPnONPYo0s= =WChc -----END PGP SIGNATURE----- Merge tag 'intel-gpio-v6.16-1' of git://git.kernel.org/pub/scm/linux/kernel/git/andy/linux-gpio-intel into gpio/for-next intel-gpio for v6.16-1 * Split GPIO ACPI quirks to its own file * Refactored GPIO ACPI library to shrink the code The following is an automated git shortlog grouped by driver: gpiolib: - acpi: Update file references in the Documentation and MAINTAINERS - acpi: Move quirks to a separate file - acpi: Add acpi_gpio_need_run_edge_events_on_boot() getter - acpi: Handle deferred list via new API - acpi: Make sure we fill struct acpi_gpio_info - acpi: Switch to use enum in acpi_gpio_in_ignore_list() - acpi: Use temporary variable for struct acpi_gpio_info - acpi: Deduplicate some code in __acpi_find_gpio() - acpi: Reuse struct acpi_gpio_params in struct acpi_gpio_lookup - acpi: Rename par to params for better readability - acpi: Reduce memory footprint for struct acpi_gpio_params - acpi: Remove index parameter from acpi_gpio_property_lookup() - acpi: Improve struct acpi_gpio_info memory footprint
This commit is contained in:
commit
0f45b538ba
|
@ -27,7 +27,7 @@ Core
|
|||
ACPI support
|
||||
============
|
||||
|
||||
.. kernel-doc:: drivers/gpio/gpiolib-acpi.c
|
||||
.. kernel-doc:: drivers/gpio/gpiolib-acpi-core.c
|
||||
:export:
|
||||
|
||||
Device tree support
|
||||
|
|
|
@ -42,7 +42,7 @@ ACPI支持
|
|||
|
||||
该API在以下内核代码中:
|
||||
|
||||
drivers/gpio/gpiolib-acpi.c
|
||||
drivers/gpio/gpiolib-acpi-core.c
|
||||
|
||||
设备树支持
|
||||
==========
|
||||
|
|
|
@ -10115,7 +10115,7 @@ L: linux-acpi@vger.kernel.org
|
|||
S: Supported
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/andy/linux-gpio-intel.git
|
||||
F: Documentation/firmware-guide/acpi/gpio-properties.rst
|
||||
F: drivers/gpio/gpiolib-acpi.c
|
||||
F: drivers/gpio/gpiolib-acpi-*.c
|
||||
F: drivers/gpio/gpiolib-acpi.h
|
||||
|
||||
GPIO AGGREGATOR
|
||||
|
|
|
@ -10,6 +10,7 @@ obj-$(CONFIG_OF_GPIO) += gpiolib-of.o
|
|||
obj-$(CONFIG_GPIO_CDEV) += gpiolib-cdev.o
|
||||
obj-$(CONFIG_GPIO_SYSFS) += gpiolib-sysfs.o
|
||||
obj-$(CONFIG_GPIO_ACPI) += gpiolib-acpi.o
|
||||
gpiolib-acpi-y := gpiolib-acpi-core.o gpiolib-acpi-quirks.o
|
||||
obj-$(CONFIG_GPIOLIB) += gpiolib-swnode.o
|
||||
|
||||
# Device drivers. Generally keep list sorted alphabetically
|
||||
|
|
|
@ -23,29 +23,6 @@
|
|||
#include "gpiolib.h"
|
||||
#include "gpiolib-acpi.h"
|
||||
|
||||
static int run_edge_events_on_boot = -1;
|
||||
module_param(run_edge_events_on_boot, int, 0444);
|
||||
MODULE_PARM_DESC(run_edge_events_on_boot,
|
||||
"Run edge _AEI event-handlers at boot: 0=no, 1=yes, -1=auto");
|
||||
|
||||
static char *ignore_wake;
|
||||
module_param(ignore_wake, charp, 0444);
|
||||
MODULE_PARM_DESC(ignore_wake,
|
||||
"controller@pin combos on which to ignore the ACPI wake flag "
|
||||
"ignore_wake=controller@pin[,controller@pin[,...]]");
|
||||
|
||||
static char *ignore_interrupt;
|
||||
module_param(ignore_interrupt, charp, 0444);
|
||||
MODULE_PARM_DESC(ignore_interrupt,
|
||||
"controller@pin combos on which to ignore interrupt "
|
||||
"ignore_interrupt=controller@pin[,controller@pin[,...]]");
|
||||
|
||||
struct acpi_gpiolib_dmi_quirk {
|
||||
bool no_edge_events_on_boot;
|
||||
char *ignore_wake;
|
||||
char *ignore_interrupt;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct acpi_gpio_event - ACPI GPIO event handler data
|
||||
*
|
||||
|
@ -96,10 +73,10 @@ struct acpi_gpio_chip {
|
|||
* @adev: reference to ACPI device which consumes GPIO resource
|
||||
* @flags: GPIO initialization flags
|
||||
* @gpioint: if %true this GPIO is of type GpioInt otherwise type is GpioIo
|
||||
* @wake_capable: wake capability as provided by ACPI
|
||||
* @pin_config: pin bias as provided by ACPI
|
||||
* @polarity: interrupt polarity as provided by ACPI
|
||||
* @triggering: triggering type as provided by ACPI
|
||||
* @wake_capable: wake capability as provided by ACPI
|
||||
* @debounce: debounce timeout as provided by ACPI
|
||||
* @quirks: Linux specific quirks as provided by struct acpi_gpio_mapping
|
||||
*/
|
||||
|
@ -107,25 +84,14 @@ struct acpi_gpio_info {
|
|||
struct acpi_device *adev;
|
||||
enum gpiod_flags flags;
|
||||
bool gpioint;
|
||||
bool wake_capable;
|
||||
int pin_config;
|
||||
int polarity;
|
||||
int triggering;
|
||||
bool wake_capable;
|
||||
unsigned int debounce;
|
||||
unsigned int quirks;
|
||||
};
|
||||
|
||||
/*
|
||||
* For GPIO chips which call acpi_gpiochip_request_interrupts() before late_init
|
||||
* (so builtin drivers) we register the ACPI GpioInt IRQ handlers from a
|
||||
* late_initcall_sync() handler, so that other builtin drivers can register their
|
||||
* OpRegions before the event handlers can run. This list contains GPIO chips
|
||||
* for which the acpi_gpiochip_request_irqs() call has been deferred.
|
||||
*/
|
||||
static DEFINE_MUTEX(acpi_gpio_deferred_req_irqs_lock);
|
||||
static LIST_HEAD(acpi_gpio_deferred_req_irqs_list);
|
||||
static bool acpi_gpio_deferred_req_irqs_done;
|
||||
|
||||
static int acpi_gpiochip_find(struct gpio_chip *gc, const void *data)
|
||||
{
|
||||
/* First check the actual GPIO device */
|
||||
|
@ -268,7 +234,7 @@ static void acpi_gpiochip_request_irq(struct acpi_gpio_chip *acpi_gpio,
|
|||
event->irq_requested = true;
|
||||
|
||||
/* Make sure we trigger the initial state of edge-triggered IRQs */
|
||||
if (run_edge_events_on_boot &&
|
||||
if (acpi_gpio_need_run_edge_events_on_boot() &&
|
||||
(event->irqflags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING))) {
|
||||
value = gpiod_get_raw_value_cansleep(event->desc);
|
||||
if (((event->irqflags & IRQF_TRIGGER_RISING) && value == 1) ||
|
||||
|
@ -350,42 +316,6 @@ static struct gpio_desc *acpi_request_own_gpiod(struct gpio_chip *chip,
|
|||
return desc;
|
||||
}
|
||||
|
||||
static bool acpi_gpio_in_ignore_list(const char *ignore_list, const char *controller_in,
|
||||
unsigned int pin_in)
|
||||
{
|
||||
const char *controller, *pin_str;
|
||||
unsigned int pin;
|
||||
char *endp;
|
||||
int len;
|
||||
|
||||
controller = ignore_list;
|
||||
while (controller) {
|
||||
pin_str = strchr(controller, '@');
|
||||
if (!pin_str)
|
||||
goto err;
|
||||
|
||||
len = pin_str - controller;
|
||||
if (len == strlen(controller_in) &&
|
||||
strncmp(controller, controller_in, len) == 0) {
|
||||
pin = simple_strtoul(pin_str + 1, &endp, 10);
|
||||
if (*endp != 0 && *endp != ',')
|
||||
goto err;
|
||||
|
||||
if (pin == pin_in)
|
||||
return true;
|
||||
}
|
||||
|
||||
controller = strchr(controller, ',');
|
||||
if (controller)
|
||||
controller++;
|
||||
}
|
||||
|
||||
return false;
|
||||
err:
|
||||
pr_err_once("Error: Invalid value for gpiolib_acpi.ignore_...: %s\n", ignore_list);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool acpi_gpio_irq_is_wake(struct device *parent,
|
||||
const struct acpi_resource_gpio *agpio)
|
||||
{
|
||||
|
@ -394,7 +324,7 @@ static bool acpi_gpio_irq_is_wake(struct device *parent,
|
|||
if (agpio->wake_capable != ACPI_WAKE_CAPABLE)
|
||||
return false;
|
||||
|
||||
if (acpi_gpio_in_ignore_list(ignore_wake, dev_name(parent), pin)) {
|
||||
if (acpi_gpio_in_ignore_list(ACPI_GPIO_IGNORE_WAKE, dev_name(parent), pin)) {
|
||||
dev_info(parent, "Ignoring wakeup on pin %u\n", pin);
|
||||
return false;
|
||||
}
|
||||
|
@ -437,7 +367,7 @@ static acpi_status acpi_gpiochip_alloc_event(struct acpi_resource *ares,
|
|||
if (!handler)
|
||||
return AE_OK;
|
||||
|
||||
if (acpi_gpio_in_ignore_list(ignore_interrupt, dev_name(chip->parent), pin)) {
|
||||
if (acpi_gpio_in_ignore_list(ACPI_GPIO_IGNORE_INTERRUPT, dev_name(chip->parent), pin)) {
|
||||
dev_info(chip->parent, "Ignoring interrupt on pin %u\n", pin);
|
||||
return AE_OK;
|
||||
}
|
||||
|
@ -525,7 +455,6 @@ void acpi_gpiochip_request_interrupts(struct gpio_chip *chip)
|
|||
struct acpi_gpio_chip *acpi_gpio;
|
||||
acpi_handle handle;
|
||||
acpi_status status;
|
||||
bool defer;
|
||||
|
||||
if (!chip->parent || !chip->to_irq)
|
||||
return;
|
||||
|
@ -544,14 +473,7 @@ void acpi_gpiochip_request_interrupts(struct gpio_chip *chip)
|
|||
acpi_walk_resources(handle, METHOD_NAME__AEI,
|
||||
acpi_gpiochip_alloc_event, acpi_gpio);
|
||||
|
||||
mutex_lock(&acpi_gpio_deferred_req_irqs_lock);
|
||||
defer = !acpi_gpio_deferred_req_irqs_done;
|
||||
if (defer)
|
||||
list_add(&acpi_gpio->deferred_req_irqs_list_entry,
|
||||
&acpi_gpio_deferred_req_irqs_list);
|
||||
mutex_unlock(&acpi_gpio_deferred_req_irqs_lock);
|
||||
|
||||
if (defer)
|
||||
if (acpi_gpio_add_to_deferred_list(&acpi_gpio->deferred_req_irqs_list_entry))
|
||||
return;
|
||||
|
||||
acpi_gpiochip_request_irqs(acpi_gpio);
|
||||
|
@ -583,10 +505,7 @@ void acpi_gpiochip_free_interrupts(struct gpio_chip *chip)
|
|||
if (ACPI_FAILURE(status))
|
||||
return;
|
||||
|
||||
mutex_lock(&acpi_gpio_deferred_req_irqs_lock);
|
||||
if (!list_empty(&acpi_gpio->deferred_req_irqs_list_entry))
|
||||
list_del_init(&acpi_gpio->deferred_req_irqs_list_entry);
|
||||
mutex_unlock(&acpi_gpio_deferred_req_irqs_lock);
|
||||
acpi_gpio_remove_from_deferred_list(&acpi_gpio->deferred_req_irqs_list_entry);
|
||||
|
||||
list_for_each_entry_safe_reverse(event, ep, &acpi_gpio->events, node) {
|
||||
if (event->irq_requested) {
|
||||
|
@ -604,6 +523,14 @@ void acpi_gpiochip_free_interrupts(struct gpio_chip *chip)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_gpiochip_free_interrupts);
|
||||
|
||||
void __init acpi_gpio_process_deferred_list(struct list_head *list)
|
||||
{
|
||||
struct acpi_gpio_chip *acpi_gpio, *tmp;
|
||||
|
||||
list_for_each_entry_safe(acpi_gpio, tmp, list, deferred_req_irqs_list_entry)
|
||||
acpi_gpiochip_request_irqs(acpi_gpio);
|
||||
}
|
||||
|
||||
int acpi_dev_add_driver_gpios(struct acpi_device *adev,
|
||||
const struct acpi_gpio_mapping *gpios)
|
||||
{
|
||||
|
@ -653,12 +580,12 @@ static bool acpi_get_driver_gpio_data(struct acpi_device *adev,
|
|||
|
||||
for (gm = adev->driver_gpios; gm->name; gm++)
|
||||
if (!strcmp(name, gm->name) && gm->data && index < gm->size) {
|
||||
const struct acpi_gpio_params *par = gm->data + index;
|
||||
const struct acpi_gpio_params *params = gm->data + index;
|
||||
|
||||
args->fwnode = acpi_fwnode_handle(adev);
|
||||
args->args[0] = par->crs_entry_index;
|
||||
args->args[1] = par->line_index;
|
||||
args->args[2] = par->active_low;
|
||||
args->args[0] = params->crs_entry_index;
|
||||
args->args[1] = params->line_index;
|
||||
args->args[2] = params->active_low;
|
||||
args->nargs = 3;
|
||||
|
||||
*quirks = gm->quirks;
|
||||
|
@ -743,10 +670,8 @@ static int acpi_gpio_update_gpiod_lookup_flags(unsigned long *lookupflags,
|
|||
}
|
||||
|
||||
struct acpi_gpio_lookup {
|
||||
struct acpi_gpio_info info;
|
||||
int index;
|
||||
u16 pin_index;
|
||||
bool active_low;
|
||||
struct acpi_gpio_params params;
|
||||
struct acpi_gpio_info *info;
|
||||
struct gpio_desc *desc;
|
||||
int n;
|
||||
};
|
||||
|
@ -754,6 +679,8 @@ struct acpi_gpio_lookup {
|
|||
static int acpi_populate_gpio_lookup(struct acpi_resource *ares, void *data)
|
||||
{
|
||||
struct acpi_gpio_lookup *lookup = data;
|
||||
struct acpi_gpio_params *params = &lookup->params;
|
||||
struct acpi_gpio_info *info = lookup->info;
|
||||
|
||||
if (ares->type != ACPI_RESOURCE_TYPE_GPIO)
|
||||
return 1;
|
||||
|
@ -764,26 +691,26 @@ static int acpi_populate_gpio_lookup(struct acpi_resource *ares, void *data)
|
|||
struct gpio_desc *desc;
|
||||
u16 pin_index;
|
||||
|
||||
if (lookup->info.quirks & ACPI_GPIO_QUIRK_ONLY_GPIOIO && gpioint)
|
||||
lookup->index++;
|
||||
if (info->quirks & ACPI_GPIO_QUIRK_ONLY_GPIOIO && gpioint)
|
||||
params->crs_entry_index++;
|
||||
|
||||
if (lookup->n++ != lookup->index)
|
||||
if (lookup->n++ != params->crs_entry_index)
|
||||
return 1;
|
||||
|
||||
pin_index = lookup->pin_index;
|
||||
pin_index = params->line_index;
|
||||
if (pin_index >= agpio->pin_table_length)
|
||||
return 1;
|
||||
|
||||
if (lookup->info.quirks & ACPI_GPIO_QUIRK_ABSOLUTE_NUMBER)
|
||||
if (info->quirks & ACPI_GPIO_QUIRK_ABSOLUTE_NUMBER)
|
||||
desc = gpio_to_desc(agpio->pin_table[pin_index]);
|
||||
else
|
||||
desc = acpi_get_gpiod(agpio->resource_source.string_ptr,
|
||||
agpio->pin_table[pin_index]);
|
||||
lookup->desc = desc;
|
||||
lookup->info.pin_config = agpio->pin_config;
|
||||
lookup->info.debounce = agpio->debounce_timeout;
|
||||
lookup->info.gpioint = gpioint;
|
||||
lookup->info.wake_capable = acpi_gpio_irq_is_wake(&lookup->info.adev->dev, agpio);
|
||||
info->pin_config = agpio->pin_config;
|
||||
info->debounce = agpio->debounce_timeout;
|
||||
info->gpioint = gpioint;
|
||||
info->wake_capable = acpi_gpio_irq_is_wake(&info->adev->dev, agpio);
|
||||
|
||||
/*
|
||||
* Polarity and triggering are only specified for GpioInt
|
||||
|
@ -792,23 +719,23 @@ static int acpi_populate_gpio_lookup(struct acpi_resource *ares, void *data)
|
|||
* - ACPI_ACTIVE_LOW == GPIO_ACTIVE_LOW
|
||||
* - ACPI_ACTIVE_HIGH == GPIO_ACTIVE_HIGH
|
||||
*/
|
||||
if (lookup->info.gpioint) {
|
||||
lookup->info.polarity = agpio->polarity;
|
||||
lookup->info.triggering = agpio->triggering;
|
||||
if (info->gpioint) {
|
||||
info->polarity = agpio->polarity;
|
||||
info->triggering = agpio->triggering;
|
||||
} else {
|
||||
lookup->info.polarity = lookup->active_low;
|
||||
info->polarity = params->active_low;
|
||||
}
|
||||
|
||||
lookup->info.flags = acpi_gpio_to_gpiod_flags(agpio, lookup->info.polarity);
|
||||
info->flags = acpi_gpio_to_gpiod_flags(agpio, info->polarity);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int acpi_gpio_resource_lookup(struct acpi_gpio_lookup *lookup,
|
||||
struct acpi_gpio_info *info)
|
||||
static int acpi_gpio_resource_lookup(struct acpi_gpio_lookup *lookup)
|
||||
{
|
||||
struct acpi_device *adev = lookup->info.adev;
|
||||
struct acpi_gpio_info *info = lookup->info;
|
||||
struct acpi_device *adev = info->adev;
|
||||
struct list_head res_list;
|
||||
int ret;
|
||||
|
||||
|
@ -825,22 +752,22 @@ static int acpi_gpio_resource_lookup(struct acpi_gpio_lookup *lookup,
|
|||
if (!lookup->desc)
|
||||
return -ENOENT;
|
||||
|
||||
if (info)
|
||||
*info = lookup->info;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acpi_gpio_property_lookup(struct fwnode_handle *fwnode,
|
||||
const char *propname, int index,
|
||||
static int acpi_gpio_property_lookup(struct fwnode_handle *fwnode, const char *propname,
|
||||
struct acpi_gpio_lookup *lookup)
|
||||
{
|
||||
struct fwnode_reference_args args;
|
||||
struct acpi_gpio_params *params = &lookup->params;
|
||||
struct acpi_gpio_info *info = lookup->info;
|
||||
unsigned int index = params->crs_entry_index;
|
||||
unsigned int quirks = 0;
|
||||
int ret;
|
||||
|
||||
memset(&args, 0, sizeof(args));
|
||||
ret = __acpi_node_get_property_reference(fwnode, propname, index, 3,
|
||||
&args);
|
||||
|
||||
ret = __acpi_node_get_property_reference(fwnode, propname, index, 3, &args);
|
||||
if (ret) {
|
||||
struct acpi_device *adev;
|
||||
|
||||
|
@ -857,12 +784,12 @@ static int acpi_gpio_property_lookup(struct fwnode_handle *fwnode,
|
|||
if (args.nargs != 3)
|
||||
return -EPROTO;
|
||||
|
||||
lookup->index = args.args[0];
|
||||
lookup->pin_index = args.args[1];
|
||||
lookup->active_low = !!args.args[2];
|
||||
params->crs_entry_index = args.args[0];
|
||||
params->line_index = args.args[1];
|
||||
params->active_low = !!args.args[2];
|
||||
|
||||
lookup->info.adev = to_acpi_device_node(args.fwnode);
|
||||
lookup->info.quirks = quirks;
|
||||
info->adev = to_acpi_device_node(args.fwnode);
|
||||
info->quirks = quirks;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -871,96 +798,83 @@ static int acpi_gpio_property_lookup(struct fwnode_handle *fwnode,
|
|||
* acpi_get_gpiod_by_index() - get a GPIO descriptor from device resources
|
||||
* @adev: pointer to a ACPI device to get GPIO from
|
||||
* @propname: Property name of the GPIO (optional)
|
||||
* @index: index of GpioIo/GpioInt resource (starting from %0)
|
||||
* @info: info pointer to fill in (optional)
|
||||
* @lookup: pointer to struct acpi_gpio_lookup to fill in
|
||||
*
|
||||
* Function goes through ACPI resources for @adev and based on @index looks
|
||||
* Function goes through ACPI resources for @adev and based on @lookup.index looks
|
||||
* up a GpioIo/GpioInt resource, translates it to the Linux GPIO descriptor,
|
||||
* and returns it. @index matches GpioIo/GpioInt resources only so if there
|
||||
* are total %3 GPIO resources, the index goes from %0 to %2.
|
||||
* and returns it. @lookup.index matches GpioIo/GpioInt resources only so if there
|
||||
* are total 3 GPIO resources, the index goes from 0 to 2.
|
||||
*
|
||||
* If @propname is specified the GPIO is looked using device property. In
|
||||
* that case @index is used to select the GPIO entry in the property value
|
||||
* (in case of multiple).
|
||||
*
|
||||
* Returns:
|
||||
* GPIO descriptor to use with Linux generic GPIO API.
|
||||
* If the GPIO cannot be translated or there is an error an ERR_PTR is
|
||||
* returned.
|
||||
* 0 on success, negative errno on failure.
|
||||
*
|
||||
* The @lookup is filled with GPIO descriptor to use with Linux generic GPIO API.
|
||||
* If the GPIO cannot be translated an error will be returned.
|
||||
*
|
||||
* Note: if the GPIO resource has multiple entries in the pin list, this
|
||||
* function only returns the first.
|
||||
*/
|
||||
static struct gpio_desc *acpi_get_gpiod_by_index(struct acpi_device *adev,
|
||||
const char *propname,
|
||||
int index,
|
||||
struct acpi_gpio_info *info)
|
||||
static int acpi_get_gpiod_by_index(struct acpi_device *adev, const char *propname,
|
||||
struct acpi_gpio_lookup *lookup)
|
||||
{
|
||||
struct acpi_gpio_lookup lookup;
|
||||
struct acpi_gpio_params *params = &lookup->params;
|
||||
struct acpi_gpio_info *info = lookup->info;
|
||||
int ret;
|
||||
|
||||
memset(&lookup, 0, sizeof(lookup));
|
||||
lookup.index = index;
|
||||
|
||||
if (propname) {
|
||||
dev_dbg(&adev->dev, "GPIO: looking up %s\n", propname);
|
||||
|
||||
ret = acpi_gpio_property_lookup(acpi_fwnode_handle(adev),
|
||||
propname, index, &lookup);
|
||||
ret = acpi_gpio_property_lookup(acpi_fwnode_handle(adev), propname, lookup);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
return ret;
|
||||
|
||||
dev_dbg(&adev->dev, "GPIO: _DSD returned %s %d %u %u\n",
|
||||
dev_name(&lookup.info.adev->dev), lookup.index,
|
||||
lookup.pin_index, lookup.active_low);
|
||||
dev_dbg(&adev->dev, "GPIO: _DSD returned %s %u %u %u\n",
|
||||
dev_name(&info->adev->dev),
|
||||
params->crs_entry_index, params->line_index, params->active_low);
|
||||
} else {
|
||||
dev_dbg(&adev->dev, "GPIO: looking up %d in _CRS\n", index);
|
||||
lookup.info.adev = adev;
|
||||
dev_dbg(&adev->dev, "GPIO: looking up %u in _CRS\n", params->crs_entry_index);
|
||||
info->adev = adev;
|
||||
}
|
||||
|
||||
ret = acpi_gpio_resource_lookup(&lookup, info);
|
||||
return ret ? ERR_PTR(ret) : lookup.desc;
|
||||
return acpi_gpio_resource_lookup(lookup);
|
||||
}
|
||||
|
||||
/**
|
||||
* acpi_get_gpiod_from_data() - get a GPIO descriptor from ACPI data node
|
||||
* @fwnode: pointer to an ACPI firmware node to get the GPIO information from
|
||||
* @propname: Property name of the GPIO
|
||||
* @index: index of GpioIo/GpioInt resource (starting from %0)
|
||||
* @info: info pointer to fill in (optional)
|
||||
* @lookup: pointer to struct acpi_gpio_lookup to fill in
|
||||
*
|
||||
* This function uses the property-based GPIO lookup to get to the GPIO
|
||||
* resource with the relevant information from a data-only ACPI firmware node
|
||||
* and uses that to obtain the GPIO descriptor to return.
|
||||
*
|
||||
* Returns:
|
||||
* GPIO descriptor to use with Linux generic GPIO API.
|
||||
* If the GPIO cannot be translated or there is an error an ERR_PTR is
|
||||
* returned.
|
||||
* 0 on success, negative errno on failure.
|
||||
*
|
||||
* The @lookup is filled with GPIO descriptor to use with Linux generic GPIO API.
|
||||
* If the GPIO cannot be translated an error will be returned.
|
||||
*/
|
||||
static struct gpio_desc *acpi_get_gpiod_from_data(struct fwnode_handle *fwnode,
|
||||
const char *propname,
|
||||
int index,
|
||||
struct acpi_gpio_info *info)
|
||||
static int acpi_get_gpiod_from_data(struct fwnode_handle *fwnode, const char *propname,
|
||||
struct acpi_gpio_lookup *lookup)
|
||||
{
|
||||
struct acpi_gpio_lookup lookup;
|
||||
int ret;
|
||||
|
||||
if (!is_acpi_data_node(fwnode))
|
||||
return ERR_PTR(-ENODEV);
|
||||
return -ENODEV;
|
||||
|
||||
if (!propname)
|
||||
return ERR_PTR(-EINVAL);
|
||||
return -EINVAL;
|
||||
|
||||
memset(&lookup, 0, sizeof(lookup));
|
||||
lookup.index = index;
|
||||
|
||||
ret = acpi_gpio_property_lookup(fwnode, propname, index, &lookup);
|
||||
ret = acpi_gpio_property_lookup(fwnode, propname, lookup);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
return ret;
|
||||
|
||||
ret = acpi_gpio_resource_lookup(&lookup, info);
|
||||
return ret ? ERR_PTR(ret) : lookup.desc;
|
||||
return acpi_gpio_resource_lookup(lookup);
|
||||
}
|
||||
|
||||
static bool acpi_can_fallback_to_crs(struct acpi_device *adev,
|
||||
|
@ -982,17 +896,25 @@ __acpi_find_gpio(struct fwnode_handle *fwnode, const char *con_id, unsigned int
|
|||
bool can_fallback, struct acpi_gpio_info *info)
|
||||
{
|
||||
struct acpi_device *adev = to_acpi_device_node(fwnode);
|
||||
struct acpi_gpio_lookup lookup;
|
||||
struct gpio_desc *desc;
|
||||
char propname[32];
|
||||
int ret;
|
||||
|
||||
memset(&lookup, 0, sizeof(lookup));
|
||||
lookup.params.crs_entry_index = idx;
|
||||
lookup.info = info;
|
||||
|
||||
/* Try first from _DSD */
|
||||
for_each_gpio_property_name(propname, con_id) {
|
||||
if (adev)
|
||||
desc = acpi_get_gpiod_by_index(adev,
|
||||
propname, idx, info);
|
||||
ret = acpi_get_gpiod_by_index(adev, propname, &lookup);
|
||||
else
|
||||
desc = acpi_get_gpiod_from_data(fwnode,
|
||||
propname, idx, info);
|
||||
ret = acpi_get_gpiod_from_data(fwnode, propname, &lookup);
|
||||
if (ret)
|
||||
continue;
|
||||
|
||||
desc = lookup.desc;
|
||||
if (PTR_ERR(desc) == -EPROBE_DEFER)
|
||||
return desc;
|
||||
|
||||
|
@ -1001,8 +923,13 @@ __acpi_find_gpio(struct fwnode_handle *fwnode, const char *con_id, unsigned int
|
|||
}
|
||||
|
||||
/* Then from plain _CRS GPIOs */
|
||||
if (can_fallback)
|
||||
return acpi_get_gpiod_by_index(adev, NULL, idx, info);
|
||||
if (can_fallback) {
|
||||
ret = acpi_get_gpiod_by_index(adev, NULL, &lookup);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
return lookup.desc;
|
||||
}
|
||||
|
||||
return ERR_PTR(-ENOENT);
|
||||
}
|
||||
|
@ -1488,248 +1415,3 @@ int acpi_gpio_count(const struct fwnode_handle *fwnode, const char *con_id)
|
|||
}
|
||||
return count ? count : -ENOENT;
|
||||
}
|
||||
|
||||
/* Run deferred acpi_gpiochip_request_irqs() */
|
||||
static int __init acpi_gpio_handle_deferred_request_irqs(void)
|
||||
{
|
||||
struct acpi_gpio_chip *acpi_gpio, *tmp;
|
||||
|
||||
mutex_lock(&acpi_gpio_deferred_req_irqs_lock);
|
||||
list_for_each_entry_safe(acpi_gpio, tmp,
|
||||
&acpi_gpio_deferred_req_irqs_list,
|
||||
deferred_req_irqs_list_entry)
|
||||
acpi_gpiochip_request_irqs(acpi_gpio);
|
||||
|
||||
acpi_gpio_deferred_req_irqs_done = true;
|
||||
mutex_unlock(&acpi_gpio_deferred_req_irqs_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
/* We must use _sync so that this runs after the first deferred_probe run */
|
||||
late_initcall_sync(acpi_gpio_handle_deferred_request_irqs);
|
||||
|
||||
static const struct dmi_system_id gpiolib_acpi_quirks[] __initconst = {
|
||||
{
|
||||
/*
|
||||
* The Minix Neo Z83-4 has a micro-USB-B id-pin handler for
|
||||
* a non existing micro-USB-B connector which puts the HDMI
|
||||
* DDC pins in GPIO mode, breaking HDMI support.
|
||||
*/
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "MINIX"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Z83-4"),
|
||||
},
|
||||
.driver_data = &(struct acpi_gpiolib_dmi_quirk) {
|
||||
.no_edge_events_on_boot = true,
|
||||
},
|
||||
},
|
||||
{
|
||||
/*
|
||||
* The Terra Pad 1061 has a micro-USB-B id-pin handler, which
|
||||
* instead of controlling the actual micro-USB-B turns the 5V
|
||||
* boost for its USB-A connector off. The actual micro-USB-B
|
||||
* connector is wired for charging only.
|
||||
*/
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Wortmann_AG"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "TERRA_PAD_1061"),
|
||||
},
|
||||
.driver_data = &(struct acpi_gpiolib_dmi_quirk) {
|
||||
.no_edge_events_on_boot = true,
|
||||
},
|
||||
},
|
||||
{
|
||||
/*
|
||||
* The Dell Venue 10 Pro 5055, with Bay Trail SoC + TI PMIC uses an
|
||||
* external embedded-controller connected via I2C + an ACPI GPIO
|
||||
* event handler on INT33FFC:02 pin 12, causing spurious wakeups.
|
||||
*/
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Venue 10 Pro 5055"),
|
||||
},
|
||||
.driver_data = &(struct acpi_gpiolib_dmi_quirk) {
|
||||
.ignore_wake = "INT33FC:02@12",
|
||||
},
|
||||
},
|
||||
{
|
||||
/*
|
||||
* HP X2 10 models with Cherry Trail SoC + TI PMIC use an
|
||||
* external embedded-controller connected via I2C + an ACPI GPIO
|
||||
* event handler on INT33FF:01 pin 0, causing spurious wakeups.
|
||||
* When suspending by closing the LID, the power to the USB
|
||||
* keyboard is turned off, causing INT0002 ACPI events to
|
||||
* trigger once the XHCI controller notices the keyboard is
|
||||
* gone. So INT0002 events cause spurious wakeups too. Ignoring
|
||||
* EC wakes breaks wakeup when opening the lid, the user needs
|
||||
* to press the power-button to wakeup the system. The
|
||||
* alternative is suspend simply not working, which is worse.
|
||||
*/
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "HP"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "HP x2 Detachable 10-p0XX"),
|
||||
},
|
||||
.driver_data = &(struct acpi_gpiolib_dmi_quirk) {
|
||||
.ignore_wake = "INT33FF:01@0,INT0002:00@2",
|
||||
},
|
||||
},
|
||||
{
|
||||
/*
|
||||
* HP X2 10 models with Bay Trail SoC + AXP288 PMIC use an
|
||||
* external embedded-controller connected via I2C + an ACPI GPIO
|
||||
* event handler on INT33FC:02 pin 28, causing spurious wakeups.
|
||||
*/
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion x2 Detachable"),
|
||||
DMI_MATCH(DMI_BOARD_NAME, "815D"),
|
||||
},
|
||||
.driver_data = &(struct acpi_gpiolib_dmi_quirk) {
|
||||
.ignore_wake = "INT33FC:02@28",
|
||||
},
|
||||
},
|
||||
{
|
||||
/*
|
||||
* HP X2 10 models with Cherry Trail SoC + AXP288 PMIC use an
|
||||
* external embedded-controller connected via I2C + an ACPI GPIO
|
||||
* event handler on INT33FF:01 pin 0, causing spurious wakeups.
|
||||
*/
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "HP"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion x2 Detachable"),
|
||||
DMI_MATCH(DMI_BOARD_NAME, "813E"),
|
||||
},
|
||||
.driver_data = &(struct acpi_gpiolib_dmi_quirk) {
|
||||
.ignore_wake = "INT33FF:01@0",
|
||||
},
|
||||
},
|
||||
{
|
||||
/*
|
||||
* Interrupt storm caused from edge triggered floating pin
|
||||
* Found in BIOS UX325UAZ.300
|
||||
* https://bugzilla.kernel.org/show_bug.cgi?id=216208
|
||||
*/
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "ZenBook UX325UAZ_UM325UAZ"),
|
||||
},
|
||||
.driver_data = &(struct acpi_gpiolib_dmi_quirk) {
|
||||
.ignore_interrupt = "AMDI0030:00@18",
|
||||
},
|
||||
},
|
||||
{
|
||||
/*
|
||||
* Spurious wakeups from TP_ATTN# pin
|
||||
* Found in BIOS 1.7.8
|
||||
* https://gitlab.freedesktop.org/drm/amd/-/issues/1722#note_1720627
|
||||
*/
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_NAME, "NL5xNU"),
|
||||
},
|
||||
.driver_data = &(struct acpi_gpiolib_dmi_quirk) {
|
||||
.ignore_wake = "ELAN0415:00@9",
|
||||
},
|
||||
},
|
||||
{
|
||||
/*
|
||||
* Spurious wakeups from TP_ATTN# pin
|
||||
* Found in BIOS 1.7.8
|
||||
* https://gitlab.freedesktop.org/drm/amd/-/issues/1722#note_1720627
|
||||
*/
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_NAME, "NL5xRU"),
|
||||
},
|
||||
.driver_data = &(struct acpi_gpiolib_dmi_quirk) {
|
||||
.ignore_wake = "ELAN0415:00@9",
|
||||
},
|
||||
},
|
||||
{
|
||||
/*
|
||||
* Spurious wakeups from TP_ATTN# pin
|
||||
* Found in BIOS 1.7.7
|
||||
*/
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_NAME, "NH5xAx"),
|
||||
},
|
||||
.driver_data = &(struct acpi_gpiolib_dmi_quirk) {
|
||||
.ignore_wake = "SYNA1202:00@16",
|
||||
},
|
||||
},
|
||||
{
|
||||
/*
|
||||
* On the Peaq C1010 2-in-1 INT33FC:00 pin 3 is connected to
|
||||
* a "dolby" button. At the ACPI level an _AEI event-handler
|
||||
* is connected which sets an ACPI variable to 1 on both
|
||||
* edges. This variable can be polled + cleared to 0 using
|
||||
* WMI. But since the variable is set on both edges the WMI
|
||||
* interface is pretty useless even when polling.
|
||||
* So instead the x86-android-tablets code instantiates
|
||||
* a gpio-keys platform device for it.
|
||||
* Ignore the _AEI handler for the pin, so that it is not busy.
|
||||
*/
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "PEAQ"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "PEAQ PMM C1010 MD99187"),
|
||||
},
|
||||
.driver_data = &(struct acpi_gpiolib_dmi_quirk) {
|
||||
.ignore_interrupt = "INT33FC:00@3",
|
||||
},
|
||||
},
|
||||
{
|
||||
/*
|
||||
* Spurious wakeups from TP_ATTN# pin
|
||||
* Found in BIOS 0.35
|
||||
* https://gitlab.freedesktop.org/drm/amd/-/issues/3073
|
||||
*/
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "GPD"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "G1619-04"),
|
||||
},
|
||||
.driver_data = &(struct acpi_gpiolib_dmi_quirk) {
|
||||
.ignore_wake = "PNP0C50:00@8",
|
||||
},
|
||||
},
|
||||
{
|
||||
/*
|
||||
* Spurious wakeups from GPIO 11
|
||||
* Found in BIOS 1.04
|
||||
* https://gitlab.freedesktop.org/drm/amd/-/issues/3954
|
||||
*/
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
|
||||
DMI_MATCH(DMI_PRODUCT_FAMILY, "Acer Nitro V 14"),
|
||||
},
|
||||
.driver_data = &(struct acpi_gpiolib_dmi_quirk) {
|
||||
.ignore_interrupt = "AMDI0030:00@11",
|
||||
},
|
||||
},
|
||||
{} /* Terminating entry */
|
||||
};
|
||||
|
||||
static int __init acpi_gpio_setup_params(void)
|
||||
{
|
||||
const struct acpi_gpiolib_dmi_quirk *quirk = NULL;
|
||||
const struct dmi_system_id *id;
|
||||
|
||||
id = dmi_first_match(gpiolib_acpi_quirks);
|
||||
if (id)
|
||||
quirk = id->driver_data;
|
||||
|
||||
if (run_edge_events_on_boot < 0) {
|
||||
if (quirk && quirk->no_edge_events_on_boot)
|
||||
run_edge_events_on_boot = 0;
|
||||
else
|
||||
run_edge_events_on_boot = 1;
|
||||
}
|
||||
|
||||
if (ignore_wake == NULL && quirk && quirk->ignore_wake)
|
||||
ignore_wake = quirk->ignore_wake;
|
||||
|
||||
if (ignore_interrupt == NULL && quirk && quirk->ignore_interrupt)
|
||||
ignore_interrupt = quirk->ignore_interrupt;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Directly after dmi_setup() which runs as core_initcall() */
|
||||
postcore_initcall(acpi_gpio_setup_params);
|
363
drivers/gpio/gpiolib-acpi-quirks.c
Normal file
363
drivers/gpio/gpiolib-acpi-quirks.c
Normal file
|
@ -0,0 +1,363 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* ACPI quirks for GPIO ACPI helpers
|
||||
*
|
||||
* Author: Hans de Goede <hdegoede@redhat.com>
|
||||
*/
|
||||
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/kstrtox.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "gpiolib-acpi.h"
|
||||
|
||||
static int run_edge_events_on_boot = -1;
|
||||
module_param(run_edge_events_on_boot, int, 0444);
|
||||
MODULE_PARM_DESC(run_edge_events_on_boot,
|
||||
"Run edge _AEI event-handlers at boot: 0=no, 1=yes, -1=auto");
|
||||
|
||||
static char *ignore_wake;
|
||||
module_param(ignore_wake, charp, 0444);
|
||||
MODULE_PARM_DESC(ignore_wake,
|
||||
"controller@pin combos on which to ignore the ACPI wake flag "
|
||||
"ignore_wake=controller@pin[,controller@pin[,...]]");
|
||||
|
||||
static char *ignore_interrupt;
|
||||
module_param(ignore_interrupt, charp, 0444);
|
||||
MODULE_PARM_DESC(ignore_interrupt,
|
||||
"controller@pin combos on which to ignore interrupt "
|
||||
"ignore_interrupt=controller@pin[,controller@pin[,...]]");
|
||||
|
||||
/*
|
||||
* For GPIO chips which call acpi_gpiochip_request_interrupts() before late_init
|
||||
* (so builtin drivers) we register the ACPI GpioInt IRQ handlers from a
|
||||
* late_initcall_sync() handler, so that other builtin drivers can register their
|
||||
* OpRegions before the event handlers can run. This list contains GPIO chips
|
||||
* for which the acpi_gpiochip_request_irqs() call has been deferred.
|
||||
*/
|
||||
static DEFINE_MUTEX(acpi_gpio_deferred_req_irqs_lock);
|
||||
static LIST_HEAD(acpi_gpio_deferred_req_irqs_list);
|
||||
static bool acpi_gpio_deferred_req_irqs_done;
|
||||
|
||||
bool acpi_gpio_add_to_deferred_list(struct list_head *list)
|
||||
{
|
||||
bool defer;
|
||||
|
||||
mutex_lock(&acpi_gpio_deferred_req_irqs_lock);
|
||||
defer = !acpi_gpio_deferred_req_irqs_done;
|
||||
if (defer)
|
||||
list_add(list, &acpi_gpio_deferred_req_irqs_list);
|
||||
mutex_unlock(&acpi_gpio_deferred_req_irqs_lock);
|
||||
|
||||
return defer;
|
||||
}
|
||||
|
||||
void acpi_gpio_remove_from_deferred_list(struct list_head *list)
|
||||
{
|
||||
mutex_lock(&acpi_gpio_deferred_req_irqs_lock);
|
||||
if (!list_empty(list))
|
||||
list_del_init(list);
|
||||
mutex_unlock(&acpi_gpio_deferred_req_irqs_lock);
|
||||
}
|
||||
|
||||
int acpi_gpio_need_run_edge_events_on_boot(void)
|
||||
{
|
||||
return run_edge_events_on_boot;
|
||||
}
|
||||
|
||||
bool acpi_gpio_in_ignore_list(enum acpi_gpio_ignore_list list,
|
||||
const char *controller_in, unsigned int pin_in)
|
||||
{
|
||||
const char *ignore_list, *controller, *pin_str;
|
||||
unsigned int pin;
|
||||
char *endp;
|
||||
int len;
|
||||
|
||||
switch (list) {
|
||||
case ACPI_GPIO_IGNORE_WAKE:
|
||||
ignore_list = ignore_wake;
|
||||
break;
|
||||
case ACPI_GPIO_IGNORE_INTERRUPT:
|
||||
ignore_list = ignore_interrupt;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
controller = ignore_list;
|
||||
while (controller) {
|
||||
pin_str = strchr(controller, '@');
|
||||
if (!pin_str)
|
||||
goto err;
|
||||
|
||||
len = pin_str - controller;
|
||||
if (len == strlen(controller_in) &&
|
||||
strncmp(controller, controller_in, len) == 0) {
|
||||
pin = simple_strtoul(pin_str + 1, &endp, 10);
|
||||
if (*endp != 0 && *endp != ',')
|
||||
goto err;
|
||||
|
||||
if (pin == pin_in)
|
||||
return true;
|
||||
}
|
||||
|
||||
controller = strchr(controller, ',');
|
||||
if (controller)
|
||||
controller++;
|
||||
}
|
||||
|
||||
return false;
|
||||
err:
|
||||
pr_err_once("Error: Invalid value for gpiolib_acpi.ignore_...: %s\n", ignore_list);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Run deferred acpi_gpiochip_request_irqs() */
|
||||
static int __init acpi_gpio_handle_deferred_request_irqs(void)
|
||||
{
|
||||
mutex_lock(&acpi_gpio_deferred_req_irqs_lock);
|
||||
acpi_gpio_process_deferred_list(&acpi_gpio_deferred_req_irqs_list);
|
||||
acpi_gpio_deferred_req_irqs_done = true;
|
||||
mutex_unlock(&acpi_gpio_deferred_req_irqs_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
/* We must use _sync so that this runs after the first deferred_probe run */
|
||||
late_initcall_sync(acpi_gpio_handle_deferred_request_irqs);
|
||||
|
||||
struct acpi_gpiolib_dmi_quirk {
|
||||
bool no_edge_events_on_boot;
|
||||
char *ignore_wake;
|
||||
char *ignore_interrupt;
|
||||
};
|
||||
|
||||
static const struct dmi_system_id gpiolib_acpi_quirks[] __initconst = {
|
||||
{
|
||||
/*
|
||||
* The Minix Neo Z83-4 has a micro-USB-B id-pin handler for
|
||||
* a non existing micro-USB-B connector which puts the HDMI
|
||||
* DDC pins in GPIO mode, breaking HDMI support.
|
||||
*/
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "MINIX"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Z83-4"),
|
||||
},
|
||||
.driver_data = &(struct acpi_gpiolib_dmi_quirk) {
|
||||
.no_edge_events_on_boot = true,
|
||||
},
|
||||
},
|
||||
{
|
||||
/*
|
||||
* The Terra Pad 1061 has a micro-USB-B id-pin handler, which
|
||||
* instead of controlling the actual micro-USB-B turns the 5V
|
||||
* boost for its USB-A connector off. The actual micro-USB-B
|
||||
* connector is wired for charging only.
|
||||
*/
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Wortmann_AG"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "TERRA_PAD_1061"),
|
||||
},
|
||||
.driver_data = &(struct acpi_gpiolib_dmi_quirk) {
|
||||
.no_edge_events_on_boot = true,
|
||||
},
|
||||
},
|
||||
{
|
||||
/*
|
||||
* The Dell Venue 10 Pro 5055, with Bay Trail SoC + TI PMIC uses an
|
||||
* external embedded-controller connected via I2C + an ACPI GPIO
|
||||
* event handler on INT33FFC:02 pin 12, causing spurious wakeups.
|
||||
*/
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Venue 10 Pro 5055"),
|
||||
},
|
||||
.driver_data = &(struct acpi_gpiolib_dmi_quirk) {
|
||||
.ignore_wake = "INT33FC:02@12",
|
||||
},
|
||||
},
|
||||
{
|
||||
/*
|
||||
* HP X2 10 models with Cherry Trail SoC + TI PMIC use an
|
||||
* external embedded-controller connected via I2C + an ACPI GPIO
|
||||
* event handler on INT33FF:01 pin 0, causing spurious wakeups.
|
||||
* When suspending by closing the LID, the power to the USB
|
||||
* keyboard is turned off, causing INT0002 ACPI events to
|
||||
* trigger once the XHCI controller notices the keyboard is
|
||||
* gone. So INT0002 events cause spurious wakeups too. Ignoring
|
||||
* EC wakes breaks wakeup when opening the lid, the user needs
|
||||
* to press the power-button to wakeup the system. The
|
||||
* alternative is suspend simply not working, which is worse.
|
||||
*/
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "HP"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "HP x2 Detachable 10-p0XX"),
|
||||
},
|
||||
.driver_data = &(struct acpi_gpiolib_dmi_quirk) {
|
||||
.ignore_wake = "INT33FF:01@0,INT0002:00@2",
|
||||
},
|
||||
},
|
||||
{
|
||||
/*
|
||||
* HP X2 10 models with Bay Trail SoC + AXP288 PMIC use an
|
||||
* external embedded-controller connected via I2C + an ACPI GPIO
|
||||
* event handler on INT33FC:02 pin 28, causing spurious wakeups.
|
||||
*/
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion x2 Detachable"),
|
||||
DMI_MATCH(DMI_BOARD_NAME, "815D"),
|
||||
},
|
||||
.driver_data = &(struct acpi_gpiolib_dmi_quirk) {
|
||||
.ignore_wake = "INT33FC:02@28",
|
||||
},
|
||||
},
|
||||
{
|
||||
/*
|
||||
* HP X2 10 models with Cherry Trail SoC + AXP288 PMIC use an
|
||||
* external embedded-controller connected via I2C + an ACPI GPIO
|
||||
* event handler on INT33FF:01 pin 0, causing spurious wakeups.
|
||||
*/
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "HP"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion x2 Detachable"),
|
||||
DMI_MATCH(DMI_BOARD_NAME, "813E"),
|
||||
},
|
||||
.driver_data = &(struct acpi_gpiolib_dmi_quirk) {
|
||||
.ignore_wake = "INT33FF:01@0",
|
||||
},
|
||||
},
|
||||
{
|
||||
/*
|
||||
* Interrupt storm caused from edge triggered floating pin
|
||||
* Found in BIOS UX325UAZ.300
|
||||
* https://bugzilla.kernel.org/show_bug.cgi?id=216208
|
||||
*/
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "ZenBook UX325UAZ_UM325UAZ"),
|
||||
},
|
||||
.driver_data = &(struct acpi_gpiolib_dmi_quirk) {
|
||||
.ignore_interrupt = "AMDI0030:00@18",
|
||||
},
|
||||
},
|
||||
{
|
||||
/*
|
||||
* Spurious wakeups from TP_ATTN# pin
|
||||
* Found in BIOS 1.7.8
|
||||
* https://gitlab.freedesktop.org/drm/amd/-/issues/1722#note_1720627
|
||||
*/
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_NAME, "NL5xNU"),
|
||||
},
|
||||
.driver_data = &(struct acpi_gpiolib_dmi_quirk) {
|
||||
.ignore_wake = "ELAN0415:00@9",
|
||||
},
|
||||
},
|
||||
{
|
||||
/*
|
||||
* Spurious wakeups from TP_ATTN# pin
|
||||
* Found in BIOS 1.7.8
|
||||
* https://gitlab.freedesktop.org/drm/amd/-/issues/1722#note_1720627
|
||||
*/
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_NAME, "NL5xRU"),
|
||||
},
|
||||
.driver_data = &(struct acpi_gpiolib_dmi_quirk) {
|
||||
.ignore_wake = "ELAN0415:00@9",
|
||||
},
|
||||
},
|
||||
{
|
||||
/*
|
||||
* Spurious wakeups from TP_ATTN# pin
|
||||
* Found in BIOS 1.7.7
|
||||
*/
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_NAME, "NH5xAx"),
|
||||
},
|
||||
.driver_data = &(struct acpi_gpiolib_dmi_quirk) {
|
||||
.ignore_wake = "SYNA1202:00@16",
|
||||
},
|
||||
},
|
||||
{
|
||||
/*
|
||||
* On the Peaq C1010 2-in-1 INT33FC:00 pin 3 is connected to
|
||||
* a "dolby" button. At the ACPI level an _AEI event-handler
|
||||
* is connected which sets an ACPI variable to 1 on both
|
||||
* edges. This variable can be polled + cleared to 0 using
|
||||
* WMI. But since the variable is set on both edges the WMI
|
||||
* interface is pretty useless even when polling.
|
||||
* So instead the x86-android-tablets code instantiates
|
||||
* a gpio-keys platform device for it.
|
||||
* Ignore the _AEI handler for the pin, so that it is not busy.
|
||||
*/
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "PEAQ"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "PEAQ PMM C1010 MD99187"),
|
||||
},
|
||||
.driver_data = &(struct acpi_gpiolib_dmi_quirk) {
|
||||
.ignore_interrupt = "INT33FC:00@3",
|
||||
},
|
||||
},
|
||||
{
|
||||
/*
|
||||
* Spurious wakeups from TP_ATTN# pin
|
||||
* Found in BIOS 0.35
|
||||
* https://gitlab.freedesktop.org/drm/amd/-/issues/3073
|
||||
*/
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "GPD"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "G1619-04"),
|
||||
},
|
||||
.driver_data = &(struct acpi_gpiolib_dmi_quirk) {
|
||||
.ignore_wake = "PNP0C50:00@8",
|
||||
},
|
||||
},
|
||||
{
|
||||
/*
|
||||
* Spurious wakeups from GPIO 11
|
||||
* Found in BIOS 1.04
|
||||
* https://gitlab.freedesktop.org/drm/amd/-/issues/3954
|
||||
*/
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
|
||||
DMI_MATCH(DMI_PRODUCT_FAMILY, "Acer Nitro V 14"),
|
||||
},
|
||||
.driver_data = &(struct acpi_gpiolib_dmi_quirk) {
|
||||
.ignore_interrupt = "AMDI0030:00@11",
|
||||
},
|
||||
},
|
||||
{} /* Terminating entry */
|
||||
};
|
||||
|
||||
static int __init acpi_gpio_setup_params(void)
|
||||
{
|
||||
const struct acpi_gpiolib_dmi_quirk *quirk = NULL;
|
||||
const struct dmi_system_id *id;
|
||||
|
||||
id = dmi_first_match(gpiolib_acpi_quirks);
|
||||
if (id)
|
||||
quirk = id->driver_data;
|
||||
|
||||
if (run_edge_events_on_boot < 0) {
|
||||
if (quirk && quirk->no_edge_events_on_boot)
|
||||
run_edge_events_on_boot = 0;
|
||||
else
|
||||
run_edge_events_on_boot = 1;
|
||||
}
|
||||
|
||||
if (ignore_wake == NULL && quirk && quirk->ignore_wake)
|
||||
ignore_wake = quirk->ignore_wake;
|
||||
|
||||
if (ignore_interrupt == NULL && quirk && quirk->ignore_interrupt)
|
||||
ignore_interrupt = quirk->ignore_interrupt;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Directly after dmi_setup() which runs as core_initcall() */
|
||||
postcore_initcall(acpi_gpio_setup_params);
|
|
@ -58,4 +58,19 @@ static inline int acpi_gpio_count(const struct fwnode_handle *fwnode,
|
|||
}
|
||||
#endif
|
||||
|
||||
void acpi_gpio_process_deferred_list(struct list_head *list);
|
||||
|
||||
bool acpi_gpio_add_to_deferred_list(struct list_head *list);
|
||||
void acpi_gpio_remove_from_deferred_list(struct list_head *list);
|
||||
|
||||
int acpi_gpio_need_run_edge_events_on_boot(void);
|
||||
|
||||
enum acpi_gpio_ignore_list {
|
||||
ACPI_GPIO_IGNORE_WAKE,
|
||||
ACPI_GPIO_IGNORE_INTERRUPT,
|
||||
};
|
||||
|
||||
bool acpi_gpio_in_ignore_list(enum acpi_gpio_ignore_list list,
|
||||
const char *controller_in, unsigned int pin_in);
|
||||
|
||||
#endif /* GPIOLIB_ACPI_H */
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
* ACPI mechanisms, this is not a real GPIO at all.
|
||||
*
|
||||
* This driver will bind to the INT0002 device, and register as a GPIO
|
||||
* controller, letting gpiolib-acpi.c call the _L02 handler as it would
|
||||
* controller, letting gpiolib-acpi call the _L02 handler as it would
|
||||
* for a real GPIO controller.
|
||||
*/
|
||||
|
||||
|
|
|
@ -596,7 +596,7 @@ struct gpio_desc *devm_fwnode_gpiod_get(struct device *dev,
|
|||
|
||||
struct acpi_gpio_params {
|
||||
unsigned int crs_entry_index;
|
||||
unsigned int line_index;
|
||||
unsigned short line_index;
|
||||
bool active_low;
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user