Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 05fde26a authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'platform-drivers-x86-v4.2-1' of...

Merge tag 'platform-drivers-x86-v4.2-1' of git://git.infradead.org/users/dvhart/linux-platform-drivers-x86

Pull x86 platform driver updates from Darren Hart:
 "Fairly routine update for platform-drivers-x86.

  Mostly fixes and cleanups, with a significant refactoring of toshiba*
  drivers.  Includes the addition of the dell-rbtn driver.

  Details:

  asus-wmi:
   - fan control

  dell*:
   - add Dell airplane mode switch driver

  ideapad-laptop:
   - platform rfkill fixes, and regression fix

  pvpanic:
   - handle missing _STA correctly

  toshiba*:
   - rafactor bluetooth support
   - haps documentation
   - driver cleanup

  other:
   - Use acpi_video_unregister_backlight instead of
     acpi_video_unregister in serveral drivers.
   - Orphan msi-wmi.

* tag 'platform-drivers-x86-v4.2-1' of git://git.infradead.org/users/dvhart/linux-platform-drivers-x86: (24 commits)
  MAINTAINERS: Orphan x86 driver msi-wmi
  ideapad: fix software rfkill setting
  dell-laptop: Use dell-rbtn instead i8042 filter when possible
  dell-rbtn: Export notifier for other kernel modules
  dell-rbtn: Dell Airplane Mode Switch driver
  samsung-laptop: Use acpi_video_unregister_backlight instead of acpi_video_unregister
  asus-wmi: Use acpi_video_unregister_backlight instead of acpi_video_unregister
  apple_gmux: Use acpi_video_unregister_backlight instead of acpi_video_unregister
  pvpanic: handle missing _STA correctly
  ideapad_laptop: Lenovo G50-30 fix rfkill reports wireless blocked
  asus-wmi: add fan control
  Documentation/ABI: Add file describing the sysfs entries for toshiba_haps
  toshiba_haps: Make use of DEVICE_ATTR_{RW, WO} macros
  toshiba_haps: Replace sscanf with kstrtoint
  toshiba_acpi: Bump driver version to 0.22
  toshiba_acpi: Remove TOS_FAILURE check from some functions
  toshiba_acpi: Comments cleanup
  toshiba_acpi: Rename hci_{read, write}1 functions
  toshiba_acpi: Remove no longer needed hci_{read, write}2 functions
  toshiba_bluetooth: Change BT status message to debug
  ...
parents 2d01eedf 5ee7041e
Loading
Loading
Loading
Loading
+20 −0
Original line number Diff line number Diff line
What:		/sys/devices/LNXSYSTM:00/LNXSYBUS:00/TOS620A:00/protection_level
Date:		August 16, 2014
KernelVersion:	3.17
Contact:	Azael Avalos <coproscefalo@gmail.com>
Description:	This file controls the built-in accelerometer protection level,
		valid values are:
			* 0 -> Disabled
			* 1 -> Low
			* 2 -> Medium
			* 3 -> High
		The default potection value is set to 2 (Medium).
Users:		KToshiba

What:		/sys/devices/LNXSYSTM:00/LNXSYBUS:00/TOS620A:00/reset_protection
Date:		August 16, 2014
KernelVersion:	3.17
Contact:	Azael Avalos <coproscefalo@gmail.com>
Description:	This file turns off the built-in accelerometer for a few
		seconds and then restore normal operation. Accepting 1 as the
		only parameter.
+6 −2
Original line number Diff line number Diff line
@@ -3216,6 +3216,11 @@ L: platform-driver-x86@vger.kernel.org
S:	Maintained
F:	drivers/platform/x86/dell-laptop.c

DELL LAPTOP RBTN DRIVER
M:	Pali Rohár <pali.rohar@gmail.com>
S:	Maintained
F:	drivers/platform/x86/dell-rbtn.*

DELL LAPTOP FREEFALL DRIVER
M:	Pali Rohár <pali.rohar@gmail.com>
S:	Maintained
@@ -6786,9 +6791,8 @@ S: Maintained
F:	drivers/platform/x86/msi-laptop.c

MSI WMI SUPPORT
M:	Anisse Astier <anisse@astier.eu>
L:	platform-driver-x86@vger.kernel.org
S:	Supported
S:	Orphan
F:	drivers/platform/x86/msi-wmi.c

