Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7a0b6fc6 authored by Oliver Neukum's avatar Oliver Neukum Committed by Greg Kroah-Hartman
Browse files

USB: dwc2: write HCINT with INTMASK applied



commit 0583bc776ca5b5a3f5752869fc31cf7322df2b35 upstream.

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: default avatarOliver Neukum <oneukum@suse.com>
Tested-by: default avatarIvan Ivanov <ivan.ivanov@suse.com>
Tested-by: default avatarAndrea 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: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent d1c86635
Loading
Loading
Loading
Loading
+7 −8
Original line number Diff line number Diff line
@@ -2045,15 +2045,17 @@ static void dwc2_hc_n_intr(struct dwc2_hsotg *hsotg, int chnum)
{
	struct dwc2_qtd *qtd;
	struct dwc2_host_chan *chan;
	u32 hcint, hcintmsk;
	u32 hcint, hcintraw, hcintmsk;

	chan = hsotg->hc_ptr_array[chnum];

	hcint = dwc2_readl(hsotg, HCINT(chnum));
	hcintraw = dwc2_readl(hsotg, HCINT(chnum));
	hcintmsk = dwc2_readl(hsotg, HCINTMSK(chnum));
	hcint = hcintraw & hcintmsk;
	dwc2_writel(hsotg, hcint, HCINT(chnum));

	if (!chan) {
		dev_err(hsotg->dev, "## hc_ptr_array for channel is NULL ##\n");
		dwc2_writel(hsotg, hcint, HCINT(chnum));
		return;
	}

@@ -2062,11 +2064,9 @@ static void dwc2_hc_n_intr(struct dwc2_hsotg *hsotg, int chnum)
			 chnum);
		dev_vdbg(hsotg->dev,
			 "  hcint 0x%08x, hcintmsk 0x%08x, hcint&hcintmsk 0x%08x\n",
			 hcint, hcintmsk, hcint & hcintmsk);
			 hcintraw, hcintmsk, hcint);
	}

	dwc2_writel(hsotg, hcint, HCINT(chnum));

	/*
	 * If we got an interrupt after someone called
	 * dwc2_hcd_endpoint_disable() we don't want to crash below
@@ -2076,8 +2076,7 @@ static void dwc2_hc_n_intr(struct dwc2_hsotg *hsotg, int chnum)
		return;
	}

	chan->hcint = hcint;
	hcint &= hcintmsk;
	chan->hcint = hcintraw;

	/*
	 * If the channel was halted due to a dequeue, the qtd list might