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
@ -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 New Issue
Block a user