diff options
Diffstat (limited to 'drivers/acpi/button.c')
| -rw-r--r-- | drivers/acpi/button.c | 152 | 
1 files changed, 88 insertions, 64 deletions
diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c index 4a2cde2c536a..b758b45737f5 100644 --- a/drivers/acpi/button.c +++ b/drivers/acpi/button.c @@ -44,9 +44,19 @@  #define ACPI_BUTTON_DEVICE_NAME_LID	"Lid Switch"  #define ACPI_BUTTON_TYPE_LID		0x05 -#define ACPI_BUTTON_LID_INIT_IGNORE	0x00 -#define ACPI_BUTTON_LID_INIT_OPEN	0x01 -#define ACPI_BUTTON_LID_INIT_METHOD	0x02 +enum { +	ACPI_BUTTON_LID_INIT_IGNORE, +	ACPI_BUTTON_LID_INIT_OPEN, +	ACPI_BUTTON_LID_INIT_METHOD, +	ACPI_BUTTON_LID_INIT_DISABLED, +}; + +static const char * const lid_init_state_str[] = { +	[ACPI_BUTTON_LID_INIT_IGNORE]		= "ignore", +	[ACPI_BUTTON_LID_INIT_OPEN]		= "open", +	[ACPI_BUTTON_LID_INIT_METHOD]		= "method", +	[ACPI_BUTTON_LID_INIT_DISABLED]		= "disabled", +};  #define _COMPONENT		ACPI_BUTTON_COMPONENT  ACPI_MODULE_NAME("button"); @@ -65,18 +75,52 @@ static const struct acpi_device_id button_device_ids[] = {  };  MODULE_DEVICE_TABLE(acpi, button_device_ids); -/* - * Some devices which don't even have a lid in anyway have a broken _LID - * method (e.g. pointing to a floating gpio pin) causing spurious LID events. - */ -static const struct dmi_system_id lid_blacklst[] = { +/* Please keep this list sorted alphabetically by vendor and model */ +static const struct dmi_system_id dmi_lid_quirks[] = {  	{ -		/* GP-electronic T701 */ +		/* +		 * Acer Switch 10 SW5-012. _LID method messes with home and +		 * power button GPIO IRQ settings causing an interrupt storm on +		 * both GPIOs. This is unfixable without a DSDT override, so we +		 * have to disable the lid-switch functionality altogether :| +		 */ +		.matches = { +			DMI_MATCH(DMI_SYS_VENDOR, "Acer"), +			DMI_MATCH(DMI_PRODUCT_NAME, "Aspire SW5-012"), +		}, +		.driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_DISABLED, +	}, +	{ +		/* +		 * Asus T200TA, _LID keeps reporting closed after every second +		 * openening of the lid. Causing immediate re-suspend after +		 * opening every other open. Using LID_INIT_OPEN fixes this. +		 */ +		.matches = { +			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), +			DMI_MATCH(DMI_PRODUCT_NAME, "T200TA"), +		}, +		.driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_OPEN, +	}, +	{ +		/* GP-electronic T701, _LID method points to a floating GPIO */  		.matches = {  			DMI_MATCH(DMI_SYS_VENDOR, "Insyde"),  			DMI_MATCH(DMI_PRODUCT_NAME, "T701"),  			DMI_MATCH(DMI_BIOS_VERSION, "BYT70A.YNCHENG.WIN.007"),  		}, +		.driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_DISABLED, +	}, +	{ +		/* +		 * Medion Akoya E2215T, notification of the LID device only +		 * happens on close, not on open and _LID always returns closed. +		 */ +		.matches = { +			DMI_MATCH(DMI_SYS_VENDOR, "MEDION"), +			DMI_MATCH(DMI_PRODUCT_NAME, "E2215T MD60198"), +		}, +		.driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_OPEN,  	},  	{}  }; @@ -116,9 +160,8 @@ struct acpi_button {  	bool suspended;  }; -static BLOCKING_NOTIFIER_HEAD(acpi_lid_notifier);  static struct acpi_device *lid_device; -static u8 lid_init_state = ACPI_BUTTON_LID_INIT_METHOD; +static long lid_init_state = -1;  static unsigned long lid_report_interval __read_mostly = 500;  module_param(lid_report_interval, ulong, 0644); @@ -146,7 +189,6 @@ static int acpi_lid_evaluate_state(struct acpi_device *device)  static int acpi_lid_notify_state(struct acpi_device *device, int state)  {  	struct acpi_button *button = acpi_driver_data(device); -	int ret;  	ktime_t next_report;  	bool do_update; @@ -223,18 +265,7 @@ static int acpi_lid_notify_state(struct acpi_device *device, int state)  		button->last_time = ktime_get();  	} -	ret = blocking_notifier_call_chain(&acpi_lid_notifier, state, device); -	if (ret == NOTIFY_DONE) -		ret = blocking_notifier_call_chain(&acpi_lid_notifier, state, -						   device); -	if (ret == NOTIFY_DONE || ret == NOTIFY_OK) { -		/* -		 * It is also regarded as success if the notifier_chain -		 * returns NOTIFY_OK or NOTIFY_DONE. -		 */ -		ret = 0; -	} -	return ret; +	return 0;  }  static int __maybe_unused acpi_button_state_seq_show(struct seq_file *seq, @@ -331,18 +362,6 @@ static int acpi_button_remove_fs(struct acpi_device *device)  /* --------------------------------------------------------------------------                                  Driver Interface     -------------------------------------------------------------------------- */ -int acpi_lid_notifier_register(struct notifier_block *nb) -{ -	return blocking_notifier_chain_register(&acpi_lid_notifier, nb); -} -EXPORT_SYMBOL(acpi_lid_notifier_register); - -int acpi_lid_notifier_unregister(struct notifier_block *nb) -{ -	return blocking_notifier_chain_unregister(&acpi_lid_notifier, nb); -} -EXPORT_SYMBOL(acpi_lid_notifier_unregister); -  int acpi_lid_open(void)  {  	if (!lid_device) @@ -472,7 +491,8 @@ static int acpi_button_add(struct acpi_device *device)  	char *name, *class;  	int error; -	if (!strcmp(hid, ACPI_BUTTON_HID_LID) && dmi_check_system(lid_blacklst)) +	if (!strcmp(hid, ACPI_BUTTON_HID_LID) && +	     lid_init_state == ACPI_BUTTON_LID_INIT_DISABLED)  		return -ENODEV;  	button = kzalloc(sizeof(struct acpi_button), GFP_KERNEL); @@ -578,36 +598,30 @@ static int acpi_button_remove(struct acpi_device *device)  static int param_set_lid_init_state(const char *val,  				    const struct kernel_param *kp)  { -	int result = 0; - -	if (!strncmp(val, "open", sizeof("open") - 1)) { -		lid_init_state = ACPI_BUTTON_LID_INIT_OPEN; -		pr_info("Notify initial lid state as open\n"); -	} else if (!strncmp(val, "method", sizeof("method") - 1)) { -		lid_init_state = ACPI_BUTTON_LID_INIT_METHOD; -		pr_info("Notify initial lid state with _LID return value\n"); -	} else if (!strncmp(val, "ignore", sizeof("ignore") - 1)) { -		lid_init_state = ACPI_BUTTON_LID_INIT_IGNORE; -		pr_info("Do not notify initial lid state\n"); -	} else -		result = -EINVAL; -	return result; +	int i; + +	i = sysfs_match_string(lid_init_state_str, val); +	if (i < 0) +		return i; + +	lid_init_state = i; +	pr_info("Initial lid state set to '%s'\n", lid_init_state_str[i]); +	return 0;  } -static int param_get_lid_init_state(char *buffer, -				    const struct kernel_param *kp) +static int param_get_lid_init_state(char *buf, const struct kernel_param *kp)  { -	switch (lid_init_state) { -	case ACPI_BUTTON_LID_INIT_OPEN: -		return sprintf(buffer, "open"); -	case ACPI_BUTTON_LID_INIT_METHOD: -		return sprintf(buffer, "method"); -	case ACPI_BUTTON_LID_INIT_IGNORE: -		return sprintf(buffer, "ignore"); -	default: -		return sprintf(buffer, "invalid"); -	} -	return 0; +	int i, c = 0; + +	for (i = 0; i < ARRAY_SIZE(lid_init_state_str); i++) +		if (i == lid_init_state) +			c += sprintf(buf + c, "[%s] ", lid_init_state_str[i]); +		else +			c += sprintf(buf + c, "%s ", lid_init_state_str[i]); + +	buf[c - 1] = '\n'; /* Replace the final space with a newline */ + +	return c;  }  module_param_call(lid_init_state, @@ -617,6 +631,16 @@ MODULE_PARM_DESC(lid_init_state, "Behavior for reporting LID initial state");  static int acpi_button_register_driver(struct acpi_driver *driver)  { +	const struct dmi_system_id *dmi_id; + +	if (lid_init_state == -1) { +		dmi_id = dmi_first_match(dmi_lid_quirks); +		if (dmi_id) +			lid_init_state = (long)dmi_id->driver_data; +		else +			lid_init_state = ACPI_BUTTON_LID_INIT_METHOD; +	} +  	/*  	 * Modules such as nouveau.ko and i915.ko have a link time dependency  	 * on acpi_lid_open(), and would therefore not be loadable on ACPI  |