|  | // SPDX-License-Identifier: GPL-2.0 | 
|  | /* | 
|  | * max31827.c - Support for Maxim Low-Power Switch | 
|  | * | 
|  | * Copyright (c) 2023 Daniel Matyas <daniel.matyas@analog.com> | 
|  | */ | 
|  |  | 
|  | #include <linux/bitfield.h> | 
|  | #include <linux/bitops.h> | 
|  | #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 | 
|  | #define MAX31827_TH_REG			0x4 | 
|  | #define MAX31827_TL_REG			0x6 | 
|  | #define MAX31827_TH_HYST_REG		0x8 | 
|  | #define MAX31827_TL_HYST_REG		0xA | 
|  |  | 
|  | #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_ALRM_POL_LOW	0x0 | 
|  | #define MAX31827_ALRM_POL_HIGH	0x1 | 
|  | #define MAX31827_FLT_Q_1	0x0 | 
|  | #define MAX31827_FLT_Q_4	0x2 | 
|  |  | 
|  | #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 = { | 
|  | .reg_bits = 8, | 
|  | .val_bits = 16, | 
|  | .max_register = 0xA, | 
|  | }; | 
|  |  | 
|  | static int shutdown_write(struct max31827_state *st, unsigned int reg, | 
|  | unsigned int mask, unsigned int val) | 
|  | { | 
|  | unsigned int cfg; | 
|  | unsigned int cnv_rate; | 
|  | int ret; | 
|  |  | 
|  | /* | 
|  | * 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 is used to ensure, that some other process doesn't change the | 
|  | * configuration register. | 
|  | */ | 
|  | mutex_lock(&st->lock); | 
|  |  | 
|  | if (!st->enable) { | 
|  | if (!mask) | 
|  | ret = regmap_write(st->regmap, reg, val); | 
|  | else | 
|  | ret = regmap_update_bits(st->regmap, reg, mask, val); | 
|  | goto unlock; | 
|  | } | 
|  |  | 
|  | ret = regmap_read(st->regmap, MAX31827_CONFIGURATION_REG, &cfg); | 
|  | if (ret) | 
|  | goto unlock; | 
|  |  | 
|  | 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, cfg); | 
|  | if (ret) | 
|  | goto unlock; | 
|  |  | 
|  | if (!mask) | 
|  | ret = regmap_write(st->regmap, reg, val); | 
|  | else | 
|  | ret = regmap_update_bits(st->regmap, reg, mask, val); | 
|  |  | 
|  | if (ret) | 
|  | goto unlock; | 
|  |  | 
|  | ret = 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, | 
|  | enum hwmon_sensor_types type, u32 attr, | 
|  | int channel) | 
|  | { | 
|  | if (type == hwmon_temp) { | 
|  | switch (attr) { | 
|  | case hwmon_temp_enable: | 
|  | case hwmon_temp_max: | 
|  | case hwmon_temp_min: | 
|  | case hwmon_temp_max_hyst: | 
|  | case hwmon_temp_min_hyst: | 
|  | return 0644; | 
|  | case hwmon_temp_input: | 
|  | case hwmon_temp_min_alarm: | 
|  | case hwmon_temp_max_alarm: | 
|  | return 0444; | 
|  | default: | 
|  | return 0; | 
|  | } | 
|  | } else if (type == hwmon_chip) { | 
|  | if (attr == hwmon_chip_update_interval) | 
|  | return 0644; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int max31827_read(struct device *dev, enum hwmon_sensor_types type, | 
|  | u32 attr, int channel, long *val) | 
|  | { | 
|  | struct max31827_state *st = dev_get_drvdata(dev); | 
|  | unsigned int uval; | 
|  | int ret = 0; | 
|  |  | 
|  | switch (type) { | 
|  | case hwmon_temp: | 
|  | switch (attr) { | 
|  | case hwmon_temp_enable: | 
|  | ret = regmap_read(st->regmap, | 
|  | MAX31827_CONFIGURATION_REG, &uval); | 
|  | if (ret) | 
|  | break; | 
|  |  | 
|  | uval = FIELD_GET(MAX31827_CONFIGURATION_1SHOT_MASK | | 
|  | MAX31827_CONFIGURATION_CNV_RATE_MASK, | 
|  | uval); | 
|  | *val = !!uval; | 
|  |  | 
|  | 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); | 
|  | return ret; | 
|  | } | 
|  | msleep(max31827_conv_times[st->resolution]); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * 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); | 
|  |  | 
|  | mutex_unlock(&st->lock); | 
|  |  | 
|  | if (ret) | 
|  | break; | 
|  |  | 
|  | *val = MAX31827_16_BIT_TO_M_DGR(uval); | 
|  |  | 
|  | break; | 
|  | case hwmon_temp_max: | 
|  | ret = regmap_read(st->regmap, MAX31827_TH_REG, &uval); | 
|  | if (ret) | 
|  | break; | 
|  |  | 
|  | *val = MAX31827_16_BIT_TO_M_DGR(uval); | 
|  | break; | 
|  | case hwmon_temp_max_hyst: | 
|  | ret = regmap_read(st->regmap, MAX31827_TH_HYST_REG, | 
|  | &uval); | 
|  | if (ret) | 
|  | break; | 
|  |  | 
|  | *val = MAX31827_16_BIT_TO_M_DGR(uval); | 
|  | break; | 
|  | case hwmon_temp_max_alarm: | 
|  | ret = regmap_read(st->regmap, | 
|  | MAX31827_CONFIGURATION_REG, &uval); | 
|  | if (ret) | 
|  | break; | 
|  |  | 
|  | *val = FIELD_GET(MAX31827_CONFIGURATION_O_TEMP_STAT_MASK, | 
|  | uval); | 
|  | break; | 
|  | case hwmon_temp_min: | 
|  | ret = regmap_read(st->regmap, MAX31827_TL_REG, &uval); | 
|  | if (ret) | 
|  | break; | 
|  |  | 
|  | *val = MAX31827_16_BIT_TO_M_DGR(uval); | 
|  | break; | 
|  | case hwmon_temp_min_hyst: | 
|  | ret = regmap_read(st->regmap, MAX31827_TL_HYST_REG, | 
|  | &uval); | 
|  | if (ret) | 
|  | break; | 
|  |  | 
|  | *val = MAX31827_16_BIT_TO_M_DGR(uval); | 
|  | break; | 
|  | case hwmon_temp_min_alarm: | 
|  | ret = regmap_read(st->regmap, | 
|  | MAX31827_CONFIGURATION_REG, &uval); | 
|  | if (ret) | 
|  | break; | 
|  |  | 
|  | *val = FIELD_GET(MAX31827_CONFIGURATION_U_TEMP_STAT_MASK, | 
|  | uval); | 
|  | break; | 
|  | default: | 
|  | ret = -EOPNOTSUPP; | 
|  | break; | 
|  | } | 
|  |  | 
|  | break; | 
|  |  | 
|  | case hwmon_chip: | 
|  | if (attr == hwmon_chip_update_interval) { | 
|  | ret = regmap_read(st->regmap, | 
|  | MAX31827_CONFIGURATION_REG, &uval); | 
|  | if (ret) | 
|  | break; | 
|  |  | 
|  | uval = FIELD_GET(MAX31827_CONFIGURATION_CNV_RATE_MASK, | 
|  | uval); | 
|  | *val = max31827_conversions[uval]; | 
|  | } | 
|  | break; | 
|  |  | 
|  | default: | 
|  | ret = -EOPNOTSUPP; | 
|  | break; | 
|  | } | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | 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) { | 
|  | case hwmon_temp: | 
|  | switch (attr) { | 
|  | case hwmon_temp_enable: | 
|  | 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 | 
|  | * the alarm values are changed. | 
|  | */ | 
|  |  | 
|  | 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; | 
|  |  | 
|  | case hwmon_temp_max: | 
|  | return write_alarm_val(st, MAX31827_TH_REG, val); | 
|  |  | 
|  | case hwmon_temp_max_hyst: | 
|  | return write_alarm_val(st, MAX31827_TH_HYST_REG, val); | 
|  |  | 
|  | case hwmon_temp_min: | 
|  | return write_alarm_val(st, MAX31827_TL_REG, val); | 
|  |  | 
|  | case hwmon_temp_min_hyst: | 
|  | return write_alarm_val(st, MAX31827_TL_HYST_REG, val); | 
|  |  | 
|  | default: | 
|  | return -EOPNOTSUPP; | 
|  | } | 
|  |  | 
|  | case hwmon_chip: | 
|  | switch (attr) { | 
|  | case hwmon_chip_update_interval: | 
|  | if (!st->enable) | 
|  | 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++; | 
|  |  | 
|  | if (res == ARRAY_SIZE(max31827_conversions)) | 
|  | res = ARRAY_SIZE(max31827_conversions) - 1; | 
|  |  | 
|  | 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; | 
|  |  | 
|  | val = FIELD_GET(MAX31827_CONFIGURATION_RESOLUTION_MASK, val); | 
|  |  | 
|  | return scnprintf(buf, PAGE_SIZE, "%u\n", max31827_resolutions[val]); | 
|  | } | 
|  |  | 
|  | 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); | 
|  |  | 
|  | 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[] = { | 
|  | HWMON_CHANNEL_INFO(temp, HWMON_T_ENABLE | HWMON_T_INPUT | HWMON_T_MIN | | 
|  | 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_C_PEC), | 
|  | NULL, | 
|  | }; | 
|  |  | 
|  | static const struct hwmon_ops max31827_hwmon_ops = { | 
|  | .is_visible = max31827_is_visible, | 
|  | .read = max31827_read, | 
|  | .write = max31827_write, | 
|  | }; | 
|  |  | 
|  | static const struct hwmon_chip_info max31827_chip_info = { | 
|  | .ops = &max31827_hwmon_ops, | 
|  | .info = max31827_info, | 
|  | }; | 
|  |  | 
|  | static int max31827_probe(struct i2c_client *client) | 
|  | { | 
|  | struct device *dev = &client->dev; | 
|  | struct device *hwmon_dev; | 
|  | struct max31827_state *st; | 
|  | int err; | 
|  |  | 
|  | if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) | 
|  | return -EOPNOTSUPP; | 
|  |  | 
|  | st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL); | 
|  | 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 = 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, | 
|  | max31827_groups); | 
|  |  | 
|  | return PTR_ERR_OR_ZERO(hwmon_dev); | 
|  | } | 
|  |  | 
|  | static const struct of_device_id max31827_of_match[] = { | 
|  | { | 
|  | .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 = { | 
|  | .driver = { | 
|  | .name = "max31827", | 
|  | .of_match_table = max31827_of_match, | 
|  | }, | 
|  | .probe = max31827_probe, | 
|  | .id_table = max31827_i2c_ids, | 
|  | }; | 
|  | module_i2c_driver(max31827_driver); | 
|  |  | 
|  | MODULE_AUTHOR("Daniel Matyas <daniel.matyas@analog.com>"); | 
|  | MODULE_DESCRIPTION("Maxim MAX31827 low-power temperature switch driver"); | 
|  | MODULE_LICENSE("GPL"); |