Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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 <joao.goncalves@toradex.com>
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 <linux@roeck-us.net>

authored by

João Paulo Gonçalves and committed by
Guenter Roeck
90cf254f b0078b2c

+107 -6
+107 -6
drivers/hwmon/amc6821.c
··· 26 26 #include <linux/pwm.h> 27 27 #include <linux/regmap.h> 28 28 #include <linux/slab.h> 29 + #include <linux/thermal.h> 29 30 30 31 #include <dt-bindings/pwm/pwm.h> 31 32 ··· 127 126 struct amc6821_data { 128 127 struct regmap *regmap; 129 128 struct mutex update_lock; 129 + unsigned long fan_state; 130 + unsigned long fan_max_state; 131 + unsigned int *fan_cooling_levels; 130 132 enum pwm_polarity pwm_polarity; 131 133 }; 132 134 ··· 809 805 .info = amc6821_info, 810 806 }; 811 807 808 + static int amc6821_set_sw_dcy(struct amc6821_data *data, u8 duty_cycle) 809 + { 810 + int ret; 811 + 812 + ret = regmap_write(data->regmap, AMC6821_REG_DCY, duty_cycle); 813 + if (ret) 814 + return ret; 815 + 816 + return regmap_update_bits(data->regmap, AMC6821_REG_CONF1, 817 + AMC6821_CONF1_FDRC0 | AMC6821_CONF1_FDRC1, 0); 818 + } 819 + 820 + static int amc6821_get_max_state(struct thermal_cooling_device *cdev, unsigned long *state) 821 + { 822 + struct amc6821_data *data = cdev->devdata; 823 + 824 + if (!data) 825 + return -EINVAL; 826 + 827 + *state = data->fan_max_state; 828 + 829 + return 0; 830 + } 831 + 832 + static int amc6821_get_cur_state(struct thermal_cooling_device *cdev, unsigned long *state) 833 + { 834 + struct amc6821_data *data = cdev->devdata; 835 + 836 + if (!data) 837 + return -EINVAL; 838 + 839 + *state = data->fan_state; 840 + 841 + return 0; 842 + } 843 + 844 + static int amc6821_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state) 845 + { 846 + struct amc6821_data *data = cdev->devdata; 847 + int ret; 848 + 849 + if (!data || state > data->fan_max_state) 850 + return -EINVAL; 851 + 852 + ret = amc6821_set_sw_dcy(data, data->fan_cooling_levels[state]); 853 + if (ret) 854 + return ret; 855 + 856 + data->fan_state = state; 857 + 858 + return 0; 859 + } 860 + 861 + static const struct thermal_cooling_device_ops amc6821_cooling_ops = { 862 + .get_max_state = amc6821_get_max_state, 863 + .get_cur_state = amc6821_get_cur_state, 864 + .set_cur_state = amc6821_set_cur_state, 865 + }; 866 + 812 867 /* Return 0 if detection is successful, -ENODEV otherwise */ 813 868 static int amc6821_detect(struct i2c_client *client, struct i2c_board_info *info) 814 869 { ··· 940 877 return polarity; 941 878 } 942 879 943 - static void amc6821_of_fan_read_data(struct i2c_client *client, 944 - struct amc6821_data *data, 945 - struct device_node *fan_np) 880 + static int amc6821_of_fan_read_data(struct i2c_client *client, 881 + struct amc6821_data *data, 882 + struct device_node *fan_np) 946 883 { 884 + int num; 885 + 947 886 data->pwm_polarity = amc6821_pwm_polarity(client, fan_np); 887 + 888 + num = of_property_count_u32_elems(fan_np, "cooling-levels"); 889 + if (num <= 0) 890 + return 0; 891 + 892 + data->fan_max_state = num - 1; 893 + 894 + data->fan_cooling_levels = devm_kcalloc(&client->dev, num, 895 + sizeof(u32), 896 + GFP_KERNEL); 897 + 898 + if (!data->fan_cooling_levels) 899 + return -ENOMEM; 900 + 901 + return of_property_read_u32_array(fan_np, "cooling-levels", 902 + data->fan_cooling_levels, num); 948 903 } 949 904 950 905 static int amc6821_init_client(struct i2c_client *client, struct amc6821_data *data) ··· 995 914 regval); 996 915 if (err) 997 916 return err; 917 + 918 + /* Software DCY-control mode with fan enabled when cooling-levels present */ 919 + if (data->fan_cooling_levels) { 920 + err = amc6821_set_sw_dcy(data, 921 + data->fan_cooling_levels[data->fan_max_state]); 922 + if (err) 923 + return err; 924 + } 998 925 } 999 926 return 0; 1000 927 } ··· 1050 961 data->regmap = regmap; 1051 962 1052 963 fan_np = of_get_child_by_name(dev->of_node, "fan"); 1053 - if (fan_np) 1054 - amc6821_of_fan_read_data(client, data, fan_np); 964 + if (fan_np) { 965 + err = amc6821_of_fan_read_data(client, data, fan_np); 966 + if (err) 967 + return dev_err_probe(dev, err, 968 + "Failed to read fan device tree data\n"); 969 + } 1055 970 1056 971 err = amc6821_init_client(client, data); 1057 972 if (err) ··· 1071 978 hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, 1072 979 data, &amc6821_chip_info, 1073 980 amc6821_groups); 1074 - return PTR_ERR_OR_ZERO(hwmon_dev); 981 + if (IS_ERR(hwmon_dev)) 982 + return dev_err_probe(dev, PTR_ERR(hwmon_dev), 983 + "Failed to initialize hwmon\n"); 984 + 985 + if (IS_ENABLED(CONFIG_THERMAL) && fan_np && data->fan_cooling_levels) 986 + return PTR_ERR_OR_ZERO(devm_thermal_of_cooling_device_register(dev, 987 + fan_np, client->name, data, &amc6821_cooling_ops)); 988 + 989 + return 0; 1075 990 } 1076 991 1077 992 static const struct i2c_device_id amc6821_id[] = {