diff mbox series

[1/2] usb: ehci: add workaround for chipidea PORTSC.PEC bug

Message ID 20230808102959.479264-1-xu.yang_2@nxp.com
State New
Headers show
Series [1/2] usb: ehci: add workaround for chipidea PORTSC.PEC bug | expand

Commit Message

Xu Yang Aug. 8, 2023, 10:29 a.m. UTC
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 balle. 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 didn'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>
---
 drivers/usb/host/ehci-hcd.c |  5 +++++
 drivers/usb/host/ehci-hub.c | 10 +++++++++-
 drivers/usb/host/ehci.h     | 10 ++++++++++
 3 files changed, 24 insertions(+), 1 deletion(-)

Comments

Xu Yang Aug. 9, 2023, 2:05 a.m. UTC | #1
Hi Alan,

> On Tue, Aug 08, 2023 at 06:29:58PM +0800, Xu Yang wrote:
> > 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 balle. The host controller
> 
> s/balle/babble/

Okay.

> 
> > 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 didn't aware that port has been disabled. Then the
> 
> s/didn't/isn't/

Okay.

> 
> > 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>
> > ---
> >  drivers/usb/host/ehci-hcd.c |  5 +++++
> >  drivers/usb/host/ehci-hub.c | 10 +++++++++-
> >  drivers/usb/host/ehci.h     | 10 ++++++++++
> >  3 files changed, 24 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
> > index a1930db0da1c..d6b276c354db 100644
> > --- a/drivers/usb/host/ehci-hcd.c
> > +++ b/drivers/usb/host/ehci-hcd.c
> > @@ -762,6 +762,11 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
> >               bh = 1;
> >       }
> >
> > +     /* Force to check port status */
> > +     if (ehci->has_ci_pec_bug && (status & STS_ERR)
> > +                     && !(status & STS_PCD))
> > +             status |= STS_PCD;
> 
> Suggestion for minor improvement: First, you don't really need the
> (status & STS_PCD) test, because if the bit is already set then turning
> it again on won't matter.  Second, after that test has been removed you
> can merge this code with the INCR(ehci->stats.error) line above,
> removing the (status & STS_ERR) test.

Okay. Will try this.

Thanks,
Xu Yang

> 
> The rest of the patch looks okay.
> 
> Alan Stern
diff mbox series

Patch

diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index a1930db0da1c..d6b276c354db 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -762,6 +762,11 @@  static irqreturn_t ehci_irq (struct usb_hcd *hcd)
 		bh = 1;
 	}
 
+	/* Force to check port status */
+	if (ehci->has_ci_pec_bug && (status & STS_ERR)
+			&& !(status & STS_PCD))
+		status |= STS_PCD;
+
 	/* complete the unlinking of some qh [4.15.2.3] */
 	if (status & STS_IAA) {
 
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index efe30e3be22f..1aee392e8492 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -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;
 
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index c5c7f8782549..1441e3400796 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -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