diff options
Diffstat (limited to 'drivers/power/gpio-charger.c')
-rw-r--r-- | drivers/power/gpio-charger.c | 185 |
1 files changed, 185 insertions, 0 deletions
diff --git a/drivers/power/gpio-charger.c b/drivers/power/gpio-charger.c new file mode 100644 index 000000000000..fccbe99b619c --- /dev/null +++ b/drivers/power/gpio-charger.c | |||
@@ -0,0 +1,185 @@ | |||
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 | |||
28 | struct gpio_charger { | ||
29 | const struct gpio_charger_platform_data *pdata; | ||
30 | unsigned int irq; | ||
31 | |||
32 | struct power_supply charger; | ||
33 | }; | ||
34 | |||
35 | static 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 | |||
44 | static inline struct gpio_charger *psy_to_gpio_charger(struct power_supply *psy) | ||
45 | { | ||
46 | return container_of(psy, struct gpio_charger, charger); | ||
47 | } | ||
48 | |||
49 | static 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 | |||
67 | static enum power_supply_property gpio_charger_properties[] = { | ||
68 | POWER_SUPPLY_PROP_ONLINE, | ||
69 | }; | ||
70 | |||
71 | static 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 | |||
135 | err_irq_free: | ||
136 | if (gpio_charger->irq) | ||
137 | free_irq(gpio_charger->irq, charger); | ||
138 | err_gpio_free: | ||
139 | gpio_free(pdata->gpio); | ||
140 | err_free: | ||
141 | kfree(gpio_charger); | ||
142 | return ret; | ||
143 | } | ||
144 | |||
145 | static 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 | |||
161 | static 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 | |||
170 | static int __init gpio_charger_init(void) | ||
171 | { | ||
172 | return platform_driver_register(&gpio_charger_driver); | ||
173 | } | ||
174 | module_init(gpio_charger_init); | ||
175 | |||
176 | static void __exit gpio_charger_exit(void) | ||
177 | { | ||
178 | platform_driver_unregister(&gpio_charger_driver); | ||
179 | } | ||
180 | module_exit(gpio_charger_exit); | ||
181 | |||
182 | MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); | ||
183 | MODULE_DESCRIPTION("Driver for chargers which report their online status through a GPIO"); | ||
184 | MODULE_LICENSE("GPL"); | ||
185 | MODULE_ALIAS("platform:gpio-charger"); | ||