usb: xhci-mtk: fix a short packet issue of gen1 isoc-in transfer
[platform/kernel/linux-starfive.git] / drivers / usb / host / xhci-mtk.c
index bbdf1b0..3252e3d 100644 (file)
@@ -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
 
 #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");