[PATCH] platform/x86: asus-wmi: Add fan support for UX550 series

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On UX550 calling WMI methods on the device id ASUS_WMI_DEVID_FAN_CTRL
does not work anymore. Asus provides two new fan-related device ids
that can be used with the DSTS and DEVS methods to query and control
the CPU and GPU fans. This enables a new path in the code that if the
old call fails first checks for the new devices and then exposes them
in sysfs.

Signed-off-by: Federico Di Gregorio <fog@xxxxxxx>
---
 drivers/platform/x86/asus-wmi.c | 257 +++++++++++++++++++++++++++++---
 1 file changed, 234 insertions(+), 23 deletions(-)

diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index 3d523ca64694..1430d8ed9a62 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -134,6 +134,8 @@ MODULE_LICENSE("GPL");
 /* Fan, Thermal */
 #define ASUS_WMI_DEVID_THERMAL_CTRL	0x00110011
 #define ASUS_WMI_DEVID_FAN_CTRL		0x00110012
+#define ASUS_WMI_DEVID_FAN_CPU		0x00110013
+#define ASUS_WMI_DEVID_FAN_GPU		0x00110014
 
 /* Power */
 #define ASUS_WMI_DEVID_PROCESSOR_STATE	0x00120012
@@ -152,6 +154,7 @@ MODULE_LICENSE("GPL");
 #define ASUS_WMI_DSTS_LIGHTBAR_MASK	0x0000000F
 
 #define ASUS_FAN_DESC			"cpu_fan"
+#define ASUS_FAN_GPU_DESC		"gpu_fan"
 #define ASUS_FAN_MFUN			0x13
 #define ASUS_FAN_SFUN_READ		0x06
 #define ASUS_FAN_SFUN_WRITE		0x07
@@ -250,6 +253,7 @@ struct asus_wmi {
 	struct asus_rfkill uwb;
 
 	bool asus_hwmon_fan_manual_mode;
+	bool asus_hwmon_fan_cpu_gpu;
 	int asus_hwmon_num_fans;
 	int asus_hwmon_pwm;
 
@@ -1219,6 +1223,31 @@ static int asus_hwmon_agfn_fan_speed_read(struct asus_wmi *asus, int fan,
 	return 0;
 }
 
+static int asus_wmi_fan_speed_read(struct asus_wmi *asus, int fan,
+					int *speed, bool *full_on)
+{
+	int status, retval;
+
+	if (fan == 1)
+		status = asus_wmi_get_devstate(asus,
+					       ASUS_WMI_DEVID_FAN_CPU,
+					       &retval);
+	else if (fan == 2)
+		status = asus_wmi_get_devstate(asus,
+					       ASUS_WMI_DEVID_FAN_GPU,
+					       &retval);
+	else
+		return -EINVAL;
+
+	if (status)
+		return -ENXIO;
+
+	*speed = retval & 0x7fff;
+	*full_on = (retval & 0x40000) == 0x40000;
+
+	return 0;
+}
+
 static int asus_hwmon_agfn_fan_speed_write(struct asus_wmi *asus, int fan,
 				     int *speed)
 {
@@ -1247,9 +1276,32 @@ static int asus_hwmon_agfn_fan_speed_write(struct asus_wmi *asus, int fan,
 	return 0;
 }
 
+static int asus_wmi_fan_speed_write(struct asus_wmi *asus, int fan,
+					bool full_on)
+{
+	int status, retval;
+
+	if (fan == 1)
+		status = asus_wmi_set_devstate(ASUS_WMI_DEVID_FAN_CPU,
+					       full_on ? 1 : 0,
+					       &retval);
+	else if (fan == 2)
+		status = asus_wmi_set_devstate(ASUS_WMI_DEVID_FAN_GPU,
+					       full_on ? 1 : 0,
+					       &retval);
+	else
+		return -EINVAL;
+
+	if (status || retval == 1)
+		return -ENXIO;
+
+	return 0;
+}
+
 /*
  * Check if we can read the speed of one fan. If true we assume we can also
- * control it.
+ * control it. If the old method does not work try to read the speed of the
+ * CPU and GPU fans using the DSTS wMI method.
  */
 static int asus_hwmon_get_fan_number(struct asus_wmi *asus, int *num_fans)
 {
@@ -1259,22 +1311,51 @@ static int asus_hwmon_get_fan_number(struct asus_wmi *asus, int *num_fans)
 	*num_fans = 0;
 
 	status = asus_hwmon_agfn_fan_speed_read(asus, 1, &speed);
-	if (!status)
+	if (!status) {
 		*num_fans = 1;
+		goto exit;
+	}
 
+	status = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_FAN_CPU, &speed);
+	if (!status)
+		*num_fans += 1;
+
+	status = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_FAN_GPU, &speed);
+	if (!status)
+		*num_fans += 1;
+
+	if (*num_fans > 0)
+		asus->asus_hwmon_fan_cpu_gpu = true;
+
+exit:
 	return 0;
 }
 
 static int asus_hwmon_fan_set_auto(struct asus_wmi *asus)
 {
 	int status;
+	int retval;
 
-	status = asus_hwmon_agfn_fan_speed_write(asus, 0, NULL);
-	if (status)
+	if (!asus->asus_hwmon_fan_cpu_gpu) {
+		status = asus_hwmon_agfn_fan_speed_write(asus, 0, NULL);
+		if (status)
+			return -ENXIO;
+		goto exit;
+	}
+
+	status = asus_wmi_set_devstate(ASUS_WMI_DEVID_FAN_CPU, 0, &retval);
+	if (status || retval == 1)
 		return -ENXIO;
 
-	asus->asus_hwmon_fan_manual_mode = false;
+	if (asus->asus_hwmon_num_fans == 1)
+		goto exit;
 
+	status = asus_wmi_set_devstate(ASUS_WMI_DEVID_FAN_GPU, 0, &retval);
+	if (status || retval == 1)
+		return -ENXIO;
+
+exit:
+	asus->asus_hwmon_fan_manual_mode = false;
 	return 0;
 }
 
