summaryrefslogtreecommitdiff
path: root/packages/linux/linux-2.6.18/husb2_udc-test-mode.patch
diff options
context:
space:
mode:
Diffstat (limited to 'packages/linux/linux-2.6.18/husb2_udc-test-mode.patch')
-rw-r--r--packages/linux/linux-2.6.18/husb2_udc-test-mode.patch267
1 files changed, 267 insertions, 0 deletions
diff --git a/packages/linux/linux-2.6.18/husb2_udc-test-mode.patch b/packages/linux/linux-2.6.18/husb2_udc-test-mode.patch
new file mode 100644
index 0000000000..f87f71f805
--- /dev/null
+++ b/packages/linux/linux-2.6.18/husb2_udc-test-mode.patch
@@ -0,0 +1,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;