@@ -1939,6 +1939,7 @@ config SENSORS_TMP108
config SENSORS_TMP401
tristate "Texas Instruments TMP401 and compatibles"
depends on I2C
+ select REGMAP
help
If you say yes here you get support for Texas Instruments TMP401,
TMP411, TMP431, TMP432, and TMP435 temperature sensor chips.
@@ -25,6 +25,7 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/mutex.h>
+#include <linux/regmap.h>
#include <linux/slab.h>
/* Addresses to scan */
@@ -114,6 +115,7 @@ MODULE_DEVICE_TABLE(i2c, tmp401_id);
struct tmp401_data {
struct i2c_client *client;
+ struct regmap *regmap;
struct mutex update_lock;
enum chips kind;
@@ -128,32 +130,30 @@ struct tmp401_data {
struct hwmon_chip_info chip;
};
-static int tmp401_register_to_temp(u16 reg, bool extended)
-{
- int temp = reg;
-
- if (extended)
- temp -= 64 * 256;
+/* regmap */
- return DIV_ROUND_CLOSEST(temp * 125, 32);
-}
-
-static u16 tmp401_temp_to_register(long temp, bool extended, int zbits)
+static bool tmp401_regmap_is_volatile(struct device *dev, unsigned int reg)
{
- if (extended) {
- temp = clamp_val(temp, -64000, 191000);
- temp += 64000;
- } else {
- temp = clamp_val(temp, 0, 127000);
+ switch (reg) {
+ case 0: /* local temp msb */
+ case 1: /* remote temp msb */
+ case 2: /* status */
+ case 0x10: /* remote temp lsb */
+ case 0x15: /* local temp lsb */
+ case 0x1b: /* status (tmp432) */
+ case 0x23 ... 0x24: /* remote temp 2 msb / lsb */
+ case 0x30 ... 0x37: /* lowest/highest temp; status (tmp432) */
+ return true;
+ default:
+ return false;
}
-
- return DIV_ROUND_CLOSEST(temp * (1 << (8 - zbits)), 1000) << zbits;
}
-static int tmp401_reg_read(struct tmp401_data *data, unsigned int reg)
+static int tmp401_reg_read(void *context, unsigned int reg, unsigned int *val)
{
+ struct tmp401_data *data = context;
struct i2c_client *client = data->client;
- int val, regval;
+ int regval;
switch (reg) {
case 0: /* local temp msb */
@@ -172,55 +172,71 @@ static int tmp401_reg_read(struct tmp401_data *data, unsigned int reg)
/* work around register overlap between TMP411 and TMP432 */
if (reg == 0xf6)
reg = 0x36;
- return i2c_smbus_read_word_swapped(client, reg);
+ regval = i2c_smbus_read_word_swapped(client, reg);
+ if (regval < 0)
+ return regval;
+ *val = regval;
+ break;
case 0x19: /* critical limits, 8-bit registers */
case 0x1a:
case 0x20:
regval = i2c_smbus_read_byte_data(client, reg);
if (regval < 0)
return regval;
- return regval << 8;
+ *val = regval << 8;
+ break;
case 0x1b:
case 0x35 ... 0x37:
- if (data->kind == tmp432)
- return i2c_smbus_read_byte_data(client, reg);
+ if (data->kind == tmp432) {
+ regval = i2c_smbus_read_byte_data(client, reg);
+ if (regval < 0)
+ return regval;
+ *val = regval;
+ break;
+ }
/* simulate TMP432 status registers */
regval = i2c_smbus_read_byte_data(client, TMP401_STATUS);
if (regval < 0)
return regval;
- val = 0;
+ *val = 0;
switch (reg) {
case 0x1b: /* open / fault */
if (regval & TMP401_STATUS_REMOTE_OPEN)
- val |= BIT(1);
+ *val |= BIT(1);
break;
case 0x35: /* high limit */
if (regval & TMP401_STATUS_LOCAL_HIGH)
- val |= BIT(0);
+ *val |= BIT(0);
if (regval & TMP401_STATUS_REMOTE_HIGH)
- val |= BIT(1);
+ *val |= BIT(1);
break;
case 0x36: /* low limit */
if (regval & TMP401_STATUS_LOCAL_LOW)
- val |= BIT(0);
+ *val |= BIT(0);
if (regval & TMP401_STATUS_REMOTE_LOW)
- val |= BIT(1);
+ *val |= BIT(1);
break;
case 0x37: /* therm / crit limit */
if (regval & TMP401_STATUS_LOCAL_CRIT)
- val |= BIT(0);
+ *val |= BIT(0);
if (regval & TMP401_STATUS_REMOTE_CRIT)
- val |= BIT(1);
+ *val |= BIT(1);
break;
}
- return val;
+ break;
default:
- return i2c_smbus_read_byte_data(client, reg);
+ regval = i2c_smbus_read_byte_data(client, reg);
+ if (regval < 0)
+ return regval;
+ *val = regval;
+ break;
}
+ return 0;
}
-static int tmp401_reg_write(struct tmp401_data *data, unsigned int reg, unsigned int val)
+static int tmp401_reg_write(void *context, unsigned int reg, unsigned int val)
{
+ struct tmp401_data *data = context;
struct i2c_client *client = data->client;
switch (reg) {
@@ -240,6 +256,41 @@ static int tmp401_reg_write(struct tmp401_data *data, unsigned int reg, unsigned
}
}
+static const struct regmap_config tmp401_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .cache_type = REGCACHE_RBTREE,
+ .volatile_reg = tmp401_regmap_is_volatile,
+ .reg_read = tmp401_reg_read,
+ .reg_write = tmp401_reg_write,
+};
+
+/* temperature conversion */
+
+static int tmp401_register_to_temp(u16 reg, bool extended)
+{
+ int temp = reg;
+
+ if (extended)
+ temp -= 64 * 256;
+
+ return DIV_ROUND_CLOSEST(temp * 125, 32);
+}
+
+static u16 tmp401_temp_to_register(long temp, bool extended, int zbits)
+{
+ if (extended) {
+ temp = clamp_val(temp, -64000, 191000);
+ temp += 64000;
+ } else {
+ temp = clamp_val(temp, 0, 127000);
+ }
+
+ return DIV_ROUND_CLOSEST(temp * (1 << (8 - zbits)), 1000) << zbits;
+}
+
+/* hwmon API functions */
+
static const u8 tmp401_temp_reg_index[] = {
[hwmon_temp_input] = 0,
[hwmon_temp_min] = 1,
@@ -259,7 +310,9 @@ static const u8 tmp401_status_reg_index[] = {
static int tmp401_temp_read(struct device *dev, u32 attr, int channel, long *val)
{
struct tmp401_data *data = dev_get_drvdata(dev);
- int reg, regval;
+ struct regmap *regmap = data->regmap;
+ unsigned int regval;
+ int reg, ret;
switch (attr) {
case hwmon_temp_input:
@@ -269,36 +322,35 @@ static int tmp401_temp_read(struct device *dev, u32 attr, int channel, long *val
case hwmon_temp_lowest:
case hwmon_temp_highest:
reg = TMP401_TEMP_MSB_READ[tmp401_temp_reg_index[attr]][channel];
- regval = tmp401_reg_read(data, reg);
- if (regval < 0)
- return regval;
+ ret = regmap_read(regmap, reg, ®val);
+ if (ret < 0)
+ return ret;
*val = tmp401_register_to_temp(regval, data->extended_range);
break;
case hwmon_temp_crit_hyst:
mutex_lock(&data->update_lock);
reg = TMP401_TEMP_MSB_READ[3][channel];
- regval = tmp401_reg_read(data, reg);
- if (regval < 0)
+ ret = regmap_read(regmap, reg, ®val);
+ if (ret < 0)
goto unlock;
*val = tmp401_register_to_temp(regval, data->extended_range);
- regval = tmp401_reg_read(data, TMP401_TEMP_CRIT_HYST);
- if (regval < 0)
+ ret = regmap_read(regmap, TMP401_TEMP_CRIT_HYST, ®val);
+ if (ret < 0)
goto unlock;
*val -= regval * 1000;
- regval = 0;
unlock:
mutex_unlock(&data->update_lock);
- if (regval < 0)
- return regval;
+ if (ret < 0)
+ return ret;
break;
case hwmon_temp_fault:
case hwmon_temp_min_alarm:
case hwmon_temp_max_alarm:
case hwmon_temp_crit_alarm:
reg = TMP432_STATUS_REG[tmp401_status_reg_index[attr]];
- regval = tmp401_reg_read(data, reg);
- if (regval < 0)
- return regval;
+ ret = regmap_read(regmap, reg, ®val);
+ if (ret < 0)
+ return ret;
*val = !!(regval & BIT(channel));
break;
default:
@@ -311,7 +363,9 @@ static int tmp401_temp_write(struct device *dev, u32 attr, int channel,
long val)
{
struct tmp401_data *data = dev_get_drvdata(dev);
- int reg, regval, ret, temp;
+ struct regmap *regmap = data->regmap;
+ unsigned int regval;
+ int reg, ret, temp;
mutex_lock(&data->update_lock);
switch (attr) {
@@ -321,7 +375,14 @@ static int tmp401_temp_write(struct device *dev, u32 attr, int channel,
reg = TMP401_TEMP_MSB_WRITE[tmp401_temp_reg_index[attr]][channel];
regval = tmp401_temp_to_register(val, data->extended_range,
attr == hwmon_temp_crit ? 8 : 4);
- ret = tmp401_reg_write(data, reg, regval);
+ ret = regmap_write(regmap, reg, regval);
+ if (ret)
+ break;
+ /*
+ * Read and write limit registers are different, so we need to
+ * reinitialize the cache.
+ */
+ ret = regmap_reinit_cache(regmap, &tmp401_regmap_config);
break;
case hwmon_temp_crit_hyst:
if (data->extended_range)
@@ -330,13 +391,13 @@ static int tmp401_temp_write(struct device *dev, u32 attr, int channel,
val = clamp_val(val, 0, 127000);
reg = TMP401_TEMP_MSB_READ[3][channel];
- ret = tmp401_reg_read(data, reg);
+ ret = regmap_read(regmap, reg, ®val);
if (ret < 0)
break;
- temp = tmp401_register_to_temp(ret, data->extended_range);
+ temp = tmp401_register_to_temp(regval, data->extended_range);
val = clamp_val(val, temp - 255000, temp);
regval = ((temp - val) + 500) / 1000;
- ret = tmp401_reg_write(data, TMP401_TEMP_CRIT_HYST, regval);
+ ret = regmap_write(regmap, TMP401_TEMP_CRIT_HYST, regval);
break;
default:
ret = -EOPNOTSUPP;
@@ -349,13 +410,14 @@ static int tmp401_temp_write(struct device *dev, u32 attr, int channel,
static int tmp401_chip_read(struct device *dev, u32 attr, int channel, long *val)
{
struct tmp401_data *data = dev_get_drvdata(dev);
- int regval;
+ u32 regval;
+ int ret;
switch (attr) {
case hwmon_chip_update_interval:
- regval = tmp401_reg_read(data, TMP401_CONVERSION_RATE_READ);
- if (regval < 0)
- return regval;
+ ret = regmap_read(data->regmap, TMP401_CONVERSION_RATE_READ, ®val);
+ if (ret < 0)
+ return ret;
*val = (1 << (7 - regval)) * 125;
break;
case hwmon_chip_temp_reset_history:
@@ -368,7 +430,7 @@ static int tmp401_chip_read(struct device *dev, u32 attr, int channel, long *val
return 0;
}
-static int tmp401_set_convrate(struct i2c_client *client, struct tmp401_data *data, long val)
+static int tmp401_set_convrate(struct regmap *regmap, long val)
{
int err, rate;
@@ -382,22 +444,26 @@ static int tmp401_set_convrate(struct i2c_client *client, struct tmp401_data *da
*/
val = clamp_val(val, 125, 16000);
rate = 7 - __fls(val * 4 / (125 * 3));
- err = i2c_smbus_write_byte_data(client, TMP401_CONVERSION_RATE_WRITE, rate);
+ err = regmap_write(regmap, TMP401_CONVERSION_RATE_WRITE, rate);
if (err)
return err;
- return 0;
+ /*
+ * Read and write conversion rate registers are different, so we need to
+ * reinitialize the cache.
+ */
+ return regmap_reinit_cache(regmap, &tmp401_regmap_config);
}
static int tmp401_chip_write(struct device *dev, u32 attr, int channel, long val)
{
struct tmp401_data *data = dev_get_drvdata(dev);
- struct i2c_client *client = data->client;
+ struct regmap *regmap = data->regmap;
int err;
mutex_lock(&data->update_lock);
switch (attr) {
case hwmon_chip_update_interval:
- err = tmp401_set_convrate(client, data, val);
+ err = tmp401_set_convrate(regmap, val);
break;
case hwmon_chip_temp_reset_history:
if (val != 1) {
@@ -408,7 +474,7 @@ static int tmp401_chip_write(struct device *dev, u32 attr, int channel, long val
* Reset history by writing any value to any of the
* minimum/maximum registers (0x30-0x37).
*/
- err = i2c_smbus_write_byte_data(client, 0x30, 0);
+ err = regmap_write(regmap, 0x30, 0);
break;
default:
err = -EOPNOTSUPP;
@@ -488,18 +554,23 @@ static const struct hwmon_ops tmp401_ops = {
.write = tmp401_write,
};
-static int tmp401_init_client(struct tmp401_data *data,
- struct i2c_client *client)
+/* chip initialization, detect, probe */
+
+static int tmp401_init_client(struct tmp401_data *data)
{
- int config, config_orig, status = 0;
+ struct regmap *regmap = data->regmap;
+ u32 config, config_orig;
+ int ret;
- /* Set the conversion rate to 2 Hz */
- i2c_smbus_write_byte_data(client, TMP401_CONVERSION_RATE_WRITE, 5);
+ /* Set conversion rate to 2 Hz */
+ ret = regmap_write(regmap, TMP401_CONVERSION_RATE_WRITE, 5);
+ if (ret < 0)
+ return ret;
/* Start conversions (disable shutdown if necessary) */
- config = i2c_smbus_read_byte_data(client, TMP401_CONFIG_READ);
- if (config < 0)
- return config;
+ ret = regmap_read(regmap, TMP401_CONFIG_READ, &config);
+ if (ret < 0)
+ return ret;
config_orig = config;
config &= ~TMP401_CONFIG_SHUTDOWN;
@@ -507,11 +578,9 @@ static int tmp401_init_client(struct tmp401_data *data,
data->extended_range = !!(config & TMP401_CONFIG_RANGE);
if (config != config_orig)
- status = i2c_smbus_write_byte_data(client,
- TMP401_CONFIG_WRITE,
- config);
+ ret = regmap_write(regmap, TMP401_CONFIG_WRITE, config);
- return status;
+ return ret;
}
static int tmp401_detect(struct i2c_client *client,
@@ -602,6 +671,10 @@ static int tmp401_probe(struct i2c_client *client)
mutex_init(&data->update_lock);
data->kind = i2c_match_id(tmp401_id, client)->driver_data;
+ data->regmap = devm_regmap_init(dev, NULL, data, &tmp401_regmap_config);
+ if (IS_ERR(data->regmap))
+ return PTR_ERR(data->regmap);
+
/* initialize configuration data */
data->chip.ops = &tmp401_ops;
data->chip.info = data->info;
@@ -639,7 +712,7 @@ static int tmp401_probe(struct i2c_client *client)
}
/* Initialize the TMP401 chip */
- status = tmp401_init_client(data, client);
+ status = tmp401_init_client(data);
if (status < 0)
return status;
Use regmap for register accesses to be able to utilize its caching functionality. This also lets us hide register access differences in regmap code. Signed-off-by: Guenter Roeck <linux@roeck-us.net> --- drivers/hwmon/Kconfig | 1 + drivers/hwmon/tmp401.c | 225 +++++++++++++++++++++++++++-------------- 2 files changed, 150 insertions(+), 76 deletions(-)