aboutsummaryrefslogtreecommitdiffstats
path: root/usb/usb-at91-udc-updates-mostly-power-management.patch
blob: f804ddf05ac399e5db95695dc118b279f9b45805 (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
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
From david-b@pacbell.net Mon Jun 19 14:31:06 2006
From: David Brownell <david-b@pacbell.net>
To: Greg KH <greg@kroah.com>
Subject: USB: AT91 UDC updates, mostly power management
Date: Mon, 19 Jun 2006 14:25:11 -0700
Cc: Andrew Victor <andrew@sanpeople.com>
Message-Id: <200606191425.11737.david-b@pacbell.net>


UDC updates for AT91 series processors:

 - Get ready for at91sam926x processors (ARMv5tej not ARMv4t)

 - Suspend/resume support now behaves properly

 - In "standby" mode, UDC can be a source of system wakeup events
   (host resume, device connect/disconnect, etc)

 - Fix a startup problem that crept in with the initial merge

And minor cleanups, especially whitespace.

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Andrew Victor <andrew@sanpeople.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>


---
 drivers/usb/gadget/at91_udc.c |  157 +++++++++++++++++++++++++-----------------
 drivers/usb/gadget/at91_udc.h |    1 
 2 files changed, 98 insertions(+), 60 deletions(-)

--- gregkh-2.6.orig/drivers/usb/gadget/at91_udc.c
+++ gregkh-2.6/drivers/usb/gadget/at91_udc.c
@@ -69,7 +69,7 @@
  * power.  During USB suspend, the 48 MHz clock is gated off.
  */
 
-#define	DRIVER_VERSION	"8 March 2005"
+#define	DRIVER_VERSION	"3 May 2006"
 
 static const char driver_name [] = "at91_udc";
 static const char ep0name[] = "ep0";
@@ -318,7 +318,8 @@ static void done(struct at91_ep *ep, str
  * that shouldn't normally be changed.
  */
 #define	SET_FX	(AT91_UDP_TXPKTRDY)
-#define	CLR_FX	(RX_DATA_READY | AT91_UDP_RXSETUP | AT91_UDP_STALLSENT | AT91_UDP_TXCOMP)
+#define	CLR_FX	(RX_DATA_READY | AT91_UDP_RXSETUP \
+		| AT91_UDP_STALLSENT | AT91_UDP_TXCOMP)
 
 /* pull OUT packet data from the endpoint's fifo */
 static int read_fifo (struct at91_ep *ep, struct at91_request *req)
@@ -472,7 +473,8 @@ static void nuke(struct at91_ep *ep, int
 
 /*-------------------------------------------------------------------------*/
 
-static int at91_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
+static int at91_ep_enable(struct usb_ep *_ep,
+				const struct usb_endpoint_descriptor *desc)
 {
 	struct at91_ep	*ep = container_of(_ep, struct at91_ep, ep);
 	struct at91_udc	*dev = ep->udc;
@@ -582,11 +584,12 @@ static int at91_ep_disable (struct usb_e
  * interesting for request or buffer allocation.
  */
 
-static struct usb_request *at91_ep_alloc_request (struct usb_ep *_ep, unsigned int gfp_flags)
+static struct usb_request *
+at91_ep_alloc_request(struct usb_ep *_ep, unsigned int gfp_flags)
 {
 	struct at91_request *req;
 
-	req = kcalloc(1, sizeof (struct at91_request), SLAB_KERNEL);
+	req = kcalloc(1, sizeof (struct at91_request), gfp_flags);
 	if (!req)
 		return NULL;
 
@@ -889,8 +892,8 @@ static void clk_off(struct at91_udc *udc
 		return;
 	udc->clocked = 0;
 	udc->gadget.speed = USB_SPEED_UNKNOWN;
-	clk_disable(udc->iclk);
 	clk_disable(udc->fclk);
+	clk_disable(udc->iclk);
 }
 
 /*
@@ -911,9 +914,6 @@ static void pullup(struct at91_udc *udc,
 		at91_udp_write(AT91_UDP_TXVC, AT91_UDP_TXVC_TXVDIS);
 		at91_set_gpio_value(udc->board.pullup_pin, 0);
 		clk_off(udc);
-
-		// REVISIT:  with transceiver disabled, will D- float
-		// so that a host would falsely detect a device?
 	}
 }
 
@@ -1290,7 +1290,8 @@ static void handle_ep0(struct at91_udc *
 			if (udc->wait_for_addr_ack) {
 				u32	tmp;
 
-				at91_udp_write(AT91_UDP_FADDR, AT91_UDP_FEN | udc->addr);
+				at91_udp_write(AT91_UDP_FADDR,
+						AT91_UDP_FEN | udc->addr);
 				tmp = at91_udp_read(AT91_UDP_GLB_STAT);
 				tmp &= ~AT91_UDP_FADDEN;
 				if (udc->addr)
@@ -1361,9 +1362,10 @@ static irqreturn_t at91_udc_irq (int irq
 	u32			rescans = 5;
 
 	while (rescans--) {
-		u32	status = at91_udp_read(AT91_UDP_ISR);
+		u32 status;
 
-		status &= at91_udp_read(AT91_UDP_IMR);
+		status = at91_udp_read(AT91_UDP_ISR)
+			& at91_udp_read(AT91_UDP_IMR);
 		if (!status)
 			break;
 
@@ -1379,18 +1381,17 @@ static irqreturn_t at91_udc_irq (int irq
 			stop_activity(udc);
 
 			/* enable ep0 */
-			at91_udp_write(AT91_UDP_CSR(0), AT91_UDP_EPEDS | AT91_UDP_EPTYPE_CTRL);
+			at91_udp_write(AT91_UDP_CSR(0),
+					AT91_UDP_EPEDS | AT91_UDP_EPTYPE_CTRL);
 			udc->gadget.speed = USB_SPEED_FULL;
 			udc->suspended = 0;
 			at91_udp_write(AT91_UDP_IER, AT91_UDP_EP(0));
 
 			/*
 			 * NOTE:  this driver keeps clocks off unless the
-			 * USB host is present.  That saves power, and also
-			 * eliminates IRQs (reset, resume, suspend) that can
-			 * otherwise flood from the controller.  If your
-			 * board doesn't support VBUS detection, suspend and
-			 * resume irq logic may need more attention...
+			 * USB host is present.  That saves power, but for
+			 * boards that don't support VBUS detection, both
+			 * clocks need to be active most of the time.
 			 */
 
 		/* host initiated suspend (3+ms bus idle) */
@@ -1452,13 +1453,19 @@ static irqreturn_t at91_udc_irq (int irq
 
 /*-------------------------------------------------------------------------*/
 
+static void nop_release(struct device *dev)
+{
+	/* nothing to free */
+}
+
 static struct at91_udc controller = {
 	.gadget = {
-		.ops = &at91_udc_ops,
-		.ep0 = &controller.ep[0].ep,
-		.name = driver_name,
-		.dev = {
-			.bus_id = "gadget"
+		.ops	= &at91_udc_ops,
+		.ep0	= &controller.ep[0].ep,
+		.name	= driver_name,
+		.dev	= {
+			.bus_id = "gadget",
+			.release = nop_release,
 		}
 	},
 	.ep[0] = {
@@ -1468,7 +1475,8 @@ static struct at91_udc controller = {
 		},
 		.udc		= &controller,
 		.maxpacket	= 8,
-		.creg		= (void __iomem *)(AT91_VA_BASE_UDP + AT91_UDP_CSR(0)),
+		.creg		= (void __iomem *)(AT91_VA_BASE_UDP
+					+ AT91_UDP_CSR(0)),
 		.int_mask	= 1 << 0,
 	},
 	.ep[1] = {
@@ -1479,7 +1487,8 @@ static struct at91_udc controller = {
 		.udc		= &controller,
 		.is_pingpong	= 1,
 		.maxpacket	= 64,
-		.creg		= (void __iomem *)(AT91_VA_BASE_UDP + AT91_UDP_CSR(1)),
+		.creg		= (void __iomem *)(AT91_VA_BASE_UDP
+					+ AT91_UDP_CSR(1)),
 		.int_mask	= 1 << 1,
 	},
 	.ep[2] = {
@@ -1490,7 +1499,8 @@ static struct at91_udc controller = {
 		.udc		= &controller,
 		.is_pingpong	= 1,
 		.maxpacket	= 64,
-		.creg		= (void __iomem *)(AT91_VA_BASE_UDP + AT91_UDP_CSR(2)),
+		.creg		= (void __iomem *)(AT91_VA_BASE_UDP
+					+ AT91_UDP_CSR(2)),
 		.int_mask	= 1 << 2,
 	},
 	.ep[3] = {
@@ -1501,7 +1511,8 @@ static struct at91_udc controller = {
 		},
 		.udc		= &controller,
 		.maxpacket	= 8,
-		.creg		= (void __iomem *)(AT91_VA_BASE_UDP + AT91_UDP_CSR(3)),
+		.creg		= (void __iomem *)(AT91_VA_BASE_UDP
+					+ AT91_UDP_CSR(3)),
 		.int_mask	= 1 << 3,
 	},
 	.ep[4] = {
@@ -1512,7 +1523,8 @@ static struct at91_udc controller = {
 		.udc		= &controller,
 		.is_pingpong	= 1,
 		.maxpacket	= 256,
-		.creg		= (void __iomem *)(AT91_VA_BASE_UDP + AT91_UDP_CSR(4)),
+		.creg		= (void __iomem *)(AT91_VA_BASE_UDP
+					+ AT91_UDP_CSR(4)),
 		.int_mask	= 1 << 4,
 	},
 	.ep[5] = {
@@ -1523,10 +1535,11 @@ static struct at91_udc controller = {
 		.udc		= &controller,
 		.is_pingpong	= 1,
 		.maxpacket	= 256,
-		.creg		= (void __iomem *)(AT91_VA_BASE_UDP + AT91_UDP_CSR(5)),
+		.creg		= (void __iomem *)(AT91_VA_BASE_UDP
+					+ AT91_UDP_CSR(5)),
 		.int_mask	= 1 << 5,
 	},
-	/* ep6 and ep7 are also reserved */
+	/* ep6 and ep7 are also reserved (custom silicon might use them) */
 };
 
 static irqreturn_t at91_vbus_irq(int irq, void *_udc, struct pt_regs *r)
@@ -1624,6 +1637,16 @@ static int __devinit at91udc_probe(struc
 		return -ENODEV;
 	}
 
+	if (pdev->num_resources != 2) {
+		DBG("invalid num_resources");
+		return -ENODEV;
+	}
+	if ((pdev->resource[0].flags != IORESOURCE_MEM)
+			|| (pdev->resource[1].flags != IORESOURCE_IRQ)) {
+		DBG("invalid resource type");
+		return -ENODEV;
+	}
+
 	if (!request_mem_region(AT91_BASE_UDP, SZ_16K, driver_name)) {
 		DBG("someone's using UDC memory\n");
 		return -EBUSY;
@@ -1649,19 +1672,26 @@ static int __devinit at91udc_probe(struc
 	if (retval < 0)
 		goto fail0;
 
-	/* disable everything until there's a gadget driver and vbus */
-	pullup(udc, 0);
+	/* don't do anything until we have both gadget driver and VBUS */
+	clk_enable(udc->iclk);
+	at91_udp_write(AT91_UDP_TXVC, AT91_UDP_TXVC_TXVDIS);
+	at91_udp_write(AT91_UDP_IDR, 0xffffffff);
+	clk_disable(udc->iclk);
 
 	/* request UDC and maybe VBUS irqs */
-	if (request_irq(AT91_ID_UDP, at91_udc_irq, IRQF_DISABLED, driver_name, udc)) {
-		DBG("request irq %d failed\n", AT91_ID_UDP);
+	udc->udp_irq = platform_get_irq(pdev, 0);
+	if (request_irq(udc->udp_irq, at91_udc_irq,
+			IRQF_DISABLED, driver_name, udc)) {
+		DBG("request irq %d failed\n", udc->udp_irq);
 		retval = -EBUSY;
 		goto fail1;
 	}
 	if (udc->board.vbus_pin > 0) {
-		if (request_irq(udc->board.vbus_pin, at91_vbus_irq, IRQF_DISABLED, driver_name, udc)) {
-			DBG("request vbus irq %d failed\n", udc->board.vbus_pin);
-			free_irq(AT91_ID_UDP, udc);
+		if (request_irq(udc->board.vbus_pin, at91_vbus_irq,
+				IRQF_DISABLED, driver_name, udc)) {
+			DBG("request vbus irq %d failed\n",
+					udc->board.vbus_pin);
+			free_irq(udc->udp_irq, udc);
 			retval = -EBUSY;
 			goto fail1;
 		}
@@ -1670,6 +1700,7 @@ static int __devinit at91udc_probe(struc
 		udc->vbus = 1;
 	}
 	dev_set_drvdata(dev, udc);
+	device_init_wakeup(dev, 1);
 	create_debug_file(udc);
 
 	INFO("%s version %s\n", driver_name, DRIVER_VERSION);
@@ -1678,14 +1709,14 @@ static int __devinit at91udc_probe(struc
 fail1:
 	device_unregister(&udc->gadget.dev);
 fail0:
-	release_mem_region(AT91_VA_BASE_UDP, SZ_16K);
+	release_mem_region(AT91_BASE_UDP, SZ_16K);
 	DBG("%s probe failed, %d\n", driver_name, retval);
 	return retval;
 }
 
-static int __devexit at91udc_remove(struct platform_device *dev)
+static int __devexit at91udc_remove(struct platform_device *pdev)
 {
-	struct at91_udc *udc = platform_get_drvdata(dev);
+	struct at91_udc *udc = platform_get_drvdata(pdev);
 
 	DBG("remove\n");
 
@@ -1694,10 +1725,11 @@ static int __devexit at91udc_remove(stru
 	if (udc->driver != 0)
 		usb_gadget_unregister_driver(udc->driver);
 
+	device_init_wakeup(&pdev->dev, 0);
 	remove_debug_file(udc);
 	if (udc->board.vbus_pin > 0)
 		free_irq(udc->board.vbus_pin, udc);
-	free_irq(AT91_ID_UDP, udc);
+	free_irq(udc->udp_irq, udc);
 	device_unregister(&udc->gadget.dev);
 	release_mem_region(AT91_BASE_UDP, SZ_16K);
 
@@ -1708,31 +1740,36 @@ static int __devexit at91udc_remove(stru
 }
 
 #ifdef CONFIG_PM
-static int at91udc_suspend(struct platform_device *dev, pm_message_t mesg)
+static int at91udc_suspend(struct platform_device *pdev, pm_message_t mesg)
 {
-	struct at91_udc *udc = platform_get_drvdata(dev);
+	struct at91_udc *udc = platform_get_drvdata(pdev);
+	int		wake = udc->driver && device_may_wakeup(&pdev->dev);
 
-	/*
-	 * The "safe" suspend transitions are opportunistic ... e.g. when
-	 * the USB link is suspended (48MHz clock autogated off), or when
-	 * it's disconnected (programmatically gated off, elsewhere).
-	 * Then we can suspend, and the chip can enter slow clock mode.
-	 *
-	 * The problem case is some component (user mode?) suspending this
-	 * device while it's active, with the 48 MHz clock in use.  There
-	 * are two basic approaches:  (a) veto suspend levels involving slow
-	 * clock mode, (b) disconnect, so 48 MHz will no longer be in use
-	 * and we can enter slow clock mode.  This uses (b) for now, since
-	 * it's simplest until AT91 PM exists and supports the other option.
+	/* Unless we can act normally to the host (letting it wake us up
+	 * whenever it has work for us) force disconnect.  Wakeup requires
+	 * PLLB for USB events (signaling for reset, wakeup, or incoming
+	 * tokens) and VBUS irqs (on systems which support them).
 	 */
-	if (udc->vbus && !udc->suspended)
+	if ((!udc->suspended && udc->addr)
+			|| !wake
+			|| at91_suspend_entering_slow_clock()) {
 		pullup(udc, 0);
+		disable_irq_wake(udc->udp_irq);
+	} else
+		enable_irq_wake(udc->udp_irq);
+
+	if (udc->board.vbus_pin > 0) {
+		if (wake)
+			enable_irq_wake(udc->board.vbus_pin);
+		else
+			disable_irq_wake(udc->board.vbus_pin);
+	}
 	return 0;
 }
 
-static int at91udc_resume(struct platform_device *dev)
+static int at91udc_resume(struct platform_device *pdev)
 {
-	struct at91_udc *udc = platform_get_drvdata(dev);
+	struct at91_udc *udc = platform_get_drvdata(pdev);
 
 	/* maybe reconnect to host; if so, clocks on */
 	pullup(udc, 1);
@@ -1748,7 +1785,7 @@ static struct platform_driver at91_udc =
 	.remove		= __devexit_p(at91udc_remove),
 	.shutdown	= at91udc_shutdown,
 	.suspend	= at91udc_suspend,
-	.resume 	= at91udc_resume,
+	.resume		= at91udc_resume,
 	.driver		= {
 		.name	= (char *) driver_name,
 		.owner	= THIS_MODULE,
@@ -1767,6 +1804,6 @@ static void __devexit udc_exit_module(vo
 }
 module_exit(udc_exit_module);
 
-MODULE_DESCRIPTION("AT91RM9200 udc driver");
+MODULE_DESCRIPTION("AT91 udc driver");
 MODULE_AUTHOR("Thomas Rathbone, David Brownell");
 MODULE_LICENSE("GPL");
--- gregkh-2.6.orig/drivers/usb/gadget/at91_udc.h
+++ gregkh-2.6/drivers/usb/gadget/at91_udc.h
@@ -141,6 +141,7 @@ struct at91_udc {
 	struct clk			*iclk, *fclk;
 	struct platform_device		*pdev;
 	struct proc_dir_entry		*pde;
+	int				udp_irq;
 };
 
 static inline struct at91_udc *to_udc(struct usb_gadget *g)