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

USB: dwc2: write HCINT with INTMASK applied

dwc2_hc_n_intr() writes back INTMASK as read but evaluates it
with intmask applied. In stress testing this causes spurious
interrupts like this:

[Mon Aug 14 10:51:07 2023] dwc2 3f980000.usb: dwc2_hc_chhltd_intr_dma: Channel 7 - ChHltd set, but reason is unknown
[Mon Aug 14 10:51:07 2023] dwc2 3f980000.usb: hcint 0x00000002, intsts 0x04600001
[Mon Aug 14 10:51:08 2023] dwc2 3f980000.usb: dwc2_hc_chhltd_intr_dma: Channel 0 - ChHltd set, but reason is unknown
[Mon Aug 14 10:51:08 2023] dwc2 3f980000.usb: hcint 0x00000002, intsts 0x04600001
[Mon Aug 14 10:51:08 2023] dwc2 3f980000.usb: dwc2_hc_chhltd_intr_dma: Channel 4 - ChHltd set, but reason is unknown
[Mon Aug 14 10:51:08 2023] dwc2 3f980000.usb: hcint 0x00000002, intsts 0x04600001
[Mon Aug 14 10:51:08 2023] dwc2 3f980000.usb: dwc2_update_urb_state_abn(): trimming xfer length

Applying INTMASK prevents this. The issue exists in all versions of the
driver.

Signed-off-by: Oliver Neukum <oneukum@suse.com>
Tested-by: Ivan Ivanov <ivan.ivanov@suse.com>
Tested-by: Andrea della Porta <andrea.porta@suse.com>
Link: https://lore.kernel.org/r/20231115144514.15248-1-oneukum@suse.com
Cc: stable <stable@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Oliver Neukum and committed by
Greg Kroah-Hartman
0583bc77 30ce1c03

+7 -8
+7 -8
drivers/usb/dwc2/hcd_intr.c
··· 2015 2015 { 2016 2016 struct dwc2_qtd *qtd; 2017 2017 struct dwc2_host_chan *chan; 2018 - u32 hcint, hcintmsk; 2018 + u32 hcint, hcintraw, hcintmsk; 2019 2019 2020 2020 chan = hsotg->hc_ptr_array[chnum]; 2021 2021 2022 - hcint = dwc2_readl(hsotg, HCINT(chnum)); 2022 + hcintraw = dwc2_readl(hsotg, HCINT(chnum)); 2023 2023 hcintmsk = dwc2_readl(hsotg, HCINTMSK(chnum)); 2024 + hcint = hcintraw & hcintmsk; 2025 + dwc2_writel(hsotg, hcint, HCINT(chnum)); 2026 + 2024 2027 if (!chan) { 2025 2028 dev_err(hsotg->dev, "## hc_ptr_array for channel is NULL ##\n"); 2026 - dwc2_writel(hsotg, hcint, HCINT(chnum)); 2027 2029 return; 2028 2030 } 2029 2031 ··· 2034 2032 chnum); 2035 2033 dev_vdbg(hsotg->dev, 2036 2034 " hcint 0x%08x, hcintmsk 0x%08x, hcint&hcintmsk 0x%08x\n", 2037 - hcint, hcintmsk, hcint & hcintmsk); 2035 + hcintraw, hcintmsk, hcint); 2038 2036 } 2039 - 2040 - dwc2_writel(hsotg, hcint, HCINT(chnum)); 2041 2037 2042 2038 /* 2043 2039 * If we got an interrupt after someone called ··· 2046 2046 return; 2047 2047 } 2048 2048 2049 - chan->hcint = hcint; 2050 - hcint &= hcintmsk; 2049 + chan->hcint = hcintraw; 2051 2050 2052 2051 /* 2053 2052 * If the channel was halted due to a dequeue, the qtd list might