diff options
author | Bartosz Golaszewski <bgolaszewski@baylibre.com> | 2015-01-05 09:20:52 -0500 |
---|---|---|
committer | Guenter Roeck <linux@roeck-us.net> | 2015-01-26 00:23:58 -0500 |
commit | 509416a8e741d11d65c027f510b79573f69f6de8 (patch) | |
tree | 0d74b0fa17306f70b65510c855ce3d0807f6363e /drivers/hwmon | |
parent | 3c535bca9a43eb04d64537b55994c88e399a9981 (diff) |
hwmon: (ina2xx) reinitialize the chip in case it's been reset
Chips from the ina family don't like to be uninitialized. In case the power
is cut-off and restored again the calibration register will be reset
to 0 and both the power and current registers will remain at 0.
Check the calibration register in ina2xx_update_device() and reinitialize
the chip if needed.
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Diffstat (limited to 'drivers/hwmon')
-rw-r--r-- | drivers/hwmon/ina2xx.c | 128 |
1 files changed, 91 insertions, 37 deletions
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 @@ | |||
35 | #include <linux/hwmon-sysfs.h> | 35 | #include <linux/hwmon-sysfs.h> |
36 | #include <linux/jiffies.h> | 36 | #include <linux/jiffies.h> |
37 | #include <linux/of.h> | 37 | #include <linux/of.h> |
38 | #include <linux/delay.h> | ||
38 | 39 | ||
39 | #include <linux/platform_data/ina2xx.h> | 40 | #include <linux/platform_data/ina2xx.h> |
40 | 41 | ||
@@ -64,6 +65,9 @@ | |||
64 | 65 | ||
65 | /* worst case is 68.10 ms (~14.6Hz, ina219) */ | 66 | /* worst case is 68.10 ms (~14.6Hz, ina219) */ |
66 | #define INA2XX_CONVERSION_RATE 15 | 67 | #define INA2XX_CONVERSION_RATE 15 |
68 | #define INA2XX_MAX_DELAY 69 /* worst case delay in ms */ | ||
69 | |||
70 | #define INA2XX_RSHUNT_DEFAULT 10000 | ||
67 | 71 | ||
68 | enum ina2xx_ids { ina219, ina226 }; | 72 | enum ina2xx_ids { ina219, ina226 }; |
69 | 73 | ||
@@ -81,6 +85,8 @@ struct ina2xx_data { | |||
81 | struct i2c_client *client; | 85 | struct i2c_client *client; |
82 | const struct ina2xx_config *config; | 86 | const struct ina2xx_config *config; |
83 | 87 | ||
88 | long rshunt; | ||
89 | |||
84 | struct mutex update_lock; | 90 | struct mutex update_lock; |
85 | bool valid; | 91 | bool valid; |
86 | unsigned long last_updated; | 92 | unsigned long last_updated; |
@@ -110,34 +116,96 @@ static const struct ina2xx_config ina2xx_config[] = { | |||
110 | }, | 116 | }, |
111 | }; | 117 | }; |
112 | 118 | ||
113 | static struct ina2xx_data *ina2xx_update_device(struct device *dev) | 119 | /* |
120 | * Initialize the configuration and calibration registers. | ||
121 | */ | ||
122 | static int ina2xx_init(struct ina2xx_data *data) | ||
114 | { | 123 | { |
115 | struct ina2xx_data *data = dev_get_drvdata(dev); | ||
116 | struct i2c_client *client = data->client; | 124 | struct i2c_client *client = data->client; |
117 | struct ina2xx_data *ret = data; | 125 | int ret; |
118 | 126 | ||
119 | mutex_lock(&data->update_lock); | 127 | /* device configuration */ |
128 | ret = i2c_smbus_write_word_swapped(client, INA2XX_CONFIG, | ||
129 | data->config->config_default); | ||
130 | if (ret < 0) | ||
131 | return ret; | ||
120 | 132 | ||
121 | if (time_after(jiffies, data->last_updated + | 133 | /* |
122 | HZ / INA2XX_CONVERSION_RATE) || !data->valid) { | 134 | * Set current LSB to 1mA, shunt is in uOhms |
135 | * (equation 13 in datasheet). | ||
136 | */ | ||
137 | return i2c_smbus_write_word_swapped(client, INA2XX_CALIBRATION, | ||
138 | data->config->calibration_factor / data->rshunt); | ||
139 | } | ||
123 | 140 | ||
124 | int i; | 141 | static int ina2xx_do_update(struct device *dev) |
142 | { | ||
143 | struct ina2xx_data *data = dev_get_drvdata(dev); | ||
144 | struct i2c_client *client = data->client; | ||
145 | int i, rv, retry; | ||
125 | 146 | ||
126 | dev_dbg(&client->dev, "Starting ina2xx update\n"); | 147 | dev_dbg(&client->dev, "Starting ina2xx update\n"); |
127 | 148 | ||
149 | for (retry = 5; retry; retry--) { | ||
128 | /* Read all registers */ | 150 | /* Read all registers */ |
129 | for (i = 0; i < data->config->registers; i++) { | 151 | for (i = 0; i < data->config->registers; i++) { |
130 | int rv = i2c_smbus_read_word_swapped(client, i); | 152 | rv = i2c_smbus_read_word_swapped(client, i); |
131 | if (rv < 0) { | 153 | if (rv < 0) |
132 | ret = ERR_PTR(rv); | 154 | return rv; |
133 | goto abort; | ||
134 | } | ||
135 | data->regs[i] = rv; | 155 | data->regs[i] = rv; |
136 | } | 156 | } |
157 | |||
158 | /* | ||
159 | * If the current value in the calibration register is 0, the | ||
160 | * power and current registers will also remain at 0. In case | ||
161 | * the chip has been reset let's check the calibration | ||
162 | * register and reinitialize if needed. | ||
163 | */ | ||
164 | if (data->regs[INA2XX_CALIBRATION] == 0) { | ||
165 | dev_warn(dev, "chip not calibrated, reinitializing\n"); | ||
166 | |||
167 | rv = ina2xx_init(data); | ||
168 | if (rv < 0) | ||
169 | return rv; | ||
170 | |||
171 | /* | ||
172 | * Let's make sure the power and current registers | ||
173 | * have been updated before trying again. | ||
174 | */ | ||
175 | msleep(INA2XX_MAX_DELAY); | ||
176 | continue; | ||
177 | } | ||
178 | |||
137 | data->last_updated = jiffies; | 179 | data->last_updated = jiffies; |
138 | data->valid = 1; | 180 | data->valid = 1; |
181 | |||
182 | return 0; | ||
139 | } | 183 | } |
140 | abort: | 184 | |
185 | /* | ||
186 | * If we're here then although all write operations succeeded, the | ||
187 | * chip still returns 0 in the calibration register. Nothing more we | ||
188 | * can do here. | ||
189 | */ | ||
190 | dev_err(dev, "unable to reinitialize the chip\n"); | ||
191 | return -ENODEV; | ||
192 | } | ||
193 | |||
194 | static struct ina2xx_data *ina2xx_update_device(struct device *dev) | ||
195 | { | ||
196 | struct ina2xx_data *data = dev_get_drvdata(dev); | ||
197 | struct ina2xx_data *ret = data; | ||
198 | int rv; | ||
199 | |||
200 | mutex_lock(&data->update_lock); | ||
201 | |||
202 | if (time_after(jiffies, data->last_updated + | ||
203 | HZ / INA2XX_CONVERSION_RATE) || !data->valid) { | ||
204 | rv = ina2xx_do_update(dev); | ||
205 | if (rv < 0) | ||
206 | ret = ERR_PTR(rv); | ||
207 | } | ||
208 | |||
141 | mutex_unlock(&data->update_lock); | 209 | mutex_unlock(&data->update_lock); |
142 | return ret; | 210 | return ret; |
143 | } | 211 | } |
@@ -221,7 +289,6 @@ static int ina2xx_probe(struct i2c_client *client, | |||
221 | struct device *dev = &client->dev; | 289 | struct device *dev = &client->dev; |
222 | struct ina2xx_data *data; | 290 | struct ina2xx_data *data; |
223 | struct device *hwmon_dev; | 291 | struct device *hwmon_dev; |
224 | long shunt = 10000; /* default shunt value 10mOhms */ | ||
225 | u32 val; | 292 | u32 val; |
226 | int ret; | 293 | int ret; |
227 | 294 | ||
@@ -234,41 +301,28 @@ static int ina2xx_probe(struct i2c_client *client, | |||
234 | 301 | ||
235 | if (dev_get_platdata(dev)) { | 302 | if (dev_get_platdata(dev)) { |
236 | pdata = dev_get_platdata(dev); | 303 | pdata = dev_get_platdata(dev); |
237 | shunt = pdata->shunt_uohms; | 304 | data->rshunt = pdata->shunt_uohms; |
238 | } else if (!of_property_read_u32(dev->of_node, | 305 | } else if (!of_property_read_u32(dev->of_node, |
239 | "shunt-resistor", &val)) { | 306 | "shunt-resistor", &val)) { |
240 | shunt = val; | 307 | data->rshunt = val; |
308 | } else { | ||
309 | data->rshunt = INA2XX_RSHUNT_DEFAULT; | ||
241 | } | 310 | } |
242 | 311 | ||
243 | if (shunt <= 0) | ||
244 | return -ENODEV; | ||
245 | |||
246 | /* set the device type */ | 312 | /* set the device type */ |
247 | data->kind = id->driver_data; | 313 | data->kind = id->driver_data; |
248 | data->config = &ina2xx_config[data->kind]; | 314 | data->config = &ina2xx_config[data->kind]; |
315 | data->client = client; | ||
249 | 316 | ||
250 | /* device configuration */ | 317 | if (data->rshunt <= 0) |
251 | ret = i2c_smbus_write_word_swapped(client, INA2XX_CONFIG, | ||
252 | data->config->config_default); | ||
253 | if (ret < 0) { | ||
254 | dev_err(dev, | ||
255 | "error writing to the config register: %d", ret); | ||
256 | return -ENODEV; | 318 | return -ENODEV; |
257 | } | ||
258 | 319 | ||
259 | /* | 320 | ret = ina2xx_init(data); |
260 | * Set current LSB to 1mA, shunt is in uOhms | ||
261 | * (equation 13 in datasheet). | ||
262 | */ | ||
263 | ret = i2c_smbus_write_word_swapped(client, INA2XX_CALIBRATION, | ||
264 | data->config->calibration_factor / shunt); | ||
265 | if (ret < 0) { | 321 | if (ret < 0) { |
266 | dev_err(dev, | 322 | dev_err(dev, "error configuring the device: %d\n", ret); |
267 | "error writing to the calibration register: %d", ret); | ||
268 | return -ENODEV; | 323 | return -ENODEV; |
269 | } | 324 | } |
270 | 325 | ||
271 | data->client = client; | ||
272 | mutex_init(&data->update_lock); | 326 | mutex_init(&data->update_lock); |
273 | 327 | ||
274 | hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, | 328 | hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, |
@@ -277,7 +331,7 @@ static int ina2xx_probe(struct i2c_client *client, | |||
277 | return PTR_ERR(hwmon_dev); | 331 | return PTR_ERR(hwmon_dev); |
278 | 332 | ||
279 | dev_info(dev, "power monitor %s (Rshunt = %li uOhm)\n", | 333 | dev_info(dev, "power monitor %s (Rshunt = %li uOhm)\n", |
280 | id->name, shunt); | 334 | id->name, data->rshunt); |
281 | 335 | ||
282 | return 0; | 336 | return 0; |
283 | } | 337 | } |