aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/power/smb349-charger.c
diff options
context:
space:
mode:
authorJonathan Herman <hermanjl@cs.unc.edu>2013-01-22 10:38:37 -0500
committerJonathan Herman <hermanjl@cs.unc.edu>2013-01-22 10:38:37 -0500
commitfcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch)
treea57612d1888735a2ec7972891b68c1ac5ec8faea /drivers/power/smb349-charger.c
parent8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff)
Added missing tegra files.HEADmaster
Diffstat (limited to 'drivers/power/smb349-charger.c')
-rw-r--r--drivers/power/smb349-charger.c588
1 files changed, 588 insertions, 0 deletions
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
85static struct smb349_charger *charger;
86static int smb349_configure_charger(struct i2c_client *client, int value);
87static int smb349_configure_interrupts(struct i2c_client *client);
88
89static 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
101static 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
113static 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
132int 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
161static 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
171static 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__);
231error:
232 return ret;
233}
234
235static 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 }
282error:
283 return ret;
284}
285
286static 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);
334irq_error:
335 smb349_clear_interrupts(client);
336 return IRQ_HANDLED;
337}
338
339int 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;
380val_error:
381 return val;
382ret_error:
383 return ret;
384}
385EXPORT_SYMBOL_GPL(update_charger_status);
386
387int 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}
397EXPORT_SYMBOL_GPL(register_callback);
398
399int 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
416static 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
449error:
450 return ret;
451}
452
453static 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
493static 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;
544error:
545 kfree(charger);
546 return ret;
547}
548
549static 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
559static const struct i2c_device_id smb349_id[] = {
560 { "smb349", 0 },
561 { }
562};
563MODULE_DEVICE_TABLE(i2c, smb349_id);
564
565static 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
574static int __init smb349_init(void)
575{
576 return i2c_add_driver(&smb349_i2c_driver);
577}
578module_init(smb349_init);
579
580static void __exit smb349_exit(void)
581{
582 i2c_del_driver(&smb349_i2c_driver);
583}
584module_exit(smb349_exit);
585
586MODULE_AUTHOR("Syed Rafiuddin <srafiuddin@nvidia.com>");
587MODULE_DESCRIPTION("SMB349 Battery-Charger");
588MODULE_LICENSE("GPL");