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
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
© 2016 - 2025 Red Hat, Inc.