mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	hwmon: (adt7475) Add support for configuring initial PWM state
By default the PWM duty cycle in hardware is 100%. On some systems this can cause unwanted fan noise. Add the ability to specify the fan connections and initial state of the PWMs via device properties. Signed-off-by: Chris Packham <chris.packham@alliedtelesis.co.nz> Link: https://lore.kernel.org/r/20240722221737.3407958-4-chris.packham@alliedtelesis.co.nz [groeck: Cleaned up formatting] Signed-off-by: Guenter Roeck <linux@roeck-us.net>
This commit is contained in:
		
							parent
							
								
									2070562955
								
							
						
					
					
						commit
						777c97ff08
					
				
					 1 changed files with 130 additions and 0 deletions
				
			
		| 
						 | 
				
			
			@ -21,6 +21,8 @@
 | 
			
		|||
#include <linux/of.h>
 | 
			
		||||
#include <linux/util_macros.h>
 | 
			
		||||
 | 
			
		||||
#include <dt-bindings/pwm/pwm.h>
 | 
			
		||||
 | 
			
		||||
/* Indexes for the sysfs hooks */
 | 
			
		||||
 | 
			
		||||
#define INPUT		0
 | 
			
		||||
| 
						 | 
				
			
			@ -1662,6 +1664,130 @@ static int adt7475_set_pwm_polarity(struct i2c_client *client)
 | 
			
		|||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct adt7475_pwm_config {
 | 
			
		||||
	int index;
 | 
			
		||||
	int freq;
 | 
			
		||||
	int flags;
 | 
			
		||||
	int duty;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int _adt7475_pwm_properties_parse_args(u32 args[4], struct adt7475_pwm_config *cfg)
 | 
			
		||||
{
 | 
			
		||||
	int freq_hz;
 | 
			
		||||
	int duty;
 | 
			
		||||
 | 
			
		||||
	if (args[1] == 0)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	freq_hz = 1000000000UL / args[1];
 | 
			
		||||
	if (args[3] >= args[1])
 | 
			
		||||
		duty = 255;
 | 
			
		||||
	else
 | 
			
		||||
		duty = div_u64(255ULL * args[3], args[1]);
 | 
			
		||||
 | 
			
		||||
	cfg->index = args[0];
 | 
			
		||||
	cfg->freq = find_closest(freq_hz, pwmfreq_table, ARRAY_SIZE(pwmfreq_table));
 | 
			
		||||
	cfg->flags = args[2];
 | 
			
		||||
	cfg->duty = duty;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int adt7475_pwm_properties_parse_reference_args(struct fwnode_handle *fwnode,
 | 
			
		||||
						       struct adt7475_pwm_config *cfg)
 | 
			
		||||
{
 | 
			
		||||
	int ret, i;
 | 
			
		||||
	struct fwnode_reference_args rargs = {};
 | 
			
		||||
	u32 args[4] = {};
 | 
			
		||||
 | 
			
		||||
	ret = fwnode_property_get_reference_args(fwnode, "pwms", "#pwm-cells", 0, 0, &rargs);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	if (rargs.nargs != 4) {
 | 
			
		||||
		fwnode_handle_put(rargs.fwnode);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < 4; i++)
 | 
			
		||||
		args[i] = rargs.args[i];
 | 
			
		||||
 | 
			
		||||
	ret = _adt7475_pwm_properties_parse_args(args, cfg);
 | 
			
		||||
 | 
			
		||||
	fwnode_handle_put(rargs.fwnode);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int adt7475_pwm_properties_parse_args(struct fwnode_handle *fwnode,
 | 
			
		||||
					     struct adt7475_pwm_config *cfg)
 | 
			
		||||
{
 | 
			
		||||
	int ret;
 | 
			
		||||
	u32 args[4] = {};
 | 
			
		||||
 | 
			
		||||
	ret = fwnode_property_read_u32_array(fwnode, "pwms", args, ARRAY_SIZE(args));
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	return _adt7475_pwm_properties_parse_args(args, cfg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int adt7475_fan_pwm_config(struct i2c_client *client)
 | 
			
		||||
{
 | 
			
		||||
	struct adt7475_data *data = i2c_get_clientdata(client);
 | 
			
		||||
	struct fwnode_handle *child;
 | 
			
		||||
	struct adt7475_pwm_config cfg = {};
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	device_for_each_child_node(&client->dev, child) {
 | 
			
		||||
		if (!fwnode_property_present(child, "pwms"))
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		if (is_of_node(child))
 | 
			
		||||
			ret = adt7475_pwm_properties_parse_reference_args(child, &cfg);
 | 
			
		||||
		else
 | 
			
		||||
			ret = adt7475_pwm_properties_parse_args(child, &cfg);
 | 
			
		||||
 | 
			
		||||
		if (cfg.index >= ADT7475_PWM_COUNT)
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
 | 
			
		||||
		ret = adt7475_read(PWM_CONFIG_REG(cfg.index));
 | 
			
		||||
		if (ret < 0)
 | 
			
		||||
			return ret;
 | 
			
		||||
		data->pwm[CONTROL][cfg.index] = ret;
 | 
			
		||||
		if (cfg.flags & PWM_POLARITY_INVERTED)
 | 
			
		||||
			data->pwm[CONTROL][cfg.index] |= BIT(4);
 | 
			
		||||
		else
 | 
			
		||||
			data->pwm[CONTROL][cfg.index] &= ~BIT(4);
 | 
			
		||||
 | 
			
		||||
		/* Force to manual mode so PWM values take effect */
 | 
			
		||||
		data->pwm[CONTROL][cfg.index] &= ~0xE0;
 | 
			
		||||
		data->pwm[CONTROL][cfg.index] |= 0x07 << 5;
 | 
			
		||||
 | 
			
		||||
		ret = i2c_smbus_write_byte_data(client, PWM_CONFIG_REG(cfg.index),
 | 
			
		||||
						data->pwm[CONTROL][cfg.index]);
 | 
			
		||||
		if (ret)
 | 
			
		||||
			return ret;
 | 
			
		||||
 | 
			
		||||
		data->pwm[INPUT][cfg.index] = cfg.duty;
 | 
			
		||||
		ret = i2c_smbus_write_byte_data(client, PWM_REG(cfg.index),
 | 
			
		||||
						data->pwm[INPUT][cfg.index]);
 | 
			
		||||
		if (ret)
 | 
			
		||||
			return ret;
 | 
			
		||||
 | 
			
		||||
		data->range[cfg.index] = adt7475_read(TEMP_TRANGE_REG(cfg.index));
 | 
			
		||||
		data->range[cfg.index] &= ~0xf;
 | 
			
		||||
		data->range[cfg.index] |= cfg.freq;
 | 
			
		||||
 | 
			
		||||
		ret = i2c_smbus_write_byte_data(client, TEMP_TRANGE_REG(cfg.index),
 | 
			
		||||
						data->range[cfg.index]);
 | 
			
		||||
		if (ret)
 | 
			
		||||
			return ret;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int adt7475_probe(struct i2c_client *client)
 | 
			
		||||
{
 | 
			
		||||
	enum chips chip;
 | 
			
		||||
| 
						 | 
				
			
			@ -1774,6 +1900,10 @@ static int adt7475_probe(struct i2c_client *client)
 | 
			
		|||
	if (ret && ret != -EINVAL)
 | 
			
		||||
		dev_warn(&client->dev, "Error configuring pwm polarity\n");
 | 
			
		||||
 | 
			
		||||
	ret = adt7475_fan_pwm_config(client);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		dev_warn(&client->dev, "Error %d configuring fan/pwm\n", ret);
 | 
			
		||||
 | 
			
		||||
	/* Start monitoring */
 | 
			
		||||
	switch (chip) {
 | 
			
		||||
	case adt7475:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue