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

platform/x86: asus-wmi: Add support for custom fan curves

Add support for custom fan curves found on some ASUS ROG laptops.

These laptops have the ability to set a custom curve for the CPU
and GPU fans via two ACPI methods.

This patch adds two pwm<N> attributes to the hwmon sysfs,
pwm1 for CPU fan, pwm2 for GPU fan. Both are under the hwmon of the
name `asus_custom_fan_curve`. There is no safety check of the set
fan curves - this must be done in userspace.

The fans have settings [1,2,3] under pwm<N>_enable:
1. Enable and write settings out
2. Disable and use factory fan mode
3. Same as 2, additionally restoring default factory curve.

Use of 2 means that the curve the user has set is still stored and
won't be erased, but the laptop will be using its default auto-fan
mode. Re-enabling the manual mode then activates the curves again.

Notes:
- pwm<N>_enable = 0 is an invalid setting.
- pwm is actually a percentage and is scaled on writing to device.

Signed-off-by: Luke D. Jones <luke@ljones.dev>
Link: https://lore.kernel.org/r/20211024033705.5595-2-luke@ljones.dev
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>

authored by

Luke D. Jones and committed by
Hans de Goede
0f0ac158 79f960e2

+564 -5
+562 -5
drivers/platform/x86/asus-wmi.c
··· 106 106 107 107 #define WMI_EVENT_MASK 0xFFFF 108 108 109 + #define FAN_CURVE_POINTS 8 110 + #define FAN_CURVE_BUF_LEN (FAN_CURVE_POINTS * 2) 111 + #define FAN_CURVE_DEV_CPU 0x00 112 + #define FAN_CURVE_DEV_GPU 0x01 113 + /* Mask to determine if setting temperature or percentage */ 114 + #define FAN_CURVE_PWM_MASK 0x04 115 + 109 116 static const char * const ashs_ids[] = { "ATK4001", "ATK4002", NULL }; 117 + 118 + static int throttle_thermal_policy_write(struct asus_wmi *); 110 119 111 120 static bool ashs_present(void) 112 121 { ··· 131 122 u32 arg0; 132 123 u32 arg1; 133 124 u32 arg2; /* At least TUF Gaming series uses 3 dword input buffer. */ 134 - u32 arg4; 125 + u32 arg3; 126 + u32 arg4; /* Some ROG laptops require a full 5 input args */ 135 127 u32 arg5; 136 128 } __packed; 137 129 ··· 183 173 FAN_TYPE_SPEC83, /* starting in Spec 8.3, use CPU_FAN_CTRL */ 184 174 }; 185 175 176 + struct fan_curve_data { 177 + bool enabled; 178 + u32 device_id; 179 + u8 temps[FAN_CURVE_POINTS]; 180 + u8 percents[FAN_CURVE_POINTS]; 181 + }; 182 + 186 183 struct asus_wmi { 187 184 int dsts_id; 188 185 int spec; ··· 236 219 237 220 bool throttle_thermal_policy_available; 238 221 u8 throttle_thermal_policy_mode; 222 + 223 + bool cpu_fan_curve_available; 224 + bool gpu_fan_curve_available; 225 + struct fan_curve_data custom_fan_curves[2]; 239 226 240 227 struct platform_profile_handler platform_profile_handler; 241 228 bool platform_profile_support; ··· 305 284 return asus_wmi_evaluate_method3(method_id, arg0, arg1, 0, retval); 306 285 } 307 286 EXPORT_SYMBOL_GPL(asus_wmi_evaluate_method); 287 + 288 + static int asus_wmi_evaluate_method5(u32 method_id, 289 + u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4, u32 *retval) 290 + { 291 + struct bios_args args = { 292 + .arg0 = arg0, 293 + .arg1 = arg1, 294 + .arg2 = arg2, 295 + .arg3 = arg3, 296 + .arg4 = arg4, 297 + }; 298 + struct acpi_buffer input = { (acpi_size) sizeof(args), &args }; 299 + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; 300 + acpi_status status; 301 + union acpi_object *obj; 302 + u32 tmp = 0; 303 + 304 + status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID, 0, method_id, 305 + &input, &output); 306 + 307 + if (ACPI_FAILURE(status)) 308 + return -EIO; 309 + 310 + obj = (union acpi_object *)output.pointer; 311 + if (obj && obj->type == ACPI_TYPE_INTEGER) 312 + tmp = (u32) obj->integer.value; 313 + 314 + if (retval) 315 + *retval = tmp; 316 + 317 + kfree(obj); 318 + 319 + if (tmp == ASUS_WMI_UNSUPPORTED_METHOD) 320 + return -ENODEV; 321 + 322 + return 0; 323 + } 324 + 325 + /* 326 + * Returns as an error if the method output is not a buffer. Typically this 327 + * means that the method called is unsupported. 328 + */ 329 + static int asus_wmi_evaluate_method_buf(u32 method_id, 330 + u32 arg0, u32 arg1, u8 *ret_buffer, size_t size) 331 + { 332 + struct bios_args args = { 333 + .arg0 = arg0, 334 + .arg1 = arg1, 335 + .arg2 = 0, 336 + }; 337 + struct acpi_buffer input = { (acpi_size) sizeof(args), &args }; 338 + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; 339 + acpi_status status; 340 + union acpi_object *obj; 341 + int err = 0; 342 + 343 + status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID, 0, method_id, 344 + &input, &output); 345 + 346 + if (ACPI_FAILURE(status)) 347 + return -EIO; 348 + 349 + obj = (union acpi_object *)output.pointer; 350 + 351 + switch (obj->type) { 352 + case ACPI_TYPE_BUFFER: 353 + if (obj->buffer.length > size) 354 + err = -ENOSPC; 355 + if (obj->buffer.length == 0) 356 + err = -ENODATA; 357 + 358 + memcpy(ret_buffer, obj->buffer.pointer, obj->buffer.length); 359 + break; 360 + case ACPI_TYPE_INTEGER: 361 + err = (u32)obj->integer.value; 362 + 363 + if (err == ASUS_WMI_UNSUPPORTED_METHOD) 364 + err = -ENODEV; 365 + /* 366 + * At least one method returns a 0 with no buffer if no arg 367 + * is provided, such as ASUS_WMI_DEVID_CPU_FAN_CURVE 368 + */ 369 + if (err == 0) 370 + err = -ENODATA; 371 + break; 372 + default: 373 + err = -ENODATA; 374 + break; 375 + } 376 + 377 + kfree(obj); 378 + 379 + if (err) 380 + return err; 381 + 382 + return 0; 383 + } 308 384 309 385 static int asus_wmi_evaluate_method_agfn(const struct acpi_buffer args) 310 386 { ··· 1924 1806 } 1925 1807 1926 1808 asus->fan_pwm_mode = state; 1809 + 1810 + /* Must set to disabled if mode is toggled */ 1811 + if (asus->cpu_fan_curve_available) 1812 + asus->custom_fan_curves[FAN_CURVE_DEV_CPU].enabled = false; 1813 + if (asus->gpu_fan_curve_available) 1814 + asus->custom_fan_curves[FAN_CURVE_DEV_GPU].enabled = false; 1815 + 1927 1816 return count; 1928 1817 } 1929 1818 ··· 2078 1953 2079 1954 static int fan_boost_mode_write(struct asus_wmi *asus) 2080 1955 { 2081 - int err; 2082 - u8 value; 2083 1956 u32 retval; 1957 + u8 value; 1958 + int err; 2084 1959 2085 1960 value = asus->fan_boost_mode; 2086 1961 ··· 2138 2013 struct device_attribute *attr, 2139 2014 const char *buf, size_t count) 2140 2015 { 2141 - int result; 2142 - u8 new_mode; 2143 2016 struct asus_wmi *asus = dev_get_drvdata(dev); 2144 2017 u8 mask = asus->fan_boost_mode_mask; 2018 + u8 new_mode; 2019 + int result; 2145 2020 2146 2021 result = kstrtou8(buf, 10, &new_mode); 2147 2022 if (result < 0) { ··· 2167 2042 2168 2043 // Fan boost mode: 0 - normal, 1 - overboost, 2 - silent 2169 2044 static DEVICE_ATTR_RW(fan_boost_mode); 2045 + 2046 + /* Custom fan curves **********************************************************/ 2047 + 2048 + static void fan_curve_copy_from_buf(struct fan_curve_data *data, u8 *buf) 2049 + { 2050 + int i; 2051 + 2052 + for (i = 0; i < FAN_CURVE_POINTS; i++) { 2053 + data->temps[i] = buf[i]; 2054 + } 2055 + 2056 + for (i = 0; i < FAN_CURVE_POINTS; i++) { 2057 + data->percents[i] = 2058 + 255 * buf[i + FAN_CURVE_POINTS] / 100; 2059 + } 2060 + } 2061 + 2062 + static int fan_curve_get_factory_default(struct asus_wmi *asus, u32 fan_dev) 2063 + { 2064 + struct fan_curve_data *curves; 2065 + u8 buf[FAN_CURVE_BUF_LEN]; 2066 + int fan_idx = 0; 2067 + u8 mode = 0; 2068 + int err; 2069 + 2070 + if (asus->throttle_thermal_policy_available) 2071 + mode = asus->throttle_thermal_policy_mode; 2072 + /* DEVID_<C/G>PU_FAN_CURVE is switched for OVERBOOST vs SILENT */ 2073 + if (mode == 2) 2074 + mode = 1; 2075 + else if (mode == 1) 2076 + mode = 2; 2077 + 2078 + if (fan_dev == ASUS_WMI_DEVID_GPU_FAN_CURVE) 2079 + fan_idx = FAN_CURVE_DEV_GPU; 2080 + 2081 + curves = &asus->custom_fan_curves[fan_idx]; 2082 + err = asus_wmi_evaluate_method_buf(asus->dsts_id, fan_dev, mode, buf, 2083 + FAN_CURVE_BUF_LEN); 2084 + if (err) 2085 + return err; 2086 + 2087 + fan_curve_copy_from_buf(curves, buf); 2088 + curves->device_id = fan_dev; 2089 + 2090 + return 0; 2091 + } 2092 + 2093 + /* Check if capability exists, and populate defaults */ 2094 + static int fan_curve_check_present(struct asus_wmi *asus, bool *available, 2095 + u32 fan_dev) 2096 + { 2097 + int err; 2098 + 2099 + *available = false; 2100 + 2101 + err = fan_curve_get_factory_default(asus, fan_dev); 2102 + if (err) { 2103 + if (err == -ENODEV) 2104 + return 0; 2105 + return err; 2106 + } 2107 + 2108 + *available = true; 2109 + return 0; 2110 + } 2111 + 2112 + /* Determine which fan the attribute is for if SENSOR_ATTR */ 2113 + static struct fan_curve_data *fan_curve_attr_select(struct asus_wmi *asus, 2114 + struct device_attribute *attr) 2115 + { 2116 + int index = to_sensor_dev_attr(attr)->index; 2117 + 2118 + return &asus->custom_fan_curves[index & FAN_CURVE_DEV_GPU]; 2119 + } 2120 + 2121 + /* Determine which fan the attribute is for if SENSOR_ATTR_2 */ 2122 + static struct fan_curve_data *fan_curve_attr_2_select(struct asus_wmi *asus, 2123 + struct device_attribute *attr) 2124 + { 2125 + int nr = to_sensor_dev_attr_2(attr)->nr; 2126 + 2127 + return &asus->custom_fan_curves[nr & FAN_CURVE_DEV_GPU]; 2128 + } 2129 + 2130 + static ssize_t fan_curve_show(struct device *dev, 2131 + struct device_attribute *attr, char *buf) 2132 + { 2133 + struct sensor_device_attribute_2 *dev_attr = to_sensor_dev_attr_2(attr); 2134 + struct asus_wmi *asus = dev_get_drvdata(dev); 2135 + struct fan_curve_data *data; 2136 + int value, index, nr; 2137 + 2138 + data = fan_curve_attr_2_select(asus, attr); 2139 + index = dev_attr->index; 2140 + nr = dev_attr->nr; 2141 + 2142 + if (nr & FAN_CURVE_PWM_MASK) 2143 + value = data->percents[index]; 2144 + else 2145 + value = data->temps[index]; 2146 + 2147 + return sysfs_emit(buf, "%d\n", value); 2148 + } 2149 + 2150 + /* 2151 + * "fan_dev" is the related WMI method such as ASUS_WMI_DEVID_CPU_FAN_CURVE. 2152 + */ 2153 + static int fan_curve_write(struct asus_wmi *asus, 2154 + struct fan_curve_data *data) 2155 + { 2156 + u32 arg1 = 0, arg2 = 0, arg3 = 0, arg4 = 0; 2157 + u8 *percents = data->percents; 2158 + u8 *temps = data->temps; 2159 + int ret, i, shift = 0; 2160 + 2161 + if (!data->enabled) 2162 + return 0; 2163 + 2164 + for (i = 0; i < FAN_CURVE_POINTS / 2; i++) { 2165 + arg1 += (temps[i]) << shift; 2166 + arg2 += (temps[i + 4]) << shift; 2167 + /* Scale to percentage for device */ 2168 + arg3 += (100 * percents[i] / 255) << shift; 2169 + arg4 += (100 * percents[i + 4] / 255) << shift; 2170 + shift += 8; 2171 + } 2172 + 2173 + return asus_wmi_evaluate_method5(ASUS_WMI_METHODID_DEVS, 2174 + data->device_id, 2175 + arg1, arg2, arg3, arg4, &ret); 2176 + } 2177 + 2178 + static ssize_t fan_curve_store(struct device *dev, 2179 + struct device_attribute *attr, const char *buf, 2180 + size_t count) 2181 + { 2182 + struct sensor_device_attribute_2 *dev_attr = to_sensor_dev_attr_2(attr); 2183 + struct asus_wmi *asus = dev_get_drvdata(dev); 2184 + struct fan_curve_data *data; 2185 + u8 value; 2186 + int err; 2187 + 2188 + int pwm = dev_attr->nr & FAN_CURVE_PWM_MASK; 2189 + int index = dev_attr->index; 2190 + 2191 + data = fan_curve_attr_2_select(asus, attr); 2192 + 2193 + err = kstrtou8(buf, 10, &value); 2194 + if (err < 0) 2195 + return err; 2196 + 2197 + if (pwm) { 2198 + data->percents[index] = value; 2199 + } else { 2200 + data->temps[index] = value; 2201 + } 2202 + 2203 + /* 2204 + * Mark as disabled so the user has to explicitly enable to apply a 2205 + * changed fan curve. This prevents potential lockups from writing out 2206 + * many changes as one-write-per-change. 2207 + */ 2208 + data->enabled = false; 2209 + 2210 + return count; 2211 + } 2212 + 2213 + static ssize_t fan_curve_enable_show(struct device *dev, 2214 + struct device_attribute *attr, char *buf) 2215 + { 2216 + struct asus_wmi *asus = dev_get_drvdata(dev); 2217 + struct fan_curve_data *data; 2218 + int out = 2; 2219 + 2220 + data = fan_curve_attr_select(asus, attr); 2221 + 2222 + if (data->enabled) 2223 + out = 1; 2224 + 2225 + return sysfs_emit(buf, "%d\n", out); 2226 + } 2227 + 2228 + static ssize_t fan_curve_enable_store(struct device *dev, 2229 + struct device_attribute *attr, 2230 + const char *buf, size_t count) 2231 + { 2232 + struct asus_wmi *asus = dev_get_drvdata(dev); 2233 + struct fan_curve_data *data; 2234 + int value, err; 2235 + 2236 + data = fan_curve_attr_select(asus, attr); 2237 + 2238 + err = kstrtoint(buf, 10, &value); 2239 + if (err < 0) 2240 + return err; 2241 + 2242 + switch (value) { 2243 + case 1: 2244 + data->enabled = true; 2245 + break; 2246 + case 2: 2247 + data->enabled = false; 2248 + break; 2249 + /* 2250 + * Auto + reset the fan curve data to defaults. Make it an explicit 2251 + * option so that users don't accidentally overwrite a set fan curve. 2252 + */ 2253 + case 3: 2254 + err = fan_curve_get_factory_default(asus, data->device_id); 2255 + if (err) 2256 + return err; 2257 + data->enabled = false; 2258 + break; 2259 + default: 2260 + return -EINVAL; 2261 + }; 2262 + 2263 + if (data->enabled) { 2264 + err = fan_curve_write(asus, data); 2265 + if (err) 2266 + return err; 2267 + } else { 2268 + /* 2269 + * For machines with throttle this is the only way to reset fans 2270 + * to default mode of operation (does not erase curve data). 2271 + */ 2272 + if (asus->throttle_thermal_policy_available) { 2273 + err = throttle_thermal_policy_write(asus); 2274 + if (err) 2275 + return err; 2276 + /* Similar is true for laptops with this fan */ 2277 + } else if (asus->fan_type == FAN_TYPE_SPEC83) { 2278 + err = asus_fan_set_auto(asus); 2279 + if (err) 2280 + return err; 2281 + } else { 2282 + /* Safeguard against fautly ACPI tables */ 2283 + err = fan_curve_get_factory_default(asus, data->device_id); 2284 + if (err) 2285 + return err; 2286 + err = fan_curve_write(asus, data); 2287 + if (err) 2288 + return err; 2289 + } 2290 + } 2291 + return count; 2292 + } 2293 + 2294 + /* CPU */ 2295 + static SENSOR_DEVICE_ATTR_RW(pwm1_enable, fan_curve_enable, FAN_CURVE_DEV_CPU); 2296 + static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point1_temp, fan_curve, 2297 + FAN_CURVE_DEV_CPU, 0); 2298 + static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point2_temp, fan_curve, 2299 + FAN_CURVE_DEV_CPU, 1); 2300 + static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point3_temp, fan_curve, 2301 + FAN_CURVE_DEV_CPU, 2); 2302 + static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point4_temp, fan_curve, 2303 + FAN_CURVE_DEV_CPU, 3); 2304 + static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point5_temp, fan_curve, 2305 + FAN_CURVE_DEV_CPU, 4); 2306 + static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point6_temp, fan_curve, 2307 + FAN_CURVE_DEV_CPU, 5); 2308 + static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point7_temp, fan_curve, 2309 + FAN_CURVE_DEV_CPU, 6); 2310 + static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point8_temp, fan_curve, 2311 + FAN_CURVE_DEV_CPU, 7); 2312 + 2313 + static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point1_pwm, fan_curve, 2314 + FAN_CURVE_DEV_CPU | FAN_CURVE_PWM_MASK, 0); 2315 + static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point2_pwm, fan_curve, 2316 + FAN_CURVE_DEV_CPU | FAN_CURVE_PWM_MASK, 1); 2317 + static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point3_pwm, fan_curve, 2318 + FAN_CURVE_DEV_CPU | FAN_CURVE_PWM_MASK, 2); 2319 + static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point4_pwm, fan_curve, 2320 + FAN_CURVE_DEV_CPU | FAN_CURVE_PWM_MASK, 3); 2321 + static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point5_pwm, fan_curve, 2322 + FAN_CURVE_DEV_CPU | FAN_CURVE_PWM_MASK, 4); 2323 + static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point6_pwm, fan_curve, 2324 + FAN_CURVE_DEV_CPU | FAN_CURVE_PWM_MASK, 5); 2325 + static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point7_pwm, fan_curve, 2326 + FAN_CURVE_DEV_CPU | FAN_CURVE_PWM_MASK, 6); 2327 + static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point8_pwm, fan_curve, 2328 + FAN_CURVE_DEV_CPU | FAN_CURVE_PWM_MASK, 7); 2329 + 2330 + /* GPU */ 2331 + static SENSOR_DEVICE_ATTR_RW(pwm2_enable, fan_curve_enable, FAN_CURVE_DEV_GPU); 2332 + static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point1_temp, fan_curve, 2333 + FAN_CURVE_DEV_GPU, 0); 2334 + static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point2_temp, fan_curve, 2335 + FAN_CURVE_DEV_GPU, 1); 2336 + static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point3_temp, fan_curve, 2337 + FAN_CURVE_DEV_GPU, 2); 2338 + static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point4_temp, fan_curve, 2339 + FAN_CURVE_DEV_GPU, 3); 2340 + static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point5_temp, fan_curve, 2341 + FAN_CURVE_DEV_GPU, 4); 2342 + static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point6_temp, fan_curve, 2343 + FAN_CURVE_DEV_GPU, 5); 2344 + static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point7_temp, fan_curve, 2345 + FAN_CURVE_DEV_GPU, 6); 2346 + static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point8_temp, fan_curve, 2347 + FAN_CURVE_DEV_GPU, 7); 2348 + 2349 + static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point1_pwm, fan_curve, 2350 + FAN_CURVE_DEV_GPU | FAN_CURVE_PWM_MASK, 0); 2351 + static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point2_pwm, fan_curve, 2352 + FAN_CURVE_DEV_GPU | FAN_CURVE_PWM_MASK, 1); 2353 + static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point3_pwm, fan_curve, 2354 + FAN_CURVE_DEV_GPU | FAN_CURVE_PWM_MASK, 2); 2355 + static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point4_pwm, fan_curve, 2356 + FAN_CURVE_DEV_GPU | FAN_CURVE_PWM_MASK, 3); 2357 + static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point5_pwm, fan_curve, 2358 + FAN_CURVE_DEV_GPU | FAN_CURVE_PWM_MASK, 4); 2359 + static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point6_pwm, fan_curve, 2360 + FAN_CURVE_DEV_GPU | FAN_CURVE_PWM_MASK, 5); 2361 + static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point7_pwm, fan_curve, 2362 + FAN_CURVE_DEV_GPU | FAN_CURVE_PWM_MASK, 6); 2363 + static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point8_pwm, fan_curve, 2364 + FAN_CURVE_DEV_GPU | FAN_CURVE_PWM_MASK, 7); 2365 + 2366 + static struct attribute *asus_fan_curve_attr[] = { 2367 + /* CPU */ 2368 + &sensor_dev_attr_pwm1_enable.dev_attr.attr, 2369 + &sensor_dev_attr_pwm1_auto_point1_temp.dev_attr.attr, 2370 + &sensor_dev_attr_pwm1_auto_point2_temp.dev_attr.attr, 2371 + &sensor_dev_attr_pwm1_auto_point3_temp.dev_attr.attr, 2372 + &sensor_dev_attr_pwm1_auto_point4_temp.dev_attr.attr, 2373 + &sensor_dev_attr_pwm1_auto_point5_temp.dev_attr.attr, 2374 + &sensor_dev_attr_pwm1_auto_point6_temp.dev_attr.attr, 2375 + &sensor_dev_attr_pwm1_auto_point7_temp.dev_attr.attr, 2376 + &sensor_dev_attr_pwm1_auto_point8_temp.dev_attr.attr, 2377 + &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr, 2378 + &sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr, 2379 + &sensor_dev_attr_pwm1_auto_point3_pwm.dev_attr.attr, 2380 + &sensor_dev_attr_pwm1_auto_point4_pwm.dev_attr.attr, 2381 + &sensor_dev_attr_pwm1_auto_point5_pwm.dev_attr.attr, 2382 + &sensor_dev_attr_pwm1_auto_point6_pwm.dev_attr.attr, 2383 + &sensor_dev_attr_pwm1_auto_point7_pwm.dev_attr.attr, 2384 + &sensor_dev_attr_pwm1_auto_point8_pwm.dev_attr.attr, 2385 + /* GPU */ 2386 + &sensor_dev_attr_pwm2_enable.dev_attr.attr, 2387 + &sensor_dev_attr_pwm2_auto_point1_temp.dev_attr.attr, 2388 + &sensor_dev_attr_pwm2_auto_point2_temp.dev_attr.attr, 2389 + &sensor_dev_attr_pwm2_auto_point3_temp.dev_attr.attr, 2390 + &sensor_dev_attr_pwm2_auto_point4_temp.dev_attr.attr, 2391 + &sensor_dev_attr_pwm2_auto_point5_temp.dev_attr.attr, 2392 + &sensor_dev_attr_pwm2_auto_point6_temp.dev_attr.attr, 2393 + &sensor_dev_attr_pwm2_auto_point7_temp.dev_attr.attr, 2394 + &sensor_dev_attr_pwm2_auto_point8_temp.dev_attr.attr, 2395 + &sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr, 2396 + &sensor_dev_attr_pwm2_auto_point2_pwm.dev_attr.attr, 2397 + &sensor_dev_attr_pwm2_auto_point3_pwm.dev_attr.attr, 2398 + &sensor_dev_attr_pwm2_auto_point4_pwm.dev_attr.attr, 2399 + &sensor_dev_attr_pwm2_auto_point5_pwm.dev_attr.attr, 2400 + &sensor_dev_attr_pwm2_auto_point6_pwm.dev_attr.attr, 2401 + &sensor_dev_attr_pwm2_auto_point7_pwm.dev_attr.attr, 2402 + &sensor_dev_attr_pwm2_auto_point8_pwm.dev_attr.attr, 2403 + NULL 2404 + }; 2405 + 2406 + static umode_t asus_fan_curve_is_visible(struct kobject *kobj, 2407 + struct attribute *attr, int idx) 2408 + { 2409 + struct device *dev = container_of(kobj, struct device, kobj); 2410 + struct asus_wmi *asus = dev_get_drvdata(dev->parent); 2411 + 2412 + /* 2413 + * Check the char instead of casting attr as there are two attr types 2414 + * involved here (attr1 and attr2) 2415 + */ 2416 + if (asus->cpu_fan_curve_available && attr->name[3] == '1') 2417 + return 0644; 2418 + 2419 + if (asus->gpu_fan_curve_available && attr->name[3] == '2') 2420 + return 0644; 2421 + 2422 + return 0; 2423 + } 2424 + 2425 + static const struct attribute_group asus_fan_curve_attr_group = { 2426 + .is_visible = asus_fan_curve_is_visible, 2427 + .attrs = asus_fan_curve_attr, 2428 + }; 2429 + __ATTRIBUTE_GROUPS(asus_fan_curve_attr); 2430 + 2431 + /* 2432 + * Must be initialised after throttle_thermal_policy_check_present() as 2433 + * we check the status of throttle_thermal_policy_available during init. 2434 + */ 2435 + static int asus_wmi_custom_fan_curve_init(struct asus_wmi *asus) 2436 + { 2437 + struct device *dev = &asus->platform_device->dev; 2438 + struct device *hwmon; 2439 + int err; 2440 + 2441 + err = fan_curve_check_present(asus, &asus->cpu_fan_curve_available, 2442 + ASUS_WMI_DEVID_CPU_FAN_CURVE); 2443 + if (err) 2444 + return err; 2445 + 2446 + err = fan_curve_check_present(asus, &asus->gpu_fan_curve_available, 2447 + ASUS_WMI_DEVID_GPU_FAN_CURVE); 2448 + if (err) 2449 + return err; 2450 + 2451 + if (!asus->cpu_fan_curve_available && !asus->gpu_fan_curve_available) 2452 + return 0; 2453 + 2454 + hwmon = devm_hwmon_device_register_with_groups( 2455 + dev, "asus_custom_fan_curve", asus, asus_fan_curve_attr_groups); 2456 + 2457 + if (IS_ERR(hwmon)) { 2458 + dev_err(dev, 2459 + "Could not register asus_custom_fan_curve device\n"); 2460 + return PTR_ERR(hwmon); 2461 + } 2462 + 2463 + return 0; 2464 + } 2170 2465 2171 2466 /* Throttle thermal policy ****************************************************/ 2172 2467 ··· 2636 2091 retval); 2637 2092 return -EIO; 2638 2093 } 2094 + 2095 + /* Must set to disabled if mode is toggled */ 2096 + if (asus->cpu_fan_curve_available) 2097 + asus->custom_fan_curves[FAN_CURVE_DEV_CPU].enabled = false; 2098 + if (asus->gpu_fan_curve_available) 2099 + asus->custom_fan_curves[FAN_CURVE_DEV_GPU].enabled = false; 2639 2100 2640 2101 return 0; 2641 2102 } ··· 3586 3035 if (err) 3587 3036 goto fail_hwmon; 3588 3037 3038 + err = asus_wmi_custom_fan_curve_init(asus); 3039 + if (err) 3040 + goto fail_custom_fan_curve; 3041 + 3589 3042 err = asus_wmi_led_init(asus); 3590 3043 if (err) 3591 3044 goto fail_leds; ··· 3661 3106 asus_wmi_sysfs_exit(asus->platform_device); 3662 3107 fail_sysfs: 3663 3108 fail_throttle_thermal_policy: 3109 + fail_custom_fan_curve: 3664 3110 fail_platform_profile_setup: 3665 3111 if (asus->platform_profile_support) 3666 3112 platform_profile_remove(); ··· 3687 3131 asus_wmi_debugfs_exit(asus); 3688 3132 asus_wmi_sysfs_exit(asus->platform_device); 3689 3133 asus_fan_set_auto(asus); 3134 + throttle_thermal_policy_set_default(asus); 3690 3135 asus_wmi_battery_exit(asus); 3691 3136 3692 3137 if (asus->platform_profile_support)
+2
include/linux/platform_data/x86/asus-wmi.h
··· 77 77 #define ASUS_WMI_DEVID_THERMAL_CTRL 0x00110011 78 78 #define ASUS_WMI_DEVID_FAN_CTRL 0x00110012 /* deprecated */ 79 79 #define ASUS_WMI_DEVID_CPU_FAN_CTRL 0x00110013 80 + #define ASUS_WMI_DEVID_CPU_FAN_CURVE 0x00110024 81 + #define ASUS_WMI_DEVID_GPU_FAN_CURVE 0x00110025 80 82 81 83 /* Power */ 82 84 #define ASUS_WMI_DEVID_PROCESSOR_STATE 0x00120012