Commit 777c97ff authored by Chris Packham's avatar Chris Packham Committed by Guenter Roeck
Browse files

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: default avatarChris 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: default avatarGuenter Roeck <linux@roeck-us.net>
parent 20705629
Loading
Loading
Loading
Loading
+130 −0
Original line number Diff line number Diff line
@@ -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: