mvebu: puzzle: fix fan thermal cooling driver
Several fixes for the Puzzle WT61P803 hwmon driver were needed to make it behave well as thermal cooling device: - wire-up cooling device with OF node in device tree - properly parse cooling-levels (u32 with range check vs. u8) - actually use cooling-levels - keep current state and only write to uC if state has changed (avoids flooding the uC with commands which will result in uC crashing) Signed-off-by: Daniel Golle <daniel@makrotopia.org>
This commit is contained in:
		| @@ -53,7 +53,7 @@ Cc: Robert Marko <robert.marko@sartura.hr> | |||||||
|  obj-$(CONFIG_SENSORS_IBMPOWERNV)+= ibmpowernv.o |  obj-$(CONFIG_SENSORS_IBMPOWERNV)+= ibmpowernv.o | ||||||
| --- /dev/null | --- /dev/null | ||||||
| +++ b/drivers/hwmon/iei-wt61p803-puzzle-hwmon.c | +++ b/drivers/hwmon/iei-wt61p803-puzzle-hwmon.c | ||||||
| @@ -0,0 +1,413 @@ | @@ -0,0 +1,445 @@ | ||||||
| +// SPDX-License-Identifier: GPL-2.0-only | +// SPDX-License-Identifier: GPL-2.0-only | ||||||
| +/* IEI WT61P803 PUZZLE MCU HWMON Driver | +/* IEI WT61P803 PUZZLE MCU HWMON Driver | ||||||
| + * | + * | ||||||
| @@ -84,13 +84,17 @@ Cc: Robert Marko <robert.marko@sartura.hr> | |||||||
| + * @name:		Thermal cooling device name | + * @name:		Thermal cooling device name | ||||||
| + * @pwm_channel:	Controlled PWM channel (0 or 1) | + * @pwm_channel:	Controlled PWM channel (0 or 1) | ||||||
| + * @cooling_levels:	Thermal cooling device cooling levels (DT) | + * @cooling_levels:	Thermal cooling device cooling levels (DT) | ||||||
|  | + * @cur_level:		Current cooling level | ||||||
|  | + * @num_levels:	Number of cooling levels | ||||||
| + */ | + */ | ||||||
| +struct iei_wt61p803_puzzle_thermal_cooling_device { | +struct iei_wt61p803_puzzle_thermal_cooling_device { | ||||||
| +	struct iei_wt61p803_puzzle_hwmon *mcu_hwmon; | +	struct iei_wt61p803_puzzle_hwmon *mcu_hwmon; | ||||||
| +	struct thermal_cooling_device *tcdev; | +	struct thermal_cooling_device *tcdev; | ||||||
| +	char name[THERMAL_NAME_LENGTH]; | +	char name[THERMAL_NAME_LENGTH]; | ||||||
| +	int pwm_channel; | +	int pwm_channel; | ||||||
| +	u8 *cooling_levels; | +	u32 *cooling_levels; | ||||||
|  | +	int cur_level; | ||||||
|  | +	u8 num_levels; | ||||||
| +}; | +}; | ||||||
| + | + | ||||||
| +/** | +/** | ||||||
| @@ -326,8 +330,12 @@ Cc: Robert Marko <robert.marko@sartura.hr> | |||||||
| +static int iei_wt61p803_puzzle_get_max_state(struct thermal_cooling_device *tcdev, | +static int iei_wt61p803_puzzle_get_max_state(struct thermal_cooling_device *tcdev, | ||||||
| +					     unsigned long *state) | +					     unsigned long *state) | ||||||
| +{ | +{ | ||||||
| +	*state = IEI_WT61P803_PUZZLE_HWMON_MAX_PWM_VAL; | +	struct iei_wt61p803_puzzle_thermal_cooling_device *cdev = tcdev->devdata; | ||||||
| + | + | ||||||
|  | +	if (!cdev) | ||||||
|  | +		return -EINVAL; | ||||||
|  | + | ||||||
|  | +	*state = cdev->num_levels - 1; | ||||||
| +	return 0; | +	return 0; | ||||||
| +} | +} | ||||||
| + | + | ||||||
| @@ -335,14 +343,14 @@ Cc: Robert Marko <robert.marko@sartura.hr> | |||||||
| +					     unsigned long *state) | +					     unsigned long *state) | ||||||
| +{ | +{ | ||||||
| +	struct iei_wt61p803_puzzle_thermal_cooling_device *cdev = tcdev->devdata; | +	struct iei_wt61p803_puzzle_thermal_cooling_device *cdev = tcdev->devdata; | ||||||
| +	struct iei_wt61p803_puzzle_hwmon *mcu_hwmon = cdev->mcu_hwmon; |  | ||||||
| +	long value; |  | ||||||
| +	int ret; |  | ||||||
| + | + | ||||||
| +	ret = iei_wt61p803_puzzle_read_pwm_channel(mcu_hwmon, cdev->pwm_channel, &value); | +	if (!cdev) | ||||||
| +	if (ret) | +		return -EINVAL; | ||||||
| +		return ret; | + | ||||||
| +	*state = value; | +	if (cdev->cur_level < 0) | ||||||
|  | +		return -EAGAIN; | ||||||
|  | + | ||||||
|  | +	*state = cdev->cur_level; | ||||||
| +	return 0; | +	return 0; | ||||||
| +} | +} | ||||||
| + | + | ||||||
| @@ -350,9 +358,21 @@ Cc: Robert Marko <robert.marko@sartura.hr> | |||||||
| +					     unsigned long state) | +					     unsigned long state) | ||||||
| +{ | +{ | ||||||
| +	struct iei_wt61p803_puzzle_thermal_cooling_device *cdev = tcdev->devdata; | +	struct iei_wt61p803_puzzle_thermal_cooling_device *cdev = tcdev->devdata; | ||||||
| +	struct iei_wt61p803_puzzle_hwmon *mcu_hwmon = cdev->mcu_hwmon; | +	u8 pwm_level; | ||||||
| + | + | ||||||
| +	return iei_wt61p803_puzzle_write_pwm_channel(mcu_hwmon, cdev->pwm_channel, state); | +	if (!cdev) | ||||||
|  | +		return -EINVAL; | ||||||
|  | + | ||||||
|  | +	if (state >= cdev->num_levels) | ||||||
|  | +		return -EINVAL; | ||||||
|  | + | ||||||
|  | +	if (state == cdev->cur_level) | ||||||
|  | +		return 0; | ||||||
|  | + | ||||||
|  | +	cdev->cur_level = state; | ||||||
|  | +	pwm_level = cdev->cooling_levels[state]; | ||||||
|  | + | ||||||
|  | +	return iei_wt61p803_puzzle_write_pwm_channel(cdev->mcu_hwmon, cdev->pwm_channel, pwm_level); | ||||||
| +} | +} | ||||||
| + | + | ||||||
| +static const struct thermal_cooling_device_ops iei_wt61p803_puzzle_cooling_ops = { | +static const struct thermal_cooling_device_ops iei_wt61p803_puzzle_cooling_ops = { | ||||||
| @@ -369,7 +389,7 @@ Cc: Robert Marko <robert.marko@sartura.hr> | |||||||
| +	struct iei_wt61p803_puzzle_thermal_cooling_device *cdev; | +	struct iei_wt61p803_puzzle_thermal_cooling_device *cdev; | ||||||
| +	u32 pwm_channel; | +	u32 pwm_channel; | ||||||
| +	u8 num_levels; | +	u8 num_levels; | ||||||
| +	int ret; | +	int i, ret; | ||||||
| + | + | ||||||
| +	ret = fwnode_property_read_u32(child, "reg", &pwm_channel); | +	ret = fwnode_property_read_u32(child, "reg", &pwm_channel); | ||||||
| +	if (ret) | +	if (ret) | ||||||
| @@ -377,7 +397,7 @@ Cc: Robert Marko <robert.marko@sartura.hr> | |||||||
| + | + | ||||||
| +	mcu_hwmon->thermal_cooling_dev_present[pwm_channel] = true; | +	mcu_hwmon->thermal_cooling_dev_present[pwm_channel] = true; | ||||||
| + | + | ||||||
| +	num_levels = fwnode_property_count_u8(child, "cooling-levels"); | +	num_levels = fwnode_property_count_u32(child, "cooling-levels"); | ||||||
| +	if (!num_levels) | +	if (!num_levels) | ||||||
| +		return -EINVAL; | +		return -EINVAL; | ||||||
| + | + | ||||||
| @@ -385,11 +405,11 @@ Cc: Robert Marko <robert.marko@sartura.hr> | |||||||
| +	if (!cdev) | +	if (!cdev) | ||||||
| +		return -ENOMEM; | +		return -ENOMEM; | ||||||
| + | + | ||||||
| +	cdev->cooling_levels = devm_kmalloc_array(dev, num_levels, sizeof(u8), GFP_KERNEL); | +	cdev->cooling_levels = devm_kmalloc_array(dev, num_levels, sizeof(u32), GFP_KERNEL); | ||||||
| +	if (!cdev->cooling_levels) | +	if (!cdev->cooling_levels) | ||||||
| +		return -ENOMEM; | +		return -ENOMEM; | ||||||
| + | + | ||||||
| +	ret = fwnode_property_read_u8_array(child, "cooling-levels", | +	ret = fwnode_property_read_u32_array(child, "cooling-levels", | ||||||
| +					     cdev->cooling_levels, | +					     cdev->cooling_levels, | ||||||
| +					     num_levels); | +					     num_levels); | ||||||
| +	if (ret) { | +	if (ret) { | ||||||
| @@ -397,16 +417,28 @@ Cc: Robert Marko <robert.marko@sartura.hr> | |||||||
| +		return ret; | +		return ret; | ||||||
| +	} | +	} | ||||||
| + | + | ||||||
| +	snprintf(cdev->name, THERMAL_NAME_LENGTH, "wt61p803_puzzle_%d", pwm_channel); | +	for (i = 0; i < num_levels; i++) { | ||||||
| +	cdev->tcdev = devm_thermal_of_cooling_device_register(dev, NULL, cdev->name, cdev, | +		if (cdev->cooling_levels[i] > | ||||||
| +							      &iei_wt61p803_puzzle_cooling_ops); | +		    IEI_WT61P803_PUZZLE_HWMON_MAX_PWM_VAL) { | ||||||
| +	if (IS_ERR(cdev->tcdev)) | +			dev_err(dev, "iei_wt61p803_fan state[%d]:%d > %d\n", i, | ||||||
| +		return PTR_ERR(cdev->tcdev); | +				cdev->cooling_levels[i], | ||||||
|  | +				IEI_WT61P803_PUZZLE_HWMON_MAX_PWM_VAL); | ||||||
|  | +			return -EINVAL; | ||||||
|  | +		} | ||||||
|  | +	} | ||||||
| + | + | ||||||
| +	cdev->mcu_hwmon = mcu_hwmon; | +	cdev->mcu_hwmon = mcu_hwmon; | ||||||
| +	cdev->pwm_channel = pwm_channel; | +	cdev->pwm_channel = pwm_channel; | ||||||
|  | +	cdev->num_levels = num_levels; | ||||||
|  | +	cdev->cur_level = -1; | ||||||
| +	mcu_hwmon->cdev[pwm_channel] = cdev; | +	mcu_hwmon->cdev[pwm_channel] = cdev; | ||||||
| + | + | ||||||
|  | +	snprintf(cdev->name, THERMAL_NAME_LENGTH, "wt61p803_puzzle_%d", pwm_channel); | ||||||
|  | +	cdev->tcdev = devm_thermal_of_cooling_device_register(dev, to_of_node(child), cdev->name, | ||||||
|  | +							      cdev, &iei_wt61p803_puzzle_cooling_ops); | ||||||
|  | +	if (IS_ERR(cdev->tcdev)) | ||||||
|  | +		return PTR_ERR(cdev->tcdev); | ||||||
|  | + | ||||||
| +	return 0; | +	return 0; | ||||||
| +} | +} | ||||||
| + | + | ||||||
|   | |||||||
| @@ -53,7 +53,7 @@ Cc: Robert Marko <robert.marko@sartura.hr> | |||||||
|  obj-$(CONFIG_SENSORS_IBMPOWERNV)+= ibmpowernv.o |  obj-$(CONFIG_SENSORS_IBMPOWERNV)+= ibmpowernv.o | ||||||
| --- /dev/null | --- /dev/null | ||||||
| +++ b/drivers/hwmon/iei-wt61p803-puzzle-hwmon.c | +++ b/drivers/hwmon/iei-wt61p803-puzzle-hwmon.c | ||||||
| @@ -0,0 +1,413 @@ | @@ -0,0 +1,445 @@ | ||||||
| +// SPDX-License-Identifier: GPL-2.0-only | +// SPDX-License-Identifier: GPL-2.0-only | ||||||
| +/* IEI WT61P803 PUZZLE MCU HWMON Driver | +/* IEI WT61P803 PUZZLE MCU HWMON Driver | ||||||
| + * | + * | ||||||
| @@ -84,13 +84,17 @@ Cc: Robert Marko <robert.marko@sartura.hr> | |||||||
| + * @name:		Thermal cooling device name | + * @name:		Thermal cooling device name | ||||||
| + * @pwm_channel:	Controlled PWM channel (0 or 1) | + * @pwm_channel:	Controlled PWM channel (0 or 1) | ||||||
| + * @cooling_levels:	Thermal cooling device cooling levels (DT) | + * @cooling_levels:	Thermal cooling device cooling levels (DT) | ||||||
|  | + * @cur_level:		Current cooling level | ||||||
|  | + * @num_levels:	Number of cooling levels | ||||||
| + */ | + */ | ||||||
| +struct iei_wt61p803_puzzle_thermal_cooling_device { | +struct iei_wt61p803_puzzle_thermal_cooling_device { | ||||||
| +	struct iei_wt61p803_puzzle_hwmon *mcu_hwmon; | +	struct iei_wt61p803_puzzle_hwmon *mcu_hwmon; | ||||||
| +	struct thermal_cooling_device *tcdev; | +	struct thermal_cooling_device *tcdev; | ||||||
| +	char name[THERMAL_NAME_LENGTH]; | +	char name[THERMAL_NAME_LENGTH]; | ||||||
| +	int pwm_channel; | +	int pwm_channel; | ||||||
| +	u8 *cooling_levels; | +	u32 *cooling_levels; | ||||||
|  | +	int cur_level; | ||||||
|  | +	u8 num_levels; | ||||||
| +}; | +}; | ||||||
| + | + | ||||||
| +/** | +/** | ||||||
| @@ -326,8 +330,12 @@ Cc: Robert Marko <robert.marko@sartura.hr> | |||||||
| +static int iei_wt61p803_puzzle_get_max_state(struct thermal_cooling_device *tcdev, | +static int iei_wt61p803_puzzle_get_max_state(struct thermal_cooling_device *tcdev, | ||||||
| +					     unsigned long *state) | +					     unsigned long *state) | ||||||
| +{ | +{ | ||||||
| +	*state = IEI_WT61P803_PUZZLE_HWMON_MAX_PWM_VAL; | +	struct iei_wt61p803_puzzle_thermal_cooling_device *cdev = tcdev->devdata; | ||||||
| + | + | ||||||
|  | +	if (!cdev) | ||||||
|  | +		return -EINVAL; | ||||||
|  | + | ||||||
|  | +	*state = cdev->num_levels - 1; | ||||||
| +	return 0; | +	return 0; | ||||||
| +} | +} | ||||||
| + | + | ||||||
| @@ -335,14 +343,14 @@ Cc: Robert Marko <robert.marko@sartura.hr> | |||||||
| +					     unsigned long *state) | +					     unsigned long *state) | ||||||
| +{ | +{ | ||||||
| +	struct iei_wt61p803_puzzle_thermal_cooling_device *cdev = tcdev->devdata; | +	struct iei_wt61p803_puzzle_thermal_cooling_device *cdev = tcdev->devdata; | ||||||
| +	struct iei_wt61p803_puzzle_hwmon *mcu_hwmon = cdev->mcu_hwmon; |  | ||||||
| +	long value; |  | ||||||
| +	int ret; |  | ||||||
| + | + | ||||||
| +	ret = iei_wt61p803_puzzle_read_pwm_channel(mcu_hwmon, cdev->pwm_channel, &value); | +	if (!cdev) | ||||||
| +	if (ret) | +		return -EINVAL; | ||||||
| +		return ret; | + | ||||||
| +	*state = value; | +	if (cdev->cur_level < 0) | ||||||
|  | +		return -EAGAIN; | ||||||
|  | + | ||||||
|  | +	*state = cdev->cur_level; | ||||||
| +	return 0; | +	return 0; | ||||||
| +} | +} | ||||||
| + | + | ||||||
| @@ -350,9 +358,21 @@ Cc: Robert Marko <robert.marko@sartura.hr> | |||||||
| +					     unsigned long state) | +					     unsigned long state) | ||||||
| +{ | +{ | ||||||
| +	struct iei_wt61p803_puzzle_thermal_cooling_device *cdev = tcdev->devdata; | +	struct iei_wt61p803_puzzle_thermal_cooling_device *cdev = tcdev->devdata; | ||||||
| +	struct iei_wt61p803_puzzle_hwmon *mcu_hwmon = cdev->mcu_hwmon; | +	u8 pwm_level; | ||||||
| + | + | ||||||
| +	return iei_wt61p803_puzzle_write_pwm_channel(mcu_hwmon, cdev->pwm_channel, state); | +	if (!cdev) | ||||||
|  | +		return -EINVAL; | ||||||
|  | + | ||||||
|  | +	if (state >= cdev->num_levels) | ||||||
|  | +		return -EINVAL; | ||||||
|  | + | ||||||
|  | +	if (state == cdev->cur_level) | ||||||
|  | +		return 0; | ||||||
|  | + | ||||||
|  | +	cdev->cur_level = state; | ||||||
|  | +	pwm_level = cdev->cooling_levels[state]; | ||||||
|  | + | ||||||
|  | +	return iei_wt61p803_puzzle_write_pwm_channel(cdev->mcu_hwmon, cdev->pwm_channel, pwm_level); | ||||||
| +} | +} | ||||||
| + | + | ||||||
| +static const struct thermal_cooling_device_ops iei_wt61p803_puzzle_cooling_ops = { | +static const struct thermal_cooling_device_ops iei_wt61p803_puzzle_cooling_ops = { | ||||||
| @@ -369,7 +389,7 @@ Cc: Robert Marko <robert.marko@sartura.hr> | |||||||
| +	struct iei_wt61p803_puzzle_thermal_cooling_device *cdev; | +	struct iei_wt61p803_puzzle_thermal_cooling_device *cdev; | ||||||
| +	u32 pwm_channel; | +	u32 pwm_channel; | ||||||
| +	u8 num_levels; | +	u8 num_levels; | ||||||
| +	int ret; | +	int i, ret; | ||||||
| + | + | ||||||
| +	ret = fwnode_property_read_u32(child, "reg", &pwm_channel); | +	ret = fwnode_property_read_u32(child, "reg", &pwm_channel); | ||||||
| +	if (ret) | +	if (ret) | ||||||
| @@ -377,7 +397,7 @@ Cc: Robert Marko <robert.marko@sartura.hr> | |||||||
| + | + | ||||||
| +	mcu_hwmon->thermal_cooling_dev_present[pwm_channel] = true; | +	mcu_hwmon->thermal_cooling_dev_present[pwm_channel] = true; | ||||||
| + | + | ||||||
| +	num_levels = fwnode_property_count_u8(child, "cooling-levels"); | +	num_levels = fwnode_property_count_u32(child, "cooling-levels"); | ||||||
| +	if (!num_levels) | +	if (!num_levels) | ||||||
| +		return -EINVAL; | +		return -EINVAL; | ||||||
| + | + | ||||||
| @@ -385,11 +405,11 @@ Cc: Robert Marko <robert.marko@sartura.hr> | |||||||
| +	if (!cdev) | +	if (!cdev) | ||||||
| +		return -ENOMEM; | +		return -ENOMEM; | ||||||
| + | + | ||||||
| +	cdev->cooling_levels = devm_kmalloc_array(dev, num_levels, sizeof(u8), GFP_KERNEL); | +	cdev->cooling_levels = devm_kmalloc_array(dev, num_levels, sizeof(u32), GFP_KERNEL); | ||||||
| +	if (!cdev->cooling_levels) | +	if (!cdev->cooling_levels) | ||||||
| +		return -ENOMEM; | +		return -ENOMEM; | ||||||
| + | + | ||||||
| +	ret = fwnode_property_read_u8_array(child, "cooling-levels", | +	ret = fwnode_property_read_u32_array(child, "cooling-levels", | ||||||
| +					     cdev->cooling_levels, | +					     cdev->cooling_levels, | ||||||
| +					     num_levels); | +					     num_levels); | ||||||
| +	if (ret) { | +	if (ret) { | ||||||
| @@ -397,16 +417,28 @@ Cc: Robert Marko <robert.marko@sartura.hr> | |||||||
| +		return ret; | +		return ret; | ||||||
| +	} | +	} | ||||||
| + | + | ||||||
| +	snprintf(cdev->name, THERMAL_NAME_LENGTH, "wt61p803_puzzle_%d", pwm_channel); | +	for (i = 0; i < num_levels; i++) { | ||||||
| +	cdev->tcdev = devm_thermal_of_cooling_device_register(dev, NULL, cdev->name, cdev, | +		if (cdev->cooling_levels[i] > | ||||||
| +							      &iei_wt61p803_puzzle_cooling_ops); | +		    IEI_WT61P803_PUZZLE_HWMON_MAX_PWM_VAL) { | ||||||
| +	if (IS_ERR(cdev->tcdev)) | +			dev_err(dev, "iei_wt61p803_fan state[%d]:%d > %d\n", i, | ||||||
| +		return PTR_ERR(cdev->tcdev); | +				cdev->cooling_levels[i], | ||||||
|  | +				IEI_WT61P803_PUZZLE_HWMON_MAX_PWM_VAL); | ||||||
|  | +			return -EINVAL; | ||||||
|  | +		} | ||||||
|  | +	} | ||||||
| + | + | ||||||
| +	cdev->mcu_hwmon = mcu_hwmon; | +	cdev->mcu_hwmon = mcu_hwmon; | ||||||
| +	cdev->pwm_channel = pwm_channel; | +	cdev->pwm_channel = pwm_channel; | ||||||
|  | +	cdev->num_levels = num_levels; | ||||||
|  | +	cdev->cur_level = -1; | ||||||
| +	mcu_hwmon->cdev[pwm_channel] = cdev; | +	mcu_hwmon->cdev[pwm_channel] = cdev; | ||||||
| + | + | ||||||
|  | +	snprintf(cdev->name, THERMAL_NAME_LENGTH, "wt61p803_puzzle_%d", pwm_channel); | ||||||
|  | +	cdev->tcdev = devm_thermal_of_cooling_device_register(dev, to_of_node(child), cdev->name, | ||||||
|  | +							      cdev, &iei_wt61p803_puzzle_cooling_ops); | ||||||
|  | +	if (IS_ERR(cdev->tcdev)) | ||||||
|  | +		return PTR_ERR(cdev->tcdev); | ||||||
|  | + | ||||||
| +	return 0; | +	return 0; | ||||||
| +} | +} | ||||||
| + | + | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Daniel Golle
					Daniel Golle