MSI001 MEDIA DRIVER
+17 −1
Original line number Diff line number Diff line
@@ -141,6 +141,22 @@ config DELL_SMO8800
	  To compile this driver as a module, choose M here: the module will
	  be called dell-smo8800.

config DELL_RBTN
	tristate "Dell Airplane Mode Switch driver"
	depends on ACPI
	depends on INPUT
	depends on RFKILL
	---help---
	  Say Y here if you want to support Dell Airplane Mode Switch ACPI
	  device on Dell laptops. Sometimes it has names: DELLABCE or DELRBTN.
	  This driver register rfkill device or input hotkey device depending
	  on hardware type (hw switch slider or keyboard toggle button). For
	  rfkill devices it receive HW switch events and set correct hard
	  rfkill state.

	  To compile this driver as a module, choose M here: the module will
	  be called dell-rbtn.


config FUJITSU_LAPTOP
	tristate "Fujitsu Laptop Extras"
@@ -622,7 +638,6 @@ config ACPI_TOSHIBA
	select NEW_LEDS
	depends on BACKLIGHT_CLASS_DEVICE
	depends on INPUT
	depends on RFKILL || RFKILL = n
	depends on SERIO_I8042 || SERIO_I8042 = n
	depends on ACPI_VIDEO || ACPI_VIDEO = n
	select INPUT_POLLDEV
@@ -653,6 +668,7 @@ config ACPI_TOSHIBA
config TOSHIBA_BT_RFKILL
	tristate "Toshiba Bluetooth RFKill switch support"
	depends on ACPI
	depends on RFKILL || RFKILL = n
	---help---
	  This driver adds support for Bluetooth events for the RFKill
	  switch on modern Toshiba laptops with full ACPI support and
+1 −0
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@ obj-$(CONFIG_DELL_LAPTOP) += dell-laptop.o
obj-$(CONFIG_DELL_WMI)		+= dell-wmi.o
obj-$(CONFIG_DELL_WMI_AIO)	+= dell-wmi-aio.o
obj-$(CONFIG_DELL_SMO8800)	+= dell-smo8800.o
obj-$(CONFIG_DELL_RBTN)		+= dell-rbtn.o
obj-$(CONFIG_ACER_WMI)		+= acer-wmi.o
obj-$(CONFIG_ACERHDF)		+= acerhdf.o
obj-$(CONFIG_HP_ACCEL)		+= hp_accel.o
+323 −21
Original line number Diff line number Diff line
@@ -78,6 +78,7 @@ MODULE_LICENSE("GPL");
#define ASUS_WMI_METHODID_GPID		0x44495047 /* Get Panel ID?? (Resol) */
#define ASUS_WMI_METHODID_QMOD		0x444F4D51 /* Quiet MODe */
#define ASUS_WMI_METHODID_SPLV		0x4C425053 /* Set Panel Light Value */
#define ASUS_WMI_METHODID_AGFN		0x4E464741 /* FaN? */
#define ASUS_WMI_METHODID_SFUN		0x4E554653 /* FUNCtionalities */
#define ASUS_WMI_METHODID_SDSP		0x50534453 /* Set DiSPlay output */
#define ASUS_WMI_METHODID_GDSP		0x50534447 /* Get DiSPlay output */
@@ -150,11 +151,37 @@ MODULE_LICENSE("GPL");
#define ASUS_WMI_DSTS_BRIGHTNESS_MASK	0x000000FF
#define ASUS_WMI_DSTS_MAX_BRIGTH_MASK	0x0000FF00

#define ASUS_FAN_DESC			"cpu_fan"
#define ASUS_FAN_MFUN			0x13
#define ASUS_FAN_SFUN_READ		0x06
#define ASUS_FAN_SFUN_WRITE		0x07
#define ASUS_FAN_CTRL_MANUAL		1
#define ASUS_FAN_CTRL_AUTO		2

struct bios_args {
	u32 arg0;
	u32 arg1;
} __packed;

/*
 * Struct that's used for all methods called via AGFN. Naming is
 * identically to the AML code.
 */
struct agfn_args {
	u16 mfun; /* probably "Multi-function" to be called */
	u16 sfun; /* probably "Sub-function" to be called */
	u16 len;  /* size of the hole struct, including subfunction fields */
	u8 stas;  /* not used by now */
	u8 err;   /* zero on success */
} __packed;

