Commit 1dfb642b authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull GPIO fixes from Linus Walleij:

 - One core quirk by myself to fix the .irq_disable() semantics when the
   gpiolib core takes over this callback.

 - The rest is an elaborate series of four patches fixing Intel laptop
   ACPI wakeup quirks.

* tag 'gpio-v5.6-3' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio:
  gpiolib: acpi: Add quirk to ignore EC wakeups on HP x2 10 CHT + AXP288 model
  gpiolib: acpi: Add quirk to ignore EC wakeups on HP x2 10 BYT + AXP288 model
  gpiolib: acpi: Rework honor_wakeup option into an ignore_wake option
  gpiolib: acpi: Correct comment for HP x2 10 honor_wakeup quirk
  gpiolib: Fix irq_disable() semantics
parents e2cf67f6 0c625ccf
Loading
Loading
Loading
Loading
+114 −26
Original line number Diff line number Diff line
@@ -21,18 +21,21 @@
#include "gpiolib.h"
#include "gpiolib-acpi.h"

#define QUIRK_NO_EDGE_EVENTS_ON_BOOT		0x01l
#define QUIRK_NO_WAKEUP				0x02l

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 int honor_wakeup = -1;
module_param(honor_wakeup, int, 0444);
MODULE_PARM_DESC(honor_wakeup,
		 "Honor the ACPI wake-capable flag: 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[,...]]");

struct acpi_gpiolib_dmi_quirk {
	bool no_edge_events_on_boot;
	char *ignore_wake;
};

/**
 * struct acpi_gpio_event - ACPI GPIO event handler data
@@ -202,6 +205,57 @@ static void acpi_gpiochip_request_irqs(struct acpi_gpio_chip *acpi_gpio)
		acpi_gpiochip_request_irq(acpi_gpio, event);
}

static bool acpi_gpio_in_ignore_list(const char *controller_in, int pin_in)
{
	const char *controller, *pin_str;
	int len, pin;
	char *endp;

	controller = ignore_wake;
	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_wake: %s\n",
		    ignore_wake);
	return false;
}

static bool acpi_gpio_irq_is_wake(struct device *parent,
				  struct acpi_resource_gpio *agpio)
{
	int pin = agpio->pin_table[0];

	if (agpio->wake_capable != ACPI_WAKE_CAPABLE)
		return false;

	if (acpi_gpio_in_ignore_list(dev_name(parent), pin)) {
		dev_info(parent, "Ignoring wakeup on pin %d\n", pin);
		return false;
	}

	return true;
}

/* Always returns AE_OK so that we keep looping over the resources */
static acpi_status acpi_gpiochip_alloc_event(struct acpi_resource *ares,
					     void *context)
@@ -289,7 +343,7 @@ static acpi_status acpi_gpiochip_alloc_event(struct acpi_resource *ares,
	event->handle = evt_handle;
	event->handler = handler;
	event->irq = irq;
	event->irq_is_wake = honor_wakeup && agpio->wake_capable == ACPI_WAKE_CAPABLE;
	event->irq_is_wake = acpi_gpio_irq_is_wake(chip->parent, agpio);
	event->pin = pin;
	event->desc = desc;

@@ -1328,7 +1382,9 @@ static const struct dmi_system_id gpiolib_acpi_quirks[] = {
			DMI_MATCH(DMI_SYS_VENDOR, "MINIX"),
			DMI_MATCH(DMI_PRODUCT_NAME, "Z83-4"),
		},
		.driver_data = (void *)QUIRK_NO_EDGE_EVENTS_ON_BOOT,
		.driver_data = &(struct acpi_gpiolib_dmi_quirk) {
			.no_edge_events_on_boot = true,
		},
	},
	{
		/*
@@ -1341,16 +1397,20 @@ static const struct dmi_system_id gpiolib_acpi_quirks[] = {
			DMI_MATCH(DMI_SYS_VENDOR, "Wortmann_AG"),
			DMI_MATCH(DMI_PRODUCT_NAME, "TERRA_PAD_1061"),
		},
		.driver_data = (void *)QUIRK_NO_EDGE_EVENTS_ON_BOOT,
		.driver_data = &(struct acpi_gpiolib_dmi_quirk) {
			.no_edge_events_on_boot = true,
		},
	},
	{
		/*
		 * Various HP X2 10 Cherry Trail models use an external
		 * embedded-controller connected via I2C + an ACPI GPIO
		 * event handler. The embedded controller generates various
		 * spurious wakeup events when suspended. So disable wakeup
		 * for its handler (it uses the only ACPI GPIO event handler).
		 * This breaks wakeup when opening the lid, the user needs
		 * 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.
		 */
@@ -1358,33 +1418,61 @@ static const struct dmi_system_id gpiolib_acpi_quirks[] = {
			DMI_MATCH(DMI_SYS_VENDOR, "HP"),
			DMI_MATCH(DMI_PRODUCT_NAME, "HP x2 Detachable 10-p0XX"),
		},
		.driver_data = (void *)QUIRK_NO_WAKEUP,
		.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",
		},
	},
	{} /* Terminating entry */
};

static int acpi_gpio_setup_params(void)
{
	const struct acpi_gpiolib_dmi_quirk *quirk = NULL;
	const struct dmi_system_id *id;
	long quirks = 0;

	id = dmi_first_match(gpiolib_acpi_quirks);
	if (id)
		quirks = (long)id->driver_data;
		quirk = id->driver_data;

	if (run_edge_events_on_boot < 0) {
		if (quirks & QUIRK_NO_EDGE_EVENTS_ON_BOOT)
		if (quirk && quirk->no_edge_events_on_boot)
			run_edge_events_on_boot = 0;
		else
			run_edge_events_on_boot = 1;
	}

	if (honor_wakeup < 0) {
		if (quirks & QUIRK_NO_WAKEUP)
			honor_wakeup = 0;
		else
			honor_wakeup = 1;
	}
	if (ignore_wake == NULL && quirk && quirk->ignore_wake)
		ignore_wake = quirk->ignore_wake;

	return 0;
}
+8 −1
Original line number Diff line number Diff line
@@ -2306,9 +2306,16 @@ static void gpiochip_irq_disable(struct irq_data *d)
{
	struct gpio_chip *chip = irq_data_get_irq_chip_data(d);

	/*
	 * Since we override .irq_disable() we need to mimic the
	 * behaviour of __irq_disable() in irq/chip.c.
	 * First call .irq_disable() if it exists, else mimic the
	 * behaviour of mask_irq() which calls .irq_mask() if
	 * it exists.
	 */
	if (chip->irq.irq_disable)
		chip->irq.irq_disable(d);
	else
	else if (chip->irq.chip->irq_mask)
		chip->irq.chip->irq_mask(d);
	gpiochip_disable_irq(chip, d->hwirq);
}