]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - drivers/usb/otg/gpio_vbus.c
add new gpio_vbus "transceiver"
[linux-2.6-omap-h63xx.git] / drivers / usb / otg / gpio_vbus.c
1 /*
2  * gpio-vbus.c - simple GPIO VBUS sensing driver for B peripheral devices
3  *
4  * Copyright (c) 2008 Philipp Zabel <philipp.zabel@gmail.com>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  */
10
11 #include <linux/kernel.h>
12 #include <linux/platform_device.h>
13 #include <linux/gpio.h>
14 #include <linux/interrupt.h>
15 #include <linux/usb.h>
16
17 #include <linux/regulator/consumer.h>
18
19 #include <linux/usb/gadget.h>
20 #include <linux/usb/gpio_vbus.h>
21 #include <linux/usb/otg.h>
22
23
24 /*
25  * A simple GPIO VBUS sensing driver for B peripheral only devices
26  * with internal transceivers. It can control a D+ pullup GPIO and
27  * a regulator to limit the current drawn from VBUS.
28  *
29  * Needs to be loaded before the UDC driver that will use it.
30  */
31 struct gpio_vbus_data {
32         struct otg_transceiver otg;
33         struct device          *dev;
34         struct regulator       *vbus_draw;
35         int                     vbus_draw_enabled;
36         unsigned                mA;
37 };
38
39
40 /*
41  * This driver relies on "both edges" triggering.  VBUS has 100 msec to
42  * stabilize, so the peripheral controller driver may need to cope with
43  * some bouncing due to current surges (e.g. charging local capacitance)
44  * and contact chatter.
45  *
46  * REVISIT in desperate straits, toggling between rising and falling
47  * edges might be workable.
48  */
49 #define VBUS_IRQ_FLAGS \
50         ( IRQF_SAMPLE_RANDOM | IRQF_SHARED \
51         | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING )
52
53
54 /* interface to regulator framework */
55 static void set_vbus_draw(struct gpio_vbus_data *gpio_vbus, unsigned mA)
56 {
57         struct regulator *vbus_draw = gpio_vbus->vbus_draw;
58         int enabled;
59
60         if (!vbus_draw)
61                 return;
62
63         enabled = gpio_vbus->vbus_draw_enabled;
64         if (mA) {
65                 regulator_set_current_limit(vbus_draw, 0, 1000 * mA);
66                 if (!enabled) {
67                         regulator_enable(vbus_draw);
68                         gpio_vbus->vbus_draw_enabled = 1;
69                 }
70         } else {
71                 if (enabled) {
72                         regulator_disable(vbus_draw);
73                         gpio_vbus->vbus_draw_enabled = 0;
74                 }
75         }
76         gpio_vbus->mA = mA;
77 }
78
79 /* VBUS change IRQ handler */
80 static irqreturn_t gpio_vbus_irq(int irq, void *data)
81 {
82         struct platform_device *pdev = data;
83         struct gpio_vbus_mach_info *pdata = pdev->dev.platform_data;
84         struct gpio_vbus_data *gpio_vbus = platform_get_drvdata(pdev);
85         int gpio, vbus;
86
87         vbus = gpio_get_value(pdata->gpio_vbus);
88         if (pdata->gpio_vbus_inverted)
89                 vbus = !vbus;
90
91         dev_dbg(&pdev->dev, "VBUS %s (gadget: %s)\n",
92                 vbus ? "supplied" : "inactive",
93                 gpio_vbus->otg.gadget ? gpio_vbus->otg.gadget->name : "none");
94
95         if (!gpio_vbus->otg.gadget)
96                 return IRQ_HANDLED;
97
98         /* Peripheral controllers which manage the pullup themselves won't have
99          * gpio_pullup configured here.  If it's configured here, we'll do what
100          * isp1301_omap::b_peripheral() does and enable the pullup here... although
101          * that may complicate usb_gadget_{,dis}connect() support.
102          */
103         gpio = pdata->gpio_pullup;
104         if (vbus) {
105                 gpio_vbus->otg.state = OTG_STATE_B_PERIPHERAL;
106                 usb_gadget_vbus_connect(gpio_vbus->otg.gadget);
107
108                 /* drawing a "unit load" is *always* OK, except for OTG */
109                 set_vbus_draw(gpio_vbus, 100);
110
111                 /* optionally enable D+ pullup */
112                 if (gpio_is_valid(gpio))
113                         gpio_set_value(gpio, !pdata->gpio_pullup_inverted);
114         } else {
115                 /* optionally disable D+ pullup */
116                 if (gpio_is_valid(gpio))
117                         gpio_set_value(gpio, pdata->gpio_pullup_inverted);
118
119                 set_vbus_draw(gpio_vbus, 0);
120
121                 usb_gadget_vbus_disconnect(gpio_vbus->otg.gadget);
122                 gpio_vbus->otg.state = OTG_STATE_B_IDLE;
123         }
124
125         return IRQ_HANDLED;
126 }
127
128 /* OTG transceiver interface */
129
130 /* bind/unbind the peripheral controller */
131 static int gpio_vbus_set_peripheral(struct otg_transceiver *otg,
132                                 struct usb_gadget *gadget)
133 {
134         struct gpio_vbus_data *gpio_vbus;
135         struct gpio_vbus_mach_info *pdata;
136         struct platform_device *pdev;
137         int gpio, irq;
138
139         gpio_vbus = container_of(otg, struct gpio_vbus_data, otg);
140         pdev = to_platform_device(gpio_vbus->dev);
141         pdata = gpio_vbus->dev->platform_data;
142         irq = gpio_to_irq(pdata->gpio_vbus);
143         gpio = pdata->gpio_pullup;
144
145         if (!gadget) {
146                 dev_dbg(&pdev->dev, "unregistering gadget '%s'\n",
147                         otg->gadget->name);
148
149                 /* optionally disable D+ pullup */
150                 if (gpio_is_valid(gpio))
151                         gpio_set_value(gpio, pdata->gpio_pullup_inverted);
152
153                 set_vbus_draw(gpio_vbus, 0);
154
155                 usb_gadget_vbus_disconnect(otg->gadget);
156                 otg->state = OTG_STATE_UNDEFINED;
157
158                 otg->gadget = NULL;
159                 return 0;
160         }
161
162         otg->gadget = gadget;
163         dev_dbg(&pdev->dev, "registered gadget '%s'\n", gadget->name);
164
165         /* initialize connection state */
166         gpio_vbus_irq(irq, pdev);
167         return 0;
168 }
169
170 /* effective for B devices, ignored for A-peripheral */
171 static int gpio_vbus_set_power(struct otg_transceiver *otg, unsigned mA)
172 {
173         struct gpio_vbus_data *gpio_vbus;
174
175         gpio_vbus = container_of(otg, struct gpio_vbus_data, otg);
176
177         if (otg->state == OTG_STATE_B_PERIPHERAL)
178                 set_vbus_draw(gpio_vbus, mA);
179         return 0;
180 }
181
182 /* for non-OTG B devices: set/clear transceiver suspend mode */
183 static int gpio_vbus_set_suspend(struct otg_transceiver *otg, int suspend)
184 {
185         struct gpio_vbus_data *gpio_vbus;
186
187         gpio_vbus = container_of(otg, struct gpio_vbus_data, otg);
188
189         /* draw max 0 mA from vbus in suspend mode; or the previously
190          * recorded amount of current if not suspended
191          *
192          * NOTE: high powered configs (mA > 100) may draw up to 2.5 mA
193          * if they're wake-enabled ... we don't handle that yet.
194          */
195         return gpio_vbus_set_power(otg, suspend ? 0 : gpio_vbus->mA);
196 }
197
198 /* platform driver interface */
199
200 static int __init gpio_vbus_probe(struct platform_device *pdev)
201 {
202         struct gpio_vbus_mach_info *pdata = pdev->dev.platform_data;
203         struct gpio_vbus_data *gpio_vbus;
204         struct resource *res;
205         int err, gpio, irq;
206
207         if (!pdata || !gpio_is_valid(pdata->gpio_vbus))
208                 return -EINVAL;
209         gpio = pdata->gpio_vbus;
210
211         gpio_vbus = kzalloc(sizeof(struct gpio_vbus_data), GFP_KERNEL);
212         if (!gpio_vbus)
213                 return -ENOMEM;
214
215         platform_set_drvdata(pdev, gpio_vbus);
216         gpio_vbus->dev = &pdev->dev;
217         gpio_vbus->otg.label = "gpio-vbus";
218         gpio_vbus->otg.state = OTG_STATE_UNDEFINED;
219         gpio_vbus->otg.set_peripheral = gpio_vbus_set_peripheral;
220         gpio_vbus->otg.set_power = gpio_vbus_set_power;
221         gpio_vbus->otg.set_suspend = gpio_vbus_set_suspend;
222
223         err = gpio_request(gpio, "vbus_detect");
224         if (err) {
225                 dev_err(&pdev->dev, "can't request vbus gpio %d, err: %d\n",
226                         gpio, err);
227                 goto err_gpio;
228         }
229         gpio_direction_input(gpio);
230
231         res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
232         if (res) {
233                 irq = res->start;
234                 res->flags &= IRQF_TRIGGER_MASK;
235                 res->flags |= IRQF_SAMPLE_RANDOM | IRQF_SHARED;
236         } else
237                 irq = gpio_to_irq(gpio);
238
239         /* if data line pullup is in use, initialize it to "not pulling up" */
240         gpio = pdata->gpio_pullup;
241         if (gpio_is_valid(gpio)) {
242                 err = gpio_request(gpio, "udc_pullup");
243                 if (err) {
244                         dev_err(&pdev->dev,
245                                 "can't request pullup gpio %d, err: %d\n",
246                                 gpio, err);
247                         gpio_free(pdata->gpio_vbus);
248                         goto err_gpio;
249                 }
250                 gpio_direction_output(gpio, pdata->gpio_pullup_inverted);
251         }
252
253         err = request_irq(irq, gpio_vbus_irq, VBUS_IRQ_FLAGS,
254                 "vbus_detect", pdev);
255         if (err) {
256                 dev_err(&pdev->dev, "can't request irq %i, err: %d\n",
257                         irq, err);
258                 goto err_irq;
259         }
260
261         /* only active when a gadget is registered */
262         err = otg_set_transceiver(&gpio_vbus->otg);
263         if (err) {
264                 dev_err(&pdev->dev, "can't register transceiver, err: %d\n",
265                         err);
266                 goto err_otg;
267         }
268
269         gpio_vbus->vbus_draw = regulator_get(&pdev->dev, "vbus_draw");
270         if (IS_ERR(gpio_vbus->vbus_draw)) {
271                 dev_dbg(&pdev->dev, "can't get vbus_draw regulator, err: %ld\n",
272                         PTR_ERR(gpio_vbus->vbus_draw));
273                 gpio_vbus->vbus_draw = NULL;
274         }
275
276         return 0;
277 err_otg:
278         free_irq(irq, &pdev->dev);
279 err_irq:
280         if (gpio_is_valid(pdata->gpio_pullup))
281                 gpio_free(pdata->gpio_pullup);
282         gpio_free(pdata->gpio_vbus);
283 err_gpio:
284         platform_set_drvdata(pdev, NULL);
285         kfree(gpio_vbus);
286         return err;
287 }
288
289 static int __exit gpio_vbus_remove(struct platform_device *pdev)
290 {
291         struct gpio_vbus_data *gpio_vbus = platform_get_drvdata(pdev);
292         struct gpio_vbus_mach_info *pdata = pdev->dev.platform_data;
293         int gpio = pdata->gpio_vbus;
294
295         regulator_put(gpio_vbus->vbus_draw);
296
297         otg_set_transceiver(NULL);
298
299         free_irq(gpio_to_irq(gpio), &pdev->dev);
300         if (gpio_is_valid(pdata->gpio_pullup))
301                 gpio_free(pdata->gpio_pullup);
302         gpio_free(gpio);
303         platform_set_drvdata(pdev, NULL);
304         kfree(gpio_vbus);
305
306         return 0;
307 }
308
309 /* NOTE:  the gpio-vbus device may *NOT* be hotplugged */
310
311 MODULE_ALIAS("platform:gpio-vbus");
312
313 static struct platform_driver gpio_vbus_driver = {
314         .driver = {
315                 .name  = "gpio-vbus",
316                 .owner = THIS_MODULE,
317         },
318         .remove  = __exit_p(gpio_vbus_remove),
319 };
320
321 static int __init gpio_vbus_init(void)
322 {
323         return platform_driver_probe(&gpio_vbus_driver, gpio_vbus_probe);
324 }
325 module_init(gpio_vbus_init);
326
327 static void __exit gpio_vbus_exit(void)
328 {
329         platform_driver_unregister(&gpio_vbus_driver);
330 }
331 module_exit(gpio_vbus_exit);
332
333 MODULE_DESCRIPTION("simple GPIO controlled OTG transceiver driver");
334 MODULE_AUTHOR("Philipp Zabel");
335 MODULE_LICENSE("GPL");