mvebu: puzzle: fix fan thermal cooling driver
authorDaniel Golle <daniel@makrotopia.org>
Sat, 25 Feb 2023 02:35:16 +0000 (02:35 +0000)
committerDaniel Golle <daniel@makrotopia.org>
Sat, 25 Feb 2023 19:01:40 +0000 (19:01 +0000)
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>
target/linux/mvebu/patches-5.10/903-drivers-hwmon-Add-the-IEI-WT61P803-PUZZLE-HWMON-driv.patch
target/linux/mvebu/patches-5.15/903-drivers-hwmon-Add-the-IEI-WT61P803-PUZZLE-HWMON-driv.patch

index 684f09c800debe0378ec33ef3a677ef73374e5af..d31709fd853cdef20bc60f0efbba68c7ea22b3fc 100644 (file)
@@ -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;
++
++      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 <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;
 +}
 +
index c22314e41c5be6bec85bb7e834688a0bd1be8cd1..023495373b86ed64e5fdf089237ca92b72337629 100644 (file)
@@ -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;
++
++      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 <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;
 +}
 +