diff options
author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
---|---|---|
committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
commit | fcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch) | |
tree | a57612d1888735a2ec7972891b68c1ac5ec8faea /drivers/power | |
parent | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff) |
Diffstat (limited to 'drivers/power')
-rw-r--r-- | drivers/power/bq20z75.c | 810 | ||||
-rw-r--r-- | drivers/power/max17048_battery.c | 579 | ||||
-rw-r--r-- | drivers/power/max8907c-charger.c | 228 | ||||
-rw-r--r-- | drivers/power/smb349-charger.c | 588 | ||||
-rw-r--r-- | drivers/power/tegra_bpc_mgmt.c | 139 | ||||
-rw-r--r-- | drivers/power/tps80031-charger.c | 476 | ||||
-rw-r--r-- | drivers/power/tps80031_battery_gauge.c | 606 |
7 files changed, 3426 insertions, 0 deletions
diff --git a/drivers/power/bq20z75.c b/drivers/power/bq20z75.c new file mode 100644 index 00000000000..32e5a8c5c3d --- /dev/null +++ b/drivers/power/bq20z75.c | |||
@@ -0,0 +1,810 @@ | |||
1 | /* | ||
2 | * Gas Gauge driver for TI's BQ20Z75 | ||
3 | * | ||
4 | * Copyright (c) 2010, NVIDIA Corporation. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
14 | * more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License along | ||
17 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
19 | */ | ||
20 | |||
21 | #include <linux/init.h> | ||
22 | #include <linux/module.h> | ||
23 | #include <linux/kernel.h> | ||
24 | #include <linux/err.h> | ||
25 | #include <linux/power_supply.h> | ||
26 | #include <linux/i2c.h> | ||
27 | #include <linux/slab.h> | ||
28 | #include <linux/interrupt.h> | ||
29 | #include <linux/gpio.h> | ||
30 | |||
31 | #include <linux/power/bq20z75.h> | ||
32 | |||
33 | enum { | ||
34 | REG_MANUFACTURER_DATA, | ||
35 | REG_TEMPERATURE, | ||
36 | REG_VOLTAGE, | ||
37 | REG_CURRENT, | ||
38 | REG_CAPACITY, | ||
39 | REG_TIME_TO_EMPTY, | ||
40 | REG_TIME_TO_FULL, | ||
41 | REG_STATUS, | ||
42 | REG_CYCLE_COUNT, | ||
43 | REG_SERIAL_NUMBER, | ||
44 | REG_REMAINING_CAPACITY, | ||
45 | REG_REMAINING_CAPACITY_CHARGE, | ||
46 | REG_FULL_CHARGE_CAPACITY, | ||
47 | REG_FULL_CHARGE_CAPACITY_CHARGE, | ||
48 | REG_DESIGN_CAPACITY, | ||
49 | REG_DESIGN_CAPACITY_CHARGE, | ||
50 | REG_DESIGN_VOLTAGE, | ||
51 | }; | ||
52 | |||
53 | /* Battery Mode defines */ | ||
54 | #define BATTERY_MODE_OFFSET 0x03 | ||
55 | #define BATTERY_MODE_MASK 0x8000 | ||
56 | enum bq20z75_battery_mode { | ||
57 | BATTERY_MODE_AMPS, | ||
58 | BATTERY_MODE_WATTS | ||
59 | }; | ||
60 | |||
61 | /* manufacturer access defines */ | ||
62 | #define MANUFACTURER_ACCESS_STATUS 0x0006 | ||
63 | #define MANUFACTURER_ACCESS_SLEEP 0x0011 | ||
64 | |||
65 | /* battery status value bits */ | ||
66 | #define BATTERY_DISCHARGING 0x40 | ||
67 | #define BATTERY_FULL_CHARGED 0x20 | ||
68 | #define BATTERY_FULL_DISCHARGED 0x10 | ||
69 | |||
70 | #define BQ20Z75_DATA(_psp, _addr, _min_value, _max_value) { \ | ||
71 | .psp = _psp, \ | ||
72 | .addr = _addr, \ | ||
73 | .min_value = _min_value, \ | ||
74 | .max_value = _max_value, \ | ||
75 | } | ||
76 | |||
77 | static const struct bq20z75_device_data { | ||
78 | enum power_supply_property psp; | ||
79 | u8 addr; | ||
80 | int min_value; | ||
81 | int max_value; | ||
82 | } bq20z75_data[] = { | ||
83 | [REG_MANUFACTURER_DATA] = | ||
84 | BQ20Z75_DATA(POWER_SUPPLY_PROP_PRESENT, 0x00, 0, 65535), | ||
85 | [REG_TEMPERATURE] = | ||
86 | BQ20Z75_DATA(POWER_SUPPLY_PROP_TEMP, 0x08, 0, 65535), | ||
87 | [REG_VOLTAGE] = | ||
88 | BQ20Z75_DATA(POWER_SUPPLY_PROP_VOLTAGE_NOW, 0x09, 0, 20000), | ||
89 | [REG_CURRENT] = | ||
90 | BQ20Z75_DATA(POWER_SUPPLY_PROP_CURRENT_NOW, 0x0A, -32768, | ||
91 | 32767), | ||
92 | [REG_CAPACITY] = | ||
93 | BQ20Z75_DATA(POWER_SUPPLY_PROP_CAPACITY, 0x0E, 0, 100), | ||
94 | [REG_REMAINING_CAPACITY] = | ||
95 | BQ20Z75_DATA(POWER_SUPPLY_PROP_ENERGY_NOW, 0x0F, 0, 65535), | ||
96 | [REG_REMAINING_CAPACITY_CHARGE] = | ||
97 | BQ20Z75_DATA(POWER_SUPPLY_PROP_CHARGE_NOW, 0x0F, 0, 65535), | ||
98 | [REG_FULL_CHARGE_CAPACITY] = | ||
99 | BQ20Z75_DATA(POWER_SUPPLY_PROP_ENERGY_FULL, 0x10, 0, 65535), | ||
100 | [REG_FULL_CHARGE_CAPACITY_CHARGE] = | ||
101 | BQ20Z75_DATA(POWER_SUPPLY_PROP_CHARGE_FULL, 0x10, 0, 65535), | ||
102 | [REG_TIME_TO_EMPTY] = | ||
103 | BQ20Z75_DATA(POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, 0x12, 0, | ||
104 | 65535), | ||
105 | [REG_TIME_TO_FULL] = | ||
106 | BQ20Z75_DATA(POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, 0x13, 0, | ||
107 | 65535), | ||
108 | [REG_STATUS] = | ||
109 | BQ20Z75_DATA(POWER_SUPPLY_PROP_STATUS, 0x16, 0, 65535), | ||
110 | [REG_CYCLE_COUNT] = | ||
111 | BQ20Z75_DATA(POWER_SUPPLY_PROP_CYCLE_COUNT, 0x17, 0, 65535), | ||
112 | [REG_DESIGN_CAPACITY] = | ||
113 | BQ20Z75_DATA(POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, 0x18, 0, | ||
114 | 65535), | ||
115 | [REG_DESIGN_CAPACITY_CHARGE] = | ||
116 | BQ20Z75_DATA(POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 0x18, 0, | ||
117 | 65535), | ||
118 | [REG_DESIGN_VOLTAGE] = | ||
119 | BQ20Z75_DATA(POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, 0x19, 0, | ||
120 | 65535), | ||
121 | [REG_SERIAL_NUMBER] = | ||
122 | BQ20Z75_DATA(POWER_SUPPLY_PROP_SERIAL_NUMBER, 0x1C, 0, 65535), | ||
123 | }; | ||
124 | |||
125 | static enum power_supply_property bq20z75_properties[] = { | ||
126 | POWER_SUPPLY_PROP_STATUS, | ||
127 | POWER_SUPPLY_PROP_HEALTH, | ||
128 | POWER_SUPPLY_PROP_PRESENT, | ||
129 | POWER_SUPPLY_PROP_TECHNOLOGY, | ||
130 | POWER_SUPPLY_PROP_CYCLE_COUNT, | ||
131 | POWER_SUPPLY_PROP_VOLTAGE_NOW, | ||
132 | POWER_SUPPLY_PROP_CURRENT_NOW, | ||
133 | POWER_SUPPLY_PROP_CAPACITY, | ||
134 | POWER_SUPPLY_PROP_TEMP, | ||
135 | POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, | ||
136 | POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, | ||
137 | POWER_SUPPLY_PROP_SERIAL_NUMBER, | ||
138 | POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, | ||
139 | POWER_SUPPLY_PROP_ENERGY_NOW, | ||
140 | POWER_SUPPLY_PROP_ENERGY_FULL, | ||
141 | POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, | ||
142 | POWER_SUPPLY_PROP_CHARGE_NOW, | ||
143 | POWER_SUPPLY_PROP_CHARGE_FULL, | ||
144 | POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, | ||
145 | }; | ||
146 | |||
147 | struct bq20z75_info { | ||
148 | struct i2c_client *client; | ||
149 | struct power_supply power_supply; | ||
150 | struct bq20z75_platform_data plat_data; | ||
151 | bool is_present; | ||
152 | bool gpio_detect; | ||
153 | bool enable_detection; | ||
154 | int irq; | ||
155 | int last_state; | ||
156 | int poll_time; | ||
157 | struct delayed_work work; | ||
158 | int ignore_changes; | ||
159 | }; | ||
160 | |||
161 | static int bq20z75_read_word_data(struct i2c_client *client, u8 address) | ||
162 | { | ||
163 | struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); | ||
164 | s32 ret = 0; | ||
165 | int retries = 1; | ||
166 | |||
167 | retries = max(bq20z75_device->plat_data.i2c_retry_count + 1, 1); | ||
168 | |||
169 | while (retries > 0) { | ||
170 | ret = i2c_smbus_read_word_data(client, address); | ||
171 | if (ret >= 0) | ||
172 | break; | ||
173 | retries--; | ||
174 | } | ||
175 | |||
176 | if (ret < 0) { | ||
177 | dev_dbg(&client->dev, | ||
178 | "%s: i2c read at address 0x%x failed\n", | ||
179 | __func__, address); | ||
180 | return ret; | ||
181 | } | ||
182 | |||
183 | return le16_to_cpu(ret); | ||
184 | } | ||
185 | |||
186 | static int bq20z75_write_word_data(struct i2c_client *client, u8 address, | ||
187 | u16 value) | ||
188 | { | ||
189 | struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); | ||
190 | s32 ret = 0; | ||
191 | int retries = 1; | ||
192 | |||
193 | retries = max(bq20z75_device->plat_data.i2c_retry_count + 1, 1); | ||
194 | |||
195 | while (retries > 0) { | ||
196 | ret = i2c_smbus_write_word_data(client, address, | ||
197 | le16_to_cpu(value)); | ||
198 | if (ret >= 0) | ||
199 | break; | ||
200 | retries--; | ||
201 | } | ||
202 | |||
203 | if (ret < 0) { | ||
204 | dev_dbg(&client->dev, | ||
205 | "%s: i2c write to address 0x%x failed\n", | ||
206 | __func__, address); | ||
207 | return ret; | ||
208 | } | ||
209 | |||
210 | return 0; | ||
211 | } | ||
212 | |||
213 | static int bq20z75_get_battery_presence_and_health( | ||
214 | struct i2c_client *client, enum power_supply_property psp, | ||
215 | union power_supply_propval *val) | ||
216 | { | ||
217 | s32 ret; | ||
218 | struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); | ||
219 | |||
220 | if (psp == POWER_SUPPLY_PROP_PRESENT && | ||
221 | bq20z75_device->gpio_detect) { | ||
222 | ret = gpio_get_value( | ||
223 | bq20z75_device->plat_data.battery_detect); | ||
224 | if (ret == bq20z75_device->plat_data.battery_detect_present) | ||
225 | val->intval = 1; | ||
226 | else | ||
227 | val->intval = 0; | ||
228 | bq20z75_device->is_present = val->intval; | ||
229 | return ret; | ||
230 | } | ||
231 | |||
232 | /* Write to ManufacturerAccess with | ||
233 | * ManufacturerAccess command and then | ||
234 | * read the status */ | ||
235 | ret = bq20z75_write_word_data(client, | ||
236 | bq20z75_data[REG_MANUFACTURER_DATA].addr, | ||
237 | MANUFACTURER_ACCESS_STATUS); | ||
238 | if (ret < 0) { | ||
239 | if (psp == POWER_SUPPLY_PROP_PRESENT) | ||
240 | val->intval = 0; /* battery removed */ | ||
241 | return ret; | ||
242 | } | ||
243 | |||
244 | ret = bq20z75_read_word_data(client, | ||
245 | bq20z75_data[REG_MANUFACTURER_DATA].addr); | ||
246 | if (ret < 0) | ||
247 | return ret; | ||
248 | |||
249 | if (ret < bq20z75_data[REG_MANUFACTURER_DATA].min_value || | ||
250 | ret > bq20z75_data[REG_MANUFACTURER_DATA].max_value) { | ||
251 | val->intval = 0; | ||
252 | return 0; | ||
253 | } | ||
254 | |||
255 | /* Mask the upper nibble of 2nd byte and | ||
256 | * lower byte of response then | ||
257 | * shift the result by 8 to get status*/ | ||
258 | ret &= 0x0F00; | ||
259 | ret >>= 8; | ||
260 | if (psp == POWER_SUPPLY_PROP_PRESENT) { | ||
261 | if (ret == 0x0F) | ||
262 | /* battery removed */ | ||
263 | val->intval = 0; | ||
264 | else | ||
265 | val->intval = 1; | ||
266 | } else if (psp == POWER_SUPPLY_PROP_HEALTH) { | ||
267 | if (ret == 0x09) | ||
268 | val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; | ||
269 | else if (ret == 0x0B) | ||
270 | val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; | ||
271 | else if (ret == 0x0C) | ||
272 | val->intval = POWER_SUPPLY_HEALTH_DEAD; | ||
273 | else | ||
274 | val->intval = POWER_SUPPLY_HEALTH_GOOD; | ||
275 | } | ||
276 | |||
277 | return 0; | ||
278 | } | ||
279 | |||
280 | static int bq20z75_get_battery_property(struct i2c_client *client, | ||
281 | int reg_offset, enum power_supply_property psp, | ||
282 | union power_supply_propval *val) | ||
283 | { | ||
284 | struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); | ||
285 | s32 ret; | ||
286 | |||
287 | ret = bq20z75_read_word_data(client, | ||
288 | bq20z75_data[reg_offset].addr); | ||
289 | if (ret < 0) | ||
290 | return ret; | ||
291 | |||
292 | /* returned values are 16 bit */ | ||
293 | if (bq20z75_data[reg_offset].min_value < 0) | ||
294 | ret = (s16)ret; | ||
295 | |||
296 | if (ret >= bq20z75_data[reg_offset].min_value && | ||
297 | ret <= bq20z75_data[reg_offset].max_value) { | ||
298 | val->intval = ret; | ||
299 | if (psp != POWER_SUPPLY_PROP_STATUS) | ||
300 | return 0; | ||
301 | |||
302 | if (ret & BATTERY_FULL_CHARGED) | ||
303 | val->intval = POWER_SUPPLY_STATUS_FULL; | ||
304 | else if (ret & BATTERY_FULL_DISCHARGED) | ||
305 | val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; | ||
306 | else if (ret & BATTERY_DISCHARGING) | ||
307 | val->intval = POWER_SUPPLY_STATUS_DISCHARGING; | ||
308 | else | ||
309 | val->intval = POWER_SUPPLY_STATUS_CHARGING; | ||
310 | |||
311 | if (bq20z75_device->poll_time == 0) | ||
312 | bq20z75_device->last_state = val->intval; | ||
313 | else if (bq20z75_device->last_state != val->intval) { | ||
314 | cancel_delayed_work_sync(&bq20z75_device->work); | ||
315 | power_supply_changed(&bq20z75_device->power_supply); | ||
316 | bq20z75_device->poll_time = 0; | ||
317 | } | ||
318 | } else { | ||
319 | if (psp == POWER_SUPPLY_PROP_STATUS) | ||
320 | val->intval = POWER_SUPPLY_STATUS_UNKNOWN; | ||
321 | else | ||
322 | val->intval = 0; | ||
323 | } | ||
324 | |||
325 | return 0; | ||
326 | } | ||
327 | |||
328 | static void bq20z75_unit_adjustment(struct i2c_client *client, | ||
329 | enum power_supply_property psp, union power_supply_propval *val) | ||
330 | { | ||
331 | #define BASE_UNIT_CONVERSION 1000 | ||
332 | #define BATTERY_MODE_CAP_MULT_WATT (10 * BASE_UNIT_CONVERSION) | ||
333 | #define TIME_UNIT_CONVERSION 60 | ||
334 | #define TEMP_KELVIN_TO_CELSIUS 2731 | ||
335 | switch (psp) { | ||
336 | case POWER_SUPPLY_PROP_ENERGY_NOW: | ||
337 | case POWER_SUPPLY_PROP_ENERGY_FULL: | ||
338 | case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: | ||
339 | /* bq20z75 provides energy in units of 10mWh. | ||
340 | * Convert to µWh | ||
341 | */ | ||
342 | val->intval *= BATTERY_MODE_CAP_MULT_WATT; | ||
343 | break; | ||
344 | |||
345 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: | ||
346 | case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: | ||
347 | case POWER_SUPPLY_PROP_CURRENT_NOW: | ||
348 | case POWER_SUPPLY_PROP_CHARGE_NOW: | ||
349 | case POWER_SUPPLY_PROP_CHARGE_FULL: | ||
350 | case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: | ||
351 | val->intval *= BASE_UNIT_CONVERSION; | ||
352 | break; | ||
353 | |||
354 | case POWER_SUPPLY_PROP_TEMP: | ||
355 | /* bq20z75 provides battery temperature in 0.1K | ||
356 | * so convert it to 0.1°C | ||
357 | */ | ||
358 | val->intval -= TEMP_KELVIN_TO_CELSIUS; | ||
359 | break; | ||
360 | |||
361 | case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: | ||
362 | case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG: | ||
363 | /* bq20z75 provides time to empty and time to full in minutes. | ||
364 | * Convert to seconds | ||
365 | */ | ||
366 | val->intval *= TIME_UNIT_CONVERSION; | ||
367 | break; | ||
368 | |||
369 | default: | ||
370 | dev_dbg(&client->dev, | ||
371 | "%s: no need for unit conversion %d\n", __func__, psp); | ||
372 | } | ||
373 | } | ||
374 | |||
375 | static enum bq20z75_battery_mode | ||
376 | bq20z75_set_battery_mode(struct i2c_client *client, | ||
377 | enum bq20z75_battery_mode mode) | ||
378 | { | ||
379 | int ret, original_val; | ||
380 | |||
381 | original_val = bq20z75_read_word_data(client, BATTERY_MODE_OFFSET); | ||
382 | if (original_val < 0) | ||
383 | return original_val; | ||
384 | |||
385 | if ((original_val & BATTERY_MODE_MASK) == mode) | ||
386 | return mode; | ||
387 | |||
388 | if (mode == BATTERY_MODE_AMPS) | ||
389 | ret = original_val & ~BATTERY_MODE_MASK; | ||
390 | else | ||
391 | ret = original_val | BATTERY_MODE_MASK; | ||
392 | |||
393 | ret = bq20z75_write_word_data(client, BATTERY_MODE_OFFSET, ret); | ||
394 | if (ret < 0) | ||
395 | return ret; | ||
396 | |||
397 | return original_val & BATTERY_MODE_MASK; | ||
398 | } | ||
399 | |||
400 | static int bq20z75_get_battery_capacity(struct i2c_client *client, | ||
401 | int reg_offset, enum power_supply_property psp, | ||
402 | union power_supply_propval *val) | ||
403 | { | ||
404 | s32 ret; | ||
405 | enum bq20z75_battery_mode mode = BATTERY_MODE_WATTS; | ||
406 | |||
407 | if (power_supply_is_amp_property(psp)) | ||
408 | mode = BATTERY_MODE_AMPS; | ||
409 | |||
410 | mode = bq20z75_set_battery_mode(client, mode); | ||
411 | if (mode < 0) | ||
412 | return mode; | ||
413 | |||
414 | ret = bq20z75_read_word_data(client, bq20z75_data[reg_offset].addr); | ||
415 | if (ret < 0) | ||
416 | return ret; | ||
417 | |||
418 | if (psp == POWER_SUPPLY_PROP_CAPACITY) { | ||
419 | /* bq20z75 spec says that this can be >100 % | ||
420 | * even if max value is 100 % */ | ||
421 | val->intval = min(ret, 100); | ||
422 | } else | ||
423 | val->intval = ret; | ||
424 | |||
425 | ret = bq20z75_set_battery_mode(client, mode); | ||
426 | if (ret < 0) | ||
427 | return ret; | ||
428 | |||
429 | return 0; | ||
430 | } | ||
431 | |||
432 | static char bq20z75_serial[5]; | ||
433 | static int bq20z75_get_battery_serial_number(struct i2c_client *client, | ||
434 | union power_supply_propval *val) | ||
435 | { | ||
436 | int ret; | ||
437 | |||
438 | ret = bq20z75_read_word_data(client, | ||
439 | bq20z75_data[REG_SERIAL_NUMBER].addr); | ||
440 | if (ret < 0) | ||
441 | return ret; | ||
442 | |||
443 | ret = sprintf(bq20z75_serial, "%04x", ret); | ||
444 | val->strval = bq20z75_serial; | ||
445 | |||
446 | return 0; | ||
447 | } | ||
448 | |||
449 | static int bq20z75_get_property_index(struct i2c_client *client, | ||
450 | enum power_supply_property psp) | ||
451 | { | ||
452 | int count; | ||
453 | for (count = 0; count < ARRAY_SIZE(bq20z75_data); count++) | ||
454 | if (psp == bq20z75_data[count].psp) | ||
455 | return count; | ||
456 | |||
457 | dev_warn(&client->dev, | ||
458 | "%s: Invalid Property - %d\n", __func__, psp); | ||
459 | |||
460 | return -EINVAL; | ||
461 | } | ||
462 | |||
463 | static int bq20z75_get_property(struct power_supply *psy, | ||
464 | enum power_supply_property psp, | ||
465 | union power_supply_propval *val) | ||
466 | { | ||
467 | int ret = 0; | ||
468 | struct bq20z75_info *bq20z75_device = container_of(psy, | ||
469 | struct bq20z75_info, power_supply); | ||
470 | struct i2c_client *client = bq20z75_device->client; | ||
471 | |||
472 | switch (psp) { | ||
473 | case POWER_SUPPLY_PROP_PRESENT: | ||
474 | case POWER_SUPPLY_PROP_HEALTH: | ||
475 | ret = bq20z75_get_battery_presence_and_health(client, psp, val); | ||
476 | if (psp == POWER_SUPPLY_PROP_PRESENT) | ||
477 | return 0; | ||
478 | break; | ||
479 | |||
480 | case POWER_SUPPLY_PROP_TECHNOLOGY: | ||
481 | val->intval = POWER_SUPPLY_TECHNOLOGY_LION; | ||
482 | break; | ||
483 | |||
484 | case POWER_SUPPLY_PROP_ENERGY_NOW: | ||
485 | case POWER_SUPPLY_PROP_ENERGY_FULL: | ||
486 | case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: | ||
487 | case POWER_SUPPLY_PROP_CHARGE_NOW: | ||
488 | case POWER_SUPPLY_PROP_CHARGE_FULL: | ||
489 | case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: | ||
490 | case POWER_SUPPLY_PROP_CAPACITY: | ||
491 | ret = bq20z75_get_property_index(client, psp); | ||
492 | if (ret < 0) | ||
493 | break; | ||
494 | |||
495 | ret = bq20z75_get_battery_capacity(client, ret, psp, val); | ||
496 | break; | ||
497 | |||
498 | case POWER_SUPPLY_PROP_SERIAL_NUMBER: | ||
499 | ret = bq20z75_get_battery_serial_number(client, val); | ||
500 | break; | ||
501 | |||
502 | case POWER_SUPPLY_PROP_STATUS: | ||
503 | case POWER_SUPPLY_PROP_CYCLE_COUNT: | ||
504 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: | ||
505 | case POWER_SUPPLY_PROP_CURRENT_NOW: | ||
506 | case POWER_SUPPLY_PROP_TEMP: | ||
507 | case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: | ||
508 | case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG: | ||
509 | case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: | ||
510 | ret = bq20z75_get_property_index(client, psp); | ||
511 | if (ret < 0) | ||
512 | break; | ||
513 | |||
514 | ret = bq20z75_get_battery_property(client, ret, psp, val); | ||
515 | break; | ||
516 | |||
517 | default: | ||
518 | dev_err(&client->dev, | ||
519 | "%s: INVALID property\n", __func__); | ||
520 | return -EINVAL; | ||
521 | } | ||
522 | |||
523 | if (!bq20z75_device->enable_detection) | ||
524 | goto done; | ||
525 | |||
526 | if (!bq20z75_device->gpio_detect && | ||
527 | bq20z75_device->is_present != (ret >= 0)) { | ||
528 | bq20z75_device->is_present = (ret >= 0); | ||
529 | power_supply_changed(&bq20z75_device->power_supply); | ||
530 | } | ||
531 | |||
532 | done: | ||
533 | if (!ret) { | ||
534 | /* Convert units to match requirements for power supply class */ | ||
535 | bq20z75_unit_adjustment(client, psp, val); | ||
536 | } | ||
537 | |||
538 | dev_dbg(&client->dev, | ||
539 | "%s: property = %d, value = %x\n", __func__, psp, val->intval); | ||
540 | |||
541 | if (ret && bq20z75_device->is_present) | ||
542 | return ret; | ||
543 | |||
544 | /* battery not present, so return NODATA for properties */ | ||
545 | if (ret) | ||
546 | return -ENODATA; | ||
547 | |||
548 | return 0; | ||
549 | } | ||
550 | |||
551 | static irqreturn_t bq20z75_irq(int irq, void *devid) | ||
552 | { | ||
553 | struct power_supply *battery = devid; | ||
554 | |||
555 | power_supply_changed(battery); | ||
556 | |||
557 | return IRQ_HANDLED; | ||
558 | } | ||
559 | |||
560 | static void bq20z75_external_power_changed(struct power_supply *psy) | ||
561 | { | ||
562 | struct bq20z75_info *bq20z75_device; | ||
563 | |||
564 | bq20z75_device = container_of(psy, struct bq20z75_info, power_supply); | ||
565 | |||
566 | if (bq20z75_device->ignore_changes > 0) { | ||
567 | bq20z75_device->ignore_changes--; | ||
568 | return; | ||
569 | } | ||
570 | |||
571 | /* cancel outstanding work */ | ||
572 | cancel_delayed_work_sync(&bq20z75_device->work); | ||
573 | |||
574 | schedule_delayed_work(&bq20z75_device->work, HZ); | ||
575 | bq20z75_device->poll_time = bq20z75_device->plat_data.poll_retry_count; | ||
576 | } | ||
577 | |||
578 | static void bq20z75_delayed_work(struct work_struct *work) | ||
579 | { | ||
580 | struct bq20z75_info *bq20z75_device; | ||
581 | s32 ret; | ||
582 | |||
583 | bq20z75_device = container_of(work, struct bq20z75_info, work.work); | ||
584 | |||
585 | ret = bq20z75_read_word_data(bq20z75_device->client, | ||
586 | bq20z75_data[REG_STATUS].addr); | ||
587 | /* if the read failed, give up on this work */ | ||
588 | if (ret < 0) { | ||
589 | bq20z75_device->poll_time = 0; | ||
590 | return; | ||
591 | } | ||
592 | |||
593 | if (ret & BATTERY_FULL_CHARGED) | ||
594 | ret = POWER_SUPPLY_STATUS_FULL; | ||
595 | else if (ret & BATTERY_FULL_DISCHARGED) | ||
596 | ret = POWER_SUPPLY_STATUS_NOT_CHARGING; | ||
597 | else if (ret & BATTERY_DISCHARGING) | ||
598 | ret = POWER_SUPPLY_STATUS_DISCHARGING; | ||
599 | else | ||
600 | ret = POWER_SUPPLY_STATUS_CHARGING; | ||
601 | |||
602 | if (bq20z75_device->last_state != ret) { | ||
603 | bq20z75_device->poll_time = 0; | ||
604 | power_supply_changed(&bq20z75_device->power_supply); | ||
605 | return; | ||
606 | } | ||
607 | if (bq20z75_device->poll_time > 0) { | ||
608 | schedule_delayed_work(&bq20z75_device->work, HZ); | ||
609 | bq20z75_device->poll_time--; | ||
610 | return; | ||
611 | } | ||
612 | } | ||
613 | |||
614 | static int __devinit bq20z75_probe(struct i2c_client *client, | ||
615 | const struct i2c_device_id *id) | ||
616 | { | ||
617 | struct bq20z75_info *bq20z75_device; | ||
618 | struct bq20z75_platform_data *pdata = client->dev.platform_data; | ||
619 | int rc; | ||
620 | int irq; | ||
621 | |||
622 | bq20z75_device = kzalloc(sizeof(struct bq20z75_info), GFP_KERNEL); | ||
623 | if (!bq20z75_device) | ||
624 | return -ENOMEM; | ||
625 | |||
626 | bq20z75_device->client = client; | ||
627 | bq20z75_device->enable_detection = false; | ||
628 | bq20z75_device->gpio_detect = false; | ||
629 | bq20z75_device->power_supply.name = "battery"; | ||
630 | bq20z75_device->power_supply.type = POWER_SUPPLY_TYPE_BATTERY; | ||
631 | bq20z75_device->power_supply.properties = bq20z75_properties; | ||
632 | bq20z75_device->power_supply.num_properties = | ||
633 | ARRAY_SIZE(bq20z75_properties); | ||
634 | bq20z75_device->power_supply.get_property = bq20z75_get_property; | ||
635 | /* ignore first notification of external change, it is generated | ||
636 | * from the power_supply_register call back | ||
637 | */ | ||
638 | bq20z75_device->ignore_changes = 1; | ||
639 | bq20z75_device->last_state = POWER_SUPPLY_STATUS_UNKNOWN; | ||
640 | bq20z75_device->power_supply.external_power_changed = | ||
641 | bq20z75_external_power_changed; | ||
642 | |||
643 | if (pdata) { | ||
644 | bq20z75_device->gpio_detect = | ||
645 | gpio_is_valid(pdata->battery_detect); | ||
646 | memcpy(&bq20z75_device->plat_data, pdata, sizeof(struct bq20z75_platform_data)); | ||
647 | } | ||
648 | |||
649 | i2c_set_clientdata(client, bq20z75_device); | ||
650 | |||
651 | /* Probing for the presence of the bq20z75 */ | ||
652 | rc = bq20z75_read_word_data(client, | ||
653 | bq20z75_data[REG_SERIAL_NUMBER].addr); | ||
654 | |||
655 | if (rc < 0) { | ||
656 | dev_err(&client->dev, | ||
657 | "%s: bq20z75 is not responding\n", __func__); | ||
658 | rc = -ENODEV; | ||
659 | goto exit_mem_free; | ||
660 | } | ||
661 | |||
662 | if (!bq20z75_device->gpio_detect) | ||
663 | goto skip_gpio; | ||
664 | |||
665 | rc = gpio_request(pdata->battery_detect, dev_name(&client->dev)); | ||
666 | if (rc) { | ||
667 | dev_warn(&client->dev, "Failed to request gpio: %d\n", rc); | ||
668 | bq20z75_device->gpio_detect = false; | ||
669 | goto skip_gpio; | ||
670 | } | ||
671 | |||
672 | rc = gpio_direction_input(pdata->battery_detect); | ||
673 | if (rc) { | ||
674 | dev_warn(&client->dev, "Failed to get gpio as input: %d\n", rc); | ||
675 | gpio_free(pdata->battery_detect); | ||
676 | bq20z75_device->gpio_detect = false; | ||
677 | goto skip_gpio; | ||
678 | } | ||
679 | |||
680 | irq = gpio_to_irq(pdata->battery_detect); | ||
681 | if (irq <= 0) { | ||
682 | dev_warn(&client->dev, "Failed to get gpio as irq: %d\n", irq); | ||
683 | gpio_free(pdata->battery_detect); | ||
684 | bq20z75_device->gpio_detect = false; | ||
685 | goto skip_gpio; | ||
686 | } | ||
687 | |||
688 | rc = request_irq(irq, bq20z75_irq, | ||
689 | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, | ||
690 | dev_name(&client->dev), &bq20z75_device->power_supply); | ||
691 | if (rc) { | ||
692 | dev_warn(&client->dev, "Failed to request irq: %d\n", rc); | ||
693 | gpio_free(pdata->battery_detect); | ||
694 | bq20z75_device->gpio_detect = false; | ||
695 | goto skip_gpio; | ||
696 | } | ||
697 | |||
698 | bq20z75_device->irq = irq; | ||
699 | |||
700 | skip_gpio: | ||
701 | rc = power_supply_register(&client->dev, &bq20z75_device->power_supply); | ||
702 | if (rc) { | ||
703 | dev_err(&client->dev, | ||
704 | "%s: Failed to register power supply\n", __func__); | ||
705 | goto exit_psupply; | ||
706 | } | ||
707 | |||
708 | dev_info(&client->dev, | ||
709 | "%s: battery gas gauge device registered\n", client->name); | ||
710 | |||
711 | INIT_DELAYED_WORK(&bq20z75_device->work, bq20z75_delayed_work); | ||
712 | |||
713 | bq20z75_device->enable_detection = true; | ||
714 | |||
715 | return 0; | ||
716 | |||
717 | exit_psupply: | ||
718 | if (bq20z75_device->irq) | ||
719 | free_irq(bq20z75_device->irq, &bq20z75_device->power_supply); | ||
720 | if (bq20z75_device->gpio_detect) | ||
721 | gpio_free(pdata->battery_detect); | ||
722 | |||
723 | exit_mem_free: | ||
724 | kfree(bq20z75_device); | ||
725 | |||
726 | return rc; | ||
727 | } | ||
728 | |||
729 | static int __devexit bq20z75_remove(struct i2c_client *client) | ||
730 | { | ||
731 | struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); | ||
732 | |||
733 | if (bq20z75_device->irq) | ||
734 | free_irq(bq20z75_device->irq, &bq20z75_device->power_supply); | ||
735 | if (bq20z75_device->gpio_detect) | ||
736 | gpio_free(bq20z75_device->plat_data.battery_detect); | ||
737 | |||
738 | power_supply_unregister(&bq20z75_device->power_supply); | ||
739 | |||
740 | cancel_delayed_work_sync(&bq20z75_device->work); | ||
741 | |||
742 | kfree(bq20z75_device); | ||
743 | bq20z75_device = NULL; | ||
744 | |||
745 | return 0; | ||
746 | } | ||
747 | |||
748 | #if defined CONFIG_PM | ||
749 | static int bq20z75_suspend(struct i2c_client *client, | ||
750 | pm_message_t state) | ||
751 | { | ||
752 | struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); | ||
753 | s32 ret; | ||
754 | |||
755 | if (bq20z75_device->poll_time > 0) | ||
756 | cancel_delayed_work_sync(&bq20z75_device->work); | ||
757 | |||
758 | /* write to manufacturer access with sleep command */ | ||
759 | ret = bq20z75_write_word_data(client, | ||
760 | bq20z75_data[REG_MANUFACTURER_DATA].addr, | ||
761 | MANUFACTURER_ACCESS_SLEEP); | ||
762 | if (bq20z75_device->is_present && ret < 0) | ||
763 | return ret; | ||
764 | |||
765 | return 0; | ||
766 | } | ||
767 | |||
768 | static int bq20z75_resume(struct i2c_client *client) | ||
769 | { | ||
770 | struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); | ||
771 | |||
772 | schedule_delayed_work(&bq20z75_device->work, HZ); | ||
773 | return 0; | ||
774 | } | ||
775 | #else | ||
776 | #define bq20z75_suspend NULL | ||
777 | #define bq20z75_resume NULL | ||
778 | #endif | ||
779 | |||
780 | static const struct i2c_device_id bq20z75_id[] = { | ||
781 | { "bq20z75", 0 }, | ||
782 | {} | ||
783 | }; | ||
784 | MODULE_DEVICE_TABLE(i2c, bq20z75_id); | ||
785 | |||
786 | static struct i2c_driver bq20z75_battery_driver = { | ||
787 | .probe = bq20z75_probe, | ||
788 | .remove = __devexit_p(bq20z75_remove), | ||
789 | .suspend = bq20z75_suspend, | ||
790 | .resume = bq20z75_resume, | ||
791 | .id_table = bq20z75_id, | ||
792 | .driver = { | ||
793 | .name = "bq20z75-battery", | ||
794 | }, | ||
795 | }; | ||
796 | |||
797 | static int __init bq20z75_battery_init(void) | ||
798 | { | ||
799 | return i2c_add_driver(&bq20z75_battery_driver); | ||
800 | } | ||
801 | module_init(bq20z75_battery_init); | ||
802 | |||
803 | static void __exit bq20z75_battery_exit(void) | ||
804 | { | ||
805 | i2c_del_driver(&bq20z75_battery_driver); | ||
806 | } | ||
807 | module_exit(bq20z75_battery_exit); | ||
808 | |||
809 | MODULE_DESCRIPTION("BQ20z75 battery monitor driver"); | ||
810 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/power/max17048_battery.c b/drivers/power/max17048_battery.c new file mode 100644 index 00000000000..44275e329c5 --- /dev/null +++ b/drivers/power/max17048_battery.c | |||
@@ -0,0 +1,579 @@ | |||
1 | /* | ||
2 | * max17048_battery.c | ||
3 | * fuel-gauge systems for lithium-ion (Li+) batteries | ||
4 | * | ||
5 | * Copyright (C) 2012 Nvidia Cooperation | ||
6 | * Chandler Zhang <chazhang@nvidia.com> | ||
7 | * Syed Rafiuddin <srafiuddin@nvidia.com> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | */ | ||
13 | |||
14 | #include <asm/unaligned.h> | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/init.h> | ||
17 | #include <linux/platform_device.h> | ||
18 | #include <linux/mutex.h> | ||
19 | #include <linux/err.h> | ||
20 | #include <linux/i2c.h> | ||
21 | #include <linux/delay.h> | ||
22 | #include <linux/power_supply.h> | ||
23 | #include <linux/slab.h> | ||
24 | #include <linux/max17048_battery.h> | ||
25 | |||
26 | #define MAX17048_VCELL 0x02 | ||
27 | #define MAX17048_SOC 0x04 | ||
28 | #define MAX17048_VER 0x08 | ||
29 | #define MAX17048_HIBRT 0x0A | ||
30 | #define MAX17048_CONFIG 0x0C | ||
31 | #define MAX17048_OCV 0x0E | ||
32 | #define MAX17048_VLRT 0x14 | ||
33 | #define MAX17048_VRESET 0x18 | ||
34 | #define MAX17048_STATUS 0x1A | ||
35 | #define MAX17048_UNLOCK 0x3E | ||
36 | #define MAX17048_TABLE 0x40 | ||
37 | #define MAX17048_RCOMPSEG1 0x80 | ||
38 | #define MAX17048_RCOMPSEG2 0x90 | ||
39 | #define MAX17048_CMD 0xFF | ||
40 | #define MAX17048_UNLOCK_VALUE 0x4a57 | ||
41 | #define MAX17048_RESET_VALUE 0x5400 | ||
42 | #define MAX17048_DELAY 1000 | ||
43 | #define MAX17048_BATTERY_FULL 95 | ||
44 | #define MAX17048_VERSION_NO 0x11 | ||
45 | |||
46 | struct max17048_chip { | ||
47 | struct i2c_client *client; | ||
48 | struct delayed_work work; | ||
49 | struct power_supply battery; | ||
50 | struct power_supply ac; | ||
51 | struct power_supply usb; | ||
52 | struct max17048_battery_model *model_data; | ||
53 | |||
54 | /* State Of Connect */ | ||
55 | int ac_online; | ||
56 | int usb_online; | ||
57 | /* battery voltage */ | ||
58 | int vcell; | ||
59 | /* battery capacity */ | ||
60 | int soc; | ||
61 | /* State Of Charge */ | ||
62 | int status; | ||
63 | |||
64 | int lasttime_vcell; | ||
65 | int lasttime_soc; | ||
66 | int lasttime_status; | ||
67 | }; | ||
68 | |||
69 | uint8_t max17048_custom_data[] = { | ||
70 | 0xAA, 0x00, 0xB1, 0xF0, 0xB7, 0xE0, 0xB9, 0x60, 0xBB, 0x80, | ||
71 | 0xBC, 0x40, 0xBD, 0x30, 0xBD, 0x50, 0xBD, 0xF0, 0xBE, 0x40, | ||
72 | 0xBF, 0xD0, 0xC0, 0x90, 0xC4, 0x30, 0xC7, 0xC0, 0xCA, 0x60, | ||
73 | 0xCF, 0x30, 0x01, 0x20, 0x09, 0xC0, 0x1F, 0xC0, 0x2B, 0xE0, | ||
74 | 0x4F, 0xC0, 0x30, 0x00, 0x47, 0x80, 0x4F, 0xE0, 0x77, 0x00, | ||
75 | 0x15, 0x60, 0x46, 0x20, 0x13, 0x80, 0x1A, 0x60, 0x12, 0x20, | ||
76 | 0x14, 0xA0, 0x14, 0xA0}; | ||
77 | |||
78 | static int max17048_write_word(struct i2c_client *client, int reg, u16 value) | ||
79 | { | ||
80 | int ret; | ||
81 | |||
82 | ret = i2c_smbus_write_word_data(client, reg, swab16(value)); | ||
83 | |||
84 | if (ret < 0) | ||
85 | dev_err(&client->dev, "%s(): Failed in writing register" | ||
86 | "0x%02x err %d\n", __func__, reg, ret); | ||
87 | |||
88 | return ret; | ||
89 | } | ||
90 | |||
91 | static uint16_t max17048_read_word(struct i2c_client *client, int reg) | ||
92 | { | ||
93 | uint16_t ret; | ||
94 | |||
95 | ret = i2c_smbus_read_word_data(client, reg); | ||
96 | |||
97 | if (ret < 0) | ||
98 | dev_err(&client->dev, "%s(): Failed in reading register" | ||
99 | "0x%02x err %d\n", __func__, reg, ret); | ||
100 | |||
101 | return swab16(ret); | ||
102 | } | ||
103 | |||
104 | static int max17048_get_property(struct power_supply *psy, | ||
105 | enum power_supply_property psp, | ||
106 | union power_supply_propval *val) | ||
107 | { | ||
108 | struct max17048_chip *chip = container_of(psy, | ||
109 | struct max17048_chip, battery); | ||
110 | |||
111 | switch (psp) { | ||
112 | case POWER_SUPPLY_PROP_STATUS: | ||
113 | val->intval = chip->status; | ||
114 | break; | ||
115 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: | ||
116 | val->intval = chip->vcell; | ||
117 | break; | ||
118 | case POWER_SUPPLY_PROP_CAPACITY: | ||
119 | val->intval = chip->soc; | ||
120 | break; | ||
121 | default: | ||
122 | return -EINVAL; | ||
123 | } | ||
124 | return 0; | ||
125 | } | ||
126 | |||
127 | static int max17048_ac_get_property(struct power_supply *psy, | ||
128 | enum power_supply_property psp, | ||
129 | union power_supply_propval *val) | ||
130 | { | ||
131 | struct max17048_chip *chip = container_of(psy, | ||
132 | struct max17048_chip, ac); | ||
133 | |||
134 | if (psp == POWER_SUPPLY_PROP_ONLINE) | ||
135 | val->intval = chip->ac_online; | ||
136 | else | ||
137 | return -EINVAL; | ||
138 | |||
139 | return 0; | ||
140 | } | ||
141 | |||
142 | static int max17048_usb_get_property(struct power_supply *psy, | ||
143 | enum power_supply_property psp, | ||
144 | union power_supply_propval *val) | ||
145 | { | ||
146 | struct max17048_chip *chip = container_of(psy, | ||
147 | struct max17048_chip, usb); | ||
148 | |||
149 | if (psp == POWER_SUPPLY_PROP_ONLINE) | ||
150 | val->intval = chip->usb_online; | ||
151 | else | ||
152 | return -EINVAL; | ||
153 | |||
154 | return 0; | ||
155 | } | ||
156 | |||
157 | static void max17048_get_vcell(struct i2c_client *client) | ||
158 | { | ||
159 | struct max17048_chip *chip = i2c_get_clientdata(client); | ||
160 | uint16_t vcell; | ||
161 | |||
162 | vcell = max17048_read_word(client, MAX17048_VCELL); | ||
163 | if (vcell < 0) | ||
164 | dev_err(&client->dev, "%s: err %d\n", __func__, vcell); | ||
165 | |||
166 | chip->vcell = vcell; | ||
167 | } | ||
168 | |||
169 | static void max17048_get_soc(struct i2c_client *client) | ||
170 | { | ||
171 | struct max17048_chip *chip = i2c_get_clientdata(client); | ||
172 | uint16_t soc; | ||
173 | |||
174 | soc = max17048_read_word(client, MAX17048_SOC); | ||
175 | if (soc < 0) | ||
176 | dev_err(&client->dev, "%s: err %d\n", __func__, soc); | ||
177 | |||
178 | chip->soc = soc >> 9; | ||
179 | } | ||
180 | |||
181 | static uint16_t max17048_get_version(struct i2c_client *client) | ||
182 | { | ||
183 | return swab16(i2c_smbus_read_word_data(client, MAX17048_VER)); | ||
184 | } | ||
185 | |||
186 | static void max17048_work(struct work_struct *work) | ||
187 | { | ||
188 | struct max17048_chip *chip; | ||
189 | |||
190 | chip = container_of(work, struct max17048_chip, work.work); | ||
191 | |||
192 | max17048_get_vcell(chip->client); | ||
193 | max17048_get_soc(chip->client); | ||
194 | |||
195 | if (chip->vcell != chip->lasttime_vcell || | ||
196 | chip->soc != chip->lasttime_soc || | ||
197 | chip->status != chip->lasttime_status) { | ||
198 | |||
199 | chip->lasttime_vcell = chip->vcell; | ||
200 | chip->lasttime_soc = chip->soc; | ||
201 | |||
202 | power_supply_changed(&chip->battery); | ||
203 | } | ||
204 | schedule_delayed_work(&chip->work, MAX17048_DELAY); | ||
205 | } | ||
206 | |||
207 | static void max17048_battery_status(enum charging_states status, | ||
208 | enum charger_type chrg_type, void *data) | ||
209 | { | ||
210 | struct max17048_chip *chip = data; | ||
211 | |||
212 | chip->ac_online = 0; | ||
213 | chip->usb_online = 0; | ||
214 | |||
215 | if (chrg_type == AC) | ||
216 | chip->ac_online = 1; | ||
217 | else if (chrg_type == USB) | ||
218 | chip->usb_online = 1; | ||
219 | |||
220 | if (status == progress) | ||
221 | chip->status = POWER_SUPPLY_STATUS_CHARGING; | ||
222 | else | ||
223 | chip->status = POWER_SUPPLY_STATUS_DISCHARGING; | ||
224 | |||
225 | if (chip->soc > MAX17048_BATTERY_FULL) | ||
226 | chip->status = POWER_SUPPLY_STATUS_FULL; | ||
227 | |||
228 | power_supply_changed(&chip->battery); | ||
229 | power_supply_changed(&chip->usb); | ||
230 | power_supply_changed(&chip->ac); | ||
231 | } | ||
232 | |||
233 | static enum power_supply_property max17048_battery_props[] = { | ||
234 | POWER_SUPPLY_PROP_STATUS, | ||
235 | POWER_SUPPLY_PROP_VOLTAGE_NOW, | ||
236 | POWER_SUPPLY_PROP_CAPACITY, | ||
237 | }; | ||
238 | |||
239 | static enum power_supply_property max17048_ac_props[] = { | ||
240 | POWER_SUPPLY_PROP_ONLINE, | ||
241 | }; | ||
242 | |||
243 | static enum power_supply_property max17048_usb_props[] = { | ||
244 | POWER_SUPPLY_PROP_ONLINE, | ||
245 | }; | ||
246 | |||
247 | static int max17048_write_rcomp_seg(struct i2c_client *client, | ||
248 | uint16_t rcomp_seg) | ||
249 | { | ||
250 | uint8_t rs1, rs2; | ||
251 | int ret; | ||
252 | |||
253 | rs2 = rcomp_seg | 0x00FF; | ||
254 | rs1 = rcomp_seg >> 8; | ||
255 | uint8_t rcomp_seg_table[16] = { rs1, rs2, rs1, rs2, | ||
256 | rs1, rs2, rs1, rs2, | ||
257 | rs1, rs2, rs1, rs2, | ||
258 | rs1, rs2, rs1, rs2}; | ||
259 | |||
260 | ret = i2c_smbus_write_i2c_block_data(client, MAX17048_RCOMPSEG1, | ||
261 | 16, (uint8_t *)rcomp_seg_table); | ||
262 | if (ret < 0) { | ||
263 | dev_err(&client->dev, "%s: err %d\n", __func__, ret); | ||
264 | return ret; | ||
265 | } | ||
266 | |||
267 | ret = i2c_smbus_write_i2c_block_data(client, MAX17048_RCOMPSEG2, | ||
268 | 16, (uint8_t *)rcomp_seg_table); | ||
269 | if (ret < 0) { | ||
270 | dev_err(&client->dev, "%s: err %d\n", __func__, ret); | ||
271 | return ret; | ||
272 | } | ||
273 | |||
274 | return 0; | ||
275 | } | ||
276 | |||
277 | static int max17048_load_model_data(struct max17048_chip *chip) | ||
278 | { | ||
279 | struct i2c_client *client = chip->client; | ||
280 | struct max17048_battery_model *mdata = chip->model_data; | ||
281 | uint16_t soc_tst, ocv; | ||
282 | int i, ret = 0; | ||
283 | |||
284 | /* read OCV */ | ||
285 | ocv = max17048_read_word(client, MAX17048_OCV); | ||
286 | if (ocv == 0xffff) { | ||
287 | dev_err(&client->dev, "%s: Failed in unlocking" | ||
288 | "max17048 err: %d\n", __func__, ocv); | ||
289 | return -1; | ||
290 | } | ||
291 | |||
292 | /* write custom model data */ | ||
293 | for (i = 0; i < 4; i += 1) { | ||
294 | if (i2c_smbus_write_i2c_block_data(client, | ||
295 | (MAX17048_TABLE+i*16), 16, | ||
296 | &max17048_custom_data[i*0x10]) < 0) { | ||
297 | dev_err(&client->dev, "%s: error writing model data:\n", | ||
298 | __func__); | ||
299 | return -1; | ||
300 | } | ||
301 | } | ||
302 | |||
303 | /* Write OCV Test value */ | ||
304 | ret = max17048_write_word(client, MAX17048_OCV, mdata->ocvtest); | ||
305 | if (ret < 0) | ||
306 | return ret; | ||
307 | |||
308 | ret = max17048_write_rcomp_seg(client, mdata->rcomp_seg); | ||
309 | if (ret < 0) | ||
310 | return ret; | ||
311 | |||
312 | /* Disable hibernate */ | ||
313 | ret = max17048_write_word(client, MAX17048_HIBRT, 0x0000); | ||
314 | if (ret < 0) | ||
315 | return ret; | ||
316 | |||
317 | /* Lock model access */ | ||
318 | ret = max17048_write_word(client, MAX17048_UNLOCK, 0x0000); | ||
319 | if (ret < 0) | ||
320 | return ret; | ||
321 | |||
322 | /* Delay between 150ms to 600ms */ | ||
323 | mdelay(200); | ||
324 | |||
325 | /* Read SOC Register and compare to expected result */ | ||
326 | soc_tst = max17048_read_word(client, MAX17048_SOC); | ||
327 | if (!((soc_tst >> 8) >= mdata->soccheck_A && | ||
328 | (soc_tst >> 8) <= mdata->soccheck_B)) { | ||
329 | dev_err(&client->dev, "%s: soc comparison failed %d\n", | ||
330 | __func__, ret); | ||
331 | return ret; | ||
332 | } else { | ||
333 | dev_info(&client->dev, "MAX17048 Custom data" | ||
334 | " loading successfull\n"); | ||
335 | } | ||
336 | |||
337 | /* unlock model access */ | ||
338 | ret = max17048_write_word(client, MAX17048_UNLOCK, | ||
339 | MAX17048_UNLOCK_VALUE); | ||
340 | if (ret < 0) | ||
341 | return ret; | ||
342 | |||
343 | /* Restore OCV */ | ||
344 | ret = max17048_write_word(client, MAX17048_OCV, ocv); | ||
345 | if (ret < 0) | ||
346 | return ret; | ||
347 | |||
348 | return ret; | ||
349 | } | ||
350 | |||
351 | static int max17048_initialize(struct max17048_chip *chip) | ||
352 | { | ||
353 | uint8_t ret, config, status; | ||
354 | uint16_t wrt_status; | ||
355 | struct i2c_client *client = chip->client; | ||
356 | struct max17048_battery_model *mdata = chip->model_data; | ||
357 | |||
358 | /* unlock model access */ | ||
359 | ret = max17048_write_word(client, MAX17048_UNLOCK, | ||
360 | MAX17048_UNLOCK_VALUE); | ||
361 | if (ret < 0) | ||
362 | return ret; | ||
363 | |||
364 | /* load model data */ | ||
365 | ret = max17048_load_model_data(chip); | ||
366 | if (ret < 0) { | ||
367 | dev_err(&client->dev, "%s: err %d\n", __func__, ret); | ||
368 | return ret; | ||
369 | } | ||
370 | |||
371 | if (mdata->bits == 19) | ||
372 | config = 32 - (mdata->alert_threshold * 2); | ||
373 | else if (mdata->bits == 18) | ||
374 | config = 32 - mdata->alert_threshold; | ||
375 | |||
376 | config = mdata->one_percent_alerts | config; | ||
377 | |||
378 | ret = max17048_write_word(client, MAX17048_CONFIG, | ||
379 | ((mdata->rcomp << 8) | config)); | ||
380 | if (ret < 0) | ||
381 | return ret; | ||
382 | |||
383 | /* Voltage Alert configuration */ | ||
384 | ret = max17048_write_word(client, MAX17048_VLRT, mdata->valert); | ||
385 | if (ret < 0) | ||
386 | return ret; | ||
387 | |||
388 | /* Hibernate configuration */ | ||
389 | ret = max17048_write_word(client, MAX17048_HIBRT, mdata->hibernate); | ||
390 | if (ret < 0) | ||
391 | return ret; | ||
392 | |||
393 | ret = max17048_write_word(client, MAX17048_VRESET, mdata->vreset); | ||
394 | if (ret < 0) | ||
395 | return ret; | ||
396 | |||
397 | /* clears the reset indicator */ | ||
398 | ret = max17048_read_word(client, MAX17048_STATUS); | ||
399 | if (ret < 0) | ||
400 | return ret; | ||
401 | |||
402 | /* Sets the EnVR bit if selected */ | ||
403 | status = (ret & 0xFE) | mdata->alert_on_reset; | ||
404 | wrt_status = status << 8; | ||
405 | |||
406 | ret = max17048_write_word(client, MAX17048_STATUS, wrt_status); | ||
407 | if (ret < 0) | ||
408 | return ret; | ||
409 | |||
410 | /* Lock model access */ | ||
411 | ret = max17048_write_word(client, MAX17048_UNLOCK, 0x0000); | ||
412 | if (ret < 0) | ||
413 | return ret; | ||
414 | |||
415 | /* Add delay */ | ||
416 | mdelay(200); | ||
417 | return 0; | ||
418 | } | ||
419 | |||
420 | static int __devinit max17048_probe(struct i2c_client *client, | ||
421 | const struct i2c_device_id *id) | ||
422 | { | ||
423 | struct max17048_chip *chip; | ||
424 | int ret; | ||
425 | uint16_t version; | ||
426 | |||
427 | chip = kzalloc(sizeof(*chip), GFP_KERNEL); | ||
428 | if (!chip) | ||
429 | return -ENOMEM; | ||
430 | |||
431 | chip->client = client; | ||
432 | chip->model_data = client->dev.platform_data; | ||
433 | chip->ac_online = 0; | ||
434 | chip->usb_online = 0; | ||
435 | |||
436 | i2c_set_clientdata(client, chip); | ||
437 | |||
438 | version = max17048_get_version(client); | ||
439 | if (version != MAX17048_VERSION_NO) { | ||
440 | ret = -ENODEV; | ||
441 | goto error2; | ||
442 | } | ||
443 | dev_info(&client->dev, "MAX17048 Fuel-Gauge Ver 0x%x\n", version); | ||
444 | |||
445 | ret = max17048_initialize(chip); | ||
446 | if (ret < 0) { | ||
447 | dev_err(&client->dev, "Error: Initializing fuel-gauge\n"); | ||
448 | goto error2; | ||
449 | } | ||
450 | |||
451 | ret = register_callback(max17048_battery_status, chip); | ||
452 | if (ret < 0) | ||
453 | goto error2; | ||
454 | |||
455 | chip->battery.name = "battery"; | ||
456 | chip->battery.type = POWER_SUPPLY_TYPE_BATTERY; | ||
457 | chip->battery.get_property = max17048_get_property; | ||
458 | chip->battery.properties = max17048_battery_props; | ||
459 | chip->battery.num_properties = ARRAY_SIZE(max17048_battery_props); | ||
460 | |||
461 | ret = power_supply_register(&client->dev, &chip->battery); | ||
462 | if (ret) { | ||
463 | dev_err(&client->dev, "failed: power supply register\n"); | ||
464 | goto error2; | ||
465 | } | ||
466 | |||
467 | chip->ac.name = "maxim-ac"; | ||
468 | chip->ac.type = POWER_SUPPLY_TYPE_MAINS; | ||
469 | chip->ac.get_property = max17048_ac_get_property; | ||
470 | chip->ac.properties = max17048_ac_props; | ||
471 | chip->ac.num_properties = ARRAY_SIZE(max17048_ac_props); | ||
472 | |||
473 | ret = power_supply_register(&client->dev, &chip->ac); | ||
474 | if (ret) { | ||
475 | dev_err(&client->dev, "failed: power supply register\n"); | ||
476 | goto error1; | ||
477 | } | ||
478 | |||
479 | chip->usb.name = "maxim-usb"; | ||
480 | chip->usb.type = POWER_SUPPLY_TYPE_USB; | ||
481 | chip->usb.get_property = max17048_usb_get_property; | ||
482 | chip->usb.properties = max17048_usb_props; | ||
483 | chip->usb.num_properties = ARRAY_SIZE(max17048_usb_props); | ||
484 | |||
485 | ret = power_supply_register(&client->dev, &chip->usb); | ||
486 | if (ret) { | ||
487 | dev_err(&client->dev, "failed: power supply register\n"); | ||
488 | goto error; | ||
489 | } | ||
490 | |||
491 | INIT_DELAYED_WORK_DEFERRABLE(&chip->work, max17048_work); | ||
492 | schedule_delayed_work(&chip->work, MAX17048_DELAY); | ||
493 | |||
494 | ret = update_charger_status(); | ||
495 | if (ret) { | ||
496 | dev_err(&client->dev, "failed: update_charger_status\n"); | ||
497 | goto error; | ||
498 | } | ||
499 | |||
500 | return 0; | ||
501 | error: | ||
502 | power_supply_unregister(&chip->ac); | ||
503 | error1: | ||
504 | power_supply_unregister(&chip->battery); | ||
505 | error2: | ||
506 | kfree(chip); | ||
507 | return ret; | ||
508 | } | ||
509 | |||
510 | static int __devexit max17048_remove(struct i2c_client *client) | ||
511 | { | ||
512 | struct max17048_chip *chip = i2c_get_clientdata(client); | ||
513 | |||
514 | power_supply_unregister(&chip->battery); | ||
515 | power_supply_unregister(&chip->usb); | ||
516 | power_supply_unregister(&chip->ac); | ||
517 | cancel_delayed_work(&chip->work); | ||
518 | kfree(chip); | ||
519 | return 0; | ||
520 | } | ||
521 | |||
522 | #ifdef CONFIG_PM | ||
523 | |||
524 | static int max17048_suspend(struct i2c_client *client, | ||
525 | pm_message_t state) | ||
526 | { | ||
527 | struct max17048_chip *chip = i2c_get_clientdata(client); | ||
528 | |||
529 | cancel_delayed_work(&chip->work); | ||
530 | return 0; | ||
531 | } | ||
532 | |||
533 | static int max17048_resume(struct i2c_client *client) | ||
534 | { | ||
535 | struct max17048_chip *chip = i2c_get_clientdata(client); | ||
536 | |||
537 | schedule_delayed_work(&chip->work, MAX17048_DELAY); | ||
538 | return 0; | ||
539 | } | ||
540 | |||
541 | #else | ||
542 | |||
543 | #define max17048_suspend NULL | ||
544 | #define max17048_resume NULL | ||
545 | |||
546 | #endif /* CONFIG_PM */ | ||
547 | |||
548 | static const struct i2c_device_id max17048_id[] = { | ||
549 | { "max17048", 0 }, | ||
550 | { } | ||
551 | }; | ||
552 | MODULE_DEVICE_TABLE(i2c, max17048_id); | ||
553 | |||
554 | static struct i2c_driver max17048_i2c_driver = { | ||
555 | .driver = { | ||
556 | .name = "max17048", | ||
557 | }, | ||
558 | .probe = max17048_probe, | ||
559 | .remove = __devexit_p(max17048_remove), | ||
560 | .suspend = max17048_suspend, | ||
561 | .resume = max17048_resume, | ||
562 | .id_table = max17048_id, | ||
563 | }; | ||
564 | |||
565 | static int __init max17048_init(void) | ||
566 | { | ||
567 | return i2c_add_driver(&max17048_i2c_driver); | ||
568 | } | ||
569 | module_init(max17048_init); | ||
570 | |||
571 | static void __exit max17048_exit(void) | ||
572 | { | ||
573 | i2c_del_driver(&max17048_i2c_driver); | ||
574 | } | ||
575 | module_exit(max17048_exit); | ||
576 | |||
577 | MODULE_AUTHOR("Chandler Zhang <chazhang@nvidia.com>"); | ||
578 | MODULE_DESCRIPTION("MAX17048 Fuel Gauge"); | ||
579 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/power/max8907c-charger.c b/drivers/power/max8907c-charger.c new file mode 100644 index 00000000000..64855c589b1 --- /dev/null +++ b/drivers/power/max8907c-charger.c | |||
@@ -0,0 +1,228 @@ | |||
1 | /* | ||
2 | * Battery driver for Maxim MAX8907C | ||
3 | * | ||
4 | * Copyright (c) 2011, NVIDIA Corporation. | ||
5 | * Copyright (C) 2010 Gyungoh Yoo <jack.yoo@maxim-ic.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #include <linux/module.h> | ||
13 | #include <linux/err.h> | ||
14 | #include <linux/interrupt.h> | ||
15 | #include <linux/platform_device.h> | ||
16 | #include <linux/power_supply.h> | ||
17 | #include <linux/mfd/max8907c.h> | ||
18 | #include <linux/power/max8907c-charger.h> | ||
19 | #include <linux/slab.h> | ||
20 | |||
21 | struct max8907c_charger { | ||
22 | struct max8907c_charger_pdata *pdata; | ||
23 | struct max8907c *chip; | ||
24 | struct i2c_client *i2c; | ||
25 | int online; | ||
26 | }; | ||
27 | |||
28 | static void max8907c_set_charger(struct max8907c_charger *charger) | ||
29 | { | ||
30 | struct max8907c_charger_pdata *pdata = charger->pdata; | ||
31 | int ret; | ||
32 | if (charger->online) { | ||
33 | ret = max8907c_reg_write(charger->i2c, MAX8907C_REG_CHG_CNTL1, | ||
34 | (pdata->topoff_threshold << 5) | | ||
35 | (pdata->restart_hysteresis << 3) | | ||
36 | (pdata->fast_charging_current)); | ||
37 | if (unlikely(ret != 0)) | ||
38 | pr_err("Failed to set CHG_CNTL1: %d\n", ret); | ||
39 | |||
40 | ret = max8907c_set_bits(charger->i2c, MAX8907C_REG_CHG_CNTL2, | ||
41 | 0x30, pdata->fast_charger_time << 4); | ||
42 | if (unlikely(ret != 0)) | ||
43 | pr_err("Failed to set CHG_CNTL2: %d\n", ret); | ||
44 | } else { | ||
45 | ret = max8907c_set_bits(charger->i2c, MAX8907C_REG_CHG_CNTL1, 0x80, 0x1); | ||
46 | if (unlikely(ret != 0)) | ||
47 | pr_err("Failed to set CHG_CNTL1: %d\n", ret); | ||
48 | } | ||
49 | } | ||
50 | |||
51 | static irqreturn_t max8907c_charger_isr(int irq, void *dev_id) | ||
52 | { | ||
53 | struct max8907c_charger *charger = dev_id; | ||
54 | struct max8907c *chip = charger->chip; | ||
55 | |||
56 | switch (irq - chip->irq_base) { | ||
57 | case MAX8907C_IRQ_VCHG_DC_R: | ||
58 | charger->online = 1; | ||
59 | max8907c_set_charger(charger); | ||
60 | break; | ||
61 | case MAX8907C_IRQ_VCHG_DC_F: | ||
62 | charger->online = 0; | ||
63 | max8907c_set_charger(charger); | ||
64 | break; | ||
65 | } | ||
66 | |||
67 | return IRQ_HANDLED; | ||
68 | } | ||
69 | |||
70 | static int max8907c_charger_get_property(struct power_supply *psy, | ||
71 | enum power_supply_property psp, | ||
72 | union power_supply_propval *val) | ||
73 | { | ||
74 | const static int types[] = { | ||
75 | POWER_SUPPLY_CHARGE_TYPE_TRICKLE, | ||
76 | POWER_SUPPLY_CHARGE_TYPE_FAST, | ||
77 | POWER_SUPPLY_CHARGE_TYPE_FAST, | ||
78 | POWER_SUPPLY_CHARGE_TYPE_NONE, | ||
79 | }; | ||
80 | int ret = -ENODEV; | ||
81 | int status; | ||
82 | |||
83 | struct max8907c_charger *charger = dev_get_drvdata(psy->dev->parent); | ||
84 | |||
85 | switch (psp) { | ||
86 | case POWER_SUPPLY_PROP_ONLINE: | ||
87 | val->intval = charger->online; | ||
88 | ret = 0; | ||
89 | break; | ||
90 | |||
91 | case POWER_SUPPLY_PROP_STATUS: | ||
92 | /* Get charger status from CHG_EN_STAT */ | ||
93 | status = max8907c_reg_read(charger->i2c, MAX8907C_REG_CHG_STAT); | ||
94 | val->intval = ((status & 0x10) == 0x10) ? | ||
95 | POWER_SUPPLY_STATUS_CHARGING : | ||
96 | POWER_SUPPLY_STATUS_NOT_CHARGING; | ||
97 | ret = 0; | ||
98 | break; | ||
99 | |||
100 | case POWER_SUPPLY_PROP_CHARGE_TYPE: | ||
101 | /* Get charging type from CHG_MODE */ | ||
102 | status = max8907c_reg_read(charger->i2c, MAX8907C_REG_CHG_STAT); | ||
103 | val->intval = types[(status & 0x0C) >> 2]; | ||
104 | ret = 0; | ||
105 | break; | ||
106 | |||
107 | default: | ||
108 | val->intval = 0; | ||
109 | ret = -EINVAL; | ||
110 | break; | ||
111 | } | ||
112 | return ret; | ||
113 | } | ||
114 | |||
115 | static enum power_supply_property max8907c_charger_props[] = { | ||
116 | POWER_SUPPLY_PROP_ONLINE, | ||
117 | POWER_SUPPLY_PROP_STATUS, | ||
118 | POWER_SUPPLY_PROP_CHARGE_TYPE, | ||
119 | }; | ||
120 | |||
121 | static struct power_supply max8907c_charger_ps = { | ||
122 | .name = "charger", | ||
123 | .type = POWER_SUPPLY_TYPE_MAINS, | ||
124 | .properties = max8907c_charger_props, | ||
125 | .num_properties = ARRAY_SIZE(max8907c_charger_props), | ||
126 | .get_property = max8907c_charger_get_property, | ||
127 | }; | ||
128 | |||
129 | static __devinit int max8907c_charger_probe(struct platform_device *pdev) | ||
130 | { | ||
131 | struct max8907c_charger_pdata *pdata = pdev->dev.platform_data; | ||
132 | struct max8907c_charger *charger = 0; | ||
133 | struct max8907c *chip = dev_get_drvdata(pdev->dev.parent); | ||
134 | int ret; | ||
135 | |||
136 | charger = kzalloc(sizeof(*charger), GFP_KERNEL); | ||
137 | if (!charger) | ||
138 | return -ENOMEM; | ||
139 | |||
140 | charger->pdata = pdata; | ||
141 | charger->online = 0; | ||
142 | charger->chip = chip; | ||
143 | charger->i2c = chip->i2c_power; | ||
144 | |||
145 | platform_set_drvdata(pdev, charger); | ||
146 | |||
147 | ret = max8907c_reg_read(charger->i2c, MAX8907C_REG_CHG_STAT); | ||
148 | if (ret & (1 << 7)) { | ||
149 | charger->online = 1; | ||
150 | max8907c_set_charger(charger); | ||
151 | } | ||
152 | |||
153 | ret = request_threaded_irq(chip->irq_base + MAX8907C_IRQ_VCHG_DC_F, NULL, | ||
154 | max8907c_charger_isr, IRQF_ONESHOT, | ||
155 | "power-remove", charger); | ||
156 | if (unlikely(ret < 0)) { | ||
157 | pr_debug("max8907c: failed to request IRQ %X\n", ret); | ||
158 | goto out; | ||
159 | } | ||
160 | |||
161 | ret = request_threaded_irq(chip->irq_base + MAX8907C_IRQ_VCHG_DC_R, NULL, | ||
162 | max8907c_charger_isr, IRQF_ONESHOT, | ||
163 | "power-insert", charger); | ||
164 | if (unlikely(ret < 0)) { | ||
165 | pr_debug("max8907c: failed to request IRQ %X\n", ret); | ||
166 | goto out1; | ||
167 | } | ||
168 | |||
169 | |||
170 | ret = power_supply_register(&pdev->dev, &max8907c_charger_ps); | ||
171 | if (unlikely(ret != 0)) { | ||
172 | pr_err("Failed to register max8907c_charger driver: %d\n", ret); | ||
173 | goto out2; | ||
174 | } | ||
175 | |||
176 | return 0; | ||
177 | out2: | ||
178 | free_irq(chip->irq_base + MAX8907C_IRQ_VCHG_DC_R, charger); | ||
179 | out1: | ||
180 | free_irq(chip->irq_base + MAX8907C_IRQ_VCHG_DC_F, charger); | ||
181 | out: | ||
182 | kfree(charger); | ||
183 | return ret; | ||
184 | } | ||
185 | |||
186 | static __devexit int max8907c_charger_remove(struct platform_device *pdev) | ||
187 | { | ||
188 | struct max8907c_charger *charger = platform_get_drvdata(pdev); | ||
189 | struct max8907c *chip = charger->chip; | ||
190 | int ret; | ||
191 | |||
192 | ret = max8907c_reg_write(charger->i2c, MAX8907C_REG_CHG_IRQ1_MASK, 0xFF); | ||
193 | if (unlikely(ret != 0)) { | ||
194 | pr_err("Failed to set IRQ1_MASK: %d\n", ret); | ||
195 | goto out; | ||
196 | } | ||
197 | |||
198 | free_irq(chip->irq_base + MAX8907C_IRQ_VCHG_DC_R, charger); | ||
199 | free_irq(chip->irq_base + MAX8907C_IRQ_VCHG_DC_F, charger); | ||
200 | power_supply_unregister(&max8907c_charger_ps); | ||
201 | out: | ||
202 | kfree(charger); | ||
203 | return 0; | ||
204 | } | ||
205 | |||
206 | static struct platform_driver max8907c_charger_driver = { | ||
207 | .probe = max8907c_charger_probe, | ||
208 | .remove = __devexit_p(max8907c_charger_remove), | ||
209 | .driver = { | ||
210 | .name = "max8907c-charger", | ||
211 | }, | ||
212 | }; | ||
213 | |||
214 | static int __init max8907c_charger_init(void) | ||
215 | { | ||
216 | return platform_driver_register(&max8907c_charger_driver); | ||
217 | } | ||
218 | module_init(max8907c_charger_init); | ||
219 | |||
220 | static void __exit max8907c_charger_exit(void) | ||
221 | { | ||
222 | platform_driver_unregister(&max8907c_charger_driver); | ||
223 | } | ||
224 | module_exit(max8907c_charger_exit); | ||
225 | |||
226 | MODULE_DESCRIPTION("Charger driver for MAX8907C"); | ||
227 | MODULE_AUTHOR("Gyungoh Yoo <jack.yoo@maxim-ic.com>"); | ||
228 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/power/smb349-charger.c b/drivers/power/smb349-charger.c new file mode 100644 index 00000000000..1f230baadcb --- /dev/null +++ b/drivers/power/smb349-charger.c | |||
@@ -0,0 +1,588 @@ | |||
1 | /* | ||
2 | * drivers/power/smb349-charger.c | ||
3 | * | ||
4 | * Battery charger driver for smb349 from summit microelectronics | ||
5 | * | ||
6 | * Copyright (c) 2012, NVIDIA Corporation. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
15 | * more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License along | ||
18 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
20 | */ | ||
21 | |||
22 | #include <linux/module.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/platform_device.h> | ||
25 | #include <linux/mutex.h> | ||
26 | #include <linux/err.h> | ||
27 | #include <linux/i2c.h> | ||
28 | #include <linux/delay.h> | ||
29 | #include <linux/power_supply.h> | ||
30 | #include <linux/platform_device.h> | ||
31 | #include <linux/regulator/driver.h> | ||
32 | #include <linux/regulator/machine.h> | ||
33 | #include <linux/smb349-charger.h> | ||
34 | #include <linux/slab.h> | ||
35 | #include <linux/gpio.h> | ||
36 | #include <linux/interrupt.h> | ||
37 | #include <linux/irq.h> | ||
38 | #include <linux/usb/otg.h> | ||
39 | |||
40 | #define SMB349_CHARGE 0x00 | ||
41 | #define SMB349_CHRG_CRNTS 0x01 | ||
42 | #define SMB349_VRS_FUNC 0x02 | ||
43 | #define SMB349_FLOAT_VLTG 0x03 | ||
44 | #define SMB349_CHRG_CTRL 0x04 | ||
45 | #define SMB349_STAT_TIME_CTRL 0x05 | ||
46 | #define SMB349_PIN_CTRL 0x06 | ||
47 | #define SMB349_THERM_CTRL 0x07 | ||
48 | #define SMB349_CTRL_REG 0x09 | ||
49 | |||
50 | #define SMB349_OTG_TLIM_REG 0x0A | ||
51 | #define SMB349_HRD_SFT_TEMP 0x0B | ||
52 | #define SMB349_FAULT_INTR 0x0C | ||
53 | #define SMB349_STS_INTR_1 0x0D | ||
54 | #define SMB349_SYSOK_USB3 0x0E | ||
55 | #define SMB349_IN_CLTG_DET 0x10 | ||
56 | #define SMB349_STS_INTR_2 0x11 | ||
57 | |||
58 | #define SMB349_CMD_REG 0x30 | ||
59 | #define SMB349_CMD_REG_B 0x31 | ||
60 | #define SMB349_CMD_REG_c 0x33 | ||
61 | |||
62 | #define SMB349_INTR_STS_A 0x35 | ||
63 | #define SMB349_INTR_STS_B 0x36 | ||
64 | #define SMB349_INTR_STS_C 0x37 | ||
65 | #define SMB349_INTR_STS_D 0x38 | ||
66 | #define SMB349_INTR_STS_E 0x39 | ||
67 | #define SMB349_INTR_STS_F 0x3A | ||
68 | |||
69 | #define SMB349_STS_REG_A 0x3B | ||
70 | #define SMB349_STS_REG_B 0x3C | ||
71 | #define SMB349_STS_REG_C 0x3D | ||
72 | #define SMB349_STS_REG_D 0x3E | ||
73 | #define SMB349_STS_REG_E 0x3F | ||
74 | |||
75 | #define SMB349_ENABLE_WRITE 1 | ||
76 | #define SMB349_DISABLE_WRITE 0 | ||
77 | #define ENABLE_WRT_ACCESS 0x80 | ||
78 | #define THERM_CTRL 0x10 | ||
79 | #define BATTERY_MISSING 0x10 | ||
80 | #define CHARGING 0x06 | ||
81 | #define DEDICATED_CHARGER 0x04 | ||
82 | #define CHRG_DOWNSTRM_PORT 0x08 | ||
83 | #define ENABLE_CHARGE 0x02 | ||
84 | |||
85 | static struct smb349_charger *charger; | ||
86 | static int smb349_configure_charger(struct i2c_client *client, int value); | ||
87 | static int smb349_configure_interrupts(struct i2c_client *client); | ||
88 | |||
89 | static int smb349_read(struct i2c_client *client, int reg) | ||
90 | { | ||
91 | int ret; | ||
92 | |||
93 | ret = i2c_smbus_read_byte_data(client, reg); | ||
94 | |||
95 | if (ret < 0) | ||
96 | dev_err(&client->dev, "%s: err %d\n", __func__, ret); | ||
97 | |||
98 | return ret; | ||
99 | } | ||
100 | |||
101 | static int smb349_write(struct i2c_client *client, int reg, u8 value) | ||
102 | { | ||
103 | int ret; | ||
104 | |||
105 | ret = i2c_smbus_write_byte_data(client, reg, value); | ||
106 | |||
107 | if (ret < 0) | ||
108 | dev_err(&client->dev, "%s: err %d\n", __func__, ret); | ||
109 | |||
110 | return ret; | ||
111 | } | ||
112 | |||
113 | static int smb349_update_reg(struct i2c_client *client, int reg, u8 value) | ||
114 | { | ||
115 | int ret, retval; | ||
116 | |||
117 | retval = smb349_read(client, reg); | ||
118 | if (retval < 0) { | ||
119 | dev_err(&client->dev, "%s: err %d\n", __func__, retval); | ||
120 | return retval; | ||
121 | } | ||
122 | |||
123 | ret = smb349_write(client, reg, retval | value); | ||
124 | if (ret < 0) { | ||
125 | dev_err(&client->dev, "%s: err %d\n", __func__, ret); | ||
126 | return ret; | ||
127 | } | ||
128 | |||
129 | return ret; | ||
130 | } | ||
131 | |||
132 | int smb349_volatile_writes(struct i2c_client *client, uint8_t value) | ||
133 | { | ||
134 | int ret = 0; | ||
135 | |||
136 | if (value == SMB349_ENABLE_WRITE) { | ||
137 | /* Enable volatile write to config registers */ | ||
138 | ret = smb349_update_reg(client, SMB349_CMD_REG, | ||
139 | ENABLE_WRT_ACCESS); | ||
140 | if (ret < 0) { | ||
141 | dev_err(&client->dev, "%s(): Failed in writing" | ||
142 | "register 0x%02x\n", __func__, SMB349_CMD_REG); | ||
143 | return ret; | ||
144 | } | ||
145 | } else { | ||
146 | ret = smb349_read(client, SMB349_CMD_REG); | ||
147 | if (ret < 0) { | ||
148 | dev_err(&client->dev, "%s: err %d\n", __func__, ret); | ||
149 | return ret; | ||
150 | } | ||
151 | |||
152 | ret = smb349_write(client, SMB349_CMD_REG, ret & (~(1<<7))); | ||
153 | if (ret < 0) { | ||
154 | dev_err(&client->dev, "%s: err %d\n", __func__, ret); | ||
155 | return ret; | ||
156 | } | ||
157 | } | ||
158 | return ret; | ||
159 | } | ||
160 | |||
161 | static void smb349_clear_interrupts(struct i2c_client *client) | ||
162 | { | ||
163 | uint8_t val, buf[6]; | ||
164 | |||
165 | val = i2c_smbus_read_i2c_block_data(client, SMB349_INTR_STS_A, 6, buf); | ||
166 | if (val < 0) | ||
167 | dev_err(&client->dev, "%s(): Failed in clearing interrupts\n", | ||
168 | __func__); | ||
169 | } | ||
170 | |||
171 | static int smb349_configure_otg(struct i2c_client *client, int enable) | ||
172 | { | ||
173 | int ret = 0; | ||
174 | |||
175 | /*Enable volatile writes to registers*/ | ||
176 | ret = smb349_volatile_writes(client, SMB349_ENABLE_WRITE); | ||
177 | if (ret < 0) { | ||
178 | dev_err(&client->dev, "%s error in configuring otg..\n", | ||
179 | __func__); | ||
180 | goto error; | ||
181 | } | ||
182 | |||
183 | if (enable) { | ||
184 | /* Configure PGOOD to be active low */ | ||
185 | ret = smb349_read(client, SMB349_SYSOK_USB3); | ||
186 | if (ret < 0) { | ||
187 | dev_err(&client->dev, "%s: err %d\n", __func__, ret); | ||
188 | goto error; | ||
189 | } | ||
190 | |||
191 | ret = smb349_write(client, SMB349_SYSOK_USB3, (ret & (~(1)))); | ||
192 | if (ret < 0) { | ||
193 | dev_err(&client->dev, "%s: err %d\n", __func__, ret); | ||
194 | goto error; | ||
195 | } | ||
196 | |||
197 | /* Enable OTG */ | ||
198 | ret = smb349_update_reg(client, SMB349_CMD_REG, 0x10); | ||
199 | if (ret < 0) { | ||
200 | dev_err(&client->dev, "%s: Failed in writing register" | ||
201 | "0x%02x\n", __func__, SMB349_CMD_REG); | ||
202 | goto error; | ||
203 | } | ||
204 | } else { | ||
205 | /* Disable OTG */ | ||
206 | ret = smb349_read(client, SMB349_CMD_REG); | ||
207 | if (ret < 0) { | ||
208 | dev_err(&client->dev, "%s: err %d\n", __func__, ret); | ||
209 | goto error; | ||
210 | } | ||
211 | |||
212 | ret = smb349_write(client, SMB349_CMD_REG, (ret & (~(1<<4)))); | ||
213 | if (ret < 0) { | ||
214 | dev_err(&client->dev, "%s: err %d\n", __func__, ret); | ||
215 | goto error; | ||
216 | } | ||
217 | |||
218 | /* Configure PGOOD to be active high */ | ||
219 | ret = smb349_update_reg(client, SMB349_SYSOK_USB3, 0x01); | ||
220 | if (ret < 0) { | ||
221 | dev_err(&client->dev, "%s: err %d\n", __func__, ret); | ||
222 | goto error; | ||
223 | } | ||
224 | } | ||
225 | |||
226 | /* Disable volatile writes to registers */ | ||
227 | ret = smb349_volatile_writes(client, SMB349_DISABLE_WRITE); | ||
228 | if (ret < 0) | ||
229 | dev_err(&client->dev, "%s error in configuring OTG..\n", | ||
230 | __func__); | ||
231 | error: | ||
232 | return ret; | ||
233 | } | ||
234 | |||
235 | static int smb349_configure_charger(struct i2c_client *client, int value) | ||
236 | { | ||
237 | int ret = 0; | ||
238 | |||
239 | /* Enable volatile writes to registers */ | ||
240 | ret = smb349_volatile_writes(client, SMB349_ENABLE_WRITE); | ||
241 | if (ret < 0) { | ||
242 | dev_err(&client->dev, "%s() error in configuring charger..\n", | ||
243 | __func__); | ||
244 | goto error; | ||
245 | } | ||
246 | |||
247 | if (value) { | ||
248 | /* Enable charging */ | ||
249 | ret = smb349_update_reg(client, SMB349_CMD_REG, ENABLE_CHARGE); | ||
250 | if (ret < 0) { | ||
251 | dev_err(&client->dev, "%s(): Failed in writing register" | ||
252 | "0x%02x\n", __func__, SMB349_CMD_REG); | ||
253 | goto error; | ||
254 | } | ||
255 | |||
256 | /* Configure THERM ctrl */ | ||
257 | ret = smb349_update_reg(client, SMB349_THERM_CTRL, THERM_CTRL); | ||
258 | if (ret < 0) { | ||
259 | dev_err(&client->dev, "%s: err %d\n", __func__, ret); | ||
260 | goto error; | ||
261 | } | ||
262 | } else { | ||
263 | ret = smb349_read(client, SMB349_CMD_REG); | ||
264 | if (ret < 0) { | ||
265 | dev_err(&client->dev, "%s: err %d\n", __func__, ret); | ||
266 | goto error; | ||
267 | } | ||
268 | |||
269 | ret = smb349_write(client, SMB349_CMD_REG, (ret & (~(1<<1)))); | ||
270 | if (ret < 0) { | ||
271 | dev_err(&client->dev, "%s: err %d\n", __func__, ret); | ||
272 | goto error; | ||
273 | } | ||
274 | } | ||
275 | /* Disable volatile writes to registers */ | ||
276 | ret = smb349_volatile_writes(client, SMB349_DISABLE_WRITE); | ||
277 | if (ret < 0) { | ||
278 | dev_err(&client->dev, "%s() error in configuring charger..\n", | ||
279 | __func__); | ||
280 | goto error; | ||
281 | } | ||
282 | error: | ||
283 | return ret; | ||
284 | } | ||
285 | |||
286 | static irqreturn_t smb349_status_isr(int irq, void *dev_id) | ||
287 | { | ||
288 | struct i2c_client *client = charger->client; | ||
289 | int ret, val; | ||
290 | |||
291 | val = smb349_read(client, SMB349_STS_REG_D); | ||
292 | if (val < 0) { | ||
293 | dev_err(&client->dev, "%s(): Failed in reading register" | ||
294 | "0x%02x\n", __func__, SMB349_STS_REG_D); | ||
295 | goto irq_error; | ||
296 | } else if (val != 0) { | ||
297 | if (val & DEDICATED_CHARGER) | ||
298 | charger->chrg_type = AC; | ||
299 | else if (val & CHRG_DOWNSTRM_PORT) | ||
300 | charger->chrg_type = USB; | ||
301 | |||
302 | /* configure charger */ | ||
303 | ret = smb349_configure_charger(client, 1); | ||
304 | if (ret < 0) { | ||
305 | dev_err(&client->dev, "%s() error in configuring" | ||
306 | "charger..\n", __func__); | ||
307 | goto irq_error; | ||
308 | } | ||
309 | |||
310 | charger->state = progress; | ||
311 | } else { | ||
312 | charger->state = stopped; | ||
313 | |||
314 | /* Disable charger */ | ||
315 | ret = smb349_configure_charger(client, 0); | ||
316 | if (ret < 0) { | ||
317 | dev_err(&client->dev, "%s() error in configuring" | ||
318 | "charger..\n", __func__); | ||
319 | goto irq_error; | ||
320 | } | ||
321 | |||
322 | ret = smb349_configure_interrupts(client); | ||
323 | if (ret < 0) { | ||
324 | dev_err(&client->dev, "%s() error in configuring" | ||
325 | "charger..\n", __func__); | ||
326 | goto irq_error; | ||
327 | } | ||
328 | |||
329 | } | ||
330 | |||
331 | if (charger->charger_cb) | ||
332 | charger->charger_cb(charger->state, charger->chrg_type, | ||
333 | charger->charger_cb_data); | ||
334 | irq_error: | ||
335 | smb349_clear_interrupts(client); | ||
336 | return IRQ_HANDLED; | ||
337 | } | ||
338 | |||
339 | int update_charger_status(void) | ||
340 | { | ||
341 | struct i2c_client *client = charger->client; | ||
342 | int ret, val; | ||
343 | |||
344 | val = smb349_read(client, SMB349_STS_REG_D); | ||
345 | if (val < 0) { | ||
346 | dev_err(&client->dev, "%s(): Failed in reading register" | ||
347 | "0x%02x\n", __func__, SMB349_STS_REG_D); | ||
348 | goto val_error; | ||
349 | } else if (val != 0) { | ||
350 | if (val & DEDICATED_CHARGER) | ||
351 | charger->chrg_type = AC; | ||
352 | else if (val & CHRG_DOWNSTRM_PORT) | ||
353 | charger->chrg_type = USB; | ||
354 | |||
355 | /* configure charger */ | ||
356 | ret = smb349_configure_charger(client, 1); | ||
357 | if (ret < 0) { | ||
358 | dev_err(&client->dev, "%s() error in configuring" | ||
359 | "charger..\n", __func__); | ||
360 | goto ret_error; | ||
361 | } | ||
362 | |||
363 | charger->state = progress; | ||
364 | } else { | ||
365 | charger->state = stopped; | ||
366 | |||
367 | /* Disable charger */ | ||
368 | ret = smb349_configure_charger(client, 0); | ||
369 | if (ret < 0) { | ||
370 | dev_err(&client->dev, "%s() error in configuring" | ||
371 | "charger..\n", __func__); | ||
372 | goto ret_error; | ||
373 | } | ||
374 | } | ||
375 | |||
376 | if (charger->charger_cb) | ||
377 | charger->charger_cb(charger->state, charger->chrg_type, | ||
378 | charger->charger_cb_data); | ||
379 | return 0; | ||
380 | val_error: | ||
381 | return val; | ||
382 | ret_error: | ||
383 | return ret; | ||
384 | } | ||
385 | EXPORT_SYMBOL_GPL(update_charger_status); | ||
386 | |||
387 | int register_callback(charging_callback_t cb, void *args) | ||
388 | { | ||
389 | struct smb349_charger *charger_data = charger; | ||
390 | if (!charger_data) | ||
391 | return -ENODEV; | ||
392 | |||
393 | charger_data->charger_cb = cb; | ||
394 | charger_data->charger_cb_data = args; | ||
395 | return 0; | ||
396 | } | ||
397 | EXPORT_SYMBOL_GPL(register_callback); | ||
398 | |||
399 | int smb349_battery_online(void) | ||
400 | { | ||
401 | int val; | ||
402 | struct i2c_client *client = charger->client; | ||
403 | |||
404 | val = smb349_read(charger->client, SMB349_INTR_STS_B); | ||
405 | if (val < 0) { | ||
406 | dev_err(&client->dev, "%s(): Failed in reading register" | ||
407 | "0x%02x\n", __func__, SMB349_INTR_STS_B); | ||
408 | return val; | ||
409 | } | ||
410 | if (val & BATTERY_MISSING) | ||
411 | return 0; | ||
412 | else | ||
413 | return 1; | ||
414 | } | ||
415 | |||
416 | static int smb349_configure_interrupts(struct i2c_client *client) | ||
417 | { | ||
418 | int ret = 0; | ||
419 | |||
420 | /* Enable volatile writes to registers */ | ||
421 | ret = smb349_volatile_writes(client, SMB349_ENABLE_WRITE); | ||
422 | if (ret < 0) { | ||
423 | dev_err(&client->dev, "%s() error in configuring charger..\n", | ||
424 | __func__); | ||
425 | goto error; | ||
426 | } | ||
427 | |||
428 | ret = smb349_update_reg(client, SMB349_FAULT_INTR, 0xff); | ||
429 | if (ret < 0) { | ||
430 | dev_err(&client->dev, "%s(): Failed in writing register" | ||
431 | "0x%02x\n", __func__, SMB349_CMD_REG); | ||
432 | goto error; | ||
433 | } | ||
434 | |||
435 | ret = smb349_update_reg(client, SMB349_STS_INTR_1, 0xff); | ||
436 | if (ret < 0) { | ||
437 | dev_err(&client->dev, "%s: err %d\n", __func__, ret); | ||
438 | goto error; | ||
439 | } | ||
440 | |||
441 | /* Disable volatile writes to registers */ | ||
442 | ret = smb349_volatile_writes(client, SMB349_DISABLE_WRITE); | ||
443 | if (ret < 0) { | ||
444 | dev_err(&client->dev, "%s() error in configuring charger..\n", | ||
445 | __func__); | ||
446 | goto error; | ||
447 | } | ||
448 | |||
449 | error: | ||
450 | return ret; | ||
451 | } | ||
452 | |||
453 | static void smb349_otg_status(enum usb_otg_state to, enum usb_otg_state from, void *data) | ||
454 | { | ||
455 | struct i2c_client *client = charger->client; | ||
456 | int ret; | ||
457 | |||
458 | if ((from == OTG_STATE_A_SUSPEND) && (to == OTG_STATE_A_HOST)) { | ||
459 | |||
460 | /* configure charger */ | ||
461 | ret = smb349_configure_charger(client, 0); | ||
462 | if (ret < 0) | ||
463 | dev_err(&client->dev, "%s() error in configuring" | ||
464 | "otg..\n", __func__); | ||
465 | |||
466 | /* ENABLE OTG */ | ||
467 | ret = smb349_configure_otg(client, 1); | ||
468 | if (ret < 0) | ||
469 | dev_err(&client->dev, "%s() error in configuring" | ||
470 | "otg..\n", __func__); | ||
471 | |||
472 | } else if ((from == OTG_STATE_A_HOST) && (to == OTG_STATE_A_SUSPEND)) { | ||
473 | |||
474 | /* Disable OTG */ | ||
475 | ret = smb349_configure_otg(client, 0); | ||
476 | if (ret < 0) | ||
477 | dev_err(&client->dev, "%s() error in configuring" | ||
478 | "otg..\n", __func__); | ||
479 | |||
480 | /* configure charger */ | ||
481 | ret = smb349_configure_charger(client, 1); | ||
482 | if (ret < 0) | ||
483 | dev_err(&client->dev, "%s() error in configuring" | ||
484 | "otg..\n", __func__); | ||
485 | |||
486 | ret = smb349_configure_interrupts(client); | ||
487 | if (ret < 0) | ||
488 | dev_err(&client->dev, "%s() error in configuring" | ||
489 | "otg..\n", __func__); | ||
490 | } | ||
491 | } | ||
492 | |||
493 | static int __devinit smb349_probe(struct i2c_client *client, | ||
494 | const struct i2c_device_id *id) | ||
495 | { | ||
496 | struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); | ||
497 | int ret, irq_num; | ||
498 | |||
499 | if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) | ||
500 | return -EIO; | ||
501 | |||
502 | charger = kzalloc(sizeof(*charger), GFP_KERNEL); | ||
503 | if (!charger) | ||
504 | return -ENOMEM; | ||
505 | |||
506 | charger->client = client; | ||
507 | charger->dev = &client->dev; | ||
508 | i2c_set_clientdata(client, charger); | ||
509 | |||
510 | /* Check battery presence */ | ||
511 | if (!smb349_battery_online()) { | ||
512 | dev_err(&client->dev, "%s() No Battery present, exiting..\n", | ||
513 | __func__); | ||
514 | ret = -ENODEV; | ||
515 | goto error; | ||
516 | } | ||
517 | |||
518 | ret = register_otg_callback(smb349_otg_status, charger); | ||
519 | if (ret < 0) | ||
520 | goto error; | ||
521 | |||
522 | ret = smb349_configure_charger(client, 1); | ||
523 | if (ret < 0) | ||
524 | return ret; | ||
525 | |||
526 | ret = smb349_configure_interrupts(client); | ||
527 | if (ret < 0) { | ||
528 | dev_err(&client->dev, "%s() error in configuring charger..\n", | ||
529 | __func__); | ||
530 | goto error; | ||
531 | } | ||
532 | |||
533 | irq_num = gpio_to_irq(client->irq); | ||
534 | ret = request_threaded_irq(irq_num, | ||
535 | NULL, smb349_status_isr, IRQ_TYPE_EDGE_FALLING, | ||
536 | "smb349", charger); | ||
537 | if (ret) { | ||
538 | dev_err(&client->dev, "%s(): Failed in requesting isr\n", | ||
539 | __func__); | ||
540 | goto error; | ||
541 | } | ||
542 | |||
543 | return 0; | ||
544 | error: | ||
545 | kfree(charger); | ||
546 | return ret; | ||
547 | } | ||
548 | |||
549 | static int __devexit smb349_remove(struct i2c_client *client) | ||
550 | { | ||
551 | struct smb349_charger *charger = i2c_get_clientdata(client); | ||
552 | |||
553 | free_irq(gpio_to_irq(client->irq), charger); | ||
554 | kfree(charger); | ||
555 | |||
556 | return 0; | ||
557 | } | ||
558 | |||
559 | static const struct i2c_device_id smb349_id[] = { | ||
560 | { "smb349", 0 }, | ||
561 | { } | ||
562 | }; | ||
563 | MODULE_DEVICE_TABLE(i2c, smb349_id); | ||
564 | |||
565 | static struct i2c_driver smb349_i2c_driver = { | ||
566 | .driver = { | ||
567 | .name = "smb349", | ||
568 | }, | ||
569 | .probe = smb349_probe, | ||
570 | .remove = __devexit_p(smb349_remove), | ||
571 | .id_table = smb349_id, | ||
572 | }; | ||
573 | |||
574 | static int __init smb349_init(void) | ||
575 | { | ||
576 | return i2c_add_driver(&smb349_i2c_driver); | ||
577 | } | ||
578 | module_init(smb349_init); | ||
579 | |||
580 | static void __exit smb349_exit(void) | ||
581 | { | ||
582 | i2c_del_driver(&smb349_i2c_driver); | ||
583 | } | ||
584 | module_exit(smb349_exit); | ||
585 | |||
586 | MODULE_AUTHOR("Syed Rafiuddin <srafiuddin@nvidia.com>"); | ||
587 | MODULE_DESCRIPTION("SMB349 Battery-Charger"); | ||
588 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/power/tegra_bpc_mgmt.c b/drivers/power/tegra_bpc_mgmt.c new file mode 100644 index 00000000000..0d9ddeee282 --- /dev/null +++ b/drivers/power/tegra_bpc_mgmt.c | |||
@@ -0,0 +1,139 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2010-2011 NVIDIA Corporation | ||
3 | * | ||
4 | * This software is licensed under the terms of the GNU General Public | ||
5 | * License version 2, as published by the Free Software Foundation, and | ||
6 | * may be copied, distributed, and modified under those terms. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | * | ||
13 | */ | ||
14 | |||
15 | #include <linux/module.h> | ||
16 | #include <linux/gpio.h> | ||
17 | #include <linux/platform_device.h> | ||
18 | #include <linux/interrupt.h> | ||
19 | #include <linux/irqflags.h> | ||
20 | #include <linux/slab.h> | ||
21 | #include <linux/irq.h> | ||
22 | #include <linux/sched.h> | ||
23 | #include <linux/jiffies.h> | ||
24 | #include <linux/platform_data/tegra_bpc_mgmt.h> | ||
25 | |||
26 | #include <mach/edp.h> | ||
27 | |||
28 | static irqreturn_t tegra_bpc_mgmt_bh(int irq, void *data) | ||
29 | { | ||
30 | int gpio_val = 0; | ||
31 | struct tegra_bpc_mgmt_platform_data *bpc_platform_data; | ||
32 | bpc_platform_data = (struct tegra_bpc_mgmt_platform_data *)data; | ||
33 | |||
34 | tegra_system_edp_alarm(true); | ||
35 | /** | ||
36 | * Keep on checking whether event has passed or not. | ||
37 | */ | ||
38 | while (!gpio_val) { | ||
39 | set_current_state(TASK_INTERRUPTIBLE); | ||
40 | schedule_timeout(msecs_to_jiffies( | ||
41 | bpc_platform_data->bpc_mgmt_timeout)); | ||
42 | |||
43 | gpio_val = gpio_get_value(bpc_platform_data->gpio_trigger); | ||
44 | } | ||
45 | |||
46 | tegra_system_edp_alarm(false); | ||
47 | |||
48 | return IRQ_HANDLED; | ||
49 | } | ||
50 | |||
51 | static irqreturn_t tegra_bpc_mgmt_isr(int irq, void *data) | ||
52 | { | ||
53 | tegra_edp_throttle_cpu_now(2); | ||
54 | return IRQ_WAKE_THREAD; | ||
55 | } | ||
56 | |||
57 | static __devinit int tegra_bpc_mgmt_probe(struct platform_device *pdev) | ||
58 | { | ||
59 | u32 ret; | ||
60 | struct task_struct *bh_thread; | ||
61 | struct irq_desc *bat_desc; | ||
62 | struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 }; | ||
63 | struct tegra_bpc_mgmt_platform_data *bpc_platform_data; | ||
64 | |||
65 | bpc_platform_data = pdev->dev.platform_data; | ||
66 | if (!bpc_platform_data) | ||
67 | return -ENODEV; | ||
68 | |||
69 | if (gpio_is_valid(bpc_platform_data->gpio_trigger)) { | ||
70 | ret = gpio_request(bpc_platform_data->gpio_trigger, | ||
71 | "tegra-bpc-mgmt"); | ||
72 | |||
73 | if (ret < 0) { | ||
74 | pr_err("BPC: GPIO request failed"); | ||
75 | return -ENODEV; | ||
76 | } | ||
77 | } else { | ||
78 | pr_err("BPC: GPIO check failed, gpio %d", | ||
79 | bpc_platform_data->gpio_trigger); | ||
80 | return -ENODEV; | ||
81 | } | ||
82 | |||
83 | gpio_direction_input(bpc_platform_data->gpio_trigger); | ||
84 | |||
85 | ret = request_threaded_irq( | ||
86 | gpio_to_irq(bpc_platform_data->gpio_trigger), | ||
87 | tegra_bpc_mgmt_isr, | ||
88 | tegra_bpc_mgmt_bh, IRQF_TRIGGER_LOW | IRQF_ONESHOT, | ||
89 | "tegra-bpc-mgmt", bpc_platform_data); | ||
90 | if (ret < 0) { | ||
91 | pr_err("BPC:IRQ Installation failed\n"); | ||
92 | return -ENODEV; | ||
93 | } | ||
94 | bat_desc = irq_to_desc( | ||
95 | gpio_to_irq(bpc_platform_data->gpio_trigger)); | ||
96 | |||
97 | if (bat_desc) { | ||
98 | bh_thread = bat_desc->action->thread; | ||
99 | if (bh_thread) | ||
100 | sched_setscheduler_nocheck(bh_thread, | ||
101 | SCHED_FIFO, ¶m); | ||
102 | } | ||
103 | |||
104 | return 0; | ||
105 | } | ||
106 | |||
107 | static __devexit int tegra_bpc_mgmt_remove(struct platform_device *pdev) | ||
108 | { | ||
109 | struct tegra_bpc_mgmt_platform_data *bpc_platform_data; | ||
110 | bpc_platform_data = pdev->dev.platform_data; | ||
111 | free_irq(gpio_to_irq(bpc_platform_data->gpio_trigger), NULL); | ||
112 | return 0; | ||
113 | } | ||
114 | |||
115 | static struct platform_driver tegra_bpc_mgmt_driver = { | ||
116 | .probe = tegra_bpc_mgmt_probe, | ||
117 | .remove = tegra_bpc_mgmt_remove, | ||
118 | .driver = { | ||
119 | .name = "tegra-bpc-mgmt", | ||
120 | .owner = THIS_MODULE, | ||
121 | }, | ||
122 | }; | ||
123 | |||
124 | static int __init tegra_bpc_mgmt_init(void) | ||
125 | { | ||
126 | return platform_driver_register(&tegra_bpc_mgmt_driver); | ||
127 | } | ||
128 | |||
129 | static void __exit tegra_bpc_mgmt_exit(void) | ||
130 | { | ||
131 | platform_driver_unregister(&tegra_bpc_mgmt_driver); | ||
132 | } | ||
133 | |||
134 | module_init(tegra_bpc_mgmt_init); | ||
135 | module_exit(tegra_bpc_mgmt_exit); | ||
136 | |||
137 | MODULE_DESCRIPTION("TEGRA Battery Peak current Management"); | ||
138 | MODULE_AUTHOR("NVIDIA"); | ||
139 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/power/tps80031-charger.c b/drivers/power/tps80031-charger.c new file mode 100644 index 00000000000..93b283e0b04 --- /dev/null +++ b/drivers/power/tps80031-charger.c | |||
@@ -0,0 +1,476 @@ | |||
1 | /* | ||
2 | * drivers/power/tps80031_charger.c | ||
3 | * | ||
4 | * Battery charger driver for TI's tps80031 | ||
5 | * | ||
6 | * Copyright (c) 2011, NVIDIA Corporation. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
15 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
16 | * more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License along | ||
19 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
20 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
21 | */ | ||
22 | #include <linux/kernel.h> | ||
23 | #include <linux/delay.h> | ||
24 | #include <linux/init.h> | ||
25 | #include <linux/err.h> | ||
26 | #include <linux/slab.h> | ||
27 | #include <linux/platform_device.h> | ||
28 | #include <linux/regulator/driver.h> | ||
29 | #include <linux/regulator/machine.h> | ||
30 | #include <linux/mfd/tps80031.h> | ||
31 | #include <linux/tps80031-charger.h> | ||
32 | |||
33 | #define CONTROLLER_CTRL1 0xe1 | ||
34 | #define CONTROLLER_STAT1 0xe3 | ||
35 | #define CHARGERUSB_CTRL2 0xe9 | ||
36 | #define CHARGERUSB_CTRL3 0xea | ||
37 | #define CHARGERUSB_VOREG 0xec | ||
38 | #define CHARGERUSB_VICHRG 0xed | ||
39 | #define CHARGERUSB_CINLIMIT 0xee | ||
40 | #define CHARGERUSB_CTRLLIMIT2 0xf0 | ||
41 | #define CHARGERUSB_CTRLLIMIT1 0xef | ||
42 | #define CHARGERUSB_VICHRG_PC 0xdd | ||
43 | #define CONTROLLER_WDG 0xe2 | ||
44 | #define LINEAR_CHRG_STS 0xde | ||
45 | |||
46 | #define TPS80031_VBUS_DET BIT(2) | ||
47 | #define TPS80031_VAC_DET BIT(3) | ||
48 | |||
49 | struct tps80031_charger { | ||
50 | int max_charge_current_mA; | ||
51 | int max_charge_volt_mV; | ||
52 | struct device *dev; | ||
53 | struct regulator_dev *rdev; | ||
54 | struct regulator_desc reg_desc; | ||
55 | struct regulator_init_data reg_init_data; | ||
56 | struct tps80031_charger_platform_data *pdata; | ||
57 | int (*board_init)(void *board_data); | ||
58 | void *board_data; | ||
59 | int irq_base; | ||
60 | int watch_time_sec; | ||
61 | enum charging_states state; | ||
62 | int charging_term_current_mA; | ||
63 | charging_callback_t charger_cb; | ||
64 | void *charger_cb_data; | ||
65 | }; | ||
66 | |||
67 | static struct tps80031_charger *charger_data; | ||
68 | static uint8_t charging_current_val_code[] = { | ||
69 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0x27, | ||
70 | 0x37, 0x28, 0x38, 0x29, 0x39, 0x2A, 0x3A, 0x2B, 0x3B, 0x2C, | ||
71 | 0x3C, 0x2D, 0x3D, 0x2E, | ||
72 | }; | ||
73 | |||
74 | static int set_charge_current_limit(struct regulator_dev *rdev, | ||
75 | int min_uA, int max_uA) | ||
76 | { | ||
77 | struct tps80031_charger *charger = rdev_get_drvdata(rdev); | ||
78 | int max_vbus_current = 1500; | ||
79 | int max_charge_current = 1500; | ||
80 | int ret; | ||
81 | |||
82 | dev_info(charger->dev, "%s(): Min curr %dmA and max current %dmA\n", | ||
83 | __func__, min_uA/1000, max_uA/1000); | ||
84 | |||
85 | if (!max_uA) { | ||
86 | ret = tps80031_write(charger->dev->parent, SLAVE_ID2, | ||
87 | CONTROLLER_CTRL1, 0x0); | ||
88 | if (ret < 0) | ||
89 | dev_err(charger->dev, "%s(): Failed in writing register 0x%02x\n", | ||
90 | __func__, CONTROLLER_CTRL1); | ||
91 | |||
92 | ret = tps80031_write(charger->dev->parent, SLAVE_ID2, | ||
93 | CONTROLLER_WDG, 0x0); | ||
94 | if (ret < 0) | ||
95 | dev_err(charger->dev, "%s(): Failed in writing register 0x%02x\n", | ||
96 | __func__, CONTROLLER_WDG); | ||
97 | charger->state = charging_state_charging_stopped; | ||
98 | if (charger->charger_cb) | ||
99 | charger->charger_cb(charger->state, | ||
100 | charger->charger_cb_data); | ||
101 | return ret; | ||
102 | } | ||
103 | |||
104 | max_vbus_current = min(max_uA/1000, max_vbus_current); | ||
105 | max_vbus_current = max_vbus_current/50; | ||
106 | if (max_vbus_current) | ||
107 | max_vbus_current--; | ||
108 | ret = tps80031_update(charger->dev->parent, SLAVE_ID2, | ||
109 | CHARGERUSB_CINLIMIT, | ||
110 | charging_current_val_code[max_vbus_current], 0x3F); | ||
111 | if (ret < 0) { | ||
112 | dev_err(charger->dev, "%s(): Failed in writing register 0x%02x\n", | ||
113 | __func__, CHARGERUSB_CINLIMIT); | ||
114 | return ret; | ||
115 | } | ||
116 | |||
117 | max_charge_current = min(max_uA/1000, max_charge_current); | ||
118 | if (max_charge_current <= 300) | ||
119 | max_charge_current = 0; | ||
120 | else if ((max_charge_current > 300) && (max_charge_current <= 500)) | ||
121 | max_charge_current = (max_charge_current - 300)/50; | ||
122 | else | ||
123 | max_charge_current = (max_charge_current - 500) / 100 + 4; | ||
124 | ret = tps80031_update(charger->dev->parent, SLAVE_ID2, | ||
125 | CHARGERUSB_VICHRG, (uint8_t)max_charge_current, 0xF); | ||
126 | if (ret < 0) { | ||
127 | dev_err(charger->dev, "%s(): Failed in writing register 0x%02x\n", | ||
128 | __func__, CHARGERUSB_VICHRG); | ||
129 | return ret; | ||
130 | } | ||
131 | |||
132 | /* Enable watchdog timer */ | ||
133 | ret = tps80031_write(charger->dev->parent, SLAVE_ID2, | ||
134 | CONTROLLER_WDG, charger->watch_time_sec); | ||
135 | if (ret < 0) { | ||
136 | dev_err(charger->dev, "%s(): Failed in writing register 0x%02x\n", | ||
137 | __func__, CONTROLLER_WDG); | ||
138 | return ret; | ||
139 | } | ||
140 | |||
141 | /* Enable the charging */ | ||
142 | ret = tps80031_write(charger->dev->parent, SLAVE_ID2, | ||
143 | CONTROLLER_CTRL1, 0x30); | ||
144 | if (ret < 0) { | ||
145 | dev_err(charger->dev, "%s(): Failed in writing register 0x%02x\n", | ||
146 | __func__, CONTROLLER_CTRL1); | ||
147 | return ret; | ||
148 | } | ||
149 | charger->state = charging_state_charging_in_progress; | ||
150 | if (charger->charger_cb) | ||
151 | charger->charger_cb(charger->state, | ||
152 | charger->charger_cb_data); | ||
153 | return 0; | ||
154 | } | ||
155 | |||
156 | static struct regulator_ops tegra_regulator_ops = { | ||
157 | .set_current_limit = set_charge_current_limit, | ||
158 | }; | ||
159 | |||
160 | int register_charging_state_callback(charging_callback_t cb, void *args) | ||
161 | { | ||
162 | struct tps80031_charger *charger = charger_data; | ||
163 | if (!charger_data) | ||
164 | return -ENODEV; | ||
165 | |||
166 | charger->charger_cb = cb; | ||
167 | charger->charger_cb_data = args; | ||
168 | return 0; | ||
169 | } | ||
170 | EXPORT_SYMBOL_GPL(register_charging_state_callback); | ||
171 | |||
172 | static int configure_charging_parameter(struct tps80031_charger *charger) | ||
173 | { | ||
174 | int ret; | ||
175 | int max_charge_current; | ||
176 | int max_charge_volt; | ||
177 | int term_current; | ||
178 | |||
179 | /* Disable watchdog timer */ | ||
180 | ret = tps80031_write(charger->dev->parent, SLAVE_ID2, | ||
181 | CONTROLLER_WDG, 0x0); | ||
182 | if (ret < 0) { | ||
183 | dev_err(charger->dev, "%s(): Failed in writing register 0x%02x\n", | ||
184 | __func__, CONTROLLER_WDG); | ||
185 | return ret; | ||
186 | } | ||
187 | |||
188 | /* Disable the charging if any */ | ||
189 | ret = tps80031_write(charger->dev->parent, SLAVE_ID2, | ||
190 | CONTROLLER_CTRL1, 0x0); | ||
191 | if (ret < 0) { | ||
192 | dev_err(charger->dev, "%s(): Failed in writing register 0x%02x\n", | ||
193 | __func__, CONTROLLER_CTRL1); | ||
194 | return ret; | ||
195 | } | ||
196 | |||
197 | if (charger->board_init) { | ||
198 | ret = charger->board_init(charger->board_data); | ||
199 | if (ret < 0) { | ||
200 | dev_err(charger->dev, "%s(): Failed in board init\n", | ||
201 | __func__); | ||
202 | return ret; | ||
203 | } | ||
204 | } | ||
205 | |||
206 | /* Unlock value */ | ||
207 | ret = tps80031_write(charger->dev->parent, SLAVE_ID2, | ||
208 | CHARGERUSB_CTRLLIMIT2, 0); | ||
209 | if (ret < 0) { | ||
210 | dev_err(charger->dev, "%s(): Failed in writing register 0x%02x\n", | ||
211 | __func__, CHARGERUSB_CTRLLIMIT2); | ||
212 | return ret; | ||
213 | } | ||
214 | |||
215 | /* Set max current limit */ | ||
216 | max_charge_current = min(1500, charger->max_charge_current_mA); | ||
217 | if (max_charge_current < 100) | ||
218 | max_charge_current = 0; | ||
219 | else | ||
220 | max_charge_current = (max_charge_current - 100)/100; | ||
221 | max_charge_current &= 0xF; | ||
222 | ret = tps80031_write(charger->dev->parent, SLAVE_ID2, | ||
223 | CHARGERUSB_CTRLLIMIT2, (uint8_t)max_charge_current); | ||
224 | if (ret < 0) { | ||
225 | dev_err(charger->dev, "%s(): Failed in writing register " | ||
226 | "0x%02x\n", __func__, CHARGERUSB_CTRLLIMIT2); | ||
227 | return ret; | ||
228 | } | ||
229 | |||
230 | /* Set max voltage limit */ | ||
231 | max_charge_volt = min(4760, charger->max_charge_volt_mV); | ||
232 | max_charge_volt = max(3500, max_charge_volt); | ||
233 | max_charge_volt -= 3500; | ||
234 | max_charge_volt = max_charge_volt/20; | ||
235 | ret = tps80031_write(charger->dev->parent, SLAVE_ID2, | ||
236 | CHARGERUSB_CTRLLIMIT1, (uint8_t)max_charge_volt); | ||
237 | if (ret < 0) { | ||
238 | dev_err(charger->dev, "%s(): Failed in writing register 0x%02x\n", | ||
239 | __func__, CHARGERUSB_CTRLLIMIT1); | ||
240 | return ret; | ||
241 | } | ||
242 | |||
243 | /* Lock value */ | ||
244 | ret = tps80031_set_bits(charger->dev->parent, SLAVE_ID2, | ||
245 | CHARGERUSB_CTRLLIMIT2, (1 << 4)); | ||
246 | if (ret < 0) { | ||
247 | dev_err(charger->dev, "%s(): Failed in writing register 0x%02x\n", | ||
248 | __func__, CHARGERUSB_CTRLLIMIT2); | ||
249 | return ret; | ||
250 | } | ||
251 | |||
252 | /* set Pre Charge current to 400mA */ | ||
253 | ret = tps80031_write(charger->dev->parent, SLAVE_ID2, 0xDE, 0x3); | ||
254 | if (ret < 0) { | ||
255 | dev_err(charger->dev, "%s(): Failed in writing register 0x%02x\n", | ||
256 | __func__, 0xDD); | ||
257 | return ret; | ||
258 | } | ||
259 | |||
260 | /* set charging termination current*/ | ||
261 | if (charger->charging_term_current_mA > 400) | ||
262 | term_current = 7; | ||
263 | else | ||
264 | term_current = (charger->charging_term_current_mA - 50)/50; | ||
265 | term_current = term_current << 5; | ||
266 | ret = tps80031_write(charger->dev->parent, SLAVE_ID2, | ||
267 | CHARGERUSB_CTRL2, term_current); | ||
268 | if (ret < 0) { | ||
269 | dev_err(charger->dev, "%s(): Failed in writing register 0x%02x\n", | ||
270 | __func__, CHARGERUSB_CTRL2); | ||
271 | return ret; | ||
272 | } | ||
273 | |||
274 | return 0; | ||
275 | } | ||
276 | |||
277 | static irqreturn_t linch_status_isr(int irq, void *dev_id) | ||
278 | { | ||
279 | struct tps80031_charger *charger = dev_id; | ||
280 | uint8_t linch_status; | ||
281 | int ret; | ||
282 | dev_info(charger->dev, "%s() got called\n", __func__); | ||
283 | |||
284 | ret = tps80031_read(charger->dev->parent, SLAVE_ID2, | ||
285 | LINEAR_CHRG_STS, &linch_status); | ||
286 | if (ret < 0) { | ||
287 | dev_err(charger->dev, "%s(): Failed in reading register 0x%02x\n", | ||
288 | __func__, LINEAR_CHRG_STS); | ||
289 | } else { | ||
290 | dev_info(charger->dev, "%s():The status of LINEAR_CHRG_STS is 0x%02x\n", | ||
291 | __func__, linch_status); | ||
292 | if (linch_status & 0x20) { | ||
293 | charger->state = charging_state_charging_completed; | ||
294 | if (charger->charger_cb) | ||
295 | charger->charger_cb(charger->state, | ||
296 | charger->charger_cb_data); | ||
297 | } | ||
298 | } | ||
299 | |||
300 | return IRQ_HANDLED; | ||
301 | } | ||
302 | |||
303 | static irqreturn_t watchdog_expire_isr(int irq, void *dev_id) | ||
304 | { | ||
305 | struct tps80031_charger *charger = dev_id; | ||
306 | int ret; | ||
307 | |||
308 | dev_info(charger->dev, "%s()\n", __func__); | ||
309 | if (charger->state != charging_state_charging_in_progress) | ||
310 | return IRQ_HANDLED; | ||
311 | |||
312 | /* Enable watchdog timer again*/ | ||
313 | ret = tps80031_write(charger->dev->parent, SLAVE_ID2, CONTROLLER_WDG, | ||
314 | charger->watch_time_sec); | ||
315 | if (ret < 0) | ||
316 | dev_err(charger->dev, "%s(): Failed in writing register 0x%02x\n", | ||
317 | __func__, CONTROLLER_WDG); | ||
318 | |||
319 | /* Rewrite to enable the charging */ | ||
320 | if (!ret) { | ||
321 | ret = tps80031_write(charger->dev->parent, SLAVE_ID2, | ||
322 | CONTROLLER_CTRL1, 0x30); | ||
323 | if (ret < 0) | ||
324 | dev_err(charger->dev, "%s(): Failed in writing " | ||
325 | "register 0x%02x\n", | ||
326 | __func__, CONTROLLER_CTRL1); | ||
327 | } | ||
328 | return IRQ_HANDLED; | ||
329 | } | ||
330 | |||
331 | static int tps80031_charger_probe(struct platform_device *pdev) | ||
332 | { | ||
333 | int ret = 0; | ||
334 | struct device *dev = &pdev->dev; | ||
335 | struct tps80031_charger *charger; | ||
336 | struct tps80031_charger_platform_data *pdata = pdev->dev.platform_data; | ||
337 | |||
338 | dev_info(dev, "%s()\n", __func__); | ||
339 | |||
340 | if (!pdata) { | ||
341 | dev_err(dev, "%s() No platform data, exiting..\n", __func__); | ||
342 | return -ENODEV; | ||
343 | } | ||
344 | |||
345 | if (!pdata->num_consumer_supplies) { | ||
346 | dev_err(dev, "%s() No consumer supply list, exiting..\n", | ||
347 | __func__); | ||
348 | return -ENODEV; | ||
349 | } | ||
350 | |||
351 | charger = kzalloc(sizeof(*charger), GFP_KERNEL); | ||
352 | if (!charger) { | ||
353 | dev_err(dev, "failed to allocate memory status\n"); | ||
354 | return -ENOMEM; | ||
355 | } | ||
356 | |||
357 | charger->dev = &pdev->dev; | ||
358 | |||
359 | charger->max_charge_current_mA = (pdata->max_charge_current_mA) ? | ||
360 | pdata->max_charge_current_mA : 1000; | ||
361 | charger->max_charge_volt_mV = (pdata->max_charge_volt_mV) ? | ||
362 | pdata->max_charge_volt_mV : 4200; | ||
363 | charger->irq_base = pdata->irq_base; | ||
364 | charger->watch_time_sec = min(pdata->watch_time_sec, 127); | ||
365 | if (!charger->watch_time_sec) | ||
366 | charger->watch_time_sec = 127; | ||
367 | charger->charging_term_current_mA = | ||
368 | min(50, pdata->charging_term_current_mA); | ||
369 | if (charger->charging_term_current_mA < 50) | ||
370 | charger->charging_term_current_mA = 50; | ||
371 | |||
372 | charger->reg_desc.name = "vbus_charger"; | ||
373 | charger->reg_desc.id = pdata->regulator_id; | ||
374 | charger->reg_desc.ops = &tegra_regulator_ops; | ||
375 | charger->reg_desc.type = REGULATOR_CURRENT; | ||
376 | charger->reg_desc.owner = THIS_MODULE; | ||
377 | |||
378 | charger->reg_init_data.supply_regulator = NULL; | ||
379 | charger->reg_init_data.num_consumer_supplies = | ||
380 | pdata->num_consumer_supplies; | ||
381 | charger->reg_init_data.consumer_supplies = pdata->consumer_supplies; | ||
382 | charger->reg_init_data.regulator_init = NULL; | ||
383 | charger->reg_init_data.driver_data = charger; | ||
384 | charger->reg_init_data.constraints.name = "vbus_charger"; | ||
385 | charger->reg_init_data.constraints.min_uA = 0; | ||
386 | charger->reg_init_data.constraints.max_uA = | ||
387 | pdata->max_charge_current_mA * 1000; | ||
388 | charger->reg_init_data.constraints.valid_modes_mask = | ||
389 | REGULATOR_MODE_NORMAL | | ||
390 | REGULATOR_MODE_STANDBY; | ||
391 | charger->reg_init_data.constraints.valid_ops_mask = | ||
392 | REGULATOR_CHANGE_MODE | | ||
393 | REGULATOR_CHANGE_STATUS | | ||
394 | REGULATOR_CHANGE_CURRENT; | ||
395 | |||
396 | charger->board_init = pdata->board_init; | ||
397 | charger->board_data = pdata->board_data; | ||
398 | charger->state = charging_state_idle; | ||
399 | |||
400 | charger->rdev = regulator_register(&charger->reg_desc, &pdev->dev, | ||
401 | &charger->reg_init_data, charger); | ||
402 | if (IS_ERR(charger->rdev)) { | ||
403 | dev_err(&pdev->dev, "failed to register %s\n", | ||
404 | charger->reg_desc.name); | ||
405 | ret = PTR_ERR(charger->rdev); | ||
406 | goto regulator_fail; | ||
407 | } | ||
408 | |||
409 | ret = request_threaded_irq(charger->irq_base + TPS80031_INT_LINCH_GATED, | ||
410 | NULL, linch_status_isr, 0, "tps80031-linch", charger); | ||
411 | if (ret) { | ||
412 | dev_err(&pdev->dev, "Unable to register irq %d; error %d\n", | ||
413 | charger->irq_base + TPS80031_INT_LINCH_GATED, ret); | ||
414 | goto irq_linch_fail; | ||
415 | } | ||
416 | |||
417 | ret = request_threaded_irq(charger->irq_base + TPS80031_INT_FAULT_WDG, | ||
418 | NULL, watchdog_expire_isr, 0, "tps80031-wdg", charger); | ||
419 | if (ret) { | ||
420 | dev_err(&pdev->dev, "Unable to register irq %d; error %d\n", | ||
421 | charger->irq_base + TPS80031_INT_FAULT_WDG, ret); | ||
422 | goto irq_wdg_fail; | ||
423 | } | ||
424 | |||
425 | ret = configure_charging_parameter(charger); | ||
426 | if (ret) | ||
427 | goto config_fail; | ||
428 | |||
429 | dev_set_drvdata(&pdev->dev, charger); | ||
430 | charger_data = charger; | ||
431 | return ret; | ||
432 | |||
433 | config_fail: | ||
434 | free_irq(charger->irq_base + TPS80031_INT_FAULT_WDG, charger); | ||
435 | irq_wdg_fail: | ||
436 | free_irq(charger->irq_base + TPS80031_INT_LINCH_GATED, charger); | ||
437 | irq_linch_fail: | ||
438 | regulator_unregister(charger->rdev); | ||
439 | regulator_fail: | ||
440 | kfree(charger); | ||
441 | return ret; | ||
442 | } | ||
443 | |||
444 | static int tps80031_charger_remove(struct platform_device *pdev) | ||
445 | { | ||
446 | struct tps80031_charger *charger = dev_get_drvdata(&pdev->dev); | ||
447 | |||
448 | regulator_unregister(charger->rdev); | ||
449 | kfree(charger); | ||
450 | return 0; | ||
451 | } | ||
452 | |||
453 | static struct platform_driver tps80031_charger_driver = { | ||
454 | .driver = { | ||
455 | .name = "tps80031-charger", | ||
456 | .owner = THIS_MODULE, | ||
457 | }, | ||
458 | .probe = tps80031_charger_probe, | ||
459 | .remove = tps80031_charger_remove, | ||
460 | }; | ||
461 | |||
462 | static int __init tps80031_charger_init(void) | ||
463 | { | ||
464 | return platform_driver_register(&tps80031_charger_driver); | ||
465 | } | ||
466 | |||
467 | static void __exit tps80031_charger_exit(void) | ||
468 | { | ||
469 | platform_driver_unregister(&tps80031_charger_driver); | ||
470 | } | ||
471 | |||
472 | subsys_initcall(tps80031_charger_init); | ||
473 | module_exit(tps80031_charger_exit); | ||
474 | |||
475 | MODULE_LICENSE("GPL"); | ||
476 | MODULE_DESCRIPTION("tps80031 battery charger driver"); | ||
diff --git a/drivers/power/tps80031_battery_gauge.c b/drivers/power/tps80031_battery_gauge.c new file mode 100644 index 00000000000..9ccfaad4144 --- /dev/null +++ b/drivers/power/tps80031_battery_gauge.c | |||
@@ -0,0 +1,606 @@ | |||
1 | /* | ||
2 | * drivers/power/tps80031_battery_gauge.c | ||
3 | * | ||
4 | * Gas Gauge driver for TI's tps80031 | ||
5 | * | ||
6 | * Copyright (c) 2011, NVIDIA Corporation. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
15 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
16 | * more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License along | ||
19 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
20 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
21 | */ | ||
22 | #include <linux/kernel.h> | ||
23 | #include <linux/platform_device.h> | ||
24 | #include <linux/regulator/driver.h> | ||
25 | #include <linux/err.h> | ||
26 | #include <linux/regulator/machine.h> | ||
27 | #include <linux/mutex.h> | ||
28 | |||
29 | #include <linux/module.h> | ||
30 | #include <linux/param.h> | ||
31 | #include <linux/jiffies.h> | ||
32 | #include <linux/workqueue.h> | ||
33 | #include <linux/pm.h> | ||
34 | #include <linux/i2c.h> | ||
35 | #include <linux/slab.h> | ||
36 | #include <linux/platform_device.h> | ||
37 | #include <linux/power_supply.h> | ||
38 | |||
39 | #include <linux/mfd/core.h> | ||
40 | #include <linux/mfd/tps80031.h> | ||
41 | #include <linux/tps80031-charger.h> | ||
42 | |||
43 | #define CHARGERUSB_CINLIMIT 0xee | ||
44 | #define CONTROLLER_STAT1 0xe3 | ||
45 | #define LINEAR_CHARGE_STS 0xde | ||
46 | #define STS_HW_CONDITIONS 0x21 | ||
47 | #define TOGGLE1 0x90 | ||
48 | #define TOGGLE1_FGS BIT(5) | ||
49 | #define TOGGLE1_GPADCR BIT(1) | ||
50 | #define GPCH0_LSB 0x3b | ||
51 | #define GPCH0_MSB 0x3c | ||
52 | #define GPCH0_MSB_COLLISION_GP BIT(4) | ||
53 | #define GPSELECT_ISB 0x35 | ||
54 | #define GPADC_CTRL 0x2e | ||
55 | #define MISC1 0xe4 | ||
56 | #define CTRL_P1 0x36 | ||
57 | #define CTRL_P1_SP1 BIT(3) | ||
58 | #define CTRL_P1_EOCRT BIT(2) | ||
59 | #define CTRL_P1_EOCP1 BIT(1) | ||
60 | #define CTRL_P1_BUSY BIT(0) | ||
61 | #define FG_REG_00 0xc0 | ||
62 | #define FG_REG_00_CC_CAL_EN BIT(1) | ||
63 | #define FG_REG_00_CC_AUTOCLEAR BIT(2) | ||
64 | #define FG_REG_01 0xc1 /* CONV_NR (unsigned) 0 - 7 */ | ||
65 | #define FG_REG_02 0xc2 /* CONV_NR (unsigned) 8 - 15 */ | ||
66 | #define FG_REG_03 0xc3 /* CONV_NR (unsigned) 16 - 23 */ | ||
67 | #define FG_REG_04 0xc4 /* ACCM (signed) 0 - 7 */ | ||
68 | #define FG_REG_05 0xc5 /* ACCM (signed) 8 - 15 */ | ||
69 | #define FG_REG_06 0xc6 /* ACCM (signed) 16 - 23 */ | ||
70 | #define FG_REG_07 0xc7 /* ACCM (signed) 24 - 31 */ | ||
71 | #define FG_REG_08 0xc8 /* OFFSET (signed) 0 - 7 */ | ||
72 | #define FG_REG_09 0xc9 /* OFFSET (signed) 8 - 9 */ | ||
73 | #define FG_REG_10 0xca /* LAST_READ (signed) 0 - 7 */ | ||
74 | #define FG_REG_11 0xcb /* LAST_READ (signed) 8 - 13 */ | ||
75 | |||
76 | #define TPS80031_VBUS_DET BIT(2) | ||
77 | #define TPS80031_VAC_DET BIT(3) | ||
78 | #define TPS80031_STS_VYSMIN_HI BIT(4) | ||
79 | #define END_OF_CHARGE BIT(5) | ||
80 | |||
81 | #define DRIVER_VERSION "1.1.0" | ||
82 | #define BATTERY_POLL_PERIOD 30000 | ||
83 | |||
84 | static int tps80031_temp_table[] = { | ||
85 | /* adc code for temperature in degree C */ | ||
86 | 929, 925, /* -2, -1 */ | ||
87 | 920, 917, 912, 908, 904, 899, 895, 890, 885, 880, /* 00 - 09 */ | ||
88 | 875, 869, 864, 858, 853, 847, 841, 835, 829, 823, /* 10 - 19 */ | ||
89 | 816, 810, 804, 797, 790, 783, 776, 769, 762, 755, /* 20 - 29 */ | ||
90 | 748, 740, 732, 725, 718, 710, 703, 695, 687, 679, /* 30 - 39 */ | ||
91 | 671, 663, 655, 647, 639, 631, 623, 615, 607, 599, /* 40 - 49 */ | ||
92 | 591, 583, 575, 567, 559, 551, 543, 535, 527, 519, /* 50 - 59 */ | ||
93 | 511, 504, 496 /* 60 - 62 */ | ||
94 | }; | ||
95 | |||
96 | struct tps80031_device_info { | ||
97 | struct device *dev; | ||
98 | struct i2c_client *client; | ||
99 | struct power_supply bat; | ||
100 | struct power_supply ac; | ||
101 | struct power_supply usb; | ||
102 | struct timer_list battery_poll_timer; | ||
103 | uint32_t vsys; | ||
104 | uint8_t usb_online; | ||
105 | uint8_t ac_online; | ||
106 | uint8_t usb_status; | ||
107 | uint8_t capacity_sts; | ||
108 | uint8_t health; | ||
109 | uint8_t sys_vlow_intr; | ||
110 | uint8_t fg_calib_intr; | ||
111 | int16_t fg_offset; | ||
112 | struct mutex adc_lock; | ||
113 | }; | ||
114 | |||
115 | static enum power_supply_property tps80031_bat_props[] = { | ||
116 | POWER_SUPPLY_PROP_TECHNOLOGY, | ||
117 | POWER_SUPPLY_PROP_VOLTAGE_NOW, | ||
118 | POWER_SUPPLY_PROP_TEMP, | ||
119 | POWER_SUPPLY_PROP_CAPACITY, | ||
120 | POWER_SUPPLY_PROP_HEALTH, | ||
121 | POWER_SUPPLY_PROP_STATUS, | ||
122 | POWER_SUPPLY_PROP_CHARGE_NOW, | ||
123 | POWER_SUPPLY_PROP_CHARGE_COUNTER, | ||
124 | }; | ||
125 | |||
126 | static enum power_supply_property tps80031_usb_props[] = { | ||
127 | POWER_SUPPLY_PROP_ONLINE, | ||
128 | }; | ||
129 | |||
130 | static enum power_supply_property tps80031_ac_props[] = { | ||
131 | POWER_SUPPLY_PROP_ONLINE, | ||
132 | }; | ||
133 | |||
134 | static int tps80031_reg_read(struct tps80031_device_info *di, int sid, int reg, | ||
135 | uint8_t *val) | ||
136 | { | ||
137 | int ret; | ||
138 | |||
139 | ret = tps80031_read(di->dev->parent, sid, reg, val); | ||
140 | if (ret < 0) | ||
141 | dev_err(di->dev, "Failed read register 0x%02x\n", | ||
142 | reg); | ||
143 | return ret; | ||
144 | } | ||
145 | |||
146 | static int tps80031_reg_write(struct tps80031_device_info *di, int sid, int reg, | ||
147 | uint8_t val) | ||
148 | { | ||
149 | int ret; | ||
150 | |||
151 | ret = tps80031_write(di->dev->parent, sid, reg, val); | ||
152 | if (ret < 0) | ||
153 | dev_err(di->dev, "Failed write register 0x%02x\n", | ||
154 | reg); | ||
155 | return ret; | ||
156 | } | ||
157 | |||
158 | static int tps80031_battery_capacity(struct tps80031_device_info *di, | ||
159 | union power_supply_propval *val) | ||
160 | { | ||
161 | uint8_t hwsts; | ||
162 | int ret; | ||
163 | |||
164 | ret = tps80031_reg_read(di, SLAVE_ID2, LINEAR_CHARGE_STS, &hwsts); | ||
165 | if (ret < 0) | ||
166 | return ret; | ||
167 | |||
168 | di->capacity_sts = di->vsys; | ||
169 | if (hwsts & END_OF_CHARGE) | ||
170 | di->capacity_sts = 100; | ||
171 | |||
172 | if (di->sys_vlow_intr) { | ||
173 | di->capacity_sts = 10; | ||
174 | di->sys_vlow_intr = 0; | ||
175 | } | ||
176 | |||
177 | if (di->capacity_sts <= 10) | ||
178 | di->health = POWER_SUPPLY_HEALTH_DEAD; | ||
179 | else | ||
180 | di->health = POWER_SUPPLY_HEALTH_GOOD; | ||
181 | |||
182 | return di->capacity_sts; | ||
183 | } | ||
184 | |||
185 | static int tps80031_battery_voltage(struct tps80031_device_info *di, | ||
186 | union power_supply_propval *val) | ||
187 | { | ||
188 | int voltage; | ||
189 | |||
190 | voltage = tps80031_gpadc_conversion(SYSTEM_SUPPLY); | ||
191 | if (voltage < 0) | ||
192 | return voltage; | ||
193 | voltage = ((voltage * 1000) / 4) * 5; | ||
194 | |||
195 | if (voltage < 3700000) | ||
196 | di->vsys = 10; | ||
197 | else if (voltage > 3700000 && voltage <= 3800000) | ||
198 | di->vsys = 20; | ||
199 | else if (voltage > 3800000 && voltage <= 3900000) | ||
200 | di->vsys = 50; | ||
201 | else if (voltage > 3900000 && voltage <= 4000000) | ||
202 | di->vsys = 75; | ||
203 | else if (voltage >= 4000000) | ||
204 | di->vsys = 90; | ||
205 | |||
206 | return voltage; | ||
207 | } | ||
208 | |||
209 | static int tps80031_battery_charge_now(struct tps80031_device_info *di, | ||
210 | union power_supply_propval *val) | ||
211 | { | ||
212 | int charge; | ||
213 | |||
214 | charge = tps80031_gpadc_conversion(BATTERY_CHARGING_CURRENT); | ||
215 | if (charge < 0) | ||
216 | return charge; | ||
217 | charge = charge * 78125 / 40; | ||
218 | |||
219 | return charge; | ||
220 | } | ||
221 | |||
222 | static int tps80031_battery_charge_counter(struct tps80031_device_info *di, | ||
223 | union power_supply_propval *val) | ||
224 | { | ||
225 | int retval, ret; | ||
226 | uint32_t cnt_byte; | ||
227 | uint32_t acc_byte; | ||
228 | |||
229 | /* check if calibrated */ | ||
230 | if (di->fg_calib_intr == 0) | ||
231 | return 0; | ||
232 | |||
233 | /* get current accumlator */ | ||
234 | ret = tps80031_reads(di->dev->parent, SLAVE_ID2, FG_REG_04, 4, | ||
235 | (uint8_t *) &acc_byte); | ||
236 | if (ret < 0) | ||
237 | return ret; | ||
238 | /* counter value is mAs, need report uAh */ | ||
239 | retval = (int32_t) acc_byte / 18 * 5; | ||
240 | |||
241 | /* get counter */ | ||
242 | ret = tps80031_reads(di->dev->parent, SLAVE_ID2, FG_REG_01, 3, | ||
243 | (uint8_t *) &cnt_byte); | ||
244 | if (ret < 0) | ||
245 | return ret; | ||
246 | /* we need calibrate the offset current in uAh*/ | ||
247 | retval = retval - (di->fg_offset / 4 * cnt_byte); | ||
248 | |||
249 | /* @todo, counter value will overflow if battery get continuously | ||
250 | * charged discharged for more than 108Ah using 250mS integration | ||
251 | * period althrough it is hightly impossible. | ||
252 | */ | ||
253 | |||
254 | return retval; | ||
255 | } | ||
256 | |||
257 | static int tps80031_battery_temp(struct tps80031_device_info *di, | ||
258 | union power_supply_propval *val) | ||
259 | { | ||
260 | int adc_code, temp; | ||
261 | |||
262 | adc_code = tps80031_gpadc_conversion(BATTERY_TEMPERATURE); | ||
263 | if (adc_code < 0) | ||
264 | return adc_code; | ||
265 | |||
266 | for (temp = 0; temp < ARRAY_SIZE(tps80031_temp_table); temp++) { | ||
267 | if (adc_code >= tps80031_temp_table[temp]) | ||
268 | break; | ||
269 | } | ||
270 | /* first 2 values are for negative temperature */ | ||
271 | val->intval = (temp - 2) * 10; /* in tenths of degree Celsius */ | ||
272 | |||
273 | return val->intval; | ||
274 | } | ||
275 | |||
276 | #define to_tps80031_device_info_bat(x) container_of((x), \ | ||
277 | struct tps80031_device_info, bat); | ||
278 | |||
279 | static int tps80031_bat_get_property(struct power_supply *psy, | ||
280 | enum power_supply_property psp, union power_supply_propval *val) | ||
281 | { | ||
282 | struct tps80031_device_info *di = to_tps80031_device_info_bat(psy); | ||
283 | |||
284 | switch (psp) { | ||
285 | |||
286 | case POWER_SUPPLY_PROP_TECHNOLOGY: | ||
287 | val->intval = POWER_SUPPLY_TECHNOLOGY_LION; | ||
288 | break; | ||
289 | |||
290 | case POWER_SUPPLY_PROP_HEALTH: | ||
291 | val->intval = di->health; | ||
292 | break; | ||
293 | |||
294 | case POWER_SUPPLY_PROP_CAPACITY: | ||
295 | val->intval = tps80031_battery_capacity(di, val); | ||
296 | break; | ||
297 | |||
298 | case POWER_SUPPLY_PROP_CHARGE_NOW: | ||
299 | val->intval = tps80031_battery_charge_now(di, val); | ||
300 | break; | ||
301 | |||
302 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: | ||
303 | val->intval = tps80031_battery_voltage(di, val); | ||
304 | break; | ||
305 | |||
306 | case POWER_SUPPLY_PROP_CHARGE_COUNTER: | ||
307 | val->intval = tps80031_battery_charge_counter(di, val); | ||
308 | break; | ||
309 | |||
310 | case POWER_SUPPLY_PROP_STATUS: | ||
311 | val->intval = di->usb_status; | ||
312 | break; | ||
313 | |||
314 | case POWER_SUPPLY_PROP_TEMP: | ||
315 | val->intval = tps80031_battery_temp(di, val); | ||
316 | break; | ||
317 | |||
318 | default: | ||
319 | return -EINVAL; | ||
320 | } | ||
321 | return 0; | ||
322 | } | ||
323 | |||
324 | #define to_tps80031_device_info_usb(x) container_of((x), \ | ||
325 | struct tps80031_device_info, usb); | ||
326 | |||
327 | static int tps80031_usb_get_property(struct power_supply *psy, | ||
328 | enum power_supply_property psp, union power_supply_propval *val) | ||
329 | { | ||
330 | struct tps80031_device_info *di = to_tps80031_device_info_usb(psy); | ||
331 | |||
332 | switch (psp) { | ||
333 | case POWER_SUPPLY_PROP_ONLINE: | ||
334 | val->intval = di->usb_online; | ||
335 | break; | ||
336 | default: | ||
337 | return -EINVAL; | ||
338 | } | ||
339 | return 0; | ||
340 | } | ||
341 | |||
342 | #define to_tps80031_device_info_ac(x) container_of((x), \ | ||
343 | struct tps80031_device_info, ac); | ||
344 | |||
345 | static int tps80031_ac_get_property(struct power_supply *psy, | ||
346 | enum power_supply_property psp, union power_supply_propval *val) | ||
347 | { | ||
348 | struct tps80031_device_info *di = to_tps80031_device_info_ac(psy); | ||
349 | |||
350 | switch (psp) { | ||
351 | case POWER_SUPPLY_PROP_ONLINE: | ||
352 | val->intval = di->ac_online; | ||
353 | break; | ||
354 | default: | ||
355 | return -EINVAL; | ||
356 | } | ||
357 | return 0; | ||
358 | } | ||
359 | |||
360 | static irqreturn_t tps80031_sys_vlow(int irq, void *data) | ||
361 | { | ||
362 | struct tps80031_device_info *di = data; | ||
363 | |||
364 | di->sys_vlow_intr = 1; | ||
365 | power_supply_changed(&di->bat); | ||
366 | return IRQ_HANDLED; | ||
367 | } | ||
368 | |||
369 | static irqreturn_t tps80031_fg_calibrated(int irq, void *data) | ||
370 | { | ||
371 | struct tps80031_device_info *di = data; | ||
372 | uint8_t acc_byte0; | ||
373 | uint8_t acc_byte1; | ||
374 | int ret; | ||
375 | |||
376 | ret = tps80031_reg_read(di, SLAVE_ID2, FG_REG_08, &acc_byte0); | ||
377 | if (ret < 0) | ||
378 | return IRQ_HANDLED; | ||
379 | ret = tps80031_reg_read(di, SLAVE_ID2, FG_REG_09, &acc_byte1); | ||
380 | if (ret < 0) | ||
381 | return IRQ_HANDLED; | ||
382 | /* sign extension */ | ||
383 | if (acc_byte1 & 0x02) | ||
384 | acc_byte1 = acc_byte1 | 0xFC; | ||
385 | else | ||
386 | acc_byte1 = acc_byte1 & 0x03; | ||
387 | |||
388 | di->fg_offset = (int16_t) ((acc_byte1 << 8) | acc_byte0); | ||
389 | /* fuel gauge auto calibration finished */ | ||
390 | di->fg_calib_intr = 1; | ||
391 | return IRQ_HANDLED; | ||
392 | } | ||
393 | |||
394 | static int tps80031_fg_start_gas_gauge(struct tps80031_device_info *di) | ||
395 | { | ||
396 | int ret = 0; | ||
397 | di->fg_calib_intr = 0; | ||
398 | |||
399 | /* start gas gauge */ | ||
400 | ret = tps80031_reg_write(di, SLAVE_ID2, TOGGLE1, 0x20); | ||
401 | if (ret < 0) | ||
402 | return ret; | ||
403 | /* set ADC update time to 3.9ms and start calibration */ | ||
404 | ret = tps80031_reg_write(di, SLAVE_ID2, FG_REG_00, FG_REG_00_CC_CAL_EN); | ||
405 | if (ret < 0) | ||
406 | return ret; | ||
407 | return ret; | ||
408 | } | ||
409 | |||
410 | void tps80031_battery_status(enum charging_states status, void *data) | ||
411 | { | ||
412 | struct tps80031_device_info *di = data; | ||
413 | int ret; | ||
414 | uint8_t retval; | ||
415 | |||
416 | if ((status == charging_state_charging_in_progress)) { | ||
417 | di->usb_status = POWER_SUPPLY_STATUS_CHARGING; | ||
418 | di->health = POWER_SUPPLY_HEALTH_GOOD; | ||
419 | ret = tps80031_reg_read(di, SLAVE_ID2, | ||
420 | CHARGERUSB_CINLIMIT, &retval); | ||
421 | if (ret < 0) { | ||
422 | di->ac_online = 0; | ||
423 | di->usb_online = 0; | ||
424 | } | ||
425 | if (retval == 0x9) { | ||
426 | di->ac_online = 0; | ||
427 | di->usb_online = 1; | ||
428 | } else { | ||
429 | di->usb_online = 0; | ||
430 | di->ac_online = 1; | ||
431 | } | ||
432 | } else if (status == charging_state_charging_stopped) { | ||
433 | di->usb_status = POWER_SUPPLY_STATUS_DISCHARGING; | ||
434 | di->ac_online = 0; | ||
435 | di->usb_online = 0; | ||
436 | } | ||
437 | power_supply_changed(&di->usb); | ||
438 | power_supply_changed(&di->bat); | ||
439 | power_supply_changed(&di->ac); | ||
440 | } | ||
441 | |||
442 | static void battery_poll_timer_func(unsigned long pdi) | ||
443 | { | ||
444 | struct tps80031_device_info *di = (void *)pdi; | ||
445 | power_supply_changed(&di->bat); | ||
446 | mod_timer(&di->battery_poll_timer, | ||
447 | jiffies + msecs_to_jiffies(BATTERY_POLL_PERIOD)); | ||
448 | } | ||
449 | |||
450 | static int tps80031_battery_probe(struct platform_device *pdev) | ||
451 | { | ||
452 | int ret; | ||
453 | uint8_t retval; | ||
454 | struct device *dev = &pdev->dev; | ||
455 | struct tps80031_device_info *di; | ||
456 | struct tps80031_bg_platform_data *pdata = pdev->dev.platform_data; | ||
457 | |||
458 | di = devm_kzalloc(&pdev->dev, sizeof *di, GFP_KERNEL); | ||
459 | if (!di) { | ||
460 | dev_err(dev->parent, "failed to allocate device info data\n"); | ||
461 | return -ENOMEM; | ||
462 | } | ||
463 | |||
464 | if (!pdata->battery_present) { | ||
465 | dev_err(dev, "%s() No battery detected, exiting..\n", | ||
466 | __func__); | ||
467 | return -ENODEV; | ||
468 | } | ||
469 | |||
470 | di->dev = &pdev->dev; | ||
471 | |||
472 | ret = tps80031_reg_read(di, SLAVE_ID2, CONTROLLER_STAT1, &retval); | ||
473 | if (ret < 0) | ||
474 | return ret; | ||
475 | |||
476 | if ((retval & TPS80031_VAC_DET) | (retval & TPS80031_VBUS_DET)) { | ||
477 | di->usb_status = POWER_SUPPLY_STATUS_CHARGING; | ||
478 | di->usb_online = 1; | ||
479 | } else { | ||
480 | di->usb_status = POWER_SUPPLY_STATUS_DISCHARGING; | ||
481 | di->usb_online = 0; | ||
482 | } | ||
483 | |||
484 | di->capacity_sts = 50; | ||
485 | di->health = POWER_SUPPLY_HEALTH_GOOD; | ||
486 | |||
487 | di->bat.name = "tps80031-bat"; | ||
488 | di->bat.type = POWER_SUPPLY_TYPE_BATTERY; | ||
489 | di->bat.properties = tps80031_bat_props; | ||
490 | di->bat.num_properties = ARRAY_SIZE(tps80031_bat_props); | ||
491 | di->bat.get_property = tps80031_bat_get_property; | ||
492 | |||
493 | ret = power_supply_register(dev->parent, &di->bat); | ||
494 | if (ret) { | ||
495 | dev_err(dev->parent, "failed to register bat power supply\n"); | ||
496 | return ret; | ||
497 | } | ||
498 | |||
499 | di->usb.name = "tps80031-usb"; | ||
500 | di->usb.type = POWER_SUPPLY_TYPE_USB; | ||
501 | di->usb.properties = tps80031_usb_props; | ||
502 | di->usb.num_properties = ARRAY_SIZE(tps80031_usb_props); | ||
503 | di->usb.get_property = tps80031_usb_get_property; | ||
504 | |||
505 | ret = power_supply_register(dev->parent, &di->usb); | ||
506 | if (ret) { | ||
507 | dev_err(dev->parent, "failed to register ac power supply\n"); | ||
508 | goto power_supply_fail2; | ||
509 | } | ||
510 | |||
511 | di->ac.name = "tps80031-ac"; | ||
512 | di->ac.type = POWER_SUPPLY_TYPE_MAINS; | ||
513 | di->ac.properties = tps80031_ac_props; | ||
514 | di->ac.num_properties = ARRAY_SIZE(tps80031_ac_props); | ||
515 | di->ac.get_property = tps80031_ac_get_property; | ||
516 | |||
517 | ret = power_supply_register(dev->parent, &di->ac); | ||
518 | if (ret) { | ||
519 | dev_err(dev->parent, "failed to register ac power supply\n"); | ||
520 | goto power_supply_fail1; | ||
521 | } | ||
522 | |||
523 | dev_set_drvdata(&pdev->dev, di); | ||
524 | |||
525 | ret = register_charging_state_callback(tps80031_battery_status, di); | ||
526 | if (ret < 0) | ||
527 | goto power_supply_fail0; | ||
528 | |||
529 | ret = request_threaded_irq(pdata->irq_base + TPS80031_INT_SYS_VLOW, | ||
530 | NULL, tps80031_sys_vlow, | ||
531 | IRQF_ONESHOT, "tps80031_sys_vlow", di); | ||
532 | if (ret < 0) { | ||
533 | dev_err(dev->parent, "request IRQ %d fail\n", pdata->irq_base); | ||
534 | goto power_supply_fail0; | ||
535 | } | ||
536 | |||
537 | ret = request_threaded_irq(pdata->irq_base + TPS80031_INT_CC_AUTOCAL, | ||
538 | NULL, tps80031_fg_calibrated, IRQF_ONESHOT, | ||
539 | "tps80031_fuel_gauge_calibration", di); | ||
540 | if (ret < 0) { | ||
541 | dev_err(dev->parent, "request IRQ %d fail\n", pdata->irq_base); | ||
542 | goto irq_fail2; | ||
543 | } | ||
544 | setup_timer(&di->battery_poll_timer, | ||
545 | battery_poll_timer_func, (unsigned long) di); | ||
546 | mod_timer(&di->battery_poll_timer, | ||
547 | jiffies + msecs_to_jiffies(BATTERY_POLL_PERIOD)); | ||
548 | |||
549 | ret = tps80031_fg_start_gas_gauge(di); | ||
550 | if (ret < 0) { | ||
551 | dev_err(dev->parent, "failed to start fuel-gauge\n"); | ||
552 | goto irq_fail1; | ||
553 | } | ||
554 | dev_info(dev->parent, "support ver. %s enabled\n", DRIVER_VERSION); | ||
555 | |||
556 | return ret; | ||
557 | |||
558 | irq_fail1: | ||
559 | free_irq(pdata->irq_base + TPS80031_INT_CC_AUTOCAL, di); | ||
560 | irq_fail2: | ||
561 | free_irq(pdata->irq_base + TPS80031_INT_SYS_VLOW, di); | ||
562 | power_supply_fail0: | ||
563 | power_supply_unregister(&di->ac); | ||
564 | power_supply_fail1: | ||
565 | power_supply_unregister(&di->usb); | ||
566 | power_supply_fail2: | ||
567 | power_supply_unregister(&di->bat); | ||
568 | return ret; | ||
569 | } | ||
570 | |||
571 | static int tps80031_battery_remove(struct platform_device *pdev) | ||
572 | { | ||
573 | struct tps80031_device_info *di = dev_get_drvdata(&pdev->dev); | ||
574 | |||
575 | power_supply_unregister(&di->bat); | ||
576 | power_supply_unregister(&di->usb); | ||
577 | power_supply_unregister(&di->ac); | ||
578 | |||
579 | return 0; | ||
580 | } | ||
581 | |||
582 | static struct platform_driver tps80031_battery_driver = { | ||
583 | .driver = { | ||
584 | .name = "tps80031-battery-gauge", | ||
585 | .owner = THIS_MODULE, | ||
586 | }, | ||
587 | .probe = tps80031_battery_probe, | ||
588 | .remove = tps80031_battery_remove, | ||
589 | }; | ||
590 | |||
591 | static int __init tps80031_battery_init(void) | ||
592 | { | ||
593 | return platform_driver_register(&tps80031_battery_driver); | ||
594 | } | ||
595 | |||
596 | static void __exit tps80031_battery_exit(void) | ||
597 | { | ||
598 | platform_driver_unregister(&tps80031_battery_driver); | ||
599 | } | ||
600 | |||
601 | module_init(tps80031_battery_init); | ||
602 | module_exit(tps80031_battery_exit); | ||
603 | |||
604 | MODULE_LICENSE("GPL"); | ||
605 | MODULE_AUTHOR("Syed Rafiuddin <srafiuddin@nvidia.com> "); | ||
606 | MODULE_DESCRIPTION("tps80031 battery gauge driver"); | ||