Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

platform/x86: acer-wmi: Add fan control support

Add support for controlling the fan speed using the
SetGamingFanSpeed() and GetGamingFanSpeed() WMI methods.

This feature is only enabled if the machine has ACER_CAP_PWM enabled
and depend on ACER_CAP_HWMON for detecting the number of available
fans.

Reviewed-by: Kurt Borja <kuurtb@gmail.com>
Signed-off-by: Armin Wolf <W_Armin@gmx.de>
Link: https://patch.msgid.link/20251016180008.465593-3-W_Armin@gmx.de
[ij: if nested inside else block -> else if]
Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>

authored by

Armin Wolf and committed by
Ilpo Järvinen
0cc5153f d8e8362b

+205 -1
+205 -1
drivers/platform/x86/acer-wmi.c
··· 12 12 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 13 13 14 14 #include <linux/kernel.h> 15 + #include <linux/minmax.h> 15 16 #include <linux/module.h> 16 17 #include <linux/init.h> 17 18 #include <linux/types.h> 18 19 #include <linux/dmi.h> 20 + #include <linux/fixp-arith.h> 19 21 #include <linux/backlight.h> 20 22 #include <linux/leds.h> 21 23 #include <linux/platform_device.h> ··· 71 69 #define ACER_WMID_GET_GAMING_LED_METHODID 4 72 70 #define ACER_WMID_GET_GAMING_SYS_INFO_METHODID 5 73 71 #define ACER_WMID_SET_GAMING_FAN_BEHAVIOR_METHODID 14 72 + #define ACER_WMID_GET_GAMING_FAN_BEHAVIOR_METHODID 15 73 + #define ACER_WMID_SET_GAMING_FAN_SPEED_METHODID 16 74 + #define ACER_WMID_GET_GAMING_FAN_SPEED_METHODID 17 74 75 #define ACER_WMID_SET_GAMING_MISC_SETTING_METHODID 22 75 76 #define ACER_WMID_GET_GAMING_MISC_SETTING_METHODID 23 76 77 ··· 84 79 #define ACER_GAMING_FAN_BEHAVIOR_ID_MASK GENMASK_ULL(15, 0) 85 80 #define ACER_GAMING_FAN_BEHAVIOR_SET_CPU_MODE_MASK GENMASK(17, 16) 86 81 #define ACER_GAMING_FAN_BEHAVIOR_SET_GPU_MODE_MASK GENMASK(23, 22) 82 + #define ACER_GAMING_FAN_BEHAVIOR_GET_CPU_MODE_MASK GENMASK(9, 8) 83 + #define ACER_GAMING_FAN_BEHAVIOR_GET_GPU_MODE_MASK GENMASK(15, 14) 84 + 85 + #define ACER_GAMING_FAN_SPEED_STATUS_MASK GENMASK_ULL(7, 0) 86 + #define ACER_GAMING_FAN_SPEED_ID_MASK GENMASK_ULL(7, 0) 87 + #define ACER_GAMING_FAN_SPEED_VALUE_MASK GENMASK_ULL(15, 8) 87 88 88 89 #define ACER_GAMING_MISC_SETTING_STATUS_MASK GENMASK_ULL(7, 0) 89 90 #define ACER_GAMING_MISC_SETTING_INDEX_MASK GENMASK_ULL(7, 0) ··· 138 127 ACER_WMID_SENSOR_EXTERNAL_TEMPERATURE_2 = 0x03, 139 128 ACER_WMID_SENSOR_GPU_FAN_SPEED = 0x06, 140 129 ACER_WMID_SENSOR_GPU_TEMPERATURE = 0x0A, 130 + }; 131 + 132 + enum acer_wmi_gaming_fan_id { 133 + ACER_WMID_CPU_FAN = 0x01, 134 + ACER_WMID_GPU_FAN = 0x04, 141 135 }; 142 136 143 137 enum acer_wmi_gaming_fan_mode { ··· 308 292 #define ACER_CAP_TURBO_FAN BIT(9) 309 293 #define ACER_CAP_PLATFORM_PROFILE BIT(10) 310 294 #define ACER_CAP_HWMON BIT(11) 295 + #define ACER_CAP_PWM BIT(12) 311 296 312 297 /* 313 298 * Interface type flags ··· 403 386 u8 cpu_fans; 404 387 u8 gpu_fans; 405 388 u8 predator_v4; 389 + u8 pwm; 406 390 }; 407 391 408 392 static struct quirk_entry *quirks; ··· 423 405 if (quirks->predator_v4) 424 406 interface->capability |= ACER_CAP_PLATFORM_PROFILE | 425 407 ACER_CAP_HWMON; 408 + 409 + if (quirks->pwm) 410 + interface->capability |= ACER_CAP_PWM; 426 411 } 427 412 428 413 static int __init dmi_matched(const struct dmi_system_id *dmi) ··· 1674 1653 return 0; 1675 1654 } 1676 1655 1656 + static int WMID_gaming_get_fan_behavior(u16 fan_bitmap, enum acer_wmi_gaming_fan_mode *mode) 1657 + { 1658 + acpi_status status; 1659 + u32 input = 0; 1660 + u64 result; 1661 + int value; 1662 + 1663 + input |= FIELD_PREP(ACER_GAMING_FAN_BEHAVIOR_ID_MASK, fan_bitmap); 1664 + status = WMI_gaming_execute_u32_u64(ACER_WMID_GET_GAMING_FAN_BEHAVIOR_METHODID, input, 1665 + &result); 1666 + if (ACPI_FAILURE(status)) 1667 + return -EIO; 1668 + 1669 + /* The return status must be zero for the operation to have succeeded */ 1670 + if (FIELD_GET(ACER_GAMING_FAN_BEHAVIOR_STATUS_MASK, result)) 1671 + return -EIO; 1672 + 1673 + /* Theoretically multiple fans can be specified, but this is currently unused */ 1674 + if (fan_bitmap & ACER_GAMING_FAN_BEHAVIOR_CPU) 1675 + value = FIELD_GET(ACER_GAMING_FAN_BEHAVIOR_GET_CPU_MODE_MASK, result); 1676 + else if (fan_bitmap & ACER_GAMING_FAN_BEHAVIOR_GPU) 1677 + value = FIELD_GET(ACER_GAMING_FAN_BEHAVIOR_GET_GPU_MODE_MASK, result); 1678 + else 1679 + return -EINVAL; 1680 + 1681 + if (value < ACER_WMID_FAN_MODE_AUTO || value > ACER_WMID_FAN_MODE_CUSTOM) 1682 + return -ENXIO; 1683 + 1684 + *mode = value; 1685 + 1686 + return 0; 1687 + } 1688 + 1677 1689 static void WMID_gaming_set_fan_mode(enum acer_wmi_gaming_fan_mode mode) 1678 1690 { 1679 1691 u16 fan_bitmap = 0; ··· 1718 1664 fan_bitmap |= ACER_GAMING_FAN_BEHAVIOR_GPU; 1719 1665 1720 1666 WMID_gaming_set_fan_behavior(fan_bitmap, mode); 1667 + } 1668 + 1669 + static int WMID_gaming_set_gaming_fan_speed(u8 fan, u8 speed) 1670 + { 1671 + acpi_status status; 1672 + u64 input = 0; 1673 + u64 result; 1674 + 1675 + if (speed > 100) 1676 + return -EINVAL; 1677 + 1678 + input |= FIELD_PREP(ACER_GAMING_FAN_SPEED_ID_MASK, fan); 1679 + input |= FIELD_PREP(ACER_GAMING_FAN_SPEED_VALUE_MASK, speed); 1680 + 1681 + status = WMI_gaming_execute_u64(ACER_WMID_SET_GAMING_FAN_SPEED_METHODID, input, &result); 1682 + if (ACPI_FAILURE(status)) 1683 + return -EIO; 1684 + 1685 + switch (FIELD_GET(ACER_GAMING_FAN_SPEED_STATUS_MASK, result)) { 1686 + case 0x00: 1687 + return 0; 1688 + case 0x01: 1689 + return -ENODEV; 1690 + case 0x02: 1691 + return -EINVAL; 1692 + default: 1693 + return -ENXIO; 1694 + } 1695 + } 1696 + 1697 + static int WMID_gaming_get_gaming_fan_speed(u8 fan, u8 *speed) 1698 + { 1699 + acpi_status status; 1700 + u32 input = 0; 1701 + u64 result; 1702 + 1703 + input |= FIELD_PREP(ACER_GAMING_FAN_SPEED_ID_MASK, fan); 1704 + 1705 + status = WMI_gaming_execute_u32_u64(ACER_WMID_GET_GAMING_FAN_SPEED_METHODID, input, 1706 + &result); 1707 + if (ACPI_FAILURE(status)) 1708 + return -EIO; 1709 + 1710 + if (FIELD_GET(ACER_GAMING_FAN_SPEED_STATUS_MASK, result)) 1711 + return -ENODEV; 1712 + 1713 + *speed = FIELD_GET(ACER_GAMING_FAN_SPEED_VALUE_MASK, result); 1714 + 1715 + return 0; 1721 1716 } 1722 1717 1723 1718 static int WMID_gaming_set_misc_setting(enum acer_wmi_gaming_misc_setting setting, u8 value) ··· 2895 2792 [1] = ACER_WMID_SENSOR_GPU_FAN_SPEED, 2896 2793 }; 2897 2794 2795 + static const enum acer_wmi_gaming_fan_id acer_wmi_fan_channel_to_fan_id[] = { 2796 + [0] = ACER_WMID_CPU_FAN, 2797 + [1] = ACER_WMID_GPU_FAN, 2798 + }; 2799 + 2800 + static const u16 acer_wmi_fan_channel_to_fan_bitmap[] = { 2801 + [0] = ACER_GAMING_FAN_BEHAVIOR_CPU, 2802 + [1] = ACER_GAMING_FAN_BEHAVIOR_GPU, 2803 + }; 2804 + 2898 2805 static umode_t acer_wmi_hwmon_is_visible(const void *data, 2899 2806 enum hwmon_sensor_types type, u32 attr, 2900 2807 int channel) ··· 2916 2803 case hwmon_temp: 2917 2804 sensor_id = acer_wmi_temp_channel_to_sensor_id[channel]; 2918 2805 break; 2806 + case hwmon_pwm: 2807 + if (!has_cap(ACER_CAP_PWM)) 2808 + return 0; 2809 + 2810 + fallthrough; 2919 2811 case hwmon_fan: 2920 2812 sensor_id = acer_wmi_fan_channel_to_sensor_id[channel]; 2921 2813 break; ··· 2928 2810 return 0; 2929 2811 } 2930 2812 2931 - if (*supported_sensors & BIT(sensor_id - 1)) 2813 + if (*supported_sensors & BIT(sensor_id - 1)) { 2814 + if (type == hwmon_pwm) 2815 + return 0644; 2816 + 2932 2817 return 0444; 2818 + } 2933 2819 2934 2820 return 0; 2935 2821 } ··· 2942 2820 u32 attr, int channel, long *val) 2943 2821 { 2944 2822 u64 command = ACER_WMID_CMD_GET_PREDATOR_V4_SENSOR_READING; 2823 + enum acer_wmi_gaming_fan_mode mode; 2824 + u16 fan_bitmap; 2825 + u8 fan, speed; 2945 2826 u64 result; 2946 2827 int ret; 2947 2828 ··· 2970 2845 2971 2846 *val = FIELD_GET(ACER_PREDATOR_V4_SENSOR_READING_BIT_MASK, result); 2972 2847 return 0; 2848 + case hwmon_pwm: 2849 + switch (attr) { 2850 + case hwmon_pwm_input: 2851 + fan = acer_wmi_fan_channel_to_fan_id[channel]; 2852 + ret = WMID_gaming_get_gaming_fan_speed(fan, &speed); 2853 + if (ret < 0) 2854 + return ret; 2855 + 2856 + *val = fixp_linear_interpolate(0, 0, 100, U8_MAX, speed); 2857 + return 0; 2858 + case hwmon_pwm_enable: 2859 + fan_bitmap = acer_wmi_fan_channel_to_fan_bitmap[channel]; 2860 + ret = WMID_gaming_get_fan_behavior(fan_bitmap, &mode); 2861 + if (ret < 0) 2862 + return ret; 2863 + 2864 + switch (mode) { 2865 + case ACER_WMID_FAN_MODE_AUTO: 2866 + *val = 2; 2867 + return 0; 2868 + case ACER_WMID_FAN_MODE_TURBO: 2869 + *val = 0; 2870 + return 0; 2871 + case ACER_WMID_FAN_MODE_CUSTOM: 2872 + *val = 1; 2873 + return 0; 2874 + default: 2875 + return -ENXIO; 2876 + } 2877 + default: 2878 + return -EOPNOTSUPP; 2879 + } 2880 + default: 2881 + return -EOPNOTSUPP; 2882 + } 2883 + } 2884 + 2885 + static int acer_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type, 2886 + u32 attr, int channel, long val) 2887 + { 2888 + enum acer_wmi_gaming_fan_mode mode; 2889 + u16 fan_bitmap; 2890 + u8 fan, speed; 2891 + 2892 + switch (type) { 2893 + case hwmon_pwm: 2894 + switch (attr) { 2895 + case hwmon_pwm_input: 2896 + fan = acer_wmi_fan_channel_to_fan_id[channel]; 2897 + speed = fixp_linear_interpolate(0, 0, U8_MAX, 100, 2898 + clamp_val(val, 0, U8_MAX)); 2899 + 2900 + return WMID_gaming_set_gaming_fan_speed(fan, speed); 2901 + case hwmon_pwm_enable: 2902 + fan_bitmap = acer_wmi_fan_channel_to_fan_bitmap[channel]; 2903 + 2904 + switch (val) { 2905 + case 0: 2906 + mode = ACER_WMID_FAN_MODE_TURBO; 2907 + break; 2908 + case 1: 2909 + mode = ACER_WMID_FAN_MODE_CUSTOM; 2910 + break; 2911 + case 2: 2912 + mode = ACER_WMID_FAN_MODE_AUTO; 2913 + break; 2914 + default: 2915 + return -EINVAL; 2916 + } 2917 + 2918 + return WMID_gaming_set_fan_behavior(fan_bitmap, mode); 2919 + default: 2920 + return -EOPNOTSUPP; 2921 + } 2973 2922 default: 2974 2923 return -EOPNOTSUPP; 2975 2924 } ··· 3059 2860 HWMON_F_INPUT, 3060 2861 HWMON_F_INPUT 3061 2862 ), 2863 + HWMON_CHANNEL_INFO(pwm, 2864 + HWMON_PWM_INPUT | HWMON_PWM_ENABLE, 2865 + HWMON_PWM_INPUT | HWMON_PWM_ENABLE 2866 + ), 3062 2867 NULL 3063 2868 }; 3064 2869 3065 2870 static const struct hwmon_ops acer_wmi_hwmon_ops = { 3066 2871 .read = acer_wmi_hwmon_read, 2872 + .write = acer_wmi_hwmon_write, 3067 2873 .is_visible = acer_wmi_hwmon_is_visible, 3068 2874 }; 3069 2875