aboutsummaryrefslogtreecommitdiffstats
path: root/usb/usb-pxa2xx_udc-understands-gpio-based-vbus-sensing.patch
blob: f7a45238340231a2c424b7db73a38a0838e2eb49 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
From david-b@pacbell.net Thu Jun 29 14:02:38 2006
From: David Brownell <david-b@pacbell.net>
To: Greg KH <greg@kroah.com>
Subject: USB: pxa2xx_udc understands GPIO based VBUS sensing
Date: Thu, 29 Jun 2006 12:25:39 -0700
Cc: linux-usb-devel@lists.sourceforge.net, Dave Hylands <dhylands@gmail.com>
Message-Id: <200606291225.39983.david-b@pacbell.net>

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>

---
 arch/arm/mach-pxa/corgi.c       |   15 --------
 drivers/usb/gadget/pxa2xx_udc.c |   70 ++++++++++++++++++++++++++++++++++++++--
 drivers/usb/gadget/pxa2xx_udc.h |   24 ++++---------
 include/asm-arm/arch-pxa/udc.h  |    8 ++++
 4 files changed, 85 insertions(+), 32 deletions(-)

--- gregkh-2.6.orig/arch/arm/mach-pxa/corgi.c
+++ gregkh-2.6/arch/arm/mach-pxa/corgi.c
@@ -284,21 +284,9 @@ static struct pxaficp_platform_data corg
 /*
  * USB Device Controller
  */
-static void corgi_udc_command(int cmd)
-{
-	switch(cmd)	{
-	case PXA2XX_UDC_CMD_CONNECT:
-		GPSR(CORGI_GPIO_USB_PULLUP) = GPIO_bit(CORGI_GPIO_USB_PULLUP);
-		break;
-	case PXA2XX_UDC_CMD_DISCONNECT:
-		GPCR(CORGI_GPIO_USB_PULLUP) = GPIO_bit(CORGI_GPIO_USB_PULLUP);
-		break;
-	}
-}
-
 static struct pxa2xx_udc_mach_info udc_info __initdata = {
 	/* no connect GPIO; corgi can't tell connection status */
-	.udc_command		= corgi_udc_command,
+	.gpio_pullup		= CORGI_GPIO_USB_PULLUP,
 };
 
 
@@ -350,7 +338,6 @@ static void __init corgi_init(void)
 	corgi_ssp_set_machinfo(&corgi_ssp_machinfo);
 
 	pxa_gpio_mode(CORGI_GPIO_IR_ON | GPIO_OUT);
-	pxa_gpio_mode(CORGI_GPIO_USB_PULLUP | GPIO_OUT);
 	pxa_gpio_mode(CORGI_GPIO_HSYNC | GPIO_IN);
 
  	pxa_set_udc_info(&udc_info);