@@ -1283,14 +1364,19 @@ static int asus_hwmon_fan_rpm_show(struct device *dev, int fan)
 	struct asus_wmi *asus = dev_get_drvdata(dev);
 	int value;
 	int ret;
+	bool full_on;
 
 	/* 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 (!asus->asus_hwmon_fan_cpu_gpu)
+		ret = asus_hwmon_agfn_fan_speed_read(asus, fan+1, &value);
+	else
+		ret = asus_wmi_fan_speed_read(asus, fan+1, &value, &full_on);
+
 	if (ret) {
-		pr_warn("reading fan speed failed: %d\n", ret);
+		pr_warn("Reading fan speed failed: %d\n", ret);
 		return -ENXIO;
 	}
 
@@ -1300,27 +1386,37 @@ static int asus_hwmon_fan_rpm_show(struct device *dev, int fan)
 static void asus_hwmon_pwm_show(struct asus_wmi *asus, int fan, int *value)
 {
 	int err;
+	bool full_on = 0;
 
 	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 (asus->asus_hwmon_fan_cpu_gpu)
+		err = asus_wmi_get_devstate(asus,
+					    ASUS_WMI_DEVID_FAN_CTRL, value);
+	else
+		err = asus_wmi_fan_speed_read(asus, fan, value, &full_on);
+
 	if (err < 0)
 		return;
 
-	*value &= 0xFF;
-
-	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;
+	if (!asus->asus_hwmon_fan_cpu_gpu) {
+		*value &= 0xFF;
+
+		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;
+		}
+	} else {
+		*value = full_on ? 0 : 255;
 	}
 }
 
@@ -1351,7 +1447,11 @@ static ssize_t pwm1_store(struct device *dev,
 
 	value = clamp(value, 0, 255);
 
-	state = asus_hwmon_agfn_fan_speed_write(asus, 1, &value);
+	if (!asus->asus_hwmon_fan_cpu_gpu)
+		state = asus_hwmon_agfn_fan_speed_write(asus, 1, &value);
+	else
+		state = asus_wmi_fan_speed_write(asus, 1, value > 0);
+
 	if (state)
 		pr_warn("Setting fan speed failed: %d\n", state);
 	else
@@ -1414,6 +1514,98 @@ static ssize_t fan1_label_show(struct device *dev,
 	return sprintf(buf, "%s\n", ASUS_FAN_DESC);
 }
 
+static ssize_t pwm2_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, 1, &value);
+
+	return sprintf(buf, "%d\n", value);
+}
+
+static ssize_t pwm2_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_wmi_fan_speed_write(asus, 1, value > 0);
+
+	if (state)
+		pr_warn("Setting fan speed failed: %d\n", state);
+	else
+		asus->asus_hwmon_fan_manual_mode = true;
+
+	return count;
+}
+
+static ssize_t fan2_input_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	int value = asus_hwmon_fan_rpm_show(dev, 1);
+
+	return sprintf(buf, "%d\n", value < 0 ? -1 : value*100);
+
+}
+
+static ssize_t pwm2_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 pwm2_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 fan2_label_show(struct device *dev,
+					  struct device_attribute *attr,
+					  char *buf)
+{
+	return sprintf(buf, "%s\n", ASUS_FAN_GPU_DESC);
+}
+
 static ssize_t asus_hwmon_temp1(struct device *dev,
 				struct device_attribute *attr,
 				char *buf)
@@ -1424,20 +1616,28 @@ static ssize_t asus_hwmon_temp1(struct device *dev,
 
 	err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_THERMAL_CTRL, &value);
 
-	if (err < 0)
+	if (err < 0) {
+		pr_warn("Reading temp failed: %d\n", err);
 		return err;
+	}
 
 	value = DECI_KELVIN_TO_CELSIUS((value & 0xFFFF)) * 1000;
 
 	return sprintf(buf, "%d\n", value);
 }
 
-/* Fan1 */
+/* Fan1 (old fan or CPU) */
 static DEVICE_ATTR_RW(pwm1);
 static DEVICE_ATTR_RW(pwm1_enable);
 static DEVICE_ATTR_RO(fan1_input);
 static DEVICE_ATTR_RO(fan1_label);
 
