From ea33a5def506b0ba647f779e60e6ccca03c29a17 Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Sat, 25 Feb 2023 02:35:16 +0000 Subject: [PATCH] mvebu: puzzle: fix fan thermal cooling driver Several fixes for the Puzzle WT61P803 hwmon driver were needed to make it behave well as thermal cooling device: - wire-up cooling device with OF node in device tree - properly parse cooling-levels (u32 with range check vs. u8) - actually use cooling-levels - keep current state and only write to uC if state has changed (avoids flooding the uC with commands which will result in uC crashing) Signed-off-by: Daniel Golle --- ...d-the-IEI-WT61P803-PUZZLE-HWMON-driv.patch | 78 +++++++++++++------ ...d-the-IEI-WT61P803-PUZZLE-HWMON-driv.patch | 78 +++++++++++++------ 2 files changed, 110 insertions(+), 46 deletions(-) diff --git a/target/linux/mvebu/patches-5.10/903-drivers-hwmon-Add-the-IEI-WT61P803-PUZZLE-HWMON-driv.patch b/target/linux/mvebu/patches-5.10/903-drivers-hwmon-Add-the-IEI-WT61P803-PUZZLE-HWMON-driv.patch index 684f09c800..d31709fd85 100644 --- a/target/linux/mvebu/patches-5.10/903-drivers-hwmon-Add-the-IEI-WT61P803-PUZZLE-HWMON-driv.patch +++ b/target/linux/mvebu/patches-5.10/903-drivers-hwmon-Add-the-IEI-WT61P803-PUZZLE-HWMON-driv.patch @@ -53,7 +53,7 @@ Cc: Robert Marko obj-$(CONFIG_SENSORS_IBMPOWERNV)+= ibmpowernv.o --- /dev/null +++ b/drivers/hwmon/iei-wt61p803-puzzle-hwmon.c -@@ -0,0 +1,413 @@ +@@ -0,0 +1,445 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* IEI WT61P803 PUZZLE MCU HWMON Driver + * @@ -84,13 +84,17 @@ Cc: Robert Marko + * @name: Thermal cooling device name + * @pwm_channel: Controlled PWM channel (0 or 1) + * @cooling_levels: Thermal cooling device cooling levels (DT) ++ * @cur_level: Current cooling level ++ * @num_levels: Number of cooling levels + */ +struct iei_wt61p803_puzzle_thermal_cooling_device { + struct iei_wt61p803_puzzle_hwmon *mcu_hwmon; + struct thermal_cooling_device *tcdev; + char name[THERMAL_NAME_LENGTH]; + int pwm_channel; -+ u8 *cooling_levels; ++ u32 *cooling_levels; ++ int cur_level; ++ u8 num_levels; +}; + +/** @@ -326,8 +330,12 @@ Cc: Robert Marko +static int iei_wt61p803_puzzle_get_max_state(struct thermal_cooling_device *tcdev, + unsigned long *state) +{ -+ *state = IEI_WT61P803_PUZZLE_HWMON_MAX_PWM_VAL; ++ struct iei_wt61p803_puzzle_thermal_cooling_device *cdev = tcdev->devdata; ++ ++ if (!cdev) ++ return -EINVAL; + ++ *state = cdev->num_levels - 1; + return 0; +} + @@ -335,14 +343,14 @@ Cc: Robert Marko + unsigned long *state) +{ + struct iei_wt61p803_puzzle_thermal_cooling_device *cdev = tcdev->devdata; -+ struct iei_wt61p803_puzzle_hwmon *mcu_hwmon = cdev->mcu_hwmon; -+ long value; -+ int ret; + -+ ret = iei_wt61p803_puzzle_read_pwm_channel(mcu_hwmon, cdev->pwm_channel, &value); -+ if (ret) -+ return ret; -+ *state = value; ++ if (!cdev) ++ return -EINVAL; ++ ++ if (cdev->cur_level < 0) ++ return -EAGAIN; ++ ++ *state = cdev->cur_level; + return 0; +} + @@ -350,9 +358,21 @@ Cc: Robert Marko + unsigned long state) +{ + struct iei_wt61p803_puzzle_thermal_cooling_device *cdev = tcdev->devdata; -+ struct iei_wt61p803_puzzle_hwmon *mcu_hwmon = cdev->mcu_hwmon; ++ u8 pwm_level; ++ ++ if (!cdev) ++ return -EINVAL; + -+ return iei_wt61p803_puzzle_write_pwm_channel(mcu_hwmon, cdev->pwm_channel, state); ++ if (state >= cdev->num_levels) ++ return -EINVAL; ++ ++ if (state == cdev->cur_level) ++ return 0; ++ ++ cdev->cur_level = state; ++ pwm_level = cdev->cooling_levels[state]; ++ ++ return iei_wt61p803_puzzle_write_pwm_channel(cdev->mcu_hwmon, cdev->pwm_channel, pwm_level); +} + +static const struct thermal_cooling_device_ops iei_wt61p803_puzzle_cooling_ops = { @@ -369,7 +389,7 @@ Cc: Robert Marko + struct iei_wt61p803_puzzle_thermal_cooling_device *cdev; + u32 pwm_channel; + u8 num_levels; -+ int ret; ++ int i, ret; + + ret = fwnode_property_read_u32(child, "reg", &pwm_channel); + if (ret) @@ -377,7 +397,7 @@ Cc: Robert Marko + + mcu_hwmon->thermal_cooling_dev_present[pwm_channel] = true; + -+ num_levels = fwnode_property_count_u8(child, "cooling-levels"); ++ num_levels = fwnode_property_count_u32(child, "cooling-levels"); + if (!num_levels) + return -EINVAL; + @@ -385,28 +405,40 @@ Cc: Robert Marko + if (!cdev) + return -ENOMEM; + -+ cdev->cooling_levels = devm_kmalloc_array(dev, num_levels, sizeof(u8), GFP_KERNEL); ++ cdev->cooling_levels = devm_kmalloc_array(dev, num_levels, sizeof(u32), GFP_KERNEL); + if (!cdev->cooling_levels) + return -ENOMEM; + -+ ret = fwnode_property_read_u8_array(child, "cooling-levels", -+ cdev->cooling_levels, -+ num_levels); ++ ret = fwnode_property_read_u32_array(child, "cooling-levels", ++ cdev->cooling_levels, ++ num_levels); + if (ret) { + dev_err(dev, "Couldn't read property 'cooling-levels'\n"); + return ret; + } + -+ snprintf(cdev->name, THERMAL_NAME_LENGTH, "wt61p803_puzzle_%d", pwm_channel); -+ cdev->tcdev = devm_thermal_of_cooling_device_register(dev, NULL, cdev->name, cdev, -+ &iei_wt61p803_puzzle_cooling_ops); -+ if (IS_ERR(cdev->tcdev)) -+ return PTR_ERR(cdev->tcdev); ++ for (i = 0; i < num_levels; i++) { ++ if (cdev->cooling_levels[i] > ++ IEI_WT61P803_PUZZLE_HWMON_MAX_PWM_VAL) { ++ dev_err(dev, "iei_wt61p803_fan state[%d]:%d > %d\n", i, ++ cdev->cooling_levels[i], ++ IEI_WT61P803_PUZZLE_HWMON_MAX_PWM_VAL); ++ return -EINVAL; ++ } ++ } + + cdev->mcu_hwmon = mcu_hwmon; + cdev->pwm_channel = pwm_channel; ++ cdev->num_levels = num_levels; ++ cdev->cur_level = -1; + mcu_hwmon->cdev[pwm_channel] = cdev; + ++ snprintf(cdev->name, THERMAL_NAME_LENGTH, "wt61p803_puzzle_%d", pwm_channel); ++ cdev->tcdev = devm_thermal_of_cooling_device_register(dev, to_of_node(child), cdev->name, ++ cdev, &iei_wt61p803_puzzle_cooling_ops); ++ if (IS_ERR(cdev->tcdev)) ++ return PTR_ERR(cdev->tcdev); ++ + return 0; +} + diff --git a/target/linux/mvebu/patches-5.15/903-drivers-hwmon-Add-the-IEI-WT61P803-PUZZLE-HWMON-driv.patch b/target/linux/mvebu/patches-5.15/903-drivers-hwmon-Add-the-IEI-WT61P803-PUZZLE-HWMON-driv.patch index c22314e41c..023495373b 100644 --- a/target/linux/mvebu/patches-5.15/903-drivers-hwmon-Add-the-IEI-WT61P803-PUZZLE-HWMON-driv.patch +++ b/target/linux/mvebu/patches-5.15/903-drivers-hwmon-Add-the-IEI-WT61P803-PUZZLE-HWMON-driv.patch @@ -53,7 +53,7 @@ Cc: Robert Marko obj-$(CONFIG_SENSORS_IBMPOWERNV)+= ibmpowernv.o --- /dev/null +++ b/drivers/hwmon/iei-wt61p803-puzzle-hwmon.c -@@ -0,0 +1,413 @@ +@@ -0,0 +1,445 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* IEI WT61P803 PUZZLE MCU HWMON Driver + * @@ -84,13 +84,17 @@ Cc: Robert Marko + * @name: Thermal cooling device name + * @pwm_channel: Controlled PWM channel (0 or 1) + * @cooling_levels: Thermal cooling device cooling levels (DT) ++ * @cur_level: Current cooling level ++ * @num_levels: Number of cooling levels + */ +struct iei_wt61p803_puzzle_thermal_cooling_device { + struct iei_wt61p803_puzzle_hwmon *mcu_hwmon; + struct thermal_cooling_device *tcdev; + char name[THERMAL_NAME_LENGTH]; + int pwm_channel; -+ u8 *cooling_levels; ++ u32 *cooling_levels; ++ int cur_level; ++ u8 num_levels; +}; + +/** @@ -326,8 +330,12 @@ Cc: Robert Marko +static int iei_wt61p803_puzzle_get_max_state(struct thermal_cooling_device *tcdev, + unsigned long *state) +{ -+ *state = IEI_WT61P803_PUZZLE_HWMON_MAX_PWM_VAL; ++ struct iei_wt61p803_puzzle_thermal_cooling_device *cdev = tcdev->devdata; ++ ++ if (!cdev) ++ return -EINVAL; + ++ *state = cdev->num_levels - 1; + return 0; +} + @@ -335,14 +343,14 @@ Cc: Robert Marko + unsigned long *state) +{ + struct iei_wt61p803_puzzle_thermal_cooling_device *cdev = tcdev->devdata; -+ struct iei_wt61p803_puzzle_hwmon *mcu_hwmon = cdev->mcu_hwmon; -+ long value; -+ int ret; + -+ ret = iei_wt61p803_puzzle_read_pwm_channel(mcu_hwmon, cdev->pwm_channel, &value); -+ if (ret) -+ return ret; -+ *state = value; ++ if (!cdev) ++ return -EINVAL; ++ ++ if (cdev->cur_level < 0) ++ return -EAGAIN; ++ ++ *state = cdev->cur_level; + return 0; +} + @@ -350,9 +358,21 @@ Cc: Robert Marko + unsigned long state) +{ + struct iei_wt61p803_puzzle_thermal_cooling_device *cdev = tcdev->devdata; -+ struct iei_wt61p803_puzzle_hwmon *mcu_hwmon = cdev->mcu_hwmon; ++ u8 pwm_level; ++ ++ if (!cdev) ++ return -EINVAL; + -+ return iei_wt61p803_puzzle_write_pwm_channel(mcu_hwmon, cdev->pwm_channel, state); ++ if (state >= cdev->num_levels) ++ return -EINVAL; ++ ++ if (state == cdev->cur_level) ++ return 0; ++ ++ cdev->cur_level = state; ++ pwm_level = cdev->cooling_levels[state]; ++ ++ return iei_wt61p803_puzzle_write_pwm_channel(cdev->mcu_hwmon, cdev->pwm_channel, pwm_level); +} + +static const struct thermal_cooling_device_ops iei_wt61p803_puzzle_cooling_ops = { @@ -369,7 +389,7 @@ Cc: Robert Marko + struct iei_wt61p803_puzzle_thermal_cooling_device *cdev; + u32 pwm_channel; + u8 num_levels; -+ int ret; ++ int i, ret; + + ret = fwnode_property_read_u32(child, "reg", &pwm_channel); + if (ret) @@ -377,7 +397,7 @@ Cc: Robert Marko + + mcu_hwmon->thermal_cooling_dev_present[pwm_channel] = true; + -+ num_levels = fwnode_property_count_u8(child, "cooling-levels"); ++ num_levels = fwnode_property_count_u32(child, "cooling-levels"); + if (!num_levels) + return -EINVAL; + @@ -385,28 +405,40 @@ Cc: Robert Marko + if (!cdev) + return -ENOMEM; + -+ cdev->cooling_levels = devm_kmalloc_array(dev, num_levels, sizeof(u8), GFP_KERNEL); ++ cdev->cooling_levels = devm_kmalloc_array(dev, num_levels, sizeof(u32), GFP_KERNEL); + if (!cdev->cooling_levels) + return -ENOMEM; + -+ ret = fwnode_property_read_u8_array(child, "cooling-levels", -+ cdev->cooling_levels, -+ num_levels); ++ ret = fwnode_property_read_u32_array(child, "cooling-levels", ++ cdev->cooling_levels, ++ num_levels); + if (ret) { + dev_err(dev, "Couldn't read property 'cooling-levels'\n"); + return ret; + } + -+ snprintf(cdev->name, THERMAL_NAME_LENGTH, "wt61p803_puzzle_%d", pwm_channel); -+ cdev->tcdev = devm_thermal_of_cooling_device_register(dev, NULL, cdev->name, cdev, -+ &iei_wt61p803_puzzle_cooling_ops); -+ if (IS_ERR(cdev->tcdev)) -+ return PTR_ERR(cdev->tcdev); ++ for (i = 0; i < num_levels; i++) { ++ if (cdev->cooling_levels[i] > ++ IEI_WT61P803_PUZZLE_HWMON_MAX_PWM_VAL) { ++ dev_err(dev, "iei_wt61p803_fan state[%d]:%d > %d\n", i, ++ cdev->cooling_levels[i], ++ IEI_WT61P803_PUZZLE_HWMON_MAX_PWM_VAL); ++ return -EINVAL; ++ } ++ } + + cdev->mcu_hwmon = mcu_hwmon; + cdev->pwm_channel = pwm_channel; ++ cdev->num_levels = num_levels; ++ cdev->cur_level = -1; + mcu_hwmon->cdev[pwm_channel] = cdev; + ++ snprintf(cdev->name, THERMAL_NAME_LENGTH, "wt61p803_puzzle_%d", pwm_channel); ++ cdev->tcdev = devm_thermal_of_cooling_device_register(dev, to_of_node(child), cdev->name, ++ cdev, &iei_wt61p803_puzzle_cooling_ops); ++ if (IS_ERR(cdev->tcdev)) ++ return PTR_ERR(cdev->tcdev); ++ + return 0; +} + -- 2.30.2