gpio-charger.c revision 5070437cd99511f69ae561f2ab417142a47a85ec
1/*
2 *  Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
3 *  Driver for chargers which report their online status through a GPIO pin
4 *
5 *  This program is free software; you can redistribute it and/or modify it
6 *  under  the terms of the GNU General  Public License as published by the
7 *  Free Software Foundation;  either version 2 of the License, or (at your
8 *  option) any later version.
9 *
10 *  You should have received a copy of the GNU General Public License along
11 *  with this program; if not, write to the Free Software Foundation, Inc.,
12 *  675 Mass Ave, Cambridge, MA 02139, USA.
13 *
14 */
15
16#include <linux/device.h>
17#include <linux/gpio.h>
18#include <linux/init.h>
19#include <linux/interrupt.h>
20#include <linux/kernel.h>
21#include <linux/module.h>
22#include <linux/platform_device.h>
23#include <linux/power_supply.h>
24#include <linux/slab.h>
25
26#include <linux/power/gpio-charger.h>
27
28struct gpio_charger {
29	const struct gpio_charger_platform_data *pdata;
30	unsigned int irq;
31
32	struct power_supply charger;
33};
34
35static irqreturn_t gpio_charger_irq(int irq, void *devid)
36{
37	struct power_supply *charger = devid;
38
39	power_supply_changed(charger);
40
41	return IRQ_HANDLED;
42}
43
44static inline struct gpio_charger *psy_to_gpio_charger(struct power_supply *psy)
45{
46	return container_of(psy, struct gpio_charger, charger);
47}
48
49static int gpio_charger_get_property(struct power_supply *psy,
50		enum power_supply_property psp, union power_supply_propval *val)
51{
52	struct gpio_charger *gpio_charger = psy_to_gpio_charger(psy);
53	const struct gpio_charger_platform_data *pdata = gpio_charger->pdata;
54
55	switch (psp) {
56	case POWER_SUPPLY_PROP_ONLINE:
57		val->intval = gpio_get_value(pdata->gpio);
58		val->intval ^= pdata->gpio_active_low;
59		break;
60	default:
61		return -EINVAL;
62	}
63
64	return 0;
65}
66
67static enum power_supply_property gpio_charger_properties[] = {
68	POWER_SUPPLY_PROP_ONLINE,
69};
70
71static int __devinit gpio_charger_probe(struct platform_device *pdev)
72{
73	const struct gpio_charger_platform_data *pdata = pdev->dev.platform_data;
74	struct gpio_charger *gpio_charger;
75	struct power_supply *charger;
76	int ret;
77	int irq;
78
79	if (!pdata) {
80		dev_err(&pdev->dev, "No platform data\n");
81		return -EINVAL;
82	}
83
84	if (!gpio_is_valid(pdata->gpio)) {
85		dev_err(&pdev->dev, "Invalid gpio pin\n");
86		return -EINVAL;
87	}
88
89	gpio_charger = kzalloc(sizeof(*gpio_charger), GFP_KERNEL);
90
91	charger = &gpio_charger->charger;
92
93	charger->name = pdata->name;
94	charger->type = pdata->type;
95	charger->properties = gpio_charger_properties;
96	charger->num_properties = ARRAY_SIZE(gpio_charger_properties);
97	charger->get_property  = gpio_charger_get_property;
98	charger->supplied_to = pdata->supplied_to;
99	charger->num_supplicants = pdata->num_supplicants;
100
101	ret = gpio_request(pdata->gpio, dev_name(&pdev->dev));
102	if (ret) {
103		dev_err(&pdev->dev, "Failed to request gpio pin: %d\n", ret);
104		goto err_free;
105	}
106	ret = gpio_direction_input(pdata->gpio);
107	if (ret) {
108		dev_err(&pdev->dev, "Failed to set gpio to input: %d\n", ret);
109		goto err_gpio_free;
110	}
111
112	irq = gpio_to_irq(pdata->gpio);
113	if (irq > 0) {
114		ret = request_irq(irq, gpio_charger_irq,
115				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
116				dev_name(&pdev->dev), charger);
117		if (ret)
118			dev_warn(&pdev->dev, "Failed to request irq: %d\n", ret);
119		else
120			gpio_charger->irq = irq;
121	}
122
123	gpio_charger->pdata = pdata;
124
125	ret = power_supply_register(&pdev->dev, charger);
126	if (ret < 0) {
127		dev_err(&pdev->dev, "Failed to register power supply: %d\n", ret);
128		goto err_irq_free;
129	}
130
131	platform_set_drvdata(pdev, gpio_charger);
132
133	return 0;
134
135err_irq_free:
136	if (gpio_charger->irq)
137		free_irq(gpio_charger->irq, charger);
138err_gpio_free:
139	gpio_free(pdata->gpio);
140err_free:
141	kfree(gpio_charger);
142	return ret;
143}
144
145static int __devexit gpio_charger_remove(struct platform_device *pdev)
146{
147	struct gpio_charger *gpio_charger = platform_get_drvdata(pdev);
148
149	power_supply_unregister(&gpio_charger->charger);
150
151	if (gpio_charger->irq)
152		free_irq(gpio_charger->irq, &gpio_charger->charger);
153	gpio_free(gpio_charger->pdata->gpio);
154
155	platform_set_drvdata(pdev, NULL);
156	kfree(gpio_charger);
157
158	return 0;
159}
160
161static struct platform_driver gpio_charger_driver = {
162	.probe = gpio_charger_probe,
163	.remove = __devexit_p(gpio_charger_remove),
164	.driver = {
165		.name = "gpio-charger",
166		.owner = THIS_MODULE,
167	},
168};
169
170static int __init gpio_charger_init(void)
171{
172	return platform_driver_register(&gpio_charger_driver);
173}
174module_init(gpio_charger_init);
175
176static void __exit gpio_charger_exit(void)
177{
178	platform_driver_unregister(&gpio_charger_driver);
179}
180module_exit(gpio_charger_exit);
181
182MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
183MODULE_DESCRIPTION("Driver for chargers which report their online status through a GPIO");
184MODULE_LICENSE("GPL");
185MODULE_ALIAS("platform:gpio-charger");
186