#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[,...]]");
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;
}
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;
}
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 = {
{
.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,
},
},
{
.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,
},
},
{
.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",
},
},
{
.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",
},
},
{
.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",
},
},
{
.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",
},
},
{
.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",
},
},
{
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "NL5xNU"),
},
.driver_data = &(struct acpi_gpiolib_dmi_quirk) {
.ignore_wake = "ELAN0415:00@9",
},
},
{
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "NL5xRU"),
},
.driver_data = &(struct acpi_gpiolib_dmi_quirk) {
.ignore_wake = "ELAN0415:00@9",
},
},
{
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "NH5xAx"),
},
.driver_data = &(struct acpi_gpiolib_dmi_quirk) {
.ignore_wake = "SYNA1202:00@16",
},
},
{
.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",
},
},
{
.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",
},
},
{
.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",
},
},
{
.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",
},
},
{
.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",
},
},
{
.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",
},
},
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_FAMILY, "Precision"),
DMI_MATCH(DMI_PRODUCT_NAME, "Precision 7780"),
DMI_MATCH(DMI_BOARD_NAME, "0C6JVW"),
},
.driver_data = &(struct acpi_gpiolib_dmi_quirk) {
.ignore_wake = "VEN_0488:00@355",
},
},
{}
};
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;
}
postcore_initcall(acpi_gpio_setup_params);