/* struct used for calling fan read and write methods */
struct fan_args {
	struct agfn_args agfn;	/* common fields */
	u8 fan;			/* fan number: 0: set auto mode 1: 1st fan */
	u32 speed;		/* read: RPM/100 - write: 0-255 */
} __packed;

/*
 * <platform>/    - debugfs root directory
 *   dev_id      - current dev_id
@@ -204,6 +231,10 @@ struct asus_wmi {
	struct asus_rfkill gps;
	struct asus_rfkill uwb;

	bool asus_hwmon_fan_manual_mode;
	int asus_hwmon_num_fans;
	int asus_hwmon_pwm;

	struct hotplug_slot *hotplug_slot;
	struct mutex hotplug_lock;
	struct mutex wmi_lock;
@@ -294,6 +325,36 @@ static int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1,
	return 0;
}

static int asus_wmi_evaluate_method_agfn(const struct acpi_buffer args)
{
	struct acpi_buffer input;
	u64 phys_addr;
	u32 retval;
	u32 status = -1;

	/*
	 * Copy to dma capable address otherwise memory corruption occurs as
	 * bios has to be able to access it.
	 */
	input.pointer = kzalloc(args.length, GFP_DMA | GFP_KERNEL);
	input.length = args.length;
	if (!input.pointer)
		return -ENOMEM;
	phys_addr = virt_to_phys(input.pointer);
	memcpy(input.pointer, args.pointer, args.length);

	status = asus_wmi_evaluate_method(ASUS_WMI_METHODID_AGFN,
					phys_addr, 0, &retval);
	if (!status)
		memcpy(args.pointer, input.pointer, args.length);

	kfree(input.pointer);
	if (status)
		return -ENXIO;

	return retval;
}

static int asus_wmi_get_devstate(struct asus_wmi *asus, u32 dev_id, u32 *retval)
{
	return asus_wmi_evaluate_method(asus->dsts_id, dev_id, 0, retval);
@@ -1022,35 +1083,228 @@ static int asus_wmi_rfkill_init(struct asus_wmi *asus)
/*
 * Hwmon device
 */
static ssize_t asus_hwmon_pwm1(struct device *dev,
			       struct device_attribute *attr,
			       char *buf)
static int asus_hwmon_agfn_fan_speed_read(struct asus_wmi *asus, int fan,
					  int *speed)
{
	struct fan_args args = {
		.agfn.len = sizeof(args),
		.agfn.mfun = ASUS_FAN_MFUN,
		.agfn.sfun = ASUS_FAN_SFUN_READ,
		.fan = fan,
		.speed = 0,
	};
	struct acpi_buffer input = { (acpi_size) sizeof(args), &args };
	int status;

	if (fan != 1)
		return -EINVAL;

	status = asus_wmi_evaluate_method_agfn(input);

	if (status || args.agfn.err)
		return -ENXIO;

	if (speed)
		*speed = args.speed;

	return 0;
}

static int asus_hwmon_agfn_fan_speed_write(struct asus_wmi *asus, int fan,
				     int *speed)
{
	struct fan_args args = {
		.agfn.len = sizeof(args),
		.agfn.mfun = ASUS_FAN_MFUN,
		.agfn.sfun = ASUS_FAN_SFUN_WRITE,
		.fan = fan,
		.speed = speed ?  *speed : 0,
	};
	struct acpi_buffer input = { (acpi_size) sizeof(args), &args };
	int status;

	/* 1: for setting 1st fan's speed 0: setting auto mode */
	if (fan != 1 && fan != 0)
		return -EINVAL;

	status = asus_wmi_evaluate_method_agfn(input);

	if (status || args.agfn.err)
		return -ENXIO;

	if (speed && fan == 1)
		asus->asus_hwmon_pwm = *speed;

	return 0;
}

/*
 * Check if we can read the speed of one fan. If true we assume we can also
 * control it.
 */
static int asus_hwmon_get_fan_number(struct asus_wmi *asus, int *num_fans)
{
	int status;
	int speed = 0;

	*num_fans = 0;

	status = asus_hwmon_agfn_fan_speed_read(asus, 1, &speed);
	if (!status)
		*num_fans = 1;

	return 0;
}

static int asus_hwmon_fan_set_auto(struct asus_wmi *asus)
{
	int status;

	status = asus_hwmon_agfn_fan_speed_write(asus, 0, NULL);
	if (status)
		return -ENXIO;

	asus->asus_hwmon_fan_manual_mode = false;

	return 0;
}

static int asus_hwmon_fan_rpm_show(struct device *dev, int fan)
{
	struct asus_wmi *asus = dev_get_drvdata(dev);
	u32 value;
	int value;
	int ret;

	/* no speed readable on manual mode */
	if (asus->asus_hwmon_fan_manual_mode)
		return -ENXIO;

	ret = asus_hwmon_agfn_fan_speed_read(asus, fan+1, &value);
	if (ret) {
		pr_warn("reading fan speed failed: %d\n", ret);
		return -ENXIO;
	}

	return value;
}

static void asus_hwmon_pwm_show(struct asus_wmi *asus, int fan, int *value)
{
	int err;

	err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_FAN_CTRL, &value);
	if (asus->asus_hwmon_pwm >= 0) {
		*value = asus->asus_hwmon_pwm;
		return;
	}

	err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_FAN_CTRL, value);
	if (err < 0)
		return err;
		return;

	value &= 0xFF;
	*value &= 0xFF;

	if (value == 1) /* Low Speed */
		value = 85;
	else if (value == 2)
		value = 170;
	else if (value == 3)
		value = 255;
	else if (value != 0) {
		pr_err("Unknown fan speed %#x\n", value);
		value = -1;
	if (*value == 1) /* Low Speed */
		*value = 85;
	else if (*value == 2)
		*value = 170;
	else if (*value == 3)
		*value = 255;
	else if (*value) {
		pr_err("Unknown fan speed %#x\n", *value);
		*value = -1;
	}
}

