blob: c47cf567a7e7836526eee6a7faf8680cbbb20ced [file] [log] [blame]
Laxman Dewanganf27ed352012-11-29 19:33:02 -05001/*
Syed Rafiuddin6126b492013-04-03 14:06:34 +05302 * bq2419x-charger.c -- BQ24190/BQ24192/BQ24192i/BQ24193 Charger driver
Laxman Dewanganf27ed352012-11-29 19:33:02 -05003 *
Syed Rafiuddinf2d004d2013-02-18 15:30:58 +05304 * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
Laxman Dewanganf27ed352012-11-29 19:33:02 -05005 *
6 * Author: Laxman Dewangan <ldewangan@nvidia.com>
Syed Rafiuddin6126b492013-04-03 14:06:34 +05307 * Author: Syed Rafiuddin <srafiuddin@nvidia.com>
Laxman Dewanganf27ed352012-11-29 19:33:02 -05008 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation version 2.
12 *
13 * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind,
14 * whether express or implied; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
21 * 02111-1307, USA
22 */
23#include <linux/delay.h>
24#include <linux/err.h>
25#include <linux/i2c.h>
26#include <linux/init.h>
27#include <linux/interrupt.h>
Syed Rafiuddin6126b492013-04-03 14:06:34 +053028#include <linux/kthread.h>
29#include <linux/sched.h>
30#include <linux/time.h>
31#include <linux/timer.h>
Laxman Dewanganf27ed352012-11-29 19:33:02 -050032#include <linux/gpio.h>
33#include <linux/kernel.h>
34#include <linux/module.h>
Syed Rafiuddin6126b492013-04-03 14:06:34 +053035#include <linux/power/bq2419x-charger.h>
Laxman Dewanganf27ed352012-11-29 19:33:02 -050036#include <linux/power_supply.h>
Syed Rafiuddin6126b492013-04-03 14:06:34 +053037#include <linux/regmap.h>
38#include <linux/regmap.h>
Syed Rafiuddinee69db32013-01-09 15:40:55 +053039#include <linux/regulator/driver.h>
40#include <linux/regulator/machine.h>
Syed Rafiuddin6126b492013-04-03 14:06:34 +053041#include <linux/slab.h>
Laxman Dewanganf27ed352012-11-29 19:33:02 -050042
43/* input current limit */
44static const unsigned int iinlim[] = {
45 100, 150, 500, 900, 1200, 1500, 2000, 3000,
46};
47
Syed Rafiuddin6126b492013-04-03 14:06:34 +053048/* Kthread scheduling parameters */
49struct sched_param bq2419x_param = {
50 .sched_priority = MAX_RT_PRIO - 1,
Laxman Dewanganf27ed352012-11-29 19:33:02 -050051};
52
Syed Rafiuddin6126b492013-04-03 14:06:34 +053053static const struct regmap_config bq2419x_regmap_config = {
54 .reg_bits = 8,
55 .val_bits = 8,
56 .max_register = BQ2419X_MAX_REGS,
57};
58
59struct bq2419x_chip {
60 struct device *dev;
61 struct regmap *regmap;
62 int irq;
63 int gpio_otg_iusb;
64 int wdt_refresh_timeout;
65
66 struct power_supply ac;
67 struct power_supply usb;
68 int ac_online;
69 int usb_online;
70 int in_current_limit;
71 unsigned use_mains:1;
72 unsigned use_usb:1;
73 int status;
74 void (*update_status)(int, int);
75
76 struct regulator_dev *chg_rdev;
77 struct regulator_desc chg_reg_desc;
78 struct regulator_init_data chg_reg_init_data;
79
80 struct regulator_dev *vbus_rdev;
81 struct regulator_desc vbus_reg_desc;
82 struct regulator_init_data vbus_reg_init_data;
83
84 struct kthread_worker bq_kworker;
85 struct task_struct *bq_kworker_task;
86 struct kthread_work bq_wdt_work;
87 int stop_thread;
88};
Rohith Seelaboyina2fd2e112013-02-02 18:10:54 +053089
Laxman Dewanganf27ed352012-11-29 19:33:02 -050090static enum power_supply_property bq2419x_psy_props[] = {
91 POWER_SUPPLY_PROP_ONLINE,
92};
Syed Rafiuddin6126b492013-04-03 14:06:34 +053093
Laxman Dewanganf27ed352012-11-29 19:33:02 -050094static int current_to_reg(const unsigned int *tbl,
95 size_t size, unsigned int val)
96{
97 size_t i;
98
99 for (i = 0; i < size; i++)
100 if (val < tbl[i])
101 break;
102 return i > 0 ? i - 1 : -EINVAL;
103}
104
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530105static int bq2419x_charger_enable(struct bq2419x_chip *bq2419x)
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500106{
107 int ret;
Rohith Seelaboyina2fd2e112013-02-02 18:10:54 +0530108
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530109 dev_info(bq2419x->dev, "Charging enabled\n");
110 ret = regmap_update_bits(bq2419x->regmap, BQ2419X_PWR_ON_REG,
111 BQ2419X_ENABLE_CHARGE_MASK, BQ2419X_ENABLE_CHARGE);
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500112 if (ret < 0)
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530113 dev_err(bq2419x->dev, "register update failed, err %d\n", ret);
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500114 return ret;
115}
116
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530117static int bq2419x_vbus_regulator_enable_time(struct regulator_dev *rdev)
118{
119 return 500000;
120}
121
122static int bq2419x_vbus_enable(struct regulator_dev *rdev)
123{
124 struct bq2419x_chip *bq2419x = rdev_get_drvdata(rdev);
125 int ret;
126
127 dev_info(bq2419x->dev, "VBUS enabled, charging disabled\n");
128 if (gpio_is_valid(bq2419x->gpio_otg_iusb))
129 gpio_set_value(bq2419x->gpio_otg_iusb, 1);
130
131 ret = regmap_update_bits(bq2419x->regmap, BQ2419X_PWR_ON_REG,
132 BQ2419X_ENABLE_CHARGE_MASK, BQ2419X_ENABLE_VBUS);
133 if (ret < 0)
134 dev_err(bq2419x->dev, "PWR_ON_REG update failed %d", ret);
135
136 return ret;
137}
138
139static int bq2419x_vbus_disable(struct regulator_dev *rdev)
140{
141 struct bq2419x_chip *bq2419x = rdev_get_drvdata(rdev);
142 int ret;
143
144 dev_info(bq2419x->dev, "VBUS disabled, charging enabled\n");
145 ret = bq2419x_charger_enable(bq2419x);
146 if (ret < 0) {
147 dev_err(bq2419x->dev, "Charger enable failed %d", ret);
148 return ret;
149 }
150
151 if (gpio_is_valid(bq2419x->gpio_otg_iusb))
152 gpio_set_value(bq2419x->gpio_otg_iusb, 0);
153
154 return ret;
155}
156
157static int bq2419x_vbus_is_enabled(struct regulator_dev *rdev)
158{
159 struct bq2419x_chip *bq2419x = rdev_get_drvdata(rdev);
160 int ret;
161 unsigned int data;
162
163 ret = regmap_read(bq2419x->regmap, BQ2419X_PWR_ON_REG, &data);
164 if (ret < 0) {
165 dev_err(bq2419x->dev, "PWR_ON_REG read failed %d", ret);
166 return ret;
167 }
168 return (data & BQ2419X_ENABLE_CHARGE_MASK) == BQ2419X_ENABLE_VBUS;
169}
170
171static struct regulator_ops bq2419x_vbus_ops = {
172 .enable = bq2419x_vbus_enable,
173 .disable = bq2419x_vbus_disable,
174 .is_enabled = bq2419x_vbus_is_enabled,
175 .enable_time = bq2419x_vbus_regulator_enable_time,
176};
177
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500178static int bq2419x_ac_get_property(struct power_supply *psy,
179 enum power_supply_property psp, union power_supply_propval *val)
180{
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530181 struct bq2419x_chip *bq2419x;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500182
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530183 bq2419x = container_of(psy, struct bq2419x_chip, ac);
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500184 if (psp == POWER_SUPPLY_PROP_ONLINE)
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530185 val->intval = bq2419x->ac_online;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500186 else
187 return -EINVAL;
188 return 0;
189}
190
191static int bq2419x_usb_get_property(struct power_supply *psy,
192 enum power_supply_property psp,
193 union power_supply_propval *val)
194{
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530195 struct bq2419x_chip *bq2419x;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500196
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530197 bq2419x = container_of(psy, struct bq2419x_chip, usb);
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500198 if (psp == POWER_SUPPLY_PROP_ONLINE)
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530199 val->intval = bq2419x->usb_online;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500200 else
201 return -EINVAL;
202 return 0;
203}
204
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530205static int bq2419x_init(struct bq2419x_chip *bq2419x)
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500206{
207 int val, ret = 0;
208
Syed Rafiuddinf2d004d2013-02-18 15:30:58 +0530209 /* Clear EN_HIZ */
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530210 ret = regmap_update_bits(bq2419x->regmap,
Syed Rafiuddinf2d004d2013-02-18 15:30:58 +0530211 BQ2419X_INPUT_SRC_REG, BQ2419X_EN_HIZ, 0);
212 if (ret < 0) {
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530213 dev_err(bq2419x->dev, "error reading reg: 0x%x\n",
Syed Rafiuddinf2d004d2013-02-18 15:30:58 +0530214 BQ2419X_INPUT_SRC_REG);
215 return ret;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500216 }
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530217
Syed Rafiuddinf2d004d2013-02-18 15:30:58 +0530218 /* Configure input current limit */
219 val = current_to_reg(iinlim, ARRAY_SIZE(iinlim),
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530220 bq2419x->in_current_limit);
Syed Rafiuddinf2d004d2013-02-18 15:30:58 +0530221 if (val < 0)
222 return 0;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500223
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530224 ret = regmap_update_bits(bq2419x->regmap,
Syed Rafiuddinb1ca04c2013-02-06 20:46:20 +0530225 BQ2419X_INPUT_SRC_REG, BQ2419x_CONFIG_MASK, val);
Syed Rafiuddinf2d004d2013-02-18 15:30:58 +0530226 if (ret < 0)
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530227 dev_err(bq2419x->dev, "error reading reg: 0x%x\n",
Syed Rafiuddinf2d004d2013-02-18 15:30:58 +0530228 BQ2419X_INPUT_SRC_REG);
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500229 return ret;
230}
231
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530232static int bq2419x_set_charging_current(struct regulator_dev *rdev,
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530233 int min_uA, int max_uA)
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500234{
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530235 struct bq2419x_chip *bq_charger = rdev_get_drvdata(rdev);
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530236 int ret = 0;
237 int val;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500238
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530239 dev_info(bq_charger->dev, "Setting charging current %d\n", max_uA/1000);
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530240 msleep(200);
241 bq_charger->usb_online = 0;
242 bq_charger->ac_online = 0;
243 bq_charger->status = 0;
244
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530245 ret = regmap_read(bq_charger->regmap, BQ2419X_SYS_STAT_REG, &val);
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530246 if (ret < 0)
247 dev_err(bq_charger->dev, "error reading reg: 0x%x\n",
248 BQ2419X_SYS_STAT_REG);
249
Rohith Seelaboyina2fd2e112013-02-02 18:10:54 +0530250 if (max_uA == 0 && val != 0)
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530251 return ret;
252
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530253 bq_charger->in_current_limit = max_uA/1000;
Syed Rafiuddin208dc892013-01-24 14:57:46 +0530254 if ((val & BQ2419x_VBUS_STAT) == BQ2419x_VBUS_UNKNOWN) {
255 bq_charger->status = 0;
Syed Rafiuddinb1ca04c2013-02-06 20:46:20 +0530256 bq_charger->usb_online = 0;
Syed Rafiuddinf2d004d2013-02-18 15:30:58 +0530257 bq_charger->in_current_limit = 500;
Syed Rafiuddin208dc892013-01-24 14:57:46 +0530258 ret = bq2419x_init(bq_charger);
259 if (ret < 0)
260 goto error;
261 if (bq_charger->update_status)
262 bq_charger->update_status
263 (bq_charger->status, 0);
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530264 } else if (bq_charger->in_current_limit == 500) {
Syed Rafiuddin208dc892013-01-24 14:57:46 +0530265 bq_charger->status = 1;
266 bq_charger->usb_online = 1;
Syed Rafiuddin208dc892013-01-24 14:57:46 +0530267 ret = bq2419x_init(bq_charger);
268 if (ret < 0)
269 goto error;
270 if (bq_charger->update_status)
271 bq_charger->update_status
272 (bq_charger->status, 2);
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530273 } else {
Syed Rafiuddin208dc892013-01-24 14:57:46 +0530274 bq_charger->status = 1;
275 bq_charger->ac_online = 1;
Syed Rafiuddin208dc892013-01-24 14:57:46 +0530276 ret = bq2419x_init(bq_charger);
277 if (ret < 0)
278 goto error;
279 if (bq_charger->update_status)
280 bq_charger->update_status
281 (bq_charger->status, 1);
282 }
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530283 if (ret == 0) {
284 if (bq_charger->use_mains)
285 power_supply_changed(&bq_charger->ac);
286 if (bq_charger->use_usb)
287 power_supply_changed(&bq_charger->usb);
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500288 }
Syed Rafiuddin208dc892013-01-24 14:57:46 +0530289 return 0;
290error:
291 dev_err(bq_charger->dev, "Charger enable failed, err = %d\n", ret);
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530292 return ret;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500293}
294
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530295static struct regulator_ops bq2419x_tegra_regulator_ops = {
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530296 .set_current_limit = bq2419x_set_charging_current,
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530297};
298
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530299static int bq2419x_reset_wdt(struct bq2419x_chip *bq2419x)
Kunal Agrawal24680952013-01-29 14:44:19 +0530300{
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530301 int ret;
302 unsigned int reg01;
Kunal Agrawal24680952013-01-29 14:44:19 +0530303
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530304 dev_info(bq2419x->dev, "%s() resetting BQWDT\n", __func__);
305 ret = regmap_read(bq2419x->regmap, BQ2419X_PWR_ON_REG, &reg01);
306 if (ret < 0) {
307 dev_err(bq2419x->dev, "PWR_ON_REG read failed: %d\n", ret);
308 return ret;
309 }
310
311 reg01 |= BIT(6);
312
313 /* Write two times to make sure reset WDT */
314 ret = regmap_write(bq2419x->regmap, BQ2419X_PWR_ON_REG, reg01);
315 if (ret < 0) {
316 dev_err(bq2419x->dev, "PWR_ON_REG write failed: %d\n", ret);
317 return ret;
318 }
319 ret = regmap_write(bq2419x->regmap, BQ2419X_PWR_ON_REG, reg01);
320 if (ret < 0) {
321 dev_err(bq2419x->dev, "PWR_ON_REG write failed: %d\n", ret);
322 return ret;
323 }
324 return ret;
Kunal Agrawal24680952013-01-29 14:44:19 +0530325}
326
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530327static int bq2419x_watchdog_set_timeout(struct bq2419x_chip *bq2419x,
328 int timeout)
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500329{
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500330 int ret;
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530331 unsigned int val;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500332
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530333 /* Disable WDT first */
334 ret = regmap_update_bits(bq2419x->regmap, BQ2419X_TIME_CTRL_REG,
335 BQ2419X_WD_MASK, BQ2419X_WD_DISABLE);
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500336 if (ret < 0) {
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530337 dev_err(bq2419x->dev, "TIME_CTRL_REG update failed %d\n", ret);
338 return ret;
339 }
340
341 if (!timeout)
342 return ret;
343
344 if (timeout <= 60) {
345 val = BQ2419X_WD_40ms;
346 bq2419x->wdt_refresh_timeout = 25;
347 } else if (timeout <= 120) {
348 val = BQ2419X_WD_80ms;
349 bq2419x->wdt_refresh_timeout = 50;
350 } else {
351 val = BQ2419X_WD_160ms;
352 bq2419x->wdt_refresh_timeout = 125;
353 }
354
355 ret = regmap_update_bits(bq2419x->regmap, BQ2419X_TIME_CTRL_REG,
356 BQ2419X_WD_MASK, val);
357 if (ret < 0) {
358 dev_err(bq2419x->dev, "TIME_CTRL_REG update failed %d\n", ret);
359 return ret;
360 }
361 return ret;
362}
363
364static void bq2419x_work_thread(struct kthread_work *work)
365{
366 struct bq2419x_chip *bq2419x = container_of(work,
367 struct bq2419x_chip, bq_wdt_work);
368 int ret;
369
370 for (;;) {
371 if (bq2419x->stop_thread)
372 return;
373
374 ret = bq2419x_reset_wdt(bq2419x);
375 if (ret < 0)
376 dev_err(bq2419x->dev,
377 "bq2419x_reset_wdt failed: %d\n", ret);
378
379 msleep(bq2419x->wdt_refresh_timeout * 1000);
380 }
381}
382
383static irqreturn_t bq2419x_irq(int irq, void *data)
384{
385 struct bq2419x_chip *bq2419x = data;
386 int ret;
387 unsigned int val;
388
389 ret = regmap_read(bq2419x->regmap, BQ2419X_FAULT_REG, &val);
390 if (ret < 0) {
391 dev_err(bq2419x->dev, "FAULT_REG read failed %d\n", ret);
392 return ret;
393 }
394
395 dev_info(bq2419x->dev, "%s() Irq %d status 0x%02x\n",
396 __func__, irq, val);
397
398 if (val & BQ2419x_FAULT_WATCHDOG_FAULT) {
399 dev_err(bq2419x->dev,
400 "Charging Fault: Watchdog Timer Expired\n");
401 bq2419x_reset_wdt(bq2419x);
402 }
403
404 if (val & BQ2419x_FAULT_BOOST_FAULT)
405 dev_err(bq2419x->dev, "Charging Fault: VBUS Overloaded\n");
406
407 switch (val & BQ2419x_FAULT_CHRG_FAULT_MASK) {
408 case BQ2419x_FAULT_CHRG_INPUT:
409 dev_err(bq2419x->dev, "Charging Fault: "
410 "Input Fault (VBUS OVP or VBAT<VBUS<3.8V)\n");
411 break;
412 case BQ2419x_FAULT_CHRG_THERMAL:
413 dev_err(bq2419x->dev, "Charging Fault: Thermal shutdown\n");
414 break;
415 case BQ2419x_FAULT_CHRG_SAFTY:
416 dev_err(bq2419x->dev,
417 "Charging Fault: Safety timer expiration\n");
418 break;
419 default:
420 break;
421 }
422
423 if (val & BQ2419x_FAULT_NTC_FAULT)
424 dev_err(bq2419x->dev, "Charging Fault: NTC fault %d\n",
425 val & BQ2419x_FAULT_NTC_FAULT);
426
427 ret = regmap_read(bq2419x->regmap, BQ2419X_SYS_STAT_REG, &val);
428 if (ret < 0) {
429 dev_err(bq2419x->dev, "SYS_STAT_REG read failed %d\n", ret);
430 return ret;
431 }
432
433 if ((val & BQ2419x_CHRG_STATE_MASK) == BQ2419x_CHRG_STATE_CHARGE_DONE)
434 dev_info(bq2419x->dev, "Charging completed\n");
435
436 return IRQ_HANDLED;
437}
438
439static int bq2419x_init_charger_regulator(struct bq2419x_chip *bq2419x,
440 struct bq2419x_platform_data *pdata)
441{
442 int ret = 0;
443
444 if (!pdata->bcharger_pdata) {
445 dev_err(bq2419x->dev, "No charger platform data\n");
446 return 0;
447 }
448
449 bq2419x->chg_reg_desc.name = "bq2419x-charger";
450 bq2419x->chg_reg_desc.ops = &bq2419x_tegra_regulator_ops;
451 bq2419x->chg_reg_desc.type = REGULATOR_CURRENT;
452 bq2419x->chg_reg_desc.owner = THIS_MODULE;
453
454 bq2419x->chg_reg_init_data.supply_regulator = NULL;
455 bq2419x->chg_reg_init_data.regulator_init = NULL;
456 bq2419x->chg_reg_init_data.num_consumer_supplies =
457 pdata->bcharger_pdata->num_consumer_supplies;
458 bq2419x->chg_reg_init_data.consumer_supplies =
459 pdata->bcharger_pdata->consumer_supplies;
460 bq2419x->chg_reg_init_data.driver_data = bq2419x;
461 bq2419x->chg_reg_init_data.constraints.name = "bq2419x-charger";
462 bq2419x->chg_reg_init_data.constraints.min_uA = 0;
463 bq2419x->chg_reg_init_data.constraints.max_uA =
464 pdata->bcharger_pdata->max_charge_current_mA * 1000;
465
466 bq2419x->chg_reg_init_data.constraints.valid_modes_mask =
467 REGULATOR_MODE_NORMAL |
468 REGULATOR_MODE_STANDBY;
469
470 bq2419x->chg_reg_init_data.constraints.valid_ops_mask =
471 REGULATOR_CHANGE_MODE |
472 REGULATOR_CHANGE_STATUS |
473 REGULATOR_CHANGE_CURRENT;
474
475 bq2419x->chg_rdev = regulator_register(&bq2419x->chg_reg_desc,
476 bq2419x->dev, &bq2419x->chg_reg_init_data,
477 bq2419x, NULL);
478 if (IS_ERR(bq2419x->chg_rdev)) {
479 ret = PTR_ERR(bq2419x->chg_rdev);
480 dev_err(bq2419x->dev,
481 "vbus-charger regulator register failed %d\n", ret);
482 }
483 return ret;
484}
485
486static int bq2419x_init_vbus_regulator(struct bq2419x_chip *bq2419x,
487 struct bq2419x_platform_data *pdata)
488{
489 int ret = 0;
490
491 if (!pdata->vbus_pdata) {
492 dev_err(bq2419x->dev, "No vbus platform data\n");
493 return 0;
494 }
495
496 bq2419x->gpio_otg_iusb = pdata->vbus_pdata->gpio_otg_iusb;
497 bq2419x->vbus_reg_desc.name = "bq2419x-vbus";
498 bq2419x->vbus_reg_desc.ops = &bq2419x_vbus_ops;
499 bq2419x->vbus_reg_desc.type = REGULATOR_VOLTAGE;
500 bq2419x->vbus_reg_desc.owner = THIS_MODULE;
501
502 bq2419x->vbus_reg_init_data.supply_regulator = NULL;
503 bq2419x->vbus_reg_init_data.regulator_init = NULL;
504 bq2419x->vbus_reg_init_data.num_consumer_supplies =
505 pdata->vbus_pdata->num_consumer_supplies;
506 bq2419x->vbus_reg_init_data.consumer_supplies =
507 pdata->vbus_pdata->consumer_supplies;
508 bq2419x->vbus_reg_init_data.driver_data = bq2419x;
509
510 bq2419x->vbus_reg_init_data.constraints.name = "bq2419x-vbus";
511 bq2419x->vbus_reg_init_data.constraints.min_uV = 0;
512 bq2419x->vbus_reg_init_data.constraints.max_uV = 5000000,
513 bq2419x->vbus_reg_init_data.constraints.valid_modes_mask =
514 REGULATOR_MODE_NORMAL |
515 REGULATOR_MODE_STANDBY;
516 bq2419x->vbus_reg_init_data.constraints.valid_ops_mask =
517 REGULATOR_CHANGE_MODE |
518 REGULATOR_CHANGE_STATUS |
519 REGULATOR_CHANGE_VOLTAGE;
520
521 if (gpio_is_valid(bq2419x->gpio_otg_iusb)) {
522 ret = gpio_request_one(bq2419x->gpio_otg_iusb,
523 GPIOF_OUT_INIT_LOW, dev_name(bq2419x->dev));
524 if (ret < 0) {
525 dev_err(bq2419x->dev, "gpio request failed %d\n", ret);
526 return ret;
527 }
528 }
529
530 /* Register the regulators */
531 bq2419x->vbus_rdev = regulator_register(&bq2419x->vbus_reg_desc,
532 bq2419x->dev, &bq2419x->vbus_reg_init_data,
533 bq2419x, NULL);
534 if (IS_ERR(bq2419x->vbus_rdev)) {
535 ret = PTR_ERR(bq2419x->vbus_rdev);
536 dev_err(bq2419x->dev,
537 "VBUS regulator register failed %d\n", ret);
538 goto scrub;
539 }
540
541 /* Disable the VBUS regulator and enable charging */
542 ret = bq2419x_charger_enable(bq2419x);
543 if (ret < 0) {
544 dev_err(bq2419x->dev, "Charging enable failed %d", ret);
545 goto scrub_reg;
546 }
547 return ret;
548
549scrub_reg:
550 regulator_unregister(bq2419x->vbus_rdev);
551 bq2419x->vbus_rdev = NULL;
552scrub:
553 if (gpio_is_valid(bq2419x->gpio_otg_iusb))
554 gpio_free(bq2419x->gpio_otg_iusb);
555 return ret;
556}
557
558static int bq2419x_psy_init(struct bq2419x_chip *bq2419x)
559{
560 int ret = 0;
561
562 bq2419x->ac_online = 0;
563 bq2419x->usb_online = 0;
564 bq2419x->status = 0;
565 if (bq2419x->use_mains) {
566 bq2419x->ac.name = "bq2419x-ac";
567 bq2419x->ac.type = POWER_SUPPLY_TYPE_MAINS;
568 bq2419x->ac.get_property = bq2419x_ac_get_property;
569 bq2419x->ac.properties = bq2419x_psy_props;
570 bq2419x->ac.num_properties = ARRAY_SIZE(bq2419x_psy_props);
571 ret = power_supply_register(bq2419x->dev, &bq2419x->ac);
572 if (ret < 0) {
573 dev_err(bq2419x->dev,
574 "AC power supply register failed %d\n", ret);
575 return ret;
576 }
577 }
578
579 if (bq2419x->use_usb) {
580 bq2419x->usb.name = "bq2419x-usb";
581 bq2419x->usb.type = POWER_SUPPLY_TYPE_USB;
582 bq2419x->usb.get_property = bq2419x_usb_get_property;
583 bq2419x->usb.properties = bq2419x_psy_props;
584 bq2419x->usb.num_properties = ARRAY_SIZE(bq2419x_psy_props);
585 ret = power_supply_register(bq2419x->dev, &bq2419x->usb);
586 if (ret) {
587 dev_err(bq2419x->dev,
588 "usb power supply register failed %d\n", ret);
589 goto scrub;
590 }
591 }
592 return ret;
593scrub:
594 if (bq2419x->use_mains)
595 power_supply_unregister(&bq2419x->ac);
596 return ret;
597}
598
599static int bq2419x_show_chip_version(struct bq2419x_chip *bq2419x)
600{
601 int ret;
602 unsigned int val;
603
604 ret = regmap_read(bq2419x->regmap, BQ2419X_REVISION_REG, &val);
605 if (ret < 0) {
606 dev_err(bq2419x->dev, "REVISION_REG read failed: %d\n", ret);
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500607 return ret;
608 }
609
610 if ((val & BQ24190_IC_VER) == BQ24190_IC_VER)
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530611 dev_info(bq2419x->dev, "chip type BQ24190 detected\n");
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530612 else if ((val & BQ24192_IC_VER) == BQ24192_IC_VER)
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530613 dev_info(bq2419x->dev, "chip type BQ2419X/3 detected\n");
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530614 else if ((val & BQ24192i_IC_VER) == BQ24192i_IC_VER)
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530615 dev_info(bq2419x->dev, "chip type BQ2419Xi detected\n");
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500616 return 0;
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530617}
Syed Rafiuddin208dc892013-01-24 14:57:46 +0530618
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530619static int bq2419x_charger_init(struct bq2419x_chip *bq2419x)
620{
621 int ret;
622
623 /* Configure Output Current Control to 3A*/
624 ret = regmap_write(bq2419x->regmap, BQ2419X_CHRG_CTRL_REG, 0xC0);
625 if (ret < 0) {
626 dev_err(bq2419x->dev, "CHRG_CTRL_REG write failed %d\n", ret);
627 return ret;
628 }
629
630 /*
631 * Configure Input voltage limit reset to OTP value,
632 * and charging current to 500mA.
633 */
634 ret = regmap_write(bq2419x->regmap, BQ2419X_INPUT_SRC_REG, 0x32);
635 if (ret < 0)
636 dev_err(bq2419x->dev, "INPUT_SRC_REG write failed %d\n", ret);
637
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500638 return ret;
639}
640
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530641static int __devinit bq2419x_probe(struct i2c_client *client,
642 const struct i2c_device_id *id)
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500643{
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530644 struct bq2419x_chip *bq2419x;
645 struct bq2419x_platform_data *pdata;
646 int ret = 0;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500647
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530648 pdata = client->dev.platform_data;
649 if (!pdata) {
650 dev_err(&client->dev, "No Platform data");
651 return -EINVAL;
652 }
653
654 bq2419x = devm_kzalloc(&client->dev, sizeof(*bq2419x), GFP_KERNEL);
655 if (!bq2419x) {
656 dev_err(&client->dev, "Memory allocation failed\n");
657 return -ENOMEM;
658 }
659
660 bq2419x->regmap = devm_regmap_init_i2c(client, &bq2419x_regmap_config);
661 if (IS_ERR(bq2419x->regmap)) {
662 ret = PTR_ERR(bq2419x->regmap);
663 dev_err(&client->dev, "regmap init failed with err %d\n", ret);
664 return ret;
665 }
666
667 bq2419x->dev = &client->dev;
668 bq2419x->use_usb = pdata->bcharger_pdata->use_usb;
669 bq2419x->use_mains = pdata->bcharger_pdata->use_mains;
670 bq2419x->update_status = pdata->bcharger_pdata->update_status;
671 bq2419x->wdt_refresh_timeout = 25;
672 i2c_set_clientdata(client, bq2419x);
673 bq2419x->irq = client->irq;
674
675 ret = bq2419x_show_chip_version(bq2419x);
676 if (ret < 0) {
677 dev_err(&client->dev, "version read failed %d\n", ret);
678 return ret;
679 }
680
681 ret = bq2419x_charger_init(bq2419x);
682 if (ret < 0) {
683 dev_err(bq2419x->dev, "Charger init failed: %d\n", ret);
684 return ret;
685 }
686
687 ret = bq2419x_watchdog_set_timeout(bq2419x,
688 pdata->bcharger_pdata->wdt_timeout);
689 if (ret < 0) {
690 dev_err(bq2419x->dev, "BQWDT init failed %d\n", ret);
691 return ret;
692 }
693
694 ret = bq2419x_init_charger_regulator(bq2419x, pdata);
695 if (ret < 0) {
696 dev_err(&client->dev,
697 "Charger regualtor init failed %d\n", ret);
698 return ret;
699 }
700
701 ret = bq2419x_psy_init(bq2419x);
702 if (ret < 0) {
703 dev_err(&client->dev,
704 "Charger power supply init failed %d\n", ret);
705 goto scrub_chg_reg;
706 }
707
708 ret = bq2419x_init_vbus_regulator(bq2419x, pdata);
709 if (ret < 0) {
710 dev_err(&client->dev,
711 "VBUS regualtor init failed %d\n", ret);
712 goto scrub_psy;
713 }
714
715
716 init_kthread_worker(&bq2419x->bq_kworker);
717 bq2419x->bq_kworker_task = kthread_run(kthread_worker_fn,
718 &bq2419x->bq_kworker,
719 dev_name(bq2419x->dev));
720 if (IS_ERR(bq2419x->bq_kworker_task)) {
721 ret = PTR_ERR(bq2419x->bq_kworker_task);
722 dev_err(&client->dev, "Kworker task creation failed %d\n", ret);
723 goto scrub_vbus_reg;
724 }
725
726 init_kthread_work(&bq2419x->bq_wdt_work, bq2419x_work_thread);
727 sched_setscheduler(bq2419x->bq_kworker_task,
728 SCHED_FIFO, &bq2419x_param);
729 queue_kthread_work(&bq2419x->bq_kworker, &bq2419x->bq_wdt_work);
730
731 ret = request_threaded_irq(bq2419x->irq, NULL,
732 bq2419x_irq, IRQF_TRIGGER_FALLING,
733 dev_name(bq2419x->dev), bq2419x);
734 if (ret < 0) {
735 dev_err(bq2419x->dev, "request IRQ %d fail, err = %d\n",
736 bq2419x->irq, ret);
737 goto scrub_kthread;
738 }
739
740 /* enable charging */
741 ret = bq2419x_charger_enable(bq2419x);
742 if (ret < 0)
743 goto scrub_irq;
744
745 return 0;
746
747scrub_irq:
748 free_irq(bq2419x->irq, bq2419x);
749scrub_kthread:
750 bq2419x->stop_thread = true;
751 flush_kthread_worker(&bq2419x->bq_kworker);
752 kthread_stop(bq2419x->bq_kworker_task);
753scrub_vbus_reg:
754 regulator_unregister(bq2419x->vbus_rdev);
755scrub_psy:
756 if (bq2419x->use_usb)
757 power_supply_unregister(&bq2419x->usb);
758 if (bq2419x->use_mains)
759 power_supply_unregister(&bq2419x->ac);
760scrub_chg_reg:
761 regulator_unregister(bq2419x->chg_rdev);
762 return ret;
763}
764
765static int __devexit bq2419x_remove(struct i2c_client *client)
766{
767 struct bq2419x_chip *bq2419x = i2c_get_clientdata(client);
768
769 free_irq(bq2419x->irq, bq2419x);
770 bq2419x->stop_thread = true;
771 flush_kthread_worker(&bq2419x->bq_kworker);
772 kthread_stop(bq2419x->bq_kworker_task);
773 regulator_unregister(bq2419x->vbus_rdev);
774 if (bq2419x->use_usb)
775 power_supply_unregister(&bq2419x->usb);
776 if (bq2419x->use_mains)
777 power_supply_unregister(&bq2419x->ac);
778 regulator_unregister(bq2419x->chg_rdev);
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500779 return 0;
780}
781
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530782static const struct i2c_device_id bq2419x_id[] = {
783 {.name = "bq2419x",},
784 {},
785};
786MODULE_DEVICE_TABLE(i2c, bq2419x_id);
787
788static struct i2c_driver bq2419x_i2c_driver = {
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500789 .driver = {
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530790 .name = "bq2419x",
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500791 .owner = THIS_MODULE,
792 },
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530793 .probe = bq2419x_probe,
794 .remove = __devexit_p(bq2419x_remove),
795 .id_table = bq2419x_id,
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500796};
797
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530798static int __init bq2419x_module_init(void)
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530799{
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530800 return i2c_add_driver(&bq2419x_i2c_driver);
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530801}
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530802subsys_initcall(bq2419x_module_init);
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530803
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530804static void __exit bq2419x_cleanup(void)
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530805{
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530806 i2c_del_driver(&bq2419x_i2c_driver);
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530807}
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530808module_exit(bq2419x_cleanup);
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500809
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530810MODULE_DESCRIPTION("BQ24190/BQ24192/BQ24192i/BQ24193 battery charger driver");
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500811MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530812MODULE_AUTHOR("Syed Rafiuddin <srafiuddin@nvidia.com");
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500813MODULE_LICENSE("GPL v2");