+/* Fan2 (GPU) */
+static DEVICE_ATTR_RW(pwm2);
+static DEVICE_ATTR_RW(pwm2_enable);
+static DEVICE_ATTR_RO(fan2_input);
+static DEVICE_ATTR_RO(fan2_label);
+
 /* Temperature */
 static DEVICE_ATTR(temp1_input, S_IRUGO, asus_hwmon_temp1, NULL);
 
@@ -1447,6 +1647,11 @@ static struct attribute *hwmon_attributes[] = {
 	&dev_attr_fan1_input.attr,
 	&dev_attr_fan1_label.attr,
 
+	&dev_attr_pwm2.attr,
+	&dev_attr_pwm2_enable.attr,
+	&dev_attr_fan2_input.attr,
+	&dev_attr_fan2_label.attr,
+
 	&dev_attr_temp1_input.attr,
 	NULL
 };
@@ -1462,7 +1667,7 @@ static umode_t asus_hwmon_sysfs_is_visible(struct kobject *kobj,
 	u32 value = ASUS_WMI_UNSUPPORTED_METHOD;
 	bool ok = true;
 
-	if (attr == &dev_attr_pwm1.attr)
+	if (attr == &dev_attr_pwm1.attr && !asus->asus_hwmon_fan_cpu_gpu)
 		dev_id = ASUS_WMI_DEVID_FAN_CTRL;
 	else if (attr == &dev_attr_temp1_input.attr)
 		dev_id = ASUS_WMI_DEVID_THERMAL_CTRL;
@@ -1473,6 +1678,11 @@ static umode_t asus_hwmon_sysfs_is_visible(struct kobject *kobj,
 	    || attr == &dev_attr_pwm1.attr
 	    || attr == &dev_attr_pwm1_enable.attr) {
 		fan_attr = 1;
+	} else if (attr == &dev_attr_fan2_input.attr
+	    || attr == &dev_attr_fan2_label.attr
+	    || attr == &dev_attr_pwm2.attr
+	    || attr == &dev_attr_pwm2_enable.attr) {
+		fan_attr = 2;
 	}
 
 	if (dev_id != -1) {
@@ -2121,6 +2331,7 @@ static int asus_wmi_fan_init(struct asus_wmi *asus)
 	asus->asus_hwmon_pwm = -1;
 	asus->asus_hwmon_num_fans = -1;
 	asus->asus_hwmon_fan_manual_mode = false;
+	asus->asus_hwmon_fan_cpu_gpu = false;
 
 	status = asus_hwmon_get_fan_number(asus, &asus->asus_hwmon_num_fans);
 	if (status) {
-- 
2.18.0

--
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux IBM ACPI]     [Linux Power Management]     [Linux Kernel]     [Linux Laptop]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Device Mapper]     [Linux Resources]

  Powered by Linux