summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorVenkat Moganty <vmoganty@nvidia.com>2010-06-21 18:04:40 +0530
committerGary King <gking@nvidia.com>2010-06-22 16:42:12 -0700
commit6bab883b2a2e854f93fdec6fd15bc25ecf32aa1d (patch)
treeb21a8f151a9a7b9cf5b12dbd4f1171dfbee66245 /drivers
parentc471a842349fb295dd5f76beb32631a528677fc0 (diff)
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 Bug 675483 Bug 649012 Tested on AP20/Whistler and AP20/Harmony with USB HS electrical test tool. Change-Id: I75c0990f1e5abd55bb626bb04ffe48f0e4efe5e3 Reviewed-on: http://git-master.nvidia.com/r/2534 Reviewed-by: Gary King <gking@nvidia.com> Tested-by: Hanumanth Venkateswa Moganty <vmoganty@nvidia.com> Reviewed-by: Ramachandrudu Kandhala <rkandhala@nvidia.com> Tested-by: Ramachandrudu Kandhala <rkandhala@nvidia.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/usb/gadget/fsl_udc_core.c150
1 files changed, 149 insertions, 1 deletions
diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c
index 0d132625e646..b1eb9a4be5c7 100644
--- a/drivers/usb/gadget/fsl_udc_core.c
+++ b/drivers/usb/gadget/fsl_udc_core.c
@@ -99,6 +99,25 @@ static void fsl_ep_fifo_flush(struct usb_ep *_ep);
#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
********************************************************************/
@@ -1366,6 +1385,125 @@ 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)) {
+ printk("Timeout for Ep0 IN Ack\n");
+ break;
+ }
+ cpu_relax();
+ }
+
+ switch (test_mode << PORTSCX_PTC_BIT_POS) {
+ case PORTSCX_PTC_JSTATE:
+ VDBG("TEST_J\n");
+ /* TEST_J */
+ break;
+ case PORTSCX_PTC_KSTATE:
+ /* TEST_K */
+ VDBG("TEST_K\n");
+ break;
+ case PORTSCX_PTC_SEQNAK:
+ /* TEST_SE0_NAK */
+ VDBG("TEST_SE0_NAK\n");
+ break;
+ case PORTSCX_PTC_PACKET:
+ {
+ /* TEST_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_KERNEL),
+ struct fsl_req, req);
+ /* allocate a small amount of memory to get valid address */
+ req->req.buf = kmalloc(sizeof(fsl_udc_test_packet), GFP_KERNEL);
+ 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;
+
+ /* map virtual address to hardware */
+ if (req->req.dma == DMA_ADDR_INVALID) {
+ req->req.dma = dma_map_single(ep->udc->gadget.dev.parent,
+ req->req.buf,
+ req->req.length, ep_is_in(ep)
+ ? DMA_TO_DEVICE
+ : DMA_FROM_DEVICE);
+ req->mapped = 1;
+ } else {
+ 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);
+ req->mapped = 0;
+ }
+
+ /* prime the data phase */
+ if ((fsl_req_to_dtd(req) == 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:
+ /* TEST_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.
+ */
+ printk("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)
{
@@ -1399,7 +1537,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;