aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/power/bq20z75.c204
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
104struct bq20z75_info { 122struct 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
127static 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
141static 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
109static int bq20z75_get_battery_presence_and_health( 156static 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
246static 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
209static int bq20z75_get_battery_capacity(struct i2c_client *client, 284static 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
304static char bq20z75_serial[5];
305static 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}