static ssize_t pwm1_show(struct device *dev,
			       struct device_attribute *attr,
			       char *buf)
{
	struct asus_wmi *asus = dev_get_drvdata(dev);
	int value;

	asus_hwmon_pwm_show(asus, 0, &value);

	return sprintf(buf, "%d\n", value);
}

static ssize_t pwm1_store(struct device *dev,
				     struct device_attribute *attr,
				     const char *buf, size_t count) {
	struct asus_wmi *asus = dev_get_drvdata(dev);
	int value;
	int state;
	int ret;

	ret = kstrtouint(buf, 10, &value);

	if (ret)
		return ret;

	value = clamp(value, 0, 255);

	state = asus_hwmon_agfn_fan_speed_write(asus, 1, &value);
	if (state)
		pr_warn("Setting fan speed failed: %d\n", state);
	else
		asus->asus_hwmon_fan_manual_mode = true;

	return count;
}

static ssize_t fan1_input_show(struct device *dev,
					struct device_attribute *attr,
					char *buf)
{
	int value = asus_hwmon_fan_rpm_show(dev, 0);

	return sprintf(buf, "%d\n", value < 0 ? -1 : value*100);

}

static ssize_t pwm1_enable_show(struct device *dev,
						 struct device_attribute *attr,
						 char *buf)
{
	struct asus_wmi *asus = dev_get_drvdata(dev);

	if (asus->asus_hwmon_fan_manual_mode)
		return sprintf(buf, "%d\n", ASUS_FAN_CTRL_MANUAL);

	return sprintf(buf, "%d\n", ASUS_FAN_CTRL_AUTO);
}

static ssize_t pwm1_enable_store(struct device *dev,
						  struct device_attribute *attr,
						  const char *buf, size_t count)
{
	struct asus_wmi *asus = dev_get_drvdata(dev);
	int status = 0;
	int state;
	int ret;

	ret = kstrtouint(buf, 10, &state);

	if (ret)
		return ret;

	if (state == ASUS_FAN_CTRL_MANUAL)
		asus->asus_hwmon_fan_manual_mode = true;
	else
		status = asus_hwmon_fan_set_auto(asus);

	if (status)
		return status;

	return count;
}

static ssize_t fan1_label_show(struct device *dev,
					  struct device_attribute *attr,
					  char *buf)
{
	return sprintf(buf, "%s\n", ASUS_FAN_DESC);
}

static ssize_t asus_hwmon_temp1(struct device *dev,
				struct device_attribute *attr,
				char *buf)
@@ -1069,11 +1323,21 @@ static ssize_t asus_hwmon_temp1(struct device *dev,
	return sprintf(buf, "%d\n", value);
}

