diff options
author | David Brownell <david-b@pacbell.net> | 2006-06-29 15:25:39 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2006-09-27 14:58:48 -0400 |
commit | b2bbb20b37d734443d1c279d0033a64f6095db54 (patch) | |
tree | 067510f143dcb0e4f938f932994e5ac34f5da6b9 | |
parent | 3a16f7b4a75d68364c3278523f51ac141a12758a (diff) |
USB: pxa2xx_udc understands GPIO based VBUS sensing
This updates the PXA 25x UDC board-independent infrastructure for VBUS sensing
and the D+ pullup. The original code evolved from rather bizarre support on
Intel's "Lubbock" reference hardware, so that on more sensible hardware it
doesn't work as well as it could/should.
The change is just to teach the UDC driver how to use built-in PXA GPIO pins
directly. This reduces the amount of board-specfic object code needed, and
enables the use of a VBUS sensing IRQ on boards (like Gumstix) that have one.
With VBUS sensing, the UDC is unclocked until a host is actually connected.
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r-- | arch/arm/mach-pxa/corgi.c | 15 | ||||
-rw-r--r-- | drivers/usb/gadget/pxa2xx_udc.c | 70 | ||||
-rw-r--r-- | drivers/usb/gadget/pxa2xx_udc.h | 24 | ||||
-rw-r--r-- | include/asm-arm/arch-pxa/udc.h | 8 |
4 files changed, 85 insertions, 32 deletions
diff --git a/arch/arm/mach-pxa/corgi.c b/arch/arm/mach-pxa/corgi.c index cce26576999e..337c01c4ac37 100644 --- a/arch/arm/mach-pxa/corgi.c +++ b/arch/arm/mach-pxa/corgi.c | |||
@@ -284,21 +284,9 @@ static struct pxaficp_platform_data corgi_ficp_platform_data = { | |||
284 | /* | 284 | /* |
285 | * USB Device Controller | 285 | * USB Device Controller |
286 | */ | 286 | */ |
287 | static void corgi_udc_command(int cmd) | ||
288 | { | ||
289 | switch(cmd) { | ||
290 | case PXA2XX_UDC_CMD_CONNECT: | ||
291 | GPSR(CORGI_GPIO_USB_PULLUP) = GPIO_bit(CORGI_GPIO_USB_PULLUP); | ||
292 | break; | ||
293 | case PXA2XX_UDC_CMD_DISCONNECT: | ||
294 | GPCR(CORGI_GPIO_USB_PULLUP) = GPIO_bit(CORGI_GPIO_USB_PULLUP); | ||
295 | break; | ||
296 | } | ||
297 | } | ||
298 | |||
299 | static struct pxa2xx_udc_mach_info udc_info __initdata = { | 287 | static struct pxa2xx_udc_mach_info udc_info __initdata = { |
300 | /* no connect GPIO; corgi can't tell connection status */ | 288 | /* no connect GPIO; corgi can't tell connection status */ |
301 | .udc_command = corgi_udc_command, | 289 | .gpio_pullup = CORGI_GPIO_USB_PULLUP, |
302 | }; | 290 | }; |
303 | 291 | ||
304 | 292 | ||
@@ -350,7 +338,6 @@ static void __init corgi_init(void) | |||
350 | corgi_ssp_set_machinfo(&corgi_ssp_machinfo); | 338 | corgi_ssp_set_machinfo(&corgi_ssp_machinfo); |
351 | 339 | ||
352 | pxa_gpio_mode(CORGI_GPIO_IR_ON | GPIO_OUT); | 340 | pxa_gpio_mode(CORGI_GPIO_IR_ON | GPIO_OUT); |
353 | pxa_gpio_mode(CORGI_GPIO_USB_PULLUP | GPIO_OUT); | ||
354 | pxa_gpio_mode(CORGI_GPIO_HSYNC | GPIO_IN); | 341 | pxa_gpio_mode(CORGI_GPIO_HSYNC | GPIO_IN); |
355 | 342 | ||
356 | pxa_set_udc_info(&udc_info); | 343 | pxa_set_udc_info(&udc_info); |
diff --git a/drivers/usb/gadget/pxa2xx_udc.c b/drivers/usb/gadget/pxa2xx_udc.c index fff027d30a09..f1adcf8b2023 100644 --- a/drivers/usb/gadget/pxa2xx_udc.c +++ b/drivers/usb/gadget/pxa2xx_udc.c | |||
@@ -150,6 +150,39 @@ MODULE_PARM_DESC (fifo_mode, "pxa2xx udc fifo mode"); | |||
150 | static void pxa2xx_ep_fifo_flush (struct usb_ep *ep); | 150 | static void pxa2xx_ep_fifo_flush (struct usb_ep *ep); |
151 | static void nuke (struct pxa2xx_ep *, int status); | 151 | static void nuke (struct pxa2xx_ep *, int status); |
152 | 152 | ||
153 | /* one GPIO should be used to detect VBUS from the host */ | ||
154 | static int is_vbus_present(void) | ||
155 | { | ||
156 | struct pxa2xx_udc_mach_info *mach = the_controller->mach; | ||
157 | |||
158 | if (mach->gpio_vbus) | ||
159 | return pxa_gpio_get(mach->gpio_vbus); | ||
160 | if (mach->udc_is_connected) | ||
161 | return mach->udc_is_connected(); | ||
162 | return 1; | ||
163 | } | ||
164 | |||
165 | /* one GPIO should control a D+ pullup, so host sees this device (or not) */ | ||
166 | static void pullup_off(void) | ||
167 | { | ||
168 | struct pxa2xx_udc_mach_info *mach = the_controller->mach; | ||
169 | |||
170 | if (mach->gpio_pullup) | ||
171 | pxa_gpio_set(mach->gpio_pullup, 0); | ||
172 | else if (mach->udc_command) | ||
173 | mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT); | ||
174 | } | ||
175 | |||
176 | static void pullup_on(void) | ||
177 | { | ||
178 | struct pxa2xx_udc_mach_info *mach = the_controller->mach; | ||
179 | |||
180 | if (mach->gpio_pullup) | ||
181 | pxa_gpio_set(mach->gpio_pullup, 1); | ||
182 | else if (mach->udc_command) | ||
183 | mach->udc_command(PXA2XX_UDC_CMD_CONNECT); | ||
184 | } | ||
185 | |||
153 | static void pio_irq_enable(int bEndpointAddress) | 186 | static void pio_irq_enable(int bEndpointAddress) |
154 | { | 187 | { |
155 | bEndpointAddress &= 0xf; | 188 | bEndpointAddress &= 0xf; |
@@ -1721,6 +1754,16 @@ lubbock_vbus_irq(int irq, void *_dev, struct pt_regs *r) | |||
1721 | 1754 | ||
1722 | #endif | 1755 | #endif |
1723 | 1756 | ||
1757 | static irqreturn_t | ||
1758 | udc_vbus_irq(int irq, void *_dev, struct pt_regs *r) | ||
1759 | { | ||
1760 | struct pxa2xx_udc *dev = _dev; | ||
1761 | int vbus = pxa_gpio_get(dev->mach->gpio_vbus); | ||
1762 | |||
1763 | pxa2xx_udc_vbus_session(&dev->gadget, vbus); | ||
1764 | return IRQ_HANDLED; | ||
1765 | } | ||
1766 | |||
1724 | 1767 | ||
1725 | /*-------------------------------------------------------------------------*/ | 1768 | /*-------------------------------------------------------------------------*/ |
1726 | 1769 | ||
@@ -2438,7 +2481,7 @@ static struct pxa2xx_udc memory = { | |||
2438 | static int __init pxa2xx_udc_probe(struct platform_device *pdev) | 2481 | static int __init pxa2xx_udc_probe(struct platform_device *pdev) |
2439 | { | 2482 | { |
2440 | struct pxa2xx_udc *dev = &memory; | 2483 | struct pxa2xx_udc *dev = &memory; |
2441 | int retval, out_dma = 1; | 2484 | int retval, out_dma = 1, vbus_irq; |
2442 | u32 chiprev; | 2485 | u32 chiprev; |
2443 | 2486 | ||
2444 | /* insist on Intel/ARM/XScale */ | 2487 | /* insist on Intel/ARM/XScale */ |
@@ -2502,6 +2545,16 @@ static int __init pxa2xx_udc_probe(struct platform_device *pdev) | |||
2502 | /* other non-static parts of init */ | 2545 | /* other non-static parts of init */ |
2503 | dev->dev = &pdev->dev; | 2546 | dev->dev = &pdev->dev; |
2504 | dev->mach = pdev->dev.platform_data; | 2547 | dev->mach = pdev->dev.platform_data; |
2548 | if (dev->mach->gpio_vbus) { | ||
2549 | vbus_irq = IRQ_GPIO(dev->mach->gpio_vbus & GPIO_MD_MASK_NR); | ||
2550 | pxa_gpio_mode((dev->mach->gpio_vbus & GPIO_MD_MASK_NR) | ||
2551 | | GPIO_IN); | ||
2552 | set_irq_type(vbus_irq, IRQT_BOTHEDGE); | ||
2553 | } else | ||
2554 | vbus_irq = 0; | ||
2555 | if (dev->mach->gpio_pullup) | ||
2556 | pxa_gpio_mode((dev->mach->gpio_pullup & GPIO_MD_MASK_NR) | ||
2557 | | GPIO_OUT | GPIO_DFLT_LOW); | ||
2505 | 2558 | ||
2506 | init_timer(&dev->timer); | 2559 | init_timer(&dev->timer); |
2507 | dev->timer.function = udc_watchdog; | 2560 | dev->timer.function = udc_watchdog; |
@@ -2557,8 +2610,19 @@ lubbock_fail0: | |||
2557 | HEX_DISPLAY(dev->stats.irqs); | 2610 | HEX_DISPLAY(dev->stats.irqs); |
2558 | LUB_DISC_BLNK_LED &= 0xff; | 2611 | LUB_DISC_BLNK_LED &= 0xff; |
2559 | #endif | 2612 | #endif |
2560 | } | 2613 | } else |
2561 | #endif | 2614 | #endif |
2615 | if (vbus_irq) { | ||
2616 | retval = request_irq(vbus_irq, udc_vbus_irq, | ||
2617 | SA_INTERRUPT | SA_SAMPLE_RANDOM, | ||
2618 | driver_name, dev); | ||
2619 | if (retval != 0) { | ||
2620 | printk(KERN_ERR "%s: can't get irq %i, err %d\n", | ||
2621 | driver_name, vbus_irq, retval); | ||
2622 | free_irq(IRQ_USB, dev); | ||
2623 | return -EBUSY; | ||
2624 | } | ||
2625 | } | ||
2562 | create_proc_files(); | 2626 | create_proc_files(); |
2563 | 2627 | ||
2564 | return 0; | 2628 | return 0; |
@@ -2587,6 +2651,8 @@ static int __exit pxa2xx_udc_remove(struct platform_device *pdev) | |||
2587 | free_irq(LUBBOCK_USB_IRQ, dev); | 2651 | free_irq(LUBBOCK_USB_IRQ, dev); |
2588 | } | 2652 | } |
2589 | #endif | 2653 | #endif |
2654 | if (dev->mach->gpio_vbus) | ||
2655 | free_irq(IRQ_GPIO(dev->mach->gpio_vbus), dev); | ||
2590 | platform_set_drvdata(pdev, NULL); | 2656 | platform_set_drvdata(pdev, NULL); |
2591 | the_controller = NULL; | 2657 | the_controller = NULL; |
2592 | return 0; | 2658 | return 0; |
diff --git a/drivers/usb/gadget/pxa2xx_udc.h b/drivers/usb/gadget/pxa2xx_udc.h index 19a883f7d1b8..8e598c8bf4e3 100644 --- a/drivers/usb/gadget/pxa2xx_udc.h +++ b/drivers/usb/gadget/pxa2xx_udc.h | |||
@@ -177,27 +177,19 @@ struct pxa2xx_udc { | |||
177 | 177 | ||
178 | static struct pxa2xx_udc *the_controller; | 178 | static struct pxa2xx_udc *the_controller; |
179 | 179 | ||
180 | /* one GPIO should be used to detect VBUS from the host */ | 180 | static inline int pxa_gpio_get(unsigned gpio) |
181 | static inline int is_vbus_present(void) | ||
182 | { | 181 | { |
183 | if (!the_controller->mach->udc_is_connected) | 182 | return (GPLR(gpio) & GPIO_bit(gpio)) != 0; |
184 | return 1; | ||
185 | return the_controller->mach->udc_is_connected(); | ||
186 | } | 183 | } |
187 | 184 | ||
188 | /* one GPIO should control a D+ pullup, so host sees this device (or not) */ | 185 | static inline void pxa_gpio_set(unsigned gpio, int is_on) |
189 | static inline void pullup_off(void) | ||
190 | { | 186 | { |
191 | if (!the_controller->mach->udc_command) | 187 | int mask = GPIO_bit(gpio); |
192 | return; | ||
193 | the_controller->mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT); | ||
194 | } | ||
195 | 188 | ||
196 | static inline void pullup_on(void) | 189 | if (is_on) |
197 | { | 190 | GPSR(gpio) = mask; |
198 | if (!the_controller->mach->udc_command) | 191 | else |
199 | return; | 192 | GPCR(gpio) = mask; |
200 | the_controller->mach->udc_command(PXA2XX_UDC_CMD_CONNECT); | ||
201 | } | 193 | } |
202 | 194 | ||
203 | /*-------------------------------------------------------------------------*/ | 195 | /*-------------------------------------------------------------------------*/ |
diff --git a/include/asm-arm/arch-pxa/udc.h b/include/asm-arm/arch-pxa/udc.h index 30548a30c773..121cd241115d 100644 --- a/include/asm-arm/arch-pxa/udc.h +++ b/include/asm-arm/arch-pxa/udc.h | |||
@@ -12,6 +12,14 @@ struct pxa2xx_udc_mach_info { | |||
12 | void (*udc_command)(int cmd); | 12 | void (*udc_command)(int cmd); |
13 | #define PXA2XX_UDC_CMD_CONNECT 0 /* let host see us */ | 13 | #define PXA2XX_UDC_CMD_CONNECT 0 /* let host see us */ |
14 | #define PXA2XX_UDC_CMD_DISCONNECT 1 /* so host won't see us */ | 14 | #define PXA2XX_UDC_CMD_DISCONNECT 1 /* so host won't see us */ |
15 | |||
16 | /* Boards following the design guidelines in the developer's manual, | ||
17 | * with on-chip GPIOs not Lubbock's wierd hardware, can have a sane | ||
18 | * VBUS IRQ and omit the methods above. Store the GPIO number | ||
19 | * here; for GPIO 0, also mask in one of the pxa_gpio_mode() bits. | ||
20 | */ | ||
21 | u16 gpio_vbus; /* high == vbus present */ | ||
22 | u16 gpio_pullup; /* high == pullup activated */ | ||
15 | }; | 23 | }; |
16 | 24 | ||
17 | extern void pxa_set_udc_info(struct pxa2xx_udc_mach_info *info); | 25 | extern void pxa_set_udc_info(struct pxa2xx_udc_mach_info *info); |