aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/power/max8907c-charger.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/power/max8907c-charger.c')
-rw-r--r--drivers/power/max8907c-charger.c228
1 files changed, 228 insertions, 0 deletions
diff --git a/drivers/power/max8907c-charger.c b/drivers/power/max8907c-charger.c
new file mode 100644
index 00000000000..64855c589b1
--- /dev/null
+++ b/drivers/power/max8907c-charger.c
@@ -0,0 +1,228 @@
1/*
2 * Battery driver for Maxim MAX8907C
3 *
4 * Copyright (c) 2011, NVIDIA Corporation.
5 * Copyright (C) 2010 Gyungoh Yoo <jack.yoo@maxim-ic.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 */
11
12#include <linux/module.h>
13#include <linux/err.h>
14#include <linux/interrupt.h>
15#include <linux/platform_device.h>
16#include <linux/power_supply.h>
17#include <linux/mfd/max8907c.h>
18#include <linux/power/max8907c-charger.h>
19#include <linux/slab.h>
20
21struct max8907c_charger {
22 struct max8907c_charger_pdata *pdata;
23 struct max8907c *chip;
24 struct i2c_client *i2c;
25 int online;
26};
27
28static void max8907c_set_charger(struct max8907c_charger *charger)
29{
30 struct max8907c_charger_pdata *pdata = charger->pdata;
31 int ret;
32 if (charger->online) {
33 ret = max8907c_reg_write(charger->i2c, MAX8907C_REG_CHG_CNTL1,
34 (pdata->topoff_threshold << 5) |
35 (pdata->restart_hysteresis << 3) |
36 (pdata->fast_charging_current));
37 if (unlikely(ret != 0))
38 pr_err("Failed to set CHG_CNTL1: %d\n", ret);
39
40 ret = max8907c_set_bits(charger->i2c, MAX8907C_REG_CHG_CNTL2,
41 0x30, pdata->fast_charger_time << 4);
42 if (unlikely(ret != 0))
43 pr_err("Failed to set CHG_CNTL2: %d\n", ret);
44 } else {
45 ret = max8907c_set_bits(charger->i2c, MAX8907C_REG_CHG_CNTL1, 0x80, 0x1);
46 if (unlikely(ret != 0))
47 pr_err("Failed to set CHG_CNTL1: %d\n", ret);
48 }
49}
50
51static irqreturn_t max8907c_charger_isr(int irq, void *dev_id)
52{
53 struct max8907c_charger *charger = dev_id;
54 struct max8907c *chip = charger->chip;
55
56 switch (irq - chip->irq_base) {
57 case MAX8907C_IRQ_VCHG_DC_R:
58 charger->online = 1;
59 max8907c_set_charger(charger);
60 break;
61 case MAX8907C_IRQ_VCHG_DC_F:
62 charger->online = 0;
63 max8907c_set_charger(charger);
64 break;
65 }
66
67 return IRQ_HANDLED;
68}
69
70static int max8907c_charger_get_property(struct power_supply *psy,
71 enum power_supply_property psp,
72 union power_supply_propval *val)
73{
74 const static int types[] = {
75 POWER_SUPPLY_CHARGE_TYPE_TRICKLE,
76 POWER_SUPPLY_CHARGE_TYPE_FAST,
77 POWER_SUPPLY_CHARGE_TYPE_FAST,
78 POWER_SUPPLY_CHARGE_TYPE_NONE,
79 };
80 int ret = -ENODEV;
81 int status;
82
83 struct max8907c_charger *charger = dev_get_drvdata(psy->dev->parent);
84
85 switch (psp) {
86 case POWER_SUPPLY_PROP_ONLINE:
87 val->intval = charger->online;
88 ret = 0;
89 break;
90
91 case POWER_SUPPLY_PROP_STATUS:
92 /* Get charger status from CHG_EN_STAT */
93 status = max8907c_reg_read(charger->i2c, MAX8907C_REG_CHG_STAT);
94 val->intval = ((status & 0x10) == 0x10) ?
95 POWER_SUPPLY_STATUS_CHARGING :
96 POWER_SUPPLY_STATUS_NOT_CHARGING;
97 ret = 0;
98 break;
99
100 case POWER_SUPPLY_PROP_CHARGE_TYPE:
101 /* Get charging type from CHG_MODE */
102 status = max8907c_reg_read(charger->i2c, MAX8907C_REG_CHG_STAT);
103 val->intval = types[(status & 0x0C) >> 2];
104 ret = 0;
105 break;
106
107 default:
108 val->intval = 0;
109 ret = -EINVAL;
110 break;
111 }
112 return ret;
113}
114
115static enum power_supply_property max8907c_charger_props[] = {
116 POWER_SUPPLY_PROP_ONLINE,
117 POWER_SUPPLY_PROP_STATUS,
118 POWER_SUPPLY_PROP_CHARGE_TYPE,
119};
120
121static struct power_supply max8907c_charger_ps = {
122 .name = "charger",
123 .type = POWER_SUPPLY_TYPE_MAINS,
124 .properties = max8907c_charger_props,
125 .num_properties = ARRAY_SIZE(max8907c_charger_props),
126 .get_property = max8907c_charger_get_property,
127};
128
129static __devinit int max8907c_charger_probe(struct platform_device *pdev)
130{
131 struct max8907c_charger_pdata *pdata = pdev->dev.platform_data;
132 struct max8907c_charger *charger = 0;
133 struct max8907c *chip = dev_get_drvdata(pdev->dev.parent);
134 int ret;
135
136 charger = kzalloc(sizeof(*charger), GFP_KERNEL);
137 if (!charger)
138 return -ENOMEM;
139
140 charger->pdata = pdata;
141 charger->online = 0;
142 charger->chip = chip;
143 charger->i2c = chip->i2c_power;
144
145 platform_set_drvdata(pdev, charger);
146
147 ret = max8907c_reg_read(charger->i2c, MAX8907C_REG_CHG_STAT);
148 if (ret & (1 << 7)) {
149 charger->online = 1;
150 max8907c_set_charger(charger);
151 }
152
153 ret = request_threaded_irq(chip->irq_base + MAX8907C_IRQ_VCHG_DC_F, NULL,
154 max8907c_charger_isr, IRQF_ONESHOT,
155 "power-remove", charger);
156 if (unlikely(ret < 0)) {
157 pr_debug("max8907c: failed to request IRQ %X\n", ret);
158 goto out;
159 }
160
161 ret = request_threaded_irq(chip->irq_base + MAX8907C_IRQ_VCHG_DC_R, NULL,
162 max8907c_charger_isr, IRQF_ONESHOT,
163 "power-insert", charger);
164 if (unlikely(ret < 0)) {
165 pr_debug("max8907c: failed to request IRQ %X\n", ret);
166 goto out1;
167 }
168
169
170 ret = power_supply_register(&pdev->dev, &max8907c_charger_ps);
171 if (unlikely(ret != 0)) {
172 pr_err("Failed to register max8907c_charger driver: %d\n", ret);
173 goto out2;
174 }
175
176 return 0;
177out2:
178 free_irq(chip->irq_base + MAX8907C_IRQ_VCHG_DC_R, charger);
179out1:
180 free_irq(chip->irq_base + MAX8907C_IRQ_VCHG_DC_F, charger);
181out:
182 kfree(charger);
183 return ret;
184}
185
186static __devexit int max8907c_charger_remove(struct platform_device *pdev)
187{
188 struct max8907c_charger *charger = platform_get_drvdata(pdev);
189 struct max8907c *chip = charger->chip;
190 int ret;
191
192 ret = max8907c_reg_write(charger->i2c, MAX8907C_REG_CHG_IRQ1_MASK, 0xFF);
193 if (unlikely(ret != 0)) {
194 pr_err("Failed to set IRQ1_MASK: %d\n", ret);
195 goto out;
196 }
197
198 free_irq(chip->irq_base + MAX8907C_IRQ_VCHG_DC_R, charger);
199 free_irq(chip->irq_base + MAX8907C_IRQ_VCHG_DC_F, charger);
200 power_supply_unregister(&max8907c_charger_ps);
201out:
202 kfree(charger);
203 return 0;
204}
205
206static struct platform_driver max8907c_charger_driver = {
207 .probe = max8907c_charger_probe,
208 .remove = __devexit_p(max8907c_charger_remove),
209 .driver = {
210 .name = "max8907c-charger",
211 },
212};
213
214static int __init max8907c_charger_init(void)
215{
216 return platform_driver_register(&max8907c_charger_driver);
217}
218module_init(max8907c_charger_init);
219
220static void __exit max8907c_charger_exit(void)
221{
222 platform_driver_unregister(&max8907c_charger_driver);
223}
224module_exit(max8907c_charger_exit);
225
226MODULE_DESCRIPTION("Charger driver for MAX8907C");
227MODULE_AUTHOR("Gyungoh Yoo <jack.yoo@maxim-ic.com>");
228MODULE_LICENSE("GPL");