// 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 5 V
* 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" ,
},
},
{
/*
* Same as G1619 - 04 . New model .
*/
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "GPD" ),
DMI_MATCH(DMI_PRODUCT_NAME, "G1619-05" ),
},
.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" ,
},
},
{
/*
* Wakeup only works when keyboard backlight is turned off
* https : //gitlab.freedesktop.org/drm/amd/-/issues/4169
*/
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer" ),
DMI_MATCH(DMI_PRODUCT_FAMILY, "Acer Nitro V 15" ),
},
.driver_data = &(struct acpi_gpiolib_dmi_quirk) {
.ignore_interrupt = "AMDI0030:00@8" ,
},
},
{
/*
* Spurious wakeups from TP_ATTN # pin
* Found in BIOS 5 . 35
* https : //gitlab.freedesktop.org/drm/amd/-/issues/4482
*/
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC." ),
DMI_MATCH(DMI_PRODUCT_FAMILY, "ProArt PX13" ),
},
.driver_data = &(struct acpi_gpiolib_dmi_quirk) {
.ignore_wake = "ASCP1A00:00@8" ,
},
},
{} /* 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);
Messung V0.5 in Prozent C=89 H=99 G=94
¤ Dauer der Verarbeitung: 0.13 Sekunden
(vorverarbeitet am 2026-09-27)
¤
*© Formatika GbR, Deutschland