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/of.h>
|
||||||
#include <linux/util_macros.h>
|
#include <linux/util_macros.h>
|
||||||
|
|
||||||
|
#include <dt-bindings/pwm/pwm.h>
|
||||||
|
|
||||||
/* Indexes for the sysfs hooks */
|
/* Indexes for the sysfs hooks */
|
||||||
|
|
||||||
#define INPUT 0
|
#define INPUT 0
|
||||||
@ -1662,6 +1664,130 @@ static int adt7475_set_pwm_polarity(struct i2c_client *client)
|
|||||||
return 0;
|
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)
|
static int adt7475_probe(struct i2c_client *client)
|
||||||
{
|
{
|
||||||
enum chips chip;
|
enum chips chip;
|
||||||
@ -1774,6 +1900,10 @@ static int adt7475_probe(struct i2c_client *client)
|
|||||||
if (ret && ret != -EINVAL)
|
if (ret && ret != -EINVAL)
|
||||||
dev_warn(&client->dev, "Error configuring pwm polarity\n");
|
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 */
|
/* Start monitoring */
|
||||||
switch (chip) {
|
switch (chip) {
|
||||||
case adt7475:
|
case adt7475:
|
||||||
|
Loading…
Reference in New Issue
Block a user