]> www.infradead.org Git - linux.git/commitdiff
usb: cdnsp: fix for Link TRB with TC
authorPawel Laszczak <pawell@cadence.com>
Wed, 21 Aug 2024 06:07:42 +0000 (06:07 +0000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 24 Aug 2024 02:07:46 +0000 (10:07 +0800)
Stop Endpoint command on LINK TRB with TC bit set to 1 causes that
internal cycle bit can have incorrect state after command complete.
In consequence empty transfer ring can be incorrectly detected
when EP is resumed.
NOP TRB before LINK TRB avoid such scenario. Stop Endpoint command
is then on NOP TRB and internal cycle bit is not changed and have
correct value.

Fixes: 3d82904559f4 ("usb: cdnsp: cdns3 Add main part of Cadence USBSSP DRD Driver")
cc: <stable@vger.kernel.org>
Signed-off-by: Pawel Laszczak <pawell@cadence.com>
Reviewed-by: Peter Chen <peter.chen@kernel.org>
Link: https://lore.kernel.org/r/PH7PR07MB953878279F375CCCE6C6F40FDD8E2@PH7PR07MB9538.namprd07.prod.outlook.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/cdns3/cdnsp-gadget.h
drivers/usb/cdns3/cdnsp-ring.c

index dbee6f08527773d6cdebb9f26748e2cea871197d..84887dfea7635b0dde662bb3e830c40d649ea5b2 100644 (file)
@@ -811,6 +811,7 @@ struct cdnsp_stream_info {
  *        generate Missed Service Error Event.
  *        Set skip flag when receive a Missed Service Error Event and
  *        process the missed tds on the endpoint ring.
+ * @wa1_nop_trb: hold pointer to NOP trb.
  */
 struct cdnsp_ep {
        struct usb_ep endpoint;
@@ -838,6 +839,8 @@ struct cdnsp_ep {
 #define EP_UNCONFIGURED                BIT(7)
 
        bool skip;
+       union cdnsp_trb  *wa1_nop_trb;
+
 };
 
 /**
index a60c0cb991cd1bf7430fe6db4e9d7a172e994a82..dbd83d321bca01f8840e08c484c87ec95d7d174d 100644 (file)
@@ -1904,6 +1904,23 @@ int cdnsp_queue_bulk_tx(struct cdnsp_device *pdev, struct cdnsp_request *preq)
        if (ret)
                return ret;
 
+       /*
+        * workaround 1: STOP EP command on LINK TRB with TC bit set to 1
+        * causes that internal cycle bit can have incorrect state after
+        * command complete. In consequence empty transfer ring can be
+        * incorrectly detected when EP is resumed.
+        * NOP TRB before LINK TRB avoid such scenario. STOP EP command is
+        * then on NOP TRB and internal cycle bit is not changed and have
+        * correct value.
+        */
+       if (pep->wa1_nop_trb) {
+               field = le32_to_cpu(pep->wa1_nop_trb->trans_event.flags);
+               field ^= TRB_CYCLE;
+
+               pep->wa1_nop_trb->trans_event.flags = cpu_to_le32(field);
+               pep->wa1_nop_trb = NULL;
+       }
+
        /*
         * Don't give the first TRB to the hardware (by toggling the cycle bit)
         * until we've finished creating all the other TRBs. The ring's cycle
@@ -1999,6 +2016,17 @@ int cdnsp_queue_bulk_tx(struct cdnsp_device *pdev, struct cdnsp_request *preq)
                send_addr = addr;
        }
 
+       if (cdnsp_trb_is_link(ring->enqueue + 1)) {
+               field = TRB_TYPE(TRB_TR_NOOP) | TRB_IOC;
+               if (!ring->cycle_state)
+                       field |= TRB_CYCLE;
+
+               pep->wa1_nop_trb = ring->enqueue;
+
+               cdnsp_queue_trb(pdev, ring, 0, 0x0, 0x0,
+                               TRB_INTR_TARGET(0), field);
+       }
+
        cdnsp_check_trb_math(preq, enqd_len);
        ret = cdnsp_giveback_first_trb(pdev, pep, preq->request.stream_id,
                                       start_cycle, start_trb);