diff options
author | Ramakrishna Pallala <ramakrishna.pallala@intel.com> | 2015-05-23 23:41:58 -0400 |
---|---|---|
committer | Sebastian Reichel <sre@kernel.org> | 2015-05-23 16:43:28 -0400 |
commit | edd4ab0559316a1efe0881a4e2ccaeb4fec73142 (patch) | |
tree | 6b934e269707c18aec727d8e483f3cdad978017d | |
parent | 4aeae9cb0dad117f055add68c48decaf489aecf3 (diff) |
power: max17042_battery: add HEALTH and TEMP_* properties support
This patch adds the support for following battery properties
to max17042 fuel gauge driver.
POWER_SUPPLY_PROP_TEMP_ALERT_MIN
POWER_SUPPLY_PROP_TEMP_ALERT_MAX
POWER_SUPPLY_PROP_TEMP_MIN
POWER_SUPPLY_PROP_TEMP_MAX
POWER_SUPPLY_PROP_HEALTH
Signed-off-by: Ramakrishna Pallala <ramakrishna.pallala@intel.com>
Reviewed-by: Krzysztof Kozlowski <k.kozlowski.k@gmail.com>
Signed-off-by: Sebastian Reichel <sre@kernel.org>
-rw-r--r-- | drivers/power/max17042_battery.c | 190 | ||||
-rw-r--r-- | include/linux/power/max17042_battery.h | 4 |
2 files changed, 183 insertions, 11 deletions
diff --git a/drivers/power/max17042_battery.c b/drivers/power/max17042_battery.c index 6cc5e87ec031..748d9762e89b 100644 --- a/drivers/power/max17042_battery.c +++ b/drivers/power/max17042_battery.c | |||
@@ -63,6 +63,8 @@ | |||
63 | #define dP_ACC_100 0x1900 | 63 | #define dP_ACC_100 0x1900 |
64 | #define dP_ACC_200 0x3200 | 64 | #define dP_ACC_200 0x3200 |
65 | 65 | ||
66 | #define MAX17042_VMAX_TOLERANCE 50 /* 50 mV */ | ||
67 | |||
66 | struct max17042_chip { | 68 | struct max17042_chip { |
67 | struct i2c_client *client; | 69 | struct i2c_client *client; |
68 | struct regmap *regmap; | 70 | struct regmap *regmap; |
@@ -85,10 +87,94 @@ static enum power_supply_property max17042_battery_props[] = { | |||
85 | POWER_SUPPLY_PROP_CHARGE_FULL, | 87 | POWER_SUPPLY_PROP_CHARGE_FULL, |
86 | POWER_SUPPLY_PROP_CHARGE_COUNTER, | 88 | POWER_SUPPLY_PROP_CHARGE_COUNTER, |
87 | POWER_SUPPLY_PROP_TEMP, | 89 | POWER_SUPPLY_PROP_TEMP, |
90 | POWER_SUPPLY_PROP_TEMP_ALERT_MIN, | ||
91 | POWER_SUPPLY_PROP_TEMP_ALERT_MAX, | ||
92 | POWER_SUPPLY_PROP_TEMP_MIN, | ||
93 | POWER_SUPPLY_PROP_TEMP_MAX, | ||
94 | POWER_SUPPLY_PROP_HEALTH, | ||
88 | POWER_SUPPLY_PROP_CURRENT_NOW, | 95 | POWER_SUPPLY_PROP_CURRENT_NOW, |
89 | POWER_SUPPLY_PROP_CURRENT_AVG, | 96 | POWER_SUPPLY_PROP_CURRENT_AVG, |
90 | }; | 97 | }; |
91 | 98 | ||
99 | static int max17042_get_temperature(struct max17042_chip *chip, int *temp) | ||
100 | { | ||
101 | int ret; | ||
102 | u32 data; | ||
103 | struct regmap *map = chip->regmap; | ||
104 | |||
105 | ret = regmap_read(map, MAX17042_TEMP, &data); | ||
106 | if (ret < 0) | ||
107 | return ret; | ||
108 | |||
109 | *temp = data; | ||
110 | /* The value is signed. */ | ||
111 | if (*temp & 0x8000) { | ||
112 | *temp = (0x7fff & ~*temp) + 1; | ||
113 | *temp *= -1; | ||
114 | } | ||
115 | |||
116 | /* The value is converted into deci-centigrade scale */ | ||
117 | /* Units of LSB = 1 / 256 degree Celsius */ | ||
118 | *temp = *temp * 10 / 256; | ||
119 | return 0; | ||
120 | } | ||
121 | |||
122 | static int max17042_get_battery_health(struct max17042_chip *chip, int *health) | ||
123 | { | ||
124 | int temp, vavg, vbatt, ret; | ||
125 | u32 val; | ||
126 | |||
127 | ret = regmap_read(chip->regmap, MAX17042_AvgVCELL, &val); | ||
128 | if (ret < 0) | ||
129 | goto health_error; | ||
130 | |||
131 | /* bits [0-3] unused */ | ||
132 | vavg = val * 625 / 8; | ||
133 | /* Convert to millivolts */ | ||
134 | vavg /= 1000; | ||
135 | |||
136 | ret = regmap_read(chip->regmap, MAX17042_VCELL, &val); | ||
137 | if (ret < 0) | ||
138 | goto health_error; | ||
139 | |||
140 | /* bits [0-3] unused */ | ||
141 | vbatt = val * 625 / 8; | ||
142 | /* Convert to millivolts */ | ||
143 | vbatt /= 1000; | ||
144 | |||
145 | if (vavg < chip->pdata->vmin) { | ||
146 | *health = POWER_SUPPLY_HEALTH_DEAD; | ||
147 | goto out; | ||
148 | } | ||
149 | |||
150 | if (vbatt > chip->pdata->vmax + MAX17042_VMAX_TOLERANCE) { | ||
151 | *health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; | ||
152 | goto out; | ||
153 | } | ||
154 | |||
155 | ret = max17042_get_temperature(chip, &temp); | ||
156 | if (ret < 0) | ||
157 | goto health_error; | ||
158 | |||
159 | if (temp <= chip->pdata->temp_min) { | ||
160 | *health = POWER_SUPPLY_HEALTH_COLD; | ||
161 | goto out; | ||
162 | } | ||
163 | |||
164 | if (temp >= chip->pdata->temp_max) { | ||
165 | *health = POWER_SUPPLY_HEALTH_OVERHEAT; | ||
166 | goto out; | ||
167 | } | ||
168 | |||
169 | *health = POWER_SUPPLY_HEALTH_GOOD; | ||
170 | |||
171 | out: | ||
172 | return 0; | ||
173 | |||
174 | health_error: | ||
175 | return ret; | ||
176 | } | ||
177 | |||
92 | static int max17042_get_property(struct power_supply *psy, | 178 | static int max17042_get_property(struct power_supply *psy, |
93 | enum power_supply_property psp, | 179 | enum power_supply_property psp, |
94 | union power_supply_propval *val) | 180 | union power_supply_propval *val) |
@@ -181,19 +267,34 @@ static int max17042_get_property(struct power_supply *psy, | |||
181 | val->intval = data * 1000 / 2; | 267 | val->intval = data * 1000 / 2; |
182 | break; | 268 | break; |
183 | case POWER_SUPPLY_PROP_TEMP: | 269 | case POWER_SUPPLY_PROP_TEMP: |
184 | ret = regmap_read(map, MAX17042_TEMP, &data); | 270 | ret = max17042_get_temperature(chip, &val->intval); |
271 | if (ret < 0) | ||
272 | return ret; | ||
273 | break; | ||
274 | case POWER_SUPPLY_PROP_TEMP_ALERT_MIN: | ||
275 | ret = regmap_read(map, MAX17042_TALRT_Th, &data); | ||
276 | if (ret < 0) | ||
277 | return ret; | ||
278 | /* LSB is Alert Minimum. In deci-centigrade */ | ||
279 | val->intval = (data & 0xff) * 10; | ||
280 | break; | ||
281 | case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: | ||
282 | ret = regmap_read(map, MAX17042_TALRT_Th, &data); | ||
283 | if (ret < 0) | ||
284 | return ret; | ||
285 | /* MSB is Alert Maximum. In deci-centigrade */ | ||
286 | val->intval = (data >> 8) * 10; | ||
287 | break; | ||
288 | case POWER_SUPPLY_PROP_TEMP_MIN: | ||
289 | val->intval = chip->pdata->temp_min; | ||
290 | break; | ||
291 | case POWER_SUPPLY_PROP_TEMP_MAX: | ||
292 | val->intval = chip->pdata->temp_max; | ||
293 | break; | ||
294 | case POWER_SUPPLY_PROP_HEALTH: | ||
295 | ret = max17042_get_battery_health(chip, &val->intval); | ||
185 | if (ret < 0) | 296 | if (ret < 0) |
186 | return ret; | 297 | return ret; |
187 | |||
188 | val->intval = data; | ||
189 | /* The value is signed. */ | ||
190 | if (val->intval & 0x8000) { | ||
191 | val->intval = (0x7fff & ~val->intval) + 1; | ||
192 | val->intval *= -1; | ||
193 | } | ||
194 | /* The value is converted into deci-centigrade scale */ | ||
195 | /* Units of LSB = 1 / 256 degree Celsius */ | ||
196 | val->intval = val->intval * 10 / 256; | ||
197 | break; | 298 | break; |
198 | case POWER_SUPPLY_PROP_CURRENT_NOW: | 299 | case POWER_SUPPLY_PROP_CURRENT_NOW: |
199 | if (chip->pdata->enable_current_sense) { | 300 | if (chip->pdata->enable_current_sense) { |
@@ -237,6 +338,69 @@ static int max17042_get_property(struct power_supply *psy, | |||
237 | return 0; | 338 | return 0; |
238 | } | 339 | } |
239 | 340 | ||
341 | static int max17042_set_property(struct power_supply *psy, | ||
342 | enum power_supply_property psp, | ||
343 | const union power_supply_propval *val) | ||
344 | { | ||
345 | struct max17042_chip *chip = power_supply_get_drvdata(psy); | ||
346 | struct regmap *map = chip->regmap; | ||
347 | int ret = 0; | ||
348 | u32 data; | ||
349 | int8_t temp; | ||
350 | |||
351 | switch (psp) { | ||
352 | case POWER_SUPPLY_PROP_TEMP_ALERT_MIN: | ||
353 | ret = regmap_read(map, MAX17042_TALRT_Th, &data); | ||
354 | if (ret < 0) | ||
355 | return ret; | ||
356 | |||
357 | /* Input in deci-centigrade, convert to centigrade */ | ||
358 | temp = val->intval / 10; | ||
359 | /* force min < max */ | ||
360 | if (temp >= (int8_t)(data >> 8)) | ||
361 | temp = (int8_t)(data >> 8) - 1; | ||
362 | /* Write both MAX and MIN ALERT */ | ||
363 | data = (data & 0xff00) + temp; | ||
364 | ret = regmap_write(map, MAX17042_TALRT_Th, data); | ||
365 | break; | ||
366 | case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: | ||
367 | ret = regmap_read(map, MAX17042_TALRT_Th, &data); | ||
368 | if (ret < 0) | ||
369 | return ret; | ||
370 | |||
371 | /* Input in Deci-Centigrade, convert to centigrade */ | ||
372 | temp = val->intval / 10; | ||
373 | /* force max > min */ | ||
374 | if (temp <= (int8_t)(data & 0xff)) | ||
375 | temp = (int8_t)(data & 0xff) + 1; | ||
376 | /* Write both MAX and MIN ALERT */ | ||
377 | data = (data & 0xff) + (temp << 8); | ||
378 | ret = regmap_write(map, MAX17042_TALRT_Th, data); | ||
379 | break; | ||
380 | default: | ||
381 | ret = -EINVAL; | ||
382 | } | ||
383 | |||
384 | return ret; | ||
385 | } | ||
386 | |||
387 | static int max17042_property_is_writeable(struct power_supply *psy, | ||
388 | enum power_supply_property psp) | ||
389 | { | ||
390 | int ret; | ||
391 | |||
392 | switch (psp) { | ||
393 | case POWER_SUPPLY_PROP_TEMP_ALERT_MIN: | ||
394 | case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: | ||
395 | ret = 1; | ||
396 | break; | ||
397 | default: | ||
398 | ret = 0; | ||
399 | } | ||
400 | |||
401 | return ret; | ||
402 | } | ||
403 | |||
240 | static int max17042_write_verify_reg(struct regmap *map, u8 reg, u32 value) | 404 | static int max17042_write_verify_reg(struct regmap *map, u8 reg, u32 value) |
241 | { | 405 | { |
242 | int retries = 8; | 406 | int retries = 8; |
@@ -665,6 +829,8 @@ static const struct power_supply_desc max17042_psy_desc = { | |||
665 | .name = "max170xx_battery", | 829 | .name = "max170xx_battery", |
666 | .type = POWER_SUPPLY_TYPE_BATTERY, | 830 | .type = POWER_SUPPLY_TYPE_BATTERY, |
667 | .get_property = max17042_get_property, | 831 | .get_property = max17042_get_property, |
832 | .set_property = max17042_set_property, | ||
833 | .property_is_writeable = max17042_property_is_writeable, | ||
668 | .properties = max17042_battery_props, | 834 | .properties = max17042_battery_props, |
669 | .num_properties = ARRAY_SIZE(max17042_battery_props), | 835 | .num_properties = ARRAY_SIZE(max17042_battery_props), |
670 | }; | 836 | }; |
@@ -673,6 +839,8 @@ static const struct power_supply_desc max17042_no_current_sense_psy_desc = { | |||
673 | .name = "max170xx_battery", | 839 | .name = "max170xx_battery", |
674 | .type = POWER_SUPPLY_TYPE_BATTERY, | 840 | .type = POWER_SUPPLY_TYPE_BATTERY, |
675 | .get_property = max17042_get_property, | 841 | .get_property = max17042_get_property, |
842 | .set_property = max17042_set_property, | ||
843 | .property_is_writeable = max17042_property_is_writeable, | ||
676 | .properties = max17042_battery_props, | 844 | .properties = max17042_battery_props, |
677 | .num_properties = ARRAY_SIZE(max17042_battery_props) - 2, | 845 | .num_properties = ARRAY_SIZE(max17042_battery_props) - 2, |
678 | }; | 846 | }; |
diff --git a/include/linux/power/max17042_battery.h b/include/linux/power/max17042_battery.h index cf112b4075c8..522757ac9cd4 100644 --- a/include/linux/power/max17042_battery.h +++ b/include/linux/power/max17042_battery.h | |||
@@ -215,6 +215,10 @@ struct max17042_platform_data { | |||
215 | * the datasheet although it can be changed by board designers. | 215 | * the datasheet although it can be changed by board designers. |
216 | */ | 216 | */ |
217 | unsigned int r_sns; | 217 | unsigned int r_sns; |
218 | int vmin; /* in millivolts */ | ||
219 | int vmax; /* in millivolts */ | ||
220 | int temp_min; /* in tenths of degree Celsius */ | ||
221 | int temp_max; /* in tenths of degree Celsius */ | ||
218 | }; | 222 | }; |
219 | 223 | ||
220 | #endif /* __MAX17042_BATTERY_H_ */ | 224 | #endif /* __MAX17042_BATTERY_H_ */ |