blob: 33ff0e37809e43c29e848da0eac1e72ebf27d406 [file] [log] [blame]
MyungJoo Hamb14a9cc2011-03-29 10:10:16 +09001/*
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
30struct 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
39static 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
45static 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
80static 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
121static 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
160static 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
181static __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
334err_usb_irq:
335 if (pdata->usb_valid)
336 free_irq(gpio_to_irq(pdata->uok), data);
337err_dc_irq:
338 if (pdata->dc_valid)
339 free_irq(gpio_to_irq(pdata->dok), data);
340err_psy:
341 power_supply_unregister(&data->psy);
342err:
343 kfree(data);
344 return ret;
345}
346
347static __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
367static 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
376static int __init max8903_init(void)
377{
378 return platform_driver_register(&max8903_driver);
379}
380module_init(max8903_init);
381
382static void __exit max8903_exit(void)
383{
384 platform_driver_unregister(&max8903_driver);
385}
386module_exit(max8903_exit);
387
388MODULE_LICENSE("GPL");
389MODULE_DESCRIPTION("MAX8903 Charger Driver");
390MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
391MODULE_ALIAS("max8903-charger");