[PATCH v3 3/3] hwmon: (amc6821) Add cooling device support

João Paulo Gonçalves posted 3 patches 4 months ago
There is a newer version of this series
[PATCH v3 3/3] hwmon: (amc6821) Add cooling device support
Posted by João Paulo Gonçalves 4 months ago
From: João Paulo Gonçalves <joao.goncalves@toradex.com>

Add support for using the AMC6821 as a cooling device. The AMC6821
registers with the thermal framework only if the `cooling-levels`
property is present in the fan device tree child node. If this property
is present, the driver assumes the fan will operate in open-loop, and
the kernel will control it directly. In this case, the driver will
change the AMC6821 mode to manual (software DCY) and set the initial PWM
duty cycle to the maximum fan cooling state level as defined in the DT.
It is worth mentioning that the cooling device is registered on the
child fan node, not on the fan controller node. Existing behavior is
unchanged, so the AMC6821 can still be used without the thermal
framework (hwmon only).

Signed-off-by: João Paulo Gonçalves <joao.goncalves@toradex.com>
---
v3:
- Fix using fan node after of_node_put()
- Add setting the pwm duty cycle to max fan cooling state level on
  initialization
v2: https://lore.kernel.org/lkml/20250603-b4-amc6821-cooling-device-support-v2-0-74943c889a2d@toradex.com/
v1: https://lore.kernel.org/lkml/20250530-b4-v1-amc6821-cooling-device-support-b4-v1-0-7bb98496c969@toradex.com/
---
 drivers/hwmon/amc6821.c | 114 +++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 109 insertions(+), 5 deletions(-)

diff --git a/drivers/hwmon/amc6821.c b/drivers/hwmon/amc6821.c
index 612895db7d8d4096372310c9fa71c103d642dd07..143a2d6a5593f462a456c5d9a9dad0e97fa73ab5 100644
--- a/drivers/hwmon/amc6821.c
+++ b/drivers/hwmon/amc6821.c
@@ -26,6 +26,7 @@
 #include <linux/pwm.h>
 #include <linux/regmap.h>
 #include <linux/slab.h>
+#include <linux/thermal.h>
 
 #include <dt-bindings/pwm/pwm.h>
 
@@ -126,6 +127,9 @@ module_param(init, int, 0444);
 struct amc6821_data {
 	struct regmap *regmap;
 	struct mutex update_lock;
+	unsigned long fan_state;
+	unsigned long fan_max_state;
+	unsigned int *fan_cooling_levels;
 	enum pwm_polarity pwm_polarity;
 };
 
@@ -805,6 +809,65 @@ static const struct hwmon_chip_info amc6821_chip_info = {
 	.info = amc6821_info,
 };
 
