diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c index e01feba909c3..ffbd60f52619 100644 --- a/drivers/hwmon/ina2xx.c +++ b/drivers/hwmon/ina2xx.c @@ -35,6 +35,7 @@ #include #include #include +#include #include @@ -64,6 +65,9 @@ /* worst case is 68.10 ms (~14.6Hz, ina219) */ #define INA2XX_CONVERSION_RATE 15 +#define INA2XX_MAX_DELAY 69 /* worst case delay in ms */ + +#define INA2XX_RSHUNT_DEFAULT 10000 enum ina2xx_ids { ina219, ina226 }; @@ -81,6 +85,8 @@ struct ina2xx_data { struct i2c_client *client; const struct ina2xx_config *config; + long rshunt; + struct mutex update_lock; bool valid; unsigned long last_updated; @@ -110,34 +116,96 @@ static const struct ina2xx_config ina2xx_config[] = { }, }; -static struct ina2xx_data *ina2xx_update_device(struct device *dev) +/* + * Initialize the configuration and calibration registers. + */ +static int ina2xx_init(struct ina2xx_data *data) +{ + struct i2c_client *client = data->client; + int ret; + + /* device configuration */ + ret = i2c_smbus_write_word_swapped(client, INA2XX_CONFIG, + data->config->config_default); + if (ret < 0) + return ret; + + /* + * Set current LSB to 1mA, shunt is in uOhms + * (equation 13 in datasheet). + */ + return i2c_smbus_write_word_swapped(client, INA2XX_CALIBRATION, + data->config->calibration_factor / data->rshunt); +} + +static int ina2xx_do_update(struct device *dev) { struct ina2xx_data *data = dev_get_drvdata(dev); struct i2c_client *client = data->client; + int i, rv, retry; + + dev_dbg(&client->dev, "Starting ina2xx update\n"); + + for (retry = 5; retry; retry--) { + /* Read all registers */ + for (i = 0; i < data->config->registers; i++) { + rv = i2c_smbus_read_word_swapped(client, i); + if (rv < 0) + return rv; + data->regs[i] = rv; + } + + /* + * If the current value in the calibration register is 0, the + * power and current registers will also remain at 0. In case + * the chip has been reset let's check the calibration + * register and reinitialize if needed. + */ + if (data->regs[INA2XX_CALIBRATION] == 0) { + dev_warn(dev, "chip not calibrated, reinitializing\n"); + + rv = ina2xx_init(data); + if (rv < 0) + return rv; + + /* + * Let's make sure the power and current registers + * have been updated before trying again. + */ + msleep(INA2XX_MAX_DELAY); + continue; + } + + data->last_updated = jiffies; + data->valid = 1; + + return 0; + } + + /* + * If we're here then although all write operations succeeded, the + * chip still returns 0 in the calibration register. Nothing more we + * can do here. + */ + dev_err(dev, "unable to reinitialize the chip\n"); + return -ENODEV; +} + +static struct ina2xx_data *ina2xx_update_device(struct device *dev) +{ + struct ina2xx_data *data = dev_get_drvdata(dev); struct ina2xx_data *ret = data; + int rv; mutex_lock(&data->update_lock); if (time_after(jiffies, data->last_updated + HZ / INA2XX_CONVERSION_RATE) || !data->valid) { - - int i; - - dev_dbg(&client->dev, "Starting ina2xx update\n"); - - /* Read all registers */ - for (i = 0; i < data->config->registers; i++) { - int rv = i2c_smbus_read_word_swapped(client, i); - if (rv < 0) { - ret = ERR_PTR(rv); - goto abort; - } - data->regs[i] = rv; - } - data->last_updated = jiffies; - data->valid = 1; + rv = ina2xx_do_update(dev); + if (rv < 0) + ret = ERR_PTR(rv); } -abort: + mutex_unlock(&data->update_lock); return ret; } @@ -221,7 +289,6 @@ static int ina2xx_probe(struct i2c_client *client, struct device *dev = &client->dev; struct ina2xx_data *data; struct device *hwmon_dev; - long shunt = 10000; /* default shunt value 10mOhms */ u32 val; int ret; @@ -234,41 +301,28 @@ static int ina2xx_probe(struct i2c_client *client, if (dev_get_platdata(dev)) { pdata = dev_get_platdata(dev); - shunt = pdata->shunt_uohms; + data->rshunt = pdata->shunt_uohms; } else if (!of_property_read_u32(dev->of_node, "shunt-resistor", &val)) { - shunt = val; + data->rshunt = val; + } else { + data->rshunt = INA2XX_RSHUNT_DEFAULT; } - if (shunt <= 0) - return -ENODEV; - /* set the device type */ data->kind = id->driver_data; data->config = &ina2xx_config[data->kind]; - - /* device configuration */ - ret = i2c_smbus_write_word_swapped(client, INA2XX_CONFIG, - data->config->config_default); - if (ret < 0) { - dev_err(dev, - "error writing to the config register: %d", ret); - return -ENODEV; - } - - /* - * Set current LSB to 1mA, shunt is in uOhms - * (equation 13 in datasheet). - */ - ret = i2c_smbus_write_word_swapped(client, INA2XX_CALIBRATION, - data->config->calibration_factor / shunt); - if (ret < 0) { - dev_err(dev, - "error writing to the calibration register: %d", ret); - return -ENODEV; - } - data->client = client; + + if (data->rshunt <= 0) + return -ENODEV; + + ret = ina2xx_init(data); + if (ret < 0) { + dev_err(dev, "error configuring the device: %d\n", ret); + return -ENODEV; + } + mutex_init(&data->update_lock); hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, @@ -277,7 +331,7 @@ static int ina2xx_probe(struct i2c_client *client, return PTR_ERR(hwmon_dev); dev_info(dev, "power monitor %s (Rshunt = %li uOhm)\n", - id->name, shunt); + id->name, data->rshunt); return 0; }