Skip to content

Commit 90cf254

Browse files
JPSGoncalvesgroeck
authored andcommitted
hwmon: (amc6821) Add cooling device support
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 <[email protected]> Link: https://lore.kernel.org/r/20250613-b4-amc6821-cooling-device-support-v4-3-a8fc063c55de@toradex.com [groeck: Reduced line length when registering thermal device] Signed-off-by: Guenter Roeck <[email protected]>
1 parent b0078b2 commit 90cf254

File tree

1 file changed

+107
-6
lines changed

1 file changed

+107
-6
lines changed

drivers/hwmon/amc6821.c

Lines changed: 107 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include <linux/pwm.h>
2727
#include <linux/regmap.h>
2828
#include <linux/slab.h>
29+
#include <linux/thermal.h>
2930

3031
#include <dt-bindings/pwm/pwm.h>
3132

@@ -126,6 +127,9 @@ module_param(init, int, 0444);
126127
struct amc6821_data {
127128
struct regmap *regmap;
128129
struct mutex update_lock;
130+
unsigned long fan_state;
131+
unsigned long fan_max_state;
132+
unsigned int *fan_cooling_levels;
129133
enum pwm_polarity pwm_polarity;
130134
};
131135

@@ -805,6 +809,65 @@ static const struct hwmon_chip_info amc6821_chip_info = {
805809
.info = amc6821_info,
806810
};
807811

812+
static int amc6821_set_sw_dcy(struct amc6821_data *data, u8 duty_cycle)
813+
{
814+
int ret;
815+
816+
ret = regmap_write(data->regmap, AMC6821_REG_DCY, duty_cycle);
817+
if (ret)
818+
return ret;
819+
820+
return regmap_update_bits(data->regmap, AMC6821_REG_CONF1,
821+
AMC6821_CONF1_FDRC0 | AMC6821_CONF1_FDRC1, 0);
822+
}
823+
824+
static int amc6821_get_max_state(struct thermal_cooling_device *cdev, unsigned long *state)
825+
{
826+
struct amc6821_data *data = cdev->devdata;
827+
828+
if (!data)
829+
return -EINVAL;
830+
831+
*state = data->fan_max_state;
832+
833+
return 0;
834+
}
835+
836+
static int amc6821_get_cur_state(struct thermal_cooling_device *cdev, unsigned long *state)
837+
{
838+
struct amc6821_data *data = cdev->devdata;
839+
840+
if (!data)
841+
return -EINVAL;
842+
843+
*state = data->fan_state;
844+
845+
return 0;
846+
}
847+
848+
static int amc6821_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
849+
{
850+
struct amc6821_data *data = cdev->devdata;
851+
int ret;
852+
853+
if (!data || state > data->fan_max_state)
854+
return -EINVAL;
855+
856+
ret = amc6821_set_sw_dcy(data, data->fan_cooling_levels[state]);
857+
if (ret)
858+
return ret;
859+
860+
data->fan_state = state;
861+
862+
return 0;
863+
}
864+
865+
static const struct thermal_cooling_device_ops amc6821_cooling_ops = {
866+
.get_max_state = amc6821_get_max_state,
867+
.get_cur_state = amc6821_get_cur_state,
868+
.set_cur_state = amc6821_set_cur_state,
869+
};
870+
808871
/* Return 0 if detection is successful, -ENODEV otherwise */
809872
static int amc6821_detect(struct i2c_client *client, struct i2c_board_info *info)
810873
{
@@ -877,11 +940,29 @@ static enum pwm_polarity amc6821_pwm_polarity(struct i2c_client *client,
877940
return polarity;
878941
}
879942

880-
static void amc6821_of_fan_read_data(struct i2c_client *client,
881-
struct amc6821_data *data,
882-
struct device_node *fan_np)
943+
static int amc6821_of_fan_read_data(struct i2c_client *client,
944+
struct amc6821_data *data,
945+
struct device_node *fan_np)
883946
{
947+
int num;
948+
884949
data->pwm_polarity = amc6821_pwm_polarity(client, fan_np);
950+
951+
num = of_property_count_u32_elems(fan_np, "cooling-levels");
952+
if (num <= 0)
953+
return 0;
954+
955+
data->fan_max_state = num - 1;
956+
957+
data->fan_cooling_levels = devm_kcalloc(&client->dev, num,
958+
sizeof(u32),
959+
GFP_KERNEL);
960+
961+
if (!data->fan_cooling_levels)
962+
return -ENOMEM;
963+
964+
return of_property_read_u32_array(fan_np, "cooling-levels",
965+
data->fan_cooling_levels, num);
885966
}
886967

887968
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
914995
regval);
915996
if (err)
916997
return err;
998+
999+
/* Software DCY-control mode with fan enabled when cooling-levels present */
1000+
if (data->fan_cooling_levels) {
1001+
err = amc6821_set_sw_dcy(data,
1002+
data->fan_cooling_levels[data->fan_max_state]);
1003+
if (err)
1004+
return err;
1005+
}
9171006
}
9181007
return 0;
9191008
}
@@ -961,8 +1050,12 @@ static int amc6821_probe(struct i2c_client *client)
9611050
data->regmap = regmap;
9621051

9631052
fan_np = of_get_child_by_name(dev->of_node, "fan");
964-
if (fan_np)
965-
amc6821_of_fan_read_data(client, data, fan_np);
1053+
if (fan_np) {
1054+
err = amc6821_of_fan_read_data(client, data, fan_np);
1055+
if (err)
1056+
return dev_err_probe(dev, err,
1057+
"Failed to read fan device tree data\n");
1058+
}
9661059

9671060
err = amc6821_init_client(client, data);
9681061
if (err)
@@ -978,7 +1071,15 @@ static int amc6821_probe(struct i2c_client *client)
9781071
hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
9791072
data, &amc6821_chip_info,
9801073
amc6821_groups);
981-
return PTR_ERR_OR_ZERO(hwmon_dev);
1074+
if (IS_ERR(hwmon_dev))
1075+
return dev_err_probe(dev, PTR_ERR(hwmon_dev),
1076+
"Failed to initialize hwmon\n");
1077+
1078+
if (IS_ENABLED(CONFIG_THERMAL) && fan_np && data->fan_cooling_levels)
1079+
return PTR_ERR_OR_ZERO(devm_thermal_of_cooling_device_register(dev,
1080+
fan_np, client->name, data, &amc6821_cooling_ops));
1081+
1082+
return 0;
9821083
}
9831084

9841085
static const struct i2c_device_id amc6821_id[] = {

0 commit comments

Comments
 (0)