Older controllers don't implement "Device Address Advance" which allow
to pass the device address to the controller when it is received.
To support such controller we need to store the requested address and
only apply it after the next IN transfer completed on EP0.

Signed-off-by: Alban Bedel <alban.be...@avionic-design.de>
---
 drivers/usb/gadget/ci_udc.c | 18 +++++++++++++-----
 drivers/usb/gadget/ci_udc.h |  1 +
 2 files changed, 14 insertions(+), 5 deletions(-)

diff --git a/drivers/usb/gadget/ci_udc.c b/drivers/usb/gadget/ci_udc.c
index b0ef35e..694745f 100644
--- a/drivers/usb/gadget/ci_udc.c
+++ b/drivers/usb/gadget/ci_udc.c
@@ -523,6 +523,13 @@ static void handle_ep_complete(struct ci_ep *ci_ep)
 
        DBG("ept%d %s req %p, complete %x\n",
            num, in ? "in" : "out", ci_req, len);
+       /* The device address must be applied after the next IN transfer
+        * completed on ep0. */
+       if (num == 0 && in && controller.set_address) {
+               struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor;
+               writel(controller.set_address << 25, &udc->devaddr);
+               controller.set_address = 0;
+       }
        if (num != 0 || controller.ep0_data_phase)
                ci_req->req.complete(&ci_ep->ep, &ci_req->req);
        if (num == 0 && controller.ep0_data_phase) {
@@ -614,11 +621,9 @@ static void handle_setup(void)
                return;
 
        case SETUP(USB_RECIP_DEVICE, USB_REQ_SET_ADDRESS):
-               /*
-                * write address delayed (will take effect
-                * after the next IN txn)
-                */
-               writel((r.wValue << 25) | (1 << 24), &udc->devaddr);
+               /* The device address must be updated after the next IN
+                * request completed */
+               controller.set_address = r.wValue;
                req->length = 0;
                usb_ep_queue(controller.gadget.ep0, req, 0);
                return;
@@ -670,6 +675,9 @@ static void stop_activity(void)
                        ci_flush_qh(num);
                }
        }
+
+       /* clear any pending set address */
+       controller.set_address = 0;
 }
 
 void udc_irq(void)
diff --git a/drivers/usb/gadget/ci_udc.h b/drivers/usb/gadget/ci_udc.h
index 346164a..44e70b1 100644
--- a/drivers/usb/gadget/ci_udc.h
+++ b/drivers/usb/gadget/ci_udc.h
@@ -99,6 +99,7 @@ struct ci_drv {
        struct usb_gadget               gadget;
        struct ci_req                   *ep0_req;
        bool                            ep0_data_phase;
+       uint8_t                         set_address;
        struct usb_gadget_driver        *driver;
        struct ehci_ctrl                *ctrl;
        struct ept_queue_head           *epts;
-- 
2.3.0

_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
http://lists.denx.de/mailman/listinfo/u-boot

Reply via email to