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:
Chris Packham 2024-07-23 10:17:37 +12:00 committed by Guenter Roeck
parent 2070562955
commit 777c97ff08

View File

@ -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: