diff options
Diffstat (limited to 'drivers/platform/x86')
32 files changed, 1721 insertions, 755 deletions
| diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 8489020ecf44..b04860703740 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -195,16 +195,6 @@ config FUJITSU_LAPTOP  	  If you have a Fujitsu laptop, say Y or M here. -config FUJITSU_LAPTOP_DEBUG -	bool "Verbose debug mode for Fujitsu Laptop Extras" -	depends on FUJITSU_LAPTOP -	default n -	---help--- -	  Enables extra debug output from the fujitsu extras driver, at the -	  expense of a slight increase in driver size. - -	  If you are not sure, say N here. -  config FUJITSU_TABLET         tristate "Fujitsu Tablet Extras"         depends on ACPI @@ -656,6 +646,18 @@ config ACPI_WMI  	  It is safe to enable this driver even if your DSDT doesn't define  	  any ACPI-WMI devices. +config WMI_BMOF +	tristate "WMI embedded Binary MOF driver" +	depends on ACPI_WMI +	default ACPI_WMI +	---help--- +	  Say Y here if you want to be able to read a firmware-embedded +	  WMI Binary MOF data. Using this requires userspace tools and may be +	  rather tedious. + +	  To compile this driver as a module, choose M here: the module will +	  be called wmi-bmof. +  config MSI_WMI  	tristate "MSI WMI extras"  	depends on ACPI_WMI @@ -669,6 +671,13 @@ config MSI_WMI  	 To compile this driver as a module, choose M here: the module will  	 be called msi-wmi. +config PEAQ_WMI +	tristate "PEAQ 2-in-1 WMI hotkey driver" +	depends on ACPI_WMI +	depends on INPUT +	help +	 Say Y here if you want to support WMI-based hotkeys on PEAQ 2-in-1s. +  config TOPSTAR_LAPTOP  	tristate "Topstar Laptop Extras"  	depends on ACPI @@ -794,6 +803,25 @@ config INTEL_CHT_INT33FE  	  This driver instantiates i2c-clients for these, so that standard  	  i2c drivers for these chips can bind to the them. +config INTEL_INT0002_VGPIO +	tristate "Intel ACPI INT0002 Virtual GPIO driver" +	depends on GPIOLIB && ACPI +	select GPIOLIB_IRQCHIP +	---help--- +	  Some peripherals on Bay Trail and Cherry Trail platforms signal a +	  Power Management Event (PME) to the Power Management Controller (PMC) +	  to wakeup the system. When this happens software needs to explicitly +	  clear the PME bus 0 status bit in the GPE0a_STS register to avoid an +	  IRQ storm on IRQ 9. + +	  This is modelled in ACPI through the INT0002 ACPI device, which is +	  called a "Virtual GPIO controller" in ACPI because it defines the +	  event handler to call when the PME triggers through _AEI and _L02 +	  methods as would be done for a real GPIO interrupt in ACPI. + +	  To compile this driver as a module, choose M here: the module will +	  be called intel_int0002_vgpio. +  config INTEL_HID_EVENT  	tristate "INTEL HID Event"  	depends on ACPI diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 182a3ed6605a..91cec1751461 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -35,8 +35,10 @@ obj-$(CONFIG_PANASONIC_LAPTOP)	+= panasonic-laptop.o  obj-$(CONFIG_INTEL_MENLOW)	+= intel_menlow.o  obj-$(CONFIG_ACPI_WMI)		+= wmi.o  obj-$(CONFIG_MSI_WMI)		+= msi-wmi.o +obj-$(CONFIG_PEAQ_WMI)		+= peaq-wmi.o  obj-$(CONFIG_SURFACE3_WMI)	+= surface3-wmi.o  obj-$(CONFIG_TOPSTAR_LAPTOP)	+= topstar-laptop.o +obj-$(CONFIG_WMI_BMOF)		+= wmi-bmof.o  # toshiba_acpi must link after wmi to ensure that wmi devices are found  # before toshiba_acpi initializes @@ -46,6 +48,7 @@ obj-$(CONFIG_TOSHIBA_BT_RFKILL)	+= toshiba_bluetooth.o  obj-$(CONFIG_TOSHIBA_HAPS)	+= toshiba_haps.o  obj-$(CONFIG_TOSHIBA_WMI)	+= toshiba-wmi.o  obj-$(CONFIG_INTEL_CHT_INT33FE)	+= intel_cht_int33fe.o +obj-$(CONFIG_INTEL_INT0002_VGPIO) += intel_int0002_vgpio.o  obj-$(CONFIG_INTEL_HID_EVENT)	+= intel-hid.o  obj-$(CONFIG_INTEL_VBTN)	+= intel-vbtn.o  obj-$(CONFIG_INTEL_SCU_IPC)	+= intel_scu_ipc.o diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index 79fa5ab3fd00..1be71f956d5c 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -149,6 +149,8 @@ struct event_return_value {  #define ACER_WMID3_GDS_THREEG		(1<<6)	/* 3G */  #define ACER_WMID3_GDS_WIMAX		(1<<7)	/* WiMAX */  #define ACER_WMID3_GDS_BLUETOOTH	(1<<11)	/* BT */ +#define ACER_WMID3_GDS_RFBTN		(1<<14)	/* RF Button */ +  #define ACER_WMID3_GDS_TOUCHPAD		(1<<1)	/* Touchpad */  /* Hotkey Customized Setting and Acer Application Status. @@ -221,6 +223,7 @@ struct hotkey_function_type_aa {  #define ACER_CAP_BRIGHTNESS		(1<<3)  #define ACER_CAP_THREEG			(1<<4)  #define ACER_CAP_ACCEL			(1<<5) +#define ACER_CAP_RFBTN			(1<<6)  #define ACER_CAP_ANY			(0xFFFFFFFF)  /* @@ -700,7 +703,7 @@ struct acpi_buffer *result)  	input.length = sizeof(struct wmab_args);  	input.pointer = (u8 *)regbuf; -	status = wmi_evaluate_method(AMW0_GUID1, 1, 1, &input, result); +	status = wmi_evaluate_method(AMW0_GUID1, 0, 1, &input, result);  	return status;  } @@ -965,7 +968,7 @@ WMI_execute_u32(u32 method_id, u32 in, u32 *out)  	u32 tmp = 0;  	acpi_status status; -	status = wmi_evaluate_method(WMID_GUID1, 1, method_id, &input, &result); +	status = wmi_evaluate_method(WMID_GUID1, 0, method_id, &input, &result);  	if (ACPI_FAILURE(status))  		return status; @@ -1264,6 +1267,10 @@ static void __init type_aa_dmi_decode(const struct dmi_header *header, void *d)  		interface->capability |= ACER_CAP_THREEG;  	if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_BLUETOOTH)  		interface->capability |= ACER_CAP_BLUETOOTH; +	if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_RFBTN) { +		interface->capability |= ACER_CAP_RFBTN; +		commun_func_bitmap &= ~ACER_WMID3_GDS_RFBTN; +	}  	commun_fn_key_number = type_aa->commun_fn_key_number;  } @@ -1275,7 +1282,7 @@ static acpi_status __init WMID_set_capabilities(void)  	acpi_status status;  	u32 devices; -	status = wmi_query_block(WMID_GUID2, 1, &out); +	status = wmi_query_block(WMID_GUID2, 0, &out);  	if (ACPI_FAILURE(status))  		return status; @@ -2018,7 +2025,7 @@ static u32 get_wmid_devices(void)  	acpi_status status;  	u32 devices = 0; -	status = wmi_query_block(WMID_GUID2, 1, &out); +	status = wmi_query_block(WMID_GUID2, 0, &out);  	if (ACPI_FAILURE(status))  		return 0; diff --git a/drivers/platform/x86/acerhdf.c b/drivers/platform/x86/acerhdf.c index 2acdb0d6ea89..ea22591ee66f 100644 --- a/drivers/platform/x86/acerhdf.c +++ b/drivers/platform/x86/acerhdf.c @@ -557,7 +557,7 @@ err_out:  }  /* bind fan callbacks to fan device */ -static struct thermal_cooling_device_ops acerhdf_cooling_ops = { +static const struct thermal_cooling_device_ops acerhdf_cooling_ops = {  	.get_max_state = acerhdf_get_max_state,  	.get_cur_state = acerhdf_get_cur_state,  	.set_cur_state = acerhdf_set_cur_state, diff --git a/drivers/platform/x86/alienware-wmi.c b/drivers/platform/x86/alienware-wmi.c index d6b34923fb4e..9866fec78c1c 100644 --- a/drivers/platform/x86/alienware-wmi.c +++ b/drivers/platform/x86/alienware-wmi.c @@ -303,7 +303,7 @@ static int alienware_update_led(struct platform_zone *zone)  	}  	pr_debug("alienware-wmi: guid %s method %d\n", guid, method_id); -	status = wmi_evaluate_method(guid, 1, method_id, &input, NULL); +	status = wmi_evaluate_method(guid, 0, method_id, &input, NULL);  	if (ACPI_FAILURE(status))  		pr_err("alienware-wmi: zone set failure: %u\n", status);  	return ACPI_FAILURE(status); @@ -352,7 +352,7 @@ static int wmax_brightness(int brightness)  	};  	input.length = (acpi_size) sizeof(args);  	input.pointer = &args; -	status = wmi_evaluate_method(WMAX_CONTROL_GUID, 1, +	status = wmi_evaluate_method(WMAX_CONTROL_GUID, 0,  				     WMAX_METHOD_BRIGHTNESS, &input, NULL);  	if (ACPI_FAILURE(status))  		pr_err("alienware-wmi: brightness set failure: %u\n", status); @@ -506,10 +506,10 @@ static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args,  	if (out_data != NULL) {  		output.length = ACPI_ALLOCATE_BUFFER;  		output.pointer = NULL; -		status = wmi_evaluate_method(WMAX_CONTROL_GUID, 1, +		status = wmi_evaluate_method(WMAX_CONTROL_GUID, 0,  					     command, &input, &output);  	} else -		status = wmi_evaluate_method(WMAX_CONTROL_GUID, 1, +		status = wmi_evaluate_method(WMAX_CONTROL_GUID, 0,  					     command, &input, NULL);  	if (ACPI_SUCCESS(status) && out_data != NULL) { diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index ec202094bd50..f42159fd2031 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c @@ -1510,7 +1510,11 @@ static void kbd_init(void)  	ret = kbd_init_info();  	kbd_init_tokens(); -	if (kbd_token_bits != 0 || ret == 0) +	/* +	 * Only supports keyboard backlight when it has at least two modes. +	 */ +	if ((ret == 0 && (kbd_info.levels != 0 || kbd_mode_levels_count >= 2)) +	    || kbd_get_valid_token_counts() >= 2)  		kbd_led_present = true;  } diff --git a/drivers/platform/x86/dell-rbtn.c b/drivers/platform/x86/dell-rbtn.c index dcd9f40a4b18..f3afe778001e 100644 --- a/drivers/platform/x86/dell-rbtn.c +++ b/drivers/platform/x86/dell-rbtn.c @@ -110,7 +110,7 @@ static int rbtn_rfkill_set_block(void *data, bool blocked)  	return -EINVAL;  } -static struct rfkill_ops rbtn_ops = { +static const struct rfkill_ops rbtn_ops = {  	.query = rbtn_rfkill_query,  	.set_block = rbtn_rfkill_set_block,  }; @@ -221,16 +221,27 @@ static const struct acpi_device_id rbtn_ids[] = {  	/*  	 * This driver can also handle the "DELLABC6" device that -	 * appears on the XPS 13 9350, but that device is disabled -	 * by the DSDT unless booted with acpi_osi="!Windows 2012" -	 * acpi_osi="!Windows 2013".  Even if we boot that and bind -	 * the driver, we seem to have inconsistent behavior in -	 * which NetworkManager can get out of sync with the rfkill -	 * state. +	 * appears on the XPS 13 9350, but that device is disabled by +	 * the DSDT unless booted with acpi_osi="!Windows 2012" +	 * acpi_osi="!Windows 2013".  	 * -	 * On the XPS 13 9350 and similar laptops, we're not supposed to -	 * use DELLABC6 at all.  Instead, we handle the rfkill button -	 * via the intel-hid driver. +	 * According to Mario at Dell: +	 * +	 *  DELLABC6 is a custom interface that was created solely to +	 *  have airplane mode support for Windows 7.  For Windows 10 +	 *  the proper interface is to use that which is handled by +	 *  intel-hid. A OEM airplane mode driver is not used. +	 * +	 *  Since the kernel doesn't identify as Windows 7 it would be +	 *  incorrect to do attempt to use that interface. +	 * +	 * Even if we override _OSI and bind to DELLABC6, we end up with +	 * inconsistent behavior in which userspace can get out of sync +	 * with the rfkill state as it conflicts with events from +	 * intel-hid. +	 * +	 * The upshot is that it is better to just ignore DELLABC6 +	 * devices.  	 */  	{ "", 0 }, diff --git a/drivers/platform/x86/dell-wmi-led.c b/drivers/platform/x86/dell-wmi-led.c index a0c7e99530ef..5bedaf7f0633 100644 --- a/drivers/platform/x86/dell-wmi-led.c +++ b/drivers/platform/x86/dell-wmi-led.c @@ -68,7 +68,7 @@ static int dell_led_perform_fn(u8 length, u8 result_code, u8 device_id,  	input.length = sizeof(struct bios_args);  	input.pointer = &args; -	status = wmi_evaluate_method(DELL_LED_BIOS_GUID, 1, 1, &input, &output); +	status = wmi_evaluate_method(DELL_LED_BIOS_GUID, 0, 1, &input, &output);  	if (ACPI_FAILURE(status))  		return status; diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c index 8a64c7967753..f8978464df31 100644 --- a/drivers/platform/x86/dell-wmi.c +++ b/drivers/platform/x86/dell-wmi.c @@ -36,6 +36,7 @@  #include <linux/acpi.h>  #include <linux/string.h>  #include <linux/dmi.h> +#include <linux/wmi.h>  #include <acpi/video.h>  #include "dell-smbios.h" @@ -53,6 +54,10 @@ static bool wmi_requires_smbios_request;  MODULE_ALIAS("wmi:"DELL_EVENT_GUID);  MODULE_ALIAS("wmi:"DELL_DESCRIPTOR_GUID); +struct dell_wmi_priv { +	struct input_dev *input_dev; +}; +  static int __init dmi_matched(const struct dmi_system_id *dmi)  {  	wmi_requires_smbios_request = 1; @@ -86,7 +91,7 @@ static const struct dmi_system_id dell_wmi_smbios_list[] __initconst = {   * notifications (rather than requests for change) or are also sent   * via the keyboard controller so should not be sent again.   */ -static const struct key_entry dell_wmi_keymap_type_0000[] __initconst = { +static const struct key_entry dell_wmi_keymap_type_0000[] = {  	{ KE_IGNORE, 0x003a, { KEY_CAPSLOCK } },  	/* Key code is followed by brightness level */ @@ -207,7 +212,7 @@ struct dell_dmi_results {  };  /* Uninitialized entries here are KEY_RESERVED == 0. */ -static const u16 bios_to_linux_keycode[256] __initconst = { +static const u16 bios_to_linux_keycode[256] = {  	[0]	= KEY_MEDIA,  	[1]	= KEY_NEXTSONG,  	[2]	= KEY_PLAYPAUSE, @@ -256,7 +261,7 @@ static const u16 bios_to_linux_keycode[256] __initconst = {   * These are applied if the 0xB2 DMI hotkey table is present and doesn't   * override them.   */ -static const struct key_entry dell_wmi_keymap_type_0010[] __initconst = { +static const struct key_entry dell_wmi_keymap_type_0010[] = {  	/* Fn-lock */  	{ KE_IGNORE, 0x151, { KEY_RESERVED } }, @@ -272,7 +277,12 @@ static const struct key_entry dell_wmi_keymap_type_0010[] __initconst = {  	/* RGB keyboard backlight control */  	{ KE_IGNORE, 0x154, { KEY_RESERVED } }, -	/* Stealth mode toggle */ +	/* +	 * Stealth mode toggle. This will "disable all lights and sounds". +	 * The action is performed by the BIOS and EC; the WMI event is just +	 * a notification. On the XPS 13 9350, this is Fn+F7, and there's +	 * a BIOS setting to enable and disable the hotkey. +	 */  	{ KE_IGNORE, 0x155, { KEY_RESERVED } },  	/* Rugged magnetic dock attach/detach events */ @@ -289,7 +299,7 @@ static const struct key_entry dell_wmi_keymap_type_0010[] __initconst = {  /*   * Keymap for WMI events of type 0x0011   */ -static const struct key_entry dell_wmi_keymap_type_0011[] __initconst = { +static const struct key_entry dell_wmi_keymap_type_0011[] = {  	/* Battery unplugged */  	{ KE_IGNORE, 0xfff0, { KEY_RESERVED } }, @@ -304,13 +314,12 @@ static const struct key_entry dell_wmi_keymap_type_0011[] __initconst = {  	{ KE_IGNORE, 0x02f6, { KEY_RESERVED } },  }; -static struct input_dev *dell_wmi_input_dev; - -static void dell_wmi_process_key(int type, int code) +static void dell_wmi_process_key(struct wmi_device *wdev, int type, int code)  { +	struct dell_wmi_priv *priv = dev_get_drvdata(&wdev->dev);  	const struct key_entry *key; -	key = sparse_keymap_entry_from_scancode(dell_wmi_input_dev, +	key = sparse_keymap_entry_from_scancode(priv->input_dev,  						(type << 16) | code);  	if (!key) {  		pr_info("Unknown key with type 0x%04x and code 0x%04x pressed\n", @@ -333,33 +342,18 @@ static void dell_wmi_process_key(int type, int code)  		dell_laptop_call_notifier(  			DELL_LAPTOP_KBD_BACKLIGHT_BRIGHTNESS_CHANGED, NULL); -	sparse_keymap_report_entry(dell_wmi_input_dev, key, 1, true); +	sparse_keymap_report_entry(priv->input_dev, key, 1, true);  } -static void dell_wmi_notify(u32 value, void *context) +static void dell_wmi_notify(struct wmi_device *wdev, +			    union acpi_object *obj)  { -	struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; -	union acpi_object *obj; -	acpi_status status; -	acpi_size buffer_size;  	u16 *buffer_entry, *buffer_end; +	acpi_size buffer_size;  	int len, i; -	status = wmi_get_event_data(value, &response); -	if (status != AE_OK) { -		pr_warn("bad event status 0x%x\n", status); -		return; -	} - -	obj = (union acpi_object *)response.pointer; -	if (!obj) { -		pr_warn("no response\n"); -		return; -	} -  	if (obj->type != ACPI_TYPE_BUFFER) {  		pr_warn("bad response type %x\n", obj->type); -		kfree(obj);  		return;  	} @@ -404,13 +398,14 @@ static void dell_wmi_notify(u32 value, void *context)  		switch (buffer_entry[1]) {  		case 0x0000: /* One key pressed or event occurred */  			if (len > 2) -				dell_wmi_process_key(0x0000, buffer_entry[2]); +				dell_wmi_process_key(wdev, 0x0000, +						     buffer_entry[2]);  			/* Other entries could contain additional information */  			break;  		case 0x0010: /* Sequence of keys pressed */  		case 0x0011: /* Sequence of events occurred */  			for (i = 2; i < len; ++i) -				dell_wmi_process_key(buffer_entry[1], +				dell_wmi_process_key(wdev, buffer_entry[1],  						     buffer_entry[i]);  			break;  		default: /* Unknown event */ @@ -423,7 +418,6 @@ static void dell_wmi_notify(u32 value, void *context)  	} -	kfree(obj);  }  static bool have_scancode(u32 scancode, const struct key_entry *keymap, int len) @@ -437,9 +431,7 @@ static bool have_scancode(u32 scancode, const struct key_entry *keymap, int len)  	return false;  } -static void __init handle_dmi_entry(const struct dmi_header *dm, -				    void *opaque) - +static void handle_dmi_entry(const struct dmi_header *dm, void *opaque)  {  	struct dell_dmi_results *results = opaque;  	struct dell_bios_hotkey_table *table; @@ -449,6 +441,7 @@ static void __init handle_dmi_entry(const struct dmi_header *dm,  	if (results->err || results->keymap)  		return;		/* We already found the hotkey table. */ +	/* The Dell hotkey table is type 0xB2.  Scan until we find it. */  	if (dm->type != 0xb2)  		return; @@ -509,19 +502,20 @@ static void __init handle_dmi_entry(const struct dmi_header *dm,  	results->keymap_size = pos;  } -static int __init dell_wmi_input_setup(void) +static int dell_wmi_input_setup(struct wmi_device *wdev)  { +	struct dell_wmi_priv *priv = dev_get_drvdata(&wdev->dev);  	struct dell_dmi_results dmi_results = {};  	struct key_entry *keymap;  	int err, i, pos = 0; -	dell_wmi_input_dev = input_allocate_device(); -	if (!dell_wmi_input_dev) +	priv->input_dev = input_allocate_device(); +	if (!priv->input_dev)  		return -ENOMEM; -	dell_wmi_input_dev->name = "Dell WMI hotkeys"; -	dell_wmi_input_dev->phys = "wmi/input0"; -	dell_wmi_input_dev->id.bustype = BUS_HOST; +	priv->input_dev->name = "Dell WMI hotkeys"; +	priv->input_dev->id.bustype = BUS_HOST; +	priv->input_dev->dev.parent = &wdev->dev;  	if (dmi_walk(handle_dmi_entry, &dmi_results)) {  		/* @@ -596,7 +590,7 @@ static int __init dell_wmi_input_setup(void)  	keymap[pos].type = KE_END; -	err = sparse_keymap_setup(dell_wmi_input_dev, keymap, NULL); +	err = sparse_keymap_setup(priv->input_dev, keymap, NULL);  	/*  	 * Sparse keymap library makes a copy of keymap so we don't need the  	 * original one that was allocated. @@ -605,17 +599,24 @@ static int __init dell_wmi_input_setup(void)  	if (err)  		goto err_free_dev; -	err = input_register_device(dell_wmi_input_dev); +	err = input_register_device(priv->input_dev);  	if (err)  		goto err_free_dev;  	return 0;   err_free_dev: -	input_free_device(dell_wmi_input_dev); +	input_free_device(priv->input_dev);  	return err;  } +static void dell_wmi_input_destroy(struct wmi_device *wdev) +{ +	struct dell_wmi_priv *priv = dev_get_drvdata(&wdev->dev); + +	input_unregister_device(priv->input_dev); +} +  /*   * Descriptor buffer is 128 byte long and contains:   * @@ -714,46 +715,55 @@ static int dell_wmi_events_set_enabled(bool enable)  	return dell_smbios_error(ret);  } +static int dell_wmi_probe(struct wmi_device *wdev) +{ +	struct dell_wmi_priv *priv = devm_kzalloc( +		&wdev->dev, sizeof(struct dell_wmi_priv), GFP_KERNEL); + +	dev_set_drvdata(&wdev->dev, priv); + +	return dell_wmi_input_setup(wdev); +} + +static int dell_wmi_remove(struct wmi_device *wdev) +{ +	dell_wmi_input_destroy(wdev); +	return 0; +} +static const struct wmi_device_id dell_wmi_id_table[] = { +	{ .guid_string = DELL_EVENT_GUID }, +	{ }, +}; + +static struct wmi_driver dell_wmi_driver = { +	.driver = { +		.name = "dell-wmi", +	}, +	.id_table = dell_wmi_id_table, +	.probe = dell_wmi_probe, +	.remove = dell_wmi_remove, +	.notify = dell_wmi_notify, +}; +  static int __init dell_wmi_init(void)  {  	int err; -	acpi_status status; - -	if (!wmi_has_guid(DELL_EVENT_GUID) || -	    !wmi_has_guid(DELL_DESCRIPTOR_GUID)) { -		pr_warn("Dell WMI GUID were not found\n"); -		return -ENODEV; -	}  	err = dell_wmi_check_descriptor_buffer();  	if (err)  		return err; -	err = dell_wmi_input_setup(); -	if (err) -		return err; - -	status = wmi_install_notify_handler(DELL_EVENT_GUID, -					 dell_wmi_notify, NULL); -	if (ACPI_FAILURE(status)) { -		input_unregister_device(dell_wmi_input_dev); -		pr_err("Unable to register notify handler - %d\n", status); -		return -ENODEV; -	} -  	dmi_check_system(dell_wmi_smbios_list);  	if (wmi_requires_smbios_request) {  		err = dell_wmi_events_set_enabled(true);  		if (err) {  			pr_err("Failed to enable WMI events\n"); -			wmi_remove_notify_handler(DELL_EVENT_GUID); -			input_unregister_device(dell_wmi_input_dev);  			return err;  		}  	} -	return 0; +	return wmi_driver_register(&dell_wmi_driver);  }  module_init(dell_wmi_init); @@ -761,7 +771,7 @@ static void __exit dell_wmi_exit(void)  {  	if (wmi_requires_smbios_request)  		dell_wmi_events_set_enabled(false); -	wmi_remove_notify_handler(DELL_EVENT_GUID); -	input_unregister_device(dell_wmi_input_dev); + +	wmi_driver_unregister(&dell_wmi_driver);  }  module_exit(dell_wmi_exit); diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 2426399e1e04..5a681962899c 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -445,7 +445,7 @@ static struct attribute *platform_attributes[] = {  	NULL  }; -static struct attribute_group platform_attribute_group = { +static const struct attribute_group platform_attribute_group = {  	.attrs = platform_attributes  }; diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c index 7f49d92914c9..c1a852847d02 100644 --- a/drivers/platform/x86/fujitsu-laptop.c +++ b/drivers/platform/x86/fujitsu-laptop.c @@ -112,25 +112,8 @@  #define MAX_HOTKEY_RINGBUFFER_SIZE 100  #define RINGBUFFERSIZE 40 -/* Debugging */ -#define FUJLAPTOP_DBG_ERROR	  0x0001 -#define FUJLAPTOP_DBG_WARN	  0x0002 -#define FUJLAPTOP_DBG_INFO	  0x0004 -#define FUJLAPTOP_DBG_TRACE	  0x0008 - -#ifdef CONFIG_FUJITSU_LAPTOP_DEBUG -#define vdbg_printk(a_dbg_level, format, arg...) \ -	do { if (dbg_level & a_dbg_level) \ -		printk(KERN_DEBUG pr_fmt("%s: " format), __func__, ## arg); \ -	} while (0) -#else -#define vdbg_printk(a_dbg_level, format, arg...) \ -	do { } while (0) -#endif -  /* Device controlling the backlight and associated keys */  struct fujitsu_bl { -	acpi_handle acpi_handle;  	struct input_dev *input;  	char phys[32];  	struct backlight_device *bl_device; @@ -144,8 +127,6 @@ static bool disable_brightness_adjust;  /* Device used to access hotkeys and other features on the laptop */  struct fujitsu_laptop { -	acpi_handle acpi_handle; -	struct acpi_device *dev;  	struct input_dev *input;  	char phys[32];  	struct platform_device *pf_device; @@ -155,15 +136,12 @@ struct fujitsu_laptop {  	int flags_state;  }; -static struct fujitsu_laptop *fujitsu_laptop; - -#ifdef CONFIG_FUJITSU_LAPTOP_DEBUG -static u32 dbg_level = 0x03; -#endif +static struct acpi_device *fext;  /* Fujitsu ACPI interface function */ -static int call_fext_func(int func, int op, int feature, int state) +static int call_fext_func(struct acpi_device *device, +			  int func, int op, int feature, int state)  {  	union acpi_object params[4] = {  		{ .integer.type = ACPI_TYPE_INTEGER, .integer.value = func }, @@ -175,28 +153,30 @@ static int call_fext_func(int func, int op, int feature, int state)  	unsigned long long value;  	acpi_status status; -	status = acpi_evaluate_integer(fujitsu_laptop->acpi_handle, "FUNC", -				       &arg_list, &value); +	status = acpi_evaluate_integer(device->handle, "FUNC", &arg_list, +				       &value);  	if (ACPI_FAILURE(status)) { -		vdbg_printk(FUJLAPTOP_DBG_ERROR, "Failed to evaluate FUNC\n"); +		acpi_handle_err(device->handle, "Failed to evaluate FUNC\n");  		return -ENODEV;  	} -	vdbg_printk(FUJLAPTOP_DBG_TRACE, "FUNC 0x%x (args 0x%x, 0x%x, 0x%x) returned 0x%x\n", -		    func, op, feature, state, (int)value); +	acpi_handle_debug(device->handle, +			  "FUNC 0x%x (args 0x%x, 0x%x, 0x%x) returned 0x%x\n", +			  func, op, feature, state, (int)value);  	return value;  }  /* Hardware access for LCD brightness control */ -static int set_lcd_level(int level) +static int set_lcd_level(struct acpi_device *device, int level)  { +	struct fujitsu_bl *priv = acpi_driver_data(device);  	acpi_status status;  	char *method;  	switch (use_alt_lcd_levels) {  	case -1: -		if (acpi_has_method(fujitsu_bl->acpi_handle, "SBL2")) +		if (acpi_has_method(device->handle, "SBL2"))  			method = "SBL2";  		else  			method = "SBLL"; @@ -209,74 +189,77 @@ static int set_lcd_level(int level)  		break;  	} -	vdbg_printk(FUJLAPTOP_DBG_TRACE, "set lcd level via %s [%d]\n", -		    method, level); +	acpi_handle_debug(device->handle, "set lcd level via %s [%d]\n", method, +			  level); -	if (level < 0 || level >= fujitsu_bl->max_brightness) +	if (level < 0 || level >= priv->max_brightness)  		return -EINVAL; -	status = acpi_execute_simple_method(fujitsu_bl->acpi_handle, method, -					    level); +	status = acpi_execute_simple_method(device->handle, method, level);  	if (ACPI_FAILURE(status)) { -		vdbg_printk(FUJLAPTOP_DBG_ERROR, "Failed to evaluate %s\n", -			    method); +		acpi_handle_err(device->handle, "Failed to evaluate %s\n", +				method);  		return -ENODEV;  	} -	fujitsu_bl->brightness_level = level; +	priv->brightness_level = level;  	return 0;  } -static int get_lcd_level(void) +static int get_lcd_level(struct acpi_device *device)  { +	struct fujitsu_bl *priv = acpi_driver_data(device);  	unsigned long long state = 0;  	acpi_status status = AE_OK; -	vdbg_printk(FUJLAPTOP_DBG_TRACE, "get lcd level via GBLL\n"); +	acpi_handle_debug(device->handle, "get lcd level via GBLL\n"); -	status = acpi_evaluate_integer(fujitsu_bl->acpi_handle, "GBLL", NULL, -				       &state); +	status = acpi_evaluate_integer(device->handle, "GBLL", NULL, &state);  	if (ACPI_FAILURE(status))  		return 0; -	fujitsu_bl->brightness_level = state & 0x0fffffff; +	priv->brightness_level = state & 0x0fffffff; -	return fujitsu_bl->brightness_level; +	return priv->brightness_level;  } -static int get_max_brightness(void) +static int get_max_brightness(struct acpi_device *device)  { +	struct fujitsu_bl *priv = acpi_driver_data(device);  	unsigned long long state = 0;  	acpi_status status = AE_OK; -	vdbg_printk(FUJLAPTOP_DBG_TRACE, "get max lcd level via RBLL\n"); +	acpi_handle_debug(device->handle, "get max lcd level via RBLL\n"); -	status = acpi_evaluate_integer(fujitsu_bl->acpi_handle, "RBLL", NULL, -				       &state); +	status = acpi_evaluate_integer(device->handle, "RBLL", NULL, &state);  	if (ACPI_FAILURE(status))  		return -1; -	fujitsu_bl->max_brightness = state; +	priv->max_brightness = state; -	return fujitsu_bl->max_brightness; +	return priv->max_brightness;  }  /* Backlight device stuff */  static int bl_get_brightness(struct backlight_device *b)  { -	return b->props.power == FB_BLANK_POWERDOWN ? 0 : get_lcd_level(); +	struct acpi_device *device = bl_get_data(b); + +	return b->props.power == FB_BLANK_POWERDOWN ? 0 : get_lcd_level(device);  }  static int bl_update_status(struct backlight_device *b)  { +	struct acpi_device *device = bl_get_data(b); +  	if (b->props.power == FB_BLANK_POWERDOWN) -		call_fext_func(FUNC_BACKLIGHT, 0x1, 0x4, 0x3); +		call_fext_func(fext, FUNC_BACKLIGHT, 0x1, 0x4, 0x3);  	else -		call_fext_func(FUNC_BACKLIGHT, 0x1, 0x4, 0x0); +		call_fext_func(fext, FUNC_BACKLIGHT, 0x1, 0x4, 0x0); -	return set_lcd_level(b->props.brightness); +	return set_lcd_level(device, b->props.brightness);  }  static const struct backlight_ops fujitsu_bl_ops = { @@ -287,9 +270,11 @@ static const struct backlight_ops fujitsu_bl_ops = {  static ssize_t lid_show(struct device *dev, struct device_attribute *attr,  			char *buf)  { -	if (!(fujitsu_laptop->flags_supported & FLAG_LID)) +	struct fujitsu_laptop *priv = dev_get_drvdata(dev); + +	if (!(priv->flags_supported & FLAG_LID))  		return sprintf(buf, "unknown\n"); -	if (fujitsu_laptop->flags_state & FLAG_LID) +	if (priv->flags_state & FLAG_LID)  		return sprintf(buf, "open\n");  	else  		return sprintf(buf, "closed\n"); @@ -298,9 +283,11 @@ static ssize_t lid_show(struct device *dev, struct device_attribute *attr,  static ssize_t dock_show(struct device *dev, struct device_attribute *attr,  			 char *buf)  { -	if (!(fujitsu_laptop->flags_supported & FLAG_DOCK)) +	struct fujitsu_laptop *priv = dev_get_drvdata(dev); + +	if (!(priv->flags_supported & FLAG_DOCK))  		return sprintf(buf, "unknown\n"); -	if (fujitsu_laptop->flags_state & FLAG_DOCK) +	if (priv->flags_state & FLAG_DOCK)  		return sprintf(buf, "docked\n");  	else  		return sprintf(buf, "undocked\n"); @@ -309,9 +296,11 @@ static ssize_t dock_show(struct device *dev, struct device_attribute *attr,  static ssize_t radios_show(struct device *dev, struct device_attribute *attr,  			   char *buf)  { -	if (!(fujitsu_laptop->flags_supported & FLAG_RFKILL)) +	struct fujitsu_laptop *priv = dev_get_drvdata(dev); + +	if (!(priv->flags_supported & FLAG_RFKILL))  		return sprintf(buf, "unknown\n"); -	if (fujitsu_laptop->flags_state & FLAG_RFKILL) +	if (priv->flags_state & FLAG_RFKILL)  		return sprintf(buf, "on\n");  	else  		return sprintf(buf, "killed\n"); @@ -348,89 +337,76 @@ static const struct key_entry keymap_backlight[] = {  static int acpi_fujitsu_bl_input_setup(struct acpi_device *device)  { -	struct fujitsu_bl *fujitsu_bl = acpi_driver_data(device); +	struct fujitsu_bl *priv = acpi_driver_data(device);  	int ret; -	fujitsu_bl->input = devm_input_allocate_device(&device->dev); -	if (!fujitsu_bl->input) +	priv->input = devm_input_allocate_device(&device->dev); +	if (!priv->input)  		return -ENOMEM; -	snprintf(fujitsu_bl->phys, sizeof(fujitsu_bl->phys), -		 "%s/video/input0", acpi_device_hid(device)); +	snprintf(priv->phys, sizeof(priv->phys), "%s/video/input0", +		 acpi_device_hid(device)); -	fujitsu_bl->input->name = acpi_device_name(device); -	fujitsu_bl->input->phys = fujitsu_bl->phys; -	fujitsu_bl->input->id.bustype = BUS_HOST; -	fujitsu_bl->input->id.product = 0x06; +	priv->input->name = acpi_device_name(device); +	priv->input->phys = priv->phys; +	priv->input->id.bustype = BUS_HOST; +	priv->input->id.product = 0x06; -	ret = sparse_keymap_setup(fujitsu_bl->input, keymap_backlight, NULL); +	ret = sparse_keymap_setup(priv->input, keymap_backlight, NULL);  	if (ret)  		return ret; -	return input_register_device(fujitsu_bl->input); +	return input_register_device(priv->input);  }  static int fujitsu_backlight_register(struct acpi_device *device)  { +	struct fujitsu_bl *priv = acpi_driver_data(device);  	const struct backlight_properties props = { -		.brightness = fujitsu_bl->brightness_level, -		.max_brightness = fujitsu_bl->max_brightness - 1, +		.brightness = priv->brightness_level, +		.max_brightness = priv->max_brightness - 1,  		.type = BACKLIGHT_PLATFORM  	};  	struct backlight_device *bd;  	bd = devm_backlight_device_register(&device->dev, "fujitsu-laptop", -					    &device->dev, NULL, +					    &device->dev, device,  					    &fujitsu_bl_ops, &props);  	if (IS_ERR(bd))  		return PTR_ERR(bd); -	fujitsu_bl->bl_device = bd; +	priv->bl_device = bd;  	return 0;  }  static int acpi_fujitsu_bl_add(struct acpi_device *device)  { -	int state = 0; +	struct fujitsu_bl *priv;  	int error;  	if (acpi_video_get_backlight_type() != acpi_backlight_vendor)  		return -ENODEV; -	if (!device) -		return -EINVAL; +	priv = devm_kzalloc(&device->dev, sizeof(*priv), GFP_KERNEL); +	if (!priv) +		return -ENOMEM; -	fujitsu_bl->acpi_handle = device->handle; -	sprintf(acpi_device_name(device), "%s", ACPI_FUJITSU_BL_DEVICE_NAME); -	sprintf(acpi_device_class(device), "%s", ACPI_FUJITSU_CLASS); -	device->driver_data = fujitsu_bl; +	fujitsu_bl = priv; +	strcpy(acpi_device_name(device), ACPI_FUJITSU_BL_DEVICE_NAME); +	strcpy(acpi_device_class(device), ACPI_FUJITSU_CLASS); +	device->driver_data = priv;  	error = acpi_fujitsu_bl_input_setup(device);  	if (error)  		return error; -	error = acpi_bus_update_power(fujitsu_bl->acpi_handle, &state); -	if (error) { -		pr_err("Error reading power state\n"); -		return error; -	} - -	pr_info("ACPI: %s [%s] (%s)\n", -	       acpi_device_name(device), acpi_device_bid(device), -	       !device->power.state ? "on" : "off"); +	pr_info("ACPI: %s [%s]\n", +		acpi_device_name(device), acpi_device_bid(device)); -	if (acpi_has_method(device->handle, METHOD_NAME__INI)) { -		vdbg_printk(FUJLAPTOP_DBG_INFO, "Invoking _INI\n"); -		if (ACPI_FAILURE -		    (acpi_evaluate_object -		     (device->handle, METHOD_NAME__INI, NULL, NULL))) -			pr_err("_INI Method failed\n"); -	} - -	if (get_max_brightness() <= 0) -		fujitsu_bl->max_brightness = FUJITSU_LCD_N_LEVELS; -	get_lcd_level(); +	if (get_max_brightness(device) <= 0) +		priv->max_brightness = FUJITSU_LCD_N_LEVELS; +	get_lcd_level(device);  	error = fujitsu_backlight_register(device);  	if (error) @@ -443,32 +419,30 @@ static int acpi_fujitsu_bl_add(struct acpi_device *device)  static void acpi_fujitsu_bl_notify(struct acpi_device *device, u32 event)  { -	struct input_dev *input; +	struct fujitsu_bl *priv = acpi_driver_data(device);  	int oldb, newb; -	input = fujitsu_bl->input; -  	if (event != ACPI_FUJITSU_NOTIFY_CODE1) { -		vdbg_printk(FUJLAPTOP_DBG_WARN, -			    "unsupported event [0x%x]\n", event); -		sparse_keymap_report_event(input, -1, 1, true); +		acpi_handle_info(device->handle, "unsupported event [0x%x]\n", +				 event); +		sparse_keymap_report_event(priv->input, -1, 1, true);  		return;  	} -	oldb = fujitsu_bl->brightness_level; -	get_lcd_level(); -	newb = fujitsu_bl->brightness_level; +	oldb = priv->brightness_level; +	get_lcd_level(device); +	newb = priv->brightness_level; -	vdbg_printk(FUJLAPTOP_DBG_TRACE, "brightness button event [%i -> %i]\n", -		    oldb, newb); +	acpi_handle_debug(device->handle, +			  "brightness button event [%i -> %i]\n", oldb, newb);  	if (oldb == newb)  		return;  	if (!disable_brightness_adjust) -		set_lcd_level(newb); +		set_lcd_level(device, newb); -	sparse_keymap_report_event(input, oldb < newb, 1, true); +	sparse_keymap_report_event(priv->input, oldb < newb, 1, true);  }  /* ACPI device for hotkey handling */ @@ -541,42 +515,44 @@ static const struct dmi_system_id fujitsu_laptop_dmi_table[] = {  static int acpi_fujitsu_laptop_input_setup(struct acpi_device *device)  { -	struct fujitsu_laptop *fujitsu_laptop = acpi_driver_data(device); +	struct fujitsu_laptop *priv = acpi_driver_data(device);  	int ret; -	fujitsu_laptop->input = devm_input_allocate_device(&device->dev); -	if (!fujitsu_laptop->input) +	priv->input = devm_input_allocate_device(&device->dev); +	if (!priv->input)  		return -ENOMEM; -	snprintf(fujitsu_laptop->phys, sizeof(fujitsu_laptop->phys), -		 "%s/video/input0", acpi_device_hid(device)); +	snprintf(priv->phys, sizeof(priv->phys), "%s/input0", +		 acpi_device_hid(device)); -	fujitsu_laptop->input->name = acpi_device_name(device); -	fujitsu_laptop->input->phys = fujitsu_laptop->phys; -	fujitsu_laptop->input->id.bustype = BUS_HOST; -	fujitsu_laptop->input->id.product = 0x06; +	priv->input->name = acpi_device_name(device); +	priv->input->phys = priv->phys; +	priv->input->id.bustype = BUS_HOST;  	dmi_check_system(fujitsu_laptop_dmi_table); -	ret = sparse_keymap_setup(fujitsu_laptop->input, keymap, NULL); +	ret = sparse_keymap_setup(priv->input, keymap, NULL);  	if (ret)  		return ret; -	return input_register_device(fujitsu_laptop->input); +	return input_register_device(priv->input);  } -static int fujitsu_laptop_platform_add(void) +static int fujitsu_laptop_platform_add(struct acpi_device *device)  { +	struct fujitsu_laptop *priv = acpi_driver_data(device);  	int ret; -	fujitsu_laptop->pf_device = platform_device_alloc("fujitsu-laptop", -1); -	if (!fujitsu_laptop->pf_device) +	priv->pf_device = platform_device_alloc("fujitsu-laptop", -1); +	if (!priv->pf_device)  		return -ENOMEM; -	ret = platform_device_add(fujitsu_laptop->pf_device); +	platform_set_drvdata(priv->pf_device, priv); + +	ret = platform_device_add(priv->pf_device);  	if (ret)  		goto err_put_platform_device; -	ret = sysfs_create_group(&fujitsu_laptop->pf_device->dev.kobj, +	ret = sysfs_create_group(&priv->pf_device->dev.kobj,  				 &fujitsu_pf_attribute_group);  	if (ret)  		goto err_del_platform_device; @@ -584,23 +560,26 @@ static int fujitsu_laptop_platform_add(void)  	return 0;  err_del_platform_device: -	platform_device_del(fujitsu_laptop->pf_device); +	platform_device_del(priv->pf_device);  err_put_platform_device: -	platform_device_put(fujitsu_laptop->pf_device); +	platform_device_put(priv->pf_device);  	return ret;  } -static void fujitsu_laptop_platform_remove(void) +static void fujitsu_laptop_platform_remove(struct acpi_device *device)  { -	sysfs_remove_group(&fujitsu_laptop->pf_device->dev.kobj, +	struct fujitsu_laptop *priv = acpi_driver_data(device); + +	sysfs_remove_group(&priv->pf_device->dev.kobj,  			   &fujitsu_pf_attribute_group); -	platform_device_unregister(fujitsu_laptop->pf_device); +	platform_device_unregister(priv->pf_device);  }  static int logolamp_set(struct led_classdev *cdev,  			enum led_brightness brightness)  { +	struct acpi_device *device = to_acpi_device(cdev->dev->parent);  	int poweron = FUNC_LED_ON, always = FUNC_LED_ON;  	int ret; @@ -610,132 +589,128 @@ static int logolamp_set(struct led_classdev *cdev,  	if (brightness < LED_FULL)  		always = FUNC_LED_OFF; -	ret = call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, poweron); +	ret = call_fext_func(device, FUNC_LEDS, 0x1, LOGOLAMP_POWERON, poweron);  	if (ret < 0)  		return ret; -	return call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, always); +	return call_fext_func(device, FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, always);  }  static enum led_brightness logolamp_get(struct led_classdev *cdev)  { +	struct acpi_device *device = to_acpi_device(cdev->dev->parent);  	int ret; -	ret = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_ALWAYS, 0x0); +	ret = call_fext_func(device, FUNC_LEDS, 0x2, LOGOLAMP_ALWAYS, 0x0);  	if (ret == FUNC_LED_ON)  		return LED_FULL; -	ret = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_POWERON, 0x0); +	ret = call_fext_func(device, FUNC_LEDS, 0x2, LOGOLAMP_POWERON, 0x0);  	if (ret == FUNC_LED_ON)  		return LED_HALF;  	return LED_OFF;  } -static struct led_classdev logolamp_led = { -	.name = "fujitsu::logolamp", -	.brightness_set_blocking = logolamp_set, -	.brightness_get = logolamp_get -}; -  static int kblamps_set(struct led_classdev *cdev,  		       enum led_brightness brightness)  { +	struct acpi_device *device = to_acpi_device(cdev->dev->parent); +  	if (brightness >= LED_FULL) -		return call_fext_func(FUNC_LEDS, 0x1, KEYBOARD_LAMPS, +		return call_fext_func(device, FUNC_LEDS, 0x1, KEYBOARD_LAMPS,  				      FUNC_LED_ON);  	else -		return call_fext_func(FUNC_LEDS, 0x1, KEYBOARD_LAMPS, +		return call_fext_func(device, FUNC_LEDS, 0x1, KEYBOARD_LAMPS,  				      FUNC_LED_OFF);  }  static enum led_brightness kblamps_get(struct led_classdev *cdev)  { +	struct acpi_device *device = to_acpi_device(cdev->dev->parent);  	enum led_brightness brightness = LED_OFF; -	if (call_fext_func(FUNC_LEDS, 0x2, KEYBOARD_LAMPS, 0x0) == FUNC_LED_ON) +	if (call_fext_func(device, +			   FUNC_LEDS, 0x2, KEYBOARD_LAMPS, 0x0) == FUNC_LED_ON)  		brightness = LED_FULL;  	return brightness;  } -static struct led_classdev kblamps_led = { -	.name = "fujitsu::kblamps", -	.brightness_set_blocking = kblamps_set, -	.brightness_get = kblamps_get -}; -  static int radio_led_set(struct led_classdev *cdev,  			 enum led_brightness brightness)  { +	struct acpi_device *device = to_acpi_device(cdev->dev->parent); +  	if (brightness >= LED_FULL) -		return call_fext_func(FUNC_FLAGS, 0x5, RADIO_LED_ON, +		return call_fext_func(device, FUNC_FLAGS, 0x5, RADIO_LED_ON,  				      RADIO_LED_ON);  	else -		return call_fext_func(FUNC_FLAGS, 0x5, RADIO_LED_ON, 0x0); +		return call_fext_func(device, FUNC_FLAGS, 0x5, RADIO_LED_ON, +				      0x0);  }  static enum led_brightness radio_led_get(struct led_classdev *cdev)  { +	struct acpi_device *device = to_acpi_device(cdev->dev->parent);  	enum led_brightness brightness = LED_OFF; -	if (call_fext_func(FUNC_FLAGS, 0x4, 0x0, 0x0) & RADIO_LED_ON) +	if (call_fext_func(device, FUNC_FLAGS, 0x4, 0x0, 0x0) & RADIO_LED_ON)  		brightness = LED_FULL;  	return brightness;  } -static struct led_classdev radio_led = { -	.name = "fujitsu::radio_led", -	.brightness_set_blocking = radio_led_set, -	.brightness_get = radio_led_get, -	.default_trigger = "rfkill-any" -}; -  static int eco_led_set(struct led_classdev *cdev,  		       enum led_brightness brightness)  { +	struct acpi_device *device = to_acpi_device(cdev->dev->parent);  	int curr; -	curr = call_fext_func(FUNC_LEDS, 0x2, ECO_LED, 0x0); +	curr = call_fext_func(device, FUNC_LEDS, 0x2, ECO_LED, 0x0);  	if (brightness >= LED_FULL) -		return call_fext_func(FUNC_LEDS, 0x1, ECO_LED, +		return call_fext_func(device, FUNC_LEDS, 0x1, ECO_LED,  				      curr | ECO_LED_ON);  	else -		return call_fext_func(FUNC_LEDS, 0x1, ECO_LED, +		return call_fext_func(device, FUNC_LEDS, 0x1, ECO_LED,  				      curr & ~ECO_LED_ON);  }  static enum led_brightness eco_led_get(struct led_classdev *cdev)  { +	struct acpi_device *device = to_acpi_device(cdev->dev->parent);  	enum led_brightness brightness = LED_OFF; -	if (call_fext_func(FUNC_LEDS, 0x2, ECO_LED, 0x0) & ECO_LED_ON) +	if (call_fext_func(device, FUNC_LEDS, 0x2, ECO_LED, 0x0) & ECO_LED_ON)  		brightness = LED_FULL;  	return brightness;  } -static struct led_classdev eco_led = { -	.name = "fujitsu::eco_led", -	.brightness_set_blocking = eco_led_set, -	.brightness_get = eco_led_get -}; -  static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device)  { +	struct led_classdev *led;  	int result; -	if (call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & LOGOLAMP_POWERON) { -		result = devm_led_classdev_register(&device->dev, -						    &logolamp_led); +	if (call_fext_func(device, +			   FUNC_LEDS, 0x0, 0x0, 0x0) & LOGOLAMP_POWERON) { +		led = devm_kzalloc(&device->dev, sizeof(*led), GFP_KERNEL); +		led->name = "fujitsu::logolamp"; +		led->brightness_set_blocking = logolamp_set; +		led->brightness_get = logolamp_get; +		result = devm_led_classdev_register(&device->dev, led);  		if (result)  			return result;  	} -	if ((call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & KEYBOARD_LAMPS) && -	    (call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0) == 0x0)) { -		result = devm_led_classdev_register(&device->dev, &kblamps_led); +	if ((call_fext_func(device, +			    FUNC_LEDS, 0x0, 0x0, 0x0) & KEYBOARD_LAMPS) && +	    (call_fext_func(device, FUNC_BUTTONS, 0x0, 0x0, 0x0) == 0x0)) { +		led = devm_kzalloc(&device->dev, sizeof(*led), GFP_KERNEL); +		led->name = "fujitsu::kblamps"; +		led->brightness_set_blocking = kblamps_set; +		led->brightness_get = kblamps_get; +		result = devm_led_classdev_register(&device->dev, led);  		if (result)  			return result;  	} @@ -746,8 +721,13 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device)  	 * to also have an RF LED.  Therefore use bit 24 as an indicator  	 * that an RF LED is present.  	 */ -	if (call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0) & BIT(24)) { -		result = devm_led_classdev_register(&device->dev, &radio_led); +	if (call_fext_func(device, FUNC_BUTTONS, 0x0, 0x0, 0x0) & BIT(24)) { +		led = devm_kzalloc(&device->dev, sizeof(*led), GFP_KERNEL); +		led->name = "fujitsu::radio_led"; +		led->brightness_set_blocking = radio_led_set; +		led->brightness_get = radio_led_get; +		led->default_trigger = "rfkill-any"; +		result = devm_led_classdev_register(&device->dev, led);  		if (result)  			return result;  	} @@ -757,9 +737,14 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device)  	 * bit 14 seems to indicate presence of said led as well.  	 * Confirm by testing the status.  	 */ -	if ((call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & BIT(14)) && -	    (call_fext_func(FUNC_LEDS, 0x2, ECO_LED, 0x0) != UNSUPPORTED_CMD)) { -		result = devm_led_classdev_register(&device->dev, &eco_led); +	if ((call_fext_func(device, FUNC_LEDS, 0x0, 0x0, 0x0) & BIT(14)) && +	    (call_fext_func(device, +			    FUNC_LEDS, 0x2, ECO_LED, 0x0) != UNSUPPORTED_CMD)) { +		led = devm_kzalloc(&device->dev, sizeof(*led), GFP_KERNEL); +		led->name = "fujitsu::eco_led"; +		led->brightness_set_blocking = eco_led_set; +		led->brightness_get = eco_led_get; +		result = devm_led_classdev_register(&device->dev, led);  		if (result)  			return result;  	} @@ -769,23 +754,25 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device)  static int acpi_fujitsu_laptop_add(struct acpi_device *device)  { -	int state = 0; +	struct fujitsu_laptop *priv;  	int error;  	int i; -	if (!device) -		return -EINVAL; +	priv = devm_kzalloc(&device->dev, sizeof(*priv), GFP_KERNEL); +	if (!priv) +		return -ENOMEM; + +	WARN_ONCE(fext, "More than one FUJ02E3 ACPI device was found.  Driver may not work as intended."); +	fext = device; -	fujitsu_laptop->acpi_handle = device->handle; -	sprintf(acpi_device_name(device), "%s", -		ACPI_FUJITSU_LAPTOP_DEVICE_NAME); -	sprintf(acpi_device_class(device), "%s", ACPI_FUJITSU_CLASS); -	device->driver_data = fujitsu_laptop; +	strcpy(acpi_device_name(device), ACPI_FUJITSU_LAPTOP_DEVICE_NAME); +	strcpy(acpi_device_class(device), ACPI_FUJITSU_CLASS); +	device->driver_data = priv;  	/* kfifo */ -	spin_lock_init(&fujitsu_laptop->fifo_lock); -	error = kfifo_alloc(&fujitsu_laptop->fifo, RINGBUFFERSIZE * sizeof(int), -			GFP_KERNEL); +	spin_lock_init(&priv->fifo_lock); +	error = kfifo_alloc(&priv->fifo, RINGBUFFERSIZE * sizeof(int), +			    GFP_KERNEL);  	if (error) {  		pr_err("kfifo_alloc failed\n");  		goto err_stop; @@ -795,51 +782,36 @@ static int acpi_fujitsu_laptop_add(struct acpi_device *device)  	if (error)  		goto err_free_fifo; -	error = acpi_bus_update_power(fujitsu_laptop->acpi_handle, &state); -	if (error) { -		pr_err("Error reading power state\n"); -		goto err_free_fifo; -	} - -	pr_info("ACPI: %s [%s] (%s)\n", -		acpi_device_name(device), acpi_device_bid(device), -		!device->power.state ? "on" : "off"); - -	fujitsu_laptop->dev = device; - -	if (acpi_has_method(device->handle, METHOD_NAME__INI)) { -		vdbg_printk(FUJLAPTOP_DBG_INFO, "Invoking _INI\n"); -		if (ACPI_FAILURE -		    (acpi_evaluate_object -		     (device->handle, METHOD_NAME__INI, NULL, NULL))) -			pr_err("_INI Method failed\n"); -	} +	pr_info("ACPI: %s [%s]\n", +		acpi_device_name(device), acpi_device_bid(device));  	i = 0; -	while (call_fext_func(FUNC_BUTTONS, 0x1, 0x0, 0x0) != 0 +	while (call_fext_func(device, FUNC_BUTTONS, 0x1, 0x0, 0x0) != 0  		&& (i++) < MAX_HOTKEY_RINGBUFFER_SIZE)  		; /* No action, result is discarded */ -	vdbg_printk(FUJLAPTOP_DBG_INFO, "Discarded %i ringbuffer entries\n", i); +	acpi_handle_debug(device->handle, "Discarded %i ringbuffer entries\n", +			  i); -	fujitsu_laptop->flags_supported = -		call_fext_func(FUNC_FLAGS, 0x0, 0x0, 0x0); +	priv->flags_supported = call_fext_func(device, FUNC_FLAGS, 0x0, 0x0, +					       0x0);  	/* Make sure our bitmask of supported functions is cleared if the  	   RFKILL function block is not implemented, like on the S7020. */ -	if (fujitsu_laptop->flags_supported == UNSUPPORTED_CMD) -		fujitsu_laptop->flags_supported = 0; +	if (priv->flags_supported == UNSUPPORTED_CMD) +		priv->flags_supported = 0; -	if (fujitsu_laptop->flags_supported) -		fujitsu_laptop->flags_state = -			call_fext_func(FUNC_FLAGS, 0x4, 0x0, 0x0); +	if (priv->flags_supported) +		priv->flags_state = call_fext_func(device, FUNC_FLAGS, 0x4, 0x0, +						   0x0);  	/* Suspect this is a keymap of the application panel, print it */ -	pr_info("BTNI: [0x%x]\n", call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0)); +	acpi_handle_info(device->handle, "BTNI: [0x%x]\n", +			 call_fext_func(device, FUNC_BUTTONS, 0x0, 0x0, 0x0));  	/* Sync backlight power status */ -	if (fujitsu_bl->bl_device && +	if (fujitsu_bl && fujitsu_bl->bl_device &&  	    acpi_video_get_backlight_type() == acpi_backlight_vendor) { -		if (call_fext_func(FUNC_BACKLIGHT, 0x2, 0x4, 0x0) == 3) +		if (call_fext_func(fext, FUNC_BACKLIGHT, 0x2, 0x4, 0x0) == 3)  			fujitsu_bl->bl_device->props.power = FB_BLANK_POWERDOWN;  		else  			fujitsu_bl->bl_device->props.power = FB_BLANK_UNBLANK; @@ -849,103 +821,100 @@ static int acpi_fujitsu_laptop_add(struct acpi_device *device)  	if (error)  		goto err_free_fifo; -	error = fujitsu_laptop_platform_add(); +	error = fujitsu_laptop_platform_add(device);  	if (error)  		goto err_free_fifo;  	return 0;  err_free_fifo: -	kfifo_free(&fujitsu_laptop->fifo); +	kfifo_free(&priv->fifo);  err_stop:  	return error;  }  static int acpi_fujitsu_laptop_remove(struct acpi_device *device)  { -	struct fujitsu_laptop *fujitsu_laptop = acpi_driver_data(device); +	struct fujitsu_laptop *priv = acpi_driver_data(device); -	fujitsu_laptop_platform_remove(); +	fujitsu_laptop_platform_remove(device); -	kfifo_free(&fujitsu_laptop->fifo); +	kfifo_free(&priv->fifo);  	return 0;  } -static void acpi_fujitsu_laptop_press(int scancode) +static void acpi_fujitsu_laptop_press(struct acpi_device *device, int scancode)  { -	struct input_dev *input = fujitsu_laptop->input; +	struct fujitsu_laptop *priv = acpi_driver_data(device);  	int status; -	status = kfifo_in_locked(&fujitsu_laptop->fifo, -				 (unsigned char *)&scancode, sizeof(scancode), -				 &fujitsu_laptop->fifo_lock); +	status = kfifo_in_locked(&priv->fifo, (unsigned char *)&scancode, +				 sizeof(scancode), &priv->fifo_lock);  	if (status != sizeof(scancode)) { -		vdbg_printk(FUJLAPTOP_DBG_WARN, -			    "Could not push scancode [0x%x]\n", scancode); +		dev_info(&priv->input->dev, "Could not push scancode [0x%x]\n", +			 scancode);  		return;  	} -	sparse_keymap_report_event(input, scancode, 1, false); -	vdbg_printk(FUJLAPTOP_DBG_TRACE, -		    "Push scancode into ringbuffer [0x%x]\n", scancode); +	sparse_keymap_report_event(priv->input, scancode, 1, false); +	dev_dbg(&priv->input->dev, "Push scancode into ringbuffer [0x%x]\n", +		scancode);  } -static void acpi_fujitsu_laptop_release(void) +static void acpi_fujitsu_laptop_release(struct acpi_device *device)  { -	struct input_dev *input = fujitsu_laptop->input; +	struct fujitsu_laptop *priv = acpi_driver_data(device);  	int scancode, status;  	while (true) { -		status = kfifo_out_locked(&fujitsu_laptop->fifo, +		status = kfifo_out_locked(&priv->fifo,  					  (unsigned char *)&scancode, -					  sizeof(scancode), -					  &fujitsu_laptop->fifo_lock); +					  sizeof(scancode), &priv->fifo_lock);  		if (status != sizeof(scancode))  			return; -		sparse_keymap_report_event(input, scancode, 0, false); -		vdbg_printk(FUJLAPTOP_DBG_TRACE, -			    "Pop scancode from ringbuffer [0x%x]\n", scancode); +		sparse_keymap_report_event(priv->input, scancode, 0, false); +		dev_dbg(&priv->input->dev, +			"Pop scancode from ringbuffer [0x%x]\n", scancode);  	}  }  static void acpi_fujitsu_laptop_notify(struct acpi_device *device, u32 event)  { -	struct input_dev *input; +	struct fujitsu_laptop *priv = acpi_driver_data(device);  	int scancode, i = 0;  	unsigned int irb; -	input = fujitsu_laptop->input; -  	if (event != ACPI_FUJITSU_NOTIFY_CODE1) { -		vdbg_printk(FUJLAPTOP_DBG_WARN, -			    "Unsupported event [0x%x]\n", event); -		sparse_keymap_report_event(input, -1, 1, true); +		acpi_handle_info(device->handle, "Unsupported event [0x%x]\n", +				 event); +		sparse_keymap_report_event(priv->input, -1, 1, true);  		return;  	} -	if (fujitsu_laptop->flags_supported) -		fujitsu_laptop->flags_state = -			call_fext_func(FUNC_FLAGS, 0x4, 0x0, 0x0); +	if (priv->flags_supported) +		priv->flags_state = call_fext_func(device, FUNC_FLAGS, 0x4, 0x0, +						   0x0); -	while ((irb = call_fext_func(FUNC_BUTTONS, 0x1, 0x0, 0x0)) != 0 && +	while ((irb = call_fext_func(device, +				     FUNC_BUTTONS, 0x1, 0x0, 0x0)) != 0 &&  	       i++ < MAX_HOTKEY_RINGBUFFER_SIZE) {  		scancode = irb & 0x4ff; -		if (sparse_keymap_entry_from_scancode(input, scancode)) -			acpi_fujitsu_laptop_press(scancode); +		if (sparse_keymap_entry_from_scancode(priv->input, scancode)) +			acpi_fujitsu_laptop_press(device, scancode);  		else if (scancode == 0) -			acpi_fujitsu_laptop_release(); +			acpi_fujitsu_laptop_release(device);  		else -			vdbg_printk(FUJLAPTOP_DBG_WARN, -				    "Unknown GIRB result [%x]\n", irb); +			acpi_handle_info(device->handle, +					 "Unknown GIRB result [%x]\n", irb);  	}  	/* On some models (first seen on the Skylake-based Lifebook  	 * E736/E746/E756), the touchpad toggle hotkey (Fn+F4) is  	 * handled in software; its state is queried using FUNC_FLAGS  	 */ -	if ((fujitsu_laptop->flags_supported & BIT(26)) && -	    (call_fext_func(FUNC_FLAGS, 0x1, 0x0, 0x0) & BIT(26))) -		sparse_keymap_report_event(input, BIT(26), 1, true); +	if ((priv->flags_supported & BIT(26)) && +	    (call_fext_func(device, FUNC_FLAGS, 0x1, 0x0, 0x0) & BIT(26))) +		sparse_keymap_report_event(priv->input, BIT(26), 1, true);  }  /* Initialization */ @@ -992,16 +961,9 @@ static int __init fujitsu_init(void)  {  	int ret; -	if (acpi_disabled) -		return -ENODEV; - -	fujitsu_bl = kzalloc(sizeof(struct fujitsu_bl), GFP_KERNEL); -	if (!fujitsu_bl) -		return -ENOMEM; -  	ret = acpi_bus_register_driver(&acpi_fujitsu_bl_driver);  	if (ret) -		goto err_free_fujitsu_bl; +		return ret;  	/* Register platform stuff */ @@ -1011,28 +973,18 @@ static int __init fujitsu_init(void)  	/* Register laptop driver */ -	fujitsu_laptop = kzalloc(sizeof(struct fujitsu_laptop), GFP_KERNEL); -	if (!fujitsu_laptop) { -		ret = -ENOMEM; -		goto err_unregister_platform_driver; -	} -  	ret = acpi_bus_register_driver(&acpi_fujitsu_laptop_driver);  	if (ret) -		goto err_free_fujitsu_laptop; +		goto err_unregister_platform_driver;  	pr_info("driver " FUJITSU_DRIVER_VERSION " successfully loaded\n");  	return 0; -err_free_fujitsu_laptop: -	kfree(fujitsu_laptop);  err_unregister_platform_driver:  	platform_driver_unregister(&fujitsu_pf_driver);  err_unregister_acpi:  	acpi_bus_unregister_driver(&acpi_fujitsu_bl_driver); -err_free_fujitsu_bl: -	kfree(fujitsu_bl);  	return ret;  } @@ -1041,14 +993,10 @@ static void __exit fujitsu_cleanup(void)  {  	acpi_bus_unregister_driver(&acpi_fujitsu_laptop_driver); -	kfree(fujitsu_laptop); -  	platform_driver_unregister(&fujitsu_pf_driver);  	acpi_bus_unregister_driver(&acpi_fujitsu_bl_driver); -	kfree(fujitsu_bl); -  	pr_info("driver unloaded\n");  } @@ -1059,10 +1007,6 @@ module_param(use_alt_lcd_levels, int, 0644);  MODULE_PARM_DESC(use_alt_lcd_levels, "Interface used for setting LCD brightness level (-1 = auto, 0 = force SBLL, 1 = force SBL2)");  module_param(disable_brightness_adjust, bool, 0644);  MODULE_PARM_DESC(disable_brightness_adjust, "Disable LCD brightness adjustment"); -#ifdef CONFIG_FUJITSU_LAPTOP_DEBUG -module_param_named(debug, dbg_level, uint, 0644); -MODULE_PARM_DESC(debug, "Sets debug level bit-mask"); -#endif  MODULE_AUTHOR("Jonathan Woithe, Peter Gruber, Tony Vroon");  MODULE_DESCRIPTION("Fujitsu laptop extras support"); diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index 24ca9fbe31cc..527e5d9ab9bf 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -423,9 +423,43 @@ static ssize_t store_ideapad_fan(struct device *dev,  static DEVICE_ATTR(fan_mode, 0644, show_ideapad_fan, store_ideapad_fan); +static ssize_t touchpad_show(struct device *dev, +			     struct device_attribute *attr, +			     char *buf) +{ +	struct ideapad_private *priv = dev_get_drvdata(dev); +	unsigned long result; + +	if (read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &result)) +		return sprintf(buf, "-1\n"); +	return sprintf(buf, "%lu\n", result); +} + +/* Switch to RO for now: It might be revisited in the future */ +static ssize_t __maybe_unused touchpad_store(struct device *dev, +					     struct device_attribute *attr, +					     const char *buf, size_t count) +{ +	struct ideapad_private *priv = dev_get_drvdata(dev); +	bool state; +	int ret; + +	ret = kstrtobool(buf, &state); +	if (ret) +		return ret; + +	ret = write_ec_cmd(priv->adev->handle, VPCCMD_W_TOUCHPAD, state); +	if (ret < 0) +		return -EIO; +	return count; +} + +static DEVICE_ATTR_RO(touchpad); +  static struct attribute *ideapad_attributes[] = {  	&dev_attr_camera_power.attr,  	&dev_attr_fan_mode.attr, +	&dev_attr_touchpad.attr,  	NULL  }; @@ -478,7 +512,7 @@ static int ideapad_rfk_set(void *data, bool blocked)  	return write_ec_cmd(priv->priv->adev->handle, opcode, !blocked);  } -static struct rfkill_ops ideapad_rfk_ops = { +static const struct rfkill_ops ideapad_rfk_ops = {  	.set_block = ideapad_rfk_set,  }; @@ -810,7 +844,6 @@ static void ideapad_acpi_notify(acpi_handle handle, u32 event, void *data)  			case 8:  			case 7:  			case 6: -			case 1:  				ideapad_input_report(priv, vpc_bit);  				break;  			case 5: @@ -828,6 +861,13 @@ static void ideapad_acpi_notify(acpi_handle handle, u32 event, void *data)  			case 0:  				ideapad_check_special_buttons(priv);  				break; +			case 1: +				/* Some IdeaPads report event 1 every ~20 +				 * seconds while on battery power; some +				 * report this when changing to/from tablet +				 * mode. Squelch this event. +				 */ +				break;  			default:  				pr_info("Unknown event: %lu\n", vpc_bit);  			} @@ -911,6 +951,20 @@ static const struct dmi_system_id no_hw_rfkill_list[] = {  		},  	},  	{ +		.ident = "Lenovo Legion Y520-15IKBN", +		.matches = { +			DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), +			DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Y520-15IKBN"), +		}, +	}, +	{ +		.ident = "Lenovo Legion Y720-15IKBN", +		.matches = { +			DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), +			DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Y720-15IKBN"), +		}, +	}, +	{  		.ident = "Lenovo Yoga 2 11 / 13 / Pro",  		.matches = {  			DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), diff --git a/drivers/platform/x86/intel-hid.c b/drivers/platform/x86/intel-hid.c index 63ba2cbd04c2..8519e0f97bdd 100644 --- a/drivers/platform/x86/intel-hid.c +++ b/drivers/platform/x86/intel-hid.c @@ -23,6 +23,7 @@  #include <linux/platform_device.h>  #include <linux/input/sparse-keymap.h>  #include <linux/acpi.h> +#include <linux/suspend.h>  #include <acpi/acpi_bus.h>  MODULE_LICENSE("GPL"); @@ -75,6 +76,7 @@ static const struct key_entry intel_array_keymap[] = {  struct intel_hid_priv {  	struct input_dev *input_dev;  	struct input_dev *array; +	bool wakeup_mode;  };  static int intel_hid_set_enable(struct device *device, bool enable) @@ -116,23 +118,37 @@ static void intel_button_array_enable(struct device *device, bool enable)  		dev_warn(device, "failed to set button capability\n");  } -static int intel_hid_pl_suspend_handler(struct device *device) +static int intel_hid_pm_prepare(struct device *device)  { -	intel_hid_set_enable(device, false); -	intel_button_array_enable(device, false); +	struct intel_hid_priv *priv = dev_get_drvdata(device); + +	priv->wakeup_mode = true; +	return 0; +} +static int intel_hid_pl_suspend_handler(struct device *device) +{ +	if (pm_suspend_via_firmware()) { +		intel_hid_set_enable(device, false); +		intel_button_array_enable(device, false); +	}  	return 0;  }  static int intel_hid_pl_resume_handler(struct device *device)  { -	intel_hid_set_enable(device, true); -	intel_button_array_enable(device, true); +	struct intel_hid_priv *priv = dev_get_drvdata(device); +	priv->wakeup_mode = false; +	if (pm_resume_via_firmware()) { +		intel_hid_set_enable(device, true); +		intel_button_array_enable(device, true); +	}  	return 0;  }  static const struct dev_pm_ops intel_hid_pl_pm_ops = { +	.prepare = intel_hid_pm_prepare,  	.freeze  = intel_hid_pl_suspend_handler,  	.thaw  = intel_hid_pl_resume_handler,  	.restore  = intel_hid_pl_resume_handler, @@ -186,6 +202,19 @@ static void notify_handler(acpi_handle handle, u32 event, void *context)  	unsigned long long ev_index;  	acpi_status status; +	if (priv->wakeup_mode) { +		/* Wake up on 5-button array events only. */ +		if (event == 0xc0 || !priv->array) +			return; + +		if (sparse_keymap_entry_from_scancode(priv->array, event)) +			pm_wakeup_hard_event(&device->dev); +		else +			dev_info(&device->dev, "unknown event 0x%x\n", event); + +		return; +	} +  	/* 0xC0 is for HID events, other values are for 5 button array */  	if (event != 0xc0) {  		if (!priv->array || @@ -270,6 +299,7 @@ static int intel_hid_probe(struct platform_device *device)  				 "failed to enable HID power button\n");  	} +	device_init_wakeup(&device->dev, true);  	return 0;  err_remove_notify: diff --git a/drivers/platform/x86/intel-vbtn.c b/drivers/platform/x86/intel-vbtn.c index c2035e121ac2..61f106377661 100644 --- a/drivers/platform/x86/intel-vbtn.c +++ b/drivers/platform/x86/intel-vbtn.c @@ -23,6 +23,7 @@  #include <linux/platform_device.h>  #include <linux/input/sparse-keymap.h>  #include <linux/acpi.h> +#include <linux/suspend.h>  #include <acpi/acpi_bus.h>  MODULE_LICENSE("GPL"); @@ -46,6 +47,7 @@ static const struct key_entry intel_vbtn_keymap[] = {  struct intel_vbtn_priv {  	struct input_dev *input_dev; +	bool wakeup_mode;  };  static int intel_vbtn_input_setup(struct platform_device *device) @@ -73,9 +75,15 @@ static void notify_handler(acpi_handle handle, u32 event, void *context)  	struct platform_device *device = context;  	struct intel_vbtn_priv *priv = dev_get_drvdata(&device->dev); -	if (!sparse_keymap_report_event(priv->input_dev, event, 1, true)) -		dev_info(&device->dev, "unknown event index 0x%x\n", -			 event); +	if (priv->wakeup_mode) { +		if (sparse_keymap_entry_from_scancode(priv->input_dev, event)) { +			pm_wakeup_hard_event(&device->dev); +			return; +		} +	} else if (sparse_keymap_report_event(priv->input_dev, event, 1, true)) { +		return; +	} +	dev_info(&device->dev, "unknown event index 0x%x\n", event);  }  static int intel_vbtn_probe(struct platform_device *device) @@ -109,6 +117,7 @@ static int intel_vbtn_probe(struct platform_device *device)  	if (ACPI_FAILURE(status))  		return -EBUSY; +	device_init_wakeup(&device->dev, true);  	return 0;  } @@ -125,10 +134,34 @@ static int intel_vbtn_remove(struct platform_device *device)  	return 0;  } +static int intel_vbtn_pm_prepare(struct device *dev) +{ +	struct intel_vbtn_priv *priv = dev_get_drvdata(dev); + +	priv->wakeup_mode = true; +	return 0; +} + +static int intel_vbtn_pm_resume(struct device *dev) +{ +	struct intel_vbtn_priv *priv = dev_get_drvdata(dev); + +	priv->wakeup_mode = false; +	return 0; +} + +static const struct dev_pm_ops intel_vbtn_pm_ops = { +	.prepare = intel_vbtn_pm_prepare, +	.resume = intel_vbtn_pm_resume, +	.restore = intel_vbtn_pm_resume, +	.thaw = intel_vbtn_pm_resume, +}; +  static struct platform_driver intel_vbtn_pl_driver = {  	.driver = {  		.name = "intel-vbtn",  		.acpi_match_table = intel_vbtn_ids, +		.pm = &intel_vbtn_pm_ops,  	},  	.probe = intel_vbtn_probe,  	.remove = intel_vbtn_remove, diff --git a/drivers/platform/x86/intel_bxtwc_tmu.c b/drivers/platform/x86/intel_bxtwc_tmu.c index e202abd5b0df..ea865d4ca220 100644 --- a/drivers/platform/x86/intel_bxtwc_tmu.c +++ b/drivers/platform/x86/intel_bxtwc_tmu.c @@ -92,10 +92,6 @@ static int bxt_wcove_tmu_probe(struct platform_device *pdev)  	}  	wctmu->irq = virq; -	/* Enable TMU interrupts */ -	regmap_update_bits(wctmu->regmap, BXTWC_MIRQLVL1, -				  BXTWC_MIRQLVL1_MTMU, 0); -  	/* Unmask TMU second level Wake & System alarm */  	regmap_update_bits(wctmu->regmap, BXTWC_MTMUIRQ_REG,  				  BXTWC_TMU_ALRM_MASK, 0); diff --git a/drivers/platform/x86/intel_cht_int33fe.c b/drivers/platform/x86/intel_cht_int33fe.c index 6a1b2ca5b6fe..da706e2c4232 100644 --- a/drivers/platform/x86/intel_cht_int33fe.c +++ b/drivers/platform/x86/intel_cht_int33fe.c @@ -34,6 +34,13 @@ struct cht_int33fe_data {  	struct i2c_client *pi3usb30532;  }; +static const char * const max17047_suppliers[] = { "bq24190-charger" }; + +static const struct property_entry max17047_props[] = { +	PROPERTY_ENTRY_STRING_ARRAY("supplied-from", max17047_suppliers), +	{ } +}; +  static int cht_int33fe_probe(struct i2c_client *client)  {  	struct device *dev = &client->dev; @@ -70,6 +77,7 @@ static int cht_int33fe_probe(struct i2c_client *client)  	memset(&board_info, 0, sizeof(board_info));  	strlcpy(board_info.type, "max17047", I2C_NAME_SIZE); +	board_info.properties = max17047_props;  	data->max17047 = i2c_acpi_new_device(dev, 1, &board_info);  	if (!data->max17047) diff --git a/drivers/platform/x86/intel_int0002_vgpio.c b/drivers/platform/x86/intel_int0002_vgpio.c new file mode 100644 index 000000000000..92dc230ef5b2 --- /dev/null +++ b/drivers/platform/x86/intel_int0002_vgpio.c @@ -0,0 +1,219 @@ +/* + * Intel INT0002 "Virtual GPIO" driver + * + * Copyright (C) 2017 Hans de Goede <[email protected]> + * + * Loosely based on android x86 kernel code which is: + * + * Copyright (c) 2014, Intel Corporation. + * + * Author: Dyut Kumar Sil <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Some peripherals on Bay Trail and Cherry Trail platforms signal a Power + * Management Event (PME) to the Power Management Controller (PMC) to wakeup + * the system. When this happens software needs to clear the PME bus 0 status + * bit in the GPE0a_STS register to avoid an IRQ storm on IRQ 9. + * + * This is modelled in ACPI through the INT0002 ACPI device, which is + * called a "Virtual GPIO controller" in ACPI because it defines the event + * handler to call when the PME triggers through _AEI and _L02 / _E02 + * methods as would be done for a real GPIO interrupt in ACPI. Note this + * is a hack to define an AML event handler for the PME while using existing + * 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 + * for a real GPIO controller. + */ + +#include <linux/acpi.h> +#include <linux/bitmap.h> +#include <linux/gpio/driver.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/suspend.h> + +#include <asm/cpu_device_id.h> +#include <asm/intel-family.h> + +#define DRV_NAME			"INT0002 Virtual GPIO" + +/* For some reason the virtual GPIO pin tied to the GPE is numbered pin 2 */ +#define GPE0A_PME_B0_VIRT_GPIO_PIN	2 + +#define GPE0A_PME_B0_STS_BIT		BIT(13) +#define GPE0A_PME_B0_EN_BIT		BIT(13) +#define GPE0A_STS_PORT			0x420 +#define GPE0A_EN_PORT			0x428 + +#define ICPU(model)	{ X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, } + +static const struct x86_cpu_id int0002_cpu_ids[] = { +/* + * Limit ourselves to Cherry Trail for now, until testing shows we + * need to handle the INT0002 device on Baytrail too. + *	ICPU(INTEL_FAM6_ATOM_SILVERMONT1),	 * Valleyview, Bay Trail * + */ +	ICPU(INTEL_FAM6_ATOM_AIRMONT),		/* Braswell, Cherry Trail */ +	{} +}; + +/* + * As this is not a real GPIO at all, but just a hack to model an event in + * ACPI the get / set functions are dummy functions. + */ + +static int int0002_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ +	return 0; +} + +static void int0002_gpio_set(struct gpio_chip *chip, unsigned int offset, +			     int value) +{ +} + +static int int0002_gpio_direction_output(struct gpio_chip *chip, +					 unsigned int offset, int value) +{ +	return 0; +} + +static void int0002_irq_ack(struct irq_data *data) +{ +	outl(GPE0A_PME_B0_STS_BIT, GPE0A_STS_PORT); +} + +static void int0002_irq_unmask(struct irq_data *data) +{ +	u32 gpe_en_reg; + +	gpe_en_reg = inl(GPE0A_EN_PORT); +	gpe_en_reg |= GPE0A_PME_B0_EN_BIT; +	outl(gpe_en_reg, GPE0A_EN_PORT); +} + +static void int0002_irq_mask(struct irq_data *data) +{ +	u32 gpe_en_reg; + +	gpe_en_reg = inl(GPE0A_EN_PORT); +	gpe_en_reg &= ~GPE0A_PME_B0_EN_BIT; +	outl(gpe_en_reg, GPE0A_EN_PORT); +} + +static irqreturn_t int0002_irq(int irq, void *data) +{ +	struct gpio_chip *chip = data; +	u32 gpe_sts_reg; + +	gpe_sts_reg = inl(GPE0A_STS_PORT); +	if (!(gpe_sts_reg & GPE0A_PME_B0_STS_BIT)) +		return IRQ_NONE; + +	generic_handle_irq(irq_find_mapping(chip->irqdomain, +					    GPE0A_PME_B0_VIRT_GPIO_PIN)); + +	pm_system_wakeup(); + +	return IRQ_HANDLED; +} + +static struct irq_chip int0002_irqchip = { +	.name			= DRV_NAME, +	.irq_ack		= int0002_irq_ack, +	.irq_mask		= int0002_irq_mask, +	.irq_unmask		= int0002_irq_unmask, +}; + +static int int0002_probe(struct platform_device *pdev) +{ +	struct device *dev = &pdev->dev; +	const struct x86_cpu_id *cpu_id; +	struct gpio_chip *chip; +	int irq, ret; + +	/* Menlow has a different INT0002 device? <sigh> */ +	cpu_id = x86_match_cpu(int0002_cpu_ids); +	if (!cpu_id) +		return -ENODEV; + +	irq = platform_get_irq(pdev, 0); +	if (irq < 0) { +		dev_err(dev, "Error getting IRQ: %d\n", irq); +		return irq; +	} + +	chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); +	if (!chip) +		return -ENOMEM; + +	chip->label = DRV_NAME; +	chip->parent = dev; +	chip->owner = THIS_MODULE; +	chip->get = int0002_gpio_get; +	chip->set = int0002_gpio_set; +	chip->direction_input = int0002_gpio_get; +	chip->direction_output = int0002_gpio_direction_output; +	chip->base = -1; +	chip->ngpio = GPE0A_PME_B0_VIRT_GPIO_PIN + 1; +	chip->irq_need_valid_mask = true; + +	ret = devm_gpiochip_add_data(&pdev->dev, chip, NULL); +	if (ret) { +		dev_err(dev, "Error adding gpio chip: %d\n", ret); +		return ret; +	} + +	bitmap_clear(chip->irq_valid_mask, 0, GPE0A_PME_B0_VIRT_GPIO_PIN); + +	/* +	 * We manually request the irq here instead of passing a flow-handler +	 * to gpiochip_set_chained_irqchip, because the irq is shared. +	 */ +	ret = devm_request_irq(dev, irq, int0002_irq, +			       IRQF_SHARED | IRQF_NO_THREAD, "INT0002", chip); +	if (ret) { +		dev_err(dev, "Error requesting IRQ %d: %d\n", irq, ret); +		return ret; +	} + +	ret = gpiochip_irqchip_add(chip, &int0002_irqchip, 0, handle_edge_irq, +				   IRQ_TYPE_NONE); +	if (ret) { +		dev_err(dev, "Error adding irqchip: %d\n", ret); +		return ret; +	} + +	gpiochip_set_chained_irqchip(chip, &int0002_irqchip, irq, NULL); + +	return 0; +} + +static const struct acpi_device_id int0002_acpi_ids[] = { +	{ "INT0002", 0 }, +	{ }, +}; +MODULE_DEVICE_TABLE(acpi, int0002_acpi_ids); + +static struct platform_driver int0002_driver = { +	.driver = { +		.name			= DRV_NAME, +		.acpi_match_table	= int0002_acpi_ids, +	}, +	.probe	= int0002_probe, +}; + +module_platform_driver(int0002_driver); + +MODULE_AUTHOR("Hans de Goede <[email protected]>"); +MODULE_DESCRIPTION("Intel INT0002 Virtual GPIO driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/intel_menlow.c b/drivers/platform/x86/intel_menlow.c index cbe01021c939..ef9b0af8cdd3 100644 --- a/drivers/platform/x86/intel_menlow.c +++ b/drivers/platform/x86/intel_menlow.c @@ -142,7 +142,7 @@ static int memory_set_cur_bandwidth(struct thermal_cooling_device *cdev,  	return 0;  } -static struct thermal_cooling_device_ops memory_cooling_ops = { +static const struct thermal_cooling_device_ops memory_cooling_ops = {  	.get_max_state = memory_get_max_bandwidth,  	.get_cur_state = memory_get_cur_bandwidth,  	.set_cur_state = memory_set_cur_bandwidth, diff --git a/drivers/platform/x86/intel_pmc_ipc.c b/drivers/platform/x86/intel_pmc_ipc.c index e4d4dfe3e1d1..bb792a52248b 100644 --- a/drivers/platform/x86/intel_pmc_ipc.c +++ b/drivers/platform/x86/intel_pmc_ipc.c @@ -186,7 +186,7 @@ static inline void ipc_data_writel(u32 data, u32 offset)  	writel(data, ipcdev.ipc_base + IPC_WRITE_BUFFER + offset);  } -static inline u8 ipc_data_readb(u32 offset) +static inline u8 __maybe_unused ipc_data_readb(u32 offset)  {  	return readb(ipcdev.ipc_base + IPC_READ_BUFFER + offset);  } diff --git a/drivers/platform/x86/intel_telemetry_debugfs.c b/drivers/platform/x86/intel_telemetry_debugfs.c index ef29f18b1951..4cc2f4ea0a25 100644 --- a/drivers/platform/x86/intel_telemetry_debugfs.c +++ b/drivers/platform/x86/intel_telemetry_debugfs.c @@ -97,11 +97,9 @@  	} \  } -#ifdef CONFIG_PM_SLEEP  static u8 suspend_prep_ok;  static u32 suspend_shlw_ctr_temp, suspend_deep_ctr_temp;  static u64 suspend_shlw_res_temp, suspend_deep_res_temp; -#endif  struct telemetry_susp_stats {  	u32 shlw_swake_ctr; @@ -807,7 +805,6 @@ static const struct file_operations telem_ioss_trc_verb_ops = {  	.release	= single_release,  }; -#ifdef CONFIG_PM_SLEEP  static int pm_suspend_prep_cb(void)  {  	struct telemetry_evtlog evtlog[TELEM_MAX_OS_ALLOCATED_EVENTS]; @@ -937,7 +934,6 @@ static int pm_notification(struct notifier_block *this,  static struct notifier_block pm_notifier = {  	.notifier_call = pm_notification,  }; -#endif /* CONFIG_PM_SLEEP */  static int __init telemetry_debugfs_init(void)  { @@ -960,14 +956,13 @@ static int __init telemetry_debugfs_init(void)  	if (err < 0)  		return -EINVAL; - -#ifdef CONFIG_PM_SLEEP  	register_pm_notifier(&pm_notifier); -#endif /* CONFIG_PM_SLEEP */  	debugfs_conf->telemetry_dbg_dir = debugfs_create_dir("telemetry", NULL); -	if (!debugfs_conf->telemetry_dbg_dir) -		return -ENOMEM; +	if (!debugfs_conf->telemetry_dbg_dir) { +		err = -ENOMEM; +		goto out_pm; +	}  	f = debugfs_create_file("pss_info", S_IFREG | S_IRUGO,  				debugfs_conf->telemetry_dbg_dir, NULL, @@ -1014,6 +1009,8 @@ static int __init telemetry_debugfs_init(void)  out:  	debugfs_remove_recursive(debugfs_conf->telemetry_dbg_dir);  	debugfs_conf->telemetry_dbg_dir = NULL; +out_pm: +	unregister_pm_notifier(&pm_notifier);  	return err;  } @@ -1022,6 +1019,7 @@ static void __exit telemetry_debugfs_exit(void)  {  	debugfs_remove_recursive(debugfs_conf->telemetry_dbg_dir);  	debugfs_conf->telemetry_dbg_dir = NULL; +	unregister_pm_notifier(&pm_notifier);  }  late_initcall(telemetry_debugfs_init); diff --git a/drivers/platform/x86/msi-laptop.c b/drivers/platform/x86/msi-laptop.c index 9e90827c176a..61b9014d2610 100644 --- a/drivers/platform/x86/msi-laptop.c +++ b/drivers/platform/x86/msi-laptop.c @@ -563,11 +563,11 @@ static struct attribute *msipf_old_attributes[] = {  	NULL  }; -static struct attribute_group msipf_attribute_group = { +static const struct attribute_group msipf_attribute_group = {  	.attrs = msipf_attributes  }; -static struct attribute_group msipf_old_attribute_group = { +static const struct attribute_group msipf_old_attribute_group = {  	.attrs = msipf_old_attributes  }; diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c index 975f4e100dbd..76b0a58e205b 100644 --- a/drivers/platform/x86/panasonic-laptop.c +++ b/drivers/platform/x86/panasonic-laptop.c @@ -228,10 +228,6 @@ struct pcc_acpi {  	struct backlight_device	*backlight;  }; -struct pcc_keyinput { -	struct acpi_hotkey      *hotkey; -}; -  /* method access functions */  static int acpi_pcc_write_sset(struct pcc_acpi *pcc, int func, int val)  { diff --git a/drivers/platform/x86/peaq-wmi.c b/drivers/platform/x86/peaq-wmi.c new file mode 100644 index 000000000000..ca75b4dc437e --- /dev/null +++ b/drivers/platform/x86/peaq-wmi.c @@ -0,0 +1,100 @@ +/* + * PEAQ 2-in-1 WMI hotkey driver + * Copyright (C) 2017 Hans de Goede <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/acpi.h> +#include <linux/input-polldev.h> +#include <linux/kernel.h> +#include <linux/module.h> + +#define PEAQ_DOLBY_BUTTON_GUID		"ABBC0F6F-8EA1-11D1-00A0-C90629100000" +#define PEAQ_DOLBY_BUTTON_METHOD_ID	5 +#define PEAQ_POLL_INTERVAL_MS		250 +#define PEAQ_POLL_IGNORE_MS		500 +#define PEAQ_POLL_MAX_MS		1000 + +MODULE_ALIAS("wmi:"PEAQ_DOLBY_BUTTON_GUID); + +static unsigned int peaq_ignore_events_counter; +static struct input_polled_dev *peaq_poll_dev; + +/* + * The Dolby button (yes really a Dolby button) causes an ACPI variable to get + * set on both press and release. The WMI method checks and clears that flag. + * So for a press + release we will get back One from the WMI method either once + * (if polling after the release) or twice (polling between press and release). + * We ignore events for 0.5s after the first event to avoid reporting 2 presses. + */ +static void peaq_wmi_poll(struct input_polled_dev *dev) +{ +	union acpi_object obj; +	acpi_status status; +	u32 dummy = 0; + +	struct acpi_buffer input = { sizeof(dummy), &dummy }; +	struct acpi_buffer output = { sizeof(obj), &obj }; + +	status = wmi_evaluate_method(PEAQ_DOLBY_BUTTON_GUID, 1, +				     PEAQ_DOLBY_BUTTON_METHOD_ID, +				     &input, &output); +	if (ACPI_FAILURE(status)) +		return; + +	if (obj.type != ACPI_TYPE_INTEGER) { +		dev_err(&peaq_poll_dev->input->dev, +			"Error WMBC did not return an integer\n"); +		return; +	} + +	if (peaq_ignore_events_counter && --peaq_ignore_events_counter > 0) +		return; + +	if (obj.integer.value) { +		input_event(peaq_poll_dev->input, EV_KEY, KEY_SOUND, 1); +		input_sync(peaq_poll_dev->input); +		input_event(peaq_poll_dev->input, EV_KEY, KEY_SOUND, 0); +		input_sync(peaq_poll_dev->input); +		peaq_ignore_events_counter = max(1u, +			PEAQ_POLL_IGNORE_MS / peaq_poll_dev->poll_interval); +	} +} + +static int __init peaq_wmi_init(void) +{ +	if (!wmi_has_guid(PEAQ_DOLBY_BUTTON_GUID)) +		return -ENODEV; + +	peaq_poll_dev = input_allocate_polled_device(); +	if (!peaq_poll_dev) +		return -ENOMEM; + +	peaq_poll_dev->poll = peaq_wmi_poll; +	peaq_poll_dev->poll_interval = PEAQ_POLL_INTERVAL_MS; +	peaq_poll_dev->poll_interval_max = PEAQ_POLL_MAX_MS; +	peaq_poll_dev->input->name = "PEAQ WMI hotkeys"; +	peaq_poll_dev->input->phys = "wmi/input0"; +	peaq_poll_dev->input->id.bustype = BUS_HOST; +	input_set_capability(peaq_poll_dev->input, EV_KEY, KEY_SOUND); + +	return input_register_polled_device(peaq_poll_dev); +} + +static void __exit peaq_wmi_exit(void) +{ +	if (!wmi_has_guid(PEAQ_DOLBY_BUTTON_GUID)) +		return; + +	input_unregister_polled_device(peaq_poll_dev); +} + +module_init(peaq_wmi_init); +module_exit(peaq_wmi_exit); + +MODULE_DESCRIPTION("PEAQ 2-in-1 WMI hotkey driver"); +MODULE_AUTHOR("Hans de Goede <[email protected]>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c index 8c146e2b6727..5c4dfe48f03d 100644 --- a/drivers/platform/x86/samsung-laptop.c +++ b/drivers/platform/x86/samsung-laptop.c @@ -591,7 +591,7 @@ static int seclinux_rfkill_set(void *data, bool blocked)  				 !blocked);  } -static struct rfkill_ops seclinux_rfkill_ops = { +static const struct rfkill_ops seclinux_rfkill_ops = {  	.set_block = seclinux_rfkill_set,  }; @@ -651,7 +651,7 @@ static void swsmi_rfkill_query(struct rfkill *rfkill, void *priv)  	rfkill_set_sw_state(rfkill, !ret);  } -static struct rfkill_ops swsmi_rfkill_ops = { +static const struct rfkill_ops swsmi_rfkill_ops = {  	.set_block = swsmi_rfkill_set,  	.query = swsmi_rfkill_query,  }; @@ -1446,9 +1446,9 @@ static int __init samsung_sabi_init(struct samsung_laptop *samsung)  	const struct sabi_config *config = NULL;  	const struct sabi_commands *commands;  	unsigned int ifaceP; +	int loca = 0xffff;  	int ret = 0;  	int i; -	int loca;  	samsung->f0000_segment = ioremap_nocache(0xf0000, 0xffff);  	if (!samsung->f0000_segment) { diff --git a/drivers/platform/x86/silead_dmi.c b/drivers/platform/x86/silead_dmi.c index a3a57d93cf06..3cd3bdfe51df 100644 --- a/drivers/platform/x86/silead_dmi.c +++ b/drivers/platform/x86/silead_dmi.c @@ -80,6 +80,48 @@ static const struct silead_ts_dmi_data surftab_wintron70_st70416_6_data = {  	.properties	= surftab_wintron70_st70416_6_props,  }; +static const struct property_entry gp_electronic_t701_props[] = { +	PROPERTY_ENTRY_U32("touchscreen-size-x", 960), +	PROPERTY_ENTRY_U32("touchscreen-size-y", 640), +	PROPERTY_ENTRY_STRING("firmware-name", +			      "gsl1680-gp-electronic-t701.fw"), +	{ } +}; + +static const struct silead_ts_dmi_data gp_electronic_t701_data = { +	.acpi_name	= "MSSL1680:00", +	.properties	= gp_electronic_t701_props, +}; + +static const struct property_entry pipo_w2s_props[] = { +	PROPERTY_ENTRY_U32("touchscreen-size-x", 1660), +	PROPERTY_ENTRY_U32("touchscreen-size-y", 880), +	PROPERTY_ENTRY_BOOL("touchscreen-inverted-x"), +	PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), +	PROPERTY_ENTRY_STRING("firmware-name", +			      "gsl1680-pipo-w2s.fw"), +	{ } +}; + +static const struct silead_ts_dmi_data pipo_w2s_data = { +	.acpi_name	= "MSSL1680:00", +	.properties	= pipo_w2s_props, +}; + +static const struct property_entry pov_mobii_wintab_p800w_props[] = { +	PROPERTY_ENTRY_U32("touchscreen-size-x", 1800), +	PROPERTY_ENTRY_U32("touchscreen-size-y", 1150), +	PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), +	PROPERTY_ENTRY_STRING("firmware-name", +			      "gsl3692-pov-mobii-wintab-p800w.fw"), +	{ } +}; + +static const struct silead_ts_dmi_data pov_mobii_wintab_p800w_data = { +	.acpi_name	= "MSSL1680:00", +	.properties	= pov_mobii_wintab_p800w_props, +}; +  static const struct dmi_system_id silead_ts_dmi_table[] = {  	{  		/* CUBE iwork8 Air */ @@ -117,6 +159,34 @@ static const struct dmi_system_id silead_ts_dmi_table[] = {  			DMI_MATCH(DMI_BIOS_VERSION, "TREK.G.WI71C.JGBMRBA04"),  		},  	}, +	{ +		/* GP-electronic T701 */ +		.driver_data = (void *)&gp_electronic_t701_data, +		.matches = { +			DMI_MATCH(DMI_SYS_VENDOR, "Insyde"), +			DMI_MATCH(DMI_PRODUCT_NAME, "T701"), +			DMI_MATCH(DMI_BIOS_VERSION, "BYT70A.YNCHENG.WIN.007"), +		}, +	}, +	{ +		/* Pipo W2S */ +		.driver_data = (void *)&pipo_w2s_data, +		.matches = { +			DMI_MATCH(DMI_SYS_VENDOR, "PIPO"), +			DMI_MATCH(DMI_PRODUCT_NAME, "W2S"), +		}, +	}, +	{ +		/* Point of View mobii wintab p800w */ +		.driver_data = (void *)&pov_mobii_wintab_p800w_data, +		.matches = { +			DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"), +			DMI_MATCH(DMI_BOARD_NAME, "Aptio CRB"), +			DMI_MATCH(DMI_BIOS_VERSION, "3BAIR1013"), +			/* Above matches are too generic, add bios-date match */ +			DMI_MATCH(DMI_BIOS_DATE, "08/22/2014"), +		}, +	},  	{ },  }; diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index aa2ee51d3547..bfae79534f44 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -222,7 +222,7 @@ struct sony_laptop_keypress {  /* Correspondance table between sonypi events   * and input layer indexes in the keymap   */ -static int sony_laptop_input_index[] = { +static const int sony_laptop_input_index[] = {  	-1,	/*  0 no event */  	-1,	/*  1 SONYPI_EVENT_JOGDIAL_DOWN */  	-1,	/*  2 SONYPI_EVENT_JOGDIAL_UP */ @@ -4032,7 +4032,7 @@ static struct attribute *spic_attributes[] = {  	NULL  }; -static struct attribute_group spic_attribute_group = { +static const struct attribute_group spic_attribute_group = {  	.attrs = spic_attributes  }; diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 7b6cb0c69b02..b22573131e53 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -590,8 +590,8 @@ static int acpi_evalf(acpi_handle handle,  			break;  			/* add more types as needed */  		default: -			pr_err("acpi_evalf() called " -			       "with invalid format character '%c'\n", c); +			pr_err("acpi_evalf() called with invalid format character '%c'\n", +			       c);  			va_end(ap);  			return 0;  		} @@ -619,8 +619,8 @@ static int acpi_evalf(acpi_handle handle,  		break;  		/* add more types as needed */  	default: -		pr_err("acpi_evalf() called " -		       "with invalid format character '%c'\n", res_type); +		pr_err("acpi_evalf() called with invalid format character '%c'\n", +		       res_type);  		return 0;  	} @@ -790,8 +790,8 @@ static int __init setup_acpi_notify(struct ibm_struct *ibm)  			ibm->acpi->type, dispatch_acpi_notify, ibm);  	if (ACPI_FAILURE(status)) {  		if (status == AE_ALREADY_EXISTS) { -			pr_notice("another device driver is already " -				  "handling %s events\n", ibm->name); +			pr_notice("another device driver is already handling %s events\n", +				  ibm->name);  		} else {  			pr_err("acpi_install_notify_handler(%s) failed: %s\n",  			       ibm->name, acpi_format_exception(status)); @@ -1095,8 +1095,7 @@ static void printk_deprecated_attribute(const char * const what,  					const char * const details)  {  	tpacpi_log_usertask("deprecated sysfs attribute"); -	pr_warn("WARNING: sysfs attribute %s is deprecated and " -		"will be removed. %s\n", +	pr_warn("WARNING: sysfs attribute %s is deprecated and will be removed. %s\n",  		what, details);  } @@ -1438,25 +1437,20 @@ static int tpacpi_rfk_procfs_write(const enum tpacpi_rfk_id id, char *buf)   */  /* interface_version --------------------------------------------------- */ -static ssize_t tpacpi_driver_interface_version_show( -				struct device_driver *drv, -				char *buf) +static ssize_t interface_version_show(struct device_driver *drv, char *buf)  {  	return snprintf(buf, PAGE_SIZE, "0x%08x\n", TPACPI_SYSFS_VERSION);  } - -static DRIVER_ATTR(interface_version, S_IRUGO, -		tpacpi_driver_interface_version_show, NULL); +static DRIVER_ATTR_RO(interface_version);  /* debug_level --------------------------------------------------------- */ -static ssize_t tpacpi_driver_debug_show(struct device_driver *drv, -						char *buf) +static ssize_t debug_level_show(struct device_driver *drv, char *buf)  {  	return snprintf(buf, PAGE_SIZE, "0x%04x\n", dbg_level);  } -static ssize_t tpacpi_driver_debug_store(struct device_driver *drv, -						const char *buf, size_t count) +static ssize_t debug_level_store(struct device_driver *drv, const char *buf, +				 size_t count)  {  	unsigned long t; @@ -1467,34 +1461,28 @@ static ssize_t tpacpi_driver_debug_store(struct device_driver *drv,  	return count;  } - -static DRIVER_ATTR(debug_level, S_IWUSR | S_IRUGO, -		tpacpi_driver_debug_show, tpacpi_driver_debug_store); +static DRIVER_ATTR_RW(debug_level);  /* version ------------------------------------------------------------- */ -static ssize_t tpacpi_driver_version_show(struct device_driver *drv, -						char *buf) +static ssize_t version_show(struct device_driver *drv, char *buf)  {  	return snprintf(buf, PAGE_SIZE, "%s v%s\n",  			TPACPI_DESC, TPACPI_VERSION);  } - -static DRIVER_ATTR(version, S_IRUGO, -		tpacpi_driver_version_show, NULL); +static DRIVER_ATTR_RO(version);  /* --------------------------------------------------------------------- */  #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES  /* wlsw_emulstate ------------------------------------------------------ */ -static ssize_t tpacpi_driver_wlsw_emulstate_show(struct device_driver *drv, -						char *buf) +static ssize_t wlsw_emulstate_show(struct device_driver *drv, char *buf)  {  	return snprintf(buf, PAGE_SIZE, "%d\n", !!tpacpi_wlsw_emulstate);  } -static ssize_t tpacpi_driver_wlsw_emulstate_store(struct device_driver *drv, -						const char *buf, size_t count) +static ssize_t wlsw_emulstate_store(struct device_driver *drv, const char *buf, +				    size_t count)  {  	unsigned long t; @@ -1508,22 +1496,16 @@ static ssize_t tpacpi_driver_wlsw_emulstate_store(struct device_driver *drv,  	return count;  } - -static DRIVER_ATTR(wlsw_emulstate, S_IWUSR | S_IRUGO, -		tpacpi_driver_wlsw_emulstate_show, -		tpacpi_driver_wlsw_emulstate_store); +static DRIVER_ATTR_RW(wlsw_emulstate);  /* bluetooth_emulstate ------------------------------------------------- */ -static ssize_t tpacpi_driver_bluetooth_emulstate_show( -					struct device_driver *drv, -					char *buf) +static ssize_t bluetooth_emulstate_show(struct device_driver *drv, char *buf)  {  	return snprintf(buf, PAGE_SIZE, "%d\n", !!tpacpi_bluetooth_emulstate);  } -static ssize_t tpacpi_driver_bluetooth_emulstate_store( -					struct device_driver *drv, -					const char *buf, size_t count) +static ssize_t bluetooth_emulstate_store(struct device_driver *drv, +					 const char *buf, size_t count)  {  	unsigned long t; @@ -1534,22 +1516,16 @@ static ssize_t tpacpi_driver_bluetooth_emulstate_store(  	return count;  } - -static DRIVER_ATTR(bluetooth_emulstate, S_IWUSR | S_IRUGO, -		tpacpi_driver_bluetooth_emulstate_show, -		tpacpi_driver_bluetooth_emulstate_store); +static DRIVER_ATTR_RW(bluetooth_emulstate);  /* wwan_emulstate ------------------------------------------------- */ -static ssize_t tpacpi_driver_wwan_emulstate_show( -					struct device_driver *drv, -					char *buf) +static ssize_t wwan_emulstate_show(struct device_driver *drv, char *buf)  {  	return snprintf(buf, PAGE_SIZE, "%d\n", !!tpacpi_wwan_emulstate);  } -static ssize_t tpacpi_driver_wwan_emulstate_store( -					struct device_driver *drv, -					const char *buf, size_t count) +static ssize_t wwan_emulstate_store(struct device_driver *drv, const char *buf, +				    size_t count)  {  	unsigned long t; @@ -1560,22 +1536,16 @@ static ssize_t tpacpi_driver_wwan_emulstate_store(  	return count;  } - -static DRIVER_ATTR(wwan_emulstate, S_IWUSR | S_IRUGO, -		tpacpi_driver_wwan_emulstate_show, -		tpacpi_driver_wwan_emulstate_store); +static DRIVER_ATTR_RW(wwan_emulstate);  /* uwb_emulstate ------------------------------------------------- */ -static ssize_t tpacpi_driver_uwb_emulstate_show( -					struct device_driver *drv, -					char *buf) +static ssize_t uwb_emulstate_show(struct device_driver *drv, char *buf)  {  	return snprintf(buf, PAGE_SIZE, "%d\n", !!tpacpi_uwb_emulstate);  } -static ssize_t tpacpi_driver_uwb_emulstate_store( -					struct device_driver *drv, -					const char *buf, size_t count) +static ssize_t uwb_emulstate_store(struct device_driver *drv, const char *buf, +				   size_t count)  {  	unsigned long t; @@ -1586,10 +1556,7 @@ static ssize_t tpacpi_driver_uwb_emulstate_store(  	return count;  } - -static DRIVER_ATTR(uwb_emulstate, S_IWUSR | S_IRUGO, -		tpacpi_driver_uwb_emulstate_show, -		tpacpi_driver_uwb_emulstate_store); +static DRIVER_ATTR_RW(uwb_emulstate);  #endif  /* --------------------------------------------------------------------- */ @@ -1828,8 +1795,7 @@ static void __init tpacpi_check_outdated_fw(void)  		 * best if the user upgrades the firmware anyway.  		 */  		pr_warn("WARNING: Outdated ThinkPad BIOS/EC firmware\n"); -		pr_warn("WARNING: This firmware may be missing critical bug " -			"fixes and/or important features\n"); +		pr_warn("WARNING: This firmware may be missing critical bug fixes and/or important features\n");  	}  } @@ -2198,8 +2164,7 @@ static int hotkey_mask_set(u32 mask)  	 * a given event.  	 */  	if (!hotkey_mask_get() && !rc && (fwmask & ~hotkey_acpi_mask)) { -		pr_notice("asked for hotkey mask 0x%08x, but " -			  "firmware forced it to 0x%08x\n", +		pr_notice("asked for hotkey mask 0x%08x, but firmware forced it to 0x%08x\n",  			  fwmask, hotkey_acpi_mask);  	} @@ -2224,11 +2189,9 @@ static int hotkey_user_mask_set(const u32 mask)  	    (mask == 0xffff || mask == 0xffffff ||  	     mask == 0xffffffff)) {  		tp_warned.hotkey_mask_ff = 1; -		pr_notice("setting the hotkey mask to 0x%08x is likely " -			  "not the best way to go about it\n", mask); -		pr_notice("please consider using the driver defaults, " -			  "and refer to up-to-date thinkpad-acpi " -			  "documentation\n"); +		pr_notice("setting the hotkey mask to 0x%08x is likely not the best way to go about it\n", +			  mask); +		pr_notice("please consider using the driver defaults, and refer to up-to-date thinkpad-acpi documentation\n");  	}  	/* Try to enable what the user asked for, plus whatever we need. @@ -2603,17 +2566,14 @@ static void hotkey_poll_setup(const bool may_warn)  					NULL, TPACPI_NVRAM_KTHREAD_NAME);  			if (IS_ERR(tpacpi_hotkey_task)) {  				tpacpi_hotkey_task = NULL; -				pr_err("could not create kernel thread " -				       "for hotkey polling\n"); +				pr_err("could not create kernel thread for hotkey polling\n");  			}  		}  	} else {  		hotkey_poll_stop_sync();  		if (may_warn && (poll_driver_mask || poll_user_mask) &&  		    hotkey_poll_freq == 0) { -			pr_notice("hot keys 0x%08x and/or events 0x%08x " -				  "require polling, which is currently " -				  "disabled\n", +			pr_notice("hot keys 0x%08x and/or events 0x%08x require polling, which is currently disabled\n",  				  poll_user_mask, poll_driver_mask);  		}  	} @@ -2840,12 +2800,10 @@ static ssize_t hotkey_source_mask_store(struct device *dev,  	mutex_unlock(&hotkey_mutex);  	if (rc < 0) -		pr_err("hotkey_source_mask: " -		       "failed to update the firmware event mask!\n"); +		pr_err("hotkey_source_mask: failed to update the firmware event mask!\n");  	if (r_ev) -		pr_notice("hotkey_source_mask: " -			  "some important events were disabled: 0x%04x\n", +		pr_notice("hotkey_source_mask: some important events were disabled: 0x%04x\n",  			  r_ev);  	tpacpi_disclose_usertask("hotkey_source_mask", "set to 0x%08lx\n", t); @@ -3106,8 +3064,7 @@ static void hotkey_exit(void)  	if (((tp_features.hotkey_mask &&  	      hotkey_mask_set(hotkey_orig_mask)) |  	     hotkey_status_set(false)) != 0) -		pr_err("failed to restore hot key mask " -		       "to BIOS defaults\n"); +		pr_err("failed to restore hot key mask to BIOS defaults\n");  }  static void __init hotkey_unmap(const unsigned int scancode) @@ -3619,11 +3576,8 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)  	 * userspace. tpacpi_detect_brightness_capabilities() must have  	 * been called before this point  */  	if (acpi_video_get_backlight_type() != acpi_backlight_vendor) { -		pr_info("This ThinkPad has standard ACPI backlight " -			"brightness control, supported by the ACPI " -			"video driver\n"); -		pr_notice("Disabling thinkpad-acpi brightness events " -			  "by default...\n"); +		pr_info("This ThinkPad has standard ACPI backlight brightness control, supported by the ACPI video driver\n"); +		pr_notice("Disabling thinkpad-acpi brightness events by default...\n");  		/* Disable brightness up/down on Lenovo thinkpads when  		 * ACPI is handling them, otherwise it is plain impossible @@ -3792,7 +3746,7 @@ static bool adaptive_keyboard_hotkey_notify_hotkey(unsigned int scancode)  		    TP_ACPI_HOTKEYSCAN_EXTENDED_START -  		    TP_ACPI_HOTKEYSCAN_ADAPTIVE_START) {  			pr_info("Unhandled adaptive keyboard key: 0x%x\n", -					scancode); +				scancode);  			return false;  		}  		keycode = hotkey_keycode_map[scancode - FIRST_ADAPTIVE_KEY + @@ -3989,14 +3943,12 @@ static bool hotkey_notify_6xxx(const u32 hkey,  		/* recommended action: immediate sleep/hibernate */  		break;  	case TP_HKEY_EV_ALARM_SENSOR_HOT: -		pr_crit("THERMAL ALARM: " -			"a sensor reports something is too hot!\n"); +		pr_crit("THERMAL ALARM: a sensor reports something is too hot!\n");  		/* recommended action: warn user through gui, that */  		/* some internal component is too hot */  		break;  	case TP_HKEY_EV_ALARM_SENSOR_XHOT: -		pr_alert("THERMAL EMERGENCY: " -			 "a sensor reports something is extremely hot!\n"); +		pr_alert("THERMAL EMERGENCY: a sensor reports something is extremely hot!\n");  		/* recommended action: immediate sleep/hibernate */  		break;  	case TP_HKEY_EV_AC_CHANGED: @@ -4121,8 +4073,8 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event)  		}  		if (!known_ev) {  			pr_notice("unhandled HKEY event 0x%04x\n", hkey); -			pr_notice("please report the conditions when this " -				  "event happened to %s\n", TPACPI_MAIL); +			pr_notice("please report the conditions when this event happened to %s\n", +				  TPACPI_MAIL);  		}  		/* netlink events */ @@ -4156,8 +4108,7 @@ static void hotkey_resume(void)  	if (hotkey_status_set(true) < 0 ||  	    hotkey_mask_set(hotkey_acpi_mask) < 0) -		pr_err("error while attempting to reset the event " -		       "firmware interface\n"); +		pr_err("error while attempting to reset the event firmware interface\n");  	tpacpi_send_radiosw_update();  	hotkey_tablet_mode_notify_change(); @@ -4209,12 +4160,8 @@ static void hotkey_enabledisable_warn(bool enable)  {  	tpacpi_log_usertask("procfs hotkey enable/disable");  	if (!WARN((tpacpi_lifecycle == TPACPI_LIFE_RUNNING || !enable), -		  pr_fmt("hotkey enable/disable functionality has been " -			 "removed from the driver.  " -			 "Hotkeys are always enabled.\n"))) -		pr_err("Please remove the hotkey=enable module " -		       "parameter, it is deprecated.  " -		       "Hotkeys are always enabled.\n"); +		  pr_fmt("hotkey enable/disable functionality has been removed from the driver.  Hotkeys are always enabled.\n"))) +		pr_err("Please remove the hotkey=enable module parameter, it is deprecated.  Hotkeys are always enabled.\n");  }  static int hotkey_write(char *buf) @@ -4872,8 +4819,7 @@ static void video_exit(void)  	dbg_printk(TPACPI_DBG_EXIT,  		   "restoring original video autoswitch mode\n");  	if (video_autosw_set(video_orig_autosw)) -		pr_err("error while trying to restore original " -			"video autoswitch mode\n"); +		pr_err("error while trying to restore original video autoswitch mode\n");  }  static int video_outputsw_get(void) @@ -5963,8 +5909,7 @@ static int __init led_init(struct ibm_init_struct *iibm)  	}  #ifdef CONFIG_THINKPAD_ACPI_UNSAFE_LEDS -	pr_notice("warning: userspace override of important " -		  "firmware LEDs is enabled\n"); +	pr_notice("warning: userspace override of important firmware LEDs is enabled\n");  #endif  	return 0;  } @@ -5993,8 +5938,7 @@ static int led_read(struct seq_file *m)  		}  	} -	seq_printf(m, "commands:\t" -		       "<led> on, <led> off, <led> blink (<led> is 0-15)\n"); +	seq_printf(m, "commands:\t<led> on, <led> off, <led> blink (<led> is 0-15)\n");  	return 0;  } @@ -6367,13 +6311,10 @@ static int __init thermal_init(struct ibm_init_struct *iibm)  		if (ta1 == 0) {  			/* This is sheer paranoia, but we handle it anyway */  			if (acpi_tmp7) { -				pr_err("ThinkPad ACPI EC access misbehaving, " -				       "falling back to ACPI TMPx access " -				       "mode\n"); +				pr_err("ThinkPad ACPI EC access misbehaving, falling back to ACPI TMPx access mode\n");  				thermal_read_mode = TPACPI_THERMAL_ACPI_TMP07;  			} else { -				pr_err("ThinkPad ACPI EC access misbehaving, " -				       "disabling thermal sensors access\n"); +				pr_err("ThinkPad ACPI EC access misbehaving, disabling thermal sensors access\n");  				thermal_read_mode = TPACPI_THERMAL_NONE;  			}  		} else { @@ -6852,26 +6793,20 @@ static int __init brightness_init(struct ibm_init_struct *iibm)  	if (!brightness_enable) {  		dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_BRGHT, -			   "brightness support disabled by " -			   "module parameter\n"); +			   "brightness support disabled by module parameter\n");  		return 1;  	}  	if (acpi_video_get_backlight_type() != acpi_backlight_vendor) {  		if (brightness_enable > 1) { -			pr_info("Standard ACPI backlight interface " -				"available, not loading native one\n"); +			pr_info("Standard ACPI backlight interface available, not loading native one\n");  			return 1;  		} else if (brightness_enable == 1) { -			pr_warn("Cannot enable backlight brightness support, " -				"ACPI is already handling it.  Refer to the " -				"acpi_backlight kernel parameter.\n"); +			pr_warn("Cannot enable backlight brightness support, ACPI is already handling it.  Refer to the acpi_backlight kernel parameter.\n");  			return 1;  		}  	} else if (tp_features.bright_acpimode && brightness_enable > 1) { -		pr_notice("Standard ACPI backlight interface not " -			  "available, thinkpad_acpi native " -			  "brightness control enabled\n"); +		pr_notice("Standard ACPI backlight interface not available, thinkpad_acpi native brightness control enabled\n");  	}  	/* @@ -6922,10 +6857,10 @@ static int __init brightness_init(struct ibm_init_struct *iibm)  			"brightness is supported\n");  	if (quirks & TPACPI_BRGHT_Q_ASK) { -		pr_notice("brightness: will use unverified default: " -			  "brightness_mode=%d\n", brightness_mode); -		pr_notice("brightness: please report to %s whether it works well " -			  "or not on your ThinkPad\n", TPACPI_MAIL); +		pr_notice("brightness: will use unverified default: brightness_mode=%d\n", +			  brightness_mode); +		pr_notice("brightness: please report to %s whether it works well or not on your ThinkPad\n", +			  TPACPI_MAIL);  	}  	/* Added by mistake in early 2007.  Probably useless, but it could @@ -6935,8 +6870,7 @@ static int __init brightness_init(struct ibm_init_struct *iibm)  	backlight_update_status(ibm_backlight_device);  	vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_BRGHT, -			"brightness: registering brightness hotkeys " -			"as change notification\n"); +		    "brightness: registering brightness hotkeys as change notification\n");  	tpacpi_hotkey_driver_mask_set(hotkey_driver_mask  				| TP_ACPI_HKEY_BRGHTUP_MASK  				| TP_ACPI_HKEY_BRGHTDWN_MASK); @@ -7599,8 +7533,8 @@ static int __init volume_init(struct ibm_init_struct *iibm)  		return -EINVAL;  	if (volume_mode == TPACPI_VOL_MODE_UCMS_STEP) { -		pr_err("UCMS step volume mode not implemented, " -		       "please contact %s\n", TPACPI_MAIL); +		pr_err("UCMS step volume mode not implemented, please contact %s\n", +		       TPACPI_MAIL);  		return 1;  	} @@ -7613,8 +7547,7 @@ static int __init volume_init(struct ibm_init_struct *iibm)  	 */  	if (!alsa_enable) {  		vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER, -			    "ALSA mixer disabled by parameter, " -			    "not loading volume subdriver...\n"); +			    "ALSA mixer disabled by parameter, not loading volume subdriver...\n");  		return 1;  	} @@ -7706,12 +7639,9 @@ static int volume_read(struct seq_file *m)  		if (volume_control_allowed) {  			seq_printf(m, "commands:\tunmute, mute\n");  			if (!tp_features.mixer_no_level_control) { -				seq_printf(m, -					       "commands:\tup, down\n"); -				seq_printf(m, -					       "commands:\tlevel <level>" -					       " (<level> is 0-%d)\n", -					       TP_EC_VOLUME_MAX); +				seq_printf(m, "commands:\tup, down\n"); +				seq_printf(m, "commands:\tlevel <level> (<level> is 0-%d)\n", +					      TP_EC_VOLUME_MAX);  			}  		}  	} @@ -7734,10 +7664,8 @@ static int volume_write(char *buf)  	if (!volume_control_allowed && tpacpi_lifecycle != TPACPI_LIFE_INIT) {  		if (unlikely(!tp_warned.volume_ctrl_forbidden)) {  			tp_warned.volume_ctrl_forbidden = 1; -			pr_notice("Console audio control in monitor mode, " -				  "changes are not allowed\n"); -			pr_notice("Use the volume_control=1 module parameter " -				  "to enable volume control\n"); +			pr_notice("Console audio control in monitor mode, changes are not allowed\n"); +			pr_notice("Use the volume_control=1 module parameter to enable volume control\n");  		}  		return -EPERM;  	} @@ -8019,8 +7947,7 @@ TPACPI_HANDLE(sfan, ec, "SFAN",	/* 570 */  static void fan_quirk1_setup(void)  {  	if (fan_control_initial_status == 0x07) { -		pr_notice("fan_init: initial fan status is unknown, " -			  "assuming it is in auto mode\n"); +		pr_notice("fan_init: initial fan status is unknown, assuming it is in auto mode\n");  		tp_features.fan_ctrl_status_undef = 1;  	}  } @@ -8417,8 +8344,8 @@ static void fan_watchdog_fire(struct work_struct *ignored)  	pr_notice("fan watchdog: enabling fan\n");  	rc = fan_set_enable();  	if (rc < 0) { -		pr_err("fan watchdog: error %d while enabling fan, " -		       "will try again later...\n", -rc); +		pr_err("fan watchdog: error %d while enabling fan, will try again later...\n", +		       rc);  		/* reschedule for later */  		fan_watchdog_reset();  	} @@ -8606,14 +8533,13 @@ static ssize_t fan_fan2_input_show(struct device *dev,  static DEVICE_ATTR(fan2_input, S_IRUGO, fan_fan2_input_show, NULL);  /* sysfs fan fan_watchdog (hwmon driver) ------------------------------- */ -static ssize_t fan_fan_watchdog_show(struct device_driver *drv, -				     char *buf) +static ssize_t fan_watchdog_show(struct device_driver *drv, char *buf)  {  	return snprintf(buf, PAGE_SIZE, "%u\n", fan_watchdog_maxinterval);  } -static ssize_t fan_fan_watchdog_store(struct device_driver *drv, -				      const char *buf, size_t count) +static ssize_t fan_watchdog_store(struct device_driver *drv, const char *buf, +				  size_t count)  {  	unsigned long t; @@ -8630,9 +8556,7 @@ static ssize_t fan_fan_watchdog_store(struct device_driver *drv,  	return count;  } - -static DRIVER_ATTR(fan_watchdog, S_IWUSR | S_IRUGO, -		fan_fan_watchdog_show, fan_fan_watchdog_store); +static DRIVER_ATTR_RW(fan_watchdog);  /* --------------------------------------------------------------------- */  static struct attribute *fan_attributes[] = { @@ -8715,8 +8639,7 @@ static int __init fan_init(struct ibm_init_struct *iibm)  					"secondary fan support enabled\n");  			}  		} else { -			pr_err("ThinkPad ACPI EC access misbehaving, " -			       "fan status and control unavailable\n"); +			pr_err("ThinkPad ACPI EC access misbehaving, fan status and control unavailable\n");  			return 1;  		}  	} @@ -8815,8 +8738,8 @@ static void fan_suspend(void)  	fan_control_resume_level = 0;  	rc = fan_get_status_safe(&fan_control_resume_level);  	if (rc < 0) -		pr_notice("failed to read fan level for later " -			  "restore during resume: %d\n", rc); +		pr_notice("failed to read fan level for later restore during resume: %d\n", +			  rc);  	/* if it is undefined, don't attempt to restore it.  	 * KEEP THIS LAST */ @@ -8935,20 +8858,17 @@ static int fan_read(struct seq_file *m)  			break;  		default: -			seq_printf(m, " (<level> is 0-7, " -				       "auto, disengaged, full-speed)\n"); +			seq_printf(m, " (<level> is 0-7, auto, disengaged, full-speed)\n");  			break;  		}  	}  	if (fan_control_commands & TPACPI_FAN_CMD_ENABLE)  		seq_printf(m, "commands:\tenable, disable\n" -			       "commands:\twatchdog <timeout> (<timeout> " -			       "is 0 (off), 1-120 (seconds))\n"); +			       "commands:\twatchdog <timeout> (<timeout> is 0 (off), 1-120 (seconds))\n");  	if (fan_control_commands & TPACPI_FAN_CMD_SPEED) -		seq_printf(m, "commands:\tspeed <speed>" -			       " (<speed> is 0-65535)\n"); +		seq_printf(m, "commands:\tspeed <speed> (<speed> is 0-65535)\n");  	return 0;  } @@ -9474,8 +9394,7 @@ static int __must_check __init get_thinkpad_model_data(  				tp->ec_release = (ec_fw_string[4] << 8)  						| ec_fw_string[5];  			} else { -				pr_notice("ThinkPad firmware release %s " -					  "doesn't match the known patterns\n", +				pr_notice("ThinkPad firmware release %s doesn't match the known patterns\n",  					  ec_fw_string);  				pr_notice("please report this to %s\n",  					  TPACPI_MAIL); @@ -9670,8 +9589,7 @@ MODULE_PARM_DESC(debug, "Sets debug level bit-mask");  module_param(force_load, bool, 0444);  MODULE_PARM_DESC(force_load, -		 "Attempts to load the driver even on a " -		 "mis-identified ThinkPad when true"); +		 "Attempts to load the driver even on a mis-identified ThinkPad when true");  module_param_named(fan_control, fan_control_allowed, bool, 0444);  MODULE_PARM_DESC(fan_control, @@ -9679,8 +9597,7 @@ MODULE_PARM_DESC(fan_control,  module_param_named(brightness_mode, brightness_mode, uint, 0444);  MODULE_PARM_DESC(brightness_mode, -		 "Selects brightness control strategy: " -		 "0=auto, 1=EC, 2=UCMS, 3=EC+NVRAM"); +		 "Selects brightness control strategy: 0=auto, 1=EC, 2=UCMS, 3=EC+NVRAM");  module_param(brightness_enable, uint, 0444);  MODULE_PARM_DESC(brightness_enable, @@ -9689,18 +9606,15 @@ MODULE_PARM_DESC(brightness_enable,  #ifdef CONFIG_THINKPAD_ACPI_ALSA_SUPPORT  module_param_named(volume_mode, volume_mode, uint, 0444);  MODULE_PARM_DESC(volume_mode, -		 "Selects volume control strategy: " -		 "0=auto, 1=EC, 2=N/A, 3=EC+NVRAM"); +		 "Selects volume control strategy: 0=auto, 1=EC, 2=N/A, 3=EC+NVRAM");  module_param_named(volume_capabilities, volume_capabilities, uint, 0444);  MODULE_PARM_DESC(volume_capabilities, -		 "Selects the mixer capabilites: " -		 "0=auto, 1=volume and mute, 2=mute only"); +		 "Selects the mixer capabilites: 0=auto, 1=volume and mute, 2=mute only");  module_param_named(volume_control, volume_control_allowed, bool, 0444);  MODULE_PARM_DESC(volume_control, -		 "Enables software override for the console audio " -		 "control when true"); +		 "Enables software override for the console audio control when true");  module_param_named(software_mute, software_mute_requested, bool, 0444);  MODULE_PARM_DESC(software_mute, @@ -9715,10 +9629,10 @@ module_param_named(enable, alsa_enable, bool, 0444);  MODULE_PARM_DESC(enable, "Enable the ALSA interface for the ACPI EC Mixer");  #endif /* CONFIG_THINKPAD_ACPI_ALSA_SUPPORT */ +/* The module parameter can't be read back, that's why 0 is used here */  #define TPACPI_PARAM(feature) \  	module_param_call(feature, set_ibm_param, NULL, NULL, 0); \ -	MODULE_PARM_DESC(feature, "Simulates thinkpad-acpi procfs command " \ -			 "at module load, see documentation") +	MODULE_PARM_DESC(feature, "Simulates thinkpad-acpi procfs command at module load, see documentation")  TPACPI_PARAM(hotkey);  TPACPI_PARAM(bluetooth); diff --git a/drivers/platform/x86/topstar-laptop.c b/drivers/platform/x86/topstar-laptop.c index 70205d222da9..1032c00b907b 100644 --- a/drivers/platform/x86/topstar-laptop.c +++ b/drivers/platform/x86/topstar-laptop.c @@ -162,6 +162,7 @@ static int acpi_topstar_remove(struct acpi_device *device)  }  static const struct acpi_device_id topstar_device_ids[] = { +	{ "TPS0001", 0 },  	{ "TPSACPI01", 0 },  	{ "", 0 },  }; diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index d0daf75cbed1..88f9f79a7cf6 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -1502,14 +1502,9 @@ static ssize_t video_proc_write(struct file *file, const char __user *buf,  	int ret;  	u32 video_out; -	cmd = kmalloc(count + 1, GFP_KERNEL); -	if (!cmd) -		return -ENOMEM; -	if (copy_from_user(cmd, buf, count)) { -		kfree(cmd); -		return -EFAULT; -	} -	cmd[count] = '\0'; +	cmd = memdup_user_nul(buf, count); +	if (IS_ERR(cmd)) +		return PTR_ERR(cmd);  	buffer = cmd; diff --git a/drivers/platform/x86/toshiba_haps.c b/drivers/platform/x86/toshiba_haps.c index b3dec521e2b6..fb2736602558 100644 --- a/drivers/platform/x86/toshiba_haps.c +++ b/drivers/platform/x86/toshiba_haps.c @@ -132,7 +132,7 @@ static struct attribute *haps_attributes[] = {  	NULL,  }; -static struct attribute_group haps_attr_group = { +static const struct attribute_group haps_attr_group = {  	.attrs = haps_attributes,  }; diff --git a/drivers/platform/x86/wmi-bmof.c b/drivers/platform/x86/wmi-bmof.c new file mode 100644 index 000000000000..c4530ba715e8 --- /dev/null +++ b/drivers/platform/x86/wmi-bmof.c @@ -0,0 +1,125 @@ +/* + * WMI embedded Binary MOF driver + * + * Copyright (c) 2015 Andrew Lutomirski + * Copyright (C) 2017 VMware, Inc. All Rights Reserved. + * + *  This program is free software; you can redistribute it and/or modify it + *  under the terms of the GNU General Public License version 2 as published + *  by the Free Software Foundation. + * + *  This program is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/acpi.h> +#include <linux/device.h> +#include <linux/fs.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/sysfs.h> +#include <linux/types.h> +#include <linux/wmi.h> + +#define WMI_BMOF_GUID "05901221-D566-11D1-B2F0-00A0C9062910" + +struct bmof_priv { +	union acpi_object *bmofdata; +	struct bin_attribute bmof_bin_attr; +}; + +static ssize_t +read_bmof(struct file *filp, struct kobject *kobj, +	 struct bin_attribute *attr, +	 char *buf, loff_t off, size_t count) +{ +	struct bmof_priv *priv = +		container_of(attr, struct bmof_priv, bmof_bin_attr); + +	if (off < 0) +		return -EINVAL; + +	if (off >= priv->bmofdata->buffer.length) +		return 0; + +	if (count > priv->bmofdata->buffer.length - off) +		count = priv->bmofdata->buffer.length - off; + +	memcpy(buf, priv->bmofdata->buffer.pointer + off, count); +	return count; +} + +static int wmi_bmof_probe(struct wmi_device *wdev) +{ +	struct bmof_priv *priv; +	int ret; + +	priv = devm_kzalloc(&wdev->dev, sizeof(struct bmof_priv), GFP_KERNEL); +	if (!priv) +		return -ENOMEM; + +	dev_set_drvdata(&wdev->dev, priv); + +	priv->bmofdata = wmidev_block_query(wdev, 0); +	if (!priv->bmofdata) { +		dev_err(&wdev->dev, "failed to read Binary MOF\n"); +		return -EIO; +	} + +	if (priv->bmofdata->type != ACPI_TYPE_BUFFER) { +		dev_err(&wdev->dev, "Binary MOF is not a buffer\n"); +		ret = -EIO; +		goto err_free; +	} + +	sysfs_bin_attr_init(&priv->bmof_bin_attr); +	priv->bmof_bin_attr.attr.name = "bmof"; +	priv->bmof_bin_attr.attr.mode = 0400; +	priv->bmof_bin_attr.read = read_bmof; +	priv->bmof_bin_attr.size = priv->bmofdata->buffer.length; + +	ret = sysfs_create_bin_file(&wdev->dev.kobj, &priv->bmof_bin_attr); +	if (ret) +		goto err_free; + +	return 0; + + err_free: +	kfree(priv->bmofdata); +	return ret; +} + +static int wmi_bmof_remove(struct wmi_device *wdev) +{ +	struct bmof_priv *priv = dev_get_drvdata(&wdev->dev); + +	sysfs_remove_bin_file(&wdev->dev.kobj, &priv->bmof_bin_attr); +	kfree(priv->bmofdata); +	return 0; +} + +static const struct wmi_device_id wmi_bmof_id_table[] = { +	{ .guid_string = WMI_BMOF_GUID }, +	{ }, +}; + +static struct wmi_driver wmi_bmof_driver = { +	.driver = { +		.name = "wmi-bmof", +	}, +	.probe = wmi_bmof_probe, +	.remove = wmi_bmof_remove, +	.id_table = wmi_bmof_id_table, +}; + +module_wmi_driver(wmi_bmof_driver); + +MODULE_ALIAS("wmi:" WMI_BMOF_GUID); +MODULE_AUTHOR("Andrew Lutomirski <[email protected]>"); +MODULE_DESCRIPTION("WMI embedded Binary MOF driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index ceeb8c188ef3..1a764e311e11 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -8,6 +8,10 @@   *   Copyright (c) 2001-2007 Anton Altaparmakov   *   Copyright (C) 2001,2002 Jakob Kemi <[email protected]>   * + *  WMI bus infrastructure by Andrew Lutomirski and Darren Hart: + *    Copyright (C) 2015 Andrew Lutomirski + *    Copyright (C) 2017 VMware, Inc. All Rights Reserved. + *   * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~   *   *  This program is free software; you can redistribute it and/or modify @@ -37,6 +41,8 @@  #include <linux/acpi.h>  #include <linux/slab.h>  #include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/wmi.h>  #include <linux/uuid.h>  ACPI_MODULE_NAME("wmi"); @@ -44,8 +50,6 @@ MODULE_AUTHOR("Carlos Corbacho");  MODULE_DESCRIPTION("ACPI-WMI Mapping Driver");  MODULE_LICENSE("GPL"); -#define ACPI_WMI_CLASS "wmi" -  static LIST_HEAD(wmi_block_list);  struct guid_block { @@ -62,12 +66,14 @@ struct guid_block {  };  struct wmi_block { +	struct wmi_device dev;  	struct list_head list;  	struct guid_block gblock; -	acpi_handle handle; +	struct acpi_device *acpi_device;  	wmi_notify_handler handler;  	void *handler_data; -	struct device dev; + +	bool read_takes_no_args;  }; @@ -90,9 +96,8 @@ module_param(debug_dump_wdg, bool, 0444);  MODULE_PARM_DESC(debug_dump_wdg,  		 "Dump available WMI interfaces [0/1]"); -static int acpi_wmi_remove(struct acpi_device *device); -static int acpi_wmi_add(struct acpi_device *device); -static void acpi_wmi_notify(struct acpi_device *device, u32 event); +static int acpi_wmi_remove(struct platform_device *device); +static int acpi_wmi_probe(struct platform_device *device);  static const struct acpi_device_id wmi_device_ids[] = {  	{"PNP0C14", 0}, @@ -101,15 +106,13 @@ static const struct acpi_device_id wmi_device_ids[] = {  };  MODULE_DEVICE_TABLE(acpi, wmi_device_ids); -static struct acpi_driver acpi_wmi_driver = { -	.name = "wmi", -	.class = ACPI_WMI_CLASS, -	.ids = wmi_device_ids, -	.ops = { -		.add = acpi_wmi_add, -		.remove = acpi_wmi_remove, -		.notify = acpi_wmi_notify, +static struct platform_driver acpi_wmi_driver = { +	.driver = { +		.name = "acpi-wmi", +		.acpi_match_table = wmi_device_ids,  	}, +	.probe = acpi_wmi_probe, +	.remove = acpi_wmi_remove,  };  /* @@ -139,6 +142,30 @@ static bool find_guid(const char *guid_string, struct wmi_block **out)  	return false;  } +static int get_subobj_info(acpi_handle handle, const char *pathname, +			   struct acpi_device_info **info) +{ +	struct acpi_device_info *dummy_info, **info_ptr; +	acpi_handle subobj_handle; +	acpi_status status; + +	status = acpi_get_handle(handle, (char *)pathname, &subobj_handle); +	if (status == AE_NOT_FOUND) +		return -ENOENT; +	else if (ACPI_FAILURE(status)) +		return -EIO; + +	info_ptr = info ? info : &dummy_info; +	status = acpi_get_object_info(subobj_handle, info_ptr); +	if (ACPI_FAILURE(status)) +		return -EIO; + +	if (!info) +		kfree(dummy_info); + +	return 0; +} +  static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable)  {  	struct guid_block *block = NULL; @@ -147,7 +174,7 @@ static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable)  	acpi_handle handle;  	block = &wblock->gblock; -	handle = wblock->handle; +	handle = wblock->acpi_device->handle;  	snprintf(method, 5, "WE%02X", block->notify_id);  	status = acpi_execute_simple_method(handle, method, enable); @@ -186,7 +213,7 @@ u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out)  		return AE_ERROR;  	block = &wblock->gblock; -	handle = wblock->handle; +	handle = wblock->acpi_device->handle;  	if (!(block->flags & ACPI_WMI_METHOD))  		return AE_BAD_DATA; @@ -221,19 +248,10 @@ u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out)  }  EXPORT_SYMBOL_GPL(wmi_evaluate_method); -/** - * wmi_query_block - Return contents of a WMI block - * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba - * @instance: Instance index - * &out: Empty buffer to return the contents of the data block to - * - * Return the contents of an ACPI-WMI data block to a buffer - */ -acpi_status wmi_query_block(const char *guid_string, u8 instance, -struct acpi_buffer *out) +static acpi_status __query_block(struct wmi_block *wblock, u8 instance, +				 struct acpi_buffer *out)  {  	struct guid_block *block = NULL; -	struct wmi_block *wblock = NULL;  	acpi_handle handle;  	acpi_status status, wc_status = AE_ERROR;  	struct acpi_object_list input; @@ -241,14 +259,11 @@ struct acpi_buffer *out)  	char method[5];  	char wc_method[5] = "WC"; -	if (!guid_string || !out) +	if (!out)  		return AE_BAD_PARAMETER; -	if (!find_guid(guid_string, &wblock)) -		return AE_ERROR; -  	block = &wblock->gblock; -	handle = wblock->handle; +	handle = wblock->acpi_device->handle;  	if (block->instance_count < instance)  		return AE_BAD_PARAMETER; @@ -262,6 +277,9 @@ struct acpi_buffer *out)  	wq_params[0].type = ACPI_TYPE_INTEGER;  	wq_params[0].integer.value = instance; +	if (instance == 0 && wblock->read_takes_no_args) +		input.count = 0; +  	/*  	 * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method first to  	 * enable collection. @@ -294,8 +312,59 @@ struct acpi_buffer *out)  	return status;  } + +/** + * wmi_query_block - Return contents of a WMI block (deprecated) + * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba + * @instance: Instance index + * &out: Empty buffer to return the contents of the data block to + * + * Return the contents of an ACPI-WMI data block to a buffer + */ +acpi_status wmi_query_block(const char *guid_string, u8 instance, +			    struct acpi_buffer *out) +{ +	struct wmi_block *wblock; + +	if (!guid_string) +		return AE_BAD_PARAMETER; + +	if (!find_guid(guid_string, &wblock)) +		return AE_ERROR; + +	return __query_block(wblock, instance, out); +}  EXPORT_SYMBOL_GPL(wmi_query_block); +union acpi_object *wmidev_block_query(struct wmi_device *wdev, u8 instance) +{ +	struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL }; +	struct wmi_block *wblock = container_of(wdev, struct wmi_block, dev); + +	if (ACPI_FAILURE(__query_block(wblock, instance, &out))) +		return NULL; + +	return (union acpi_object *)out.pointer; +} +EXPORT_SYMBOL_GPL(wmidev_block_query); + +struct wmi_device *wmidev_get_other_guid(struct wmi_device *wdev, +					 const char *guid_string) +{ +	struct wmi_block *this_wb = container_of(wdev, struct wmi_block, dev); +	struct wmi_block *other_wb; + +	if (!find_guid(guid_string, &other_wb)) +		return NULL; + +	if (other_wb->acpi_device != this_wb->acpi_device) +		return NULL; + +	get_device(&other_wb->dev.dev); +	return &other_wb->dev; +} +EXPORT_SYMBOL_GPL(wmidev_get_other_guid); +  /**   * wmi_set_block - Write to a WMI block   * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba @@ -305,7 +374,7 @@ EXPORT_SYMBOL_GPL(wmi_query_block);   * Write the contents of the input buffer to an ACPI-WMI data block   */  acpi_status wmi_set_block(const char *guid_string, u8 instance, -const struct acpi_buffer *in) +			  const struct acpi_buffer *in)  {  	struct guid_block *block = NULL;  	struct wmi_block *wblock = NULL; @@ -321,7 +390,7 @@ const struct acpi_buffer *in)  		return AE_ERROR;  	block = &wblock->gblock; -	handle = wblock->handle; +	handle = wblock->acpi_device->handle;  	if (block->instance_count < instance)  		return AE_BAD_PARAMETER; @@ -352,9 +421,10 @@ EXPORT_SYMBOL_GPL(wmi_set_block);  static void wmi_dump_wdg(const struct guid_block *g)  {  	pr_info("%pUL:\n", g->guid); -	pr_info("\tobject_id: %c%c\n", g->object_id[0], g->object_id[1]); -	pr_info("\tnotify_id: %02X\n", g->notify_id); -	pr_info("\treserved: %02X\n", g->reserved); +	if (g->flags & ACPI_WMI_EVENT) +		pr_info("\tnotify_id: 0x%02X\n", g->notify_id); +	else +		pr_info("\tobject_id: %2pE\n", g->object_id);  	pr_info("\tinstance_count: %d\n", g->instance_count);  	pr_info("\tflags: %#x", g->flags);  	if (g->flags) { @@ -525,8 +595,8 @@ acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out)  		if ((gblock->flags & ACPI_WMI_EVENT) &&  			(gblock->notify_id == event)) -			return acpi_evaluate_object(wblock->handle, "_WED", -				&input, out); +			return acpi_evaluate_object(wblock->acpi_device->handle, +				"_WED", &input, out);  	}  	return AE_NOT_FOUND; @@ -545,99 +615,320 @@ bool wmi_has_guid(const char *guid_string)  }  EXPORT_SYMBOL_GPL(wmi_has_guid); +static struct wmi_block *dev_to_wblock(struct device *dev) +{ +	return container_of(dev, struct wmi_block, dev.dev); +} + +static struct wmi_device *dev_to_wdev(struct device *dev) +{ +	return container_of(dev, struct wmi_device, dev); +} +  /*   * sysfs interface   */  static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,  			     char *buf)  { -	struct wmi_block *wblock; - -	wblock = dev_get_drvdata(dev); -	if (!wblock) { -		strcat(buf, "\n"); -		return strlen(buf); -	} +	struct wmi_block *wblock = dev_to_wblock(dev);  	return sprintf(buf, "wmi:%pUL\n", wblock->gblock.guid);  }  static DEVICE_ATTR_RO(modalias); +static ssize_t guid_show(struct device *dev, struct device_attribute *attr, +			 char *buf) +{ +	struct wmi_block *wblock = dev_to_wblock(dev); + +	return sprintf(buf, "%pUL\n", wblock->gblock.guid); +} +static DEVICE_ATTR_RO(guid); + +static ssize_t instance_count_show(struct device *dev, +				   struct device_attribute *attr, char *buf) +{ +	struct wmi_block *wblock = dev_to_wblock(dev); + +	return sprintf(buf, "%d\n", (int)wblock->gblock.instance_count); +} +static DEVICE_ATTR_RO(instance_count); + +static ssize_t expensive_show(struct device *dev, +			      struct device_attribute *attr, char *buf) +{ +	struct wmi_block *wblock = dev_to_wblock(dev); + +	return sprintf(buf, "%d\n", +		       (wblock->gblock.flags & ACPI_WMI_EXPENSIVE) != 0); +} +static DEVICE_ATTR_RO(expensive); +  static struct attribute *wmi_attrs[] = {  	&dev_attr_modalias.attr, +	&dev_attr_guid.attr, +	&dev_attr_instance_count.attr, +	&dev_attr_expensive.attr,  	NULL,  };  ATTRIBUTE_GROUPS(wmi); -static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env) +static ssize_t notify_id_show(struct device *dev, struct device_attribute *attr, +			      char *buf)  { -	char guid_string[37]; +	struct wmi_block *wblock = dev_to_wblock(dev); -	struct wmi_block *wblock; +	return sprintf(buf, "%02X\n", (unsigned int)wblock->gblock.notify_id); +} +static DEVICE_ATTR_RO(notify_id); + +static struct attribute *wmi_event_attrs[] = { +	&dev_attr_notify_id.attr, +	NULL, +}; +ATTRIBUTE_GROUPS(wmi_event); + +static ssize_t object_id_show(struct device *dev, struct device_attribute *attr, +			      char *buf) +{ +	struct wmi_block *wblock = dev_to_wblock(dev); + +	return sprintf(buf, "%c%c\n", wblock->gblock.object_id[0], +		       wblock->gblock.object_id[1]); +} +static DEVICE_ATTR_RO(object_id); + +static ssize_t setable_show(struct device *dev, struct device_attribute *attr, +			    char *buf) +{ +	struct wmi_device *wdev = dev_to_wdev(dev); + +	return sprintf(buf, "%d\n", (int)wdev->setable); +} +static DEVICE_ATTR_RO(setable); -	if (add_uevent_var(env, "MODALIAS=")) +static struct attribute *wmi_data_attrs[] = { +	&dev_attr_object_id.attr, +	&dev_attr_setable.attr, +	NULL, +}; +ATTRIBUTE_GROUPS(wmi_data); + +static struct attribute *wmi_method_attrs[] = { +	&dev_attr_object_id.attr, +	NULL, +}; +ATTRIBUTE_GROUPS(wmi_method); + +static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env) +{ +	struct wmi_block *wblock = dev_to_wblock(dev); + +	if (add_uevent_var(env, "MODALIAS=wmi:%pUL", wblock->gblock.guid))  		return -ENOMEM; -	wblock = dev_get_drvdata(dev); -	if (!wblock) +	if (add_uevent_var(env, "WMI_GUID=%pUL", wblock->gblock.guid))  		return -ENOMEM; -	sprintf(guid_string, "%pUL", wblock->gblock.guid); +	return 0; +} -	strcpy(&env->buf[env->buflen - 1], "wmi:"); -	memcpy(&env->buf[env->buflen - 1 + 4], guid_string, 36); -	env->buflen += 40; +static void wmi_dev_release(struct device *dev) +{ +	struct wmi_block *wblock = dev_to_wblock(dev); + +	kfree(wblock); +} + +static int wmi_dev_match(struct device *dev, struct device_driver *driver) +{ +	struct wmi_driver *wmi_driver = +		container_of(driver, struct wmi_driver, driver); +	struct wmi_block *wblock = dev_to_wblock(dev); +	const struct wmi_device_id *id = wmi_driver->id_table; + +	while (id->guid_string) { +		uuid_le driver_guid; + +		if (WARN_ON(uuid_le_to_bin(id->guid_string, &driver_guid))) +			continue; +		if (!memcmp(&driver_guid, wblock->gblock.guid, 16)) +			return 1; + +		id++; +	}  	return 0;  } -static void wmi_dev_free(struct device *dev) +static int wmi_dev_probe(struct device *dev)  { -	struct wmi_block *wmi_block = container_of(dev, struct wmi_block, dev); +	struct wmi_block *wblock = dev_to_wblock(dev); +	struct wmi_driver *wdriver = +		container_of(dev->driver, struct wmi_driver, driver); +	int ret = 0; + +	if (ACPI_FAILURE(wmi_method_enable(wblock, 1))) +		dev_warn(dev, "failed to enable device -- probing anyway\n"); + +	if (wdriver->probe) { +		ret = wdriver->probe(dev_to_wdev(dev)); +		if (ret != 0 && ACPI_FAILURE(wmi_method_enable(wblock, 0))) +			dev_warn(dev, "failed to disable device\n"); +	} + +	return ret; +} + +static int wmi_dev_remove(struct device *dev) +{ +	struct wmi_block *wblock = dev_to_wblock(dev); +	struct wmi_driver *wdriver = +		container_of(dev->driver, struct wmi_driver, driver); +	int ret = 0; + +	if (wdriver->remove) +		ret = wdriver->remove(dev_to_wdev(dev)); + +	if (ACPI_FAILURE(wmi_method_enable(wblock, 0))) +		dev_warn(dev, "failed to disable device\n"); -	kfree(wmi_block); +	return ret;  } -static struct class wmi_class = { +static struct class wmi_bus_class = { +	.name = "wmi_bus", +}; + +static struct bus_type wmi_bus_type = {  	.name = "wmi", -	.dev_release = wmi_dev_free, -	.dev_uevent = wmi_dev_uevent,  	.dev_groups = wmi_groups, +	.match = wmi_dev_match, +	.uevent = wmi_dev_uevent, +	.probe = wmi_dev_probe, +	.remove = wmi_dev_remove, +}; + +static struct device_type wmi_type_event = { +	.name = "event", +	.groups = wmi_event_groups, +	.release = wmi_dev_release,  }; -static int wmi_create_device(const struct guid_block *gblock, -			     struct wmi_block *wblock, acpi_handle handle) +static struct device_type wmi_type_method = { +	.name = "method", +	.groups = wmi_method_groups, +	.release = wmi_dev_release, +}; + +static struct device_type wmi_type_data = { +	.name = "data", +	.groups = wmi_data_groups, +	.release = wmi_dev_release, +}; + +static int wmi_create_device(struct device *wmi_bus_dev, +			     const struct guid_block *gblock, +			     struct wmi_block *wblock, +			     struct acpi_device *device)  { -	wblock->dev.class = &wmi_class; +	struct acpi_device_info *info; +	char method[5]; +	int result; -	dev_set_name(&wblock->dev, "%pUL", gblock->guid); +	if (gblock->flags & ACPI_WMI_EVENT) { +		wblock->dev.dev.type = &wmi_type_event; +		goto out_init; +	} -	dev_set_drvdata(&wblock->dev, wblock); +	if (gblock->flags & ACPI_WMI_METHOD) { +		wblock->dev.dev.type = &wmi_type_method; +		goto out_init; +	} -	return device_register(&wblock->dev); +	/* +	 * Data Block Query Control Method (WQxx by convention) is +	 * required per the WMI documentation. If it is not present, +	 * we ignore this data block. +	 */ +	strcpy(method, "WQ"); +	strncat(method, wblock->gblock.object_id, 2); +	result = get_subobj_info(device->handle, method, &info); + +	if (result) { +		dev_warn(wmi_bus_dev, +			 "%s data block query control method not found", +			 method); +		return result; +	} + +	wblock->dev.dev.type = &wmi_type_data; + +	/* +	 * The Microsoft documentation specifically states: +	 * +	 *   Data blocks registered with only a single instance +	 *   can ignore the parameter. +	 * +	 * ACPICA will get mad at us if we call the method with the wrong number +	 * of arguments, so check what our method expects.  (On some Dell +	 * laptops, WQxx may not be a method at all.) +	 */ +	if (info->type != ACPI_TYPE_METHOD || info->param_count == 0) +		wblock->read_takes_no_args = true; + +	kfree(info); + +	strcpy(method, "WS"); +	strncat(method, wblock->gblock.object_id, 2); +	result = get_subobj_info(device->handle, method, NULL); + +	if (result == 0) +		wblock->dev.setable = true; + + out_init: +	wblock->dev.dev.bus = &wmi_bus_type; +	wblock->dev.dev.parent = wmi_bus_dev; + +	dev_set_name(&wblock->dev.dev, "%pUL", gblock->guid); + +	device_initialize(&wblock->dev.dev); + +	return 0;  } -static void wmi_free_devices(void) +static void wmi_free_devices(struct acpi_device *device)  {  	struct wmi_block *wblock, *next;  	/* Delete devices for all the GUIDs */  	list_for_each_entry_safe(wblock, next, &wmi_block_list, list) { -		list_del(&wblock->list); -		if (wblock->dev.class) -			device_unregister(&wblock->dev); -		else -			kfree(wblock); +		if (wblock->acpi_device == device) { +			list_del(&wblock->list); +			device_unregister(&wblock->dev.dev); +		}  	}  } -static bool guid_already_parsed(const char *guid_string) +static bool guid_already_parsed(struct acpi_device *device, +				const u8 *guid)  {  	struct wmi_block *wblock; -	list_for_each_entry(wblock, &wmi_block_list, list) -		if (memcmp(wblock->gblock.guid, guid_string, 16) == 0) +	list_for_each_entry(wblock, &wmi_block_list, list) { +		if (memcmp(wblock->gblock.guid, guid, 16) == 0) { +			/* +			 * Because we historically didn't track the relationship +			 * between GUIDs and ACPI nodes, we don't know whether +			 * we need to suppress GUIDs that are unique on a +			 * given node but duplicated across nodes. +			 */ +			dev_warn(&device->dev, "duplicate WMI GUID %pUL (first instance was on %s)\n", +				 guid, dev_name(&wblock->acpi_device->dev));  			return true; +		} +	}  	return false;  } @@ -645,17 +936,17 @@ static bool guid_already_parsed(const char *guid_string)  /*   * Parse the _WDG method for the GUID data blocks   */ -static int parse_wdg(acpi_handle handle) +static int parse_wdg(struct device *wmi_bus_dev, struct acpi_device *device)  {  	struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL}; -	union acpi_object *obj;  	const struct guid_block *gblock; -	struct wmi_block *wblock; +	struct wmi_block *wblock, *next; +	union acpi_object *obj;  	acpi_status status; -	int retval; +	int retval = 0;  	u32 i, total; -	status = acpi_evaluate_object(handle, "_WDG", NULL, &out); +	status = acpi_evaluate_object(device->handle, "_WDG", NULL, &out);  	if (ACPI_FAILURE(status))  		return -ENXIO; @@ -675,25 +966,28 @@ static int parse_wdg(acpi_handle handle)  		if (debug_dump_wdg)  			wmi_dump_wdg(&gblock[i]); +		/* +		 * Some WMI devices, like those for nVidia hooks, have a +		 * duplicate GUID. It's not clear what we should do in this +		 * case yet, so for now, we'll just ignore the duplicate +		 * for device creation. +		 */ +		if (guid_already_parsed(device, gblock[i].guid)) +			continue; +  		wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL); -		if (!wblock) -			return -ENOMEM; +		if (!wblock) { +			retval = -ENOMEM; +			break; +		} -		wblock->handle = handle; +		wblock->acpi_device = device;  		wblock->gblock = gblock[i]; -		/* -		  Some WMI devices, like those for nVidia hooks, have a -		  duplicate GUID. It's not clear what we should do in this -		  case yet, so for now, we'll just ignore the duplicate -		  for device creation. -		*/ -		if (!guid_already_parsed(gblock[i].guid)) { -			retval = wmi_create_device(&gblock[i], wblock, handle); -			if (retval) { -				wmi_free_devices(); -				goto out_free_pointer; -			} +		retval = wmi_create_device(wmi_bus_dev, &gblock[i], wblock, device); +		if (retval) { +			kfree(wblock); +			continue;  		}  		list_add_tail(&wblock->list, &wmi_block_list); @@ -704,11 +998,27 @@ static int parse_wdg(acpi_handle handle)  		}  	} -	retval = 0; +	/* +	 * Now that all of the devices are created, add them to the +	 * device tree and probe subdrivers. +	 */ +	list_for_each_entry_safe(wblock, next, &wmi_block_list, list) { +		if (wblock->acpi_device != device) +			continue; + +		retval = device_add(&wblock->dev.dev); +		if (retval) { +			dev_err(wmi_bus_dev, "failed to register %pULL\n", +				wblock->gblock.guid); +			if (debug_event) +				wmi_method_enable(wblock, 0); +			list_del(&wblock->list); +			put_device(&wblock->dev.dev); +		} +	}  out_free_pointer:  	kfree(out.pointer); -  	return retval;  } @@ -756,67 +1066,168 @@ acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address,  	}  } -static void acpi_wmi_notify(struct acpi_device *device, u32 event) +static void acpi_wmi_notify_handler(acpi_handle handle, u32 event, +				    void *context)  {  	struct guid_block *block;  	struct wmi_block *wblock;  	struct list_head *p; +	bool found_it = false;  	list_for_each(p, &wmi_block_list) {  		wblock = list_entry(p, struct wmi_block, list);  		block = &wblock->gblock; -		if ((block->flags & ACPI_WMI_EVENT) && -			(block->notify_id == event)) { -			if (wblock->handler) -				wblock->handler(event, wblock->handler_data); -			if (debug_event) { -				pr_info("DEBUG Event GUID: %pUL\n", -					wblock->gblock.guid); -			} - -			acpi_bus_generate_netlink_event( -				device->pnp.device_class, dev_name(&device->dev), -				event, 0); +		if (wblock->acpi_device->handle == handle && +		    (block->flags & ACPI_WMI_EVENT) && +		    (block->notify_id == event)) +		{ +			found_it = true;  			break;  		}  	} + +	if (!found_it) +		return; + +	/* If a driver is bound, then notify the driver. */ +	if (wblock->dev.dev.driver) { +		struct wmi_driver *driver; +		struct acpi_object_list input; +		union acpi_object params[1]; +		struct acpi_buffer evdata = { ACPI_ALLOCATE_BUFFER, NULL }; +		acpi_status status; + +		driver = container_of(wblock->dev.dev.driver, +				      struct wmi_driver, driver); + +		input.count = 1; +		input.pointer = params; +		params[0].type = ACPI_TYPE_INTEGER; +		params[0].integer.value = event; + +		status = acpi_evaluate_object(wblock->acpi_device->handle, +					      "_WED", &input, &evdata); +		if (ACPI_FAILURE(status)) { +			dev_warn(&wblock->dev.dev, +				 "failed to get event data\n"); +			return; +		} + +		if (driver->notify) +			driver->notify(&wblock->dev, +				       (union acpi_object *)evdata.pointer); + +		kfree(evdata.pointer); +	} else if (wblock->handler) { +		/* Legacy handler */ +		wblock->handler(event, wblock->handler_data); +	} + +	if (debug_event) { +		pr_info("DEBUG Event GUID: %pUL\n", +			wblock->gblock.guid); +	} + +	acpi_bus_generate_netlink_event( +		wblock->acpi_device->pnp.device_class, +		dev_name(&wblock->dev.dev), +		event, 0); +  } -static int acpi_wmi_remove(struct acpi_device *device) +static int acpi_wmi_remove(struct platform_device *device)  { -	acpi_remove_address_space_handler(device->handle, +	struct acpi_device *acpi_device = ACPI_COMPANION(&device->dev); + +	acpi_remove_notify_handler(acpi_device->handle, ACPI_DEVICE_NOTIFY, +				   acpi_wmi_notify_handler); +	acpi_remove_address_space_handler(acpi_device->handle,  				ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler); -	wmi_free_devices(); +	wmi_free_devices(acpi_device); +	device_unregister((struct device *)dev_get_drvdata(&device->dev));  	return 0;  } -static int acpi_wmi_add(struct acpi_device *device) +static int acpi_wmi_probe(struct platform_device *device)  { +	struct acpi_device *acpi_device; +	struct device *wmi_bus_dev;  	acpi_status status;  	int error; -	status = acpi_install_address_space_handler(device->handle, +	acpi_device = ACPI_COMPANION(&device->dev); +	if (!acpi_device) { +		dev_err(&device->dev, "ACPI companion is missing\n"); +		return -ENODEV; +	} + +	status = acpi_install_address_space_handler(acpi_device->handle,  						    ACPI_ADR_SPACE_EC,  						    &acpi_wmi_ec_space_handler,  						    NULL, NULL);  	if (ACPI_FAILURE(status)) { -		pr_err("Error installing EC region handler\n"); +		dev_err(&device->dev, "Error installing EC region handler\n");  		return -ENODEV;  	} -	error = parse_wdg(device->handle); +	status = acpi_install_notify_handler(acpi_device->handle, +					     ACPI_DEVICE_NOTIFY, +					     acpi_wmi_notify_handler, +					     NULL); +	if (ACPI_FAILURE(status)) { +		dev_err(&device->dev, "Error installing notify handler\n"); +		error = -ENODEV; +		goto err_remove_ec_handler; +	} + +	wmi_bus_dev = device_create(&wmi_bus_class, &device->dev, MKDEV(0, 0), +				    NULL, "wmi_bus-%s", dev_name(&device->dev)); +	if (IS_ERR(wmi_bus_dev)) { +		error = PTR_ERR(wmi_bus_dev); +		goto err_remove_notify_handler; +	} +	dev_set_drvdata(&device->dev, wmi_bus_dev); + +	error = parse_wdg(wmi_bus_dev, acpi_device);  	if (error) { -		acpi_remove_address_space_handler(device->handle, -						  ACPI_ADR_SPACE_EC, -						  &acpi_wmi_ec_space_handler);  		pr_err("Failed to parse WDG method\n"); -		return error; +		goto err_remove_busdev;  	}  	return 0; + +err_remove_busdev: +	device_unregister(wmi_bus_dev); + +err_remove_notify_handler: +	acpi_remove_notify_handler(acpi_device->handle, ACPI_DEVICE_NOTIFY, +				   acpi_wmi_notify_handler); + +err_remove_ec_handler: +	acpi_remove_address_space_handler(acpi_device->handle, +					  ACPI_ADR_SPACE_EC, +					  &acpi_wmi_ec_space_handler); + +	return error; +} + +int __must_check __wmi_driver_register(struct wmi_driver *driver, +				       struct module *owner) +{ +	driver->driver.owner = owner; +	driver->driver.bus = &wmi_bus_type; + +	return driver_register(&driver->driver);  } +EXPORT_SYMBOL(__wmi_driver_register); + +void wmi_driver_unregister(struct wmi_driver *driver) +{ +	driver_unregister(&driver->driver); +} +EXPORT_SYMBOL(wmi_driver_unregister);  static int __init acpi_wmi_init(void)  { @@ -825,27 +1236,36 @@ static int __init acpi_wmi_init(void)  	if (acpi_disabled)  		return -ENODEV; -	error = class_register(&wmi_class); +	error = class_register(&wmi_bus_class);  	if (error)  		return error; -	error = acpi_bus_register_driver(&acpi_wmi_driver); +	error = bus_register(&wmi_bus_type); +	if (error) +		goto err_unreg_class; + +	error = platform_driver_register(&acpi_wmi_driver);  	if (error) {  		pr_err("Error loading mapper\n"); -		class_unregister(&wmi_class); -		return error; +		goto err_unreg_bus;  	} -	pr_info("Mapper loaded\n");  	return 0; + +err_unreg_class: +	class_unregister(&wmi_bus_class); + +err_unreg_bus: +	bus_unregister(&wmi_bus_type); + +	return error;  }  static void __exit acpi_wmi_exit(void)  { -	acpi_bus_unregister_driver(&acpi_wmi_driver); -	class_unregister(&wmi_class); - -	pr_info("Mapper unloaded\n"); +	platform_driver_unregister(&acpi_wmi_driver); +	class_unregister(&wmi_bus_class); +	bus_unregister(&wmi_bus_type);  }  subsys_initcall(acpi_wmi_init); |