diff options
author | Mykola Kostenok <c_mykolak@mellanox.com> | 2017-08-03 04:50:44 -0400 |
---|---|---|
committer | Guenter Roeck <linux@roeck-us.net> | 2017-08-13 11:24:38 -0400 |
commit | f198907d2ff6db9541863764576aaf3bc9f58ec0 (patch) | |
tree | 0d9f2f036ef54a83ea93417ac9ccf3e2c918cd29 /drivers/hwmon/aspeed-pwm-tacho.c | |
parent | 5e047541c1412f22d1c0950260da337ebc9bdb49 (diff) |
hwmon: (aspeed-pwm-tacho) cooling device support.
Add support in aspeed-pwm-tacho driver for cooling device creation.
This cooling device could be bound to a thermal zone
for the thermal control. Device will appear in /sys/class/thermal
folder as cooling_deviceX. Then it could be bound to particular
thermal zones. Allow specification of the cooling levels
vector - PWM duty cycle values in a range from 0 to 255
which correspond to thermal cooling states.
Signed-off-by: Mykola Kostenok <c_mykolak@mellanox.com>
Reviewed-by: Joel Stanley <joel@jms.id.au>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Diffstat (limited to 'drivers/hwmon/aspeed-pwm-tacho.c')
-rw-r--r-- | drivers/hwmon/aspeed-pwm-tacho.c | 116 |
1 files changed, 114 insertions, 2 deletions
diff --git a/drivers/hwmon/aspeed-pwm-tacho.c b/drivers/hwmon/aspeed-pwm-tacho.c index ddfe66bdff86..69b97d45e3cb 100644 --- a/drivers/hwmon/aspeed-pwm-tacho.c +++ b/drivers/hwmon/aspeed-pwm-tacho.c | |||
@@ -20,6 +20,7 @@ | |||
20 | #include <linux/platform_device.h> | 20 | #include <linux/platform_device.h> |
21 | #include <linux/sysfs.h> | 21 | #include <linux/sysfs.h> |
22 | #include <linux/regmap.h> | 22 | #include <linux/regmap.h> |
23 | #include <linux/thermal.h> | ||
23 | 24 | ||
24 | /* ASPEED PWM & FAN Tach Register Definition */ | 25 | /* ASPEED PWM & FAN Tach Register Definition */ |
25 | #define ASPEED_PTCR_CTRL 0x00 | 26 | #define ASPEED_PTCR_CTRL 0x00 |
@@ -166,6 +167,18 @@ | |||
166 | /* How long we sleep in us while waiting for an RPM result. */ | 167 | /* How long we sleep in us while waiting for an RPM result. */ |
167 | #define ASPEED_RPM_STATUS_SLEEP_USEC 500 | 168 | #define ASPEED_RPM_STATUS_SLEEP_USEC 500 |
168 | 169 | ||
170 | #define MAX_CDEV_NAME_LEN 16 | ||
171 | |||
172 | struct aspeed_cooling_device { | ||
173 | char name[16]; | ||
174 | struct aspeed_pwm_tacho_data *priv; | ||
175 | struct thermal_cooling_device *tcdev; | ||
176 | int pwm_port; | ||
177 | u8 *cooling_levels; | ||
178 | u8 max_state; | ||
179 | u8 cur_state; | ||
180 | }; | ||
181 | |||
169 | struct aspeed_pwm_tacho_data { | 182 | struct aspeed_pwm_tacho_data { |
170 | struct regmap *regmap; | 183 | struct regmap *regmap; |
171 | unsigned long clk_freq; | 184 | unsigned long clk_freq; |
@@ -180,6 +193,7 @@ struct aspeed_pwm_tacho_data { | |||
180 | u8 pwm_port_type[8]; | 193 | u8 pwm_port_type[8]; |
181 | u8 pwm_port_fan_ctrl[8]; | 194 | u8 pwm_port_fan_ctrl[8]; |
182 | u8 fan_tach_ch_source[16]; | 195 | u8 fan_tach_ch_source[16]; |
196 | struct aspeed_cooling_device *cdev[8]; | ||
183 | const struct attribute_group *groups[3]; | 197 | const struct attribute_group *groups[3]; |
184 | }; | 198 | }; |
185 | 199 | ||
@@ -765,6 +779,94 @@ static void aspeed_create_fan_tach_channel(struct aspeed_pwm_tacho_data *priv, | |||
765 | } | 779 | } |
766 | } | 780 | } |
767 | 781 | ||
782 | static int | ||
783 | aspeed_pwm_cz_get_max_state(struct thermal_cooling_device *tcdev, | ||
784 | unsigned long *state) | ||
785 | { | ||
786 | struct aspeed_cooling_device *cdev = tcdev->devdata; | ||
787 | |||
788 | *state = cdev->max_state; | ||
789 | |||
790 | return 0; | ||
791 | } | ||
792 | |||
793 | static int | ||
794 | aspeed_pwm_cz_get_cur_state(struct thermal_cooling_device *tcdev, | ||
795 | unsigned long *state) | ||
796 | { | ||
797 | struct aspeed_cooling_device *cdev = tcdev->devdata; | ||
798 | |||
799 | *state = cdev->cur_state; | ||
800 | |||
801 | return 0; | ||
802 | } | ||
803 | |||
804 | static int | ||
805 | aspeed_pwm_cz_set_cur_state(struct thermal_cooling_device *tcdev, | ||
806 | unsigned long state) | ||
807 | { | ||
808 | struct aspeed_cooling_device *cdev = tcdev->devdata; | ||
809 | |||
810 | if (state > cdev->max_state) | ||
811 | return -EINVAL; | ||
812 | |||
813 | cdev->cur_state = state; | ||
814 | cdev->priv->pwm_port_fan_ctrl[cdev->pwm_port] = | ||
815 | cdev->cooling_levels[cdev->cur_state]; | ||
816 | aspeed_set_pwm_port_fan_ctrl(cdev->priv, cdev->pwm_port, | ||
817 | cdev->cooling_levels[cdev->cur_state]); | ||
818 | |||
819 | return 0; | ||
820 | } | ||
821 | |||
822 | static const struct thermal_cooling_device_ops aspeed_pwm_cool_ops = { | ||
823 | .get_max_state = aspeed_pwm_cz_get_max_state, | ||
824 | .get_cur_state = aspeed_pwm_cz_get_cur_state, | ||
825 | .set_cur_state = aspeed_pwm_cz_set_cur_state, | ||
826 | }; | ||
827 | |||
828 | static int aspeed_create_pwm_cooling(struct device *dev, | ||
829 | struct device_node *child, | ||
830 | struct aspeed_pwm_tacho_data *priv, | ||
831 | u32 pwm_port, u8 num_levels) | ||
832 | { | ||
833 | int ret; | ||
834 | struct aspeed_cooling_device *cdev; | ||
835 | |||
836 | cdev = devm_kzalloc(dev, sizeof(*cdev), GFP_KERNEL); | ||
837 | |||
838 | if (!cdev) | ||
839 | return -ENOMEM; | ||
840 | |||
841 | cdev->cooling_levels = devm_kzalloc(dev, num_levels, GFP_KERNEL); | ||
842 | if (!cdev->cooling_levels) | ||
843 | return -ENOMEM; | ||
844 | |||
845 | cdev->max_state = num_levels - 1; | ||
846 | ret = of_property_read_u8_array(child, "cooling-levels", | ||
847 | cdev->cooling_levels, | ||
848 | num_levels); | ||
849 | if (ret) { | ||
850 | dev_err(dev, "Property 'cooling-levels' cannot be read.\n"); | ||
851 | return ret; | ||
852 | } | ||
853 | snprintf(cdev->name, MAX_CDEV_NAME_LEN, "%s%d", child->name, pwm_port); | ||
854 | |||
855 | cdev->tcdev = thermal_of_cooling_device_register(child, | ||
856 | cdev->name, | ||
857 | cdev, | ||
858 | &aspeed_pwm_cool_ops); | ||
859 | if (IS_ERR(cdev->tcdev)) | ||
860 | return PTR_ERR(cdev->tcdev); | ||
861 | |||
862 | cdev->priv = priv; | ||
863 | cdev->pwm_port = pwm_port; | ||
864 | |||
865 | priv->cdev[pwm_port] = cdev; | ||
866 | |||
867 | return 0; | ||
868 | } | ||
869 | |||
768 | static int aspeed_create_fan(struct device *dev, | 870 | static int aspeed_create_fan(struct device *dev, |
769 | struct device_node *child, | 871 | struct device_node *child, |
770 | struct aspeed_pwm_tacho_data *priv) | 872 | struct aspeed_pwm_tacho_data *priv) |
@@ -778,6 +880,15 @@ static int aspeed_create_fan(struct device *dev, | |||
778 | return ret; | 880 | return ret; |
779 | aspeed_create_pwm_port(priv, (u8)pwm_port); | 881 | aspeed_create_pwm_port(priv, (u8)pwm_port); |
780 | 882 | ||
883 | ret = of_property_count_u8_elems(child, "cooling-levels"); | ||
884 | |||
885 | if (ret > 0) { | ||
886 | ret = aspeed_create_pwm_cooling(dev, child, priv, pwm_port, | ||
887 | ret); | ||
888 | if (ret) | ||
889 | return ret; | ||
890 | } | ||
891 | |||
781 | count = of_property_count_u8_elems(child, "aspeed,fan-tach-ch"); | 892 | count = of_property_count_u8_elems(child, "aspeed,fan-tach-ch"); |
782 | if (count < 1) | 893 | if (count < 1) |
783 | return -EINVAL; | 894 | return -EINVAL; |
@@ -834,9 +945,10 @@ static int aspeed_pwm_tacho_probe(struct platform_device *pdev) | |||
834 | 945 | ||
835 | for_each_child_of_node(np, child) { | 946 | for_each_child_of_node(np, child) { |
836 | ret = aspeed_create_fan(dev, child, priv); | 947 | ret = aspeed_create_fan(dev, child, priv); |
837 | of_node_put(child); | 948 | if (ret) { |
838 | if (ret) | 949 | of_node_put(child); |
839 | return ret; | 950 | return ret; |
951 | } | ||
840 | } | 952 | } |
841 | 953 | ||
842 | priv->groups[0] = &pwm_dev_group; | 954 | priv->groups[0] = &pwm_dev_group; |