--- gregkh-2.6.orig/drivers/usb/gadget/pxa2xx_udc.c
+++ gregkh-2.6/drivers/usb/gadget/pxa2xx_udc.c
@@ -150,6 +150,39 @@ MODULE_PARM_DESC (fifo_mode, "pxa2xx udc
 static void pxa2xx_ep_fifo_flush (struct usb_ep *ep);
 static void nuke (struct pxa2xx_ep *, int status);
 
+/* one GPIO should be used to detect VBUS from the host */
+static int is_vbus_present(void)
+{
+	struct pxa2xx_udc_mach_info		*mach = the_controller->mach;
+
+	if (mach->gpio_vbus)
+		return pxa_gpio_get(mach->gpio_vbus);
+	if (mach->udc_is_connected)
+		return mach->udc_is_connected();
+	return 1;
+}
+
+/* one GPIO should control a D+ pullup, so host sees this device (or not) */
+static void pullup_off(void)
+{
+	struct pxa2xx_udc_mach_info		*mach = the_controller->mach;
+
+	if (mach->gpio_pullup)
+		pxa_gpio_set(mach->gpio_pullup, 0);
+	else if (mach->udc_command)
+		mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT);
+}
+
+static void pullup_on(void)
+{
+	struct pxa2xx_udc_mach_info		*mach = the_controller->mach;
+
+	if (mach->gpio_pullup)
+		pxa_gpio_set(mach->gpio_pullup, 1);
+	else if (mach->udc_command)
+		mach->udc_command(PXA2XX_UDC_CMD_CONNECT);
+}
+
 static void pio_irq_enable(int bEndpointAddress)
 {
         bEndpointAddress &= 0xf;
@@ -1721,6 +1754,16 @@ lubbock_vbus_irq(int irq, void *_dev, st
 
 #endif
 
+static irqreturn_t
+udc_vbus_irq(int irq, void *_dev, struct pt_regs *r)
+{
+	struct pxa2xx_udc	*dev = _dev;
+	int			vbus = pxa_gpio_get(dev->mach->gpio_vbus);
+
+	pxa2xx_udc_vbus_session(&dev->gadget, vbus);
+	return IRQ_HANDLED;
+}
+
 
 /*-------------------------------------------------------------------------*/
 
@@ -2438,7 +2481,7 @@ static struct pxa2xx_udc memory = {
 static int __init pxa2xx_udc_probe(struct platform_device *pdev)
 {
 	struct pxa2xx_udc *dev = &memory;
-	int retval, out_dma = 1;
+	int retval, out_dma = 1, vbus_irq;
 	u32 chiprev;
 
 	/* insist on Intel/ARM/XScale */
@@ -2502,6 +2545,16 @@ static int __init pxa2xx_udc_probe(struc
 	/* other non-static parts of init */
 	dev->dev = &pdev->dev;
 	dev->mach = pdev->dev.platform_data;
+	if (dev->mach->gpio_vbus) {
+		vbus_irq = IRQ_GPIO(dev->mach->gpio_vbus & GPIO_MD_MASK_NR);
+		pxa_gpio_mode((dev->mach->gpio_vbus & GPIO_MD_MASK_NR)
+				| GPIO_IN);
+		set_irq_type(vbus_irq, IRQT_BOTHEDGE);
+	} else
+		vbus_irq = 0;
+	if (dev->mach->gpio_pullup)
+		pxa_gpio_mode((dev->mach->gpio_pullup & GPIO_MD_MASK_NR)
+				| GPIO_OUT | GPIO_DFLT_LOW);
 
 	init_timer(&dev->timer);
 	dev->timer.function = udc_watchdog;
@@ -2557,8 +2610,19 @@ lubbock_fail0:
 		HEX_DISPLAY(dev->stats.irqs);
 		LUB_DISC_BLNK_LED &= 0xff;
 #endif
-	}
+	} else
 #endif
+	if (vbus_irq) {
+		retval = request_irq(vbus_irq, udc_vbus_irq,
+				SA_INTERRUPT | SA_SAMPLE_RANDOM,
+				driver_name, dev);
+		if (retval != 0) {
+			printk(KERN_ERR "%s: can't get irq %i, err %d\n",
+				driver_name, vbus_irq, retval);
+			free_irq(IRQ_USB, dev);
+			return -EBUSY;
+		}
+	}
 	create_proc_files();
 
 	return 0;
@@ -2587,6 +2651,8 @@ static int __exit pxa2xx_udc_remove(stru
 		free_irq(LUBBOCK_USB_IRQ, dev);
 	}
 #endif
+	if (dev->mach->gpio_vbus)
+		free_irq(IRQ_GPIO(dev->mach->gpio_vbus), dev);
 	platform_set_drvdata(pdev, NULL);
 	the_controller = NULL;
 	return 0;
--- gregkh-2.6.orig/drivers/usb/gadget/pxa2xx_udc.h
+++ gregkh-2.6/drivers/usb/gadget/pxa2xx_udc.h
@@ -177,27 +177,19 @@ struct pxa2xx_udc {
 
 static struct pxa2xx_udc *the_controller;
 
-/* one GPIO should be used to detect VBUS from the host */
-static inline int is_vbus_present(void)
+static inline int pxa_gpio_get(unsigned gpio)
 {
-	if (!the_controller->mach->udc_is_connected)
-		return 1;
-	return the_controller->mach->udc_is_connected();
+	return (GPLR(gpio) & GPIO_bit(gpio)) != 0;
 }
 
-/* one GPIO should control a D+ pullup, so host sees this device (or not) */
-static inline void pullup_off(void)
+static inline void pxa_gpio_set(unsigned gpio, int is_on)
 {
-	if (!the_controller->mach->udc_command)
-		return;
-	the_controller->mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT);
-}
+	int mask = GPIO_bit(gpio);
 
-static inline void pullup_on(void)
-{
-	if (!the_controller->mach->udc_command)
-		return;
-	the_controller->mach->udc_command(PXA2XX_UDC_CMD_CONNECT);
+	if (is_on)
+		GPSR(gpio) = mask;
+	else
+		GPCR(gpio) = mask;
 }
 
 /*-------------------------------------------------------------------------*/
--- gregkh-2.6.orig/include/asm-arm/arch-pxa/udc.h
+++ gregkh-2.6/include/asm-arm/arch-pxa/udc.h
@@ -12,6 +12,14 @@ struct pxa2xx_udc_mach_info {
         void (*udc_command)(int cmd);
 #define	PXA2XX_UDC_CMD_CONNECT		0	/* let host see us */
 #define	PXA2XX_UDC_CMD_DISCONNECT	1	/* so host won't see us */
+
+	/* Boards following the design guidelines in the developer's manual,
+	 * with on-chip GPIOs not Lubbock's wierd hardware, can have a sane
+	 * VBUS IRQ and omit the methods above.  Store the GPIO number
+	 * here; for GPIO 0, also mask in one of the pxa_gpio_mode() bits.
+	 */
+	u16	gpio_vbus;			/* high == vbus present */
+	u16	gpio_pullup;			/* high == pullup activated */
 };
 
 extern void pxa_set_udc_info(struct pxa2xx_udc_mach_info *info);