diff options
-rw-r--r-- | drivers/power/Kconfig | 8 | ||||
-rw-r--r-- | drivers/power/Makefile | 1 | ||||
-rw-r--r-- | drivers/power/max8903_charger.c | 391 | ||||
-rw-r--r-- | include/linux/power/max8903_charger.h | 57 |
4 files changed, 457 insertions, 0 deletions
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 52a462fc6b84..1f50ebcc6399 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig | |||
@@ -203,6 +203,14 @@ config CHARGER_ISP1704 | |||
203 | Say Y to enable support for USB Charger Detection with | 203 | Say Y to enable support for USB Charger Detection with |
204 | ISP1707/ISP1704 USB transceivers. | 204 | ISP1707/ISP1704 USB transceivers. |
205 | 205 | ||
206 | config CHARGER_MAX8903 | ||
207 | tristate "MAX8903 Battery DC-DC Charger for USB and Adapter Power" | ||
208 | help | ||
209 | Say Y to enable support for the MAX8903 DC-DC charger and sysfs. | ||
210 | The driver supports controlling charger-enable and current-limit | ||
211 | pins based on the status of charger connections with interrupt | ||
212 | handlers. | ||
213 | |||
206 | config CHARGER_TWL4030 | 214 | config CHARGER_TWL4030 |
207 | tristate "OMAP TWL4030 BCI charger driver" | 215 | tristate "OMAP TWL4030 BCI charger driver" |
208 | depends on TWL4030_CORE | 216 | depends on TWL4030_CORE |
diff --git a/drivers/power/Makefile b/drivers/power/Makefile index 8385bfae8728..8fcd93ff2353 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile | |||
@@ -32,5 +32,6 @@ obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o | |||
32 | obj-$(CONFIG_BATTERY_JZ4740) += jz4740-battery.o | 32 | obj-$(CONFIG_BATTERY_JZ4740) += jz4740-battery.o |
33 | obj-$(CONFIG_BATTERY_INTEL_MID) += intel_mid_battery.o | 33 | obj-$(CONFIG_BATTERY_INTEL_MID) += intel_mid_battery.o |
34 | obj-$(CONFIG_CHARGER_ISP1704) += isp1704_charger.o | 34 | obj-$(CONFIG_CHARGER_ISP1704) += isp1704_charger.o |
35 | obj-$(CONFIG_CHARGER_MAX8903) += max8903_charger.o | ||
35 | obj-$(CONFIG_CHARGER_TWL4030) += twl4030_charger.o | 36 | obj-$(CONFIG_CHARGER_TWL4030) += twl4030_charger.o |
36 | obj-$(CONFIG_CHARGER_GPIO) += gpio-charger.o | 37 | obj-$(CONFIG_CHARGER_GPIO) += gpio-charger.o |
diff --git a/drivers/power/max8903_charger.c b/drivers/power/max8903_charger.c new file mode 100644 index 000000000000..33ff0e37809e --- /dev/null +++ b/drivers/power/max8903_charger.c | |||
@@ -0,0 +1,391 @@ | |||
1 | /* | ||
2 | * max8903_charger.c - Maxim 8903 USB/Adapter Charger Driver | ||
3 | * | ||
4 | * Copyright (C) 2011 Samsung Electronics | ||
5 | * MyungJoo Ham <myungjoo.ham@samsung.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 as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU 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 02111-1307 USA | ||
20 | * | ||
21 | */ | ||
22 | |||
23 | #include <linux/gpio.h> | ||
24 | #include <linux/interrupt.h> | ||
25 | #include <linux/slab.h> | ||
26 | #include <linux/power_supply.h> | ||
27 | #include <linux/platform_device.h> | ||
28 | #include <linux/power/max8903_charger.h> | ||
29 | |||
30 | struct max8903_data { | ||
31 | struct max8903_pdata *pdata; | ||
32 | struct device *dev; | ||
33 | struct power_supply psy; | ||
34 | bool fault; | ||
35 | bool usb_in; | ||
36 | bool ta_in; | ||
37 | }; | ||
38 | |||
39 | static enum power_supply_property max8903_charger_props[] = { | ||
40 | POWER_SUPPLY_PROP_STATUS, /* Charger status output */ | ||
41 | POWER_SUPPLY_PROP_ONLINE, /* External power source */ | ||
42 | POWER_SUPPLY_PROP_HEALTH, /* Fault or OK */ | ||
43 | }; | ||
44 | |||
45 | static int max8903_get_property(struct power_supply *psy, | ||
46 | enum power_supply_property psp, | ||
47 | union power_supply_propval *val) | ||
48 | { | ||
49 | struct max8903_data *data = container_of(psy, | ||
50 | struct max8903_data, psy); | ||
51 | |||
52 | switch (psp) { | ||
53 | case POWER_SUPPLY_PROP_STATUS: | ||
54 | val->intval = POWER_SUPPLY_STATUS_UNKNOWN; | ||
55 | if (data->pdata->chg) { | ||
56 | if (gpio_get_value(data->pdata->chg) == 0) | ||
57 | val->intval = POWER_SUPPLY_STATUS_CHARGING; | ||
58 | else if (data->usb_in || data->ta_in) | ||
59 | val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; | ||
60 | else | ||
61 | val->intval = POWER_SUPPLY_STATUS_DISCHARGING; | ||
62 | } | ||
63 | break; | ||
64 | case POWER_SUPPLY_PROP_ONLINE: | ||
65 | val->intval = 0; | ||
66 | if (data->usb_in || data->ta_in) | ||
67 | val->intval = 1; | ||
68 | break; | ||
69 | case POWER_SUPPLY_PROP_HEALTH: | ||
70 | val->intval = POWER_SUPPLY_HEALTH_GOOD; | ||
71 | if (data->fault) | ||
72 | val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; | ||
73 | break; | ||
74 | default: | ||
75 | return -EINVAL; | ||
76 | } | ||
77 | return 0; | ||
78 | } | ||
79 | |||
80 | static irqreturn_t max8903_dcin(int irq, void *_data) | ||
81 | { | ||
82 | struct max8903_data *data = _data; | ||
83 | struct max8903_pdata *pdata = data->pdata; | ||
84 | bool ta_in; | ||
85 | enum power_supply_type old_type; | ||
86 | |||
87 | ta_in = gpio_get_value(pdata->dok) ? false : true; | ||
88 | |||
89 | if (ta_in == data->ta_in) | ||
90 | return IRQ_HANDLED; | ||
91 | |||
92 | data->ta_in = ta_in; | ||
93 | |||
94 | /* Set Current-Limit-Mode 1:DC 0:USB */ | ||
95 | if (pdata->dcm) | ||
96 | gpio_set_value(pdata->dcm, ta_in ? 1 : 0); | ||
97 | |||
98 | /* Charger Enable / Disable (cen is negated) */ | ||
99 | if (pdata->cen) | ||
100 | gpio_set_value(pdata->cen, ta_in ? 0 : | ||
101 | (data->usb_in ? 0 : 1)); | ||
102 | |||
103 | dev_dbg(data->dev, "TA(DC-IN) Charger %s.\n", ta_in ? | ||
104 | "Connected" : "Disconnected"); | ||
105 | |||
106 | old_type = data->psy.type; | ||
107 | |||
108 | if (data->ta_in) | ||
109 | data->psy.type = POWER_SUPPLY_TYPE_MAINS; | ||
110 | else if (data->usb_in) | ||
111 | data->psy.type = POWER_SUPPLY_TYPE_USB; | ||
112 | else | ||
113 | data->psy.type = POWER_SUPPLY_TYPE_BATTERY; | ||
114 | |||
115 | if (old_type != data->psy.type) | ||
116 | power_supply_changed(&data->psy); | ||
117 | |||
118 | return IRQ_HANDLED; | ||
119 | } | ||
120 | |||
121 | static irqreturn_t max8903_usbin(int irq, void *_data) | ||
122 | { | ||
123 | struct max8903_data *data = _data; | ||
124 | struct max8903_pdata *pdata = data->pdata; | ||
125 | bool usb_in; | ||
126 | enum power_supply_type old_type; | ||
127 | |||
128 | usb_in = gpio_get_value(pdata->uok) ? false : true; | ||
129 | |||
130 | if (usb_in == data->usb_in) | ||
131 | return IRQ_HANDLED; | ||
132 | |||
133 | data->usb_in = usb_in; | ||
134 | |||
135 | /* Do not touch Current-Limit-Mode */ | ||
136 | |||
137 | /* Charger Enable / Disable (cen is negated) */ | ||
138 | if (pdata->cen) | ||
139 | gpio_set_value(pdata->cen, usb_in ? 0 : | ||
140 | (data->ta_in ? 0 : 1)); | ||
141 | |||
142 | dev_dbg(data->dev, "USB Charger %s.\n", usb_in ? | ||
143 | "Connected" : "Disconnected"); | ||
144 | |||
145 | old_type = data->psy.type; | ||
146 | |||
147 | if (data->ta_in) | ||
148 | data->psy.type = POWER_SUPPLY_TYPE_MAINS; | ||
149 | else if (data->usb_in) | ||
150 | data->psy.type = POWER_SUPPLY_TYPE_USB; | ||
151 | else | ||
152 | data->psy.type = POWER_SUPPLY_TYPE_BATTERY; | ||
153 | |||
154 | if (old_type != data->psy.type) | ||
155 | power_supply_changed(&data->psy); | ||
156 | |||
157 | return IRQ_HANDLED; | ||
158 | } | ||
159 | |||
160 | static irqreturn_t max8903_fault(int irq, void *_data) | ||
161 | { | ||
162 | struct max8903_data *data = _data; | ||
163 | struct max8903_pdata *pdata = data->pdata; | ||
164 | bool fault; | ||
165 | |||
166 | fault = gpio_get_value(pdata->flt) ? false : true; | ||
167 | |||
168 | if (fault == data->fault) | ||
169 | return IRQ_HANDLED; | ||
170 | |||
171 | data->fault = fault; | ||
172 | |||
173 | if (fault) | ||
174 | dev_err(data->dev, "Charger suffers a fault and stops.\n"); | ||
175 | else | ||
176 | dev_err(data->dev, "Charger recovered from a fault.\n"); | ||
177 | |||
178 | return IRQ_HANDLED; | ||
179 | } | ||
180 | |||
181 | static __devinit int max8903_probe(struct platform_device *pdev) | ||
182 | { | ||
183 | struct max8903_data *data; | ||
184 | struct device *dev = &pdev->dev; | ||
185 | struct max8903_pdata *pdata = pdev->dev.platform_data; | ||
186 | int ret = 0; | ||
187 | int gpio; | ||
188 | int ta_in = 0; | ||
189 | int usb_in = 0; | ||
190 | |||
191 | data = kzalloc(sizeof(struct max8903_data), GFP_KERNEL); | ||
192 | if (data == NULL) { | ||
193 | dev_err(dev, "Cannot allocate memory.\n"); | ||
194 | return -ENOMEM; | ||
195 | } | ||
196 | data->pdata = pdata; | ||
197 | data->dev = dev; | ||
198 | platform_set_drvdata(pdev, data); | ||
199 | |||
200 | if (pdata->dc_valid == false && pdata->usb_valid == false) { | ||
201 | dev_err(dev, "No valid power sources.\n"); | ||
202 | ret = -EINVAL; | ||
203 | goto err; | ||
204 | } | ||
205 | |||
206 | if (pdata->dc_valid) { | ||
207 | if (pdata->dok && gpio_is_valid(pdata->dok) && | ||
208 | pdata->dcm && gpio_is_valid(pdata->dcm)) { | ||
209 | gpio = pdata->dok; /* PULL_UPed Interrupt */ | ||
210 | ta_in = gpio_get_value(gpio) ? 0 : 1; | ||
211 | |||
212 | gpio = pdata->dcm; /* Output */ | ||
213 | gpio_set_value(gpio, ta_in); | ||
214 | } else { | ||
215 | dev_err(dev, "When DC is wired, DOK and DCM should" | ||
216 | " be wired as well.\n"); | ||
217 | ret = -EINVAL; | ||
218 | goto err; | ||
219 | } | ||
220 | } else { | ||
221 | if (pdata->dcm) { | ||
222 | if (gpio_is_valid(pdata->dcm)) | ||
223 | gpio_set_value(pdata->dcm, 0); | ||
224 | else { | ||
225 | dev_err(dev, "Invalid pin: dcm.\n"); | ||
226 | ret = -EINVAL; | ||
227 | goto err; | ||
228 | } | ||
229 | } | ||
230 | } | ||
231 | |||
232 | if (pdata->usb_valid) { | ||
233 | if (pdata->uok && gpio_is_valid(pdata->uok)) { | ||
234 | gpio = pdata->uok; | ||
235 | usb_in = gpio_get_value(gpio) ? 0 : 1; | ||
236 | } else { | ||
237 | dev_err(dev, "When USB is wired, UOK should be wired." | ||
238 | "as well.\n"); | ||
239 | ret = -EINVAL; | ||
240 | goto err; | ||
241 | } | ||
242 | } | ||
243 | |||
244 | if (pdata->cen) { | ||
245 | if (gpio_is_valid(pdata->cen)) { | ||
246 | gpio_set_value(pdata->cen, (ta_in || usb_in) ? 0 : 1); | ||
247 | } else { | ||
248 | dev_err(dev, "Invalid pin: cen.\n"); | ||
249 | ret = -EINVAL; | ||
250 | goto err; | ||
251 | } | ||
252 | } | ||
253 | |||
254 | if (pdata->chg) { | ||
255 | if (!gpio_is_valid(pdata->chg)) { | ||
256 | dev_err(dev, "Invalid pin: chg.\n"); | ||
257 | ret = -EINVAL; | ||
258 | goto err; | ||
259 | } | ||
260 | } | ||
261 | |||
262 | if (pdata->flt) { | ||
263 | if (!gpio_is_valid(pdata->flt)) { | ||
264 | dev_err(dev, "Invalid pin: flt.\n"); | ||
265 | ret = -EINVAL; | ||
266 | goto err; | ||
267 | } | ||
268 | } | ||
269 | |||
270 | if (pdata->usus) { | ||
271 | if (!gpio_is_valid(pdata->usus)) { | ||
272 | dev_err(dev, "Invalid pin: usus.\n"); | ||
273 | ret = -EINVAL; | ||
274 | goto err; | ||
275 | } | ||
276 | } | ||
277 | |||
278 | data->fault = false; | ||
279 | data->ta_in = ta_in; | ||
280 | data->usb_in = usb_in; | ||
281 | |||
282 | data->psy.name = "max8903_charger"; | ||
283 | data->psy.type = (ta_in) ? POWER_SUPPLY_TYPE_MAINS : | ||
284 | ((usb_in) ? POWER_SUPPLY_TYPE_USB : | ||
285 | POWER_SUPPLY_TYPE_BATTERY); | ||
286 | data->psy.get_property = max8903_get_property; | ||
287 | data->psy.properties = max8903_charger_props; | ||
288 | data->psy.num_properties = ARRAY_SIZE(max8903_charger_props); | ||
289 | |||
290 | ret = power_supply_register(dev, &data->psy); | ||
291 | if (ret) { | ||
292 | dev_err(dev, "failed: power supply register.\n"); | ||
293 | goto err; | ||
294 | } | ||
295 | |||
296 | if (pdata->dc_valid) { | ||
297 | ret = request_threaded_irq(gpio_to_irq(pdata->dok), | ||
298 | NULL, max8903_dcin, | ||
299 | IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, | ||
300 | "MAX8903 DC IN", data); | ||
301 | if (ret) { | ||
302 | dev_err(dev, "Cannot request irq %d for DC (%d)\n", | ||
303 | gpio_to_irq(pdata->dok), ret); | ||
304 | goto err_psy; | ||
305 | } | ||
306 | } | ||
307 | |||
308 | if (pdata->usb_valid) { | ||
309 | ret = request_threaded_irq(gpio_to_irq(pdata->uok), | ||
310 | NULL, max8903_usbin, | ||
311 | IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, | ||
312 | "MAX8903 USB IN", data); | ||
313 | if (ret) { | ||
314 | dev_err(dev, "Cannot request irq %d for USB (%d)\n", | ||
315 | gpio_to_irq(pdata->uok), ret); | ||
316 | goto err_dc_irq; | ||
317 | } | ||
318 | } | ||
319 | |||
320 | if (pdata->flt) { | ||
321 | ret = request_threaded_irq(gpio_to_irq(pdata->flt), | ||
322 | NULL, max8903_fault, | ||
323 | IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, | ||
324 | "MAX8903 Fault", data); | ||
325 | if (ret) { | ||
326 | dev_err(dev, "Cannot request irq %d for Fault (%d)\n", | ||
327 | gpio_to_irq(pdata->flt), ret); | ||
328 | goto err_usb_irq; | ||
329 | } | ||
330 | } | ||
331 | |||
332 | return 0; | ||
333 | |||
334 | err_usb_irq: | ||
335 | if (pdata->usb_valid) | ||
336 | free_irq(gpio_to_irq(pdata->uok), data); | ||
337 | err_dc_irq: | ||
338 | if (pdata->dc_valid) | ||
339 | free_irq(gpio_to_irq(pdata->dok), data); | ||
340 | err_psy: | ||
341 | power_supply_unregister(&data->psy); | ||
342 | err: | ||
343 | kfree(data); | ||
344 | return ret; | ||
345 | } | ||
346 | |||
347 | static __devexit int max8903_remove(struct platform_device *pdev) | ||
348 | { | ||
349 | struct max8903_data *data = platform_get_drvdata(pdev); | ||
350 | |||
351 | if (data) { | ||
352 | struct max8903_pdata *pdata = data->pdata; | ||
353 | |||
354 | if (pdata->flt) | ||
355 | free_irq(gpio_to_irq(pdata->flt), data); | ||
356 | if (pdata->usb_valid) | ||
357 | free_irq(gpio_to_irq(pdata->uok), data); | ||
358 | if (pdata->dc_valid) | ||
359 | free_irq(gpio_to_irq(pdata->dok), data); | ||
360 | power_supply_unregister(&data->psy); | ||
361 | kfree(data); | ||
362 | } | ||
363 | |||
364 | return 0; | ||
365 | } | ||
366 | |||
367 | static struct platform_driver max8903_driver = { | ||
368 | .probe = max8903_probe, | ||
369 | .remove = __devexit_p(max8903_remove), | ||
370 | .driver = { | ||
371 | .name = "max8903-charger", | ||
372 | .owner = THIS_MODULE, | ||
373 | }, | ||
374 | }; | ||
375 | |||
376 | static int __init max8903_init(void) | ||
377 | { | ||
378 | return platform_driver_register(&max8903_driver); | ||
379 | } | ||
380 | module_init(max8903_init); | ||
381 | |||
382 | static void __exit max8903_exit(void) | ||
383 | { | ||
384 | platform_driver_unregister(&max8903_driver); | ||
385 | } | ||
386 | module_exit(max8903_exit); | ||
387 | |||
388 | MODULE_LICENSE("GPL"); | ||
389 | MODULE_DESCRIPTION("MAX8903 Charger Driver"); | ||
390 | MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); | ||
391 | MODULE_ALIAS("max8903-charger"); | ||
diff --git a/include/linux/power/max8903_charger.h b/include/linux/power/max8903_charger.h new file mode 100644 index 000000000000..24f51db8a83f --- /dev/null +++ b/include/linux/power/max8903_charger.h | |||
@@ -0,0 +1,57 @@ | |||
1 | /* | ||
2 | * max8903_charger.h - Maxim 8903 USB/Adapter Charger Driver | ||
3 | * | ||
4 | * Copyright (C) 2011 Samsung Electronics | ||
5 | * MyungJoo Ham <myungjoo.ham@samsung.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 as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU 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 02111-1307 USA | ||
20 | * | ||
21 | */ | ||
22 | |||
23 | #ifndef __MAX8903_CHARGER_H__ | ||
24 | #define __MAX8903_CHARGER_H__ | ||
25 | |||
26 | struct max8903_pdata { | ||
27 | /* | ||
28 | * GPIOs | ||
29 | * cen, chg, flt, and usus are optional. | ||
30 | * dok, dcm, and uok are not optional depending on the status of | ||
31 | * dc_valid and usb_valid. | ||
32 | */ | ||
33 | int cen; /* Charger Enable input */ | ||
34 | int dok; /* DC(Adapter) Power OK output */ | ||
35 | int uok; /* USB Power OK output */ | ||
36 | int chg; /* Charger status output */ | ||
37 | int flt; /* Fault output */ | ||
38 | int dcm; /* Current-Limit Mode input (1: DC, 2: USB) */ | ||
39 | int usus; /* USB Suspend Input (1: suspended) */ | ||
40 | |||
41 | /* | ||
42 | * DC(Adapter/TA) is wired | ||
43 | * When dc_valid is true, | ||
44 | * dok and dcm should be valid. | ||
45 | * | ||
46 | * At least one of dc_valid or usb_valid should be true. | ||
47 | */ | ||
48 | bool dc_valid; | ||
49 | /* | ||
50 | * USB is wired | ||
51 | * When usb_valid is true, | ||
52 | * uok should be valid. | ||
53 | */ | ||
54 | bool usb_valid; | ||
55 | }; | ||
56 | |||
57 | #endif /* __MAX8903_CHARGER_H__ */ | ||