On Tue, 28 Feb 2006, Marc Singer wrote: ... > > The question is this: what is the simplest gadget to use for debugging > a UDC driver? I started with the serial gadget because I figured it > would be easy to verify that it is working.
Marc, Below is a patch against 2.6.16-rc5, which works with CONFIG_USB_ETH and CONFIG_USB_ETH_RNDIS. For testing, I bring it up with, modprobe lh7a40x_udc modprobe g_ether host_addr=00:dc:c8:f7:75:05 dev_addr=00:dd:dc:eb:6d:f1 ifconfig usb0 up 192.168.4.2 then on the (x86) host, modprobe usbnet ifconfig eth1 up 192.168.4.1 It doesn't sense disconnects, but it does reset properly when plugged back in. The docs are somewhat ambiguous about the reset circuitry. The '400 Users Guide specifies a GPIO, whereas the '404 UG specifies DCP_CTRL. I'm not sure if the same code can be made to work on both, or if it should be conditionalized per CONFIG_MACH_*. To further complicate matters, the custom board I'm working with uses a different GPIO line, which is why I left some #errors in the patch (the board isn't released). The reset logic is probably still confused, but I wanted to get you the patch despite that, if it might help you. I only have the Logic LPD7A404 (not 400), and my custom board. If you can figure out how to get disconnect working properly, I'll verify it on my 404 and my custom board. The left-side in the diff has your 2-line patch already. Stuart Wells did the bulk of this work, reorganizing the endpoint setup and interrupt handling, so I cc'd him on this. -Jamie --- drivers/usb/gadget/lh7a40x_udc.c.singerpatch 2006-03-01 13:21:40.054919288 -0500 +++ drivers/usb/gadget/lh7a40x_udc.c 2006-03-01 15:26:13.877725520 -0500 @@ -186,12 +186,17 @@ /* set port C data direction register */ #define set_portc_ddr(bit, val) (val ? usb_set(_BIT(bit), GPIO_PORTC_DDR) : usb_clear(_BIT(bit), GPIO_PORTC_DDR)) +#ifdef CONFIG_MACH_LPD7A404 /* * LPD7A404 GPIO's: * Port C bit 1 = USB Port 1 Power Enable * Port C bit 2 = USB Port 1 Data Carrier Detect */ #define is_usb_connected() get_portc_pdr(2) +#else +#error Please define is_usb_connected() for your lh7a40x board. +#endif + #ifdef CONFIG_USB_GADGET_DEBUG_FILES @@ -228,9 +233,11 @@ t = scnprintf(next, size, "GPIO:\n" " Port C bit 1: %d, dir %d\n" - " Port C bit 2: %d, dir %d\n\n", + " Port C bit 2: %d, dir %d\n" + " Port C bit 3: %d, dir %d\n\n", get_portc_pdr(1), get_portc_ddr(1), - get_portc_pdr(2), get_portc_ddr(2) + get_portc_pdr(2), get_portc_ddr(2), + get_portc_pdr(3), get_portc_ddr(3) ); size -= t; next += t; @@ -270,12 +277,15 @@ usb_write(0, USB_OUT_INT_EN); usb_write(0, USB_INT_EN); - /* Disable the USB */ - usb_write(0, USB_PM); + /* Set PM_USB_DCP to disable the USB */ + usb_set(PM_USB_DCP, USB_PM); -#ifdef CONFIG_ARCH_LH7A404 +#ifdef CONFIG_MACH_LPD7A404 /* Disable USB power */ - set_portc_dr(1, 0); + set_portc_dr(1, 1); /* Note, inverter is present on circuit. */ + set_portc_dr(2, 1); /* " " */ +#else +#error Disable USB power for your lh7a40x board. #endif /* if hardware supports it, disconnect from usb */ @@ -324,19 +334,18 @@ */ static void udc_enable(struct lh7a40x_udc *dev) { - int ep; - DEBUG("%s, %p\n", __FUNCTION__, dev); dev->gadget.speed = USB_SPEED_UNKNOWN; -#ifdef CONFIG_ARCH_LH7A404 - /* Set Port C bit 1 & 2 as output */ - set_portc_ddr(1, 1); - set_portc_ddr(2, 1); - - /* Enable USB power */ - set_portc_dr(1, 0); +#ifdef CONFIG_MACH_LPD7A404 + set_portc_ddr(1, 0); /* Output */ + set_portc_ddr(2, 1); /* Input */ + + /* Enable USB power */ + set_portc_dr(1, 0); +#else +#error Enable USB for your specific lh7a40x board. #endif /* @@ -351,49 +360,45 @@ mdelay(5); usb_clear(USB_RESET_APB | USB_RESET_IO, USB_RESET); - /* Set MAXP values for each */ - for (ep = 0; ep < UDC_MAX_ENDPOINTS; ep++) { - struct lh7a40x_ep *ep_reg = &dev->ep[ep]; - u32 csr; - - usb_set_index(ep); - - switch (ep_reg->ep_type) { - case ep_bulk_in: - case ep_interrupt: - usb_clear(USB_IN_CSR2_USB_DMA_EN | USB_IN_CSR2_AUTO_SET, - ep_reg->csr2); - /* Fall through */ - case ep_control: - usb_write(BYTES2MAXP(ep_maxpacket(ep_reg)), - USB_IN_MAXP); - break; - case ep_bulk_out: - usb_clear(USB_OUT_CSR2_USB_DMA_EN | - USB_OUT_CSR2_AUTO_CLR, ep_reg->csr2); - usb_write(BYTES2MAXP(ep_maxpacket(ep_reg)), - USB_OUT_MAXP); - break; - } + /* Setup EP0: set required maxp value. */ + usb_set_index(0); + mdelay(5); + usb_write(BYTES2MAXP(ep_maxpacket(&dev->ep [0])), + USB_IN_MAXP); - /* Read & Write CSR1, just in case */ - csr = usb_read(ep_reg->csr1); - usb_write(csr, ep_reg->csr1); + /* Setup EP1: set required maxp value. Also set dma information*/ + usb_set_index(1); + mdelay(5); + usb_write(BYTES2MAXP(ep_maxpacket(&dev->ep [1])), + USB_IN_MAXP); + usb_clear(USB_IN_CSR2_USB_DMA_EN | USB_IN_CSR2_AUTO_SET, + USB_IN_CSR2); - flush(ep_reg); - } + /* Setup EP2: set required maxp value. Also set dma information*/ + usb_set_index(2); + mdelay(5); + usb_write(BYTES2MAXP(ep_maxpacket(&dev->ep [2])), + USB_OUT_MAXP); + usb_clear((USB_OUT_CSR2_USB_DMA_EN | USB_OUT_CSR2_AUTO_CLR), + USB_OUT_CSR2); - /* Disable interrupts */ - usb_write(0, USB_IN_INT_EN); - usb_write(0, USB_OUT_INT_EN); - usb_write(0, USB_INT_EN); + /* Setup EP3: set required maxp value. Also set dma information*/ + usb_set_index(3); + mdelay(5); + usb_write(BYTES2MAXP(ep_maxpacket(&dev->ep [3])), + USB_IN_MAXP); - /* Enable interrupts */ + /* Enable the interrupts. */ usb_set(USB_IN_INT_EP0, USB_IN_INT_EN); - usb_set(USB_INT_RESET_INT | USB_INT_RESUME_INT, USB_INT_EN); - /* Dont enable rest of the interrupts */ - /* usb_set(USB_IN_INT_EP3 | USB_IN_INT_EP1 | USB_IN_INT_EP0, USB_IN_INT_EN); - usb_set(USB_OUT_INT_EP2, USB_OUT_INT_EN); */ + /* usb_set(USB_IN_INT_EP1, USB_IN_INT_EN); + usb_set(USB_IN_INT_EP3, USB_IN_INT_EN); + usb_set(USB_OUT_INT_EP2, USB_OUT_INT_EN); */ + + /* Enable the reset interrupt */ + usb_set(USB_INT_RESET_INT, USB_INT_EN); + + /* Enable the suspend interrupt */ + usb_set(USB_INT_SUSPEND_INT, USB_INT_EN); /* Enable SUSPEND */ usb_set(PM_ENABLE_SUSPEND, USB_PM); @@ -889,33 +894,50 @@ */ static void lh7a40x_reset_intr(struct lh7a40x_udc *dev) { -#if 0 /* def CONFIG_ARCH_LH7A404 */ - /* Does not work always... */ + int tmp; DEBUG("%s: %d\n", __FUNCTION__, dev->usb_address); + /* Clear the reset interrupt */ + usb_set(USB_INT_RESET_INT, USB_INT); + + /* Must read, after setting high. */ + tmp = usb_read(USB_INT); + + // no address, thats it. if (!dev->usb_address) { /*usb_set(USB_RESET_IO, USB_RESET); mdelay(5); usb_clear(USB_RESET_IO, USB_RESET); */ + dev->gadget.speed = USB_SPEED_FULL; + return; } - /* Put the USB controller into reset. */ + + /* Cancel any transfers in progress */ + /* sww work */ + + /* program pmr:dcp_ctrl to 1, to disable pullup */ + usb_set(PM_USB_DCP, USB_PM); + + /* put the usb controller into reset */ usb_set(USB_RESET_IO, USB_RESET); - - /* Set Device ID to 0 */ - udc_set_address(dev, 0); - + /* Let PLL2 settle down */ mdelay(5); - /* Release the USB controller from reset */ + /* Set Device ID to 0 */ + udc_set_address(dev, 0); + + /* put the usb controller into reset */ usb_clear(USB_RESET_IO, USB_RESET); + + /* program pmr:dcp_ctrl to 0, to reenable pullup */ + usb_clear(PM_USB_DCP, USB_PM); /* Re-enable UDC */ udc_enable(dev); -#endif dev->gadget.speed = USB_SPEED_FULL; } @@ -925,6 +947,7 @@ static irqreturn_t lh7a40x_udc_irq(int irq, void *_dev, struct pt_regs *r) { struct lh7a40x_udc *dev = _dev; + int tmp; DEBUG("\n\n"); @@ -944,13 +967,66 @@ DEBUG("%s (on state %s)\n", __FUNCTION__, state_names[dev->ep0state]); + DEBUG("intr_int = %x\n", intr_int); DEBUG("intr_out = %x\n", intr_out); DEBUG("intr_in = %x\n", intr_in); - DEBUG("intr_int = %x\n", intr_int); - if (intr_in) { - usb_write(intr_in, USB_IN_INT); + if (intr_int) { + if (intr_int & USB_INT_RESET_INT) { + /* Section 19.1.3.2.1 */ + lh7a40x_reset_intr(dev); + } + + if (intr_int & USB_INT_SUSPEND_INT) { + /* Section 19.1.3.2.2 */ + + /* Clear the SUSPEND interrupt */ + usb_set(USB_INT_SUSPEND_INT, USB_INT); + + /* Must read, after setting high. */ + tmp = usb_read(USB_INT); + + /* put the device into suspend mode. */ + usb_clear(PM_ENABLE_SUSPEND, USB_PM); + + DEBUG("USB suspend%s\n", + is_usb_connected()? "" : "+disconnect"); + if (!is_usb_connected()) { + stop_activity(dev, dev->driver); + } else if (dev->gadget.speed != + USB_SPEED_UNKNOWN && dev->driver + && dev->driver->suspend) { + dev->driver->suspend(&dev->gadget); + } + } + if (intr_int & USB_INT_RESUME_INT) { + /* Section 19.1.3.2.3 */ + DEBUG("USB resume\n"); + /* Clear the SUSPEND interrupt */ + usb_set(USB_INT_RESUME_INT, USB_INT); + + /* Must read, after setting high. */ + tmp = usb_read(USB_INT); + + /* put the device into suspend mode. */ + usb_set(PM_ENABLE_SUSPEND, USB_PM); + + if (dev->gadget.speed != USB_SPEED_UNKNOWN + && dev->driver + && dev->driver->resume + && is_usb_connected()) { + dev->driver->resume(&dev->gadget); + } + } + } + + + if (intr_in) { + if (intr_in & USB_IN_INT_EP0) { + DEBUG("USB_IN_INT_EP0 (control)\n"); + lh7a40x_handle_ep0(dev, intr_in); + } if ((intr_in & USB_IN_INT_EP1) && (in_en & USB_IN_INT_EP1)) { DEBUG("USB_IN_INT_EP1\n"); @@ -961,15 +1037,17 @@ DEBUG("USB_IN_INT_EP3\n"); lh7a40x_in_epn(dev, 3, intr_in); } - if (intr_in & USB_IN_INT_EP0) { - DEBUG("USB_IN_INT_EP0 (control)\n"); - lh7a40x_handle_ep0(dev, intr_in); - } + /* clear the interrupt. */ + usb_write(intr_in, USB_IN_INT); + tmp = usb_read(USB_IN_INT); } if (intr_out) { usb_write(intr_out, USB_OUT_INT); + /* Clear the interrupt */ + tmp = usb_read(USB_OUT_INT); + if ((intr_out & USB_OUT_INT_EP2) && (out_en & USB_OUT_INT_EP2)) { DEBUG("USB_OUT_INT_EP2\n"); @@ -977,37 +1055,6 @@ } } - if (intr_int) { - usb_write(intr_int, USB_INT); - - if (intr_int & USB_INT_RESET_INT) { - lh7a40x_reset_intr(dev); - } - - if (intr_int & USB_INT_RESUME_INT) { - DEBUG("USB resume\n"); - - if (dev->gadget.speed != USB_SPEED_UNKNOWN - && dev->driver - && dev->driver->resume - && is_usb_connected()) { - dev->driver->resume(&dev->gadget); - } - } - - if (intr_int & USB_INT_SUSPEND_INT) { - DEBUG("USB suspend%s\n", - is_usb_connected()? "" : "+disconnect"); - if (!is_usb_connected()) { - stop_activity(dev, dev->driver); - } else if (dev->gadget.speed != - USB_SPEED_UNKNOWN && dev->driver - && dev->driver->suspend) { - dev->driver->suspend(&dev->gadget); - } - } - - } } spin_unlock(&dev->lock); @@ -1641,9 +1688,9 @@ if (reqtype == USB_RECIP_INTERFACE) { /* This is not supported. * And according to the USB spec, this one does nothing.. - * Just return 0 */ DEBUG_SETUP("GET_STATUS: USB_RECIP_INTERFACE\n"); + return 1; } else if (reqtype == USB_RECIP_DEVICE) { DEBUG_SETUP("GET_STATUS: USB_RECIP_DEVICE\n"); val |= (1 << 0); /* Self powered */ @@ -1839,9 +1886,11 @@ struct lh7a40x_ep *ep = &dev->ep[0]; u32 csr; - /* Set index 0 */ + /* program the index register to 0 */ usb_set_index(0); - csr = usb_read(USB_EP0_CSR); + + /* Get the incsr1 for EP0 */ + csr = usb_read(USB_EP0_CSR); DEBUG_EP0("%s: csr = %x\n", __FUNCTION__, csr); ------------------------------------------------------- This SF.Net email is sponsored by xPML, a groundbreaking scripting language that extends applications into web and mobile media. Attend the live webcast and join the prime developer group breaking into this new coding territory! http://sel.as-us.falkag.net/sel?cmd=lnk&kid=110944&bid=241720&dat=121642 _______________________________________________ linux-usb-devel@lists.sourceforge.net To unsubscribe, use the last form field at: https://lists.sourceforge.net/lists/listinfo/linux-usb-devel