diff options
Diffstat (limited to 'sys-kernel_arch-sources-g14_files-0046-fan-curvers.patch')
-rw-r--r-- | sys-kernel_arch-sources-g14_files-0046-fan-curvers.patch | 840 |
1 files changed, 497 insertions, 343 deletions
diff --git a/sys-kernel_arch-sources-g14_files-0046-fan-curvers.patch b/sys-kernel_arch-sources-g14_files-0046-fan-curvers.patch index aa6919698f41..ce6665e3db5d 100644 --- a/sys-kernel_arch-sources-g14_files-0046-fan-curvers.patch +++ b/sys-kernel_arch-sources-g14_files-0046-fan-curvers.patch @@ -1,59 +1,60 @@ -From 094c3ed89ed4a86933f69838679f4222ba288aa4 Mon Sep 17 00:00:00 2001 +From cfd3291fa2a77bf1029646da5945d83d1021609a Mon Sep 17 00:00:00 2001 From: "Luke D. Jones" <luke@ljones.dev> Date: Sun, 29 Aug 2021 13:21:23 +1200 -Subject: [PATCH v6] asus-wmi: Add support for custom fan curves +Subject: [PATCH v14 1/1] 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 an ACPI method call. This patch enables this, -additionally enabling custom fan curves per-profile, where profile -here means each of the 3 levels of "throttle_thermal_policy". +and GPU fans via two ACPI methods. -The format for input fan curves is -"30:1,49:2,59:3,69:4,79:31,89:49,99:56,109:58" -where each block of "30:1," is one point on the curve, and 30c, 1% -of fan RPM up to 100%. This follows the internal WMI format. This -format was chosen as it is used heavily by pre-existing utilities -that were based on using the acpi_call module. Further, each pair -can be defined as "T[ c:]P[ %,]" resulting in "30c:1%,", "30c1 " -or similar with a max of two chars between values. +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> --- - drivers/platform/x86/asus-wmi.c | 488 ++++++++++++++++++++- + drivers/platform/x86/asus-wmi.c | 611 ++++++++++++++++++++- include/linux/platform_data/x86/asus-wmi.h | 2 + - 2 files changed, 488 insertions(+), 2 deletions(-) + 2 files changed, 605 insertions(+), 8 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c -index cc5811844012..dccdd41917ff 100644 +index e14fb5fa7324..7569f34bac3e 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c -@@ -106,8 +106,23 @@ module_param(fnlock_default, bool, 0444); +@@ -106,8 +106,17 @@ module_param(fnlock_default, bool, 0444); #define WMI_EVENT_MASK 0xFFFF -+/* The number of ASUS_THROTTLE_THERMAL_POLICY_* available */ -+#define NUM_FAN_CURVE_PROFILES 3 -+#define NUM_FAN_CURVE_POINTS 8 -+#define NUM_FAN_CURVE_DATA (NUM_FAN_CURVE_POINTS * 2) ++#define FAN_CURVE_POINTS 8 ++#define FAN_CURVE_BUF_LEN (FAN_CURVE_POINTS * 2) ++#define FAN_CURVE_DEV_CPU 0x00 ++#define FAN_CURVE_DEV_GPU 0x01 ++/* Mask to determine if setting temperature or percentage */ ++#define FAN_CURVE_PWM_MASK 0x04 + static const char * const ashs_ids[] = { "ATK4001", "ATK4002", NULL }; -+/* -+ * The order here matters, index positions reflect -+ * ASUS_THROTTLE_THERMAL_POLICY_<name> -+ */ -+static const char * const fan_curve_names[] = { -+ "balanced", "performance", "quiet" -+}; -+ +static int throttle_thermal_policy_write(struct asus_wmi *); + static bool ashs_present(void) { int i = 0; -@@ -122,7 +137,8 @@ struct bios_args { +@@ -122,7 +131,8 @@ struct bios_args { u32 arg0; u32 arg1; u32 arg2; /* At least TUF Gaming series uses 3 dword input buffer. */ @@ -63,49 +64,38 @@ index cc5811844012..dccdd41917ff 100644 u32 arg5; } __packed; -@@ -173,6 +189,29 @@ enum fan_type { +@@ -173,6 +183,19 @@ enum fan_type { FAN_TYPE_SPEC83, /* starting in Spec 8.3, use CPU_FAN_CTRL */ }; -+enum fan_device { -+ FAN_DEVICE_CPU = 0, -+ FAN_DEVICE_GPU, -+}; -+ -+/* -+ * Each temps[n] is paired with percents[n] -+ */ -+struct fan_curve { -+ u8 temps[NUM_FAN_CURVE_POINTS]; -+ u8 percents[NUM_FAN_CURVE_POINTS]; -+}; -+ +/* + * The related ACPI method for testing availability also returns the factory + * default fan curves. We save them here so that a user can reset custom + * settings if required. + */ +struct fan_curve_data { -+ struct fan_curve custom; -+ struct fan_curve defaults; ++ bool enabled; ++ u8 temps[FAN_CURVE_POINTS]; ++ u8 percents[FAN_CURVE_POINTS]; ++ u8 default_temps[FAN_CURVE_POINTS]; ++ u8 default_percents[FAN_CURVE_POINTS]; +}; + struct asus_wmi { int dsts_id; int spec; -@@ -220,6 +259,11 @@ struct asus_wmi { +@@ -220,6 +243,10 @@ struct asus_wmi { bool throttle_thermal_policy_available; u8 throttle_thermal_policy_mode; + bool cpu_fan_curve_available; + bool gpu_fan_curve_available; -+ bool fan_curves_enabled[NUM_FAN_CURVE_PROFILES]; -+ struct fan_curve_data throttle_fan_curves[NUM_FAN_CURVE_PROFILES][2]; ++ struct fan_curve_data custom_fan_curves[2]; + struct platform_profile_handler platform_profile_handler; bool platform_profile_support; -@@ -285,6 +329,84 @@ int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval) +@@ -285,6 +312,103 @@ int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval) } EXPORT_SYMBOL_GPL(asus_wmi_evaluate_method); @@ -151,7 +141,7 @@ index cc5811844012..dccdd41917ff 100644 + * means that the method called is unsupported. + */ +static int asus_wmi_evaluate_method_buf(u32 method_id, -+ u32 arg0, u32 arg1, u8 *ret_buffer) ++ u32 arg0, u32 arg1, u8 *ret_buffer, size_t size) +{ + struct bios_args args = { + .arg0 = arg0, @@ -162,7 +152,7 @@ index cc5811844012..dccdd41917ff 100644 + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + acpi_status status; + union acpi_object *obj; -+ u32 int_tmp = 0; ++ int err = 0; + + status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID, 0, method_id, + &input, &output); @@ -172,182 +162,228 @@ index cc5811844012..dccdd41917ff 100644 + + obj = (union acpi_object *)output.pointer; + -+ if (obj && obj->type == ACPI_TYPE_INTEGER) { -+ int_tmp = (u32) obj->integer.value; -+ if (int_tmp == ASUS_WMI_UNSUPPORTED_METHOD) -+ return -ENODEV; -+ return int_tmp; -+ } ++ switch (obj->type) { ++ case ACPI_TYPE_BUFFER: ++ if (obj->buffer.length > size) ++ err = -ENOSPC; ++ if (obj->buffer.length == 0) ++ err = -ENODATA; + -+ if (obj && obj->type == ACPI_TYPE_BUFFER) + memcpy(ret_buffer, obj->buffer.pointer, obj->buffer.length); ++ break; ++ case ACPI_TYPE_INTEGER: ++ err = (u32)obj->integer.value; ++ ++ if (err == ASUS_WMI_UNSUPPORTED_METHOD) ++ err = -ENODEV; ++ /* ++ * At least one method returns a 0 with no buffer if no arg ++ * is provided, such as ASUS_WMI_DEVID_CPU_FAN_CURVE ++ */ ++ if (err == 0) ++ err = -ENODATA; ++ break; ++ default: ++ err = -ENODATA; ++ break; ++ } + + kfree(obj); + ++ if (err) ++ return err; ++ + return 0; +} + static int asus_wmi_evaluate_method_agfn(const struct acpi_buffer args) { struct acpi_buffer input; -@@ -2043,6 +2165,330 @@ static ssize_t fan_boost_mode_store(struct device *dev, +@@ -1806,6 +1930,13 @@ static ssize_t pwm1_enable_store(struct device *dev, + } + + asus->fan_pwm_mode = state; ++ ++ /* Must set to disabled if mode is toggled */ ++ if (asus->cpu_fan_curve_available) ++ asus->custom_fan_curves[FAN_CURVE_DEV_CPU].enabled = false; ++ if (asus->gpu_fan_curve_available) ++ asus->custom_fan_curves[FAN_CURVE_DEV_GPU].enabled = false; ++ + return count; + } + +@@ -1953,9 +2084,9 @@ static int fan_boost_mode_check_present(struct asus_wmi *asus) + + static int fan_boost_mode_write(struct asus_wmi *asus) + { +- int err; +- u8 value; + u32 retval; ++ u8 value; ++ int err; + + value = asus->fan_boost_mode; + +@@ -2013,10 +2144,10 @@ static ssize_t fan_boost_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); + u8 mask = asus->fan_boost_mode_mask; ++ u8 new_mode; ++ int result; + + result = kstrtou8(buf, 10, &new_mode); + if (result < 0) { +@@ -2043,6 +2174,458 @@ static ssize_t fan_boost_mode_store(struct device *dev, // Fan boost mode: 0 - normal, 1 - overboost, 2 - silent static DEVICE_ATTR_RW(fan_boost_mode); +/* Custom fan curves per-profile **********************************************/ + -+static void copy_fan_curve_buf(struct fan_curve *data, u8 *buf) ++static void fan_curve_copy_from_buf(struct fan_curve_data *data, u8 *buf) +{ + int i; + -+ for (i = 0; i < NUM_FAN_CURVE_POINTS; i++) ++ for (i = 0; i < FAN_CURVE_POINTS; i++) { + data->temps[i] = buf[i]; ++ data->default_temps[i] = buf[i]; ++ } + -+ for (i = 0; i < NUM_FAN_CURVE_POINTS; i++) -+ data->percents[i] = buf[i + 8]; ++ for (i = 0; i < FAN_CURVE_POINTS; i++) { ++ data->percents[i] = ++ 255 * buf[i + FAN_CURVE_POINTS] / 100; ++ data->default_percents[i] = ++ 255 * buf[i + FAN_CURVE_POINTS] / 100; ++ } +} + -+static void init_fan_curve(struct fan_curve_data *curves, -+ u8 *buf, u32 dev) ++static int fan_curve_get_factory_default(struct asus_wmi *asus, u32 fan_dev) +{ -+ copy_fan_curve_buf(&curves->custom, buf); -+ copy_fan_curve_buf(&curves->defaults, buf); ++ struct fan_curve_data *curves; ++ u8 buf[FAN_CURVE_BUF_LEN]; ++ int fan_idx = 0; ++ u8 mode = 0; ++ int err; ++ ++ if (asus->throttle_thermal_policy_available) ++ mode = asus->throttle_thermal_policy_mode; ++ /* DEVID_<C/G>PU_FAN_CURVE is switched for OVERBOOST vs SILENT */ ++ if (mode == 2) ++ mode = 1; ++ else if (mode == 1) ++ mode = 2; ++ ++ if (fan_dev == ASUS_WMI_DEVID_GPU_FAN_CURVE) ++ fan_idx = FAN_CURVE_DEV_GPU; ++ ++ curves = &asus->custom_fan_curves[fan_idx]; ++ err = asus_wmi_evaluate_method_buf(asus->dsts_id, fan_dev, mode, buf, ++ FAN_CURVE_BUF_LEN); ++ if (err) ++ return err; ++ ++ fan_curve_copy_from_buf(curves, buf); ++ ++ return 0; +} + +/* -+ * Check if the ability to set fan curves on either fan exists, and store the -+ * defaults for recall later plus to provide users with a starting point. -+ * -+ * "dev" is either CPU_FAN_CURVE or GPU_FAN_CURVE. ++ * Check if capability exists, and populate defaults. + */ -+static int custom_fan_check_present(struct asus_wmi *asus, -+ bool *available, u32 dev) ++static int fan_curve_check_present(struct asus_wmi *asus, bool *available, ++ u32 fan_dev) +{ -+ struct fan_curve_data *curves; -+ u8 buf[NUM_FAN_CURVE_DATA]; -+ int fan_idx = 0; + int err; + + *available = false; -+ if (dev == ASUS_WMI_DEVID_GPU_FAN_CURVE) -+ fan_idx = 1; + -+ /* Balanced default */ -+ curves = -+ &asus->throttle_fan_curves[ASUS_THROTTLE_THERMAL_POLICY_DEFAULT][fan_idx]; -+ err = asus_wmi_evaluate_method_buf(asus->dsts_id, dev, 0, buf); ++ err = fan_curve_get_factory_default(asus, fan_dev); + if (err) { + if (err == -ENODEV) + return 0; + return err; + } -+ init_fan_curve(curves, buf, dev); -+ -+ /* -+ * Quiet default. The index num for ACPI method does not match the -+ * throttle_thermal number, same for Performance. -+ */ -+ curves = -+ &asus->throttle_fan_curves[ASUS_THROTTLE_THERMAL_POLICY_SILENT][fan_idx]; -+ err = asus_wmi_evaluate_method_buf(asus->dsts_id, dev, 1, buf); -+ if (err) { -+ if (err == -ENODEV) -+ return 0; -+ return err; -+ } -+ init_fan_curve(curves, buf, dev); -+ -+ /* Performance default */ -+ curves = -+ &asus->throttle_fan_curves[ASUS_THROTTLE_THERMAL_POLICY_OVERBOOST][fan_idx]; -+ err = asus_wmi_evaluate_method_buf(asus->dsts_id, dev, 2, buf); -+ if (err) { -+ if (err == -ENODEV) -+ return 0; -+ return err; -+ } -+ init_fan_curve(curves, buf, dev); + + *available = true; + return 0; +} + ++static struct fan_curve_data *fan_curve_data_select(struct asus_wmi *asus, ++ struct device_attribute *attr) ++{ ++ /* Determine which fan the attribute is for */ ++ int nr = to_sensor_dev_attr_2(attr)->nr; ++ int fan = nr & FAN_CURVE_DEV_GPU; ++ ++ return &asus->custom_fan_curves[fan]; ++} ++ +static ssize_t fan_curve_show(struct device *dev, -+ struct device_attribute *attr, char *buf) ++ struct device_attribute *attr, char *buf) +{ + struct asus_wmi *asus = dev_get_drvdata(dev); -+ /* index maps to ASUS_THROTTLE_THERMAL_POLICY_DEFAULT */ ++ /* Determine if temperature or pwm */ + int nr = to_sensor_dev_attr_2(attr)->nr; -+ /* dev maps to ASUS_WMI_DEVID_[C/G]PU_FAN_CURVE */ -+ int index = to_sensor_dev_attr_2(attr)->index; ++ struct fan_curve_data *data; ++ int value, index; + -+ struct fan_curve *dat = &asus->throttle_fan_curves[nr][index].custom; -+ int len = 0; -+ int i = 0; ++ data = fan_curve_data_select(asus, attr); ++ index = to_sensor_dev_attr_2(attr)->index; + -+ for (i = 0; i < NUM_FAN_CURVE_POINTS; i++) { -+ len += sysfs_emit_at(buf, len, "%dc:%d%%", -+ dat->temps[i], dat->percents[i]); -+ if (i < NUM_FAN_CURVE_POINTS - 1) -+ len += sysfs_emit_at(buf, len, ","); -+ } -+ len += sysfs_emit_at(buf, len, "\n"); -+ return len; ++ if (nr & FAN_CURVE_PWM_MASK) ++ value = data->percents[index]; ++ else ++ value = data->temps[index]; ++ ++ return sysfs_emit(buf, "%d\n", value); +} + +/* -+ * "dev" is the related WMI method such as ASUS_WMI_DEVID_CPU_FAN_CURVE. ++ * "fan_dev" is the related WMI method such as ASUS_WMI_DEVID_CPU_FAN_CURVE. + */ -+static int fan_curve_write(struct asus_wmi *asus, u32 dev, -+ struct fan_curve *curve) ++static int fan_curve_write(struct asus_wmi *asus, ++ struct device_attribute *attr, u32 fan_dev) +{ -+ int ret; -+ u32 arg1, arg2, arg3, arg4; -+ -+ arg1 = curve->temps[0]; -+ arg2 = curve->temps[4]; -+ arg1 += curve->temps[1] << 8; -+ arg2 += curve->temps[5] << 8; -+ arg1 += curve->temps[2] << 16; -+ arg2 += curve->temps[6] << 16; -+ arg1 += curve->temps[3] << 24; -+ arg2 += curve->temps[7] << 24; -+ -+ -+ arg3 = curve->percents[0]; -+ arg4 = curve->percents[4]; -+ arg3 += curve->percents[1] << 8; -+ arg4 += curve->percents[5] << 8; -+ arg3 += curve->percents[2] << 16; -+ arg4 += curve->percents[6] << 16; -+ arg3 += curve->percents[3] << 24; -+ arg4 += curve->percents[7] << 24; -+ -+ return asus_wmi_evaluate_method5(ASUS_WMI_METHODID_DEVS, dev, -+ arg1, arg2, arg3, arg4, &ret); ++ struct fan_curve_data *data = fan_curve_data_select(asus, attr); ++ u32 arg1 = 0, arg2 = 0, arg3 = 0, arg4 = 0; ++ u8 *percents = data->percents; ++ u8 *temps = data->temps; ++ int ret, i, shift = 0; ++ ++ for (i = 0; i < FAN_CURVE_POINTS / 2; i++) { ++ arg1 += (temps[i]) << shift; ++ arg2 += (temps[i + 4]) << shift; ++ /* Scale to percentage for device */ ++ arg3 += (100 * percents[i] / 255) << shift; ++ arg4 += (100 * percents[i + 4] / 255) << shift; ++ shift += 8; ++ } ++ ++ return asus_wmi_evaluate_method5(ASUS_WMI_METHODID_DEVS, fan_dev, arg1, ++ arg2, arg3, arg4, &ret); +} + +/* -+ * Called only by throttle_thermal_policy_write() ++ * Called on curve enable/disable. This should be the only way to write out the ++ * fan curves. This avoids potential lockups on write to ACPI for every change. + */ -+static int fan_curve_write_data(struct asus_wmi *asus) ++static int fan_curve_write_data(struct asus_wmi *asus, ++ struct device_attribute *attr) +{ -+ struct fan_curve_data *cpu; -+ struct fan_curve_data *gpu; -+ int err, mode; -+ -+ mode = asus->throttle_thermal_policy_mode; -+ cpu = &asus->throttle_fan_curves[mode][FAN_DEVICE_CPU]; -+ gpu = &asus->throttle_fan_curves[mode][FAN_DEVICE_GPU]; ++ int err; + -+ if (asus->fan_curves_enabled[mode]) { -+ err = fan_curve_write(asus, ASUS_WMI_DEVID_CPU_FAN_CURVE, &cpu->custom); ++ if (asus->cpu_fan_curve_available) { ++ err = fan_curve_write(asus, attr, ASUS_WMI_DEVID_CPU_FAN_CURVE); + if (err) + return err; + } + -+ if (asus->fan_curves_enabled[mode]) { -+ err = fan_curve_write(asus, ASUS_WMI_DEVID_GPU_FAN_CURVE, &gpu->custom); ++ if (asus->gpu_fan_curve_available) { ++ err = fan_curve_write(asus, attr, ASUS_WMI_DEVID_GPU_FAN_CURVE); + if (err) + return err; + } @@ -355,221 +391,337 @@ index cc5811844012..dccdd41917ff 100644 + return 0; +} + -+/* -+ * The expected input is 8 sets of number pairs, where "53c30%" temperacture -+ * and fan RPM percentage. The pair can be of the format "T[ c:]P[ %,]". -+ * -+ */ +static ssize_t fan_curve_store(struct device *dev, -+ struct device_attribute *attr, const char *buf, size_t count) ++ struct device_attribute *attr, const char *buf, ++ size_t count) +{ + struct asus_wmi *asus = dev_get_drvdata(dev); -+ u8 temps[NUM_FAN_CURVE_POINTS]; -+ u8 percents[NUM_FAN_CURVE_POINTS]; -+ /* tmp1 and tmp2 exist only to allow matching to succeed */ -+ char *tmp1; -+ char *tmp2; ++ struct fan_curve_data *data; ++ u8 value; + int err; + -+ /* index maps to ASUS_THROTTLE_THERMAL_POLICY_DEFAULT */ -+ int nr = to_sensor_dev_attr_2(attr)->nr; -+ /* dev maps to ASUS_WMI_DEVID_[C/G]PU_FAN_CURVE */ + int index = to_sensor_dev_attr_2(attr)->index; -+ /* Variable format, values must at least be separated by these */ -+ char part[] = "%d%2[ c:]%d%2[ %%,\n]%n"; -+ u32 prev_percent = 0; -+ u32 prev_temp = 0; -+ u32 percent = 0; -+ u32 temp = 0; -+ int len = 0; -+ int idx = 0; -+ int at = 0; -+ -+ struct fan_curve_data *curve = &asus->throttle_fan_curves[nr][index]; -+ -+ /* Allow a user to write "" or " " to erase a curve setting */ -+ if (strlen(buf) <= 1 || strcmp(buf, "\n") == 0) { -+ memcpy(&curve->custom, &curve->defaults, NUM_FAN_CURVE_DATA); -+ err = throttle_thermal_policy_write(asus); -+ if (err) -+ return err; -+ return count; -+ } ++ int nr = to_sensor_dev_attr_2(attr)->nr; ++ int pwm = nr & FAN_CURVE_PWM_MASK; + -+ /* parse the buf */ -+ while (sscanf(&buf[at], part, &temp, &tmp1, &percent, &tmp2, &len) == 4) { -+ if (temp < prev_temp || percent < prev_percent) { -+ pr_info("Fan curve invalid: a value is sequentially lower"); -+ return -EINVAL; -+ } -+ -+ if (percent > 100) { -+ pr_info("Fan curve invalid: percentage > 100"); -+ return -EINVAL; -+ } -+ -+ prev_temp = temp; -+ prev_percent = percent; -+ -+ temps[idx] = temp; -+ percents[idx] = percent; -+ at += len; -+ idx += 1; -+ } ++ data = fan_curve_data_select(asus, attr); + -+ if (idx != NUM_FAN_CURVE_POINTS) { -+ pr_info("Fan curve invalid: incomplete string: %d", idx); -+ return -EINVAL; -+ } ++ err = kstrtou8(buf, 10, &value); ++ if (err < 0) ++ return err; + -+ memcpy(&curve->custom.temps, &temps, NUM_FAN_CURVE_POINTS); -+ memcpy(&curve->custom.percents, &percents, NUM_FAN_CURVE_POINTS); ++ if (pwm) { ++ data->percents[index] = value; ++ } else { ++ data->temps[index] = value; ++ } + -+ /* Maybe activate fan curve if in associated mode */ -+ err = throttle_thermal_policy_write(asus); -+ if (err) -+ return err; ++ /* ++ * Mark as disabled so the user has to explicitly enable to apply a ++ * changed fan curve. This prevents potential lockups from writing out ++ * many changes as one-write-per-change. ++ */ ++ data->enabled = false; + + return count; +} + -+static SENSOR_DEVICE_ATTR_2_RW(cpu_fan_curve_balanced, fan_curve, -+ ASUS_THROTTLE_THERMAL_POLICY_DEFAULT, -+ FAN_DEVICE_CPU); -+static SENSOR_DEVICE_ATTR_2_RW(cpu_fan_curve_performance, fan_curve, -+ ASUS_THROTTLE_THERMAL_POLICY_OVERBOOST, -+ FAN_DEVICE_CPU); -+static SENSOR_DEVICE_ATTR_2_RW(cpu_fan_curve_quiet, fan_curve, -+ ASUS_THROTTLE_THERMAL_POLICY_SILENT, -+ FAN_DEVICE_CPU); -+ -+static SENSOR_DEVICE_ATTR_2_RW(gpu_fan_curve_balanced, fan_curve, -+ ASUS_THROTTLE_THERMAL_POLICY_DEFAULT, -+ FAN_DEVICE_GPU); -+static SENSOR_DEVICE_ATTR_2_RW(gpu_fan_curve_performance, fan_curve, -+ ASUS_THROTTLE_THERMAL_POLICY_OVERBOOST, -+ FAN_DEVICE_GPU); -+static SENSOR_DEVICE_ATTR_2_RW(gpu_fan_curve_quiet, fan_curve, -+ ASUS_THROTTLE_THERMAL_POLICY_SILENT, -+ FAN_DEVICE_GPU); -+ -+/* -+ * Profiles with enabled fan curve setting -+ */ -+ -+static int enabled_fan_curve_profiles_write(struct asus_wmi *asus, -+ const char *names) ++static ssize_t fan_curve_enable_show(struct device *dev, ++ struct device_attribute *attr, char *buf) +{ -+ char *buf, *set, *set_end; -+ int err, index; ++ struct asus_wmi *asus = dev_get_drvdata(dev); ++ struct fan_curve_data *data = fan_curve_data_select(asus, attr); ++ int out = 2; + -+ buf = set_end = kstrdup(names, GFP_KERNEL); ++ if (data->enabled) ++ out = 1; + -+ /* Reset before checking */ -+ asus->fan_curves_enabled[ASUS_THROTTLE_THERMAL_POLICY_DEFAULT] = false; -+ asus->fan_curves_enabled[ASUS_THROTTLE_THERMAL_POLICY_SILENT] = false; -+ asus->fan_curves_enabled[ASUS_THROTTLE_THERMAL_POLICY_OVERBOOST] = false; ++ return sysfs_emit(buf, "%d\n", out); ++} + -+ while ((set = strsep(&set_end, " ")) != NULL) { -+ index = sysfs_match_string(fan_curve_names, set); -+ if (index >= 0) -+ asus->fan_curves_enabled[index] = true; -+ } ++static int fan_curve_set_default(struct asus_wmi *asus) ++{ ++ int err; + -+ err = throttle_thermal_policy_write(asus); ++ err = fan_curve_get_factory_default( ++ asus, ASUS_WMI_DEVID_CPU_FAN_CURVE); + if (err) + return err; + -+ kfree(buf); -+ ++ err = fan_curve_get_factory_default( ++ asus, ASUS_WMI_DEVID_GPU_FAN_CURVE); ++ if (err) ++ return err; + return 0; +} + -+static ssize_t enabled_fan_curve_profiles_show(struct device *dev, -+ struct device_attribute *attr, char *buf) ++static ssize_t fan_curve_enable_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) +{ + struct asus_wmi *asus = dev_get_drvdata(dev); -+ int i, len = 0; ++ struct fan_curve_data *data; ++ int value; ++ int err; ++ ++ data = fan_curve_data_select(asus, attr); ++ ++ err = kstrtoint(buf, 10, &value); ++ if (err < 0) ++ return err; ++ ++ switch (value) { ++ case 1: ++ data->enabled = true; ++ break; ++ case 2: ++ data->enabled = false; ++ break; ++ /* ++ * Auto + reset the fan curve data to defaults. Make it an explicit ++ * option so that users don't accidentally overwrite a set fan curve. ++ */ ++ case 3: ++ err = fan_curve_set_default(asus); ++ if (err) ++ return err; ++ data->enabled = false; ++ break; ++ default: ++ return -EINVAL; ++ }; ++ ++ /* ++ * For machines with throttle this is the only way to reset fans to ++ * default mode of operation (does not erase curve data). ++ */ ++ if (asus->throttle_thermal_policy_available && !data->enabled) { ++ err = throttle_thermal_policy_write(asus); ++ if (err) ++ return err; ++ } ++ /* Similar is true for laptops with this fan */ ++ if (asus->fan_type == FAN_TYPE_SPEC83) { ++ err = asus_fan_set_auto(asus); ++ if (err) ++ return err; ++ } ++ /* ++ * Machines without either need to write their defaults back always. ++ * This is more of a safeguard against ASUS faulty ACPI tables. ++ */ ++ if (!asus->throttle_thermal_policy_available ++ && asus->fan_type != FAN_TYPE_SPEC83 && !data->enabled) { ++ err = fan_curve_set_default(asus); ++ if (err) ++ return err; ++ err = fan_curve_write_data(asus, attr); ++ if (err) ++ return err; ++ } + -+ for (i = 0; i < NUM_FAN_CURVE_PROFILES; i++) { -+ if (asus->fan_curves_enabled[i]) { -+ len += sysfs_emit_at(buf, len, fan_curve_names[i]); -+ len += sysfs_emit_at(buf, len, " "); -+ } ++ if (data->enabled) { ++ err = fan_curve_write_data(asus, attr); ++ if (err) ++ return err; + } + -+ len += sysfs_emit_at(buf, len, "\n"); -+ return len; ++ return count; +} + -+static ssize_t enabled_fan_curve_profiles_store(struct device *dev, -+ struct device_attribute *attr, -+ const char *buf, size_t count) ++/* CPU */ ++static SENSOR_DEVICE_ATTR_RW(pwm1_enable, fan_curve_enable, FAN_CURVE_DEV_CPU); ++static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point1_temp, fan_curve, ++ FAN_CURVE_DEV_CPU, 0); ++static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point2_temp, fan_curve, ++ FAN_CURVE_DEV_CPU, 1); ++static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point3_temp, fan_curve, ++ FAN_CURVE_DEV_CPU, 2); ++static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point4_temp, fan_curve, ++ FAN_CURVE_DEV_CPU, 3); ++static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point5_temp, fan_curve, ++ FAN_CURVE_DEV_CPU, 4); ++static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point6_temp, fan_curve, ++ FAN_CURVE_DEV_CPU, 5); ++static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point7_temp, fan_curve, ++ FAN_CURVE_DEV_CPU, 6); ++static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point8_temp, fan_curve, ++ FAN_CURVE_DEV_CPU, 7); ++ ++static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point1_pwm, fan_curve, ++ FAN_CURVE_DEV_CPU | FAN_CURVE_PWM_MASK, 0); ++static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point2_pwm, fan_curve, ++ FAN_CURVE_DEV_CPU | FAN_CURVE_PWM_MASK, 1); ++static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point3_pwm, fan_curve, ++ FAN_CURVE_DEV_CPU | FAN_CURVE_PWM_MASK, 2); ++static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point4_pwm, fan_curve, ++ FAN_CURVE_DEV_CPU | FAN_CURVE_PWM_MASK, 3); ++static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point5_pwm, fan_curve, ++ FAN_CURVE_DEV_CPU | FAN_CURVE_PWM_MASK, 4); ++static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point6_pwm, fan_curve, ++ FAN_CURVE_DEV_CPU | FAN_CURVE_PWM_MASK, 5); ++static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point7_pwm, fan_curve, ++ FAN_CURVE_DEV_CPU | FAN_CURVE_PWM_MASK, 6); ++static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point8_pwm, fan_curve, ++ FAN_CURVE_DEV_CPU | FAN_CURVE_PWM_MASK, 7); ++ ++/* GPU */ ++static SENSOR_DEVICE_ATTR_RW(pwm2_enable, fan_curve_enable, FAN_CURVE_DEV_GPU); ++static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point1_temp, fan_curve, ++ FAN_CURVE_DEV_GPU, 0); ++static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point2_temp, fan_curve, ++ FAN_CURVE_DEV_GPU, 1); ++static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point3_temp, fan_curve, ++ FAN_CURVE_DEV_GPU, 2); ++static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point4_temp, fan_curve, ++ FAN_CURVE_DEV_GPU, 3); ++static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point5_temp, fan_curve, ++ FAN_CURVE_DEV_GPU, 4); ++static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point6_temp, fan_curve, ++ FAN_CURVE_DEV_GPU, 5); ++static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point7_temp, fan_curve, ++ FAN_CURVE_DEV_GPU, 6); ++static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point8_temp, fan_curve, ++ FAN_CURVE_DEV_GPU, 7); ++ ++static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point1_pwm, fan_curve, ++ FAN_CURVE_DEV_GPU | FAN_CURVE_PWM_MASK, 0); ++static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point2_pwm, fan_curve, ++ FAN_CURVE_DEV_GPU | FAN_CURVE_PWM_MASK, 1); ++static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point3_pwm, fan_curve, ++ FAN_CURVE_DEV_GPU | FAN_CURVE_PWM_MASK, 2); ++static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point4_pwm, fan_curve, ++ FAN_CURVE_DEV_GPU | FAN_CURVE_PWM_MASK, 3); ++static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point5_pwm, fan_curve, ++ FAN_CURVE_DEV_GPU | FAN_CURVE_PWM_MASK, 4); ++static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point6_pwm, fan_curve, ++ FAN_CURVE_DEV_GPU | FAN_CURVE_PWM_MASK, 5); ++static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point7_pwm, fan_curve, ++ FAN_CURVE_DEV_GPU | FAN_CURVE_PWM_MASK, 6); ++static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point8_pwm, fan_curve, ++ FAN_CURVE_DEV_GPU | FAN_CURVE_PWM_MASK, 7); ++ ++static struct attribute *asus_fan_curve_attr[] = { ++ /* CPU */ ++ &sensor_dev_attr_pwm1_enable.dev_attr.attr, ++ &sensor_dev_attr_pwm1_auto_point1_temp.dev_attr.attr, ++ &sensor_dev_attr_pwm1_auto_point2_temp.dev_attr.attr, ++ &sensor_dev_attr_pwm1_auto_point3_temp.dev_attr.attr, ++ &sensor_dev_attr_pwm1_auto_point4_temp.dev_attr.attr, ++ &sensor_dev_attr_pwm1_auto_point5_temp.dev_attr.attr, ++ &sensor_dev_attr_pwm1_auto_point6_temp.dev_attr.attr, ++ &sensor_dev_attr_pwm1_auto_point7_temp.dev_attr.attr, ++ &sensor_dev_attr_pwm1_auto_point8_temp.dev_attr.attr, ++ &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr, ++ &sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr, ++ &sensor_dev_attr_pwm1_auto_point3_pwm.dev_attr.attr, ++ &sensor_dev_attr_pwm1_auto_point4_pwm.dev_attr.attr, ++ &sensor_dev_attr_pwm1_auto_point5_pwm.dev_attr.attr, ++ &sensor_dev_attr_pwm1_auto_point6_pwm.dev_attr.attr, ++ &sensor_dev_attr_pwm1_auto_point7_pwm.dev_attr.attr, ++ &sensor_dev_attr_pwm1_auto_point8_pwm.dev_attr.attr, ++ /* GPU */ ++ &sensor_dev_attr_pwm2_enable.dev_attr.attr, ++ &sensor_dev_attr_pwm2_auto_point1_temp.dev_attr.attr, ++ &sensor_dev_attr_pwm2_auto_point2_temp.dev_attr.attr, ++ &sensor_dev_attr_pwm2_auto_point3_temp.dev_attr.attr, ++ &sensor_dev_attr_pwm2_auto_point4_temp.dev_attr.attr, ++ &sensor_dev_attr_pwm2_auto_point5_temp.dev_attr.attr, ++ &sensor_dev_attr_pwm2_auto_point6_temp.dev_attr.attr, ++ &sensor_dev_attr_pwm2_auto_point7_temp.dev_attr.attr, ++ &sensor_dev_attr_pwm2_auto_point8_temp.dev_attr.attr, ++ &sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr, ++ &sensor_dev_attr_pwm2_auto_point2_pwm.dev_attr.attr, ++ &sensor_dev_attr_pwm2_auto_point3_pwm.dev_attr.attr, ++ &sensor_dev_attr_pwm2_auto_point4_pwm.dev_attr.attr, ++ &sensor_dev_attr_pwm2_auto_point5_pwm.dev_attr.attr, ++ &sensor_dev_attr_pwm2_auto_point6_pwm.dev_attr.attr, ++ &sensor_dev_attr_pwm2_auto_point7_pwm.dev_attr.attr, ++ &sensor_dev_attr_pwm2_auto_point8_pwm.dev_attr.attr, ++ NULL ++}; ++ ++static umode_t asus_fan_curve_is_visible(struct kobject *kobj, ++ struct attribute *attr, int idx) +{ -+ struct asus_wmi *asus = dev_get_drvdata(dev); ++ struct device *dev = container_of(kobj, struct device, kobj); ++ struct asus_wmi *asus = dev_get_drvdata(dev->parent); ++ ++ if (asus->cpu_fan_curve_available) ++ return 0644; ++ ++ if (asus->gpu_fan_curve_available) ++ return 0644; ++ ++ return 0; ++} ++ ++static const struct attribute_group asus_fan_curve_attr_group = { ++ .is_visible = asus_fan_curve_is_visible, ++ .attrs = asus_fan_curve_attr, ++}; ++__ATTRIBUTE_GROUPS(asus_fan_curve_attr); ++ ++/* ++ * Must be initialised after throttle_thermal_policy_check_present() as ++ * we check the status of throttle_thermal_policy_available during init. ++ */ ++static int asus_wmi_custom_fan_curve_init(struct asus_wmi *asus) ++{ ++ struct device *dev = &asus->platform_device->dev; ++ struct device *hwmon; + int err; + -+ err = enabled_fan_curve_profiles_write(asus, buf); ++ err = fan_curve_check_present(asus, &asus->cpu_fan_curve_available, ++ ASUS_WMI_DEVID_CPU_FAN_CURVE); + if (err) + return err; + -+ return count; -+} ++ err = fan_curve_check_present(asus, &asus->gpu_fan_curve_available, ++ ASUS_WMI_DEVID_GPU_FAN_CURVE); ++ if (err) ++ return err; + -+static DEVICE_ATTR_RW(enabled_fan_curve_profiles); ++ hwmon = devm_hwmon_device_register_with_groups( ++ dev, "asus_custom_fan_curve", asus, asus_fan_curve_attr_groups); ++ ++ if (IS_ERR(hwmon)) { ++ dev_err(dev, ++ "Could not register asus_custom_fan_curve device\n"); ++ return PTR_ERR(hwmon); ++ } ++ ++ return 0; ++} + /* Throttle thermal policy ****************************************************/ static int throttle_thermal_policy_check_present(struct asus_wmi *asus) -@@ -2092,6 +2538,12 @@ static int throttle_thermal_policy_write(struct asus_wmi *asus) +@@ -2053,8 +2636,8 @@ static int throttle_thermal_policy_check_present(struct asus_wmi *asus) + asus->throttle_thermal_policy_available = false; + + err = asus_wmi_get_devstate(asus, +- ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY, +- &result); ++ ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY, ++ &result); + if (err) { + if (err == -ENODEV) + return 0; +@@ -2092,6 +2675,12 @@ static int throttle_thermal_policy_write(struct asus_wmi *asus) return -EIO; } -+ if (asus->cpu_fan_curve_available || asus->gpu_fan_curve_available) { -+ err = fan_curve_write_data(asus); -+ if (err) -+ return err; -+ } ++ /* Must set to disabled if mode is toggled */ ++ if (asus->cpu_fan_curve_available) ++ asus->custom_fan_curves[FAN_CURVE_DEV_CPU].enabled = false; ++ if (asus->gpu_fan_curve_available) ++ asus->custom_fan_curves[FAN_CURVE_DEV_GPU].enabled = false; + return 0; } -@@ -2711,6 +3163,13 @@ static struct attribute *platform_attributes[] = { - &dev_attr_als_enable.attr, - &dev_attr_fan_boost_mode.attr, - &dev_attr_throttle_thermal_policy.attr, -+ &sensor_dev_attr_cpu_fan_curve_balanced.dev_attr.attr, -+ &sensor_dev_attr_cpu_fan_curve_performance.dev_attr.attr, -+ &sensor_dev_attr_cpu_fan_curve_quiet.dev_attr.attr, -+ &sensor_dev_attr_gpu_fan_curve_balanced.dev_attr.attr, -+ &sensor_dev_attr_gpu_fan_curve_performance.dev_attr.attr, -+ &sensor_dev_attr_gpu_fan_curve_quiet.dev_attr.attr, -+ &dev_attr_enabled_fan_curve_profiles.attr, - &dev_attr_panel_od.attr, - NULL - }; -@@ -2741,6 +3200,20 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj, - ok = asus->fan_boost_mode_available; - else if (attr == &dev_attr_throttle_thermal_policy.attr) - ok = asus->throttle_thermal_policy_available; -+ else if (attr == &sensor_dev_attr_cpu_fan_curve_balanced.dev_attr.attr) -+ ok = asus->cpu_fan_curve_available; -+ else if (attr == &sensor_dev_attr_cpu_fan_curve_performance.dev_attr.attr) -+ ok = asus->cpu_fan_curve_available; -+ else if (attr == &sensor_dev_attr_cpu_fan_curve_quiet.dev_attr.attr) -+ ok = asus->cpu_fan_curve_available; -+ else if (attr == &sensor_dev_attr_gpu_fan_curve_balanced.dev_attr.attr) -+ ok = asus->gpu_fan_curve_available; -+ else if (attr == &sensor_dev_attr_gpu_fan_curve_performance.dev_attr.attr) -+ ok = asus->gpu_fan_curve_available; -+ else if (attr == &sensor_dev_attr_gpu_fan_curve_quiet.dev_attr.attr) -+ ok = asus->gpu_fan_curve_available; -+ else if (attr == &dev_attr_enabled_fan_curve_profiles.attr) -+ ok = asus->cpu_fan_curve_available || asus->gpu_fan_curve_available; - else if (attr == &dev_attr_panel_od.attr) - ok = asus->panel_overdrive_available; - -@@ -2904,7 +3377,7 @@ static int show_call(struct seq_file *m, void *data) +@@ -2901,7 +3490,7 @@ static int show_call(struct seq_file *m, void *data) if (ACPI_FAILURE(status)) return -EIO; @@ -578,24 +730,18 @@ index cc5811844012..dccdd41917ff 100644 if (obj && obj->type == ACPI_TYPE_INTEGER) seq_printf(m, "%#x(%#x, %#x) = %#x\n", asus->debug.method_id, asus->debug.dev_id, asus->debug.ctrl_param, -@@ -3016,6 +3489,16 @@ static int asus_wmi_add(struct platform_device *pdev) - else - throttle_thermal_policy_set_default(asus); +@@ -3035,6 +3624,10 @@ static int asus_wmi_add(struct platform_device *pdev) + if (err) + goto fail_hwmon; -+ err = custom_fan_check_present(asus, &asus->cpu_fan_curve_available, -+ ASUS_WMI_DEVID_CPU_FAN_CURVE); -+ if (err) -+ goto fail_custom_fan_curve; -+ -+ err = custom_fan_check_present(asus, &asus->gpu_fan_curve_available, -+ ASUS_WMI_DEVID_GPU_FAN_CURVE); ++ err = asus_wmi_custom_fan_curve_init(asus); + if (err) + goto fail_custom_fan_curve; + - err = platform_profile_setup(asus); + err = asus_wmi_led_init(asus); if (err) - goto fail_platform_profile_setup; -@@ -3109,6 +3592,7 @@ static int asus_wmi_add(struct platform_device *pdev) + goto fail_leds; +@@ -3106,6 +3699,7 @@ static int asus_wmi_add(struct platform_device *pdev) asus_wmi_sysfs_exit(asus->platform_device); fail_sysfs: fail_throttle_thermal_policy: @@ -603,6 +749,14 @@ index cc5811844012..dccdd41917ff 100644 fail_platform_profile_setup: if (asus->platform_profile_support) platform_profile_remove(); +@@ -3131,6 +3725,7 @@ static int asus_wmi_remove(struct platform_device *device) + asus_wmi_debugfs_exit(asus); + asus_wmi_sysfs_exit(asus->platform_device); + asus_fan_set_auto(asus); ++ throttle_thermal_policy_set_default(asus); + asus_wmi_battery_exit(asus); + + if (asus->platform_profile_support) diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h index 17dc5cb6f3f2..a571b47ff362 100644 --- a/include/linux/platform_data/x86/asus-wmi.h @@ -617,5 +771,5 @@ index 17dc5cb6f3f2..a571b47ff362 100644 /* Power */ #define ASUS_WMI_DEVID_PROCESSOR_STATE 0x00120012 -- -2.31.1 +2.32.0 |