summaryrefslogtreecommitdiff
path: root/drivers/platform
diff options
context:
space:
mode:
authorDaniel Drake <drake@endlessm.com>2019-07-29 16:27:39 +0800
committerAndy Shevchenko <andriy.shevchenko@linux.intel.com>2019-07-30 14:14:38 +0300
commite3168b874321d04c160c9eb937919eb926ae232f (patch)
tree15956230570c0409fb2dd3db1ce7b9d5e91cc40e /drivers/platform
parentf1fc032192d2872caef69fb899e939bf7cc24fc7 (diff)
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>
Diffstat (limited to 'drivers/platform')
-rw-r--r--drivers/platform/x86/asus-wmi.c125
1 files changed, 100 insertions, 25 deletions
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index 25f1e256c442..34dfbed65332 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -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;
- status = asus_agfn_fan_speed_write(asus, 0, NULL);
- if (status)
+ 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,13 +1308,29 @@ static ssize_t fan1_input_show(struct device *dev,
int value;
int ret;
- /* no speed readable on manual mode */
- if (asus->fan_pwm_mode == ASUS_FAN_CTRL_MANUAL)
- return -ENXIO;
+ 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;
- ret = asus_agfn_fan_speed_read(asus, 1, &value);
- if (ret) {
- pr_warn("reading fan speed failed: %d\n", 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;
+
+ ret = asus_agfn_fan_speed_read(asus, 1, &value);
+ if (ret) {
+ pr_warn("reading fan speed failed: %d\n", ret);
+ return -ENXIO;
+ }
+ break;
+
+ default:
return -ENXIO;
}
@@ -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,25 +1362,48 @@ 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;
- switch (state) {
- case ASUS_FAN_CTRL_MANUAL:
- break;
+ 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;
+ }
- case ASUS_FAN_CTRL_AUTO:
- status = asus_fan_set_auto(asus);
- if (status)
- return status;
- break;
+ ret = asus_wmi_set_devstate(ASUS_WMI_DEVID_CPU_FAN_CTRL,
+ value, &retval);
+ if (ret)
+ return ret;
- default:
- return -EINVAL;
+ if (retval != 1)
+ return -EIO;
+ } else if (asus->fan_type == FAN_TYPE_AGFN) {
+ switch (state) {
+ case ASUS_FAN_CTRL_MANUAL:
+ break;
+
+ case ASUS_FAN_CTRL_AUTO:
+ status = asus_fan_set_auto(asus);
+ if (status)
+ return status;
+ break;
+
+ default:
+ return -EINVAL;
+ }
}
asus->fan_pwm_mode = state;
@@ -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;
- asus_fan_set_auto(asus);
- asus->fan_pwm_mode = ASUS_FAN_CTRL_AUTO;
- }
- return asus->fan_type != FAN_TYPE_NONE;
+ if (asus->fan_type == FAN_TYPE_NONE)
+ return -ENODEV;
+
+ asus_fan_set_auto(asus);
+ asus->fan_pwm_mode = ASUS_FAN_CTRL_AUTO;
+ return 0;
}
/* Fan mode *******************************************************************/