blob: fa6e839651dafc489d67a905419e83abe265c59c [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;
Hyongbin Kim12e014c2013-04-09 17:28:27 +090097 int chg_enable;
Syed Rafiuddin6126b492013-04-03 14:06:34 +053098};
Rohith Seelaboyina2fd2e112013-02-02 18:10:54 +053099
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500100static enum power_supply_property bq2419x_psy_props[] = {
101 POWER_SUPPLY_PROP_ONLINE,
102};
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530103
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500104static int current_to_reg(const unsigned int *tbl,
105 size_t size, unsigned int val)
106{
107 size_t i;
108
109 for (i = 0; i < size; i++)
110 if (val < tbl[i])
111 break;
112 return i > 0 ? i - 1 : -EINVAL;
113}
114
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530115static int bq2419x_charger_enable(struct bq2419x_chip *bq2419x)
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500116{
117 int ret;
Rohith Seelaboyina2fd2e112013-02-02 18:10:54 +0530118
Hyongbin Kim12e014c2013-04-09 17:28:27 +0900119 if (bq2419x->chg_enable) {
120 dev_info(bq2419x->dev, "Charging enabled\n");
121 ret = regmap_update_bits(bq2419x->regmap, BQ2419X_PWR_ON_REG,
122 BQ2419X_ENABLE_CHARGE_MASK,
123 BQ2419X_ENABLE_CHARGE);
124 } else {
125 dev_info(bq2419x->dev, "Charging disabled\n");
126 ret = regmap_update_bits(bq2419x->regmap, BQ2419X_PWR_ON_REG,
127 BQ2419X_ENABLE_CHARGE_MASK,
128 BQ2419X_DISABLE_CHARGE);
129 }
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500130 if (ret < 0)
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530131 dev_err(bq2419x->dev, "register update failed, err %d\n", ret);
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500132 return ret;
133}
134
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530135static int bq2419x_vbus_regulator_enable_time(struct regulator_dev *rdev)
136{
137 return 500000;
138}
139
140static int bq2419x_vbus_enable(struct regulator_dev *rdev)
141{
142 struct bq2419x_chip *bq2419x = rdev_get_drvdata(rdev);
143 int ret;
144
145 dev_info(bq2419x->dev, "VBUS enabled, charging disabled\n");
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530146
147 ret = regmap_update_bits(bq2419x->regmap, BQ2419X_PWR_ON_REG,
148 BQ2419X_ENABLE_CHARGE_MASK, BQ2419X_ENABLE_VBUS);
149 if (ret < 0)
150 dev_err(bq2419x->dev, "PWR_ON_REG update failed %d", ret);
151
152 return ret;
153}
154
155static int bq2419x_vbus_disable(struct regulator_dev *rdev)
156{
157 struct bq2419x_chip *bq2419x = rdev_get_drvdata(rdev);
158 int ret;
159
160 dev_info(bq2419x->dev, "VBUS disabled, charging enabled\n");
161 ret = bq2419x_charger_enable(bq2419x);
162 if (ret < 0) {
163 dev_err(bq2419x->dev, "Charger enable failed %d", ret);
164 return ret;
165 }
166
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530167 return ret;
168}
169
170static int bq2419x_vbus_is_enabled(struct regulator_dev *rdev)
171{
172 struct bq2419x_chip *bq2419x = rdev_get_drvdata(rdev);
173 int ret;
174 unsigned int data;
175
176 ret = regmap_read(bq2419x->regmap, BQ2419X_PWR_ON_REG, &data);
177 if (ret < 0) {
178 dev_err(bq2419x->dev, "PWR_ON_REG read failed %d", ret);
179 return ret;
180 }
181 return (data & BQ2419X_ENABLE_CHARGE_MASK) == BQ2419X_ENABLE_VBUS;
182}
183
184static struct regulator_ops bq2419x_vbus_ops = {
185 .enable = bq2419x_vbus_enable,
186 .disable = bq2419x_vbus_disable,
187 .is_enabled = bq2419x_vbus_is_enabled,
188 .enable_time = bq2419x_vbus_regulator_enable_time,
189};
190
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500191static int bq2419x_ac_get_property(struct power_supply *psy,
192 enum power_supply_property psp, union power_supply_propval *val)
193{
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530194 struct bq2419x_chip *bq2419x;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500195
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530196 bq2419x = container_of(psy, struct bq2419x_chip, ac);
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500197 if (psp == POWER_SUPPLY_PROP_ONLINE)
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530198 val->intval = bq2419x->ac_online;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500199 else
200 return -EINVAL;
201 return 0;
202}
203
204static int bq2419x_usb_get_property(struct power_supply *psy,
205 enum power_supply_property psp,
206 union power_supply_propval *val)
207{
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530208 struct bq2419x_chip *bq2419x;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500209
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530210 bq2419x = container_of(psy, struct bq2419x_chip, usb);
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500211 if (psp == POWER_SUPPLY_PROP_ONLINE)
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530212 val->intval = bq2419x->usb_online;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500213 else
214 return -EINVAL;
215 return 0;
216}
217
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530218static int bq2419x_init(struct bq2419x_chip *bq2419x)
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500219{
220 int val, ret = 0;
221
Syed Rafiuddinf2d004d2013-02-18 15:30:58 +0530222 /* Clear EN_HIZ */
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530223 ret = regmap_update_bits(bq2419x->regmap,
Syed Rafiuddinf2d004d2013-02-18 15:30:58 +0530224 BQ2419X_INPUT_SRC_REG, BQ2419X_EN_HIZ, 0);
225 if (ret < 0) {
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530226 dev_err(bq2419x->dev, "error reading reg: 0x%x\n",
Syed Rafiuddinf2d004d2013-02-18 15:30:58 +0530227 BQ2419X_INPUT_SRC_REG);
228 return ret;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500229 }
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530230
Syed Rafiuddinf2d004d2013-02-18 15:30:58 +0530231 /* Configure input current limit */
232 val = current_to_reg(iinlim, ARRAY_SIZE(iinlim),
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530233 bq2419x->in_current_limit);
Syed Rafiuddinf2d004d2013-02-18 15:30:58 +0530234 if (val < 0)
235 return 0;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500236
Pradeep Goudagunta10f1cb82013-03-25 16:48:14 -0700237 val &= ~(BQ2419x_INPUT_VOLTAGE_MASK);
238 /* Configure inout voltage to 4.52 in case of NV
239 * NV charger.
240 */
241 if (bq2419x->in_current_limit == 2000)
242 val |= BQ2419x_NVCHARGER_INPUT_VOL_SEL;
243 else
244 val |= BQ2419x_DEFAULT_INPUT_VOL_SEL;
245
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530246 ret = regmap_update_bits(bq2419x->regmap,
Pradeep Goudagunta10f1cb82013-03-25 16:48:14 -0700247 BQ2419X_INPUT_SRC_REG, BQ2419x_CONFIG_MASK |
248 BQ2419x_INPUT_VOLTAGE_MASK, val);
Syed Rafiuddinf2d004d2013-02-18 15:30:58 +0530249 if (ret < 0)
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530250 dev_err(bq2419x->dev, "error reading reg: 0x%x\n",
Syed Rafiuddinf2d004d2013-02-18 15:30:58 +0530251 BQ2419X_INPUT_SRC_REG);
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500252 return ret;
253}
254
Syed Rafiuddin73692422013-03-08 16:36:09 +0530255static int bq2419x_charger_init(struct bq2419x_chip *bq2419x)
256{
257 int ret;
258
259 /* Configure Output Current Control to 3A*/
260 ret = regmap_write(bq2419x->regmap, BQ2419X_CHRG_CTRL_REG, 0xC0);
261 if (ret < 0) {
262 dev_err(bq2419x->dev, "CHRG_CTRL_REG write failed %d\n", ret);
263 return ret;
264 }
265
266 /*
267 * Configure Input voltage limit reset to OTP value,
268 * and charging current to 500mA.
269 */
270 ret = regmap_write(bq2419x->regmap, BQ2419X_INPUT_SRC_REG, 0x32);
271 if (ret < 0)
272 dev_err(bq2419x->dev, "INPUT_SRC_REG write failed %d\n", ret);
273
274 return ret;
275}
276
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530277static int bq2419x_set_charging_current(struct regulator_dev *rdev,
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530278 int min_uA, int max_uA)
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500279{
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530280 struct bq2419x_chip *bq_charger = rdev_get_drvdata(rdev);
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530281 int ret = 0;
282 int val;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500283
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530284 dev_info(bq_charger->dev, "Setting charging current %d\n", max_uA/1000);
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530285 msleep(200);
286 bq_charger->usb_online = 0;
287 bq_charger->ac_online = 0;
288 bq_charger->status = 0;
289
Pradeep Goudaguntada8f1f22013-03-10 01:16:33 +0530290 ret = bq2419x_charger_enable(bq_charger);
291 if (ret < 0) {
292 dev_err(bq_charger->dev, "Charger enable failed %d", ret);
293 return ret;
294 }
295
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530296 ret = regmap_read(bq_charger->regmap, BQ2419X_SYS_STAT_REG, &val);
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530297 if (ret < 0)
298 dev_err(bq_charger->dev, "error reading reg: 0x%x\n",
299 BQ2419X_SYS_STAT_REG);
300
Rohith Seelaboyina2fd2e112013-02-02 18:10:54 +0530301 if (max_uA == 0 && val != 0)
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530302 return ret;
303
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530304 bq_charger->in_current_limit = max_uA/1000;
Syed Rafiuddin208dc892013-01-24 14:57:46 +0530305 if ((val & BQ2419x_VBUS_STAT) == BQ2419x_VBUS_UNKNOWN) {
306 bq_charger->status = 0;
Syed Rafiuddinb1ca04c2013-02-06 20:46:20 +0530307 bq_charger->usb_online = 0;
Syed Rafiuddinf2d004d2013-02-18 15:30:58 +0530308 bq_charger->in_current_limit = 500;
Syed Rafiuddin208dc892013-01-24 14:57:46 +0530309 ret = bq2419x_init(bq_charger);
310 if (ret < 0)
311 goto error;
312 if (bq_charger->update_status)
313 bq_charger->update_status
314 (bq_charger->status, 0);
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530315 } else if (bq_charger->in_current_limit == 500) {
Syed Rafiuddin208dc892013-01-24 14:57:46 +0530316 bq_charger->status = 1;
317 bq_charger->usb_online = 1;
Syed Rafiuddin208dc892013-01-24 14:57:46 +0530318 ret = bq2419x_init(bq_charger);
319 if (ret < 0)
320 goto error;
321 if (bq_charger->update_status)
322 bq_charger->update_status
323 (bq_charger->status, 2);
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530324 } else {
Syed Rafiuddin208dc892013-01-24 14:57:46 +0530325 bq_charger->status = 1;
326 bq_charger->ac_online = 1;
Syed Rafiuddin208dc892013-01-24 14:57:46 +0530327 ret = bq2419x_init(bq_charger);
328 if (ret < 0)
329 goto error;
330 if (bq_charger->update_status)
331 bq_charger->update_status
332 (bq_charger->status, 1);
333 }
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530334 if (ret == 0) {
335 if (bq_charger->use_mains)
336 power_supply_changed(&bq_charger->ac);
337 if (bq_charger->use_usb)
338 power_supply_changed(&bq_charger->usb);
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500339 }
Syed Rafiuddin208dc892013-01-24 14:57:46 +0530340 return 0;
341error:
342 dev_err(bq_charger->dev, "Charger enable failed, err = %d\n", ret);
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530343 return ret;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500344}
345
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530346static struct regulator_ops bq2419x_tegra_regulator_ops = {
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530347 .set_current_limit = bq2419x_set_charging_current,
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530348};
349
Pradeep Goudagunta13f38432013-03-09 23:58:03 +0530350static int bq2419x_reset_wdt(struct bq2419x_chip *bq2419x, const char *from)
Kunal Agrawal24680952013-01-29 14:44:19 +0530351{
Pradeep Goudagunta803ab952013-03-12 14:44:32 +0530352 int ret = 0;
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530353 unsigned int reg01;
Kunal Agrawal24680952013-01-29 14:44:19 +0530354
Pradeep Goudagunta13f38432013-03-09 23:58:03 +0530355 mutex_lock(&bq2419x->mutex);
Pradeep Goudagunta803ab952013-03-12 14:44:32 +0530356 if (bq2419x->suspended)
357 goto scrub;
358
Pradeep Goudagunta13f38432013-03-09 23:58:03 +0530359 dev_info(bq2419x->dev, "%s() from %s()\n", __func__, from);
Pradeep Goudagunta9eda3b32013-03-09 23:54:16 +0530360
361 /* Clear EN_HIZ */
362 ret = regmap_update_bits(bq2419x->regmap,
363 BQ2419X_INPUT_SRC_REG, BQ2419X_EN_HIZ, 0);
364 if (ret < 0) {
365 dev_err(bq2419x->dev, "INPUT_SRC_REG update failed:%d\n", ret);
Pradeep Goudagunta803ab952013-03-12 14:44:32 +0530366 goto scrub;
Pradeep Goudagunta9eda3b32013-03-09 23:54:16 +0530367 }
368
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530369 ret = regmap_read(bq2419x->regmap, BQ2419X_PWR_ON_REG, &reg01);
370 if (ret < 0) {
371 dev_err(bq2419x->dev, "PWR_ON_REG read failed: %d\n", ret);
Pradeep Goudagunta803ab952013-03-12 14:44:32 +0530372 goto scrub;
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530373 }
374
375 reg01 |= BIT(6);
376
377 /* Write two times to make sure reset WDT */
378 ret = regmap_write(bq2419x->regmap, BQ2419X_PWR_ON_REG, reg01);
379 if (ret < 0) {
380 dev_err(bq2419x->dev, "PWR_ON_REG write failed: %d\n", ret);
Pradeep Goudagunta803ab952013-03-12 14:44:32 +0530381 goto scrub;
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530382 }
383 ret = regmap_write(bq2419x->regmap, BQ2419X_PWR_ON_REG, reg01);
384 if (ret < 0) {
385 dev_err(bq2419x->dev, "PWR_ON_REG write failed: %d\n", ret);
Pradeep Goudagunta803ab952013-03-12 14:44:32 +0530386 goto scrub;
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530387 }
Pradeep Goudagunta803ab952013-03-12 14:44:32 +0530388
389scrub:
Pradeep Goudagunta13f38432013-03-09 23:58:03 +0530390 mutex_unlock(&bq2419x->mutex);
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530391 return ret;
Kunal Agrawal24680952013-01-29 14:44:19 +0530392}
393
Syed Rafiuddin337240d2013-03-08 20:50:35 +0530394static int bq2419x_fault_clear_sts(struct bq2419x_chip *bq2419x)
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500395{
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500396 int ret;
Syed Rafiuddin8fa48bf2013-03-05 10:31:20 +0530397 unsigned int reg09;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500398
Syed Rafiuddin8fa48bf2013-03-05 10:31:20 +0530399 ret = regmap_read(bq2419x->regmap, BQ2419X_FAULT_REG, &reg09);
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500400 if (ret < 0) {
Syed Rafiuddin8fa48bf2013-03-05 10:31:20 +0530401 dev_err(bq2419x->dev, "FAULT_REG read failed: %d\n", ret);
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530402 return ret;
403 }
404
Syed Rafiuddin8fa48bf2013-03-05 10:31:20 +0530405 ret = regmap_read(bq2419x->regmap, BQ2419X_FAULT_REG, &reg09);
Syed Rafiuddin337240d2013-03-08 20:50:35 +0530406 if (ret < 0)
Syed Rafiuddin8fa48bf2013-03-05 10:31:20 +0530407 dev_err(bq2419x->dev, "FAULT_REG read failed: %d\n", ret);
Syed Rafiuddin337240d2013-03-08 20:50:35 +0530408
409 return ret;
410}
411
412static int bq2419x_watchdog_init(struct bq2419x_chip *bq2419x,
Pradeep Goudagunta7b7ac8b2013-03-11 18:13:59 +0530413 int timeout, const char *from)
Syed Rafiuddin337240d2013-03-08 20:50:35 +0530414{
415 int ret, val;
416 unsigned int reg05;
417
418 if (!timeout) {
419 ret = regmap_update_bits(bq2419x->regmap, BQ2419X_TIME_CTRL_REG,
420 BQ2419X_WD_MASK, 0);
421 if (ret < 0)
422 dev_err(bq2419x->dev,
423 "TIME_CTRL_REG read failed: %d\n", ret);
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530424 return ret;
425 }
Syed Rafiuddin8fa48bf2013-03-05 10:31:20 +0530426
Syed Rafiuddin337240d2013-03-08 20:50:35 +0530427 if (timeout <= 60) {
428 val = BQ2419X_WD_40ms;
429 bq2419x->wdt_refresh_timeout = 25;
430 } else if (timeout <= 120) {
431 val = BQ2419X_WD_80ms;
432 bq2419x->wdt_refresh_timeout = 50;
433 } else {
434 val = BQ2419X_WD_160ms;
435 bq2419x->wdt_refresh_timeout = 125;
436 }
437
438 ret = regmap_read(bq2419x->regmap, BQ2419X_TIME_CTRL_REG, &reg05);
439 if (ret < 0) {
440 dev_err(bq2419x->dev,
441 "TIME_CTRL_REG read failed:%d\n", ret);
442 return ret;
443 }
444
445 if ((reg05 & BQ2419X_WD_MASK) != val) {
446 ret = regmap_update_bits(bq2419x->regmap, BQ2419X_TIME_CTRL_REG,
447 BQ2419X_WD_MASK, val);
448 if (ret < 0) {
449 dev_err(bq2419x->dev,
450 "TIME_CTRL_REG read failed: %d\n", ret);
451 return ret;
452 }
453 }
454
Pradeep Goudagunta7b7ac8b2013-03-11 18:13:59 +0530455 ret = bq2419x_reset_wdt(bq2419x, from);
Syed Rafiuddin337240d2013-03-08 20:50:35 +0530456 if (ret < 0)
457 dev_err(bq2419x->dev, "bq2419x_reset_wdt failed: %d\n", ret);
458
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530459 return ret;
460}
461
462static void bq2419x_work_thread(struct kthread_work *work)
463{
464 struct bq2419x_chip *bq2419x = container_of(work,
465 struct bq2419x_chip, bq_wdt_work);
466 int ret;
467
468 for (;;) {
469 if (bq2419x->stop_thread)
470 return;
471
Pradeep Goudagunta6bf95072013-03-19 18:07:43 +0530472 if (bq2419x->chg_restart_timeout) {
473 mutex_lock(&bq2419x->mutex);
474 bq2419x->chg_restart_timeout--;
475 if (!bq2419x->chg_restart_timeout) {
476 ret = bq2419x_charger_enable(bq2419x);
477 if (ret < 0)
478 dev_err(bq2419x->dev,
479 "Charger enable failed %d", ret);
480 }
481 if (bq2419x->suspended)
482 bq2419x->chg_restart_timeout = 0;
483
484 mutex_unlock(&bq2419x->mutex);
485 }
486
Pradeep Goudagunta13f38432013-03-09 23:58:03 +0530487 ret = bq2419x_reset_wdt(bq2419x, "THREAD");
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530488 if (ret < 0)
489 dev_err(bq2419x->dev,
490 "bq2419x_reset_wdt failed: %d\n", ret);
491
492 msleep(bq2419x->wdt_refresh_timeout * 1000);
493 }
494}
495
496static irqreturn_t bq2419x_irq(int irq, void *data)
497{
498 struct bq2419x_chip *bq2419x = data;
499 int ret;
500 unsigned int val;
501
502 ret = regmap_read(bq2419x->regmap, BQ2419X_FAULT_REG, &val);
503 if (ret < 0) {
504 dev_err(bq2419x->dev, "FAULT_REG read failed %d\n", ret);
505 return ret;
506 }
507
508 dev_info(bq2419x->dev, "%s() Irq %d status 0x%02x\n",
509 __func__, irq, val);
510
511 if (val & BQ2419x_FAULT_WATCHDOG_FAULT) {
512 dev_err(bq2419x->dev,
513 "Charging Fault: Watchdog Timer Expired\n");
Pradeep Goudagunta7b7ac8b2013-03-11 18:13:59 +0530514 ret = bq2419x_watchdog_init(bq2419x, bq2419x->wdt_time_sec,
515 "ISR");
516 if (ret < 0) {
517 dev_err(bq2419x->dev, "BQWDT init failed %d\n", ret);
518 return ret;
519 }
Syed Rafiuddin73692422013-03-08 16:36:09 +0530520
521 ret = bq2419x_charger_init(bq2419x);
522 if (ret < 0) {
523 dev_err(bq2419x->dev, "Charger init failed: %d\n", ret);
524 return ret;
525 }
526
527 ret = bq2419x_init(bq2419x);
528 if (ret < 0) {
529 dev_err(bq2419x->dev, "bq2419x init failed: %d\n", ret);
530 return ret;
531 }
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530532 }
533
534 if (val & BQ2419x_FAULT_BOOST_FAULT)
535 dev_err(bq2419x->dev, "Charging Fault: VBUS Overloaded\n");
536
537 switch (val & BQ2419x_FAULT_CHRG_FAULT_MASK) {
538 case BQ2419x_FAULT_CHRG_INPUT:
539 dev_err(bq2419x->dev, "Charging Fault: "
540 "Input Fault (VBUS OVP or VBAT<VBUS<3.8V)\n");
541 break;
542 case BQ2419x_FAULT_CHRG_THERMAL:
543 dev_err(bq2419x->dev, "Charging Fault: Thermal shutdown\n");
544 break;
545 case BQ2419x_FAULT_CHRG_SAFTY:
546 dev_err(bq2419x->dev,
547 "Charging Fault: Safety timer expiration\n");
Pradeep Goudagunta6bf95072013-03-19 18:07:43 +0530548 bq2419x->chg_restart_timeout = bq2419x->chg_restart_time /
549 bq2419x->wdt_refresh_timeout;
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530550 break;
551 default:
552 break;
553 }
554
555 if (val & BQ2419x_FAULT_NTC_FAULT)
556 dev_err(bq2419x->dev, "Charging Fault: NTC fault %d\n",
557 val & BQ2419x_FAULT_NTC_FAULT);
558
Pradeep Goudagunta7b7ac8b2013-03-11 18:13:59 +0530559 ret = bq2419x_fault_clear_sts(bq2419x);
560 if (ret < 0) {
561 dev_err(bq2419x->dev, "fault clear status failed %d\n", ret);
562 return ret;
563 }
564
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530565 ret = regmap_read(bq2419x->regmap, BQ2419X_SYS_STAT_REG, &val);
566 if (ret < 0) {
567 dev_err(bq2419x->dev, "SYS_STAT_REG read failed %d\n", ret);
568 return ret;
569 }
570
571 if ((val & BQ2419x_CHRG_STATE_MASK) == BQ2419x_CHRG_STATE_CHARGE_DONE)
572 dev_info(bq2419x->dev, "Charging completed\n");
573
574 return IRQ_HANDLED;
575}
576
577static int bq2419x_init_charger_regulator(struct bq2419x_chip *bq2419x,
578 struct bq2419x_platform_data *pdata)
579{
580 int ret = 0;
581
582 if (!pdata->bcharger_pdata) {
583 dev_err(bq2419x->dev, "No charger platform data\n");
584 return 0;
585 }
586
587 bq2419x->chg_reg_desc.name = "bq2419x-charger";
588 bq2419x->chg_reg_desc.ops = &bq2419x_tegra_regulator_ops;
589 bq2419x->chg_reg_desc.type = REGULATOR_CURRENT;
590 bq2419x->chg_reg_desc.owner = THIS_MODULE;
591
592 bq2419x->chg_reg_init_data.supply_regulator = NULL;
593 bq2419x->chg_reg_init_data.regulator_init = NULL;
594 bq2419x->chg_reg_init_data.num_consumer_supplies =
595 pdata->bcharger_pdata->num_consumer_supplies;
596 bq2419x->chg_reg_init_data.consumer_supplies =
597 pdata->bcharger_pdata->consumer_supplies;
598 bq2419x->chg_reg_init_data.driver_data = bq2419x;
599 bq2419x->chg_reg_init_data.constraints.name = "bq2419x-charger";
600 bq2419x->chg_reg_init_data.constraints.min_uA = 0;
601 bq2419x->chg_reg_init_data.constraints.max_uA =
602 pdata->bcharger_pdata->max_charge_current_mA * 1000;
603
604 bq2419x->chg_reg_init_data.constraints.valid_modes_mask =
605 REGULATOR_MODE_NORMAL |
606 REGULATOR_MODE_STANDBY;
607
608 bq2419x->chg_reg_init_data.constraints.valid_ops_mask =
609 REGULATOR_CHANGE_MODE |
610 REGULATOR_CHANGE_STATUS |
611 REGULATOR_CHANGE_CURRENT;
612
613 bq2419x->chg_rdev = regulator_register(&bq2419x->chg_reg_desc,
614 bq2419x->dev, &bq2419x->chg_reg_init_data,
615 bq2419x, NULL);
616 if (IS_ERR(bq2419x->chg_rdev)) {
617 ret = PTR_ERR(bq2419x->chg_rdev);
618 dev_err(bq2419x->dev,
619 "vbus-charger regulator register failed %d\n", ret);
620 }
621 return ret;
622}
623
624static int bq2419x_init_vbus_regulator(struct bq2419x_chip *bq2419x,
625 struct bq2419x_platform_data *pdata)
626{
627 int ret = 0;
628
629 if (!pdata->vbus_pdata) {
630 dev_err(bq2419x->dev, "No vbus platform data\n");
631 return 0;
632 }
633
634 bq2419x->gpio_otg_iusb = pdata->vbus_pdata->gpio_otg_iusb;
635 bq2419x->vbus_reg_desc.name = "bq2419x-vbus";
636 bq2419x->vbus_reg_desc.ops = &bq2419x_vbus_ops;
637 bq2419x->vbus_reg_desc.type = REGULATOR_VOLTAGE;
638 bq2419x->vbus_reg_desc.owner = THIS_MODULE;
639
640 bq2419x->vbus_reg_init_data.supply_regulator = NULL;
641 bq2419x->vbus_reg_init_data.regulator_init = NULL;
642 bq2419x->vbus_reg_init_data.num_consumer_supplies =
643 pdata->vbus_pdata->num_consumer_supplies;
644 bq2419x->vbus_reg_init_data.consumer_supplies =
645 pdata->vbus_pdata->consumer_supplies;
646 bq2419x->vbus_reg_init_data.driver_data = bq2419x;
647
648 bq2419x->vbus_reg_init_data.constraints.name = "bq2419x-vbus";
649 bq2419x->vbus_reg_init_data.constraints.min_uV = 0;
650 bq2419x->vbus_reg_init_data.constraints.max_uV = 5000000,
651 bq2419x->vbus_reg_init_data.constraints.valid_modes_mask =
652 REGULATOR_MODE_NORMAL |
653 REGULATOR_MODE_STANDBY;
654 bq2419x->vbus_reg_init_data.constraints.valid_ops_mask =
655 REGULATOR_CHANGE_MODE |
656 REGULATOR_CHANGE_STATUS |
657 REGULATOR_CHANGE_VOLTAGE;
658
659 if (gpio_is_valid(bq2419x->gpio_otg_iusb)) {
660 ret = gpio_request_one(bq2419x->gpio_otg_iusb,
Pradeep Goudagunta9e75c232013-03-12 16:29:03 +0530661 GPIOF_OUT_INIT_HIGH, dev_name(bq2419x->dev));
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530662 if (ret < 0) {
663 dev_err(bq2419x->dev, "gpio request failed %d\n", ret);
664 return ret;
665 }
666 }
667
668 /* Register the regulators */
669 bq2419x->vbus_rdev = regulator_register(&bq2419x->vbus_reg_desc,
670 bq2419x->dev, &bq2419x->vbus_reg_init_data,
671 bq2419x, NULL);
672 if (IS_ERR(bq2419x->vbus_rdev)) {
673 ret = PTR_ERR(bq2419x->vbus_rdev);
674 dev_err(bq2419x->dev,
675 "VBUS regulator register failed %d\n", ret);
676 goto scrub;
677 }
678
679 /* Disable the VBUS regulator and enable charging */
680 ret = bq2419x_charger_enable(bq2419x);
681 if (ret < 0) {
682 dev_err(bq2419x->dev, "Charging enable failed %d", ret);
683 goto scrub_reg;
684 }
685 return ret;
686
687scrub_reg:
688 regulator_unregister(bq2419x->vbus_rdev);
689 bq2419x->vbus_rdev = NULL;
690scrub:
691 if (gpio_is_valid(bq2419x->gpio_otg_iusb))
692 gpio_free(bq2419x->gpio_otg_iusb);
693 return ret;
694}
695
696static int bq2419x_psy_init(struct bq2419x_chip *bq2419x)
697{
698 int ret = 0;
699
700 bq2419x->ac_online = 0;
701 bq2419x->usb_online = 0;
702 bq2419x->status = 0;
703 if (bq2419x->use_mains) {
704 bq2419x->ac.name = "bq2419x-ac";
705 bq2419x->ac.type = POWER_SUPPLY_TYPE_MAINS;
706 bq2419x->ac.get_property = bq2419x_ac_get_property;
707 bq2419x->ac.properties = bq2419x_psy_props;
708 bq2419x->ac.num_properties = ARRAY_SIZE(bq2419x_psy_props);
709 ret = power_supply_register(bq2419x->dev, &bq2419x->ac);
710 if (ret < 0) {
711 dev_err(bq2419x->dev,
712 "AC power supply register failed %d\n", ret);
713 return ret;
714 }
715 }
716
717 if (bq2419x->use_usb) {
718 bq2419x->usb.name = "bq2419x-usb";
719 bq2419x->usb.type = POWER_SUPPLY_TYPE_USB;
720 bq2419x->usb.get_property = bq2419x_usb_get_property;
721 bq2419x->usb.properties = bq2419x_psy_props;
722 bq2419x->usb.num_properties = ARRAY_SIZE(bq2419x_psy_props);
723 ret = power_supply_register(bq2419x->dev, &bq2419x->usb);
724 if (ret) {
725 dev_err(bq2419x->dev,
726 "usb power supply register failed %d\n", ret);
727 goto scrub;
728 }
729 }
730 return ret;
731scrub:
732 if (bq2419x->use_mains)
733 power_supply_unregister(&bq2419x->ac);
734 return ret;
735}
736
737static int bq2419x_show_chip_version(struct bq2419x_chip *bq2419x)
738{
739 int ret;
740 unsigned int val;
741
742 ret = regmap_read(bq2419x->regmap, BQ2419X_REVISION_REG, &val);
743 if (ret < 0) {
744 dev_err(bq2419x->dev, "REVISION_REG read failed: %d\n", ret);
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500745 return ret;
746 }
747
748 if ((val & BQ24190_IC_VER) == BQ24190_IC_VER)
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530749 dev_info(bq2419x->dev, "chip type BQ24190 detected\n");
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530750 else if ((val & BQ24192_IC_VER) == BQ24192_IC_VER)
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530751 dev_info(bq2419x->dev, "chip type BQ2419X/3 detected\n");
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530752 else if ((val & BQ24192i_IC_VER) == BQ24192i_IC_VER)
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530753 dev_info(bq2419x->dev, "chip type BQ2419Xi detected\n");
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500754 return 0;
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530755}
Syed Rafiuddin208dc892013-01-24 14:57:46 +0530756
Laxman Dewangan9148fd32013-03-20 17:19:35 +0530757static int bq2419x_wakealarm(struct bq2419x_chip *bq2419x, int time_sec)
Syed Rafiuddind4d862d2013-03-08 15:52:59 +0530758{
759 int ret;
760 unsigned long now;
761 struct rtc_wkalrm alm;
Laxman Dewangan9148fd32013-03-20 17:19:35 +0530762 int alarm_time = time_sec;
Syed Rafiuddind4d862d2013-03-08 15:52:59 +0530763
Hyongbin Kim05e8e682013-04-22 21:41:19 +0900764 if (!alarm_time)
765 return 0;
766
Syed Rafiuddind4d862d2013-03-08 15:52:59 +0530767 alm.enabled = true;
768 ret = rtc_read_time(bq2419x->rtc, &alm.time);
769 if (ret < 0) {
770 dev_err(bq2419x->dev, "RTC read time failed %d\n", ret);
771 return ret;
772 }
773 rtc_tm_to_time(&alm.time, &now);
774
Laxman Dewangan9148fd32013-03-20 17:19:35 +0530775 rtc_time_to_tm(now + alarm_time, &alm.time);
Syed Rafiuddind4d862d2013-03-08 15:52:59 +0530776 ret = rtc_set_alarm(bq2419x->rtc, &alm);
777 if (ret < 0) {
778 dev_err(bq2419x->dev, "RTC set alarm failed %d\n", ret);
779 alm.enabled = false;
780 return ret;
781 }
782 alm.enabled = false;
783 return 0;
784}
785
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530786static int __devinit bq2419x_probe(struct i2c_client *client,
787 const struct i2c_device_id *id)
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500788{
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530789 struct bq2419x_chip *bq2419x;
790 struct bq2419x_platform_data *pdata;
791 int ret = 0;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500792
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530793 pdata = client->dev.platform_data;
794 if (!pdata) {
795 dev_err(&client->dev, "No Platform data");
796 return -EINVAL;
797 }
798
799 bq2419x = devm_kzalloc(&client->dev, sizeof(*bq2419x), GFP_KERNEL);
800 if (!bq2419x) {
801 dev_err(&client->dev, "Memory allocation failed\n");
802 return -ENOMEM;
803 }
804
805 bq2419x->regmap = devm_regmap_init_i2c(client, &bq2419x_regmap_config);
806 if (IS_ERR(bq2419x->regmap)) {
807 ret = PTR_ERR(bq2419x->regmap);
808 dev_err(&client->dev, "regmap init failed with err %d\n", ret);
809 return ret;
810 }
811
812 bq2419x->dev = &client->dev;
Hyongbin Kim12e014c2013-04-09 17:28:27 +0900813
814 if (pdata->bcharger_pdata) {
815 bq2419x->use_usb = pdata->bcharger_pdata->use_usb;
816 bq2419x->use_mains = pdata->bcharger_pdata->use_mains;
817 bq2419x->update_status = pdata->bcharger_pdata->update_status;
818 bq2419x->rtc_alarm_time = pdata->bcharger_pdata->rtc_alarm_time;
819 bq2419x->wdt_time_sec = pdata->bcharger_pdata->wdt_timeout;
820 bq2419x->chg_restart_time =
821 pdata->bcharger_pdata->chg_restart_time;
822 bq2419x->chg_enable = true;
823 }
824
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530825 bq2419x->wdt_refresh_timeout = 25;
826 i2c_set_clientdata(client, bq2419x);
827 bq2419x->irq = client->irq;
Hyongbin Kim05e8e682013-04-22 21:41:19 +0900828
829 if (bq2419x->rtc_alarm_time)
830 bq2419x->rtc = alarmtimer_get_rtcdev();
831
Pradeep Goudagunta13f38432013-03-09 23:58:03 +0530832 mutex_init(&bq2419x->mutex);
Sang-Hun Lee7befcf92013-03-12 14:11:01 -0700833 bq2419x->suspended = 0;
Pradeep Goudagunta6bf95072013-03-19 18:07:43 +0530834 bq2419x->chg_restart_timeout = 0;
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530835
836 ret = bq2419x_show_chip_version(bq2419x);
837 if (ret < 0) {
838 dev_err(&client->dev, "version read failed %d\n", ret);
839 return ret;
840 }
841
842 ret = bq2419x_charger_init(bq2419x);
843 if (ret < 0) {
844 dev_err(bq2419x->dev, "Charger init failed: %d\n", ret);
845 return ret;
846 }
847
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530848 ret = bq2419x_init_charger_regulator(bq2419x, pdata);
849 if (ret < 0) {
850 dev_err(&client->dev,
851 "Charger regualtor init failed %d\n", ret);
852 return ret;
853 }
854
855 ret = bq2419x_psy_init(bq2419x);
856 if (ret < 0) {
857 dev_err(&client->dev,
858 "Charger power supply init failed %d\n", ret);
859 goto scrub_chg_reg;
860 }
861
862 ret = bq2419x_init_vbus_regulator(bq2419x, pdata);
863 if (ret < 0) {
864 dev_err(&client->dev,
865 "VBUS regualtor init failed %d\n", ret);
866 goto scrub_psy;
867 }
868
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530869 init_kthread_worker(&bq2419x->bq_kworker);
870 bq2419x->bq_kworker_task = kthread_run(kthread_worker_fn,
871 &bq2419x->bq_kworker,
872 dev_name(bq2419x->dev));
873 if (IS_ERR(bq2419x->bq_kworker_task)) {
874 ret = PTR_ERR(bq2419x->bq_kworker_task);
875 dev_err(&client->dev, "Kworker task creation failed %d\n", ret);
876 goto scrub_vbus_reg;
877 }
878
879 init_kthread_work(&bq2419x->bq_wdt_work, bq2419x_work_thread);
880 sched_setscheduler(bq2419x->bq_kworker_task,
881 SCHED_FIFO, &bq2419x_param);
882 queue_kthread_work(&bq2419x->bq_kworker, &bq2419x->bq_wdt_work);
883
Pradeep Goudagunta7b7ac8b2013-03-11 18:13:59 +0530884 ret = bq2419x_watchdog_init(bq2419x, bq2419x->wdt_time_sec, "PROBE");
Syed Rafiuddin8fa48bf2013-03-05 10:31:20 +0530885 if (ret < 0) {
886 dev_err(bq2419x->dev, "BQWDT init failed %d\n", ret);
887 return ret;
888 }
889
Pradeep Goudagunta7b7ac8b2013-03-11 18:13:59 +0530890 ret = bq2419x_fault_clear_sts(bq2419x);
Syed Rafiuddin337240d2013-03-08 20:50:35 +0530891 if (ret < 0) {
892 dev_err(bq2419x->dev, "fault clear status failed %d\n", ret);
893 return ret;
894 }
895
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530896 ret = request_threaded_irq(bq2419x->irq, NULL,
897 bq2419x_irq, IRQF_TRIGGER_FALLING,
898 dev_name(bq2419x->dev), bq2419x);
899 if (ret < 0) {
900 dev_err(bq2419x->dev, "request IRQ %d fail, err = %d\n",
901 bq2419x->irq, ret);
902 goto scrub_kthread;
903 }
904
905 /* enable charging */
906 ret = bq2419x_charger_enable(bq2419x);
907 if (ret < 0)
908 goto scrub_irq;
909
910 return 0;
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530911scrub_irq:
912 free_irq(bq2419x->irq, bq2419x);
913scrub_kthread:
914 bq2419x->stop_thread = true;
915 flush_kthread_worker(&bq2419x->bq_kworker);
916 kthread_stop(bq2419x->bq_kworker_task);
917scrub_vbus_reg:
918 regulator_unregister(bq2419x->vbus_rdev);
919scrub_psy:
920 if (bq2419x->use_usb)
921 power_supply_unregister(&bq2419x->usb);
922 if (bq2419x->use_mains)
923 power_supply_unregister(&bq2419x->ac);
924scrub_chg_reg:
925 regulator_unregister(bq2419x->chg_rdev);
Pradeep Goudagunta13f38432013-03-09 23:58:03 +0530926 mutex_destroy(&bq2419x->mutex);
Syed Rafiuddin6126b492013-04-03 14:06:34 +0530927 return ret;
928}
929
930static int __devexit bq2419x_remove(struct i2c_client *client)
931{
932 struct bq2419x_chip *bq2419x = i2c_get_clientdata(client);
933
934 free_irq(bq2419x->irq, bq2419x);
935 bq2419x->stop_thread = true;
936 flush_kthread_worker(&bq2419x->bq_kworker);
937 kthread_stop(bq2419x->bq_kworker_task);
938 regulator_unregister(bq2419x->vbus_rdev);
939 if (bq2419x->use_usb)
940 power_supply_unregister(&bq2419x->usb);
941 if (bq2419x->use_mains)
942 power_supply_unregister(&bq2419x->ac);
943 regulator_unregister(bq2419x->chg_rdev);
Pradeep Goudagunta13f38432013-03-09 23:58:03 +0530944 mutex_destroy(&bq2419x->mutex);
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500945 return 0;
946}
947
Syed Rafiuddind4d862d2013-03-08 15:52:59 +0530948static void bq2419x_shutdown(struct i2c_client *client)
949{
950 int ret = 0;
951 struct bq2419x_chip *bq2419x = i2c_get_clientdata(client);
Laxman Dewangan9148fd32013-03-20 17:19:35 +0530952 int alarm_time = bq2419x->rtc_alarm_time;
Syed Rafiuddind4d862d2013-03-08 15:52:59 +0530953
Anshul Jaina5f0d172013-03-11 15:05:52 -0700954 if (bq2419x->irq)
955 disable_irq(bq2419x->irq);
956
Hyongbin Kim05e8e682013-04-22 21:41:19 +0900957 if (alarm_time && !bq2419x->rtc)
Laxman Dewangan9148fd32013-03-20 17:19:35 +0530958 bq2419x->rtc = alarmtimer_get_rtcdev();
959
Hyongbin Kim05e8e682013-04-22 21:41:19 +0900960 if (alarm_time && (bq2419x->in_current_limit > 500)) {
Laxman Dewangan9148fd32013-03-20 17:19:35 +0530961 dev_info(bq2419x->dev, "HighCurrent %dmA charger is connectd\n",
962 bq2419x->in_current_limit);
963 ret = bq2419x_reset_wdt(bq2419x, "shutdown");
964 if (ret < 0)
965 dev_err(bq2419x->dev,
966 "bq2419x_reset_wdt failed: %d\n", ret);
967 alarm_time = 20;
968 }
969
Sang-Hun Lee7befcf92013-03-12 14:11:01 -0700970 mutex_lock(&bq2419x->mutex);
971 bq2419x->suspended = 1;
972 mutex_unlock(&bq2419x->mutex);
973
Syed Rafiuddind4d862d2013-03-08 15:52:59 +0530974 ret = bq2419x_charger_enable(bq2419x);
975 if (ret < 0)
976 dev_err(bq2419x->dev, "Charger enable failed %d", ret);
977
Hyongbin Kim05e8e682013-04-22 21:41:19 +0900978 if (alarm_time && (bq2419x->in_current_limit <= 500)) {
Laxman Dewangan9148fd32013-03-20 17:19:35 +0530979 /* Configure charging current to 500mA */
980 ret = regmap_write(bq2419x->regmap,
981 BQ2419X_INPUT_SRC_REG, 0x32);
982 if (ret < 0)
983 dev_err(bq2419x->dev,
984 "INPUT_SRC_REG write failed %d\n", ret);
985 }
Syed Rafiuddind4d862d2013-03-08 15:52:59 +0530986
Laxman Dewangan9148fd32013-03-20 17:19:35 +0530987 ret = bq2419x_wakealarm(bq2419x, alarm_time);
Syed Rafiuddind4d862d2013-03-08 15:52:59 +0530988 if (ret < 0)
989 dev_err(bq2419x->dev, "RTC wake alarm config failed %d\n", ret);
990}
991
Pradeep Goudagunta7b7ac8b2013-03-11 18:13:59 +0530992#ifdef CONFIG_PM_SLEEP
993static int bq2419x_suspend(struct device *dev)
994{
995 int ret = 0;
996 struct bq2419x_chip *bq2419x = dev_get_drvdata(dev);
997
Sang-Hun Lee7befcf92013-03-12 14:11:01 -0700998 mutex_lock(&bq2419x->mutex);
999 bq2419x->suspended = 1;
1000 mutex_unlock(&bq2419x->mutex);
Pradeep Goudagunta7b7ac8b2013-03-11 18:13:59 +05301001 disable_irq(bq2419x->irq);
1002 ret = bq2419x_charger_enable(bq2419x);
Pradeep Goudagunta803ab952013-03-12 14:44:32 +05301003 if (ret < 0) {
Pradeep Goudagunta7b7ac8b2013-03-11 18:13:59 +05301004 dev_err(bq2419x->dev, "Charger enable failed %d", ret);
Pradeep Goudagunta803ab952013-03-12 14:44:32 +05301005 return ret;
1006 }
Pradeep Goudagunta7b7ac8b2013-03-11 18:13:59 +05301007
1008 /* Configure charging current to 500mA */
1009 ret = regmap_write(bq2419x->regmap, BQ2419X_INPUT_SRC_REG, 0x32);
Pradeep Goudagunta803ab952013-03-12 14:44:32 +05301010 if (ret < 0) {
Pradeep Goudagunta7b7ac8b2013-03-11 18:13:59 +05301011 dev_err(bq2419x->dev, "INPUT_SRC_REG write failed %d\n", ret);
Pradeep Goudagunta803ab952013-03-12 14:44:32 +05301012 return ret;
1013 }
Pradeep Goudagunta7b7ac8b2013-03-11 18:13:59 +05301014
1015 return 0;
1016}
1017
1018static int bq2419x_resume(struct device *dev)
1019{
1020 int ret = 0;
1021 struct bq2419x_chip *bq2419x = dev_get_drvdata(dev);
1022 unsigned int val;
1023
1024 ret = regmap_read(bq2419x->regmap, BQ2419X_FAULT_REG, &val);
1025 if (ret < 0) {
1026 dev_err(bq2419x->dev, "FAULT_REG read failed %d\n", ret);
1027 return ret;
1028 }
1029
1030 if (val & BQ2419x_FAULT_WATCHDOG_FAULT) {
1031 dev_err(bq2419x->dev,
1032 "Charging Fault: Watchdog Timer Expired\n");
1033
1034 ret = bq2419x_watchdog_init(bq2419x, bq2419x->wdt_time_sec,
1035 "RESUME");
1036 if (ret < 0) {
1037 dev_err(bq2419x->dev, "BQWDT init failed %d\n", ret);
1038 return ret;
1039 }
1040 }
1041
1042 ret = bq2419x_fault_clear_sts(bq2419x);
1043 if (ret < 0) {
1044 dev_err(bq2419x->dev, "fault clear status failed %d\n", ret);
1045 return ret;
1046 }
1047
1048 ret = bq2419x_charger_init(bq2419x);
1049 if (ret < 0) {
1050 dev_err(bq2419x->dev, "Charger init failed: %d\n", ret);
1051 return ret;
1052 }
1053
1054 ret = bq2419x_init(bq2419x);
1055 if (ret < 0) {
1056 dev_err(bq2419x->dev, "bq2419x init failed: %d\n", ret);
1057 return ret;
1058 }
1059
Sang-Hun Lee7befcf92013-03-12 14:11:01 -07001060 mutex_lock(&bq2419x->mutex);
1061 bq2419x->suspended = 0;
1062 mutex_unlock(&bq2419x->mutex);
Pradeep Goudagunta9e75c232013-03-12 16:29:03 +05301063 if (gpio_is_valid(bq2419x->gpio_otg_iusb))
1064 gpio_set_value(bq2419x->gpio_otg_iusb, 1);
1065
Pradeep Goudagunta7b7ac8b2013-03-11 18:13:59 +05301066 enable_irq(bq2419x->irq);
1067 return 0;
1068};
1069#endif
1070
1071static const struct dev_pm_ops bq2419x_pm_ops = {
1072 SET_SYSTEM_SLEEP_PM_OPS(bq2419x_suspend,
1073 bq2419x_resume)
1074};
1075
Syed Rafiuddin6126b492013-04-03 14:06:34 +05301076static const struct i2c_device_id bq2419x_id[] = {
1077 {.name = "bq2419x",},
1078 {},
1079};
1080MODULE_DEVICE_TABLE(i2c, bq2419x_id);
1081
1082static struct i2c_driver bq2419x_i2c_driver = {
Laxman Dewanganf27ed352012-11-29 19:33:02 -05001083 .driver = {
Syed Rafiuddind4d862d2013-03-08 15:52:59 +05301084 .name = "bq2419x",
1085 .owner = THIS_MODULE,
Pradeep Goudagunta7b7ac8b2013-03-11 18:13:59 +05301086 .pm = &bq2419x_pm_ops,
Laxman Dewanganf27ed352012-11-29 19:33:02 -05001087 },
Syed Rafiuddind4d862d2013-03-08 15:52:59 +05301088 .probe = bq2419x_probe,
1089 .remove = __devexit_p(bq2419x_remove),
1090 .shutdown = bq2419x_shutdown,
1091 .id_table = bq2419x_id,
Laxman Dewanganf27ed352012-11-29 19:33:02 -05001092};
1093
Syed Rafiuddin6126b492013-04-03 14:06:34 +05301094static int __init bq2419x_module_init(void)
Syed Rafiuddinee69db32013-01-09 15:40:55 +05301095{
Syed Rafiuddin6126b492013-04-03 14:06:34 +05301096 return i2c_add_driver(&bq2419x_i2c_driver);
Syed Rafiuddinee69db32013-01-09 15:40:55 +05301097}
Syed Rafiuddin6126b492013-04-03 14:06:34 +05301098subsys_initcall(bq2419x_module_init);
Syed Rafiuddinee69db32013-01-09 15:40:55 +05301099
Syed Rafiuddin6126b492013-04-03 14:06:34 +05301100static void __exit bq2419x_cleanup(void)
Syed Rafiuddinee69db32013-01-09 15:40:55 +05301101{
Syed Rafiuddin6126b492013-04-03 14:06:34 +05301102 i2c_del_driver(&bq2419x_i2c_driver);
Syed Rafiuddinee69db32013-01-09 15:40:55 +05301103}
Syed Rafiuddin6126b492013-04-03 14:06:34 +05301104module_exit(bq2419x_cleanup);
Laxman Dewanganf27ed352012-11-29 19:33:02 -05001105
Syed Rafiuddin6126b492013-04-03 14:06:34 +05301106MODULE_DESCRIPTION("BQ24190/BQ24192/BQ24192i/BQ24193 battery charger driver");
Laxman Dewanganf27ed352012-11-29 19:33:02 -05001107MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
Syed Rafiuddin6126b492013-04-03 14:06:34 +05301108MODULE_AUTHOR("Syed Rafiuddin <srafiuddin@nvidia.com");
Laxman Dewanganf27ed352012-11-29 19:33:02 -05001109MODULE_LICENSE("GPL v2");