3 * MAXIM 8922 charger interface driver
5 * Copyright (C) 2011 Samsung Electronics
7 * <ms925.kim@samsung.com>
9 * This program is not provided / owned by Maxim Integrated Products.
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
16 #include <linux/module.h>
17 #include <linux/init.h>
18 #include <linux/platform_device.h>
19 #include <linux/err.h>
20 #include <linux/slab.h>
21 #include <linux/interrupt.h>
22 #include <linux/irq.h>
23 #include <linux/gpio.h>
24 #include <linux/delay.h>
25 #include <linux/power_supply.h>
26 #include <linux/max8922-charger.h>
27 #include <linux/regulator/driver.h>
28 #include <linux/notifier.h>
29 #include <linux/switch.h>
30 #include <plat/gpio-core.h>
32 #define MAX8922_USB_MAX_CURRENT 450000
33 #define MAX8922_TA_TYP_CURRENT 650000
35 enum max8922_cable_type {
43 struct power_supply psy_bat;
44 struct max8922_platform_data *pdata;
45 struct regulator_dev *rdev;
51 struct switch_dev *switch_dev;
52 struct notifier_block nb;
53 int event_code_ta, event_code_usb;
56 static enum power_supply_property max8922_battery_props[] = {
57 POWER_SUPPLY_PROP_STATUS,
58 POWER_SUPPLY_PROP_ONLINE,
61 static inline int max8922_is_charging(struct max8922_info *info)
63 int ta_nconnected = gpio_get_value(info->pdata->gpio_ta_nconnected);
64 int chg_ing = gpio_get_value(info->pdata->gpio_chg_ing);
66 dev_info(info->dev, "%s: charging state = 0x%x\n", __func__,
67 (ta_nconnected << 1) | chg_ing) ;
69 return (ta_nconnected << 1) | chg_ing;
72 static int max8922_get_property(struct power_supply *psy,
73 enum power_supply_property psp,
74 union power_supply_propval *val)
76 struct max8922_info *info =
77 container_of(psy, struct max8922_info, psy_bat);
79 int ta_nconnected = 0;
82 case POWER_SUPPLY_PROP_STATUS:
83 switch (max8922_is_charging(info)) {
85 val->intval = POWER_SUPPLY_STATUS_CHARGING;
88 val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
91 val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
95 case POWER_SUPPLY_PROP_ONLINE:
96 ta_nconnected = gpio_get_value(info->pdata->gpio_ta_nconnected);
97 val->intval = !ta_nconnected;
105 static int max8922_enable_charging(struct max8922_info *info, bool enable,
108 int gpio_chg_en = info->pdata->gpio_chg_en;
111 dev_info(info->dev, "%s: %s charging,%s\n", __func__,
112 enable ? "enable" : "disable",
113 cable_type == CABLE_USB ? "USB" : "TA");
115 if (info->is_enabled == enable && info->cable_type == cable_type)
119 info->is_enabled = true;
120 if (cable_type == CABLE_USB ||
121 (cable_type == CABLE_TA &&
122 info->max_uA <= MAX8922_USB_MAX_CURRENT)) {
123 /* Charging by USB cable */
124 gpio_set_value(gpio_chg_en, GPIO_LEVEL_HIGH);
125 } else if (cable_type == CABLE_TA &&
126 info->max_uA > MAX8922_USB_MAX_CURRENT) {
127 /* Charging by TA cable */
128 gpio_set_value(gpio_chg_en, GPIO_LEVEL_HIGH);
131 local_irq_save(flags);
132 gpio_set_value(gpio_chg_en, GPIO_LEVEL_LOW);
134 gpio_set_value(gpio_chg_en, GPIO_LEVEL_HIGH);
135 local_irq_restore(flags);
137 info->is_enabled = false;
140 gpio_set_value(gpio_chg_en, GPIO_LEVEL_LOW);
141 info->is_enabled = false;
146 return max8922_is_charging(info);
149 static int max8922_set_property(struct power_supply *psy,
150 enum power_supply_property psp,
151 const union power_supply_propval *val)
153 struct max8922_info *info =
154 container_of(psy, struct max8922_info, psy_bat);
158 case POWER_SUPPLY_PROP_CURRENT_NOW: /* Set charging current */
159 info->cable_type = val->intval > 450 ? CABLE_TA : CABLE_USB;
161 case POWER_SUPPLY_PROP_STATUS: /* Enable/Disable charging */
162 enable = (val->intval == POWER_SUPPLY_STATUS_CHARGING);
163 max8922_enable_charging(info, enable, info->cable_type);
171 static irqreturn_t max8922_chg_ing_irq(int irq, void *data)
173 struct max8922_info *info = data;
176 dev_info(info->dev, "chg_ing IRQ occurred!\n");
178 if (gpio_get_value(info->pdata->gpio_ta_nconnected))
181 if (info->pdata->topoff_cb)
182 ret = info->pdata->topoff_cb();
185 dev_err(info->dev, "%s: error from topoff_cb(%d)\n", __func__,
193 static int max8922_is_enabled(struct regulator_dev *rdev)
195 struct max8922_info *max8922 = rdev_get_drvdata(rdev);
197 return max8922->is_enabled;
200 static int max8922_enable(struct regulator_dev *rdev)
202 struct max8922_info *max8922 = rdev_get_drvdata(rdev);
204 max8922_enable_charging(max8922, true, max8922->cable_type);
209 static int max8922_disable(struct regulator_dev *rdev)
211 struct max8922_info *max8922 = rdev_get_drvdata(rdev);
213 max8922_enable_charging(max8922, false, max8922->cable_type);
218 static int max8922_get_current_limit(struct regulator_dev *rdev)
220 struct max8922_info *max8922 = rdev_get_drvdata(rdev);
222 return max8922->max_uA;
225 static int max8922_set_current_limit(struct regulator_dev *rdev,
226 int min_uA, int max_uA)
228 struct max8922_info *max8922 = rdev_get_drvdata(rdev);
230 if (max8922->max_uA != max_uA) {
231 max8922->max_uA = max_uA;
233 /* Reset charger if it was already enabled */
234 if (max8922->is_enabled) {
235 max8922_enable_charging(max8922, false,
236 max8922->cable_type);
237 max8922_enable_charging(max8922, true,
238 max8922->cable_type);
245 static struct regulator_ops max8922_ops = {
246 .is_enabled = max8922_is_enabled,
247 .enable = max8922_enable,
248 .disable = max8922_disable,
249 .get_current_limit = max8922_get_current_limit,
250 .set_current_limit = max8922_set_current_limit,
253 static struct regulator_desc regulator = {
254 .name = "MAX8922_CHARGER",
257 .type = REGULATOR_CURRENT,
258 .owner = THIS_MODULE,
261 static int max8922_charger_notifier(struct notifier_block *self,
262 unsigned long event, void *ptr)
264 struct max8922_info *info =
265 container_of(self, struct max8922_info, nb);
266 bool charger_enable = false;
267 int cable_type = CABLE_NONE;
269 if (info->event_code_ta > 0) {
270 if (event & info->event_code_ta) {
271 cable_type = CABLE_TA;
272 info->max_uA = MAX8922_TA_TYP_CURRENT;
273 charger_enable = true;
277 if (info->event_code_usb > 0) {
278 if (event & info->event_code_usb) {
279 cable_type = CABLE_USB;
280 info->max_uA = MAX8922_USB_MAX_CURRENT;
281 charger_enable = true;
285 max8922_enable_charging(info, charger_enable, cable_type);
287 info->cable_type = cable_type;
292 static struct notifier_block max8922_charger_nb = {
293 .notifier_call = max8922_charger_notifier,
296 static __devinit int max8922_probe(struct platform_device *pdev)
298 struct max8922_platform_data *pdata = dev_get_platdata(&pdev->dev);
299 struct max8922_info *info;
302 dev_info(&pdev->dev, "%s : MAX8922 Charger Driver Loading\n", __func__);
304 info = kzalloc(sizeof(*info), GFP_KERNEL);
308 platform_set_drvdata(pdev, info);
310 info->dev = &pdev->dev;
313 info->psy_bat.name = "max8922-charger",
314 info->psy_bat.type = POWER_SUPPLY_TYPE_BATTERY,
315 info->psy_bat.properties = max8922_battery_props,
316 info->psy_bat.num_properties = ARRAY_SIZE(max8922_battery_props),
317 info->psy_bat.get_property = max8922_get_property,
318 info->psy_bat.set_property = max8922_set_property,
319 ret = power_supply_register(&pdev->dev, &info->psy_bat);
321 dev_err(info->dev, "Failed to register psy_bat\n");
325 if (pdata->cfg_gpio) {
326 ret = pdata->cfg_gpio();
328 dev_err(info->dev, "failed to configure GPIO\n");
333 if (gpio_is_valid(pdata->gpio_chg_en)) {
334 if (!pdata->gpio_chg_en) {
335 dev_err(info->dev, "gpio_chg_en defined as 0\n");
336 WARN_ON(!pdata->gpio_chg_en);
340 gpio_request(pdata->gpio_chg_en, "MAX8922 CHG_EN");
343 if (gpio_is_valid(pdata->gpio_chg_ing)) {
344 if (!pdata->gpio_chg_ing) {
345 dev_err(info->dev, "gpio_chg_ing defined as 0\n");
346 WARN_ON(!pdata->gpio_chg_ing);
350 gpio_request(pdata->gpio_chg_ing, "MAX8922 CHG_ING");
353 if (gpio_is_valid(pdata->gpio_ta_nconnected)) {
354 if (!pdata->gpio_ta_nconnected) {
355 dev_err(info->dev, "gpio_ta_nconnected defined as 0\n");
356 WARN_ON(!pdata->gpio_ta_nconnected);
360 gpio_request(pdata->gpio_ta_nconnected,
361 "MAX8922 TA_nCONNECTED");
364 info->rdev = regulator_register(®ulator, &pdev->dev,
365 pdata->init_data, info);
367 if (IS_ERR(info->rdev)) {
368 ret = PTR_ERR(info->rdev);
369 dev_err(&pdev->dev, "regulator init failed (%d)\n", ret);
373 info->nb = max8922_charger_nb;
374 info->switch_dev = switch_get_switch_dev("switch-usb");
375 if (info->switch_dev) {
376 switch_register_notifier(info->switch_dev, &info->nb);
377 info->event_code_ta =
378 switch_get_event_code(info->switch_dev, "TA");
379 info->event_code_usb =
380 switch_get_event_code(info->switch_dev, "USB");
383 info->cable_type = CABLE_NONE;
387 power_supply_unregister(&info->psy_bat);
388 platform_set_drvdata(pdev, NULL);
393 static int __devexit max8922_remove(struct platform_device *pdev)
395 struct max8922_info *info = platform_get_drvdata(pdev);
397 power_supply_unregister(&info->psy_bat);
399 gpio_free(info->pdata->gpio_chg_en);
400 gpio_free(info->pdata->gpio_chg_ing);
401 gpio_free(info->pdata->gpio_ta_nconnected);
403 if (info->switch_dev)
404 switch_unregister_notifier(info->switch_dev, &info->nb);
412 static int max8922_suspend(struct device *dev)
414 struct max8922_info *info = dev_get_drvdata(dev);
416 if (info && info->irq_chg_ing)
417 disable_irq(info->irq_chg_ing);
422 static int max8922_resume(struct device *dev)
424 struct max8922_info *info = dev_get_drvdata(dev);
426 if (info && info->irq_chg_ing)
427 enable_irq(info->irq_chg_ing);
432 #define max8922_charger_suspend NULL
433 #define max8922_charger_resume NULL
436 static const struct dev_pm_ops max8922_pm_ops = {
437 .suspend = max8922_suspend,
438 .resume = max8922_resume,
441 static struct platform_driver max8922_driver = {
443 .name = "max8922-charger",
444 .owner = THIS_MODULE,
445 .pm = &max8922_pm_ops,
447 .probe = max8922_probe,
448 .remove = __devexit_p(max8922_remove),
451 static int __init max8922_init(void)
453 return platform_driver_register(&max8922_driver);
456 static void __exit max8922_exit(void)
458 platform_driver_register(&max8922_driver);
461 subsys_initcall(max8922_init);
462 module_exit(max8922_exit);
464 MODULE_DESCRIPTION("MAXIM 8922 charger control driver");
465 MODULE_AUTHOR("<ms925.kim@samsung.com>");
466 MODULE_LICENSE("GPL");