blob: a881e6c2f23cb13202fc9d00b29a9d8eca38e857 [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>
Syed Rafiuddin6126b492013-04-03 14:06:34 +053036#include <linux/regmap.h>
37#include <linux/regmap.h>
Syed Rafiuddinee69db32013-01-09 15:40:55 +053038#include <linux/regulator/driver.h>
39#include <linux/regulator/machine.h>
Syed Rafiuddin6126b492013-04-03 14:06:34 +053040#include <linux/slab.h>
Syed Rafiuddind4d862d2013-03-08 15:52:59 +053041#include <linux/rtc.h>
42#include <linux/alarmtimer.h>
Laxman Dewanganf27ed352012-11-29 19:33:02 -050043
44/* input current limit */
45static const unsigned int iinlim[] = {
46 100, 150, 500, 900, 1200, 1500, 2000, 3000,
47};
48
Syed Rafiuddin6126b492013-04-03 14:06:34 +053049/* Kthread scheduling parameters */
50struct sched_param bq2419x_param = {
51 .sched_priority = MAX_RT_PRIO - 1,
Laxman Dewanganf27ed352012-11-29 19:33:02 -050052};
53
Syed Rafiuddin6126b492013-04-03 14:06:34 +053054static const struct regmap_config bq2419x_regmap_config = {
55 .reg_bits = 8,
56 .val_bits = 8,
57 .max_register = BQ2419X_MAX_REGS,
58};
59
60struct bq2419x_chip {
61 struct device *dev;
62 struct regmap *regmap;
63 int irq;
64 int gpio_otg_iusb;
65 int wdt_refresh_timeout;
Pradeep Goudagunta7b7ac8b2013-03-11 18:13:59 +053066 int wdt_time_sec;
Syed Rafiuddin6126b492013-04-03 14:06:34 +053067
Pradeep Goudagunta13f38432013-03-09 23:58:03 +053068 struct mutex mutex;
Syed Rafiuddin6126b492013-04-03 14:06:34 +053069 int in_current_limit;
Syed Rafiuddin6126b492013-04-03 14:06:34 +053070 int status;
Syed Rafiuddind4d862d2013-03-08 15:52:59 +053071 int rtc_alarm_time;
Syed Rafiuddin6126b492013-04-03 14:06:34 +053072 void (*update_status)(int, int);
73
74 struct regulator_dev *chg_rdev;
75 struct regulator_desc chg_reg_desc;
76 struct regulator_init_data chg_reg_init_data;
77
78 struct regulator_dev *vbus_rdev;
79 struct regulator_desc vbus_reg_desc;
80 struct regulator_init_data vbus_reg_init_data;
81
82 struct kthread_worker bq_kworker;
83 struct task_struct *bq_kworker_task;
84 struct kthread_work bq_wdt_work;
Syed Rafiuddind4d862d2013-03-08 15:52:59 +053085 struct rtc_device *rtc;
Syed Rafiuddin6126b492013-04-03 14:06:34 +053086 int stop_thread;
Sang-Hun Lee7befcf92013-03-12 14:11:01 -070087 int suspended;
Pradeep Goudagunta6bf95072013-03-19 18:07:43 +053088 int chg_restart_timeout;
89 int chg_restart_time;
Hyongbin Kim12e014c2013-04-09 17:28:27 +090090 int chg_enable;
Syed Rafiuddin6126b492013-04-03 14:06:34 +053091};
Rohith Seelaboyina2fd2e112013-02-02 18:10:54 +053092
Laxman Dewanganf27ed352012-11-29 19:33:02 -050093static int current_to_reg(const unsigned int *tbl,
94 size_t size, unsigned int val)
95{
96 size_t i;
97
98 for (i = 0; i < size; i++)
99 if (val < tbl[i])
100 break;
101 return i > 0 ? i - 1 : -EINVAL;
102}
103
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530104static int bq2419x_charger_enable(struct bq2419x_chip *bq2419x)
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500105{
106 int ret;
Rohith Seelaboyina2fd2e112013-02-02 18:10:54 +0530107
Hyongbin Kim12e014c2013-04-09 17:28:27 +0900108 if (bq2419x->chg_enable) {
109 dev_info(bq2419x->dev, "Charging enabled\n");
110 ret = regmap_update_bits(bq2419x->regmap, BQ2419X_PWR_ON_REG,
Pradeep Goudaguntae31f3da2013-04-02 21:10:17 -0700111 BQ2419X_ENABLE_CHARGE_MASK, 0);
112 if (ret < 0) {
113 dev_err(bq2419x->dev,
114 "register update failed, err %d\n", ret);
115 return ret;
116 }
117
118 ret = regmap_update_bits(bq2419x->regmap, BQ2419X_PWR_ON_REG,
119 BQ2419X_ENABLE_CHARGE_MASK, BQ2419X_ENABLE_CHARGE);
Hyongbin Kim12e014c2013-04-09 17:28:27 +0900120 } else {
121 dev_info(bq2419x->dev, "Charging disabled\n");
122 ret = regmap_update_bits(bq2419x->regmap, BQ2419X_PWR_ON_REG,
Pradeep Goudaguntae31f3da2013-04-02 21:10:17 -0700123 BQ2419X_ENABLE_CHARGE_MASK,
124 BQ2419X_DISABLE_CHARGE);
Hyongbin Kim12e014c2013-04-09 17:28:27 +0900125 }
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500126 if (ret < 0)
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530127 dev_err(bq2419x->dev, "register update failed, err %d\n", ret);
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500128 return ret;
129}
130
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530131static int bq2419x_vbus_regulator_enable_time(struct regulator_dev *rdev)
132{
133 return 500000;
134}
135
136static int bq2419x_vbus_enable(struct regulator_dev *rdev)
137{
138 struct bq2419x_chip *bq2419x = rdev_get_drvdata(rdev);
139 int ret;
140
141 dev_info(bq2419x->dev, "VBUS enabled, charging disabled\n");
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530142
143 ret = regmap_update_bits(bq2419x->regmap, BQ2419X_PWR_ON_REG,
144 BQ2419X_ENABLE_CHARGE_MASK, BQ2419X_ENABLE_VBUS);
145 if (ret < 0)
146 dev_err(bq2419x->dev, "PWR_ON_REG update failed %d", ret);
147
148 return ret;
149}
150
151static int bq2419x_vbus_disable(struct regulator_dev *rdev)
152{
153 struct bq2419x_chip *bq2419x = rdev_get_drvdata(rdev);
154 int ret;
155
156 dev_info(bq2419x->dev, "VBUS disabled, charging enabled\n");
157 ret = bq2419x_charger_enable(bq2419x);
158 if (ret < 0) {
159 dev_err(bq2419x->dev, "Charger enable failed %d", ret);
160 return ret;
161 }
162
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530163 return ret;
164}
165
166static int bq2419x_vbus_is_enabled(struct regulator_dev *rdev)
167{
168 struct bq2419x_chip *bq2419x = rdev_get_drvdata(rdev);
169 int ret;
170 unsigned int data;
171
172 ret = regmap_read(bq2419x->regmap, BQ2419X_PWR_ON_REG, &data);
173 if (ret < 0) {
174 dev_err(bq2419x->dev, "PWR_ON_REG read failed %d", ret);
175 return ret;
176 }
177 return (data & BQ2419X_ENABLE_CHARGE_MASK) == BQ2419X_ENABLE_VBUS;
178}
179
180static struct regulator_ops bq2419x_vbus_ops = {
181 .enable = bq2419x_vbus_enable,
182 .disable = bq2419x_vbus_disable,
183 .is_enabled = bq2419x_vbus_is_enabled,
184 .enable_time = bq2419x_vbus_regulator_enable_time,
185};
186
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530187static int bq2419x_init(struct bq2419x_chip *bq2419x)
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500188{
Syed Rafiuddin9ca6fcc2013-04-18 10:35:24 +0530189 int val = 0;
190 int ret = 0;
191 int floor = 0;
192
193 /* Configure input voltage to 4.52 in case of NV charger */
194 if (bq2419x->in_current_limit == 2000)
195 val |= BQ2419x_NVCHARGER_INPUT_VOL_SEL;
196 else
197 val |= BQ2419x_DEFAULT_INPUT_VOL_SEL;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500198
Syed Rafiuddinf2d004d2013-02-18 15:30:58 +0530199 /* Clear EN_HIZ */
Syed Rafiuddin9ca6fcc2013-04-18 10:35:24 +0530200 ret = regmap_update_bits(bq2419x->regmap, BQ2419X_INPUT_SRC_REG,
201 BQ2419X_EN_HIZ | BQ2419x_INPUT_VOLTAGE_MASK, val);
Syed Rafiuddinf2d004d2013-02-18 15:30:58 +0530202 if (ret < 0) {
Syed Rafiuddin9ca6fcc2013-04-18 10:35:24 +0530203 dev_err(bq2419x->dev, "INPUT_SRC_REG update failed %d\n", ret);
Syed Rafiuddinf2d004d2013-02-18 15:30:58 +0530204 return ret;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500205 }
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530206
Syed Rafiuddinf2d004d2013-02-18 15:30:58 +0530207 /* Configure input current limit */
208 val = current_to_reg(iinlim, ARRAY_SIZE(iinlim),
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530209 bq2419x->in_current_limit);
Syed Rafiuddin9ca6fcc2013-04-18 10:35:24 +0530210
211 /* Start from 500mA and then step to val */
212 floor = current_to_reg(iinlim, ARRAY_SIZE(iinlim), 500);
213 if (val < 0 || floor < 0)
Syed Rafiuddinf2d004d2013-02-18 15:30:58 +0530214 return 0;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500215
Syed Rafiuddin9ca6fcc2013-04-18 10:35:24 +0530216 for (; floor <= val; floor++) {
217 udelay(BQ2419x_CHARGING_CURRENT_STEP_DELAY_US);
218 ret = regmap_update_bits(bq2419x->regmap, BQ2419X_INPUT_SRC_REG,
219 BQ2419x_CONFIG_MASK, floor);
220 if (ret < 0)
221 dev_err(bq2419x->dev,
222 "INPUT_SRC_REG update failed: %d\n", ret);
223 }
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500224 return ret;
225}
226
Syed Rafiuddin73692422013-03-08 16:36:09 +0530227static int bq2419x_charger_init(struct bq2419x_chip *bq2419x)
228{
229 int ret;
230
Pradeep Goudagunta59d3dfb2013-04-04 12:03:00 -0700231 /* Configure Output Current Control to 2.25A*/
232 ret = regmap_write(bq2419x->regmap, BQ2419X_CHRG_CTRL_REG, 0x6c);
Syed Rafiuddin73692422013-03-08 16:36:09 +0530233 if (ret < 0) {
234 dev_err(bq2419x->dev, "CHRG_CTRL_REG write failed %d\n", ret);
235 return ret;
236 }
237
238 /*
239 * Configure Input voltage limit reset to OTP value,
240 * and charging current to 500mA.
241 */
242 ret = regmap_write(bq2419x->regmap, BQ2419X_INPUT_SRC_REG, 0x32);
243 if (ret < 0)
244 dev_err(bq2419x->dev, "INPUT_SRC_REG write failed %d\n", ret);
245
246 return ret;
247}
248
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530249static int bq2419x_set_charging_current(struct regulator_dev *rdev,
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530250 int min_uA, int max_uA)
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500251{
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530252 struct bq2419x_chip *bq_charger = rdev_get_drvdata(rdev);
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530253 int ret = 0;
254 int val;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500255
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530256 dev_info(bq_charger->dev, "Setting charging current %d\n", max_uA/1000);
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530257 msleep(200);
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530258 bq_charger->status = 0;
259
Pradeep Goudaguntada8f1f22013-03-10 01:16:33 +0530260 ret = bq2419x_charger_enable(bq_charger);
261 if (ret < 0) {
262 dev_err(bq_charger->dev, "Charger enable failed %d", ret);
263 return ret;
264 }
265
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530266 ret = regmap_read(bq_charger->regmap, BQ2419X_SYS_STAT_REG, &val);
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530267 if (ret < 0)
268 dev_err(bq_charger->dev, "error reading reg: 0x%x\n",
269 BQ2419X_SYS_STAT_REG);
270
Rohith Seelaboyina2fd2e112013-02-02 18:10:54 +0530271 if (max_uA == 0 && val != 0)
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530272 return ret;
273
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530274 bq_charger->in_current_limit = max_uA/1000;
Syed Rafiuddin208dc892013-01-24 14:57:46 +0530275 if ((val & BQ2419x_VBUS_STAT) == BQ2419x_VBUS_UNKNOWN) {
Syed Rafiuddinf2d004d2013-02-18 15:30:58 +0530276 bq_charger->in_current_limit = 500;
Laxman Dewangan63a13c5e2013-06-01 16:09:42 +0530277 bq_charger->status = 0;
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530278 } else {
Syed Rafiuddin208dc892013-01-24 14:57:46 +0530279 bq_charger->status = 1;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500280 }
Laxman Dewangan63a13c5e2013-06-01 16:09:42 +0530281 ret = bq2419x_init(bq_charger);
282 if (ret < 0)
283 goto error;
284 if (bq_charger->update_status)
285 bq_charger->update_status(bq_charger->status, 0);
Syed Rafiuddin208dc892013-01-24 14:57:46 +0530286 return 0;
287error:
288 dev_err(bq_charger->dev, "Charger enable failed, err = %d\n", ret);
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530289 return ret;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500290}
291
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530292static struct regulator_ops bq2419x_tegra_regulator_ops = {
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530293 .set_current_limit = bq2419x_set_charging_current,
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530294};
295
Pradeep Goudagunta13f38432013-03-09 23:58:03 +0530296static int bq2419x_reset_wdt(struct bq2419x_chip *bq2419x, const char *from)
Kunal Agrawal24680952013-01-29 14:44:19 +0530297{
Pradeep Goudagunta803ab952013-03-12 14:44:32 +0530298 int ret = 0;
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530299 unsigned int reg01;
Kunal Agrawal24680952013-01-29 14:44:19 +0530300
Pradeep Goudagunta13f38432013-03-09 23:58:03 +0530301 mutex_lock(&bq2419x->mutex);
Pradeep Goudagunta803ab952013-03-12 14:44:32 +0530302 if (bq2419x->suspended)
303 goto scrub;
304
Pradeep Goudagunta13f38432013-03-09 23:58:03 +0530305 dev_info(bq2419x->dev, "%s() from %s()\n", __func__, from);
Pradeep Goudagunta9eda3b32013-03-09 23:54:16 +0530306
307 /* Clear EN_HIZ */
308 ret = regmap_update_bits(bq2419x->regmap,
309 BQ2419X_INPUT_SRC_REG, BQ2419X_EN_HIZ, 0);
310 if (ret < 0) {
311 dev_err(bq2419x->dev, "INPUT_SRC_REG update failed:%d\n", ret);
Pradeep Goudagunta803ab952013-03-12 14:44:32 +0530312 goto scrub;
Pradeep Goudagunta9eda3b32013-03-09 23:54:16 +0530313 }
314
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530315 ret = regmap_read(bq2419x->regmap, BQ2419X_PWR_ON_REG, &reg01);
316 if (ret < 0) {
317 dev_err(bq2419x->dev, "PWR_ON_REG read failed: %d\n", ret);
Pradeep Goudagunta803ab952013-03-12 14:44:32 +0530318 goto scrub;
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530319 }
320
321 reg01 |= BIT(6);
322
323 /* Write two times to make sure reset WDT */
324 ret = regmap_write(bq2419x->regmap, BQ2419X_PWR_ON_REG, reg01);
325 if (ret < 0) {
326 dev_err(bq2419x->dev, "PWR_ON_REG write failed: %d\n", ret);
Pradeep Goudagunta803ab952013-03-12 14:44:32 +0530327 goto scrub;
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530328 }
329 ret = regmap_write(bq2419x->regmap, BQ2419X_PWR_ON_REG, reg01);
330 if (ret < 0) {
331 dev_err(bq2419x->dev, "PWR_ON_REG write failed: %d\n", ret);
Pradeep Goudagunta803ab952013-03-12 14:44:32 +0530332 goto scrub;
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530333 }
Pradeep Goudagunta803ab952013-03-12 14:44:32 +0530334
335scrub:
Pradeep Goudagunta13f38432013-03-09 23:58:03 +0530336 mutex_unlock(&bq2419x->mutex);
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530337 return ret;
Kunal Agrawal24680952013-01-29 14:44:19 +0530338}
339
Syed Rafiuddin337240d2013-03-08 20:50:35 +0530340static int bq2419x_fault_clear_sts(struct bq2419x_chip *bq2419x)
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500341{
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500342 int ret;
Syed Rafiuddin8fa48bf2013-03-05 10:31:20 +0530343 unsigned int reg09;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500344
Syed Rafiuddin8fa48bf2013-03-05 10:31:20 +0530345 ret = regmap_read(bq2419x->regmap, BQ2419X_FAULT_REG, &reg09);
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500346 if (ret < 0) {
Syed Rafiuddin8fa48bf2013-03-05 10:31:20 +0530347 dev_err(bq2419x->dev, "FAULT_REG read failed: %d\n", ret);
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530348 return ret;
349 }
350
Syed Rafiuddin8fa48bf2013-03-05 10:31:20 +0530351 ret = regmap_read(bq2419x->regmap, BQ2419X_FAULT_REG, &reg09);
Syed Rafiuddin337240d2013-03-08 20:50:35 +0530352 if (ret < 0)
Syed Rafiuddin8fa48bf2013-03-05 10:31:20 +0530353 dev_err(bq2419x->dev, "FAULT_REG read failed: %d\n", ret);
Syed Rafiuddin337240d2013-03-08 20:50:35 +0530354
355 return ret;
356}
357
358static int bq2419x_watchdog_init(struct bq2419x_chip *bq2419x,
Pradeep Goudagunta7b7ac8b2013-03-11 18:13:59 +0530359 int timeout, const char *from)
Syed Rafiuddin337240d2013-03-08 20:50:35 +0530360{
361 int ret, val;
362 unsigned int reg05;
363
364 if (!timeout) {
365 ret = regmap_update_bits(bq2419x->regmap, BQ2419X_TIME_CTRL_REG,
366 BQ2419X_WD_MASK, 0);
367 if (ret < 0)
368 dev_err(bq2419x->dev,
369 "TIME_CTRL_REG read failed: %d\n", ret);
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530370 return ret;
371 }
Syed Rafiuddin8fa48bf2013-03-05 10:31:20 +0530372
Syed Rafiuddin337240d2013-03-08 20:50:35 +0530373 if (timeout <= 60) {
374 val = BQ2419X_WD_40ms;
375 bq2419x->wdt_refresh_timeout = 25;
376 } else if (timeout <= 120) {
377 val = BQ2419X_WD_80ms;
378 bq2419x->wdt_refresh_timeout = 50;
379 } else {
380 val = BQ2419X_WD_160ms;
381 bq2419x->wdt_refresh_timeout = 125;
382 }
383
384 ret = regmap_read(bq2419x->regmap, BQ2419X_TIME_CTRL_REG, &reg05);
385 if (ret < 0) {
386 dev_err(bq2419x->dev,
387 "TIME_CTRL_REG read failed:%d\n", ret);
388 return ret;
389 }
390
391 if ((reg05 & BQ2419X_WD_MASK) != val) {
392 ret = regmap_update_bits(bq2419x->regmap, BQ2419X_TIME_CTRL_REG,
393 BQ2419X_WD_MASK, val);
394 if (ret < 0) {
395 dev_err(bq2419x->dev,
396 "TIME_CTRL_REG read failed: %d\n", ret);
397 return ret;
398 }
399 }
400
Pradeep Goudagunta7b7ac8b2013-03-11 18:13:59 +0530401 ret = bq2419x_reset_wdt(bq2419x, from);
Syed Rafiuddin337240d2013-03-08 20:50:35 +0530402 if (ret < 0)
403 dev_err(bq2419x->dev, "bq2419x_reset_wdt failed: %d\n", ret);
404
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530405 return ret;
406}
407
408static void bq2419x_work_thread(struct kthread_work *work)
409{
410 struct bq2419x_chip *bq2419x = container_of(work,
411 struct bq2419x_chip, bq_wdt_work);
412 int ret;
Pradeep Goudaguntae31f3da2013-04-02 21:10:17 -0700413 int val = 0;
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530414
415 for (;;) {
416 if (bq2419x->stop_thread)
417 return;
418
Pradeep Goudagunta6bf95072013-03-19 18:07:43 +0530419 if (bq2419x->chg_restart_timeout) {
420 mutex_lock(&bq2419x->mutex);
421 bq2419x->chg_restart_timeout--;
422 if (!bq2419x->chg_restart_timeout) {
423 ret = bq2419x_charger_enable(bq2419x);
424 if (ret < 0)
425 dev_err(bq2419x->dev,
426 "Charger enable failed %d", ret);
Pradeep Goudaguntae31f3da2013-04-02 21:10:17 -0700427 ret = regmap_read(bq2419x->regmap,
428 BQ2419X_SYS_STAT_REG, &val);
429 if (ret < 0)
430 dev_err(bq2419x->dev,
431 "SYS_STAT_REG read failed %d\n", ret);
432 /*
433 * Update Charging status based on STAT register
434 */
Pradeep Goudaguntae31f3da2013-04-02 21:10:17 -0700435 if ((val & BQ2419x_CHRG_STATE_MASK) ==
436 BQ2419x_CHRG_STATE_NOTCHARGING) {
437 bq2419x->status = 0;
438 if (bq2419x->update_status)
439 bq2419x->update_status
Laxman Dewangan6d7070a2013-06-01 02:36:55 +0530440 (bq2419x->status, 0);
Pradeep Goudaguntae31f3da2013-04-02 21:10:17 -0700441 bq2419x->chg_restart_timeout =
442 bq2419x->chg_restart_time /
443 bq2419x->wdt_refresh_timeout;
444 } else {
445 bq2419x->status = 1;
446 if (bq2419x->update_status)
447 bq2419x->update_status
Laxman Dewangan6d7070a2013-06-01 02:36:55 +0530448 (bq2419x->status, 0);
Pradeep Goudaguntae31f3da2013-04-02 21:10:17 -0700449 }
450
Pradeep Goudagunta6bf95072013-03-19 18:07:43 +0530451 }
Pradeep Goudaguntae31f3da2013-04-02 21:10:17 -0700452
Pradeep Goudagunta6bf95072013-03-19 18:07:43 +0530453 if (bq2419x->suspended)
454 bq2419x->chg_restart_timeout = 0;
455
456 mutex_unlock(&bq2419x->mutex);
457 }
458
Pradeep Goudagunta13f38432013-03-09 23:58:03 +0530459 ret = bq2419x_reset_wdt(bq2419x, "THREAD");
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530460 if (ret < 0)
461 dev_err(bq2419x->dev,
462 "bq2419x_reset_wdt failed: %d\n", ret);
463
464 msleep(bq2419x->wdt_refresh_timeout * 1000);
465 }
466}
467
Pradeep Goudaguntae31f3da2013-04-02 21:10:17 -0700468static int bq2419x_reset_safety_timer(struct bq2419x_chip *bq2419x)
469{
470 int ret;
471
472 ret = regmap_update_bits(bq2419x->regmap, BQ2419X_TIME_CTRL_REG,
473 BQ2419X_EN_SFT_TIMER_MASK, 0);
474 if (ret < 0) {
475 dev_err(bq2419x->dev,
476 "TIME_CTRL_REG update failed: %d\n", ret);
477 return ret;
478 }
479
480 ret = regmap_update_bits(bq2419x->regmap, BQ2419X_TIME_CTRL_REG,
481 BQ2419X_EN_SFT_TIMER_MASK, BQ2419X_EN_SFT_TIMER_MASK);
482 if (ret < 0)
483 dev_err(bq2419x->dev,
484 "TIME_CTRL_REG update failed: %d\n", ret);
485 return ret;
486}
487
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530488static irqreturn_t bq2419x_irq(int irq, void *data)
489{
490 struct bq2419x_chip *bq2419x = data;
491 int ret;
492 unsigned int val;
Pradeep Goudaguntae31f3da2013-04-02 21:10:17 -0700493 int check_chg_state = 0;
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530494
495 ret = regmap_read(bq2419x->regmap, BQ2419X_FAULT_REG, &val);
496 if (ret < 0) {
497 dev_err(bq2419x->dev, "FAULT_REG read failed %d\n", ret);
498 return ret;
499 }
500
501 dev_info(bq2419x->dev, "%s() Irq %d status 0x%02x\n",
502 __func__, irq, val);
503
504 if (val & BQ2419x_FAULT_WATCHDOG_FAULT) {
505 dev_err(bq2419x->dev,
506 "Charging Fault: Watchdog Timer Expired\n");
Pradeep Goudagunta7b7ac8b2013-03-11 18:13:59 +0530507 ret = bq2419x_watchdog_init(bq2419x, bq2419x->wdt_time_sec,
508 "ISR");
509 if (ret < 0) {
510 dev_err(bq2419x->dev, "BQWDT init failed %d\n", ret);
511 return ret;
512 }
Syed Rafiuddin73692422013-03-08 16:36:09 +0530513
514 ret = bq2419x_charger_init(bq2419x);
515 if (ret < 0) {
516 dev_err(bq2419x->dev, "Charger init failed: %d\n", ret);
517 return ret;
518 }
519
520 ret = bq2419x_init(bq2419x);
521 if (ret < 0) {
522 dev_err(bq2419x->dev, "bq2419x init failed: %d\n", ret);
523 return ret;
524 }
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530525 }
526
527 if (val & BQ2419x_FAULT_BOOST_FAULT)
528 dev_err(bq2419x->dev, "Charging Fault: VBUS Overloaded\n");
529
530 switch (val & BQ2419x_FAULT_CHRG_FAULT_MASK) {
531 case BQ2419x_FAULT_CHRG_INPUT:
532 dev_err(bq2419x->dev, "Charging Fault: "
533 "Input Fault (VBUS OVP or VBAT<VBUS<3.8V)\n");
534 break;
535 case BQ2419x_FAULT_CHRG_THERMAL:
536 dev_err(bq2419x->dev, "Charging Fault: Thermal shutdown\n");
Pradeep Goudaguntae31f3da2013-04-02 21:10:17 -0700537 check_chg_state = 1;
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530538 break;
539 case BQ2419x_FAULT_CHRG_SAFTY:
540 dev_err(bq2419x->dev,
541 "Charging Fault: Safety timer expiration\n");
Pradeep Goudagunta6bf95072013-03-19 18:07:43 +0530542 bq2419x->chg_restart_timeout = bq2419x->chg_restart_time /
543 bq2419x->wdt_refresh_timeout;
Pradeep Goudaguntae31f3da2013-04-02 21:10:17 -0700544 ret = bq2419x_reset_safety_timer(bq2419x);
545 if (ret < 0) {
546 dev_err(bq2419x->dev, "Reset safety timer failed %d\n",
547 ret);
548 return ret;
549 }
550
551 check_chg_state = 1;
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530552 break;
553 default:
554 break;
555 }
556
Pradeep Goudaguntae31f3da2013-04-02 21:10:17 -0700557 if (val & BQ2419x_FAULT_NTC_FAULT) {
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530558 dev_err(bq2419x->dev, "Charging Fault: NTC fault %d\n",
559 val & BQ2419x_FAULT_NTC_FAULT);
Pradeep Goudaguntae31f3da2013-04-02 21:10:17 -0700560 check_chg_state = 1;
561 }
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530562
Pradeep Goudagunta7b7ac8b2013-03-11 18:13:59 +0530563 ret = bq2419x_fault_clear_sts(bq2419x);
564 if (ret < 0) {
565 dev_err(bq2419x->dev, "fault clear status failed %d\n", ret);
566 return ret;
567 }
568
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530569 ret = regmap_read(bq2419x->regmap, BQ2419X_SYS_STAT_REG, &val);
570 if (ret < 0) {
571 dev_err(bq2419x->dev, "SYS_STAT_REG read failed %d\n", ret);
572 return ret;
573 }
574
Pradeep Goudaguntae31f3da2013-04-02 21:10:17 -0700575 if ((val & BQ2419x_CHRG_STATE_MASK) ==
576 BQ2419x_CHRG_STATE_CHARGE_DONE) {
577 bq2419x->chg_restart_timeout = bq2419x->chg_restart_time /
578 bq2419x->wdt_refresh_timeout;
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530579 dev_info(bq2419x->dev, "Charging completed\n");
Syed Rafiuddin800de482013-04-09 20:54:51 +0530580 bq2419x->status = 4;
581 if (bq2419x->update_status)
Laxman Dewangan6d7070a2013-06-01 02:36:55 +0530582 bq2419x->update_status(bq2419x->status, 0);
Pradeep Goudaguntae31f3da2013-04-02 21:10:17 -0700583 }
584
585 /*
586 * Update Charging status based on STAT register
587 */
588 if (check_chg_state) {
589 if ((val & BQ2419x_CHRG_STATE_MASK) ==
590 BQ2419x_CHRG_STATE_NOTCHARGING) {
Pradeep Goudaguntae31f3da2013-04-02 21:10:17 -0700591 bq2419x->status = 0;
592 if (bq2419x->update_status)
Laxman Dewangan6d7070a2013-06-01 02:36:55 +0530593 bq2419x->update_status(bq2419x->status, 0);
Pradeep Goudaguntae31f3da2013-04-02 21:10:17 -0700594 }
595 }
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530596
597 return IRQ_HANDLED;
598}
599
600static int bq2419x_init_charger_regulator(struct bq2419x_chip *bq2419x,
601 struct bq2419x_platform_data *pdata)
602{
603 int ret = 0;
604
605 if (!pdata->bcharger_pdata) {
606 dev_err(bq2419x->dev, "No charger platform data\n");
607 return 0;
608 }
609
610 bq2419x->chg_reg_desc.name = "bq2419x-charger";
611 bq2419x->chg_reg_desc.ops = &bq2419x_tegra_regulator_ops;
612 bq2419x->chg_reg_desc.type = REGULATOR_CURRENT;
613 bq2419x->chg_reg_desc.owner = THIS_MODULE;
614
615 bq2419x->chg_reg_init_data.supply_regulator = NULL;
616 bq2419x->chg_reg_init_data.regulator_init = NULL;
617 bq2419x->chg_reg_init_data.num_consumer_supplies =
618 pdata->bcharger_pdata->num_consumer_supplies;
619 bq2419x->chg_reg_init_data.consumer_supplies =
620 pdata->bcharger_pdata->consumer_supplies;
621 bq2419x->chg_reg_init_data.driver_data = bq2419x;
622 bq2419x->chg_reg_init_data.constraints.name = "bq2419x-charger";
623 bq2419x->chg_reg_init_data.constraints.min_uA = 0;
624 bq2419x->chg_reg_init_data.constraints.max_uA =
625 pdata->bcharger_pdata->max_charge_current_mA * 1000;
626
627 bq2419x->chg_reg_init_data.constraints.valid_modes_mask =
628 REGULATOR_MODE_NORMAL |
629 REGULATOR_MODE_STANDBY;
630
631 bq2419x->chg_reg_init_data.constraints.valid_ops_mask =
632 REGULATOR_CHANGE_MODE |
633 REGULATOR_CHANGE_STATUS |
634 REGULATOR_CHANGE_CURRENT;
635
636 bq2419x->chg_rdev = regulator_register(&bq2419x->chg_reg_desc,
637 bq2419x->dev, &bq2419x->chg_reg_init_data,
638 bq2419x, NULL);
639 if (IS_ERR(bq2419x->chg_rdev)) {
640 ret = PTR_ERR(bq2419x->chg_rdev);
641 dev_err(bq2419x->dev,
642 "vbus-charger regulator register failed %d\n", ret);
643 }
644 return ret;
645}
646
647static int bq2419x_init_vbus_regulator(struct bq2419x_chip *bq2419x,
648 struct bq2419x_platform_data *pdata)
649{
650 int ret = 0;
651
652 if (!pdata->vbus_pdata) {
653 dev_err(bq2419x->dev, "No vbus platform data\n");
654 return 0;
655 }
656
657 bq2419x->gpio_otg_iusb = pdata->vbus_pdata->gpio_otg_iusb;
658 bq2419x->vbus_reg_desc.name = "bq2419x-vbus";
659 bq2419x->vbus_reg_desc.ops = &bq2419x_vbus_ops;
660 bq2419x->vbus_reg_desc.type = REGULATOR_VOLTAGE;
661 bq2419x->vbus_reg_desc.owner = THIS_MODULE;
662
663 bq2419x->vbus_reg_init_data.supply_regulator = NULL;
664 bq2419x->vbus_reg_init_data.regulator_init = NULL;
665 bq2419x->vbus_reg_init_data.num_consumer_supplies =
666 pdata->vbus_pdata->num_consumer_supplies;
667 bq2419x->vbus_reg_init_data.consumer_supplies =
668 pdata->vbus_pdata->consumer_supplies;
669 bq2419x->vbus_reg_init_data.driver_data = bq2419x;
670
671 bq2419x->vbus_reg_init_data.constraints.name = "bq2419x-vbus";
672 bq2419x->vbus_reg_init_data.constraints.min_uV = 0;
673 bq2419x->vbus_reg_init_data.constraints.max_uV = 5000000,
674 bq2419x->vbus_reg_init_data.constraints.valid_modes_mask =
675 REGULATOR_MODE_NORMAL |
676 REGULATOR_MODE_STANDBY;
677 bq2419x->vbus_reg_init_data.constraints.valid_ops_mask =
678 REGULATOR_CHANGE_MODE |
679 REGULATOR_CHANGE_STATUS |
680 REGULATOR_CHANGE_VOLTAGE;
681
682 if (gpio_is_valid(bq2419x->gpio_otg_iusb)) {
683 ret = gpio_request_one(bq2419x->gpio_otg_iusb,
Pradeep Goudagunta9e75c232013-03-12 16:29:03 +0530684 GPIOF_OUT_INIT_HIGH, dev_name(bq2419x->dev));
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530685 if (ret < 0) {
686 dev_err(bq2419x->dev, "gpio request failed %d\n", ret);
687 return ret;
688 }
689 }
690
691 /* Register the regulators */
692 bq2419x->vbus_rdev = regulator_register(&bq2419x->vbus_reg_desc,
693 bq2419x->dev, &bq2419x->vbus_reg_init_data,
694 bq2419x, NULL);
695 if (IS_ERR(bq2419x->vbus_rdev)) {
696 ret = PTR_ERR(bq2419x->vbus_rdev);
697 dev_err(bq2419x->dev,
698 "VBUS regulator register failed %d\n", ret);
699 goto scrub;
700 }
701
702 /* Disable the VBUS regulator and enable charging */
703 ret = bq2419x_charger_enable(bq2419x);
704 if (ret < 0) {
705 dev_err(bq2419x->dev, "Charging enable failed %d", ret);
706 goto scrub_reg;
707 }
708 return ret;
709
710scrub_reg:
711 regulator_unregister(bq2419x->vbus_rdev);
712 bq2419x->vbus_rdev = NULL;
713scrub:
714 if (gpio_is_valid(bq2419x->gpio_otg_iusb))
715 gpio_free(bq2419x->gpio_otg_iusb);
716 return ret;
717}
718
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530719static int bq2419x_show_chip_version(struct bq2419x_chip *bq2419x)
720{
721 int ret;
722 unsigned int val;
723
724 ret = regmap_read(bq2419x->regmap, BQ2419X_REVISION_REG, &val);
725 if (ret < 0) {
726 dev_err(bq2419x->dev, "REVISION_REG read failed: %d\n", ret);
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500727 return ret;
728 }
729
730 if ((val & BQ24190_IC_VER) == BQ24190_IC_VER)
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530731 dev_info(bq2419x->dev, "chip type BQ24190 detected\n");
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530732 else if ((val & BQ24192_IC_VER) == BQ24192_IC_VER)
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530733 dev_info(bq2419x->dev, "chip type BQ2419X/3 detected\n");
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530734 else if ((val & BQ24192i_IC_VER) == BQ24192i_IC_VER)
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530735 dev_info(bq2419x->dev, "chip type BQ2419Xi detected\n");
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500736 return 0;
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530737}
Syed Rafiuddin208dc892013-01-24 14:57:46 +0530738
Laxman Dewangan9148fd32013-03-20 17:19:35 +0530739static int bq2419x_wakealarm(struct bq2419x_chip *bq2419x, int time_sec)
Syed Rafiuddind4d862d2013-03-08 15:52:59 +0530740{
741 int ret;
742 unsigned long now;
743 struct rtc_wkalrm alm;
Laxman Dewangan9148fd32013-03-20 17:19:35 +0530744 int alarm_time = time_sec;
Syed Rafiuddind4d862d2013-03-08 15:52:59 +0530745
Hyongbin Kim05e8e682013-04-22 21:41:19 +0900746 if (!alarm_time)
747 return 0;
748
Syed Rafiuddind4d862d2013-03-08 15:52:59 +0530749 alm.enabled = true;
750 ret = rtc_read_time(bq2419x->rtc, &alm.time);
751 if (ret < 0) {
752 dev_err(bq2419x->dev, "RTC read time failed %d\n", ret);
753 return ret;
754 }
755 rtc_tm_to_time(&alm.time, &now);
756
Laxman Dewangan9148fd32013-03-20 17:19:35 +0530757 rtc_time_to_tm(now + alarm_time, &alm.time);
Syed Rafiuddind4d862d2013-03-08 15:52:59 +0530758 ret = rtc_set_alarm(bq2419x->rtc, &alm);
759 if (ret < 0) {
760 dev_err(bq2419x->dev, "RTC set alarm failed %d\n", ret);
761 alm.enabled = false;
762 return ret;
763 }
764 alm.enabled = false;
765 return 0;
766}
767
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530768static int __devinit bq2419x_probe(struct i2c_client *client,
769 const struct i2c_device_id *id)
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500770{
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530771 struct bq2419x_chip *bq2419x;
772 struct bq2419x_platform_data *pdata;
773 int ret = 0;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500774
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530775 pdata = client->dev.platform_data;
776 if (!pdata) {
777 dev_err(&client->dev, "No Platform data");
778 return -EINVAL;
779 }
780
781 bq2419x = devm_kzalloc(&client->dev, sizeof(*bq2419x), GFP_KERNEL);
782 if (!bq2419x) {
783 dev_err(&client->dev, "Memory allocation failed\n");
784 return -ENOMEM;
785 }
786
787 bq2419x->regmap = devm_regmap_init_i2c(client, &bq2419x_regmap_config);
788 if (IS_ERR(bq2419x->regmap)) {
789 ret = PTR_ERR(bq2419x->regmap);
790 dev_err(&client->dev, "regmap init failed with err %d\n", ret);
791 return ret;
792 }
793
794 bq2419x->dev = &client->dev;
Hyongbin Kim12e014c2013-04-09 17:28:27 +0900795
796 if (pdata->bcharger_pdata) {
Hyongbin Kim12e014c2013-04-09 17:28:27 +0900797 bq2419x->update_status = pdata->bcharger_pdata->update_status;
798 bq2419x->rtc_alarm_time = pdata->bcharger_pdata->rtc_alarm_time;
799 bq2419x->wdt_time_sec = pdata->bcharger_pdata->wdt_timeout;
800 bq2419x->chg_restart_time =
801 pdata->bcharger_pdata->chg_restart_time;
802 bq2419x->chg_enable = true;
803 }
804
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530805 bq2419x->wdt_refresh_timeout = 25;
806 i2c_set_clientdata(client, bq2419x);
807 bq2419x->irq = client->irq;
Hyongbin Kim05e8e682013-04-22 21:41:19 +0900808
809 if (bq2419x->rtc_alarm_time)
810 bq2419x->rtc = alarmtimer_get_rtcdev();
811
Pradeep Goudagunta13f38432013-03-09 23:58:03 +0530812 mutex_init(&bq2419x->mutex);
Sang-Hun Lee7befcf92013-03-12 14:11:01 -0700813 bq2419x->suspended = 0;
Pradeep Goudagunta6bf95072013-03-19 18:07:43 +0530814 bq2419x->chg_restart_timeout = 0;
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530815
816 ret = bq2419x_show_chip_version(bq2419x);
817 if (ret < 0) {
818 dev_err(&client->dev, "version read failed %d\n", ret);
819 return ret;
820 }
821
822 ret = bq2419x_charger_init(bq2419x);
823 if (ret < 0) {
824 dev_err(bq2419x->dev, "Charger init failed: %d\n", ret);
825 return ret;
826 }
827
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530828 ret = bq2419x_init_charger_regulator(bq2419x, pdata);
829 if (ret < 0) {
830 dev_err(&client->dev,
831 "Charger regualtor init failed %d\n", ret);
832 return ret;
833 }
834
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530835 ret = bq2419x_init_vbus_regulator(bq2419x, pdata);
836 if (ret < 0) {
837 dev_err(&client->dev,
838 "VBUS regualtor init failed %d\n", ret);
Laxman Dewangan6d7070a2013-06-01 02:36:55 +0530839 goto scrub_chg_reg;
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530840 }
841
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530842 init_kthread_worker(&bq2419x->bq_kworker);
843 bq2419x->bq_kworker_task = kthread_run(kthread_worker_fn,
844 &bq2419x->bq_kworker,
845 dev_name(bq2419x->dev));
846 if (IS_ERR(bq2419x->bq_kworker_task)) {
847 ret = PTR_ERR(bq2419x->bq_kworker_task);
848 dev_err(&client->dev, "Kworker task creation failed %d\n", ret);
849 goto scrub_vbus_reg;
850 }
851
852 init_kthread_work(&bq2419x->bq_wdt_work, bq2419x_work_thread);
853 sched_setscheduler(bq2419x->bq_kworker_task,
854 SCHED_FIFO, &bq2419x_param);
855 queue_kthread_work(&bq2419x->bq_kworker, &bq2419x->bq_wdt_work);
856
Pradeep Goudagunta7b7ac8b2013-03-11 18:13:59 +0530857 ret = bq2419x_watchdog_init(bq2419x, bq2419x->wdt_time_sec, "PROBE");
Syed Rafiuddin8fa48bf2013-03-05 10:31:20 +0530858 if (ret < 0) {
859 dev_err(bq2419x->dev, "BQWDT init failed %d\n", ret);
Laxman Dewangan6d7070a2013-06-01 02:36:55 +0530860 goto scrub_kthread;
Syed Rafiuddin8fa48bf2013-03-05 10:31:20 +0530861 }
862
Pradeep Goudagunta7b7ac8b2013-03-11 18:13:59 +0530863 ret = bq2419x_fault_clear_sts(bq2419x);
Syed Rafiuddin337240d2013-03-08 20:50:35 +0530864 if (ret < 0) {
865 dev_err(bq2419x->dev, "fault clear status failed %d\n", ret);
Laxman Dewangan6d7070a2013-06-01 02:36:55 +0530866 goto scrub_kthread;
Syed Rafiuddin337240d2013-03-08 20:50:35 +0530867 }
868
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530869 ret = request_threaded_irq(bq2419x->irq, NULL,
870 bq2419x_irq, IRQF_TRIGGER_FALLING,
871 dev_name(bq2419x->dev), bq2419x);
872 if (ret < 0) {
873 dev_err(bq2419x->dev, "request IRQ %d fail, err = %d\n",
874 bq2419x->irq, ret);
875 goto scrub_kthread;
876 }
877
878 /* enable charging */
879 ret = bq2419x_charger_enable(bq2419x);
880 if (ret < 0)
881 goto scrub_irq;
882
883 return 0;
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530884scrub_irq:
885 free_irq(bq2419x->irq, bq2419x);
886scrub_kthread:
887 bq2419x->stop_thread = true;
888 flush_kthread_worker(&bq2419x->bq_kworker);
889 kthread_stop(bq2419x->bq_kworker_task);
890scrub_vbus_reg:
891 regulator_unregister(bq2419x->vbus_rdev);
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530892scrub_chg_reg:
893 regulator_unregister(bq2419x->chg_rdev);
Pradeep Goudagunta13f38432013-03-09 23:58:03 +0530894 mutex_destroy(&bq2419x->mutex);
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530895 return ret;
896}
897
898static int __devexit bq2419x_remove(struct i2c_client *client)
899{
900 struct bq2419x_chip *bq2419x = i2c_get_clientdata(client);
901
902 free_irq(bq2419x->irq, bq2419x);
903 bq2419x->stop_thread = true;
904 flush_kthread_worker(&bq2419x->bq_kworker);
905 kthread_stop(bq2419x->bq_kworker_task);
906 regulator_unregister(bq2419x->vbus_rdev);
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530907 regulator_unregister(bq2419x->chg_rdev);
Pradeep Goudagunta13f38432013-03-09 23:58:03 +0530908 mutex_destroy(&bq2419x->mutex);
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500909 return 0;
910}
911
Syed Rafiuddind4d862d2013-03-08 15:52:59 +0530912static void bq2419x_shutdown(struct i2c_client *client)
913{
914 int ret = 0;
915 struct bq2419x_chip *bq2419x = i2c_get_clientdata(client);
Laxman Dewangan9148fd32013-03-20 17:19:35 +0530916 int alarm_time = bq2419x->rtc_alarm_time;
Syed Rafiuddind4d862d2013-03-08 15:52:59 +0530917
Anshul Jaina5f0d172013-03-11 15:05:52 -0700918 if (bq2419x->irq)
919 disable_irq(bq2419x->irq);
920
Hyongbin Kim05e8e682013-04-22 21:41:19 +0900921 if (alarm_time && !bq2419x->rtc)
Laxman Dewangan9148fd32013-03-20 17:19:35 +0530922 bq2419x->rtc = alarmtimer_get_rtcdev();
923
Hyongbin Kim05e8e682013-04-22 21:41:19 +0900924 if (alarm_time && (bq2419x->in_current_limit > 500)) {
Laxman Dewangan9148fd32013-03-20 17:19:35 +0530925 dev_info(bq2419x->dev, "HighCurrent %dmA charger is connectd\n",
926 bq2419x->in_current_limit);
927 ret = bq2419x_reset_wdt(bq2419x, "shutdown");
928 if (ret < 0)
929 dev_err(bq2419x->dev,
930 "bq2419x_reset_wdt failed: %d\n", ret);
931 alarm_time = 20;
932 }
933
Sang-Hun Lee7befcf92013-03-12 14:11:01 -0700934 mutex_lock(&bq2419x->mutex);
935 bq2419x->suspended = 1;
936 mutex_unlock(&bq2419x->mutex);
937
Syed Rafiuddind4d862d2013-03-08 15:52:59 +0530938 ret = bq2419x_charger_enable(bq2419x);
939 if (ret < 0)
940 dev_err(bq2419x->dev, "Charger enable failed %d", ret);
941
Hyongbin Kim05e8e682013-04-22 21:41:19 +0900942 if (alarm_time && (bq2419x->in_current_limit <= 500)) {
Laxman Dewangan9148fd32013-03-20 17:19:35 +0530943 /* Configure charging current to 500mA */
944 ret = regmap_write(bq2419x->regmap,
945 BQ2419X_INPUT_SRC_REG, 0x32);
946 if (ret < 0)
947 dev_err(bq2419x->dev,
948 "INPUT_SRC_REG write failed %d\n", ret);
949 }
Syed Rafiuddind4d862d2013-03-08 15:52:59 +0530950
Laxman Dewangan9148fd32013-03-20 17:19:35 +0530951 ret = bq2419x_wakealarm(bq2419x, alarm_time);
Syed Rafiuddind4d862d2013-03-08 15:52:59 +0530952 if (ret < 0)
953 dev_err(bq2419x->dev, "RTC wake alarm config failed %d\n", ret);
954}
955
Pradeep Goudagunta7b7ac8b2013-03-11 18:13:59 +0530956#ifdef CONFIG_PM_SLEEP
957static int bq2419x_suspend(struct device *dev)
958{
959 int ret = 0;
960 struct bq2419x_chip *bq2419x = dev_get_drvdata(dev);
961
Sang-Hun Lee7befcf92013-03-12 14:11:01 -0700962 mutex_lock(&bq2419x->mutex);
963 bq2419x->suspended = 1;
964 mutex_unlock(&bq2419x->mutex);
Pradeep Goudagunta7b7ac8b2013-03-11 18:13:59 +0530965 disable_irq(bq2419x->irq);
966 ret = bq2419x_charger_enable(bq2419x);
Pradeep Goudagunta803ab952013-03-12 14:44:32 +0530967 if (ret < 0) {
Pradeep Goudagunta7b7ac8b2013-03-11 18:13:59 +0530968 dev_err(bq2419x->dev, "Charger enable failed %d", ret);
Pradeep Goudagunta803ab952013-03-12 14:44:32 +0530969 return ret;
970 }
Pradeep Goudagunta7b7ac8b2013-03-11 18:13:59 +0530971
972 /* Configure charging current to 500mA */
973 ret = regmap_write(bq2419x->regmap, BQ2419X_INPUT_SRC_REG, 0x32);
Pradeep Goudagunta803ab952013-03-12 14:44:32 +0530974 if (ret < 0) {
Pradeep Goudagunta7b7ac8b2013-03-11 18:13:59 +0530975 dev_err(bq2419x->dev, "INPUT_SRC_REG write failed %d\n", ret);
Pradeep Goudagunta803ab952013-03-12 14:44:32 +0530976 return ret;
977 }
Pradeep Goudagunta7b7ac8b2013-03-11 18:13:59 +0530978
979 return 0;
980}
981
982static int bq2419x_resume(struct device *dev)
983{
984 int ret = 0;
985 struct bq2419x_chip *bq2419x = dev_get_drvdata(dev);
986 unsigned int val;
987
988 ret = regmap_read(bq2419x->regmap, BQ2419X_FAULT_REG, &val);
989 if (ret < 0) {
990 dev_err(bq2419x->dev, "FAULT_REG read failed %d\n", ret);
991 return ret;
992 }
993
994 if (val & BQ2419x_FAULT_WATCHDOG_FAULT) {
995 dev_err(bq2419x->dev,
996 "Charging Fault: Watchdog Timer Expired\n");
997
998 ret = bq2419x_watchdog_init(bq2419x, bq2419x->wdt_time_sec,
999 "RESUME");
1000 if (ret < 0) {
1001 dev_err(bq2419x->dev, "BQWDT init failed %d\n", ret);
1002 return ret;
1003 }
1004 }
1005
1006 ret = bq2419x_fault_clear_sts(bq2419x);
1007 if (ret < 0) {
1008 dev_err(bq2419x->dev, "fault clear status failed %d\n", ret);
1009 return ret;
1010 }
1011
1012 ret = bq2419x_charger_init(bq2419x);
1013 if (ret < 0) {
1014 dev_err(bq2419x->dev, "Charger init failed: %d\n", ret);
1015 return ret;
1016 }
1017
1018 ret = bq2419x_init(bq2419x);
1019 if (ret < 0) {
1020 dev_err(bq2419x->dev, "bq2419x init failed: %d\n", ret);
1021 return ret;
1022 }
1023
Sang-Hun Lee7befcf92013-03-12 14:11:01 -07001024 mutex_lock(&bq2419x->mutex);
1025 bq2419x->suspended = 0;
1026 mutex_unlock(&bq2419x->mutex);
Pradeep Goudagunta9e75c232013-03-12 16:29:03 +05301027 if (gpio_is_valid(bq2419x->gpio_otg_iusb))
1028 gpio_set_value(bq2419x->gpio_otg_iusb, 1);
1029
Pradeep Goudagunta7b7ac8b2013-03-11 18:13:59 +05301030 enable_irq(bq2419x->irq);
1031 return 0;
1032};
1033#endif
1034
1035static const struct dev_pm_ops bq2419x_pm_ops = {
1036 SET_SYSTEM_SLEEP_PM_OPS(bq2419x_suspend,
1037 bq2419x_resume)
1038};
1039
Syed Rafiuddin6126b492013-04-03 14:06:34 +05301040static const struct i2c_device_id bq2419x_id[] = {
1041 {.name = "bq2419x",},
1042 {},
1043};
1044MODULE_DEVICE_TABLE(i2c, bq2419x_id);
1045
1046static struct i2c_driver bq2419x_i2c_driver = {
Laxman Dewanganf27ed352012-11-29 19:33:02 -05001047 .driver = {
Syed Rafiuddind4d862d2013-03-08 15:52:59 +05301048 .name = "bq2419x",
1049 .owner = THIS_MODULE,
Pradeep Goudagunta7b7ac8b2013-03-11 18:13:59 +05301050 .pm = &bq2419x_pm_ops,
Laxman Dewanganf27ed352012-11-29 19:33:02 -05001051 },
Syed Rafiuddind4d862d2013-03-08 15:52:59 +05301052 .probe = bq2419x_probe,
1053 .remove = __devexit_p(bq2419x_remove),
1054 .shutdown = bq2419x_shutdown,
1055 .id_table = bq2419x_id,
Laxman Dewanganf27ed352012-11-29 19:33:02 -05001056};
1057
Syed Rafiuddin6126b492013-04-03 14:06:34 +05301058static int __init bq2419x_module_init(void)
Syed Rafiuddinee69db32013-01-09 15:40:55 +05301059{
Syed Rafiuddin6126b492013-04-03 14:06:34 +05301060 return i2c_add_driver(&bq2419x_i2c_driver);
Syed Rafiuddinee69db32013-01-09 15:40:55 +05301061}
Syed Rafiuddin6126b492013-04-03 14:06:34 +05301062subsys_initcall(bq2419x_module_init);
Syed Rafiuddinee69db32013-01-09 15:40:55 +05301063
Syed Rafiuddin6126b492013-04-03 14:06:34 +05301064static void __exit bq2419x_cleanup(void)
Syed Rafiuddinee69db32013-01-09 15:40:55 +05301065{
Syed Rafiuddin6126b492013-04-03 14:06:34 +05301066 i2c_del_driver(&bq2419x_i2c_driver);
Syed Rafiuddinee69db32013-01-09 15:40:55 +05301067}
Syed Rafiuddin6126b492013-04-03 14:06:34 +05301068module_exit(bq2419x_cleanup);
Laxman Dewanganf27ed352012-11-29 19:33:02 -05001069
Syed Rafiuddin6126b492013-04-03 14:06:34 +05301070MODULE_DESCRIPTION("BQ24190/BQ24192/BQ24192i/BQ24193 battery charger driver");
Laxman Dewanganf27ed352012-11-29 19:33:02 -05001071MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
Syed Rafiuddin6126b492013-04-03 14:06:34 +05301072MODULE_AUTHOR("Syed Rafiuddin <srafiuddin@nvidia.com");
Laxman Dewanganf27ed352012-11-29 19:33:02 -05001073MODULE_LICENSE("GPL v2");