+static int amc6821_set_sw_dcy(struct amc6821_data *data, u8 duty_cycle)
+{
+	int ret;
+
+	ret = regmap_write(data->regmap, AMC6821_REG_DCY, duty_cycle);
+	if (ret)
+		return ret;
+
+	return regmap_update_bits(data->regmap, AMC6821_REG_CONF1,
+				  AMC6821_CONF1_FDRC0 | AMC6821_CONF1_FDRC1, 0);
+}
+
+static int amc6821_get_max_state(struct thermal_cooling_device *cdev, unsigned long *state)
+{
+	struct amc6821_data *data = cdev->devdata;
+
+	if (!data)
+		return -EINVAL;
+
+	*state = data->fan_max_state;
+
+	return 0;
+}
+
+static int amc6821_get_cur_state(struct thermal_cooling_device *cdev, unsigned long *state)
+{
+	struct amc6821_data *data = cdev->devdata;
+
+	if (!data)
+		return -EINVAL;
+
+	*state = data->fan_state;
+
+	return 0;
+}
+
+static int amc6821_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
+{
+	struct amc6821_data *data = cdev->devdata;
+	int ret;
+
+	if (!data || state > data->fan_max_state)
+		return -EINVAL;
+
+	ret = amc6821_set_sw_dcy(data, data->fan_cooling_levels[state]);
+	if (ret)
+		return ret;
+
+	data->fan_state = state;
+
+	return 0;
+}
+
+static const struct thermal_cooling_device_ops amc6821_cooling_ops = {
+	.get_max_state = amc6821_get_max_state,
+	.get_cur_state = amc6821_get_cur_state,
+	.set_cur_state = amc6821_set_cur_state,
+};
+
 /* Return 0 if detection is successful, -ENODEV otherwise */
 static int amc6821_detect(struct i2c_client *client, struct i2c_board_info *info)
 {
@@ -877,11 +940,29 @@ static enum pwm_polarity amc6821_pwm_polarity(struct i2c_client *client,
 	return polarity;
 }
 
-static void amc6821_of_fan_read_data(struct i2c_client *client,
-				     struct amc6821_data *data,
-				     struct device_node *fan_np)
+static int amc6821_of_fan_read_data(struct i2c_client *client,
+				    struct amc6821_data *data,
+				    struct device_node *fan_np)
 {
+	int num;
+
 	data->pwm_polarity = amc6821_pwm_polarity(client, fan_np);
+
+	num = of_property_count_u32_elems(fan_np, "cooling-levels");
+	if (num <= 0)
+		return 0;
+
+	data->fan_max_state = num - 1;
+
+	data->fan_cooling_levels = devm_kcalloc(&client->dev, num,
+						sizeof(u32),
+						GFP_KERNEL);
+
+	if (!data->fan_cooling_levels)
+		return -ENOMEM;
+
+	return of_property_read_u32_array(fan_np, "cooling-levels",
+					  data->fan_cooling_levels, num);
 }
 
 static int amc6821_init_client(struct i2c_client *client, struct amc6821_data *data)
@@ -914,6 +995,14 @@ static int amc6821_init_client(struct i2c_client *client, struct amc6821_data *d
 					 regval);
 		if (err)
 			return err;
+
+		/* Software DCY-control mode with fan enabled when cooling-levels present */
+		if (data->fan_cooling_levels) {
+			err = amc6821_set_sw_dcy(data,
+						 data->fan_cooling_levels[data->fan_max_state]);
+			if (err)
+				return err;
+		}
 	}
 	return 0;
 }
@@ -962,7 +1051,11 @@ static int amc6821_probe(struct i2c_client *client)
 
 	fan_np = of_get_child_by_name(dev->of_node, "fan");
 	if (fan_np)
-		amc6821_of_fan_read_data(client, data, fan_np);
+		err = amc6821_of_fan_read_data(client, data, fan_np);
+
+	if (err)
+		return dev_err_probe(dev, err,
+				     "Failed to read fan device tree data\n");
 
 	err = amc6821_init_client(client, data);
 	if (err)
@@ -978,7 +1071,18 @@ static int amc6821_probe(struct i2c_client *client)
 	hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
 							 data, &amc6821_chip_info,
 							 amc6821_groups);
