mirror of
https://github.com/openwrt/openwrt.git
synced 2025-01-18 18:56:37 +00:00
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 <daniel@makrotopia.org>
This commit is contained in:
parent
2a104365dc
commit
ea33a5def5
@ -53,7 +53,7 @@ Cc: Robert Marko <robert.marko@sartura.hr>
|
||||
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 <robert.marko@sartura.hr>
|
||||
+ * @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 <robert.marko@sartura.hr>
|
||||
+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 <robert.marko@sartura.hr>
|
||||
+ 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 <robert.marko@sartura.hr>
|
||||
+ 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;
|
||||
+
|
||||
+ return iei_wt61p803_puzzle_write_pwm_channel(mcu_hwmon, cdev->pwm_channel, state);
|
||||
+ if (!cdev)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ 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 <robert.marko@sartura.hr>
|
||||
+ 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 <robert.marko@sartura.hr>
|
||||
+
|
||||
+ 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 <robert.marko@sartura.hr>
|
||||
+ 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;
|
||||
+}
|
||||
+
|
||||
|
@ -53,7 +53,7 @@ Cc: Robert Marko <robert.marko@sartura.hr>
|
||||
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 <robert.marko@sartura.hr>
|
||||
+ * @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 <robert.marko@sartura.hr>
|
||||
+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 <robert.marko@sartura.hr>
|
||||
+ 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 <robert.marko@sartura.hr>
|
||||
+ 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;
|
||||
+
|
||||
+ return iei_wt61p803_puzzle_write_pwm_channel(mcu_hwmon, cdev->pwm_channel, state);
|
||||
+ if (!cdev)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ 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 <robert.marko@sartura.hr>
|
||||
+ 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 <robert.marko@sartura.hr>
|
||||
+
|
||||
+ 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 <robert.marko@sartura.hr>
|
||||
+ 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;
|
||||
+}
|
||||
+
|
||||
|
Loading…
Reference in New Issue
Block a user