Commit 017dbfc0 authored by Chunfeng Yun's avatar Chunfeng Yun Committed by Greg Kroah-Hartman
Browse files

usb: xhci-mtk: fix a short packet issue of gen1 isoc-in transfer



For Gen1 isoc-in transfer, host still send out unexpected ACK after device
finish the burst with a short packet, this will cause an exception on the
connected device, such as, a usb 4k camera.
It can be fixed by setting rxfifo depth less than 4k bytes, prefer to use
3k here, the side-effect is that may cause performance drop about 10%,
including bulk transfer.

Fixes: 926d60ae ("usb: xhci-mtk: modify the SOF/ITP interval for mt8195")
Reviewed-by: default avatarAngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Signed-off-by: default avatarChunfeng Yun <chunfeng.yun@mediatek.com>
Link: https://lore.kernel.org/r/20240104061640.7335-2-chunfeng.yun@mediatek.com


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 223b4ef5
Loading
Loading
Loading
Loading
+38 −2
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@
 *  Chunfeng Yun <chunfeng.yun@mediatek.com>
 */

#include <linux/bitfield.h>
#include <linux/dma-mapping.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
@@ -73,6 +74,9 @@
#define FRMCNT_LEV1_RANG	(0x12b << 8)
#define FRMCNT_LEV1_RANG_MASK	GENMASK(19, 8)

#define HSCH_CFG1		0x960
#define SCH3_RXFIFO_DEPTH_MASK	GENMASK(21, 20)

#define SS_GEN2_EOF_CFG		0x990
#define SSG2EOF_OFFSET		0x3c

@@ -114,6 +118,8 @@
#define SSC_IP_SLEEP_EN	BIT(4)
#define SSC_SPM_INT_EN		BIT(1)

#define SCH_FIFO_TO_KB(x)	((x) >> 10)

enum ssusb_uwk_vers {
	SSUSB_UWK_V1 = 1,
	SSUSB_UWK_V2,
@@ -165,6 +171,35 @@ static void xhci_mtk_set_frame_interval(struct xhci_hcd_mtk *mtk)
	writel(value, hcd->regs + SS_GEN2_EOF_CFG);
}

/*
 * workaround: usb3.2 gen1 isoc rx hw issue
 * host send out unexpected ACK afer device fininsh a burst transfer with
 * a short packet.
 */
static void xhci_mtk_rxfifo_depth_set(struct xhci_hcd_mtk *mtk)
{
	struct usb_hcd *hcd = mtk->hcd;
	u32 value;

	if (!mtk->rxfifo_depth)
		return;

	value = readl(hcd->regs + HSCH_CFG1);
	value &= ~SCH3_RXFIFO_DEPTH_MASK;
	value |= FIELD_PREP(SCH3_RXFIFO_DEPTH_MASK,
			    SCH_FIFO_TO_KB(mtk->rxfifo_depth) - 1);
	writel(value, hcd->regs + HSCH_CFG1);
}

static void xhci_mtk_init_quirk(struct xhci_hcd_mtk *mtk)
{
	/* workaround only for mt8195 */
	xhci_mtk_set_frame_interval(mtk);

	/* workaround for SoCs using SSUSB about before IPM v1.6.0 */
	xhci_mtk_rxfifo_depth_set(mtk);
}

static int xhci_mtk_host_enable(struct xhci_hcd_mtk *mtk)
{
	struct mu3c_ippc_regs __iomem *ippc = mtk->ippc_regs;
@@ -448,8 +483,7 @@ static int xhci_mtk_setup(struct usb_hcd *hcd)
		if (ret)
			return ret;

		/* workaround only for mt8195 */
		xhci_mtk_set_frame_interval(mtk);
		xhci_mtk_init_quirk(mtk);
	}

	ret = xhci_gen_setup(hcd, xhci_mtk_quirks);
@@ -527,6 +561,8 @@ static int xhci_mtk_probe(struct platform_device *pdev)
	of_property_read_u32(node, "mediatek,u2p-dis-msk",
			     &mtk->u2p_dis_msk);

	of_property_read_u32(node, "rx-fifo-depth", &mtk->rxfifo_depth);

	ret = usb_wakeup_of_property_parse(mtk, node);
	if (ret) {
		dev_err(dev, "failed to parse uwk property\n");
+2 −0
Original line number Diff line number Diff line
@@ -171,6 +171,8 @@ struct xhci_hcd_mtk {
	struct regmap *uwk;
	u32 uwk_reg_base;
	u32 uwk_vers;
	/* quirk */
	u32 rxfifo_depth;
};

static inline struct xhci_hcd_mtk *hcd_to_mtk(struct usb_hcd *hcd)