]> git.baikalelectronics.ru Git - kernel.git/commitdiff
usb: ehci: add workaround for chipidea PORTSC.PEC bug
authorXu Yang <xu.yang_2@nxp.com>
Wed, 9 Aug 2023 02:44:31 +0000 (10:44 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 23 Sep 2023 09:11:07 +0000 (11:11 +0200)
[ Upstream commit dda4b60ed70bd670eefda081f70c0cb20bbeb1fa ]

Some NXP processor using chipidea IP has a bug when frame babble is
detected.

As per 4.15.1.1.1 Serial Bus Babble:
  A babble condition also exists if IN transaction is in progress at
High-speed SOF2 point. This is called frame babble. The host controller
must disable the port to which the frame babble is detected.

The USB controller has disabled the port (PE cleared) and has asserted
USBERRINT when frame babble is detected, but PEC is not asserted.
Therefore, the SW isn't aware that port has been disabled. Then the
SW keeps sending packets to this port, but all of the transfers will
fail.

This workaround will firstly assert PCD by SW when USBERRINT is detected
and then judge whether port change has really occurred or not by polling
roothub status. Because the PEC doesn't get asserted in our case, this
patch will also assert it by SW when specific conditions are satisfied.

Signed-off-by: Xu Yang <xu.yang_2@nxp.com>
Acked-by: Peter Chen <peter.chen@kernel.org>
Link: https://lore.kernel.org/r/20230809024432.535160-1-xu.yang_2@nxp.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/usb/host/ehci-hcd.c
drivers/usb/host/ehci-hub.c
drivers/usb/host/ehci.h

index a1930db0da1c3c82af1e6becd92ac8f34f5efe48..802bfafb1012bbb5fb14aff0d685abc12515ccb0 100644 (file)
@@ -755,10 +755,14 @@ restart:
 
        /* normal [4.15.1.2] or error [4.15.1.1] completion */
        if (likely ((status & (STS_INT|STS_ERR)) != 0)) {
-               if (likely ((status & STS_ERR) == 0))
+               if (likely ((status & STS_ERR) == 0)) {
                        INCR(ehci->stats.normal);
-               else
+               } else {
+                       /* Force to check port status */
+                       if (ehci->has_ci_pec_bug)
+                               status |= STS_PCD;
                        INCR(ehci->stats.error);
+               }
                bh = 1;
        }
 
index efe30e3be22f7284164c6210a61367d629acccb7..1aee392e84927cba6403096ce4dff91d26470cc7 100644 (file)
@@ -674,7 +674,8 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
 
                if ((temp & mask) != 0 || test_bit(i, &ehci->port_c_suspend)
                                || (ehci->reset_done[i] && time_after_eq(
-                                       jiffies, ehci->reset_done[i]))) {
+                                       jiffies, ehci->reset_done[i]))
+                               || ehci_has_ci_pec_bug(ehci, temp)) {
                        if (i < 7)
                            buf [0] |= 1 << (i + 1);
                        else
@@ -875,6 +876,13 @@ int ehci_hub_control(
                if (temp & PORT_PEC)
                        status |= USB_PORT_STAT_C_ENABLE << 16;
 
+               if (ehci_has_ci_pec_bug(ehci, temp)) {
+                       status |= USB_PORT_STAT_C_ENABLE << 16;
+                       ehci_info(ehci,
+                               "PE is cleared by HW port:%d PORTSC:%08x\n",
+                               wIndex + 1, temp);
+               }
+
                if ((temp & PORT_OCC) && (!ignore_oc && !ehci->spurious_oc)){
                        status |= USB_PORT_STAT_C_OVERCURRENT << 16;
 
index ad3f13a3eaf1b729a97e125a736b76b04078cfd3..5c0e25742e1790e4b7b1ff143fc0ee39314476e0 100644 (file)
@@ -207,6 +207,7 @@ struct ehci_hcd {                   /* one per controller */
        unsigned                has_fsl_port_bug:1; /* FreeScale */
        unsigned                has_fsl_hs_errata:1;    /* Freescale HS quirk */
        unsigned                has_fsl_susp_errata:1;  /* NXP SUSP quirk */
+       unsigned                has_ci_pec_bug:1;       /* ChipIdea PEC bug */
        unsigned                big_endian_mmio:1;
        unsigned                big_endian_desc:1;
        unsigned                big_endian_capbase:1;
@@ -707,6 +708,15 @@ ehci_port_speed(struct ehci_hcd *ehci, unsigned int portsc)
  */
 #define ehci_has_fsl_susp_errata(e)    ((e)->has_fsl_susp_errata)
 
+/*
+ * Some Freescale/NXP processors using ChipIdea IP have a bug in which
+ * disabling the port (PE is cleared) does not cause PEC to be asserted
+ * when frame babble is detected.
+ */
+#define ehci_has_ci_pec_bug(e, portsc) \
+       ((e)->has_ci_pec_bug && ((e)->command & CMD_PSE) \
+        && !(portsc & PORT_PEC) && !(portsc & PORT_PE))
+
 /*
  * While most USB host controllers implement their registers in
  * little-endian format, a minority (celleb companion chip) implement