platform/x86: asus-wmi: fix CPU fan control on recent products

Previously, asus-wmi was using the AGFN interface and FAN_CTRL device
for CPU fan control. However, this code has been found to be not fully
working on some recent products, and having checked the spec, these
interfaces are marked as being removed from future products currently
in development.

The replacement appears to be the CPU_FAN device, added in spec version
8.3 (March 2014) and present on many modern Asus laptops.

Add support for this device, and use it whenever it is detected.
The older approach based on AGFN and FAN_CTRL is used as a fallback
on products that do not have such device.

Other than switching between automatic and full speed, there is
no fan speed control through this new interface.

Signed-off-by: Daniel Drake <drake@endlessm.com>
Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
This commit is contained in:
Daniel Drake 2019-07-29 16:27:39 +08:00 committed by Andy Shevchenko
parent f1fc032192
commit e3168b8743
2 changed files with 101 additions and 25 deletions

View File

@ -67,6 +67,7 @@ MODULE_LICENSE("GPL");
#define ASUS_FAN_SFUN_WRITE 0x07
/* Based on standard hwmon pwmX_enable values */
#define ASUS_FAN_CTRL_FULLSPEED 0
#define ASUS_FAN_CTRL_MANUAL 1
#define ASUS_FAN_CTRL_AUTO 2
@ -153,6 +154,7 @@ struct asus_rfkill {
enum fan_type {
FAN_TYPE_NONE = 0,
FAN_TYPE_AGFN, /* deprecated on newer platforms */
FAN_TYPE_SPEC83, /* starting in Spec 8.3, use CPU_FAN_CTRL */
};
struct asus_wmi {
@ -1211,10 +1213,29 @@ static bool asus_wmi_has_agfn_fan(struct asus_wmi *asus)
static int asus_fan_set_auto(struct asus_wmi *asus)
{
int status;
u32 retval;
switch (asus->fan_type) {
case FAN_TYPE_SPEC83:
status = asus_wmi_set_devstate(ASUS_WMI_DEVID_CPU_FAN_CTRL,
0, &retval);
if (status)
return status;
if (retval != 1)
return -EIO;
break;
case FAN_TYPE_AGFN:
status = asus_agfn_fan_speed_write(asus, 0, NULL);
if (status)
return -ENXIO;
break;
default:
return -ENXIO;
}
return 0;
}
@ -1287,6 +1308,17 @@ static ssize_t fan1_input_show(struct device *dev,
int value;
int ret;
switch (asus->fan_type) {
case FAN_TYPE_SPEC83:
ret = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_CPU_FAN_CTRL,
&value);
if (ret < 0)
return ret;
value &= 0xffff;
break;
case FAN_TYPE_AGFN:
/* no speed readable on manual mode */
if (asus->fan_pwm_mode == ASUS_FAN_CTRL_MANUAL)
return -ENXIO;
@ -1296,6 +1328,11 @@ static ssize_t fan1_input_show(struct device *dev,
pr_warn("reading fan speed failed: %d\n", ret);
return -ENXIO;
}
break;
default:
return -ENXIO;
}
return sprintf(buf, "%d\n", value < 0 ? -1 : value*100);
}
@ -1306,6 +1343,15 @@ static ssize_t pwm1_enable_show(struct device *dev,
{
struct asus_wmi *asus = dev_get_drvdata(dev);
/*
* Just read back the cached pwm mode.
*
* For the CPU_FAN device, the spec indicates that we should be
* able to read the device status and consult bit 19 to see if we
* are in Full On or Automatic mode. However, this does not work
* in practice on X532FL at least (the bit is always 0) and there's
* also nothing in the DSDT to indicate that this behaviour exists.
*/
return sprintf(buf, "%d\n", asus->fan_pwm_mode);
}
@ -1316,13 +1362,35 @@ static ssize_t pwm1_enable_store(struct device *dev,
struct asus_wmi *asus = dev_get_drvdata(dev);
int status = 0;
int state;
int value;
int ret;
u32 retval;
ret = kstrtouint(buf, 10, &state);
if (ret)
return ret;
if (asus->fan_type == FAN_TYPE_SPEC83) {
switch (state) { /* standard documented hwmon values */
case ASUS_FAN_CTRL_FULLSPEED:
value = 1;
break;
case ASUS_FAN_CTRL_AUTO:
value = 0;
break;
default:
return -EINVAL;
}
ret = asus_wmi_set_devstate(ASUS_WMI_DEVID_CPU_FAN_CTRL,
value, &retval);
if (ret)
return ret;
if (retval != 1)
return -EIO;
} else if (asus->fan_type == FAN_TYPE_AGFN) {
switch (state) {
case ASUS_FAN_CTRL_MANUAL:
break;
@ -1336,6 +1404,7 @@ static ssize_t pwm1_enable_store(struct device *dev,
default:
return -EINVAL;
}
}
asus->fan_pwm_mode = state;
return count;
@ -1392,9 +1461,11 @@ static umode_t asus_hwmon_sysfs_is_visible(struct kobject *kobj,
struct asus_wmi *asus = dev_get_drvdata(dev->parent);
u32 value = ASUS_WMI_UNSUPPORTED_METHOD;
if (attr == &dev_attr_fan1_input.attr
if (attr == &dev_attr_pwm1.attr) {
if (asus->fan_type != FAN_TYPE_AGFN)
return 0;
} else if (attr == &dev_attr_fan1_input.attr
|| attr == &dev_attr_fan1_label.attr
|| attr == &dev_attr_pwm1.attr
|| attr == &dev_attr_pwm1_enable.attr) {
if (asus->fan_type == FAN_TYPE_NONE)
return 0;
@ -1443,13 +1514,17 @@ static int asus_wmi_fan_init(struct asus_wmi *asus)
asus->fan_type = FAN_TYPE_NONE;
asus->agfn_pwm = -1;
if (asus_wmi_has_agfn_fan(asus)) {
if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_CPU_FAN_CTRL))
asus->fan_type = FAN_TYPE_SPEC83;
else if (asus_wmi_has_agfn_fan(asus))
asus->fan_type = FAN_TYPE_AGFN;
if (asus->fan_type == FAN_TYPE_NONE)
return -ENODEV;
asus_fan_set_auto(asus);
asus->fan_pwm_mode = ASUS_FAN_CTRL_AUTO;
}
return asus->fan_type != FAN_TYPE_NONE;
return 0;
}
/* Fan mode *******************************************************************/

View File

@ -73,6 +73,7 @@
/* Fan, Thermal */
#define ASUS_WMI_DEVID_THERMAL_CTRL 0x00110011
#define ASUS_WMI_DEVID_FAN_CTRL 0x00110012 /* deprecated */
#define ASUS_WMI_DEVID_CPU_FAN_CTRL 0x00110013
/* Power */
#define ASUS_WMI_DEVID_PROCESSOR_STATE 0x00120012