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