-	return PTR_ERR_OR_ZERO(hwmon_dev);
+	if (IS_ERR(hwmon_dev))
+		return dev_err_probe(dev, PTR_ERR(hwmon_dev),
+				     "Failed to initialize hwmon\n");
+
+	if (IS_ENABLED(CONFIG_THERMAL) && fan_np && data->fan_cooling_levels)
+		return PTR_ERR_OR_ZERO(devm_thermal_of_cooling_device_register(dev,
+									       fan_np,
+									       client->name,
+									       data,
+									       &amc6821_cooling_ops));
+
+	return 0;
 }
 
 static const struct i2c_device_id amc6821_id[] = {

-- 
2.43.0

Re: [PATCH v3 3/3] hwmon: (amc6821) Add cooling device support
Posted by kernel test robot 3 months, 4 weeks ago
Hi João,

kernel test robot noticed the following build warnings:

[auto build test WARNING on b43674a549ed17319cb08ede9e0909ff6198ea70]

url:    https://github.com/intel-lab-lkp/linux/commits/Jo-o-Paulo-Gon-alves/dt-bindings-hwmon-amc6821-Add-cooling-levels/20250613-013059
base:   b43674a549ed17319cb08ede9e0909ff6198ea70
patch link:    https://lore.kernel.org/r/20250612-b4-amc6821-cooling-device-support-v3-3-360681a7652c%40toradex.com
patch subject: [PATCH v3 3/3] hwmon: (amc6821) Add cooling device support
config: hexagon-randconfig-002-20250613 (https://download.01.org/0day-ci/archive/20250613/202506131212.4UdngOz7-lkp@intel.com/config)
compiler: clang version 21.0.0git (https://github.com/llvm/llvm-project f819f46284f2a79790038e1f6649172789734ae8)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250613/202506131212.4UdngOz7-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202506131212.4UdngOz7-lkp@intel.com/

All warnings (new ones prefixed by >>):

>> drivers/hwmon/amc6821.c:1053:6: warning: variable 'err' is used uninitialized whenever 'if' condition is false [-Wsometimes-uninitialized]
    1053 |         if (fan_np)
         |             ^~~~~~
   drivers/hwmon/amc6821.c:1056:6: note: uninitialized use occurs here
    1056 |         if (err)
         |             ^~~
   drivers/hwmon/amc6821.c:1053:2: note: remove the 'if' if its condition is always true
    1053 |         if (fan_np)
         |         ^~~~~~~~~~~
    1054 |                 err = amc6821_of_fan_read_data(client, data, fan_np);
   drivers/hwmon/amc6821.c:1040:9: note: initialize the variable 'err' to silence this warning
    1040 |         int err;
         |                ^
         |                 = 0
   1 warning generated.


vim +1053 drivers/hwmon/amc6821.c

b5430a04e99508 Tomaz Mertelj        2010-01-08  1032  
6748703856d461 Stephen Kitt         2020-08-13  1033  static int amc6821_probe(struct i2c_client *client)
b5430a04e99508 Tomaz Mertelj        2010-01-08  1034  {
1276fae2a93142 Axel Lin             2014-06-29  1035  	struct device *dev = &client->dev;
28e6274d8fa67e Axel Lin             2014-06-29  1036  	struct amc6821_data *data;
1276fae2a93142 Axel Lin             2014-06-29  1037  	struct device *hwmon_dev;
a051d507ba1718 Guenter Roeck        2024-06-27  1038  	struct regmap *regmap;
194be60020ab02 João Paulo Gonçalves 2025-06-12  1039  	struct device_node *fan_np __free(device_node) = NULL;
28e6274d8fa67e Axel Lin             2014-06-29  1040  	int err;
b5430a04e99508 Tomaz Mertelj        2010-01-08  1041  
1276fae2a93142 Axel Lin             2014-06-29  1042  	data = devm_kzalloc(dev, sizeof(struct amc6821_data), GFP_KERNEL);
28e6274d8fa67e Axel Lin             2014-06-29  1043  	if (!data)
28e6274d8fa67e Axel Lin             2014-06-29  1044  		return -ENOMEM;
b5430a04e99508 Tomaz Mertelj        2010-01-08  1045  
a051d507ba1718 Guenter Roeck        2024-06-27  1046  	regmap = devm_regmap_init_i2c(client, &amc6821_regmap_config);
a051d507ba1718 Guenter Roeck        2024-06-27  1047  	if (IS_ERR(regmap))
a051d507ba1718 Guenter Roeck        2024-06-27  1048  		return dev_err_probe(dev, PTR_ERR(regmap),
a051d507ba1718 Guenter Roeck        2024-06-27  1049  				     "Failed to initialize regmap\n");
a051d507ba1718 Guenter Roeck        2024-06-27  1050  	data->regmap = regmap;
b5430a04e99508 Tomaz Mertelj        2010-01-08  1051  
194be60020ab02 João Paulo Gonçalves 2025-06-12  1052  	fan_np = of_get_child_by_name(dev->of_node, "fan");
194be60020ab02 João Paulo Gonçalves 2025-06-12 @1053  	if (fan_np)
6364a7ce37ccd1 João Paulo Gonçalves 2025-06-12  1054  		err = amc6821_of_fan_read_data(client, data, fan_np);
6364a7ce37ccd1 João Paulo Gonçalves 2025-06-12  1055  
6364a7ce37ccd1 João Paulo Gonçalves 2025-06-12  1056  	if (err)
6364a7ce37ccd1 João Paulo Gonçalves 2025-06-12  1057  		return dev_err_probe(dev, err,
6364a7ce37ccd1 João Paulo Gonçalves 2025-06-12  1058  				     "Failed to read fan device tree data\n");
194be60020ab02 João Paulo Gonçalves 2025-06-12  1059  
cd17587272e284 Francesco Dolcini    2025-04-02  1060  	err = amc6821_init_client(client, data);
28e6274d8fa67e Axel Lin             2014-06-29  1061  	if (err)
28e6274d8fa67e Axel Lin             2014-06-29  1062  		return err;
b5430a04e99508 Tomaz Mertelj        2010-01-08  1063  
8f38236de689af Farouk Bouabid       2024-09-06  1064  	if (of_device_is_compatible(dev->of_node, "tsd,mule")) {
8f38236de689af Farouk Bouabid       2024-09-06  1065  		err = devm_of_platform_populate(dev);
8f38236de689af Farouk Bouabid       2024-09-06  1066  		if (err)
8f38236de689af Farouk Bouabid       2024-09-06  1067  			return dev_err_probe(dev, err,
8f38236de689af Farouk Bouabid       2024-09-06  1068  				     "Failed to create sub-devices\n");
8f38236de689af Farouk Bouabid       2024-09-06  1069  	}
8f38236de689af Farouk Bouabid       2024-09-06  1070  
e98ab50e1f2dd6 Guenter Roeck        2024-06-26  1071  	hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
e98ab50e1f2dd6 Guenter Roeck        2024-06-26  1072  							 data, &amc6821_chip_info,
1276fae2a93142 Axel Lin             2014-06-29  1073  							 amc6821_groups);
6364a7ce37ccd1 João Paulo Gonçalves 2025-06-12  1074  	if (IS_ERR(hwmon_dev))
6364a7ce37ccd1 João Paulo Gonçalves 2025-06-12  1075  		return dev_err_probe(dev, PTR_ERR(hwmon_dev),
6364a7ce37ccd1 João Paulo Gonçalves 2025-06-12  1076  				     "Failed to initialize hwmon\n");
6364a7ce37ccd1 João Paulo Gonçalves 2025-06-12  1077  
6364a7ce37ccd1 João Paulo Gonçalves 2025-06-12  1078  	if (IS_ENABLED(CONFIG_THERMAL) && fan_np && data->fan_cooling_levels)
6364a7ce37ccd1 João Paulo Gonçalves 2025-06-12  1079  		return PTR_ERR_OR_ZERO(devm_thermal_of_cooling_device_register(dev,
6364a7ce37ccd1 João Paulo Gonçalves 2025-06-12  1080  									       fan_np,
6364a7ce37ccd1 João Paulo Gonçalves 2025-06-12  1081  									       client->name,
6364a7ce37ccd1 João Paulo Gonçalves 2025-06-12  1082  									       data,
6364a7ce37ccd1 João Paulo Gonçalves 2025-06-12  1083  									       &amc6821_cooling_ops));
6364a7ce37ccd1 João Paulo Gonçalves 2025-06-12  1084  
6364a7ce37ccd1 João Paulo Gonçalves 2025-06-12  1085  	return 0;
b5430a04e99508 Tomaz Mertelj        2010-01-08  1086  }
b5430a04e99508 Tomaz Mertelj        2010-01-08  1087  

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki