]> git.dujemihanovic.xyz Git - u-boot.git/commitdiff
usb: ci_udc: don't use "advance" feature when setting address
authorIon Agorria <ion@agorria.com>
Tue, 26 Nov 2024 07:29:56 +0000 (09:29 +0200)
committerMattijs Korpershoek <mkorpershoek@baylibre.com>
Tue, 26 Nov 2024 08:56:03 +0000 (09:56 +0100)
In the older USB controllers like for example in ChipIdea controller
used by the Tegra 2 the "USBADRA: Device Address Advance" bitflag
does not exist, so the new device address set during SET_ADDRESS
can't be deferred by hardware, which causes the host to not recognize
the device and give an error.

Instead store it until ep completes to apply the change into the hw
register as Linux kernel does. This should fix regression on old and
and be compatible with newer controllers.

Inspired by: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=ef15e5490edc7edf808d3477ab32e0e320792f65

Signed-off-by: Ion Agorria <ion@agorria.com>
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
Reviewed-by: Mattijs Korpershoek <mkorpershoek@baylibre.com>
Link: https://lore.kernel.org/r/20241126072956.64778-2-clamor95@gmail.com
Signed-off-by: Mattijs Korpershoek <mkorpershoek@baylibre.com>
drivers/usb/gadget/ci_udc.c
drivers/usb/gadget/ci_udc.h

index bbe03cfff1fb877ce92afd71e38c0225311c2d12..4bff75da759ded6716641713fbe47f6ba79386dc 100644 (file)
@@ -649,12 +649,30 @@ static void flip_ep0_direction(void)
        }
 }
 
+/*
+ * This function explicitly sets the address, without the "USBADRA" (advance)
+ * feature, which is not supported by older versions of the controller.
+ */
+static void ci_set_address(struct ci_udc *udc, u8 address)
+{
+       DBG("%s %x\n", __func__, address);
+       writel(address << 25, &udc->devaddr);
+}
+
 static void handle_ep_complete(struct ci_ep *ci_ep)
 {
        struct ept_queue_item *item, *next_td;
        int num, in, len, j;
        struct ci_req *ci_req;
 
+       /* Set the device address that was previously sent by SET_ADDRESS */
+       if (controller.next_device_address != 0) {
+               struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor;
+
+               ci_set_address(udc, controller.next_device_address);
+               controller.next_device_address = 0;
+       }
+
        num = ci_ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
        in = (ci_ep->desc->bEndpointAddress & USB_DIR_IN) != 0;
        item = ci_get_qtd(num, in);
@@ -783,7 +801,7 @@ static void handle_setup(void)
                 * write address delayed (will take effect
                 * after the next IN txn)
                 */
-               writel((r.wValue << 25) | (1 << 24), &udc->devaddr);
+               controller.next_device_address = r.wValue;
                req->length = 0;
                usb_ep_queue(controller.gadget.ep0, req, 0);
                return;
@@ -814,6 +832,9 @@ static void stop_activity(void)
        int i, num, in;
        struct ept_queue_head *head;
        struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor;
+
+       ci_set_address(udc, 0);
+
        writel(readl(&udc->epcomp), &udc->epcomp);
 #ifdef CONFIG_CI_UDC_HAS_HOSTPC
        writel(readl(&udc->epsetupstat), &udc->epsetupstat);
@@ -934,6 +955,7 @@ static int ci_pullup(struct usb_gadget *gadget, int is_on)
        struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor;
        if (is_on) {
                /* RESET */
+               controller.next_device_address = 0;
                writel(USBCMD_ITC(MICRO_8FRAME) | USBCMD_RST, &udc->usbcmd);
                udelay(200);
 
index bea2f9f3fe336a24d1c5555b9cf20a9488bf7a7f..807f2084c1e84b0f2e51c1255e114bfd03ce972e 100644 (file)
@@ -105,6 +105,7 @@ struct ci_drv {
        struct ept_queue_head           *epts;
        uint8_t                         *items_mem;
        struct ci_ep                    ep[NUM_ENDPOINTS];
+       u8                              next_device_address;
 };
 
 struct ept_queue_head {