static DEVICE_ATTR(pwm1, S_IRUGO, asus_hwmon_pwm1, NULL);
/* Fan1 */
static DEVICE_ATTR_RW(pwm1);
static DEVICE_ATTR_RW(pwm1_enable);
static DEVICE_ATTR_RO(fan1_input);
static DEVICE_ATTR_RO(fan1_label);

/* Temperature */
static DEVICE_ATTR(temp1_input, S_IRUGO, asus_hwmon_temp1, NULL);

static struct attribute *hwmon_attributes[] = {
	&dev_attr_pwm1.attr,
	&dev_attr_pwm1_enable.attr,
	&dev_attr_fan1_input.attr,
	&dev_attr_fan1_label.attr,

	&dev_attr_temp1_input.attr,
	NULL
};
@@ -1084,19 +1348,28 @@ static umode_t asus_hwmon_sysfs_is_visible(struct kobject *kobj,
	struct device *dev = container_of(kobj, struct device, kobj);
	struct platform_device *pdev = to_platform_device(dev->parent);
	struct asus_wmi *asus = platform_get_drvdata(pdev);
	bool ok = true;
	int dev_id = -1;
	int fan_attr = -1;
	u32 value = ASUS_WMI_UNSUPPORTED_METHOD;
	bool ok = true;

	if (attr == &dev_attr_pwm1.attr)
		dev_id = ASUS_WMI_DEVID_FAN_CTRL;
	else if (attr == &dev_attr_temp1_input.attr)
		dev_id = ASUS_WMI_DEVID_THERMAL_CTRL;


	if (attr == &dev_attr_fan1_input.attr
	    || attr == &dev_attr_fan1_label.attr
	    || attr == &dev_attr_pwm1.attr
	    || attr == &dev_attr_pwm1_enable.attr) {
		fan_attr = 1;
	}

	if (dev_id != -1) {
		int err = asus_wmi_get_devstate(asus, dev_id, &value);

		if (err < 0)
		if (err < 0 && fan_attr == -1)
			return 0; /* can't return negative here */
	}

@@ -1112,9 +1385,15 @@ static umode_t asus_hwmon_sysfs_is_visible(struct kobject *kobj,
		if (value == ASUS_WMI_UNSUPPORTED_METHOD || value & 0xFFF80000
		    || (!asus->sfun && !(value & ASUS_WMI_DSTS_PRESENCE_BIT)))
			ok = false;
		else
			ok = fan_attr <= asus->asus_hwmon_num_fans;
	} else if (dev_id == ASUS_WMI_DEVID_THERMAL_CTRL) {
		/* If value is zero, something is clearly wrong */
		if (value == 0)
		if (!value)
			ok = false;
	} else if (fan_attr <= asus->asus_hwmon_num_fans && fan_attr != -1) {
		ok = true;
	} else {
		ok = false;
	}

@@ -1723,6 +2002,25 @@ static int asus_wmi_debugfs_init(struct asus_wmi *asus)
	return -ENOMEM;
}

static int asus_wmi_fan_init(struct asus_wmi *asus)
{
	int status;

	asus->asus_hwmon_pwm = -1;
	asus->asus_hwmon_num_fans = -1;
	asus->asus_hwmon_fan_manual_mode = false;

	status = asus_hwmon_get_fan_number(asus, &asus->asus_hwmon_num_fans);
	if (status) {
		asus->asus_hwmon_num_fans = 0;
		pr_warn("Could not determine number of fans: %d\n", status);
		return -ENXIO;
	}

	pr_info("Number of fans: %d\n", asus->asus_hwmon_num_fans);
	return 0;
}

/*
 * WMI Driver
 */
@@ -1756,6 +2054,9 @@ static int asus_wmi_add(struct platform_device *pdev)
	if (err)
		goto fail_input;

	err = asus_wmi_fan_init(asus); /* probably no problems on error */
	asus_hwmon_fan_set_auto(asus);

	err = asus_wmi_hwmon_init(asus);
	if (err)
		goto fail_hwmon;
@@ -1831,6 +2132,7 @@ static int asus_wmi_remove(struct platform_device *device)
	asus_wmi_rfkill_exit(asus);
	asus_wmi_debugfs_exit(asus);
	asus_wmi_platform_exit(asus);
	asus_hwmon_fan_set_auto(asus);

	kfree(asus);
	return 0;
Loading