Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

usb: ehci: add workaround for chipidea PORTSC.PEC bug

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>

authored by

Xu Yang and committed by
Greg Kroah-Hartman
dda4b60e fb57f829

+25 -3
+6 -2
drivers/usb/host/ehci-hcd.c
··· 755 755 756 756 /* normal [4.15.1.2] or error [4.15.1.1] completion */ 757 757 if (likely ((status & (STS_INT|STS_ERR)) != 0)) { 758 - if (likely ((status & STS_ERR) == 0)) 758 + if (likely ((status & STS_ERR) == 0)) { 759 759 INCR(ehci->stats.normal); 760 - else 760 + } else { 761 + /* Force to check port status */ 762 + if (ehci->has_ci_pec_bug) 763 + status |= STS_PCD; 761 764 INCR(ehci->stats.error); 765 + } 762 766 bh = 1; 763 767 } 764 768
+9 -1
drivers/usb/host/ehci-hub.c
··· 674 674 675 675 if ((temp & mask) != 0 || test_bit(i, &ehci->port_c_suspend) 676 676 || (ehci->reset_done[i] && time_after_eq( 677 - jiffies, ehci->reset_done[i]))) { 677 + jiffies, ehci->reset_done[i])) 678 + || ehci_has_ci_pec_bug(ehci, temp)) { 678 679 if (i < 7) 679 680 buf [0] |= 1 << (i + 1); 680 681 else ··· 875 874 status |= USB_PORT_STAT_C_CONNECTION << 16; 876 875 if (temp & PORT_PEC) 877 876 status |= USB_PORT_STAT_C_ENABLE << 16; 877 + 878 + if (ehci_has_ci_pec_bug(ehci, temp)) { 879 + status |= USB_PORT_STAT_C_ENABLE << 16; 880 + ehci_info(ehci, 881 + "PE is cleared by HW port:%d PORTSC:%08x\n", 882 + wIndex + 1, temp); 883 + } 878 884 879 885 if ((temp & PORT_OCC) && (!ignore_oc && !ehci->spurious_oc)){ 880 886 status |= USB_PORT_STAT_C_OVERCURRENT << 16;
+10
drivers/usb/host/ehci.h
··· 207 207 unsigned has_fsl_port_bug:1; /* FreeScale */ 208 208 unsigned has_fsl_hs_errata:1; /* Freescale HS quirk */ 209 209 unsigned has_fsl_susp_errata:1; /* NXP SUSP quirk */ 210 + unsigned has_ci_pec_bug:1; /* ChipIdea PEC bug */ 210 211 unsigned big_endian_mmio:1; 211 212 unsigned big_endian_desc:1; 212 213 unsigned big_endian_capbase:1; ··· 707 706 * after setting SUSP bit. 708 707 */ 709 708 #define ehci_has_fsl_susp_errata(e) ((e)->has_fsl_susp_errata) 709 + 710 + /* 711 + * Some Freescale/NXP processors using ChipIdea IP have a bug in which 712 + * disabling the port (PE is cleared) does not cause PEC to be asserted 713 + * when frame babble is detected. 714 + */ 715 + #define ehci_has_ci_pec_bug(e, portsc) \ 716 + ((e)->has_ci_pec_bug && ((e)->command & CMD_PSE) \ 717 + && !(portsc & PORT_PEC) && !(portsc & PORT_PE)) 710 718 711 719 /* 712 720 * While most USB host controllers implement their registers in