From d774220d605d32258591a9c9e4dc94f9932bd70f Mon Sep 17 00:00:00 2001 From: Jay Cheng Date: Thu, 10 Jun 2010 16:27:56 -0400 Subject: [PATCH] usb: gadget: fsl_udc: Add support for USB test mode feature Adding support for USB-IF High Speed electrical test mode for device mode. Support added for electrical test modes: 1. TEST_J 2. TEST_K 3. TEST_SE0_NAK 4. TEST_PACKET 5. TEST_FORCE_ENABLE originally fixed by Venkat Moganty Change-Id: If5a4dcf9eb81dc368f24c660460d35495b6a4253 Signed-off-by: Jay Cheng --- drivers/usb/gadget/fsl_udc_core.c | 132 +++++++++++++++++++++++++++++- 1 file changed, 131 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c index 0619c4f7be24..20fab22cc13b 100644 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/fsl_udc_core.c @@ -94,6 +94,25 @@ static int reset_queues(struct fsl_udc *udc); #define fsl_writel(val32, addr) writel(val32, addr) #endif +/* + * High speed test mode packet(53 bytes). + * See USB 2.0 spec, section 7.1.20. + */ +static const u8 fsl_udc_test_packet[53] = { + /* JKJKJKJK x9 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* JJKKJJKK x8 */ + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + /* JJJJKKKK x8 */ + 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, + /* JJJJJJJKKKKKKK x8 */ + 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /* JJJJJJJK x8 */ + 0x7f, 0xbf, 0xdf, 0xef, 0xf7, 0xfb, 0xfd, + /* JKKKKKKK x10, JK */ + 0xfc, 0x7e, 0xbf, 0xdf, 0xef, 0xf7, 0xfb, 0xfd, 0x7e +}; + /******************************************************************** * Internal Used Function ********************************************************************/ @@ -1342,6 +1361,107 @@ stall: ep0stall(udc); } +static void udc_test_mode(struct fsl_udc *udc, u32 test_mode) +{ + struct fsl_req *req; + struct fsl_ep *ep; + u32 portsc, bitmask; + unsigned long timeout; + + /* Ack the ep0 IN */ + if (ep0_prime_status(udc, EP_DIR_IN)) + ep0stall(udc); + + /* get the ep0 */ + ep = &udc->eps[0]; + bitmask = ep_is_in(ep) + ? (1 << (ep_index(ep) + 16)) + : (1 << (ep_index(ep))); + + timeout = jiffies + HZ; + /* Wait until ep0 IN endpoint txfr is complete */ + while (!(fsl_readl(&dr_regs->endptcomplete) & bitmask)) { + if (time_after(jiffies, timeout)) { + pr_err("Timeout for Ep0 IN Ack\n"); + break; + } + cpu_relax(); + } + + switch (test_mode << PORTSCX_PTC_BIT_POS) { + case PORTSCX_PTC_JSTATE: + VDBG("TEST_J\n"); + break; + case PORTSCX_PTC_KSTATE: + VDBG("TEST_K\n"); + break; + case PORTSCX_PTC_SEQNAK: + VDBG("TEST_SE0_NAK\n"); + break; + case PORTSCX_PTC_PACKET: + VDBG("TEST_PACKET\n"); + + /* get the ep and configure for IN direction */ + ep = &udc->eps[0]; + udc->ep0_dir = USB_DIR_IN; + + /* Initialize ep0 status request structure */ + req = container_of(fsl_alloc_request(NULL, GFP_ATOMIC), + struct fsl_req, req); + /* allocate a small amount of memory to get valid address */ + req->req.buf = kmalloc(sizeof(fsl_udc_test_packet), GFP_ATOMIC); + req->req.dma = virt_to_phys(req->req.buf); + + /* Fill in the reqest structure */ + memcpy(req->req.buf, fsl_udc_test_packet, sizeof(fsl_udc_test_packet)); + req->ep = ep; + req->req.length = sizeof(fsl_udc_test_packet); + req->req.status = -EINPROGRESS; + req->req.actual = 0; + req->req.complete = NULL; + req->dtd_count = 0; + req->mapped = 0; + + dma_sync_single_for_device(ep->udc->gadget.dev.parent, + req->req.dma, req->req.length, + ep_is_in(ep) + ? DMA_TO_DEVICE + : DMA_FROM_DEVICE); + + /* prime the data phase */ + if ((fsl_req_to_dtd(req, GFP_ATOMIC) == 0)) + fsl_queue_td(ep, req); + else /* no mem */ + goto stall; + + list_add_tail(&req->queue, &ep->queue); + udc->ep0_state = DATA_STATE_XMIT; + break; + case PORTSCX_PTC_FORCE_EN: + VDBG("TEST_FORCE_EN\n"); + break; + default: + ERR("udc unknown test mode[%d]!\n", test_mode); + goto stall; + } + + /* read the portsc register */ + portsc = fsl_readl(&dr_regs->portsc1); + /* set the test mode selector */ + portsc |= test_mode << PORTSCX_PTC_BIT_POS; + fsl_writel(portsc, &dr_regs->portsc1); + + /* + * The device must have its power cycled to exit test mode. + * See USB 2.0 spec, section 9.4.9 for test modes operation in "Set Feature" + * See USB 2.0 spec, section 7.1.20 for test modes. + */ + pr_info("udc entering the test mode, power cycle to exit test mode\n"); + return; +stall: + ep0stall(udc); +} + static void setup_received_irq(struct fsl_udc *udc, struct usb_ctrlrequest *setup) { @@ -1375,7 +1495,17 @@ static void setup_received_irq(struct fsl_udc *udc, { int rc = -EOPNOTSUPP; - if ((setup->bRequestType & (USB_RECIP_MASK | USB_TYPE_MASK)) + if (setup->bRequestType == USB_RECIP_DEVICE && + wValue == USB_DEVICE_TEST_MODE) { + /* + * If the feature selector is TEST_MODE, then the most + * significant byte of wIndex is used to specify the specific + * test mode and the lower byte of wIndex must be zero. + */ + udc_test_mode(udc, wIndex >> 8); + return; + + } else if ((setup->bRequestType & (USB_RECIP_MASK | USB_TYPE_MASK)) == (USB_RECIP_ENDPOINT | USB_TYPE_STANDARD)) { int pipe = get_pipe_by_windex(wIndex); struct fsl_ep *ep; -- 2.34.1