usb: ci_udc: Support larger packets
PATCH 1/5 usb: ci_udc: Support larger packets
From: Stephen Warren <[email protected]>
ci_ep_queue() currently only fills in the page0/page1 fields in the queue item. If the buffer is larger than 4KiB (unaligned) or 8KiB (page-aligned), then this prevents the HW from knowing where to write the balance of the data.
Fix this by initializing all 5 pageN pointers, which allows up to 16KiB (potentially non-page-aligned) buffers.
--- drivers/usb/gadget/ci_udc.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/usb/gadget/ci_udc.c +3 -0
@@ -350,6 +350,9 @@ static int ci_ep_queue(struct usb_ep *ep, item->info =INFO_BYTES (len) |INFO_IOC |INFO_ACTIVE ; item->page0 = (uint32_t )ci_ep->b_buf; item->page1 = ((uint32_t )ci_ep->b_buf &0xfffff000 ) +0x1000 ; + item->page2 = ((uint32_t )ci_ep->b_buf &0xfffff000 ) +0x2000 ; + item->page3 = ((uint32_t )ci_ep->b_buf &0xfffff000 ) +0x3000 ; + item->page4 = ((uint32_t )ci_ep->b_buf &0xfffff000 ) +0x4000 ;ci_flush_qtd (num); head->next = (unsigned ) item;
Signature · 1 line
1.8.1.5
PATCH 2/5 usb: ci_udc: set ep->req.actual after transfer
From: Stephen Warren <[email protected]>
At least drivers/usb/gadget/storage_common.c expects that ep->req.actual contain the number of bytes actually transferred. (At least in practice, I observed it failing to work correctly unless this was the case).
However, ci_udc.c modifies ep->req.length instead. I assume that .length
is supposed to represent the allocated buffer size, whereas .actual is
supposed to represent the actual number of bytes transferred. In the OUT transaction case, this may happen simply because the host sends a smaller
packet than the max possible size, which is quite legal. In the IN case,
transferring fewer bytes than requested could presumably happen as an error.
Modify handle_ep_complete() to write to .actual rather than modifying .length.
--- drivers/usb/gadget/ci_udc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/usb/gadget/ci_udc.c +2 -2
@@ -321,7 +321,7 @@ static void ci_debounce(struct ci_ep *ep, int in)if (addr == ba)return ;/* not a bounce */ -memcpy (ep->req.buf, ep->b_buf, ep->req.length); +memcpy (ep->req.buf, ep->b_buf, ep->req.actual); free:/* Large payloads use allocated buffer, free it. */ if (ep->b_buf != ep->b_fast)
@@ -388,7 +388,7 @@ static void handle_ep_complete(struct ci_ep *ep) num, in ? "in" : "out", item->info, item->page0); len = (item->info >>16 ) &0x7fff ; - ep->req.length -= len; + ep->req.actual = ep->req.length - len;ci_debounce (ep, in); DBG("ept%d %s complete %x\n",
Signature · 1 line
1.8.1.5
PATCH 3/5 usb: ci_udc: make PHY initialization conditional
In reply to Stephen WarrenFrom: Stephen Warren <[email protected]>
usb_gadget_register_driver() currently unconditionally programs PORTSC to select a ULPI PHY. This is incorrect on at least the Tegra boards I am testing with, which use a UTMI PHY for the OTG ports. Make the PHY selection code conditional upon the specific EHCI controller that is in use.
Ideally, I believe that the PHY initialization code should be part of ehci_hcd_init() in the relevant EHCI controller driver, or some board- specific function that ehci_hcd_init() calls.
For MX6, I'm not sure this PHY initialization code is correct even before this patch, since ehci-mx6's ehci_hcd_init() already configures PORTSC to a board-specific value, and it seems likely that the code in ci_udc.c is incorrectly undoing this. Perhaps this is not an issue if the PHY selection register bits aren't implemented on this instance of the MX6 USB controller?
ehci-mxs.c doens't appear to touch PORTSC, so this code is likely still required there.
--- drivers/usb/gadget/ci_udc.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-)
drivers/usb/gadget/ci_udc.c +7 -2
@@ -702,7 +702,6 @@ static int ci_udc_probe(void)int usb_gadget_register_driver (struct usb_gadget_driver *driver) { -struct ci_udc *udc;int ret;if (!driver)
@@ -717,12 +716,18 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver)return ret; ret =ci_udc_probe (); +#if defined(CONFIG_USB_EHCI_MX6 ) || defined(CONFIG_USB_EHCI_MXS ) +/* +* FIXME: usb_lowlevel_init()->ehci_hcd_init() should be doing all +* HW-specific initialization, e.g. ULPI-vs-UTMI PHY selection +*/ if (!ret) { - udc = (struct ci_udc *)controller.ctrl->hcor; +struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor;/* select ULPI phy */ writel (PTS (PTS_ENABLE ) |PFSC , &udc->portsc); } +#endif ret = driver->bind (&controller.gadget);if (ret) {
Signature · 1 line
1.8.1.5
PATCH 4/5 usb: ci_udc: support variants with hostpc register
In reply to Stephen WarrenFrom: Stephen Warren <[email protected]>
Tegra's USB controller appears to be a variant of the ChipIdea controller; perhaps derived from it, or simply a different version of the IP core to what U-Boot supports today.
In this variant, at least the following difference are present:
- Some registers are moved about.
- Setup transaction completion is reported in a separate 'epsetupstat' register, rather than in 'epstat' (which still exists, perhaps for other transaction types).
- USB connection speed is reported in a separate 'hostpc1_devlc' register, rather than 'portsc'.
- The registers used by ci_udc.c begin at offset 0x130 from the USB register base, rather than offset 0x140. However, this is handled by the associated EHCI controller driver, since the register address is stored in controller.ctrl->hcor.
Introduce define CONFIG_CI_UDC_HAS_HOSTPC to indicate which variant of the controller should be supported. The "HAS_HOSTPC" part of this name mirrors the similar "has_hostpc" field used by the Linux EHCI controller core to represent the presence/absence of the hostpc1_devlc register.
--- drivers/usb/gadget/ci_udc.c | 15 +++++++++++ drivers/usb/gadget/ci_udc.h | 65 ++++++++++++++++++++++++++++++++------------- 2 files changed, 62 insertions(+), 18 deletions(-)
drivers/usb/gadget/ci_udc.c +15 -0
@@ -416,7 +416,11 @@ static void handle_setup(void)ci_invalidate_qh (0 );memcpy (&r, head->setup_data,sizeof (struct usb_ctrlrequest )); +#ifdef CONFIG_CI_UDC_HAS_HOSTPC +writel (EPT_RX (0 ), &udc->epsetupstat); +#else writel (EPT_RX (0 ), &udc->epstat); +#endif DBG ("handle setup %s, %x, %x index %x value %x\n" ,reqname (r.bRequest), r.bRequestType, r.bRequest, r.wIndex, r.wValue);
@@ -483,6 +487,9 @@ static void stop_activity(void)struct ept_queue_head *head;struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor;writel (readl (&udc->epcomp), &udc->epcomp); +#ifdef CONFIG_CI_UDC_HAS_HOSTPC +writel (readl (&udc->epsetupstat), &udc->epsetupstat); +#endif writel (readl (&udc->epstat), &udc->epstat);writel (0xffffffff , &udc->epflush);
@@ -524,7 +531,11 @@ void udc_irq(void)int max =64 ;int speed =USB_SPEED_FULL ; +#ifdef CONFIG_CI_UDC_HAS_HOSTPC + bit = (readl (&udc->hostpc1_devlc) >>25 ) &3 ; +#else bit = (readl (&udc->portsc) >>26 ) &3 ; +#endif DBG ("-- portchange %x %s\n" , bit, (bit ==2 ) ?"High" :"Full" );if (bit ==2 ) { speed =USB_SPEED_HIGH ;
@@ -541,7 +552,11 @@ void udc_irq(void)printf ("<UEI %x>\n" ,readl (&udc->epcomp));if ((n &STS_UI ) || (n &STS_UEI )) { +#ifdef CONFIG_CI_UDC_HAS_HOSTPC + n =readl (&udc->epsetupstat); +#else n =readl (&udc->epstat); +#endif if (n &EPT_RX (0 ))handle_setup ();
drivers/usb/gadget/ci_udc.h +47 -18
92 lines not on this page · raw message
Signature · 1 line
1.8.1.5
PATCH 5/5 usb: ums: use only 1 buffer for CI_UDC
In reply to Stephen WarrenFrom: Stephen Warren <[email protected]>
ci_udc.c allocates only a single buffer for each endpoint, which ci_ep_alloc_request() returns as a hard-coded value rather than dynamically allocating. Consequently, storage_common.c must limit itself to using a single buffer at a time. Add a special case to the definition of FSG_NUM_BUFFERS for this.
Another option would be to fix ci_ep_alloc_request() to dynamically allocate the buffers like some/all(?) other device mode drivers do. However, I don't think that ci_ep_queue() supports queueing up multiple buffers either yet, and I'm not familiar enough with the controller yet to implement that. As such, any attempt to use multiple buffers simply results in data corruption and other errors.
--- drivers/usb/gadget/storage_common.c | 4 ++++ 1 file changed, 4 insertions(+)
drivers/usb/gadget/storage_common.c +4 -0
@@ -311,7 +311,11 @@ static struct fsg_lun *fsg_lun_from_dev(struct device *dev)#define DELAYED_STATUS (EP0_BUFSIZE + 999)/* An impossibly large value */ /* Number of buffers we will use. 2 is enough for double-buffering */ +#ifndef CONFIG_CI_UDC #define FSG_NUM_BUFFERS 2 +#else +#define FSG_NUM_BUFFERS 1/* ci_udc only allows 1 req per ep at present */ +#endif /* Default size of buffer length. */ #define FSG_BUFLEN ((u32)16384)
Signature · 1 line
1.8.1.5
Marek Vasut
AppliedPATCH 1/5 usb: ci_udc: Support larger packets
In reply to Stephen WarrenQuoted 11 lines · Stephen Warren
From: Stephen Warren <[email protected]>
ci_ep_queue()currently only fills in the page0/page1 fields in the queue item. If the buffer is larger than 4KiB (unaligned) or 8KiB (page-aligned), then this prevents the HW from knowing where to write the balance of the data.Fix this by initializing all 5 pageN pointers, which allows up to 16KiB (potentially non-page-aligned) buffers.
Signed-off-by Stephen Warren <[email protected]>
Applied all, thanks. But can you please check if we can get the buffer allocation from 5/5 sorted please ? I'd really like that instead of such hacks.
Best regards,
Marek Vasut