blob: 9957fc9087381617668072e2de097d894ceef510 [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>
Syed Rafiuddind4d862d2013-03-08 15:52:59 +053042#include <linux/rtc.h>
43#include <linux/alarmtimer.h>
Laxman Dewanganf27ed352012-11-29 19:33:02 -050044
45/* input current limit */
46static const unsigned int iinlim[] = {
47 100, 150, 500, 900, 1200, 1500, 2000, 3000,
48};
49
Syed Rafiuddin6126b492013-04-03 14:06:34 +053050/* Kthread scheduling parameters */
51struct sched_param bq2419x_param = {
52 .sched_priority = MAX_RT_PRIO - 1,
Laxman Dewanganf27ed352012-11-29 19:33:02 -050053};
54
Syed Rafiuddin6126b492013-04-03 14:06:34 +053055static const struct regmap_config bq2419x_regmap_config = {
56 .reg_bits = 8,
57 .val_bits = 8,
58 .max_register = BQ2419X_MAX_REGS,
59};
60
61struct bq2419x_chip {
62 struct device *dev;
63 struct regmap *regmap;
64 int irq;
65 int gpio_otg_iusb;
66 int wdt_refresh_timeout;
Pradeep Goudagunta7b7ac8b2013-03-11 18:13:59 +053067 int wdt_time_sec;
Syed Rafiuddin6126b492013-04-03 14:06:34 +053068
69 struct power_supply ac;
70 struct power_supply usb;
Pradeep Goudagunta13f38432013-03-09 23:58:03 +053071 struct mutex mutex;
Syed Rafiuddin6126b492013-04-03 14:06:34 +053072 int ac_online;
73 int usb_online;
74 int in_current_limit;
75 unsigned use_mains:1;
76 unsigned use_usb:1;
77 int status;
Syed Rafiuddind4d862d2013-03-08 15:52:59 +053078 int rtc_alarm_time;
Syed Rafiuddin6126b492013-04-03 14:06:34 +053079 void (*update_status)(int, int);
80
81 struct regulator_dev *chg_rdev;
82 struct regulator_desc chg_reg_desc;
83 struct regulator_init_data chg_reg_init_data;
84
85 struct regulator_dev *vbus_rdev;
86 struct regulator_desc vbus_reg_desc;
87 struct regulator_init_data vbus_reg_init_data;
88
89 struct kthread_worker bq_kworker;
90 struct task_struct *bq_kworker_task;
91 struct kthread_work bq_wdt_work;
Syed Rafiuddind4d862d2013-03-08 15:52:59 +053092 struct rtc_device *rtc;
Syed Rafiuddin6126b492013-04-03 14:06:34 +053093 int stop_thread;
Sang-Hun Lee7befcf92013-03-12 14:11:01 -070094 int suspended;
Pradeep Goudagunta6bf95072013-03-19 18:07:43 +053095 int chg_restart_timeout;
96 int chg_restart_time;
Syed Rafiuddin6126b492013-04-03 14:06:34 +053097};
Rohith Seelaboyina2fd2e112013-02-02 18:10:54 +053098
Laxman Dewanganf27ed352012-11-29 19:33:02 -050099static enum power_supply_property bq2419x_psy_props[] = {
100 POWER_SUPPLY_PROP_ONLINE,
101};
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530102
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500103static int current_to_reg(const unsigned int *tbl,
104 size_t size, unsigned int val)
105{
106 size_t i;
107
108 for (i = 0; i < size; i++)
109 if (val < tbl[i])
110 break;
111 return i > 0 ? i - 1 : -EINVAL;
112}
113
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530114static int bq2419x_charger_enable(struct bq2419x_chip *bq2419x)
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500115{
116 int ret;
Rohith Seelaboyina2fd2e112013-02-02 18:10:54 +0530117
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530118 dev_info(bq2419x->dev, "Charging enabled\n");
119 ret = regmap_update_bits(bq2419x->regmap, BQ2419X_PWR_ON_REG,
120 BQ2419X_ENABLE_CHARGE_MASK, BQ2419X_ENABLE_CHARGE);
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500121 if (ret < 0)
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530122 dev_err(bq2419x->dev, "register update failed, err %d\n", ret);
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500123 return ret;
124}
125
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530126static int bq2419x_vbus_regulator_enable_time(struct regulator_dev *rdev)
127{
128 return 500000;
129}
130
131static int bq2419x_vbus_enable(struct regulator_dev *rdev)
132{
133 struct bq2419x_chip *bq2419x = rdev_get_drvdata(rdev);
134 int ret;
135
136 dev_info(bq2419x->dev, "VBUS enabled, charging disabled\n");
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530137
138 ret = regmap_update_bits(bq2419x->regmap, BQ2419X_PWR_ON_REG,
139 BQ2419X_ENABLE_CHARGE_MASK, BQ2419X_ENABLE_VBUS);
140 if (ret < 0)
141 dev_err(bq2419x->dev, "PWR_ON_REG update failed %d", ret);
142
143 return ret;
144}
145
146static int bq2419x_vbus_disable(struct regulator_dev *rdev)
147{
148 struct bq2419x_chip *bq2419x = rdev_get_drvdata(rdev);
149 int ret;
150
151 dev_info(bq2419x->dev, "VBUS disabled, charging enabled\n");
152 ret = bq2419x_charger_enable(bq2419x);
153 if (ret < 0) {
154 dev_err(bq2419x->dev, "Charger enable failed %d", ret);
155 return ret;
156 }
157
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530158 return ret;
159}
160
161static int bq2419x_vbus_is_enabled(struct regulator_dev *rdev)
162{
163 struct bq2419x_chip *bq2419x = rdev_get_drvdata(rdev);
164 int ret;
165 unsigned int data;
166
167 ret = regmap_read(bq2419x->regmap, BQ2419X_PWR_ON_REG, &data);
168 if (ret < 0) {
169 dev_err(bq2419x->dev, "PWR_ON_REG read failed %d", ret);
170 return ret;
171 }
172 return (data & BQ2419X_ENABLE_CHARGE_MASK) == BQ2419X_ENABLE_VBUS;
173}
174
175static struct regulator_ops bq2419x_vbus_ops = {
176 .enable = bq2419x_vbus_enable,
177 .disable = bq2419x_vbus_disable,
178 .is_enabled = bq2419x_vbus_is_enabled,
179 .enable_time = bq2419x_vbus_regulator_enable_time,
180};
181
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500182static int bq2419x_ac_get_property(struct power_supply *psy,
183 enum power_supply_property psp, union power_supply_propval *val)
184{
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530185 struct bq2419x_chip *bq2419x;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500186
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530187 bq2419x = container_of(psy, struct bq2419x_chip, ac);
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500188 if (psp == POWER_SUPPLY_PROP_ONLINE)
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530189 val->intval = bq2419x->ac_online;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500190 else
191 return -EINVAL;
192 return 0;
193}
194
195static int bq2419x_usb_get_property(struct power_supply *psy,
196 enum power_supply_property psp,
197 union power_supply_propval *val)
198{
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530199 struct bq2419x_chip *bq2419x;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500200
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530201 bq2419x = container_of(psy, struct bq2419x_chip, usb);
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500202 if (psp == POWER_SUPPLY_PROP_ONLINE)
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530203 val->intval = bq2419x->usb_online;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500204 else
205 return -EINVAL;
206 return 0;
207}
208
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530209static int bq2419x_init(struct bq2419x_chip *bq2419x)
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500210{
211 int val, ret = 0;
212
Syed Rafiuddinf2d004d2013-02-18 15:30:58 +0530213 /* Clear EN_HIZ */
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530214 ret = regmap_update_bits(bq2419x->regmap,
Syed Rafiuddinf2d004d2013-02-18 15:30:58 +0530215 BQ2419X_INPUT_SRC_REG, BQ2419X_EN_HIZ, 0);
216 if (ret < 0) {
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530217 dev_err(bq2419x->dev, "error reading reg: 0x%x\n",
Syed Rafiuddinf2d004d2013-02-18 15:30:58 +0530218 BQ2419X_INPUT_SRC_REG);
219 return ret;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500220 }
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530221
Syed Rafiuddinf2d004d2013-02-18 15:30:58 +0530222 /* Configure input current limit */
223 val = current_to_reg(iinlim, ARRAY_SIZE(iinlim),
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530224 bq2419x->in_current_limit);
Syed Rafiuddinf2d004d2013-02-18 15:30:58 +0530225 if (val < 0)
226 return 0;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500227
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530228 ret = regmap_update_bits(bq2419x->regmap,
Syed Rafiuddinb1ca04c2013-02-06 20:46:20 +0530229 BQ2419X_INPUT_SRC_REG, BQ2419x_CONFIG_MASK, val);
Syed Rafiuddinf2d004d2013-02-18 15:30:58 +0530230 if (ret < 0)
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530231 dev_err(bq2419x->dev, "error reading reg: 0x%x\n",
Syed Rafiuddinf2d004d2013-02-18 15:30:58 +0530232 BQ2419X_INPUT_SRC_REG);
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500233 return ret;
234}
235
Syed Rafiuddin73692422013-03-08 16:36:09 +0530236static int bq2419x_charger_init(struct bq2419x_chip *bq2419x)
237{
238 int ret;
239
240 /* Configure Output Current Control to 3A*/
241 ret = regmap_write(bq2419x->regmap, BQ2419X_CHRG_CTRL_REG, 0xC0);
242 if (ret < 0) {
243 dev_err(bq2419x->dev, "CHRG_CTRL_REG write failed %d\n", ret);
244 return ret;
245 }
246
247 /*
248 * Configure Input voltage limit reset to OTP value,
249 * and charging current to 500mA.
250 */
251 ret = regmap_write(bq2419x->regmap, BQ2419X_INPUT_SRC_REG, 0x32);
252 if (ret < 0)
253 dev_err(bq2419x->dev, "INPUT_SRC_REG write failed %d\n", ret);
254
255 return ret;
256}
257
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530258static int bq2419x_set_charging_current(struct regulator_dev *rdev,
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530259 int min_uA, int max_uA)
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500260{
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530261 struct bq2419x_chip *bq_charger = rdev_get_drvdata(rdev);
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530262 int ret = 0;
263 int val;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500264
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530265 dev_info(bq_charger->dev, "Setting charging current %d\n", max_uA/1000);
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530266 msleep(200);
267 bq_charger->usb_online = 0;
268 bq_charger->ac_online = 0;
269 bq_charger->status = 0;
270
Pradeep Goudaguntada8f1f22013-03-10 01:16:33 +0530271 ret = bq2419x_charger_enable(bq_charger);
272 if (ret < 0) {
273 dev_err(bq_charger->dev, "Charger enable failed %d", ret);
274 return ret;
275 }
276
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530277 ret = regmap_read(bq_charger->regmap, BQ2419X_SYS_STAT_REG, &val);
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530278 if (ret < 0)
279 dev_err(bq_charger->dev, "error reading reg: 0x%x\n",
280 BQ2419X_SYS_STAT_REG);
281
Rohith Seelaboyina2fd2e112013-02-02 18:10:54 +0530282 if (max_uA == 0 && val != 0)
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530283 return ret;
284
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530285 bq_charger->in_current_limit = max_uA/1000;
Syed Rafiuddin208dc892013-01-24 14:57:46 +0530286 if ((val & BQ2419x_VBUS_STAT) == BQ2419x_VBUS_UNKNOWN) {
287 bq_charger->status = 0;
Syed Rafiuddinb1ca04c2013-02-06 20:46:20 +0530288 bq_charger->usb_online = 0;
Syed Rafiuddinf2d004d2013-02-18 15:30:58 +0530289 bq_charger->in_current_limit = 500;
Syed Rafiuddin208dc892013-01-24 14:57:46 +0530290 ret = bq2419x_init(bq_charger);
291 if (ret < 0)
292 goto error;
293 if (bq_charger->update_status)
294 bq_charger->update_status
295 (bq_charger->status, 0);
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530296 } else if (bq_charger->in_current_limit == 500) {
Syed Rafiuddin208dc892013-01-24 14:57:46 +0530297 bq_charger->status = 1;
298 bq_charger->usb_online = 1;
Syed Rafiuddin208dc892013-01-24 14:57:46 +0530299 ret = bq2419x_init(bq_charger);
300 if (ret < 0)
301 goto error;
302 if (bq_charger->update_status)
303 bq_charger->update_status
304 (bq_charger->status, 2);
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530305 } else {
Syed Rafiuddin208dc892013-01-24 14:57:46 +0530306 bq_charger->status = 1;
307 bq_charger->ac_online = 1;
Syed Rafiuddin208dc892013-01-24 14:57:46 +0530308 ret = bq2419x_init(bq_charger);
309 if (ret < 0)
310 goto error;
311 if (bq_charger->update_status)
312 bq_charger->update_status
313 (bq_charger->status, 1);
314 }
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530315 if (ret == 0) {
316 if (bq_charger->use_mains)
317 power_supply_changed(&bq_charger->ac);
318 if (bq_charger->use_usb)
319 power_supply_changed(&bq_charger->usb);
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500320 }
Syed Rafiuddin208dc892013-01-24 14:57:46 +0530321 return 0;
322error:
323 dev_err(bq_charger->dev, "Charger enable failed, err = %d\n", ret);
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530324 return ret;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500325}
326
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530327static struct regulator_ops bq2419x_tegra_regulator_ops = {
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530328 .set_current_limit = bq2419x_set_charging_current,
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530329};
330
Pradeep Goudagunta13f38432013-03-09 23:58:03 +0530331static int bq2419x_reset_wdt(struct bq2419x_chip *bq2419x, const char *from)
Kunal Agrawal24680952013-01-29 14:44:19 +0530332{
Pradeep Goudagunta803ab952013-03-12 14:44:32 +0530333 int ret = 0;
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530334 unsigned int reg01;
Kunal Agrawal24680952013-01-29 14:44:19 +0530335
Pradeep Goudagunta13f38432013-03-09 23:58:03 +0530336 mutex_lock(&bq2419x->mutex);
Pradeep Goudagunta803ab952013-03-12 14:44:32 +0530337 if (bq2419x->suspended)
338 goto scrub;
339
Pradeep Goudagunta13f38432013-03-09 23:58:03 +0530340 dev_info(bq2419x->dev, "%s() from %s()\n", __func__, from);
Pradeep Goudagunta9eda3b32013-03-09 23:54:16 +0530341
342 /* Clear EN_HIZ */
343 ret = regmap_update_bits(bq2419x->regmap,
344 BQ2419X_INPUT_SRC_REG, BQ2419X_EN_HIZ, 0);
345 if (ret < 0) {
346 dev_err(bq2419x->dev, "INPUT_SRC_REG update failed:%d\n", ret);
Pradeep Goudagunta803ab952013-03-12 14:44:32 +0530347 goto scrub;
Pradeep Goudagunta9eda3b32013-03-09 23:54:16 +0530348 }
349
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530350 ret = regmap_read(bq2419x->regmap, BQ2419X_PWR_ON_REG, &reg01);
351 if (ret < 0) {
352 dev_err(bq2419x->dev, "PWR_ON_REG read failed: %d\n", ret);
Pradeep Goudagunta803ab952013-03-12 14:44:32 +0530353 goto scrub;
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530354 }
355
356 reg01 |= BIT(6);
357
358 /* Write two times to make sure reset WDT */
359 ret = regmap_write(bq2419x->regmap, BQ2419X_PWR_ON_REG, reg01);
360 if (ret < 0) {
361 dev_err(bq2419x->dev, "PWR_ON_REG write failed: %d\n", ret);
Pradeep Goudagunta803ab952013-03-12 14:44:32 +0530362 goto scrub;
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530363 }
364 ret = regmap_write(bq2419x->regmap, BQ2419X_PWR_ON_REG, reg01);
365 if (ret < 0) {
366 dev_err(bq2419x->dev, "PWR_ON_REG write failed: %d\n", ret);
Pradeep Goudagunta803ab952013-03-12 14:44:32 +0530367 goto scrub;
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530368 }
Pradeep Goudagunta803ab952013-03-12 14:44:32 +0530369
370scrub:
Pradeep Goudagunta13f38432013-03-09 23:58:03 +0530371 mutex_unlock(&bq2419x->mutex);
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530372 return ret;
Kunal Agrawal24680952013-01-29 14:44:19 +0530373}
374
Syed Rafiuddin337240d2013-03-08 20:50:35 +0530375static int bq2419x_fault_clear_sts(struct bq2419x_chip *bq2419x)
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500376{
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500377 int ret;
Syed Rafiuddin8fa48bf2013-03-05 10:31:20 +0530378 unsigned int reg09;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500379
Syed Rafiuddin8fa48bf2013-03-05 10:31:20 +0530380 ret = regmap_read(bq2419x->regmap, BQ2419X_FAULT_REG, &reg09);
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500381 if (ret < 0) {
Syed Rafiuddin8fa48bf2013-03-05 10:31:20 +0530382 dev_err(bq2419x->dev, "FAULT_REG read failed: %d\n", ret);
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530383 return ret;
384 }
385
Syed Rafiuddin8fa48bf2013-03-05 10:31:20 +0530386 ret = regmap_read(bq2419x->regmap, BQ2419X_FAULT_REG, &reg09);
Syed Rafiuddin337240d2013-03-08 20:50:35 +0530387 if (ret < 0)
Syed Rafiuddin8fa48bf2013-03-05 10:31:20 +0530388 dev_err(bq2419x->dev, "FAULT_REG read failed: %d\n", ret);
Syed Rafiuddin337240d2013-03-08 20:50:35 +0530389
390 return ret;
391}
392
393static int bq2419x_watchdog_init(struct bq2419x_chip *bq2419x,
Pradeep Goudagunta7b7ac8b2013-03-11 18:13:59 +0530394 int timeout, const char *from)
Syed Rafiuddin337240d2013-03-08 20:50:35 +0530395{
396 int ret, val;
397 unsigned int reg05;
398
399 if (!timeout) {
400 ret = regmap_update_bits(bq2419x->regmap, BQ2419X_TIME_CTRL_REG,
401 BQ2419X_WD_MASK, 0);
402 if (ret < 0)
403 dev_err(bq2419x->dev,
404 "TIME_CTRL_REG read failed: %d\n", ret);
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530405 return ret;
406 }
Syed Rafiuddin8fa48bf2013-03-05 10:31:20 +0530407
Syed Rafiuddin337240d2013-03-08 20:50:35 +0530408 if (timeout <= 60) {
409 val = BQ2419X_WD_40ms;
410 bq2419x->wdt_refresh_timeout = 25;
411 } else if (timeout <= 120) {
412 val = BQ2419X_WD_80ms;
413 bq2419x->wdt_refresh_timeout = 50;
414 } else {
415 val = BQ2419X_WD_160ms;
416 bq2419x->wdt_refresh_timeout = 125;
417 }
418
419 ret = regmap_read(bq2419x->regmap, BQ2419X_TIME_CTRL_REG, &reg05);
420 if (ret < 0) {
421 dev_err(bq2419x->dev,
422 "TIME_CTRL_REG read failed:%d\n", ret);
423 return ret;
424 }
425
426 if ((reg05 & BQ2419X_WD_MASK) != val) {
427 ret = regmap_update_bits(bq2419x->regmap, BQ2419X_TIME_CTRL_REG,
428 BQ2419X_WD_MASK, val);
429 if (ret < 0) {
430 dev_err(bq2419x->dev,
431 "TIME_CTRL_REG read failed: %d\n", ret);
432 return ret;
433 }
434 }
435
Pradeep Goudagunta7b7ac8b2013-03-11 18:13:59 +0530436 ret = bq2419x_reset_wdt(bq2419x, from);
Syed Rafiuddin337240d2013-03-08 20:50:35 +0530437 if (ret < 0)
438 dev_err(bq2419x->dev, "bq2419x_reset_wdt failed: %d\n", ret);
439
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530440 return ret;
441}
442
443static void bq2419x_work_thread(struct kthread_work *work)
444{
445 struct bq2419x_chip *bq2419x = container_of(work,
446 struct bq2419x_chip, bq_wdt_work);
447 int ret;
448
449 for (;;) {
450 if (bq2419x->stop_thread)
451 return;
452
Pradeep Goudagunta6bf95072013-03-19 18:07:43 +0530453 if (bq2419x->chg_restart_timeout) {
454 mutex_lock(&bq2419x->mutex);
455 bq2419x->chg_restart_timeout--;
456 if (!bq2419x->chg_restart_timeout) {
457 ret = bq2419x_charger_enable(bq2419x);
458 if (ret < 0)
459 dev_err(bq2419x->dev,
460 "Charger enable failed %d", ret);
461 }
462 if (bq2419x->suspended)
463 bq2419x->chg_restart_timeout = 0;
464
465 mutex_unlock(&bq2419x->mutex);
466 }
467
Pradeep Goudagunta13f38432013-03-09 23:58:03 +0530468 ret = bq2419x_reset_wdt(bq2419x, "THREAD");
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530469 if (ret < 0)
470 dev_err(bq2419x->dev,
471 "bq2419x_reset_wdt failed: %d\n", ret);
472
473 msleep(bq2419x->wdt_refresh_timeout * 1000);
474 }
475}
476
477static irqreturn_t bq2419x_irq(int irq, void *data)
478{
479 struct bq2419x_chip *bq2419x = data;
480 int ret;
481 unsigned int val;
482
483 ret = regmap_read(bq2419x->regmap, BQ2419X_FAULT_REG, &val);
484 if (ret < 0) {
485 dev_err(bq2419x->dev, "FAULT_REG read failed %d\n", ret);
486 return ret;
487 }
488
489 dev_info(bq2419x->dev, "%s() Irq %d status 0x%02x\n",
490 __func__, irq, val);
491
492 if (val & BQ2419x_FAULT_WATCHDOG_FAULT) {
493 dev_err(bq2419x->dev,
494 "Charging Fault: Watchdog Timer Expired\n");
Pradeep Goudagunta7b7ac8b2013-03-11 18:13:59 +0530495 ret = bq2419x_watchdog_init(bq2419x, bq2419x->wdt_time_sec,
496 "ISR");
497 if (ret < 0) {
498 dev_err(bq2419x->dev, "BQWDT init failed %d\n", ret);
499 return ret;
500 }
Syed Rafiuddin73692422013-03-08 16:36:09 +0530501
502 ret = bq2419x_charger_init(bq2419x);
503 if (ret < 0) {
504 dev_err(bq2419x->dev, "Charger init failed: %d\n", ret);
505 return ret;
506 }
507
508 ret = bq2419x_init(bq2419x);
509 if (ret < 0) {
510 dev_err(bq2419x->dev, "bq2419x init failed: %d\n", ret);
511 return ret;
512 }
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530513 }
514
515 if (val & BQ2419x_FAULT_BOOST_FAULT)
516 dev_err(bq2419x->dev, "Charging Fault: VBUS Overloaded\n");
517
518 switch (val & BQ2419x_FAULT_CHRG_FAULT_MASK) {
519 case BQ2419x_FAULT_CHRG_INPUT:
520 dev_err(bq2419x->dev, "Charging Fault: "
521 "Input Fault (VBUS OVP or VBAT<VBUS<3.8V)\n");
522 break;
523 case BQ2419x_FAULT_CHRG_THERMAL:
524 dev_err(bq2419x->dev, "Charging Fault: Thermal shutdown\n");
525 break;
526 case BQ2419x_FAULT_CHRG_SAFTY:
527 dev_err(bq2419x->dev,
528 "Charging Fault: Safety timer expiration\n");
Pradeep Goudagunta6bf95072013-03-19 18:07:43 +0530529 bq2419x->chg_restart_timeout = bq2419x->chg_restart_time /
530 bq2419x->wdt_refresh_timeout;
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530531 break;
532 default:
533 break;
534 }
535
536 if (val & BQ2419x_FAULT_NTC_FAULT)
537 dev_err(bq2419x->dev, "Charging Fault: NTC fault %d\n",
538 val & BQ2419x_FAULT_NTC_FAULT);
539
Pradeep Goudagunta7b7ac8b2013-03-11 18:13:59 +0530540 ret = bq2419x_fault_clear_sts(bq2419x);
541 if (ret < 0) {
542 dev_err(bq2419x->dev, "fault clear status failed %d\n", ret);
543 return ret;
544 }
545
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530546 ret = regmap_read(bq2419x->regmap, BQ2419X_SYS_STAT_REG, &val);
547 if (ret < 0) {
548 dev_err(bq2419x->dev, "SYS_STAT_REG read failed %d\n", ret);
549 return ret;
550 }
551
552 if ((val & BQ2419x_CHRG_STATE_MASK) == BQ2419x_CHRG_STATE_CHARGE_DONE)
553 dev_info(bq2419x->dev, "Charging completed\n");
554
555 return IRQ_HANDLED;
556}
557
558static int bq2419x_init_charger_regulator(struct bq2419x_chip *bq2419x,
559 struct bq2419x_platform_data *pdata)
560{
561 int ret = 0;
562
563 if (!pdata->bcharger_pdata) {
564 dev_err(bq2419x->dev, "No charger platform data\n");
565 return 0;
566 }
567
568 bq2419x->chg_reg_desc.name = "bq2419x-charger";
569 bq2419x->chg_reg_desc.ops = &bq2419x_tegra_regulator_ops;
570 bq2419x->chg_reg_desc.type = REGULATOR_CURRENT;
571 bq2419x->chg_reg_desc.owner = THIS_MODULE;
572
573 bq2419x->chg_reg_init_data.supply_regulator = NULL;
574 bq2419x->chg_reg_init_data.regulator_init = NULL;
575 bq2419x->chg_reg_init_data.num_consumer_supplies =
576 pdata->bcharger_pdata->num_consumer_supplies;
577 bq2419x->chg_reg_init_data.consumer_supplies =
578 pdata->bcharger_pdata->consumer_supplies;
579 bq2419x->chg_reg_init_data.driver_data = bq2419x;
580 bq2419x->chg_reg_init_data.constraints.name = "bq2419x-charger";
581 bq2419x->chg_reg_init_data.constraints.min_uA = 0;
582 bq2419x->chg_reg_init_data.constraints.max_uA =
583 pdata->bcharger_pdata->max_charge_current_mA * 1000;
584
585 bq2419x->chg_reg_init_data.constraints.valid_modes_mask =
586 REGULATOR_MODE_NORMAL |
587 REGULATOR_MODE_STANDBY;
588
589 bq2419x->chg_reg_init_data.constraints.valid_ops_mask =
590 REGULATOR_CHANGE_MODE |
591 REGULATOR_CHANGE_STATUS |
592 REGULATOR_CHANGE_CURRENT;
593
594 bq2419x->chg_rdev = regulator_register(&bq2419x->chg_reg_desc,
595 bq2419x->dev, &bq2419x->chg_reg_init_data,
596 bq2419x, NULL);
597 if (IS_ERR(bq2419x->chg_rdev)) {
598 ret = PTR_ERR(bq2419x->chg_rdev);
599 dev_err(bq2419x->dev,
600 "vbus-charger regulator register failed %d\n", ret);
601 }
602 return ret;
603}
604
605static int bq2419x_init_vbus_regulator(struct bq2419x_chip *bq2419x,
606 struct bq2419x_platform_data *pdata)
607{
608 int ret = 0;
609
610 if (!pdata->vbus_pdata) {
611 dev_err(bq2419x->dev, "No vbus platform data\n");
612 return 0;
613 }
614
615 bq2419x->gpio_otg_iusb = pdata->vbus_pdata->gpio_otg_iusb;
616 bq2419x->vbus_reg_desc.name = "bq2419x-vbus";
617 bq2419x->vbus_reg_desc.ops = &bq2419x_vbus_ops;
618 bq2419x->vbus_reg_desc.type = REGULATOR_VOLTAGE;
619 bq2419x->vbus_reg_desc.owner = THIS_MODULE;
620
621 bq2419x->vbus_reg_init_data.supply_regulator = NULL;
622 bq2419x->vbus_reg_init_data.regulator_init = NULL;
623 bq2419x->vbus_reg_init_data.num_consumer_supplies =
624 pdata->vbus_pdata->num_consumer_supplies;
625 bq2419x->vbus_reg_init_data.consumer_supplies =
626 pdata->vbus_pdata->consumer_supplies;
627 bq2419x->vbus_reg_init_data.driver_data = bq2419x;
628
629 bq2419x->vbus_reg_init_data.constraints.name = "bq2419x-vbus";
630 bq2419x->vbus_reg_init_data.constraints.min_uV = 0;
631 bq2419x->vbus_reg_init_data.constraints.max_uV = 5000000,
632 bq2419x->vbus_reg_init_data.constraints.valid_modes_mask =
633 REGULATOR_MODE_NORMAL |
634 REGULATOR_MODE_STANDBY;
635 bq2419x->vbus_reg_init_data.constraints.valid_ops_mask =
636 REGULATOR_CHANGE_MODE |
637 REGULATOR_CHANGE_STATUS |
638 REGULATOR_CHANGE_VOLTAGE;
639
640 if (gpio_is_valid(bq2419x->gpio_otg_iusb)) {
641 ret = gpio_request_one(bq2419x->gpio_otg_iusb,
Pradeep Goudagunta9e75c232013-03-12 16:29:03 +0530642 GPIOF_OUT_INIT_HIGH, dev_name(bq2419x->dev));
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530643 if (ret < 0) {
644 dev_err(bq2419x->dev, "gpio request failed %d\n", ret);
645 return ret;
646 }
647 }
648
649 /* Register the regulators */
650 bq2419x->vbus_rdev = regulator_register(&bq2419x->vbus_reg_desc,
651 bq2419x->dev, &bq2419x->vbus_reg_init_data,
652 bq2419x, NULL);
653 if (IS_ERR(bq2419x->vbus_rdev)) {
654 ret = PTR_ERR(bq2419x->vbus_rdev);
655 dev_err(bq2419x->dev,
656 "VBUS regulator register failed %d\n", ret);
657 goto scrub;
658 }
659
660 /* Disable the VBUS regulator and enable charging */
661 ret = bq2419x_charger_enable(bq2419x);
662 if (ret < 0) {
663 dev_err(bq2419x->dev, "Charging enable failed %d", ret);
664 goto scrub_reg;
665 }
666 return ret;
667
668scrub_reg:
669 regulator_unregister(bq2419x->vbus_rdev);
670 bq2419x->vbus_rdev = NULL;
671scrub:
672 if (gpio_is_valid(bq2419x->gpio_otg_iusb))
673 gpio_free(bq2419x->gpio_otg_iusb);
674 return ret;
675}
676
677static int bq2419x_psy_init(struct bq2419x_chip *bq2419x)
678{
679 int ret = 0;
680
681 bq2419x->ac_online = 0;
682 bq2419x->usb_online = 0;
683 bq2419x->status = 0;
684 if (bq2419x->use_mains) {
685 bq2419x->ac.name = "bq2419x-ac";
686 bq2419x->ac.type = POWER_SUPPLY_TYPE_MAINS;
687 bq2419x->ac.get_property = bq2419x_ac_get_property;
688 bq2419x->ac.properties = bq2419x_psy_props;
689 bq2419x->ac.num_properties = ARRAY_SIZE(bq2419x_psy_props);
690 ret = power_supply_register(bq2419x->dev, &bq2419x->ac);
691 if (ret < 0) {
692 dev_err(bq2419x->dev,
693 "AC power supply register failed %d\n", ret);
694 return ret;
695 }
696 }
697
698 if (bq2419x->use_usb) {
699 bq2419x->usb.name = "bq2419x-usb";
700 bq2419x->usb.type = POWER_SUPPLY_TYPE_USB;
701 bq2419x->usb.get_property = bq2419x_usb_get_property;
702 bq2419x->usb.properties = bq2419x_psy_props;
703 bq2419x->usb.num_properties = ARRAY_SIZE(bq2419x_psy_props);
704 ret = power_supply_register(bq2419x->dev, &bq2419x->usb);
705 if (ret) {
706 dev_err(bq2419x->dev,
707 "usb power supply register failed %d\n", ret);
708 goto scrub;
709 }
710 }
711 return ret;
712scrub:
713 if (bq2419x->use_mains)
714 power_supply_unregister(&bq2419x->ac);
715 return ret;
716}
717
718static int bq2419x_show_chip_version(struct bq2419x_chip *bq2419x)
719{
720 int ret;
721 unsigned int val;
722
723 ret = regmap_read(bq2419x->regmap, BQ2419X_REVISION_REG, &val);
724 if (ret < 0) {
725 dev_err(bq2419x->dev, "REVISION_REG read failed: %d\n", ret);
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500726 return ret;
727 }
728
729 if ((val & BQ24190_IC_VER) == BQ24190_IC_VER)
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530730 dev_info(bq2419x->dev, "chip type BQ24190 detected\n");
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530731 else if ((val & BQ24192_IC_VER) == BQ24192_IC_VER)
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530732 dev_info(bq2419x->dev, "chip type BQ2419X/3 detected\n");
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530733 else if ((val & BQ24192i_IC_VER) == BQ24192i_IC_VER)
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530734 dev_info(bq2419x->dev, "chip type BQ2419Xi detected\n");
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500735 return 0;
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530736}
Syed Rafiuddin208dc892013-01-24 14:57:46 +0530737
Laxman Dewangan9148fd32013-03-20 17:19:35 +0530738static int bq2419x_wakealarm(struct bq2419x_chip *bq2419x, int time_sec)
Syed Rafiuddind4d862d2013-03-08 15:52:59 +0530739{
740 int ret;
741 unsigned long now;
742 struct rtc_wkalrm alm;
Laxman Dewangan9148fd32013-03-20 17:19:35 +0530743 int alarm_time = time_sec;
Syed Rafiuddind4d862d2013-03-08 15:52:59 +0530744
745 alm.enabled = true;
746 ret = rtc_read_time(bq2419x->rtc, &alm.time);
747 if (ret < 0) {
748 dev_err(bq2419x->dev, "RTC read time failed %d\n", ret);
749 return ret;
750 }
751 rtc_tm_to_time(&alm.time, &now);
752
Laxman Dewangan9148fd32013-03-20 17:19:35 +0530753 if (!alarm_time)
754 alarm_time = 3600;
755 rtc_time_to_tm(now + alarm_time, &alm.time);
Syed Rafiuddind4d862d2013-03-08 15:52:59 +0530756 ret = rtc_set_alarm(bq2419x->rtc, &alm);
757 if (ret < 0) {
758 dev_err(bq2419x->dev, "RTC set alarm failed %d\n", ret);
759 alm.enabled = false;
760 return ret;
761 }
762 alm.enabled = false;
763 return 0;
764}
765
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530766static int __devinit bq2419x_probe(struct i2c_client *client,
767 const struct i2c_device_id *id)
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500768{
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530769 struct bq2419x_chip *bq2419x;
770 struct bq2419x_platform_data *pdata;
771 int ret = 0;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500772
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530773 pdata = client->dev.platform_data;
774 if (!pdata) {
775 dev_err(&client->dev, "No Platform data");
776 return -EINVAL;
777 }
778
779 bq2419x = devm_kzalloc(&client->dev, sizeof(*bq2419x), GFP_KERNEL);
780 if (!bq2419x) {
781 dev_err(&client->dev, "Memory allocation failed\n");
782 return -ENOMEM;
783 }
784
785 bq2419x->regmap = devm_regmap_init_i2c(client, &bq2419x_regmap_config);
786 if (IS_ERR(bq2419x->regmap)) {
787 ret = PTR_ERR(bq2419x->regmap);
788 dev_err(&client->dev, "regmap init failed with err %d\n", ret);
789 return ret;
790 }
791
792 bq2419x->dev = &client->dev;
793 bq2419x->use_usb = pdata->bcharger_pdata->use_usb;
794 bq2419x->use_mains = pdata->bcharger_pdata->use_mains;
795 bq2419x->update_status = pdata->bcharger_pdata->update_status;
Syed Rafiuddind4d862d2013-03-08 15:52:59 +0530796 bq2419x->rtc_alarm_time = pdata->bcharger_pdata->rtc_alarm_time;
Pradeep Goudagunta7b7ac8b2013-03-11 18:13:59 +0530797 bq2419x->wdt_time_sec = pdata->bcharger_pdata->wdt_timeout;
Pradeep Goudagunta6bf95072013-03-19 18:07:43 +0530798 bq2419x->chg_restart_time = pdata->bcharger_pdata->chg_restart_time;
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530799 bq2419x->wdt_refresh_timeout = 25;
800 i2c_set_clientdata(client, bq2419x);
801 bq2419x->irq = client->irq;
Syed Rafiuddind4d862d2013-03-08 15:52:59 +0530802 bq2419x->rtc = alarmtimer_get_rtcdev();
Pradeep Goudagunta13f38432013-03-09 23:58:03 +0530803 mutex_init(&bq2419x->mutex);
Sang-Hun Lee7befcf92013-03-12 14:11:01 -0700804 bq2419x->suspended = 0;
Pradeep Goudagunta6bf95072013-03-19 18:07:43 +0530805 bq2419x->chg_restart_timeout = 0;
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530806
807 ret = bq2419x_show_chip_version(bq2419x);
808 if (ret < 0) {
809 dev_err(&client->dev, "version read failed %d\n", ret);
810 return ret;
811 }
812
813 ret = bq2419x_charger_init(bq2419x);
814 if (ret < 0) {
815 dev_err(bq2419x->dev, "Charger init failed: %d\n", ret);
816 return ret;
817 }
818
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530819 ret = bq2419x_init_charger_regulator(bq2419x, pdata);
820 if (ret < 0) {
821 dev_err(&client->dev,
822 "Charger regualtor init failed %d\n", ret);
823 return ret;
824 }
825
826 ret = bq2419x_psy_init(bq2419x);
827 if (ret < 0) {
828 dev_err(&client->dev,
829 "Charger power supply init failed %d\n", ret);
830 goto scrub_chg_reg;
831 }
832
833 ret = bq2419x_init_vbus_regulator(bq2419x, pdata);
834 if (ret < 0) {
835 dev_err(&client->dev,
836 "VBUS regualtor init failed %d\n", ret);
837 goto scrub_psy;
838 }
839
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530840 init_kthread_worker(&bq2419x->bq_kworker);
841 bq2419x->bq_kworker_task = kthread_run(kthread_worker_fn,
842 &bq2419x->bq_kworker,
843 dev_name(bq2419x->dev));
844 if (IS_ERR(bq2419x->bq_kworker_task)) {
845 ret = PTR_ERR(bq2419x->bq_kworker_task);
846 dev_err(&client->dev, "Kworker task creation failed %d\n", ret);
847 goto scrub_vbus_reg;
848 }
849
850 init_kthread_work(&bq2419x->bq_wdt_work, bq2419x_work_thread);
851 sched_setscheduler(bq2419x->bq_kworker_task,
852 SCHED_FIFO, &bq2419x_param);
853 queue_kthread_work(&bq2419x->bq_kworker, &bq2419x->bq_wdt_work);
854
Pradeep Goudagunta7b7ac8b2013-03-11 18:13:59 +0530855 ret = bq2419x_watchdog_init(bq2419x, bq2419x->wdt_time_sec, "PROBE");
Syed Rafiuddin8fa48bf2013-03-05 10:31:20 +0530856 if (ret < 0) {
857 dev_err(bq2419x->dev, "BQWDT init failed %d\n", ret);
858 return ret;
859 }
860
Pradeep Goudagunta7b7ac8b2013-03-11 18:13:59 +0530861 ret = bq2419x_fault_clear_sts(bq2419x);
Syed Rafiuddin337240d2013-03-08 20:50:35 +0530862 if (ret < 0) {
863 dev_err(bq2419x->dev, "fault clear status failed %d\n", ret);
864 return ret;
865 }
866
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530867 ret = request_threaded_irq(bq2419x->irq, NULL,
868 bq2419x_irq, IRQF_TRIGGER_FALLING,
869 dev_name(bq2419x->dev), bq2419x);
870 if (ret < 0) {
871 dev_err(bq2419x->dev, "request IRQ %d fail, err = %d\n",
872 bq2419x->irq, ret);
873 goto scrub_kthread;
874 }
875
876 /* enable charging */
877 ret = bq2419x_charger_enable(bq2419x);
878 if (ret < 0)
879 goto scrub_irq;
880
881 return 0;
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530882scrub_irq:
883 free_irq(bq2419x->irq, bq2419x);
884scrub_kthread:
885 bq2419x->stop_thread = true;
886 flush_kthread_worker(&bq2419x->bq_kworker);
887 kthread_stop(bq2419x->bq_kworker_task);
888scrub_vbus_reg:
889 regulator_unregister(bq2419x->vbus_rdev);
890scrub_psy:
891 if (bq2419x->use_usb)
892 power_supply_unregister(&bq2419x->usb);
893 if (bq2419x->use_mains)
894 power_supply_unregister(&bq2419x->ac);
895scrub_chg_reg:
896 regulator_unregister(bq2419x->chg_rdev);
Pradeep Goudagunta13f38432013-03-09 23:58:03 +0530897 mutex_destroy(&bq2419x->mutex);
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530898 return ret;
899}
900
901static int __devexit bq2419x_remove(struct i2c_client *client)
902{
903 struct bq2419x_chip *bq2419x = i2c_get_clientdata(client);
904
905 free_irq(bq2419x->irq, bq2419x);
906 bq2419x->stop_thread = true;
907 flush_kthread_worker(&bq2419x->bq_kworker);
908 kthread_stop(bq2419x->bq_kworker_task);
909 regulator_unregister(bq2419x->vbus_rdev);
910 if (bq2419x->use_usb)
911 power_supply_unregister(&bq2419x->usb);
912 if (bq2419x->use_mains)
913 power_supply_unregister(&bq2419x->ac);
914 regulator_unregister(bq2419x->chg_rdev);
Pradeep Goudagunta13f38432013-03-09 23:58:03 +0530915 mutex_destroy(&bq2419x->mutex);
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500916 return 0;
917}
918
Syed Rafiuddind4d862d2013-03-08 15:52:59 +0530919static void bq2419x_shutdown(struct i2c_client *client)
920{
921 int ret = 0;
922 struct bq2419x_chip *bq2419x = i2c_get_clientdata(client);
Laxman Dewangan9148fd32013-03-20 17:19:35 +0530923 int alarm_time = bq2419x->rtc_alarm_time;
Syed Rafiuddind4d862d2013-03-08 15:52:59 +0530924
Anshul Jaina5f0d172013-03-11 15:05:52 -0700925 if (bq2419x->irq)
926 disable_irq(bq2419x->irq);
927
Laxman Dewangan9148fd32013-03-20 17:19:35 +0530928 if (!bq2419x->rtc)
929 bq2419x->rtc = alarmtimer_get_rtcdev();
930
931 if (bq2419x->in_current_limit > 500) {
932 dev_info(bq2419x->dev, "HighCurrent %dmA charger is connectd\n",
933 bq2419x->in_current_limit);
934 ret = bq2419x_reset_wdt(bq2419x, "shutdown");
935 if (ret < 0)
936 dev_err(bq2419x->dev,
937 "bq2419x_reset_wdt failed: %d\n", ret);
938 alarm_time = 20;
939 }
940
Sang-Hun Lee7befcf92013-03-12 14:11:01 -0700941 mutex_lock(&bq2419x->mutex);
942 bq2419x->suspended = 1;
943 mutex_unlock(&bq2419x->mutex);
944
Syed Rafiuddind4d862d2013-03-08 15:52:59 +0530945 ret = bq2419x_charger_enable(bq2419x);
946 if (ret < 0)
947 dev_err(bq2419x->dev, "Charger enable failed %d", ret);
948
Laxman Dewangan9148fd32013-03-20 17:19:35 +0530949 if (bq2419x->in_current_limit <= 500) {
950 /* Configure charging current to 500mA */
951 ret = regmap_write(bq2419x->regmap,
952 BQ2419X_INPUT_SRC_REG, 0x32);
953 if (ret < 0)
954 dev_err(bq2419x->dev,
955 "INPUT_SRC_REG write failed %d\n", ret);
956 }
Syed Rafiuddind4d862d2013-03-08 15:52:59 +0530957
Laxman Dewangan9148fd32013-03-20 17:19:35 +0530958 ret = bq2419x_wakealarm(bq2419x, alarm_time);
Syed Rafiuddind4d862d2013-03-08 15:52:59 +0530959 if (ret < 0)
960 dev_err(bq2419x->dev, "RTC wake alarm config failed %d\n", ret);
961}
962
Pradeep Goudagunta7b7ac8b2013-03-11 18:13:59 +0530963#ifdef CONFIG_PM_SLEEP
964static int bq2419x_suspend(struct device *dev)
965{
966 int ret = 0;
967 struct bq2419x_chip *bq2419x = dev_get_drvdata(dev);
968
Sang-Hun Lee7befcf92013-03-12 14:11:01 -0700969 mutex_lock(&bq2419x->mutex);
970 bq2419x->suspended = 1;
971 mutex_unlock(&bq2419x->mutex);
Pradeep Goudagunta7b7ac8b2013-03-11 18:13:59 +0530972 disable_irq(bq2419x->irq);
973 ret = bq2419x_charger_enable(bq2419x);
Pradeep Goudagunta803ab952013-03-12 14:44:32 +0530974 if (ret < 0) {
Pradeep Goudagunta7b7ac8b2013-03-11 18:13:59 +0530975 dev_err(bq2419x->dev, "Charger enable failed %d", ret);
Pradeep Goudagunta803ab952013-03-12 14:44:32 +0530976 return ret;
977 }
Pradeep Goudagunta7b7ac8b2013-03-11 18:13:59 +0530978
979 /* Configure charging current to 500mA */
980 ret = regmap_write(bq2419x->regmap, BQ2419X_INPUT_SRC_REG, 0x32);
Pradeep Goudagunta803ab952013-03-12 14:44:32 +0530981 if (ret < 0) {
Pradeep Goudagunta7b7ac8b2013-03-11 18:13:59 +0530982 dev_err(bq2419x->dev, "INPUT_SRC_REG write failed %d\n", ret);
Pradeep Goudagunta803ab952013-03-12 14:44:32 +0530983 return ret;
984 }
Pradeep Goudagunta7b7ac8b2013-03-11 18:13:59 +0530985
986 return 0;
987}
988
989static int bq2419x_resume(struct device *dev)
990{
991 int ret = 0;
992 struct bq2419x_chip *bq2419x = dev_get_drvdata(dev);
993 unsigned int val;
994
995 ret = regmap_read(bq2419x->regmap, BQ2419X_FAULT_REG, &val);
996 if (ret < 0) {
997 dev_err(bq2419x->dev, "FAULT_REG read failed %d\n", ret);
998 return ret;
999 }
1000
1001 if (val & BQ2419x_FAULT_WATCHDOG_FAULT) {
1002 dev_err(bq2419x->dev,
1003 "Charging Fault: Watchdog Timer Expired\n");
1004
1005 ret = bq2419x_watchdog_init(bq2419x, bq2419x->wdt_time_sec,
1006 "RESUME");
1007 if (ret < 0) {
1008 dev_err(bq2419x->dev, "BQWDT init failed %d\n", ret);
1009 return ret;
1010 }
1011 }
1012
1013 ret = bq2419x_fault_clear_sts(bq2419x);
1014 if (ret < 0) {
1015 dev_err(bq2419x->dev, "fault clear status failed %d\n", ret);
1016 return ret;
1017 }
1018
1019 ret = bq2419x_charger_init(bq2419x);
1020 if (ret < 0) {
1021 dev_err(bq2419x->dev, "Charger init failed: %d\n", ret);
1022 return ret;
1023 }
1024
1025 ret = bq2419x_init(bq2419x);
1026 if (ret < 0) {
1027 dev_err(bq2419x->dev, "bq2419x init failed: %d\n", ret);
1028 return ret;
1029 }
1030
Sang-Hun Lee7befcf92013-03-12 14:11:01 -07001031 mutex_lock(&bq2419x->mutex);
1032 bq2419x->suspended = 0;
1033 mutex_unlock(&bq2419x->mutex);
Pradeep Goudagunta9e75c232013-03-12 16:29:03 +05301034 if (gpio_is_valid(bq2419x->gpio_otg_iusb))
1035 gpio_set_value(bq2419x->gpio_otg_iusb, 1);
1036
Pradeep Goudagunta7b7ac8b2013-03-11 18:13:59 +05301037 enable_irq(bq2419x->irq);
1038 return 0;
1039};
1040#endif
1041
1042static const struct dev_pm_ops bq2419x_pm_ops = {
1043 SET_SYSTEM_SLEEP_PM_OPS(bq2419x_suspend,
1044 bq2419x_resume)
1045};
1046
Syed Rafiuddin6126b492013-04-03 14:06:34 +05301047static const struct i2c_device_id bq2419x_id[] = {
1048 {.name = "bq2419x",},
1049 {},
1050};
1051MODULE_DEVICE_TABLE(i2c, bq2419x_id);
1052
1053static struct i2c_driver bq2419x_i2c_driver = {
Laxman Dewanganf27ed352012-11-29 19:33:02 -05001054 .driver = {
Syed Rafiuddind4d862d2013-03-08 15:52:59 +05301055 .name = "bq2419x",
1056 .owner = THIS_MODULE,
Pradeep Goudagunta7b7ac8b2013-03-11 18:13:59 +05301057 .pm = &bq2419x_pm_ops,
Laxman Dewanganf27ed352012-11-29 19:33:02 -05001058 },
Syed Rafiuddind4d862d2013-03-08 15:52:59 +05301059 .probe = bq2419x_probe,
1060 .remove = __devexit_p(bq2419x_remove),
1061 .shutdown = bq2419x_shutdown,
1062 .id_table = bq2419x_id,
Laxman Dewanganf27ed352012-11-29 19:33:02 -05001063};
1064
Syed Rafiuddin6126b492013-04-03 14:06:34 +05301065static int __init bq2419x_module_init(void)
Syed Rafiuddinee69db32013-01-09 15:40:55 +05301066{
Syed Rafiuddin6126b492013-04-03 14:06:34 +05301067 return i2c_add_driver(&bq2419x_i2c_driver);
Syed Rafiuddinee69db32013-01-09 15:40:55 +05301068}
Syed Rafiuddin6126b492013-04-03 14:06:34 +05301069subsys_initcall(bq2419x_module_init);
Syed Rafiuddinee69db32013-01-09 15:40:55 +05301070
Syed Rafiuddin6126b492013-04-03 14:06:34 +05301071static void __exit bq2419x_cleanup(void)
Syed Rafiuddinee69db32013-01-09 15:40:55 +05301072{
Syed Rafiuddin6126b492013-04-03 14:06:34 +05301073 i2c_del_driver(&bq2419x_i2c_driver);
Syed Rafiuddinee69db32013-01-09 15:40:55 +05301074}
Syed Rafiuddin6126b492013-04-03 14:06:34 +05301075module_exit(bq2419x_cleanup);
Laxman Dewanganf27ed352012-11-29 19:33:02 -05001076
Syed Rafiuddin6126b492013-04-03 14:06:34 +05301077MODULE_DESCRIPTION("BQ24190/BQ24192/BQ24192i/BQ24193 battery charger driver");
Laxman Dewanganf27ed352012-11-29 19:33:02 -05001078MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
Syed Rafiuddin6126b492013-04-03 14:06:34 +05301079MODULE_AUTHOR("Syed Rafiuddin <srafiuddin@nvidia.com");
Laxman Dewanganf27ed352012-11-29 19:33:02 -05001080MODULE_LICENSE("GPL v2");