diff options
Diffstat (limited to 'drivers/platform/x86/asus-wmi.c')
-rw-r--r-- | drivers/platform/x86/asus-wmi.c | 151 |
1 files changed, 143 insertions, 8 deletions
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index a1d85667383c8..5712bc56fa106 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -70,6 +70,7 @@ MODULE_LICENSE("GPL"); #define NOTIFY_KBD_BRTUP 0xc4 #define NOTIFY_KBD_BRTDWN 0xc5 #define NOTIFY_KBD_BRTTOGGLE 0xc7 +#define NOTIFY_KBD_FBM 0x99 #define ASUS_WMI_FNLOCK_BIOS_DISABLED BIT(0) @@ -80,6 +81,13 @@ MODULE_LICENSE("GPL"); #define ASUS_FAN_CTRL_MANUAL 1 #define ASUS_FAN_CTRL_AUTO 2 +#define ASUS_FAN_MODE_NORMAL 0 +#define ASUS_FAN_MODE_OVERBOOST 1 +#define ASUS_FAN_MODE_OVERBOOST_MASK 0x01 +#define ASUS_FAN_MODE_SILENT 2 +#define ASUS_FAN_MODE_SILENT_MASK 0x02 +#define ASUS_FAN_MODES_MASK 0x03 + #define USB_INTEL_XUSB2PR 0xD0 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31 @@ -187,6 +195,10 @@ struct asus_wmi { int asus_hwmon_num_fans; int asus_hwmon_pwm; + bool fan_mode_available; + u8 fan_mode_mask; + u8 fan_mode; + struct hotplug_slot hotplug_slot; struct mutex hotplug_lock; struct mutex wmi_lock; @@ -1483,6 +1495,116 @@ static int asus_wmi_fan_init(struct asus_wmi *asus) return 0; } +/* Fan mode *******************************************************************/ + +static int fan_mode_check_present(struct asus_wmi *asus) +{ + u32 result; + int err; + + asus->fan_mode_available = false; + + err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_FAN_MODE, &result); + if (err) { + if (err == -ENODEV) + return 0; + else + return err; + } + + if ((result & ASUS_WMI_DSTS_PRESENCE_BIT) && + (result & ASUS_FAN_MODES_MASK)) { + asus->fan_mode_available = true; + asus->fan_mode_mask = result & ASUS_FAN_MODES_MASK; + } + + return 0; +} + +static int fan_mode_write(struct asus_wmi *asus) +{ + int err; + u8 value; + u32 retval; + + value = asus->fan_mode; + + pr_info("Set fan mode: %u\n", value); + err = asus_wmi_set_devstate(ASUS_WMI_DEVID_FAN_MODE, value, &retval); + + if (err) { + pr_warn("Failed to set fan mode: %d\n", err); + return err; + } + + if (retval != 1) { + pr_warn("Failed to set fan mode (retval): 0x%x\n", retval); + return -EIO; + } + + return 0; +} + +static int fan_mode_switch_next(struct asus_wmi *asus) +{ + if (asus->fan_mode == ASUS_FAN_MODE_NORMAL) { + if (asus->fan_mode_mask & ASUS_FAN_MODE_OVERBOOST_MASK) + asus->fan_mode = ASUS_FAN_MODE_OVERBOOST; + else if (asus->fan_mode_mask & ASUS_FAN_MODE_SILENT_MASK) + asus->fan_mode = ASUS_FAN_MODE_SILENT; + } else if (asus->fan_mode == ASUS_FAN_MODE_OVERBOOST) { + if (asus->fan_mode_mask & ASUS_FAN_MODE_SILENT_MASK) + asus->fan_mode = ASUS_FAN_MODE_SILENT; + else + asus->fan_mode = ASUS_FAN_MODE_NORMAL; + } else { + asus->fan_mode = ASUS_FAN_MODE_NORMAL; + } + + return fan_mode_write(asus); +} + +static ssize_t fan_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct asus_wmi *asus = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%d\n", asus->fan_mode); +} + +static ssize_t fan_mode_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int result; + u8 new_mode; + + struct asus_wmi *asus = dev_get_drvdata(dev); + + result = kstrtou8(buf, 10, &new_mode); + if (result < 0) { + pr_warn("Trying to store invalid value\n"); + return result; + } + + if (new_mode == ASUS_FAN_MODE_OVERBOOST) { + if (!(asus->fan_mode_mask & ASUS_FAN_MODE_OVERBOOST_MASK)) + return -EINVAL; + } else if (new_mode == ASUS_FAN_MODE_SILENT) { + if (!(asus->fan_mode_mask & ASUS_FAN_MODE_SILENT_MASK)) + return -EINVAL; + } else if (new_mode != ASUS_FAN_MODE_NORMAL) { + return -EINVAL; + } + + asus->fan_mode = new_mode; + fan_mode_write(asus); + + return result; +} + +// Fan mode: 0 - normal, 1 - overboost, 2 - silent +static DEVICE_ATTR_RW(fan_mode); + /* Backlight ******************************************************************/ static int read_backlight_power(struct asus_wmi *asus) @@ -1761,6 +1883,11 @@ static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus) return; } + if (asus->fan_mode_available && code == NOTIFY_KBD_FBM) { + fan_mode_switch_next(asus); + return; + } + if (is_display_toggle(code) && asus->driver->quirks->no_display_toggle) return; @@ -1917,6 +2044,7 @@ static struct attribute *platform_attributes[] = { &dev_attr_touchpad.attr, &dev_attr_lid_resume.attr, &dev_attr_als_enable.attr, + &dev_attr_fan_mode.attr, NULL }; @@ -1938,6 +2066,8 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj, devid = ASUS_WMI_DEVID_LID_RESUME; else if (attr == &dev_attr_als_enable.attr) devid = ASUS_WMI_DEVID_ALS_ENABLE; + else if (attr == &dev_attr_fan_mode.attr) + ok = asus->fan_mode_available; if (devid != -1) ok = !(asus_wmi_get_devstate_simple(asus, devid) < 0); @@ -2037,12 +2167,7 @@ static int asus_wmi_platform_init(struct asus_wmi *asus) asus_wmi_set_devstate(ASUS_WMI_DEVID_CWAP, asus->driver->quirks->wapf, NULL); - return asus_wmi_sysfs_init(asus->platform_device); -} - -static void asus_wmi_platform_exit(struct asus_wmi *asus) -{ - asus_wmi_sysfs_exit(asus->platform_device); + return 0; } /* debugfs ********************************************************************/ @@ -2200,6 +2325,14 @@ static int asus_wmi_add(struct platform_device *pdev) if (err) goto fail_platform; + err = fan_mode_check_present(asus); + if (err) + goto fail_fan_mode; + + err = asus_wmi_sysfs_init(asus->platform_device); + if (err) + goto fail_sysfs; + err = asus_wmi_input_init(asus); if (err) goto fail_input; @@ -2277,7 +2410,9 @@ fail_leds: fail_hwmon: asus_wmi_input_exit(asus); fail_input: - asus_wmi_platform_exit(asus); + asus_wmi_sysfs_exit(asus->platform_device); +fail_sysfs: +fail_fan_mode: fail_platform: kfree(asus); return err; @@ -2294,7 +2429,7 @@ static int asus_wmi_remove(struct platform_device *device) asus_wmi_led_exit(asus); asus_wmi_rfkill_exit(asus); asus_wmi_debugfs_exit(asus); - asus_wmi_platform_exit(asus); + asus_wmi_sysfs_exit(asus->platform_device); asus_hwmon_fan_set_auto(asus); kfree(asus); |