Skip to content

Commit

Permalink
usb: ehci: add workaround for chipidea PORTSC.PEC bug
Browse files Browse the repository at this point in the history
[ Upstream commit dda4b60 ]

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>
  • Loading branch information
Xu Yang authored and gregkh committed Sep 23, 2023
1 parent 55072da commit ba3df8c
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 3 deletions.
8 changes: 6 additions & 2 deletions drivers/usb/host/ehci-hcd.c
Expand Up @@ -755,10 +755,14 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)

/* 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;
}

Expand Down
10 changes: 9 additions & 1 deletion drivers/usb/host/ehci-hub.c
Expand Up @@ -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
Expand Down Expand Up @@ -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;

Expand Down
10 changes: 10 additions & 0 deletions drivers/usb/host/ehci.h
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit ba3df8c

Please sign in to comment.