blob: 8c54d57227280eff837abe45728eb4ed38349a48 [file] [log] [blame]
Laxman Dewanganf27ed352012-11-29 19:33:02 -05001/*
2 * bq2419x-charger.c - Battery charger driver
3 *
4 * Copyright (c) 2012, NVIDIA Corporation.
5 *
6 * Author: Laxman Dewangan <ldewangan@nvidia.com>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation version 2.
11 *
12 * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind,
13 * whether express or implied; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
20 * 02111-1307, USA
21 */
22#include <linux/delay.h>
23#include <linux/err.h>
24#include <linux/i2c.h>
25#include <linux/init.h>
26#include <linux/interrupt.h>
27#include <linux/gpio.h>
28#include <linux/kernel.h>
29#include <linux/module.h>
30#include <linux/mfd/bq2419x.h>
31#include <linux/regmap.h>
32#include <linux/platform_device.h>
33#include <linux/power_supply.h>
34#include <linux/slab.h>
Syed Rafiuddinee69db32013-01-09 15:40:55 +053035#include <linux/regulator/driver.h>
36#include <linux/regulator/machine.h>
Laxman Dewanganf27ed352012-11-29 19:33:02 -050037
Syed Rafiuddinee69db32013-01-09 15:40:55 +053038struct bq2419x_charger *bq_charger;
Laxman Dewanganf27ed352012-11-29 19:33:02 -050039/* input current limit */
40static const unsigned int iinlim[] = {
41 100, 150, 500, 900, 1200, 1500, 2000, 3000,
42};
43
44struct bq2419x_charger {
45 struct device *dev;
46 struct bq2419x_chip *chip;
47 struct power_supply ac;
48 struct power_supply usb;
49 int ac_online;
50 int usb_online;
51 int gpio_status;
52 int gpio_interrupt;
53 int usb_in_current_limit;
54 int ac_in_current_limit;
55 unsigned use_mains:1;
56 unsigned use_usb:1;
57 int irq;
Syed Rafiuddin5ea55fb2012-12-12 20:58:29 +053058 int status;
59 void (*update_status)(int, int);
Syed Rafiuddinee69db32013-01-09 15:40:55 +053060 struct regulator_dev *rdev;
61 struct regulator_desc reg_desc;
62 struct regulator_init_data reg_init_data;
Laxman Dewanganf27ed352012-11-29 19:33:02 -050063};
64
65static enum power_supply_property bq2419x_psy_props[] = {
66 POWER_SUPPLY_PROP_ONLINE,
67};
68
69static int current_to_reg(const unsigned int *tbl,
70 size_t size, unsigned int val)
71{
72 size_t i;
73
74 for (i = 0; i < size; i++)
75 if (val < tbl[i])
76 break;
77 return i > 0 ? i - 1 : -EINVAL;
78}
79
80static int bq2419x_charger_enable(struct bq2419x_charger *charger)
81{
82 int ret;
83
84 dev_info(charger->dev, "Charging enabled\n");
85 ret = regmap_update_bits(charger->chip->regmap, BQ2419X_PWR_ON_REG,
86 ENABLE_CHARGE_MASK, ENABLE_CHARGE);
87 if (ret < 0)
88 dev_err(charger->dev, "register update failed, err %d\n", ret);
89 return ret;
90}
91
92static int bq2419x_charger_disable(struct bq2419x_charger *charger)
93{
94 int ret;
95
96 dev_info(charger->dev, "Charging disabled\n");
97 ret = regmap_update_bits(charger->chip->regmap, BQ2419X_PWR_ON_REG,
98 ENABLE_CHARGE_MASK, 0);
99 if (ret < 0)
100 dev_err(charger->dev, "register update failed, err %d\n", ret);
101 return ret;
102}
103
104static int bq2419x_ac_get_property(struct power_supply *psy,
105 enum power_supply_property psp, union power_supply_propval *val)
106{
107 struct bq2419x_charger *chip;
108
109 chip = container_of(psy, struct bq2419x_charger, ac);
110 if (psp == POWER_SUPPLY_PROP_ONLINE)
111 val->intval = chip->ac_online;
112 else
113 return -EINVAL;
114 return 0;
115}
116
117static int bq2419x_usb_get_property(struct power_supply *psy,
118 enum power_supply_property psp,
119 union power_supply_propval *val)
120{
121 struct bq2419x_charger *chip;
122
123 chip = container_of(psy, struct bq2419x_charger, usb);
124 if (psp == POWER_SUPPLY_PROP_ONLINE)
125 val->intval = chip->usb_online;
126 else
127 return -EINVAL;
128 return 0;
129}
130
131static int bq2419x_init(struct bq2419x_charger *charger)
132{
133 int val, ret = 0;
134
135 if (charger->usb_online) {
136 val = current_to_reg(iinlim, ARRAY_SIZE(iinlim),
137 charger->usb_in_current_limit);
138 if (val < 0)
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530139 val = 0;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500140
141 ret = regmap_write(charger->chip->regmap,
142 BQ2419X_INPUT_SRC_REG, val);
143 if (ret < 0)
144 dev_err(charger->dev, "error reading reg: 0x%x\n",
145 BQ2419X_INPUT_SRC_REG);
146 }
147
148 if (charger->ac_online) {
149 val = current_to_reg(iinlim, ARRAY_SIZE(iinlim),
150 charger->ac_in_current_limit);
151 if (val < 0)
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530152 val = 0;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500153
154 ret = regmap_write(charger->chip->regmap,
155 BQ2419X_INPUT_SRC_REG, val);
156 if (ret < 0)
157 dev_err(charger->dev, "error reading reg: 0x%x\n",
158 BQ2419X_INPUT_SRC_REG);
159 }
160 return ret;
161}
162
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530163static int bq2419x_get_status(struct bq2419x_charger *charger,
164 int min_uA, int max_uA)
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500165{
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530166 int ret = 0;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500167 u32 val;
168
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500169 ret = regmap_read(charger->chip->regmap, BQ2419X_SYS_STAT_REG, &val);
170 if (ret < 0)
171 dev_err(charger->dev, "error reading reg: 0x%x\n",
172 BQ2419X_SYS_STAT_REG);
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530173
174 if (max_uA == 0 && val != 0)
175 return 0;
176
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500177 if ((val & 0xc0) == 0) {
178 /* disable charging */
179 ret = bq2419x_charger_disable(charger);
180 if (ret < 0)
181 goto error;
Syed Rafiuddin5ea55fb2012-12-12 20:58:29 +0530182 charger->status = 0;
183 if (charger->update_status)
184 charger->update_status
185 (charger->status, 0);
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500186 } else if ((val & 0xc0) == 0x40) {
Syed Rafiuddin5ea55fb2012-12-12 20:58:29 +0530187 charger->status = 1;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500188 charger->usb_online = 1;
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530189 charger->usb_in_current_limit = max_uA;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500190 ret = bq2419x_init(charger);
191 if (ret < 0)
192 goto error;
193 /* enable charging */
194 ret = bq2419x_charger_enable(charger);
195 if (ret < 0)
196 goto error;
Syed Rafiuddin5ea55fb2012-12-12 20:58:29 +0530197 if (charger->update_status)
198 charger->update_status
199 (charger->status, 2);
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500200 } else if ((val & 0xc0) == 0x80) {
Syed Rafiuddin5ea55fb2012-12-12 20:58:29 +0530201 charger->status = 1;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500202 charger->ac_online = 1;
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530203 charger->ac_in_current_limit = max_uA;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500204 ret = bq2419x_init(charger);
205 if (ret < 0)
206 goto error;
207 /* enable charging */
208 ret = bq2419x_charger_enable(charger);
209 if (ret < 0)
210 goto error;
Syed Rafiuddin5ea55fb2012-12-12 20:58:29 +0530211 if (charger->update_status)
212 charger->update_status
213 (charger->status, 1);
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500214 }
215 return 0;
216error:
217 dev_err(charger->dev, "Charger get status failed, err = %d\n", ret);
218 return ret;
219}
220
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530221static int bq2419x_enable_charger(struct regulator_dev *rdev,
222 int min_uA, int max_uA)
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500223{
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530224 int ret = 0;
225 int val;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500226
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530227 msleep(200);
228 bq_charger->usb_online = 0;
229 bq_charger->ac_online = 0;
230 bq_charger->status = 0;
231
232 ret = regmap_read(bq_charger->chip->regmap, BQ2419X_SYS_STAT_REG, &val);
233 if (ret < 0)
234 dev_err(bq_charger->dev, "error reading reg: 0x%x\n",
235 BQ2419X_SYS_STAT_REG);
236
237 if (max_uA == 0 && val != 0)
238 return ret;
239
240 ret = bq2419x_get_status(bq_charger, min_uA, max_uA/1000);
241 if (ret == 0) {
242 if (bq_charger->use_mains)
243 power_supply_changed(&bq_charger->ac);
244 if (bq_charger->use_usb)
245 power_supply_changed(&bq_charger->usb);
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500246 }
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530247 return ret;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500248}
249
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530250static struct regulator_ops bq2419x_tegra_regulator_ops = {
251 .set_current_limit = bq2419x_enable_charger,
252};
253
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500254static int __devinit bq2419x_charger_probe(struct platform_device *pdev)
255{
256 struct bq2419x_platform_data *chip_pdata;
257 struct bq2419x_charger_platform_data *bcharger_pdata = NULL;
258 struct bq2419x_charger *charger;
259 int ret;
260 u32 val;
261
262 chip_pdata = dev_get_platdata(pdev->dev.parent);
263 if (chip_pdata)
264 bcharger_pdata = chip_pdata->bcharger_pdata;
265
266 if (!bcharger_pdata) {
267 dev_err(&pdev->dev, "No Platform data");
268 return -EIO;
269 }
270
271 charger = devm_kzalloc(&pdev->dev, sizeof(*charger), GFP_KERNEL);
272 if (!charger) {
273 dev_err(&pdev->dev, "Memory alloc failed\n");
274 return -ENOMEM;
275 }
276
277 charger->chip = dev_get_drvdata(pdev->dev.parent);
278 charger->dev = &pdev->dev;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500279 charger->use_usb = bcharger_pdata->use_usb;
280 charger->use_mains = bcharger_pdata->use_mains;
281 charger->gpio_status = bcharger_pdata->gpio_status;
282 charger->gpio_interrupt = bcharger_pdata->gpio_interrupt;
Syed Rafiuddin5ea55fb2012-12-12 20:58:29 +0530283 charger->update_status = bcharger_pdata->update_status;
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530284 bq_charger = charger;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500285 platform_set_drvdata(pdev, charger);
286
287 ret = regmap_read(charger->chip->regmap, BQ2419X_REVISION_REG, &val);
288 if (ret < 0) {
289 dev_err(charger->dev, "error reading reg: 0x%x, err = %d\n",
290 BQ2419X_REVISION_REG, ret);
291 return ret;
292 }
293
294 if ((val & BQ24190_IC_VER) == BQ24190_IC_VER)
295 dev_info(charger->dev, "chip type BQ24190 detected\n");
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530296 else if ((val & BQ24192_IC_VER) == BQ24192_IC_VER)
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500297 dev_info(charger->dev, "chip type BQ2419X/3 detected\n");
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530298 else if ((val & BQ24192i_IC_VER) == BQ24192i_IC_VER)
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500299 dev_info(charger->dev, "chip type BQ2419Xi detected\n");
300
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500301 charger->gpio_status = bcharger_pdata->gpio_status;
302 charger->gpio_interrupt = bcharger_pdata->gpio_interrupt;
303
304 if (gpio_is_valid(charger->gpio_status)) {
305 ret = gpio_request_one(charger->gpio_status,
306 GPIOF_IN, "bq2419x-status");
307 if (ret < 0) {
308 dev_err(charger->dev,
309 "status gpio request failed, err = %d\n", ret);
310 return ret;
311 }
312 }
313
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530314 charger->reg_desc.name = "vbus_charger";
315 charger->reg_desc.ops = &bq2419x_tegra_regulator_ops;
316 charger->reg_desc.type = REGULATOR_CURRENT;
317 charger->reg_desc.id = bcharger_pdata->regulator_id;
318 charger->reg_desc.type = REGULATOR_CURRENT;
319 charger->reg_desc.owner = THIS_MODULE;
320
321 charger->reg_init_data.supply_regulator = NULL;
322 charger->reg_init_data.num_consumer_supplies =
323 bcharger_pdata->num_consumer_supplies;
324
325 charger->reg_init_data.regulator_init = NULL;
326 charger->reg_init_data.consumer_supplies =
327 bcharger_pdata->consumer_supplies;
328 charger->reg_init_data.driver_data = charger;
329 charger->reg_init_data.constraints.name = "vbus_charger";
330 charger->reg_init_data.constraints.min_uA = 0;
331 charger->reg_init_data.constraints.max_uA =
332 bcharger_pdata->max_charge_current_mA * 1000;
333
334 charger->reg_init_data.constraints.valid_modes_mask =
335 REGULATOR_MODE_NORMAL |
336 REGULATOR_MODE_STANDBY;
337
338 charger->reg_init_data.constraints.valid_ops_mask =
339 REGULATOR_CHANGE_MODE |
340 REGULATOR_CHANGE_STATUS |
341 REGULATOR_CHANGE_CURRENT;
342
343 charger->rdev = regulator_register(&charger->reg_desc, charger->dev,
344 &charger->reg_init_data, charger, NULL);
345
346 if (IS_ERR(charger->rdev)) {
347 dev_err(&pdev->dev, "failed to register %s\n",
348 charger->reg_desc.name);
349 ret = PTR_ERR(charger->rdev);
350 goto free_gpio_status;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500351 }
352
353 charger->ac_online = 0;
354 charger->usb_online = 0;
Syed Rafiuddin5ea55fb2012-12-12 20:58:29 +0530355 charger->status = 0;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500356 if (charger->use_mains) {
357 charger->ac.name = "bq2419x-ac";
358 charger->ac.type = POWER_SUPPLY_TYPE_MAINS;
359 charger->ac.get_property = bq2419x_ac_get_property;
360 charger->ac.properties = bq2419x_psy_props;
361 charger->ac.num_properties = ARRAY_SIZE(bq2419x_psy_props);
362 ret = power_supply_register(&pdev->dev, &charger->ac);
363 if (ret) {
364 dev_err(&pdev->dev, "failed: power supply register\n");
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530365 return ret;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500366 }
367 }
368
369 if (charger->use_usb) {
370 charger->usb.name = "bq2419x-usb";
371 charger->usb.type = POWER_SUPPLY_TYPE_USB;
372 charger->usb.get_property = bq2419x_usb_get_property;
373 charger->usb.properties = bq2419x_psy_props;
374 charger->usb.num_properties = ARRAY_SIZE(bq2419x_psy_props);
375 ret = power_supply_register(&pdev->dev, &charger->usb);
376 if (ret) {
377 dev_err(&pdev->dev, "failed: power supply register\n");
378 goto psy_error;
379 }
380 }
381
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500382 return 0;
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500383psy_error:
384 power_supply_unregister(&charger->ac);
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500385free_gpio_status:
386 if (gpio_is_valid(charger->gpio_status))
387 gpio_free(charger->gpio_status);
388
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500389 return ret;
390}
391
392static int __devexit bq2419x_charger_remove(struct platform_device *pdev)
393{
394 struct bq2419x_charger *charger = platform_get_drvdata(pdev);
395
396 free_irq(charger->irq, charger);
397 if (charger->use_mains)
398 power_supply_unregister(&charger->usb);
399 if (charger->use_usb)
400 power_supply_unregister(&charger->ac);
401 if (gpio_is_valid(charger->gpio_status))
402 gpio_free(charger->gpio_status);
403 if (gpio_is_valid(charger->gpio_interrupt))
404 gpio_free(charger->gpio_interrupt);
405 return 0;
406}
407
408static struct platform_driver bq2419x_charger_driver = {
409 .driver = {
410 .name = "bq2419x-battery-charger",
411 .owner = THIS_MODULE,
412 },
413 .probe = bq2419x_charger_probe,
414 .remove = __devexit_p(bq2419x_charger_probe),
415};
416
Syed Rafiuddinee69db32013-01-09 15:40:55 +0530417static int __init bq2419x_charger_init(void)
418{
419 return platform_driver_register(&bq2419x_charger_driver);
420}
421subsys_initcall(bq2419x_charger_init);
422
423static void __exit bq2419x_charger_cleanup(void)
424{
425 platform_driver_unregister(&bq2419x_charger_driver);
426}
427module_exit(bq2419x_charger_cleanup);
Laxman Dewanganf27ed352012-11-29 19:33:02 -0500428
429MODULE_DESCRIPTION("bq2419x battery charger driver");
430MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
431MODULE_LICENSE("GPL v2");