max8903_charger.c revision bd19c756b1a69ec2c8f5f81624d66a1a0daad7c0
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
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	memcpy(&data->pdata, pdata, sizeof(struct max8903_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("platform:max8903-charger");
392