diff options
Diffstat (limited to 'drivers/power/max8907c-charger.c')
-rw-r--r-- | drivers/power/max8907c-charger.c | 228 |
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 | |||
21 | struct max8907c_charger { | ||
22 | struct max8907c_charger_pdata *pdata; | ||
23 | struct max8907c *chip; | ||
24 | struct i2c_client *i2c; | ||
25 | int online; | ||
26 | }; | ||
27 | |||
28 | static 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 | |||
51 | static 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 | |||
70 | static 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 | |||
115 | static 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 | |||
121 | static 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 | |||
129 | static __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; | ||
177 | out2: | ||
178 | free_irq(chip->irq_base + MAX8907C_IRQ_VCHG_DC_R, charger); | ||
179 | out1: | ||
180 | free_irq(chip->irq_base + MAX8907C_IRQ_VCHG_DC_F, charger); | ||
181 | out: | ||
182 | kfree(charger); | ||
183 | return ret; | ||
184 | } | ||
185 | |||
186 | static __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); | ||
201 | out: | ||
202 | kfree(charger); | ||
203 | return 0; | ||
204 | } | ||
205 | |||
206 | static 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 | |||
214 | static int __init max8907c_charger_init(void) | ||
215 | { | ||
216 | return platform_driver_register(&max8907c_charger_driver); | ||
217 | } | ||
218 | module_init(max8907c_charger_init); | ||
219 | |||
220 | static void __exit max8907c_charger_exit(void) | ||
221 | { | ||
222 | platform_driver_unregister(&max8907c_charger_driver); | ||
223 | } | ||
224 | module_exit(max8907c_charger_exit); | ||
225 | |||
226 | MODULE_DESCRIPTION("Charger driver for MAX8907C"); | ||
227 | MODULE_AUTHOR("Gyungoh Yoo <jack.yoo@maxim-ic.com>"); | ||
228 | MODULE_LICENSE("GPL"); | ||