diff options
-rw-r--r-- | drivers/power/bq20z75.c | 204 |
1 files changed, 156 insertions, 48 deletions
diff --git a/drivers/power/bq20z75.c b/drivers/power/bq20z75.c index a1c7ae2ec9f8..492da27e1a47 100644 --- a/drivers/power/bq20z75.c +++ b/drivers/power/bq20z75.c | |||
@@ -36,7 +36,11 @@ enum { | |||
36 | REG_TIME_TO_FULL, | 36 | REG_TIME_TO_FULL, |
37 | REG_STATUS, | 37 | REG_STATUS, |
38 | REG_CYCLE_COUNT, | 38 | REG_CYCLE_COUNT, |
39 | REG_SERIAL_NUMBER | 39 | REG_SERIAL_NUMBER, |
40 | REG_REMAINING_CAPACITY, | ||
41 | REG_FULL_CHARGE_CAPACITY, | ||
42 | REG_DESIGN_CAPACITY, | ||
43 | REG_DESIGN_VOLTAGE, | ||
40 | }; | 44 | }; |
41 | 45 | ||
42 | /* manufacturer access defines */ | 46 | /* manufacturer access defines */ |
@@ -44,7 +48,7 @@ enum { | |||
44 | #define MANUFACTURER_ACCESS_SLEEP 0x0011 | 48 | #define MANUFACTURER_ACCESS_SLEEP 0x0011 |
45 | 49 | ||
46 | /* battery status value bits */ | 50 | /* battery status value bits */ |
47 | #define BATTERY_CHARGING 0x40 | 51 | #define BATTERY_DISCHARGING 0x40 |
48 | #define BATTERY_FULL_CHARGED 0x20 | 52 | #define BATTERY_FULL_CHARGED 0x20 |
49 | #define BATTERY_FULL_DISCHARGED 0x10 | 53 | #define BATTERY_FULL_DISCHARGED 0x10 |
50 | 54 | ||
@@ -72,6 +76,10 @@ static const struct bq20z75_device_data { | |||
72 | 32767), | 76 | 32767), |
73 | [REG_CAPACITY] = | 77 | [REG_CAPACITY] = |
74 | BQ20Z75_DATA(POWER_SUPPLY_PROP_CAPACITY, 0x0E, 0, 100), | 78 | BQ20Z75_DATA(POWER_SUPPLY_PROP_CAPACITY, 0x0E, 0, 100), |
79 | [REG_REMAINING_CAPACITY] = | ||
80 | BQ20Z75_DATA(POWER_SUPPLY_PROP_ENERGY_NOW, 0x0F, 0, 65535), | ||
81 | [REG_FULL_CHARGE_CAPACITY] = | ||
82 | BQ20Z75_DATA(POWER_SUPPLY_PROP_ENERGY_FULL, 0x10, 0, 65535), | ||
75 | [REG_TIME_TO_EMPTY] = | 83 | [REG_TIME_TO_EMPTY] = |
76 | BQ20Z75_DATA(POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, 0x12, 0, | 84 | BQ20Z75_DATA(POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, 0x12, 0, |
77 | 65535), | 85 | 65535), |
@@ -82,6 +90,12 @@ static const struct bq20z75_device_data { | |||
82 | BQ20Z75_DATA(POWER_SUPPLY_PROP_STATUS, 0x16, 0, 65535), | 90 | BQ20Z75_DATA(POWER_SUPPLY_PROP_STATUS, 0x16, 0, 65535), |
83 | [REG_CYCLE_COUNT] = | 91 | [REG_CYCLE_COUNT] = |
84 | BQ20Z75_DATA(POWER_SUPPLY_PROP_CYCLE_COUNT, 0x17, 0, 65535), | 92 | BQ20Z75_DATA(POWER_SUPPLY_PROP_CYCLE_COUNT, 0x17, 0, 65535), |
93 | [REG_DESIGN_CAPACITY] = | ||
94 | BQ20Z75_DATA(POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, 0x18, 0, | ||
95 | 65535), | ||
96 | [REG_DESIGN_VOLTAGE] = | ||
97 | BQ20Z75_DATA(POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, 0x19, 0, | ||
98 | 65535), | ||
85 | [REG_SERIAL_NUMBER] = | 99 | [REG_SERIAL_NUMBER] = |
86 | BQ20Z75_DATA(POWER_SUPPLY_PROP_SERIAL_NUMBER, 0x1C, 0, 65535), | 100 | BQ20Z75_DATA(POWER_SUPPLY_PROP_SERIAL_NUMBER, 0x1C, 0, 65535), |
87 | }; | 101 | }; |
@@ -99,6 +113,10 @@ static enum power_supply_property bq20z75_properties[] = { | |||
99 | POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, | 113 | POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, |
100 | POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, | 114 | POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, |
101 | POWER_SUPPLY_PROP_SERIAL_NUMBER, | 115 | POWER_SUPPLY_PROP_SERIAL_NUMBER, |
116 | POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, | ||
117 | POWER_SUPPLY_PROP_ENERGY_NOW, | ||
118 | POWER_SUPPLY_PROP_ENERGY_FULL, | ||
119 | POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, | ||
102 | }; | 120 | }; |
103 | 121 | ||
104 | struct bq20z75_info { | 122 | struct bq20z75_info { |
@@ -106,6 +124,35 @@ struct bq20z75_info { | |||
106 | struct power_supply power_supply; | 124 | struct power_supply power_supply; |
107 | }; | 125 | }; |
108 | 126 | ||
127 | static int bq20z75_read_word_data(struct i2c_client *client, u8 address) | ||
128 | { | ||
129 | s32 ret; | ||
130 | |||
131 | ret = i2c_smbus_read_word_data(client, address); | ||
132 | if (ret < 0) { | ||
133 | dev_err(&client->dev, | ||
134 | "%s: i2c read at address 0x%x failed\n", | ||
135 | __func__, address); | ||
136 | return ret; | ||
137 | } | ||
138 | return le16_to_cpu(ret); | ||
139 | } | ||
140 | |||
141 | static int bq20z75_write_word_data(struct i2c_client *client, u8 address, | ||
142 | u16 value) | ||
143 | { | ||
144 | s32 ret; | ||
145 | |||
146 | ret = i2c_smbus_write_word_data(client, address, le16_to_cpu(value)); | ||
147 | if (ret < 0) { | ||
148 | dev_err(&client->dev, | ||
149 | "%s: i2c write to address 0x%x failed\n", | ||
150 | __func__, address); | ||
151 | return ret; | ||
152 | } | ||
153 | return 0; | ||
154 | } | ||
155 | |||
109 | static int bq20z75_get_battery_presence_and_health( | 156 | static int bq20z75_get_battery_presence_and_health( |
110 | struct i2c_client *client, enum power_supply_property psp, | 157 | struct i2c_client *client, enum power_supply_property psp, |
111 | union power_supply_propval *val) | 158 | union power_supply_propval *val) |
@@ -115,24 +162,17 @@ static int bq20z75_get_battery_presence_and_health( | |||
115 | /* Write to ManufacturerAccess with | 162 | /* Write to ManufacturerAccess with |
116 | * ManufacturerAccess command and then | 163 | * ManufacturerAccess command and then |
117 | * read the status */ | 164 | * read the status */ |
118 | ret = i2c_smbus_write_word_data(client, | 165 | ret = bq20z75_write_word_data(client, |
119 | bq20z75_data[REG_MANUFACTURER_DATA].addr, | 166 | bq20z75_data[REG_MANUFACTURER_DATA].addr, |
120 | MANUFACTURER_ACCESS_STATUS); | 167 | MANUFACTURER_ACCESS_STATUS); |
121 | if (ret < 0) { | 168 | if (ret < 0) |
122 | dev_err(&client->dev, | 169 | return ret; |
123 | "%s: i2c write for battery presence failed\n", | ||
124 | __func__); | ||
125 | return -ENODEV; | ||
126 | } | ||
127 | 170 | ||
128 | ret = i2c_smbus_read_word_data(client, | 171 | |
172 | ret = bq20z75_read_word_data(client, | ||
129 | bq20z75_data[REG_MANUFACTURER_DATA].addr); | 173 | bq20z75_data[REG_MANUFACTURER_DATA].addr); |
130 | if (ret < 0) { | 174 | if (ret < 0) |
131 | dev_err(&client->dev, | 175 | return ret; |
132 | "%s: i2c read for battery presence failed\n", | ||
133 | __func__); | ||
134 | return -EIO; | ||
135 | } | ||
136 | 176 | ||
137 | if (ret < bq20z75_data[REG_MANUFACTURER_DATA].min_value || | 177 | if (ret < bq20z75_data[REG_MANUFACTURER_DATA].min_value || |
138 | ret > bq20z75_data[REG_MANUFACTURER_DATA].max_value) { | 178 | ret > bq20z75_data[REG_MANUFACTURER_DATA].max_value) { |
@@ -171,31 +211,28 @@ static int bq20z75_get_battery_property(struct i2c_client *client, | |||
171 | { | 211 | { |
172 | s32 ret; | 212 | s32 ret; |
173 | 213 | ||
174 | ret = i2c_smbus_read_word_data(client, | 214 | ret = bq20z75_read_word_data(client, |
175 | bq20z75_data[reg_offset].addr); | 215 | bq20z75_data[reg_offset].addr); |
176 | if (ret < 0) { | 216 | if (ret < 0) |
177 | dev_err(&client->dev, | 217 | return ret; |
178 | "%s: i2c read for %d failed\n", __func__, reg_offset); | 218 | |
179 | return -EIO; | 219 | /* returned values are 16 bit */ |
180 | } | 220 | if (bq20z75_data[reg_offset].min_value < 0) |
221 | ret = (s16)ret; | ||
181 | 222 | ||
182 | if (ret >= bq20z75_data[reg_offset].min_value && | 223 | if (ret >= bq20z75_data[reg_offset].min_value && |
183 | ret <= bq20z75_data[reg_offset].max_value) { | 224 | ret <= bq20z75_data[reg_offset].max_value) { |
184 | val->intval = ret; | 225 | val->intval = ret; |
185 | if (psp == POWER_SUPPLY_PROP_STATUS) { | 226 | if (psp == POWER_SUPPLY_PROP_STATUS) { |
186 | if (ret & BATTERY_CHARGING) | 227 | if (ret & BATTERY_FULL_CHARGED) |
187 | val->intval = POWER_SUPPLY_STATUS_CHARGING; | ||
188 | else if (ret & BATTERY_FULL_CHARGED) | ||
189 | val->intval = POWER_SUPPLY_STATUS_FULL; | 228 | val->intval = POWER_SUPPLY_STATUS_FULL; |
190 | else if (ret & BATTERY_FULL_DISCHARGED) | 229 | else if (ret & BATTERY_FULL_DISCHARGED) |
191 | val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; | 230 | val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; |
192 | else | 231 | else if (ret & BATTERY_DISCHARGING) |
193 | val->intval = POWER_SUPPLY_STATUS_DISCHARGING; | 232 | val->intval = POWER_SUPPLY_STATUS_DISCHARGING; |
233 | else | ||
234 | val->intval = POWER_SUPPLY_STATUS_CHARGING; | ||
194 | } | 235 | } |
195 | /* bq20z75 provides battery tempreture in 0.1°K | ||
196 | * so convert it to °C */ | ||
197 | else if (psp == POWER_SUPPLY_PROP_TEMP) | ||
198 | val->intval = ret - 2731; | ||
199 | } else { | 236 | } else { |
200 | if (psp == POWER_SUPPLY_PROP_STATUS) | 237 | if (psp == POWER_SUPPLY_PROP_STATUS) |
201 | val->intval = POWER_SUPPLY_STATUS_UNKNOWN; | 238 | val->intval = POWER_SUPPLY_STATUS_UNKNOWN; |
@@ -206,21 +243,77 @@ static int bq20z75_get_battery_property(struct i2c_client *client, | |||
206 | return 0; | 243 | return 0; |
207 | } | 244 | } |
208 | 245 | ||
246 | static void bq20z75_unit_adjustment(struct i2c_client *client, | ||
247 | enum power_supply_property psp, union power_supply_propval *val) | ||
248 | { | ||
249 | #define BASE_UNIT_CONVERSION 1000 | ||
250 | #define BATTERY_MODE_CAP_MULT_WATT (10 * BASE_UNIT_CONVERSION) | ||
251 | #define TIME_UNIT_CONVERSION 600 | ||
252 | #define TEMP_KELVIN_TO_CELCIUS 2731 | ||
253 | switch (psp) { | ||
254 | case POWER_SUPPLY_PROP_ENERGY_NOW: | ||
255 | case POWER_SUPPLY_PROP_ENERGY_FULL: | ||
256 | case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: | ||
257 | val->intval *= BATTERY_MODE_CAP_MULT_WATT; | ||
258 | break; | ||
259 | |||
260 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: | ||
261 | case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: | ||
262 | case POWER_SUPPLY_PROP_CURRENT_NOW: | ||
263 | val->intval *= BASE_UNIT_CONVERSION; | ||
264 | break; | ||
265 | |||
266 | case POWER_SUPPLY_PROP_TEMP: | ||
267 | /* bq20z75 provides battery tempreture in 0.1°K | ||
268 | * so convert it to 0.1°C */ | ||
269 | val->intval -= TEMP_KELVIN_TO_CELCIUS; | ||
270 | val->intval *= 10; | ||
271 | break; | ||
272 | |||
273 | case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: | ||
274 | case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG: | ||
275 | val->intval *= TIME_UNIT_CONVERSION; | ||
276 | break; | ||
277 | |||
278 | default: | ||
279 | dev_dbg(&client->dev, | ||
280 | "%s: no need for unit conversion %d\n", __func__, psp); | ||
281 | } | ||
282 | } | ||
283 | |||
209 | static int bq20z75_get_battery_capacity(struct i2c_client *client, | 284 | static int bq20z75_get_battery_capacity(struct i2c_client *client, |
285 | int reg_offset, enum power_supply_property psp, | ||
210 | union power_supply_propval *val) | 286 | union power_supply_propval *val) |
211 | { | 287 | { |
212 | s32 ret; | 288 | s32 ret; |
213 | 289 | ||
214 | ret = i2c_smbus_read_byte_data(client, bq20z75_data[REG_CAPACITY].addr); | 290 | ret = bq20z75_read_word_data(client, bq20z75_data[reg_offset].addr); |
215 | if (ret < 0) { | 291 | if (ret < 0) |
216 | dev_err(&client->dev, | 292 | return ret; |
217 | "%s: i2c read for %d failed\n", __func__, REG_CAPACITY); | ||
218 | return -EIO; | ||
219 | } | ||
220 | 293 | ||
221 | /* bq20z75 spec says that this can be >100 % | 294 | if (psp == POWER_SUPPLY_PROP_CAPACITY) { |
222 | * even if max value is 100 % */ | 295 | /* bq20z75 spec says that this can be >100 % |
223 | val->intval = min(ret, 100); | 296 | * even if max value is 100 % */ |
297 | val->intval = min(ret, 100); | ||
298 | } else | ||
299 | val->intval = ret; | ||
300 | |||
301 | return 0; | ||
302 | } | ||
303 | |||
304 | static char bq20z75_serial[5]; | ||
305 | static int bq20z75_get_battery_serial_number(struct i2c_client *client, | ||
306 | union power_supply_propval *val) | ||
307 | { | ||
308 | int ret; | ||
309 | |||
310 | ret = bq20z75_read_word_data(client, | ||
311 | bq20z75_data[REG_SERIAL_NUMBER].addr); | ||
312 | if (ret < 0) | ||
313 | return ret; | ||
314 | |||
315 | ret = sprintf(bq20z75_serial, "%04x", ret); | ||
316 | val->strval = bq20z75_serial; | ||
224 | 317 | ||
225 | return 0; | 318 | return 0; |
226 | } | 319 | } |
@@ -247,8 +340,23 @@ static int bq20z75_get_property(struct power_supply *psy, | |||
247 | val->intval = POWER_SUPPLY_TECHNOLOGY_LION; | 340 | val->intval = POWER_SUPPLY_TECHNOLOGY_LION; |
248 | break; | 341 | break; |
249 | 342 | ||
343 | case POWER_SUPPLY_PROP_ENERGY_NOW: | ||
344 | case POWER_SUPPLY_PROP_ENERGY_FULL: | ||
345 | case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: | ||
250 | case POWER_SUPPLY_PROP_CAPACITY: | 346 | case POWER_SUPPLY_PROP_CAPACITY: |
251 | ret = bq20z75_get_battery_capacity(client, val); | 347 | for (count = 0; count < ARRAY_SIZE(bq20z75_data); count++) { |
348 | if (psp == bq20z75_data[count].psp) | ||
349 | break; | ||
350 | } | ||
351 | |||
352 | ret = bq20z75_get_battery_capacity(client, count, psp, val); | ||
353 | if (ret) | ||
354 | return ret; | ||
355 | |||
356 | break; | ||
357 | |||
358 | case POWER_SUPPLY_PROP_SERIAL_NUMBER: | ||
359 | ret = bq20z75_get_battery_serial_number(client, val); | ||
252 | if (ret) | 360 | if (ret) |
253 | return ret; | 361 | return ret; |
254 | break; | 362 | break; |
@@ -260,7 +368,7 @@ static int bq20z75_get_property(struct power_supply *psy, | |||
260 | case POWER_SUPPLY_PROP_TEMP: | 368 | case POWER_SUPPLY_PROP_TEMP: |
261 | case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: | 369 | case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: |
262 | case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG: | 370 | case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG: |
263 | case POWER_SUPPLY_PROP_SERIAL_NUMBER: | 371 | case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: |
264 | for (count = 0; count < ARRAY_SIZE(bq20z75_data); count++) { | 372 | for (count = 0; count < ARRAY_SIZE(bq20z75_data); count++) { |
265 | if (psp == bq20z75_data[count].psp) | 373 | if (psp == bq20z75_data[count].psp) |
266 | break; | 374 | break; |
@@ -269,6 +377,7 @@ static int bq20z75_get_property(struct power_supply *psy, | |||
269 | ret = bq20z75_get_battery_property(client, count, psp, val); | 377 | ret = bq20z75_get_battery_property(client, count, psp, val); |
270 | if (ret) | 378 | if (ret) |
271 | return ret; | 379 | return ret; |
380 | |||
272 | break; | 381 | break; |
273 | 382 | ||
274 | default: | 383 | default: |
@@ -277,6 +386,9 @@ static int bq20z75_get_property(struct power_supply *psy, | |||
277 | return -EINVAL; | 386 | return -EINVAL; |
278 | } | 387 | } |
279 | 388 | ||
389 | /* Convert units to match requirements for power supply class */ | ||
390 | bq20z75_unit_adjustment(client, psp, val); | ||
391 | |||
280 | dev_dbg(&client->dev, | 392 | dev_dbg(&client->dev, |
281 | "%s: property = %d, value = %d\n", __func__, psp, val->intval); | 393 | "%s: property = %d, value = %d\n", __func__, psp, val->intval); |
282 | 394 | ||
@@ -335,15 +447,11 @@ static int bq20z75_suspend(struct i2c_client *client, | |||
335 | s32 ret; | 447 | s32 ret; |
336 | 448 | ||
337 | /* write to manufacturer access with sleep command */ | 449 | /* write to manufacturer access with sleep command */ |
338 | ret = i2c_smbus_write_word_data(client, | 450 | ret = bq20z75_write_word_data(client, |
339 | bq20z75_data[REG_MANUFACTURER_DATA].addr, | 451 | bq20z75_data[REG_MANUFACTURER_DATA].addr, |
340 | MANUFACTURER_ACCESS_SLEEP); | 452 | MANUFACTURER_ACCESS_SLEEP); |
341 | if (ret < 0) { | 453 | if (ret < 0) |
342 | dev_err(&client->dev, | 454 | return ret; |
343 | "%s: i2c write for %d failed\n", | ||
344 | __func__, MANUFACTURER_ACCESS_SLEEP); | ||
345 | return -EIO; | ||
346 | } | ||
347 | 455 | ||
348 | return 0; | 456 | return 0; |
349 | } | 457 | } |