diff options
Diffstat (limited to 'drivers/hwmon/max31827.c')
| -rw-r--r-- | drivers/hwmon/max31827.c | 457 |
1 files changed, 318 insertions, 139 deletions
diff --git a/drivers/hwmon/max31827.c b/drivers/hwmon/max31827.c index 602f4e4f81ff..9b2e56c040df 100644 --- a/drivers/hwmon/max31827.c +++ b/drivers/hwmon/max31827.c @@ -10,8 +10,9 @@ #include <linux/delay.h> #include <linux/hwmon.h> #include <linux/i2c.h> -#include <linux/mutex.h> +#include <linux/of_device.h> #include <linux/regmap.h> +#include <linux/regulator/consumer.h> #define MAX31827_T_REG 0x0 #define MAX31827_CONFIGURATION_REG 0x2 @@ -22,30 +23,85 @@ #define MAX31827_CONFIGURATION_1SHOT_MASK BIT(0) #define MAX31827_CONFIGURATION_CNV_RATE_MASK GENMASK(3, 1) +#define MAX31827_CONFIGURATION_PEC_EN_MASK BIT(4) +#define MAX31827_CONFIGURATION_TIMEOUT_MASK BIT(5) +#define MAX31827_CONFIGURATION_RESOLUTION_MASK GENMASK(7, 6) +#define MAX31827_CONFIGURATION_ALRM_POL_MASK BIT(8) +#define MAX31827_CONFIGURATION_COMP_INT_MASK BIT(9) +#define MAX31827_CONFIGURATION_FLT_Q_MASK GENMASK(11, 10) #define MAX31827_CONFIGURATION_U_TEMP_STAT_MASK BIT(14) #define MAX31827_CONFIGURATION_O_TEMP_STAT_MASK BIT(15) -#define MAX31827_12_BIT_CNV_TIME 141 +#define MAX31827_ALRM_POL_LOW 0x0 +#define MAX31827_ALRM_POL_HIGH 0x1 +#define MAX31827_FLT_Q_1 0x0 +#define MAX31827_FLT_Q_4 0x2 -#define MAX31827_CNV_1_DIV_64_HZ 0x1 -#define MAX31827_CNV_1_DIV_32_HZ 0x2 -#define MAX31827_CNV_1_DIV_16_HZ 0x3 -#define MAX31827_CNV_1_DIV_4_HZ 0x4 -#define MAX31827_CNV_1_HZ 0x5 -#define MAX31827_CNV_4_HZ 0x6 -#define MAX31827_CNV_8_HZ 0x7 +#define MAX31827_8_BIT_CNV_TIME 9 +#define MAX31827_9_BIT_CNV_TIME 18 +#define MAX31827_10_BIT_CNV_TIME 35 +#define MAX31827_12_BIT_CNV_TIME 140 #define MAX31827_16_BIT_TO_M_DGR(x) (sign_extend32(x, 15) * 1000 / 16) #define MAX31827_M_DGR_TO_16_BIT(x) (((x) << 4) / 1000) #define MAX31827_DEVICE_ENABLE(x) ((x) ? 0xA : 0x0) +/* + * The enum passed in the .data pointer of struct of_device_id must + * start with a value != 0 since that is a requirement for using + * device_get_match_data(). + */ +enum chips { max31827 = 1, max31828, max31829 }; + +enum max31827_cnv { + MAX31827_CNV_1_DIV_64_HZ = 1, + MAX31827_CNV_1_DIV_32_HZ, + MAX31827_CNV_1_DIV_16_HZ, + MAX31827_CNV_1_DIV_4_HZ, + MAX31827_CNV_1_HZ, + MAX31827_CNV_4_HZ, + MAX31827_CNV_8_HZ, +}; + +static const u16 max31827_conversions[] = { + [MAX31827_CNV_1_DIV_64_HZ] = 64000, + [MAX31827_CNV_1_DIV_32_HZ] = 32000, + [MAX31827_CNV_1_DIV_16_HZ] = 16000, + [MAX31827_CNV_1_DIV_4_HZ] = 4000, + [MAX31827_CNV_1_HZ] = 1000, + [MAX31827_CNV_4_HZ] = 250, + [MAX31827_CNV_8_HZ] = 125, +}; + +enum max31827_resolution { + MAX31827_RES_8_BIT = 0, + MAX31827_RES_9_BIT, + MAX31827_RES_10_BIT, + MAX31827_RES_12_BIT, +}; + +static const u16 max31827_resolutions[] = { + [MAX31827_RES_8_BIT] = 1000, + [MAX31827_RES_9_BIT] = 500, + [MAX31827_RES_10_BIT] = 250, + [MAX31827_RES_12_BIT] = 62, +}; + +static const u16 max31827_conv_times[] = { + [MAX31827_RES_8_BIT] = MAX31827_8_BIT_CNV_TIME, + [MAX31827_RES_9_BIT] = MAX31827_9_BIT_CNV_TIME, + [MAX31827_RES_10_BIT] = MAX31827_10_BIT_CNV_TIME, + [MAX31827_RES_12_BIT] = MAX31827_12_BIT_CNV_TIME, +}; + struct max31827_state { /* * Prevent simultaneous access to the i2c client. */ - struct mutex lock; struct regmap *regmap; bool enable; + unsigned int resolution; + unsigned int update_interval; }; static const struct regmap_config max31827_regmap = { @@ -54,49 +110,54 @@ static const struct regmap_config max31827_regmap = { .max_register = 0xA, }; -static int write_alarm_val(struct max31827_state *st, unsigned int reg, - long val) +static int shutdown_write(struct max31827_state *st, unsigned int reg, + unsigned int mask, unsigned int val) { unsigned int cfg; - unsigned int tmp; + unsigned int cnv_rate; int ret; - val = MAX31827_M_DGR_TO_16_BIT(val); - /* - * Before the Temperature Threshold Alarm and Alarm Hysteresis Threshold - * register values are changed over I2C, the part must be in shutdown - * mode. - * - * Mutex is used to ensure, that some other process doesn't change the - * configuration register. + * Before the Temperature Threshold Alarm, Alarm Hysteresis Threshold + * and Resolution bits from Configuration register are changed over I2C, + * the part must be in shutdown mode. */ - mutex_lock(&st->lock); - if (!st->enable) { - ret = regmap_write(st->regmap, reg, val); - goto unlock; + if (!mask) + return regmap_write(st->regmap, reg, val); + return regmap_update_bits(st->regmap, reg, mask, val); } ret = regmap_read(st->regmap, MAX31827_CONFIGURATION_REG, &cfg); if (ret) - goto unlock; + return ret; - tmp = cfg & ~(MAX31827_CONFIGURATION_1SHOT_MASK | + cnv_rate = MAX31827_CONFIGURATION_CNV_RATE_MASK & cfg; + cfg = cfg & ~(MAX31827_CONFIGURATION_1SHOT_MASK | MAX31827_CONFIGURATION_CNV_RATE_MASK); - ret = regmap_write(st->regmap, MAX31827_CONFIGURATION_REG, tmp); + ret = regmap_write(st->regmap, MAX31827_CONFIGURATION_REG, cfg); if (ret) - goto unlock; + return ret; + + if (!mask) + ret = regmap_write(st->regmap, reg, val); + else + ret = regmap_update_bits(st->regmap, reg, mask, val); - ret = regmap_write(st->regmap, reg, val); if (ret) - goto unlock; + return ret; - ret = regmap_write(st->regmap, MAX31827_CONFIGURATION_REG, cfg); + return regmap_update_bits(st->regmap, MAX31827_CONFIGURATION_REG, + MAX31827_CONFIGURATION_CNV_RATE_MASK, + cnv_rate); +} -unlock: - mutex_unlock(&st->lock); - return ret; +static int write_alarm_val(struct max31827_state *st, unsigned int reg, + long val) +{ + val = MAX31827_M_DGR_TO_16_BIT(val); + + return shutdown_write(st, reg, 0, val); } static umode_t max31827_is_visible(const void *state, @@ -149,29 +210,26 @@ static int max31827_read(struct device *dev, enum hwmon_sensor_types type, break; case hwmon_temp_input: - mutex_lock(&st->lock); - if (!st->enable) { - /* - * This operation requires mutex protection, - * because the chip configuration should not - * be changed during the conversion process. - */ - ret = regmap_update_bits(st->regmap, MAX31827_CONFIGURATION_REG, MAX31827_CONFIGURATION_1SHOT_MASK, 1); - if (ret) { - mutex_unlock(&st->lock); + if (ret) return ret; - } - - msleep(MAX31827_12_BIT_CNV_TIME); + msleep(max31827_conv_times[st->resolution]); } - ret = regmap_read(st->regmap, MAX31827_T_REG, &uval); - mutex_unlock(&st->lock); + /* + * For 12-bit resolution the conversion time is 140 ms, + * thus an additional 15 ms is needed to complete the + * conversion: 125 ms + 15 ms = 140 ms + */ + if (max31827_resolutions[st->resolution] == 12 && + st->update_interval == 125) + usleep_range(15000, 20000); + + ret = regmap_read(st->regmap, MAX31827_T_REG, &uval); if (ret) break; @@ -243,32 +301,7 @@ static int max31827_read(struct device *dev, enum hwmon_sensor_types type, uval = FIELD_GET(MAX31827_CONFIGURATION_CNV_RATE_MASK, uval); - switch (uval) { - case MAX31827_CNV_1_DIV_64_HZ: - *val = 64000; - break; - case MAX31827_CNV_1_DIV_32_HZ: - *val = 32000; - break; - case MAX31827_CNV_1_DIV_16_HZ: - *val = 16000; - break; - case MAX31827_CNV_1_DIV_4_HZ: - *val = 4000; - break; - case MAX31827_CNV_1_HZ: - *val = 1000; - break; - case MAX31827_CNV_4_HZ: - *val = 250; - break; - case MAX31827_CNV_8_HZ: - *val = 125; - break; - default: - *val = 0; - break; - } + *val = max31827_conversions[uval]; } break; @@ -284,6 +317,7 @@ static int max31827_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long val) { struct max31827_state *st = dev_get_drvdata(dev); + int res = 1; int ret; switch (type) { @@ -293,7 +327,6 @@ static int max31827_write(struct device *dev, enum hwmon_sensor_types type, if (val >> 1) return -EINVAL; - mutex_lock(&st->lock); /** * The chip should not be enabled while a conversion is * performed. Neither should the chip be enabled when @@ -302,15 +335,11 @@ static int max31827_write(struct device *dev, enum hwmon_sensor_types type, st->enable = val; - ret = regmap_update_bits(st->regmap, - MAX31827_CONFIGURATION_REG, - MAX31827_CONFIGURATION_1SHOT_MASK | - MAX31827_CONFIGURATION_CNV_RATE_MASK, - MAX31827_DEVICE_ENABLE(val)); - - mutex_unlock(&st->lock); - - return ret; + return regmap_update_bits(st->regmap, + MAX31827_CONFIGURATION_REG, + MAX31827_CONFIGURATION_1SHOT_MASK | + MAX31827_CONFIGURATION_CNV_RATE_MASK, + MAX31827_DEVICE_ENABLE(val)); case hwmon_temp_max: return write_alarm_val(st, MAX31827_TH_REG, val); @@ -329,61 +358,205 @@ static int max31827_write(struct device *dev, enum hwmon_sensor_types type, } case hwmon_chip: - if (attr == hwmon_chip_update_interval) { + switch (attr) { + case hwmon_chip_update_interval: if (!st->enable) return -EINVAL; - switch (val) { - case 125: - val = MAX31827_CNV_8_HZ; - break; - case 250: - val = MAX31827_CNV_4_HZ; - break; - case 1000: - val = MAX31827_CNV_1_HZ; - break; - case 4000: - val = MAX31827_CNV_1_DIV_4_HZ; - break; - case 16000: - val = MAX31827_CNV_1_DIV_16_HZ; - break; - case 32000: - val = MAX31827_CNV_1_DIV_32_HZ; - break; - case 64000: - val = MAX31827_CNV_1_DIV_64_HZ; - break; - default: - return -EINVAL; - } + /* + * Convert the desired conversion rate into register + * bits. res is already initialized with 1. + * + * This was inspired by lm73 driver. + */ + while (res < ARRAY_SIZE(max31827_conversions) && + val < max31827_conversions[res]) + res++; - val = FIELD_PREP(MAX31827_CONFIGURATION_CNV_RATE_MASK, - val); + if (res == ARRAY_SIZE(max31827_conversions)) + res = ARRAY_SIZE(max31827_conversions) - 1; - return regmap_update_bits(st->regmap, - MAX31827_CONFIGURATION_REG, - MAX31827_CONFIGURATION_CNV_RATE_MASK, - val); - } - break; + res = FIELD_PREP(MAX31827_CONFIGURATION_CNV_RATE_MASK, + res); + + ret = regmap_update_bits(st->regmap, + MAX31827_CONFIGURATION_REG, + MAX31827_CONFIGURATION_CNV_RATE_MASK, + res); + if (ret) + return ret; + + st->update_interval = val; + return 0; + case hwmon_chip_pec: + return regmap_update_bits(st->regmap, MAX31827_CONFIGURATION_REG, + MAX31827_CONFIGURATION_PEC_EN_MASK, + val ? MAX31827_CONFIGURATION_PEC_EN_MASK : 0); + default: + return -EOPNOTSUPP; + } default: return -EOPNOTSUPP; } +} + +static ssize_t temp1_resolution_show(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct max31827_state *st = dev_get_drvdata(dev); + unsigned int val; + int ret; + + ret = regmap_read(st->regmap, MAX31827_CONFIGURATION_REG, &val); + if (ret) + return ret; - return -EOPNOTSUPP; + val = FIELD_GET(MAX31827_CONFIGURATION_RESOLUTION_MASK, val); + + return sysfs_emit(buf, "%u\n", max31827_resolutions[val]); } -static int max31827_init_client(struct max31827_state *st) +static ssize_t temp1_resolution_store(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) { + struct max31827_state *st = dev_get_drvdata(dev); + unsigned int idx = 0; + unsigned int val; + int ret; + + ret = kstrtouint(buf, 10, &val); + if (ret) + return ret; + + /* + * Convert the desired resolution into register + * bits. idx is already initialized with 0. + * + * This was inspired by lm73 driver. + */ + while (idx < ARRAY_SIZE(max31827_resolutions) && + val < max31827_resolutions[idx]) + idx++; + + if (idx == ARRAY_SIZE(max31827_resolutions)) + idx = ARRAY_SIZE(max31827_resolutions) - 1; + + st->resolution = idx; + + ret = shutdown_write(st, MAX31827_CONFIGURATION_REG, + MAX31827_CONFIGURATION_RESOLUTION_MASK, + FIELD_PREP(MAX31827_CONFIGURATION_RESOLUTION_MASK, + idx)); + + return ret ? ret : count; +} + +static DEVICE_ATTR_RW(temp1_resolution); + +static struct attribute *max31827_attrs[] = { + &dev_attr_temp1_resolution.attr, + NULL +}; +ATTRIBUTE_GROUPS(max31827); + +static const struct i2c_device_id max31827_i2c_ids[] = { + { "max31827", max31827 }, + { "max31828", max31828 }, + { "max31829", max31829 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max31827_i2c_ids); + +static int max31827_init_client(struct max31827_state *st, + struct device *dev) +{ + struct fwnode_handle *fwnode; + unsigned int res = 0; + u32 data, lsb_idx; + enum chips type; + bool prop; + int ret; + + fwnode = dev_fwnode(dev); + st->enable = true; + res |= MAX31827_DEVICE_ENABLE(1); - return regmap_update_bits(st->regmap, MAX31827_CONFIGURATION_REG, - MAX31827_CONFIGURATION_1SHOT_MASK | - MAX31827_CONFIGURATION_CNV_RATE_MASK, - MAX31827_DEVICE_ENABLE(1)); + res |= MAX31827_CONFIGURATION_RESOLUTION_MASK; + + prop = fwnode_property_read_bool(fwnode, "adi,comp-int"); + res |= FIELD_PREP(MAX31827_CONFIGURATION_COMP_INT_MASK, prop); + + prop = fwnode_property_read_bool(fwnode, "adi,timeout-enable"); + res |= FIELD_PREP(MAX31827_CONFIGURATION_TIMEOUT_MASK, !prop); + + type = (enum chips)(uintptr_t)device_get_match_data(dev); + + if (fwnode_property_present(fwnode, "adi,alarm-pol")) { + ret = fwnode_property_read_u32(fwnode, "adi,alarm-pol", &data); + if (ret) + return ret; + + res |= FIELD_PREP(MAX31827_CONFIGURATION_ALRM_POL_MASK, !!data); + } else { + /* + * Set default value. + */ + switch (type) { + case max31827: + case max31828: + res |= FIELD_PREP(MAX31827_CONFIGURATION_ALRM_POL_MASK, + MAX31827_ALRM_POL_LOW); + break; + case max31829: + res |= FIELD_PREP(MAX31827_CONFIGURATION_ALRM_POL_MASK, + MAX31827_ALRM_POL_HIGH); + break; + default: + return -EOPNOTSUPP; + } + } + + if (fwnode_property_present(fwnode, "adi,fault-q")) { + ret = fwnode_property_read_u32(fwnode, "adi,fault-q", &data); + if (ret) + return ret; + + /* + * Convert the desired fault queue into register bits. + */ + if (data != 0) + lsb_idx = __ffs(data); + + if (hweight32(data) != 1 || lsb_idx > 4) { + dev_err(dev, "Invalid data in adi,fault-q\n"); + return -EINVAL; + } + + res |= FIELD_PREP(MAX31827_CONFIGURATION_FLT_Q_MASK, lsb_idx); + } else { + /* + * Set default value. + */ + switch (type) { + case max31827: + res |= FIELD_PREP(MAX31827_CONFIGURATION_FLT_Q_MASK, + MAX31827_FLT_Q_1); + break; + case max31828: + case max31829: + res |= FIELD_PREP(MAX31827_CONFIGURATION_FLT_Q_MASK, + MAX31827_FLT_Q_4); + break; + default: + return -EOPNOTSUPP; + } + } + + return regmap_write(st->regmap, MAX31827_CONFIGURATION_REG, res); } static const struct hwmon_channel_info *max31827_info[] = { @@ -391,7 +564,7 @@ static const struct hwmon_channel_info *max31827_info[] = { HWMON_T_MIN_HYST | HWMON_T_MIN_ALARM | HWMON_T_MAX | HWMON_T_MAX_HYST | HWMON_T_MAX_ALARM), - HWMON_CHANNEL_INFO(chip, HWMON_C_UPDATE_INTERVAL), + HWMON_CHANNEL_INFO(chip, HWMON_C_UPDATE_INTERVAL | HWMON_C_PEC), NULL, }; @@ -420,38 +593,44 @@ static int max31827_probe(struct i2c_client *client) if (!st) return -ENOMEM; - mutex_init(&st->lock); - st->regmap = devm_regmap_init_i2c(client, &max31827_regmap); if (IS_ERR(st->regmap)) return dev_err_probe(dev, PTR_ERR(st->regmap), "Failed to allocate regmap.\n"); - err = max31827_init_client(st); + err = devm_regulator_get_enable(dev, "vref"); + if (err) + return dev_err_probe(dev, err, "failed to enable regulator\n"); + + err = max31827_init_client(st, dev); if (err) return err; hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, st, &max31827_chip_info, - NULL); + max31827_groups); return PTR_ERR_OR_ZERO(hwmon_dev); } -static const struct i2c_device_id max31827_i2c_ids[] = { - { "max31827", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, max31827_i2c_ids); - static const struct of_device_id max31827_of_match[] = { - { .compatible = "adi,max31827" }, + { + .compatible = "adi,max31827", + .data = (void *)max31827 + }, + { + .compatible = "adi,max31828", + .data = (void *)max31828 + }, + { + .compatible = "adi,max31829", + .data = (void *)max31829 + }, { } }; MODULE_DEVICE_TABLE(of, max31827_of_match); static struct i2c_driver max31827_driver = { - .class = I2C_CLASS_HWMON, .driver = { .name = "max31827", .of_match_table = max31827_of_match, |
