diff options
Diffstat (limited to 'drivers/hwmon/adt7475.c')
-rw-r--r-- | drivers/hwmon/adt7475.c | 171 |
1 files changed, 148 insertions, 23 deletions
diff --git a/drivers/hwmon/adt7475.c b/drivers/hwmon/adt7475.c index 4224ffb30483..5f2541c11fe9 100644 --- a/drivers/hwmon/adt7475.c +++ b/drivers/hwmon/adt7475.c @@ -21,24 +21,26 @@ #include <linux/of.h> #include <linux/util_macros.h> -/* Indexes for the sysfs hooks */ - -#define INPUT 0 -#define MIN 1 -#define MAX 2 -#define CONTROL 3 -#define OFFSET 3 -#define AUTOMIN 4 -#define THERM 5 -#define HYSTERSIS 6 +#include <dt-bindings/pwm/pwm.h> +/* Indexes for the sysfs hooks */ +enum adt_sysfs_id { + INPUT = 0, + MIN = 1, + MAX = 2, + CONTROL = 3, + OFFSET = 3, // Dup + AUTOMIN = 4, + THERM = 5, + HYSTERSIS = 6, /* * These are unique identifiers for the sysfs functions - unlike the * numbers above, these are not also indexes into an array */ + ALARM = 9, + FAULT = 10, +}; -#define ALARM 9 -#define FAULT 10 /* 7475 Common Registers */ @@ -1662,6 +1664,129 @@ 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 adt7475_pwm_config cfg = {}; + int ret; + + device_for_each_child_node_scoped(&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; @@ -1676,7 +1801,6 @@ static int adt7475_probe(struct i2c_client *client) struct device *hwmon_dev; int i, ret = 0, revision, group_num = 0; u8 config3; - const struct i2c_device_id *id = i2c_match_id(adt7475_id, client); data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); if (data == NULL) @@ -1686,10 +1810,7 @@ static int adt7475_probe(struct i2c_client *client) data->client = client; i2c_set_clientdata(client, data); - if (client->dev.of_node) - chip = (uintptr_t)of_device_get_match_data(&client->dev); - else - chip = id->driver_data; + chip = (uintptr_t)i2c_get_match_data(client); /* Initialize device-specific values */ switch (chip) { @@ -1717,7 +1838,7 @@ static int adt7475_probe(struct i2c_client *client) if (!(config3 & CONFIG3_SMBALERT)) data->has_pwm2 = 1; /* Meaning of this bit is inverted for the ADT7473-1 */ - if (id->driver_data == adt7473 && revision >= 1) + if (chip == adt7473 && revision >= 1) data->has_pwm2 = !data->has_pwm2; data->config4 = adt7475_read(REG_CONFIG4); @@ -1730,12 +1851,12 @@ static int adt7475_probe(struct i2c_client *client) * because 2 different pins (TACH4 and +2.5 Vin) can be used for * this function */ - if (id->driver_data == adt7490) { + if (chip == adt7490) { if ((data->config4 & CONFIG4_PINFUNC) == 0x1 && !(config3 & CONFIG3_THERM)) data->has_fan4 = 1; } - if (id->driver_data == adt7476 || id->driver_data == adt7490) { + if (chip == adt7476 || chip == adt7490) { if (!(config3 & CONFIG3_THERM) || (data->config4 & CONFIG4_PINFUNC) == 0x1) data->has_voltage |= (1 << 0); /* in0 */ @@ -1745,7 +1866,7 @@ static int adt7475_probe(struct i2c_client *client) * On the ADT7476, the +12V input pin may instead be used as VID5, * and VID pins may alternatively be used as GPIO */ - if (id->driver_data == adt7476) { + if (chip == adt7476) { u8 vid = adt7475_read(REG_VID); if (!(vid & VID_VIDSEL)) data->has_voltage |= (1 << 4); /* in4 */ @@ -1778,6 +1899,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: @@ -1829,7 +1954,7 @@ static int adt7475_probe(struct i2c_client *client) } dev_info(&client->dev, "%s device, revision %d\n", - names[id->driver_data], revision); + names[chip], revision); if ((data->has_voltage & 0x11) || data->has_fan4 || data->has_pwm2) dev_info(&client->dev, "Optional features:%s%s%s%s%s\n", (data->has_voltage & (1 << 0)) ? " in0" : "", @@ -1900,7 +2025,7 @@ static void adt7475_read_pwm(struct i2c_client *client, int index) data->pwm[CONTROL][index] &= ~0xE0; data->pwm[CONTROL][index] |= (7 << 5); - i2c_smbus_write_byte_data(client, PWM_CONFIG_REG(index), + i2c_smbus_write_byte_data(client, PWM_REG(index), data->pwm[INPUT][index]); i2c_smbus_write_byte_data(client, PWM_CONFIG_REG(index), |