blob: c80ea574d1a764c5029b1914ccb45883245c723e [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 Rafiuddin8fa48bf2013-03-05 10:31:20 +0530327static int bq2419x_watchdog_clear_sts(struct bq2419x_chip *bq2419x,
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530328 int timeout)
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500329{
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500330 int ret;
Syed Rafiuddin8fa48bf2013-03-05 10:31:20 +0530331 unsigned int reg09;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500332
Syed Rafiuddin8fa48bf2013-03-05 10:31:20 +0530333 ret = regmap_read(bq2419x->regmap, BQ2419X_FAULT_REG, &reg09);
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500334 if (ret < 0) {
Syed Rafiuddin8fa48bf2013-03-05 10:31:20 +0530335 dev_err(bq2419x->dev, "FAULT_REG read failed: %d\n", ret);
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530336 return ret;
337 }
338
Syed Rafiuddin8fa48bf2013-03-05 10:31:20 +0530339 ret = regmap_read(bq2419x->regmap, BQ2419X_FAULT_REG, &reg09);
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530340 if (ret < 0) {
Syed Rafiuddin8fa48bf2013-03-05 10:31:20 +0530341 dev_err(bq2419x->dev, "FAULT_REG read failed: %d\n", ret);
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530342 return ret;
343 }
Syed Rafiuddin8fa48bf2013-03-05 10:31:20 +0530344
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530345 return ret;
346}
347
348static void bq2419x_work_thread(struct kthread_work *work)
349{
350 struct bq2419x_chip *bq2419x = container_of(work,
351 struct bq2419x_chip, bq_wdt_work);
352 int ret;
353
354 for (;;) {
355 if (bq2419x->stop_thread)
356 return;
357
358 ret = bq2419x_reset_wdt(bq2419x);
359 if (ret < 0)
360 dev_err(bq2419x->dev,
361 "bq2419x_reset_wdt failed: %d\n", ret);
362
363 msleep(bq2419x->wdt_refresh_timeout * 1000);
364 }
365}
366
367static irqreturn_t bq2419x_irq(int irq, void *data)
368{
369 struct bq2419x_chip *bq2419x = data;
370 int ret;
371 unsigned int val;
372
373 ret = regmap_read(bq2419x->regmap, BQ2419X_FAULT_REG, &val);
374 if (ret < 0) {
375 dev_err(bq2419x->dev, "FAULT_REG read failed %d\n", ret);
376 return ret;
377 }
378
379 dev_info(bq2419x->dev, "%s() Irq %d status 0x%02x\n",
380 __func__, irq, val);
381
382 if (val & BQ2419x_FAULT_WATCHDOG_FAULT) {
383 dev_err(bq2419x->dev,
384 "Charging Fault: Watchdog Timer Expired\n");
385 bq2419x_reset_wdt(bq2419x);
386 }
387
388 if (val & BQ2419x_FAULT_BOOST_FAULT)
389 dev_err(bq2419x->dev, "Charging Fault: VBUS Overloaded\n");
390
391 switch (val & BQ2419x_FAULT_CHRG_FAULT_MASK) {
392 case BQ2419x_FAULT_CHRG_INPUT:
393 dev_err(bq2419x->dev, "Charging Fault: "
394 "Input Fault (VBUS OVP or VBAT<VBUS<3.8V)\n");
395 break;
396 case BQ2419x_FAULT_CHRG_THERMAL:
397 dev_err(bq2419x->dev, "Charging Fault: Thermal shutdown\n");
398 break;
399 case BQ2419x_FAULT_CHRG_SAFTY:
400 dev_err(bq2419x->dev,
401 "Charging Fault: Safety timer expiration\n");
402 break;
403 default:
404 break;
405 }
406
407 if (val & BQ2419x_FAULT_NTC_FAULT)
408 dev_err(bq2419x->dev, "Charging Fault: NTC fault %d\n",
409 val & BQ2419x_FAULT_NTC_FAULT);
410
411 ret = regmap_read(bq2419x->regmap, BQ2419X_SYS_STAT_REG, &val);
412 if (ret < 0) {
413 dev_err(bq2419x->dev, "SYS_STAT_REG read failed %d\n", ret);
414 return ret;
415 }
416
417 if ((val & BQ2419x_CHRG_STATE_MASK) == BQ2419x_CHRG_STATE_CHARGE_DONE)
418 dev_info(bq2419x->dev, "Charging completed\n");
419
420 return IRQ_HANDLED;
421}
422
423static int bq2419x_init_charger_regulator(struct bq2419x_chip *bq2419x,
424 struct bq2419x_platform_data *pdata)
425{
426 int ret = 0;
427
428 if (!pdata->bcharger_pdata) {
429 dev_err(bq2419x->dev, "No charger platform data\n");
430 return 0;
431 }
432
433 bq2419x->chg_reg_desc.name = "bq2419x-charger";
434 bq2419x->chg_reg_desc.ops = &bq2419x_tegra_regulator_ops;
435 bq2419x->chg_reg_desc.type = REGULATOR_CURRENT;
436 bq2419x->chg_reg_desc.owner = THIS_MODULE;
437
438 bq2419x->chg_reg_init_data.supply_regulator = NULL;
439 bq2419x->chg_reg_init_data.regulator_init = NULL;
440 bq2419x->chg_reg_init_data.num_consumer_supplies =
441 pdata->bcharger_pdata->num_consumer_supplies;
442 bq2419x->chg_reg_init_data.consumer_supplies =
443 pdata->bcharger_pdata->consumer_supplies;
444 bq2419x->chg_reg_init_data.driver_data = bq2419x;
445 bq2419x->chg_reg_init_data.constraints.name = "bq2419x-charger";
446 bq2419x->chg_reg_init_data.constraints.min_uA = 0;
447 bq2419x->chg_reg_init_data.constraints.max_uA =
448 pdata->bcharger_pdata->max_charge_current_mA * 1000;
449
450 bq2419x->chg_reg_init_data.constraints.valid_modes_mask =
451 REGULATOR_MODE_NORMAL |
452 REGULATOR_MODE_STANDBY;
453
454 bq2419x->chg_reg_init_data.constraints.valid_ops_mask =
455 REGULATOR_CHANGE_MODE |
456 REGULATOR_CHANGE_STATUS |
457 REGULATOR_CHANGE_CURRENT;
458
459 bq2419x->chg_rdev = regulator_register(&bq2419x->chg_reg_desc,
460 bq2419x->dev, &bq2419x->chg_reg_init_data,
461 bq2419x, NULL);
462 if (IS_ERR(bq2419x->chg_rdev)) {
463 ret = PTR_ERR(bq2419x->chg_rdev);
464 dev_err(bq2419x->dev,
465 "vbus-charger regulator register failed %d\n", ret);
466 }
467 return ret;
468}
469
470static int bq2419x_init_vbus_regulator(struct bq2419x_chip *bq2419x,
471 struct bq2419x_platform_data *pdata)
472{
473 int ret = 0;
474
475 if (!pdata->vbus_pdata) {
476 dev_err(bq2419x->dev, "No vbus platform data\n");
477 return 0;
478 }
479
480 bq2419x->gpio_otg_iusb = pdata->vbus_pdata->gpio_otg_iusb;
481 bq2419x->vbus_reg_desc.name = "bq2419x-vbus";
482 bq2419x->vbus_reg_desc.ops = &bq2419x_vbus_ops;
483 bq2419x->vbus_reg_desc.type = REGULATOR_VOLTAGE;
484 bq2419x->vbus_reg_desc.owner = THIS_MODULE;
485
486 bq2419x->vbus_reg_init_data.supply_regulator = NULL;
487 bq2419x->vbus_reg_init_data.regulator_init = NULL;
488 bq2419x->vbus_reg_init_data.num_consumer_supplies =
489 pdata->vbus_pdata->num_consumer_supplies;
490 bq2419x->vbus_reg_init_data.consumer_supplies =
491 pdata->vbus_pdata->consumer_supplies;
492 bq2419x->vbus_reg_init_data.driver_data = bq2419x;
493
494 bq2419x->vbus_reg_init_data.constraints.name = "bq2419x-vbus";
495 bq2419x->vbus_reg_init_data.constraints.min_uV = 0;
496 bq2419x->vbus_reg_init_data.constraints.max_uV = 5000000,
497 bq2419x->vbus_reg_init_data.constraints.valid_modes_mask =
498 REGULATOR_MODE_NORMAL |
499 REGULATOR_MODE_STANDBY;
500 bq2419x->vbus_reg_init_data.constraints.valid_ops_mask =
501 REGULATOR_CHANGE_MODE |
502 REGULATOR_CHANGE_STATUS |
503 REGULATOR_CHANGE_VOLTAGE;
504
505 if (gpio_is_valid(bq2419x->gpio_otg_iusb)) {
506 ret = gpio_request_one(bq2419x->gpio_otg_iusb,
507 GPIOF_OUT_INIT_LOW, dev_name(bq2419x->dev));
508 if (ret < 0) {
509 dev_err(bq2419x->dev, "gpio request failed %d\n", ret);
510 return ret;
511 }
512 }
513
514 /* Register the regulators */
515 bq2419x->vbus_rdev = regulator_register(&bq2419x->vbus_reg_desc,
516 bq2419x->dev, &bq2419x->vbus_reg_init_data,
517 bq2419x, NULL);
518 if (IS_ERR(bq2419x->vbus_rdev)) {
519 ret = PTR_ERR(bq2419x->vbus_rdev);
520 dev_err(bq2419x->dev,
521 "VBUS regulator register failed %d\n", ret);
522 goto scrub;
523 }
524
525 /* Disable the VBUS regulator and enable charging */
526 ret = bq2419x_charger_enable(bq2419x);
527 if (ret < 0) {
528 dev_err(bq2419x->dev, "Charging enable failed %d", ret);
529 goto scrub_reg;
530 }
531 return ret;
532
533scrub_reg:
534 regulator_unregister(bq2419x->vbus_rdev);
535 bq2419x->vbus_rdev = NULL;
536scrub:
537 if (gpio_is_valid(bq2419x->gpio_otg_iusb))
538 gpio_free(bq2419x->gpio_otg_iusb);
539 return ret;
540}
541
542static int bq2419x_psy_init(struct bq2419x_chip *bq2419x)
543{
544 int ret = 0;
545
546 bq2419x->ac_online = 0;
547 bq2419x->usb_online = 0;
548 bq2419x->status = 0;
549 if (bq2419x->use_mains) {
550 bq2419x->ac.name = "bq2419x-ac";
551 bq2419x->ac.type = POWER_SUPPLY_TYPE_MAINS;
552 bq2419x->ac.get_property = bq2419x_ac_get_property;
553 bq2419x->ac.properties = bq2419x_psy_props;
554 bq2419x->ac.num_properties = ARRAY_SIZE(bq2419x_psy_props);
555 ret = power_supply_register(bq2419x->dev, &bq2419x->ac);
556 if (ret < 0) {
557 dev_err(bq2419x->dev,
558 "AC power supply register failed %d\n", ret);
559 return ret;
560 }
561 }
562
563 if (bq2419x->use_usb) {
564 bq2419x->usb.name = "bq2419x-usb";
565 bq2419x->usb.type = POWER_SUPPLY_TYPE_USB;
566 bq2419x->usb.get_property = bq2419x_usb_get_property;
567 bq2419x->usb.properties = bq2419x_psy_props;
568 bq2419x->usb.num_properties = ARRAY_SIZE(bq2419x_psy_props);
569 ret = power_supply_register(bq2419x->dev, &bq2419x->usb);
570 if (ret) {
571 dev_err(bq2419x->dev,
572 "usb power supply register failed %d\n", ret);
573 goto scrub;
574 }
575 }
576 return ret;
577scrub:
578 if (bq2419x->use_mains)
579 power_supply_unregister(&bq2419x->ac);
580 return ret;
581}
582
583static int bq2419x_show_chip_version(struct bq2419x_chip *bq2419x)
584{
585 int ret;
586 unsigned int val;
587
588 ret = regmap_read(bq2419x->regmap, BQ2419X_REVISION_REG, &val);
589 if (ret < 0) {
590 dev_err(bq2419x->dev, "REVISION_REG read failed: %d\n", ret);
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500591 return ret;
592 }
593
594 if ((val & BQ24190_IC_VER) == BQ24190_IC_VER)
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530595 dev_info(bq2419x->dev, "chip type BQ24190 detected\n");
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530596 else if ((val & BQ24192_IC_VER) == BQ24192_IC_VER)
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530597 dev_info(bq2419x->dev, "chip type BQ2419X/3 detected\n");
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530598 else if ((val & BQ24192i_IC_VER) == BQ24192i_IC_VER)
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530599 dev_info(bq2419x->dev, "chip type BQ2419Xi detected\n");
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500600 return 0;
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530601}
Syed Rafiuddin208dc892013-01-24 14:57:46 +0530602
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530603static int bq2419x_charger_init(struct bq2419x_chip *bq2419x)
604{
605 int ret;
606
607 /* Configure Output Current Control to 3A*/
608 ret = regmap_write(bq2419x->regmap, BQ2419X_CHRG_CTRL_REG, 0xC0);
609 if (ret < 0) {
610 dev_err(bq2419x->dev, "CHRG_CTRL_REG write failed %d\n", ret);
611 return ret;
612 }
613
614 /*
615 * Configure Input voltage limit reset to OTP value,
616 * and charging current to 500mA.
617 */
618 ret = regmap_write(bq2419x->regmap, BQ2419X_INPUT_SRC_REG, 0x32);
619 if (ret < 0)
620 dev_err(bq2419x->dev, "INPUT_SRC_REG write failed %d\n", ret);
621
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500622 return ret;
623}
624
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530625static int __devinit bq2419x_probe(struct i2c_client *client,
626 const struct i2c_device_id *id)
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500627{
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530628 struct bq2419x_chip *bq2419x;
629 struct bq2419x_platform_data *pdata;
630 int ret = 0;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500631
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530632 pdata = client->dev.platform_data;
633 if (!pdata) {
634 dev_err(&client->dev, "No Platform data");
635 return -EINVAL;
636 }
637
638 bq2419x = devm_kzalloc(&client->dev, sizeof(*bq2419x), GFP_KERNEL);
639 if (!bq2419x) {
640 dev_err(&client->dev, "Memory allocation failed\n");
641 return -ENOMEM;
642 }
643
644 bq2419x->regmap = devm_regmap_init_i2c(client, &bq2419x_regmap_config);
645 if (IS_ERR(bq2419x->regmap)) {
646 ret = PTR_ERR(bq2419x->regmap);
647 dev_err(&client->dev, "regmap init failed with err %d\n", ret);
648 return ret;
649 }
650
651 bq2419x->dev = &client->dev;
652 bq2419x->use_usb = pdata->bcharger_pdata->use_usb;
653 bq2419x->use_mains = pdata->bcharger_pdata->use_mains;
654 bq2419x->update_status = pdata->bcharger_pdata->update_status;
655 bq2419x->wdt_refresh_timeout = 25;
656 i2c_set_clientdata(client, bq2419x);
657 bq2419x->irq = client->irq;
658
659 ret = bq2419x_show_chip_version(bq2419x);
660 if (ret < 0) {
661 dev_err(&client->dev, "version read failed %d\n", ret);
662 return ret;
663 }
664
665 ret = bq2419x_charger_init(bq2419x);
666 if (ret < 0) {
667 dev_err(bq2419x->dev, "Charger init failed: %d\n", ret);
668 return ret;
669 }
670
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530671 ret = bq2419x_init_charger_regulator(bq2419x, pdata);
672 if (ret < 0) {
673 dev_err(&client->dev,
674 "Charger regualtor init failed %d\n", ret);
675 return ret;
676 }
677
678 ret = bq2419x_psy_init(bq2419x);
679 if (ret < 0) {
680 dev_err(&client->dev,
681 "Charger power supply init failed %d\n", ret);
682 goto scrub_chg_reg;
683 }
684
685 ret = bq2419x_init_vbus_regulator(bq2419x, pdata);
686 if (ret < 0) {
687 dev_err(&client->dev,
688 "VBUS regualtor init failed %d\n", ret);
689 goto scrub_psy;
690 }
691
692
693 init_kthread_worker(&bq2419x->bq_kworker);
694 bq2419x->bq_kworker_task = kthread_run(kthread_worker_fn,
695 &bq2419x->bq_kworker,
696 dev_name(bq2419x->dev));
697 if (IS_ERR(bq2419x->bq_kworker_task)) {
698 ret = PTR_ERR(bq2419x->bq_kworker_task);
699 dev_err(&client->dev, "Kworker task creation failed %d\n", ret);
700 goto scrub_vbus_reg;
701 }
702
703 init_kthread_work(&bq2419x->bq_wdt_work, bq2419x_work_thread);
704 sched_setscheduler(bq2419x->bq_kworker_task,
705 SCHED_FIFO, &bq2419x_param);
706 queue_kthread_work(&bq2419x->bq_kworker, &bq2419x->bq_wdt_work);
707
Syed Rafiuddin8fa48bf2013-03-05 10:31:20 +0530708 ret = bq2419x_watchdog_clear_sts(bq2419x,
709 pdata->bcharger_pdata->wdt_timeout);
710 if (ret < 0) {
711 dev_err(bq2419x->dev, "BQWDT init failed %d\n", ret);
712 return ret;
713 }
714
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530715 ret = request_threaded_irq(bq2419x->irq, NULL,
716 bq2419x_irq, IRQF_TRIGGER_FALLING,
717 dev_name(bq2419x->dev), bq2419x);
718 if (ret < 0) {
719 dev_err(bq2419x->dev, "request IRQ %d fail, err = %d\n",
720 bq2419x->irq, ret);
721 goto scrub_kthread;
722 }
723
724 /* enable charging */
725 ret = bq2419x_charger_enable(bq2419x);
726 if (ret < 0)
727 goto scrub_irq;
728
729 return 0;
730
731scrub_irq:
732 free_irq(bq2419x->irq, bq2419x);
733scrub_kthread:
734 bq2419x->stop_thread = true;
735 flush_kthread_worker(&bq2419x->bq_kworker);
736 kthread_stop(bq2419x->bq_kworker_task);
737scrub_vbus_reg:
738 regulator_unregister(bq2419x->vbus_rdev);
739scrub_psy:
740 if (bq2419x->use_usb)
741 power_supply_unregister(&bq2419x->usb);
742 if (bq2419x->use_mains)
743 power_supply_unregister(&bq2419x->ac);
744scrub_chg_reg:
745 regulator_unregister(bq2419x->chg_rdev);
746 return ret;
747}
748
749static int __devexit bq2419x_remove(struct i2c_client *client)
750{
751 struct bq2419x_chip *bq2419x = i2c_get_clientdata(client);
752
753 free_irq(bq2419x->irq, bq2419x);
754 bq2419x->stop_thread = true;
755 flush_kthread_worker(&bq2419x->bq_kworker);
756 kthread_stop(bq2419x->bq_kworker_task);
757 regulator_unregister(bq2419x->vbus_rdev);
758 if (bq2419x->use_usb)
759 power_supply_unregister(&bq2419x->usb);
760 if (bq2419x->use_mains)
761 power_supply_unregister(&bq2419x->ac);
762 regulator_unregister(bq2419x->chg_rdev);
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500763 return 0;
764}
765
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530766static const struct i2c_device_id bq2419x_id[] = {
767 {.name = "bq2419x",},
768 {},
769};
770MODULE_DEVICE_TABLE(i2c, bq2419x_id);
771
772static struct i2c_driver bq2419x_i2c_driver = {
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500773 .driver = {
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530774 .name = "bq2419x",
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500775 .owner = THIS_MODULE,
776 },
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530777 .probe = bq2419x_probe,
778 .remove = __devexit_p(bq2419x_remove),
779 .id_table = bq2419x_id,
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500780};
781
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530782static int __init bq2419x_module_init(void)
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530783{
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530784 return i2c_add_driver(&bq2419x_i2c_driver);
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530785}
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530786subsys_initcall(bq2419x_module_init);
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530787
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530788static void __exit bq2419x_cleanup(void)
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530789{
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530790 i2c_del_driver(&bq2419x_i2c_driver);
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530791}
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530792module_exit(bq2419x_cleanup);
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500793
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530794MODULE_DESCRIPTION("BQ24190/BQ24192/BQ24192i/BQ24193 battery charger driver");
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500795MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530796MODULE_AUTHOR("Syed Rafiuddin <srafiuddin@nvidia.com");
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500797MODULE_LICENSE("GPL v2");