aboutsummaryrefslogtreecommitdiffstats
path: root/recipes/linux/linux-2.6.18/husb2_udc-test-mode.patch
blob: f87f71f805f0180d45b3c2bee37dd9dd41470c18 (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
From hskinnemoen@atmel.com Wed Jan 17 10:05:04 2007
Date: Wed, 17 Jan 2007 10:05:04 +0100
From: Haavard Skinnemoen <hskinnemoen@atmel.com>
Subject: [PATCH] Implement USB test modes for husb2_udc

This patch implements the four test modes defined by the USB 2.0
standard: Test_J, Test_K, Test_SE0_NAK and Test_Packet.

This patch also contains a couple of more or less unrelated bug fixes
and debug features:

  * Add "state" file to debugfs for control endpoint. This allows us to
    see which state the control logic got stuck in when things go bad.
  * REMOTE_WAKEUP requests are ignored instead of stalling the device.
  * Bad packet length causes warning message + stall instead of BUG().

Signed-off-by: Haavard Skinnemoen <hskinnemoen@atmel.com>
---
 drivers/usb/gadget/husb2_udc.c |  135 +++++++++++++++++++++++++++++++++++++++--
 drivers/usb/gadget/husb2_udc.h |    8 +-
 2 files changed, 135 insertions(+), 8 deletions(-)

Index: linux-2.6.18-avr32/drivers/usb/gadget/husb2_udc.c
===================================================================
--- linux-2.6.18-avr32.orig/drivers/usb/gadget/husb2_udc.c	2007-01-16 15:01:42.000000000 +0100
+++ linux-2.6.18-avr32/drivers/usb/gadget/husb2_udc.c	2007-01-17 09:56:24.000000000 +0100
@@ -254,9 +254,20 @@ static void husb2_ep_init_debugfs(struct
 		if (!ep->debugfs_dma_status)
 			goto err_dma_status;
 	}
+	if (ep_is_control(ep)) {
+		ep->debugfs_state
+			= debugfs_create_u32("state", 0400, ep_root,
+					     &ep->state);
+		if (!ep->debugfs_state)
+			goto err_state;
+	}
+
 
 	return;
 
+err_state:
+	if (ep_can_dma(ep))
+		debugfs_remove(ep->debugfs_dma_status);
 err_dma_status:
 	debugfs_remove(ep->debugfs_queue);
 err_queue:
@@ -270,6 +281,7 @@ static void husb2_ep_cleanup_debugfs(str
 {
 	debugfs_remove(ep->debugfs_queue);
 	debugfs_remove(ep->debugfs_dma_status);
+	debugfs_remove(ep->debugfs_state);
 	debugfs_remove(ep->debugfs_dir);
 	ep->debugfs_dma_status = NULL;
 	ep->debugfs_dir = NULL;
@@ -336,7 +348,7 @@ static inline void husb2_cleanup_debugfs
 }
 #endif
 
-static void copy_to_fifo(void __iomem *fifo, void *buf, int len)
+static void copy_to_fifo(void __iomem *fifo, const void *buf, int len)
 {
 	unsigned long tmp;
 
@@ -1302,6 +1314,90 @@ static inline void set_address(struct hu
 	husb2_writel(udc, CTRL, regval);
 }
 
+static int do_test_mode(struct husb2_udc *udc)
+{
+	static const char test_packet_buffer[] = {
+		/* JKJKJKJK * 9 */
+		0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+		/* JJKKJJKK * 8 */
+		0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,
+		/* JJKKJJKK * 8 */
+		0xEE,0xEE,0xEE,0xEE,0xEE,0xEE,0xEE,0xEE,
+		/* JJJJJJJKKKKKKK * 8 */
+		0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+		/* JJJJJJJK * 8 */
+		0x7F,0xBF,0xDF,0xEF,0xF7,0xFB,0xFD,
+		/* {JKKKKKKK * 10}, JK */
+		0xFC,0x7E,0xBF,0xDF,0xEF,0xF7,0xFB,0xFD,0x7E
+	};
+	struct husb2_ep *ep;
+	int test_mode;
+
+	test_mode = udc->test_mode;
+
+	/* Start from a clean slate */
+	reset_all_endpoints(udc);
+
+	switch (test_mode) {
+	case 0x0100:
+		/* Test_J */
+		husb2_writel(udc, TST, HUSB2_BIT(TST_J_MODE));
+		printk("udc: Entering Test_J mode...\n");
+		break;
+	case 0x0200:
+		/* Test_K */
+		husb2_writel(udc, TST, HUSB2_BIT(TST_K_MODE));
+		printk("udc: Entering Test_K mode...\n");
+		break;
+	case 0x0300:
+		/*
+		 * Test_SE0_NAK: Force high-speed mode and set up ep0
+		 * for Bulk IN transfers
+		 */
+		ep = &husb2_ep[0];
+		husb2_writel(udc, TST,
+			     HUSB2_BF(SPEED_CFG, HUSB2_SPEED_CFG_FORCE_HIGH));
+		husb2_ep_writel(ep, CFG,
+				HUSB2_BF(EPT_SIZE, HUSB2_EPT_SIZE_64)
+				| HUSB2_BIT(EPT_DIR)
+				| HUSB2_BF(EPT_TYPE, HUSB2_EPT_TYPE_BULK)
+				| HUSB2_BF(BK_NUMBER, 1));
+		if (!(husb2_ep_readl(ep, CFG) & HUSB2_BIT(EPT_MAPPED))) {
+			set_protocol_stall(udc, ep);
+			printk("udc: Test_SE0_NAK: ep0 not mapped\n");
+		} else {
+			husb2_ep_writel(ep, CTL_ENB, HUSB2_BIT(EPT_ENABLE));
+			printk("udc: Entering Test_SE0_NAK mode...\n");
+		}
+		break;
+	case 0x0400:
+		/* Test_Packet */
+		ep = &husb2_ep[0];
+		husb2_ep_writel(ep, CFG,
+				HUSB2_BF(EPT_SIZE, HUSB2_EPT_SIZE_64)
+				| HUSB2_BIT(EPT_DIR)
+				| HUSB2_BF(EPT_TYPE, HUSB2_EPT_TYPE_BULK)
+				| HUSB2_BF(BK_NUMBER, 1));
+		if (!(husb2_ep_readl(ep, CFG) & HUSB2_BIT(EPT_MAPPED))) {
+			set_protocol_stall(udc, ep);
+			printk("udc: Test_Packet: ep0 not mapped\n");
+		} else {
+			husb2_ep_writel(ep, CTL_ENB, HUSB2_BIT(EPT_ENABLE));
+			husb2_writel(udc, TST, HUSB2_BIT(TST_PKT_MODE));
+			copy_to_fifo(ep->fifo, test_packet_buffer,
+				     sizeof(test_packet_buffer));
+			husb2_ep_writel(ep, SET_STA, HUSB2_BIT(TX_PK_RDY));
+			printk("udc: Entering Test_Packet mode...\n");
+		}
+		break;
+	default:
+		printk("udc: Invalid test mode: 0x%04x\n", test_mode);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 static int handle_ep0_setup(struct husb2_udc *udc, struct husb2_ep *ep,
 			    struct usb_ctrlrequest *crq)
 {
@@ -1341,8 +1437,13 @@ static int handle_ep0_setup(struct husb2
 
 	case USB_REQ_CLEAR_FEATURE: {
 		if (crq->bRequestType == USB_RECIP_DEVICE) {
-			/* We don't support TEST_MODE */
-			goto stall;
+			if (crq->wValue
+			    == __constant_cpu_to_le16(USB_DEVICE_REMOTE_WAKEUP)) {
+				/* TODO: Handle REMOTE_WAKEUP */
+			} else {
+				/* CLEAR_FEATURE doesn't make sense for TEST_MODE */
+				goto stall;
+			}
 		} else if (crq->bRequestType == USB_RECIP_ENDPOINT) {
 			struct husb2_ep *target;
 
@@ -1365,8 +1466,18 @@ static int handle_ep0_setup(struct husb2
 
 	case USB_REQ_SET_FEATURE: {
 		if (crq->bRequestType == USB_RECIP_DEVICE) {
-			/* We don't support TEST_MODE */
-			goto stall;
+			if (crq->wValue
+			    == __constant_cpu_to_le16(USB_DEVICE_TEST_MODE)) {
+				send_status(udc, ep);
+				ep->state = STATUS_STAGE_TEST;
+				udc->test_mode = le16_to_cpu(crq->wIndex);
+				return 0;
+			} else if (crq->wValue
+				   == __constant_cpu_to_le16(USB_DEVICE_REMOTE_WAKEUP)) {
+				/* TODO: Handle REMOTE_WAKEUP */
+			} else {
+				goto stall;
+			}
 		} else if (crq->bRequestType == USB_RECIP_ENDPOINT) {
 			struct husb2_ep *target;
 
@@ -1476,6 +1587,12 @@ restart:
 					HUSB2_BIT(TX_COMPLETE));
 			ep->state = WAIT_FOR_SETUP;
 			break;
+		case STATUS_STAGE_TEST:
+			husb2_ep_writel(ep, CTL_DIS, HUSB2_BIT(TX_COMPLETE));
+			ep->state = WAIT_FOR_SETUP;
+			if (do_test_mode(udc))
+				set_protocol_stall(udc, ep);
+			break;
 		default:
 			printk(KERN_ERR
 			       "udc: %s: TXCOMP: Invalid endpoint state %d, "
@@ -1550,7 +1667,13 @@ restart:
 
 		pkt_len = HUSB2_BFEXT(BYTE_COUNT, husb2_ep_readl(ep, STA));
 		DBG(DBG_HW, "Packet length: %u\n", pkt_len);
-		BUG_ON(pkt_len != sizeof(crq));
+		if (pkt_len != sizeof(crq)) {
+			printk(KERN_WARNING
+			       "udc: Invalid packet length %u (expected %lu)\n",
+			       pkt_len, sizeof(crq));
+			set_protocol_stall(udc, ep);
+			return;
+		}
 
 		DBG(DBG_FIFO, "Copying ctrl request from 0x%p:\n", ep->fifo);
 		copy_from_fifo(crq.data, ep->fifo, sizeof(crq));
Index: linux-2.6.18-avr32/drivers/usb/gadget/husb2_udc.h
===================================================================
--- linux-2.6.18-avr32.orig/drivers/usb/gadget/husb2_udc.h	2007-01-16 15:01:42.000000000 +0100
+++ linux-2.6.18-avr32/drivers/usb/gadget/husb2_udc.h	2007-01-17 09:54:03.000000000 +0100
@@ -21,7 +21,7 @@
 #define HUSB2_TST_CNT_A				0x00d4
 #define HUSB2_TST_CNT_B				0x00d8
 #define HUSB2_TST_MODE_REG			0x00dc
-#define HUSB2_TST				0x00f0
+#define HUSB2_TST				0x00e0
 
 /* USB endpoint register offsets */
 #define HUSB2_EPT_CFG				0x0000
@@ -113,7 +113,7 @@
 #define HUSB2_TST_J_MODE_SIZE			1
 #define HUSB2_TST_K_MODE_OFFSET			3
 #define HUSB2_TST_K_MODE_SIZE			1
-#define HUSB2_TST_PKT_MODE_OFFSE		4
+#define HUSB2_TST_PKT_MODE_OFFSET		4
 #define HUSB2_TST_PKT_MODE_SIZE			1
 #define HUSB2_OPMODE2_OFFSET			5
 #define HUSB2_OPMODE2_SIZE			1
@@ -304,6 +304,7 @@ enum husb2_ctrl_state {
 	STATUS_STAGE_IN,
 	STATUS_STAGE_OUT,
 	STATUS_STAGE_ADDR,
+	STATUS_STAGE_TEST,
 };
 /*
   EP_STATE_IDLE,
@@ -343,6 +344,7 @@ struct husb2_ep {
 	struct dentry				*debugfs_dir;
 	struct dentry				*debugfs_queue;
 	struct dentry				*debugfs_dma_status;
+	struct dentry				*debugfs_state;
 #endif
 };
 #define HUSB2_EP_CAP_ISOC	0x0001
@@ -381,6 +383,8 @@ struct husb2_udc {
 	struct clk *pclk;
 	struct clk *hclk;
 
+	int test_mode;
+
 #ifdef CONFIG_DEBUG_FS
 	struct dentry *debugfs_root;
 	struct dentry *debugfs_regs;