/* * MT8195 has 4 controllers, the controller1~3's default SOF/ITP interval * is calculated from the frame counter clock 24M, but in fact, the clock * is 48M, add workaround for it.
*/ staticvoid xhci_mtk_set_frame_interval(struct xhci_hcd_mtk *mtk)
{ struct device *dev = mtk->dev; struct usb_hcd *hcd = mtk->hcd;
u32 value;
if (!of_device_is_compatible(dev->of_node, "mediatek,mt8195-xhci")) return;
value = readl(hcd->regs + HFCNTR_CFG);
value &= ~(ITP_DELTA_CLK_MASK | FRMCNT_LEV1_RANG_MASK);
value |= (ITP_DELTA_CLK | FRMCNT_LEV1_RANG);
writel(value, hcd->regs + HFCNTR_CFG);
value = readl(hcd->regs + LS_EOF_CFG);
value &= ~XSEOF_OFFSET_MASK;
value |= LSEOF_OFFSET;
writel(value, hcd->regs + LS_EOF_CFG);
value = readl(hcd->regs + FS_EOF_CFG);
value &= ~XSEOF_OFFSET_MASK;
value |= FSEOF_OFFSET;
writel(value, hcd->regs + FS_EOF_CFG);
value = readl(hcd->regs + SS_GEN1_EOF_CFG);
value &= ~XSEOF_OFFSET_MASK;
value |= SSG1EOF_OFFSET;
writel(value, hcd->regs + SS_GEN1_EOF_CFG);
value = readl(hcd->regs + SS_GEN2_EOF_CFG);
value &= ~XSEOF_OFFSET_MASK;
value |= SSG2EOF_OFFSET;
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.
*/ staticvoid 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);
}
staticvoid 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);
}
staticint xhci_mtk_host_enable(struct xhci_hcd_mtk *mtk)
{ struct mu3c_ippc_regs __iomem *ippc = mtk->ippc_regs;
u32 value, check_val; int u3_ports_disabled = 0; int ret; int i;
if (!mtk->has_ippc) return 0;
/* power on host ip */
value = readl(&ippc->ip_pw_ctr1);
value &= ~CTRL1_IP_HOST_PDN;
writel(value, &ippc->ip_pw_ctr1);
/* power on and enable u3 ports except skipped ones */ for (i = 0; i < mtk->num_u3_ports; i++) { if ((0x1 << i) & mtk->u3p_dis_msk) {
u3_ports_disabled++; continue;
}
value = readl(&ippc->u3_ctrl_p[i]);
value &= ~(CTRL_U3_PORT_PDN | CTRL_U3_PORT_DIS);
value |= CTRL_U3_PORT_HOST_SEL;
writel(value, &ippc->u3_ctrl_p[i]);
}
/* power on and enable all u2 ports except skipped ones */ for (i = 0; i < mtk->num_u2_ports; i++) { if (BIT(i) & mtk->u2p_dis_msk) continue;
value = readl(&ippc->u2_ctrl_p[i]);
value &= ~(CTRL_U2_PORT_PDN | CTRL_U2_PORT_DIS);
value |= CTRL_U2_PORT_HOST_SEL;
writel(value, &ippc->u2_ctrl_p[i]);
}
/* * wait for clocks to be stable, and clock domains reset to * be inactive after power on and enable ports
*/
check_val = STS1_SYSPLL_STABLE | STS1_REF_RST |
STS1_SYS125_RST | STS1_XHCI_RST;
if (mtk->num_u3_ports > u3_ports_disabled)
check_val |= STS1_U3_MAC_RST;
ret = readl_poll_timeout(&ippc->ip_pw_sts1, value,
(check_val == (value & check_val)), 100, 20000); if (ret) {
dev_err(mtk->dev, "clocks are not stable (0x%x)\n", value); return ret;
}
return 0;
}
staticint xhci_mtk_host_disable(struct xhci_hcd_mtk *mtk)
{ struct mu3c_ippc_regs __iomem *ippc = mtk->ippc_regs;
u32 value; int ret; int i;
if (!mtk->has_ippc) return 0;
/* power down u3 ports except skipped ones */ for (i = 0; i < mtk->num_u3_ports; i++) { if ((0x1 << i) & mtk->u3p_dis_msk) continue;
value = readl(&ippc->u3_ctrl_p[i]);
value |= CTRL_U3_PORT_PDN;
writel(value, &ippc->u3_ctrl_p[i]);
}
/* power down all u2 ports except skipped ones */ for (i = 0; i < mtk->num_u2_ports; i++) { if (BIT(i) & mtk->u2p_dis_msk) continue;
value = readl(&ippc->u2_ctrl_p[i]);
value |= CTRL_U2_PORT_PDN;
writel(value, &ippc->u2_ctrl_p[i]);
}
/* power down host ip */
value = readl(&ippc->ip_pw_ctr1);
value |= CTRL1_IP_HOST_PDN;
writel(value, &ippc->ip_pw_ctr1);
/* wait for host ip to sleep */
ret = readl_poll_timeout(&ippc->ip_pw_sts1, value,
(value & STS1_IP_SLEEP_STS), 100, 100000); if (ret)
dev_err(mtk->dev, "ip sleep failed!!!\n"); else/* workaound for platforms using low level latch */
usleep_range(100, 200);
/* reset whole ip */
value = readl(&ippc->ip_pw_ctr0);
value |= CTRL0_IP_SW_RST;
writel(value, &ippc->ip_pw_ctr0);
udelay(1);
value = readl(&ippc->ip_pw_ctr0);
value &= ~CTRL0_IP_SW_RST;
writel(value, &ippc->ip_pw_ctr0);
/* * device ip is default power-on in fact * power down device ip, otherwise ip-sleep will fail
*/
value = readl(&ippc->ip_pw_ctr2);
value |= CTRL2_IP_DEV_PDN;
writel(value, &ippc->ip_pw_ctr2);
/* only clocks can be turn off for ip-sleep wakeup mode */ staticvoid usb_wakeup_ip_sleep_set(struct xhci_hcd_mtk *mtk, bool enable)
{
u32 reg, msk, val;
xhci->quirks |= XHCI_MTK_HOST; /* * MTK host controller gives a spurious successful event after a * short transfer. Ignore it.
*/
xhci->quirks |= XHCI_SPURIOUS_SUCCESS; if (mtk->lpm_support)
xhci->quirks |= XHCI_LPM_SUPPORT; if (mtk->u2_lpm_disable)
xhci->quirks |= XHCI_HW_LPM_DISABLE;
/* * MTK xHCI 0.96: PSA is 1 by default even if doesn't support stream, * and it's 3 when support it.
*/ if (xhci->hci_version < 0x100 && HCC_MAX_PSA(xhci->hcc_params) == 4)
xhci->quirks |= XHCI_BROKEN_STREAMS;
}
/* called during probe() after chip reset completes */ staticint xhci_mtk_setup(struct usb_hcd *hcd)
{ struct xhci_hcd_mtk *mtk = hcd_to_mtk(hcd); int ret;
if (usb_hcd_is_primary_hcd(hcd)) {
ret = xhci_mtk_ssusb_config(mtk); if (ret) return ret;
xhci_mtk_init_quirk(mtk);
}
ret = xhci_gen_setup(hcd, xhci_mtk_quirks); if (ret) return ret;
if (usb_hcd_is_primary_hcd(hcd))
ret = xhci_mtk_sch_init(mtk);
ret = regulator_bulk_enable(BULK_VREGS_NUM, mtk->supplies); if (ret) goto disable_pm;
ret = clk_bulk_prepare_enable(BULK_CLKS_NUM, mtk->clks); if (ret) goto disable_ldos;
ret = device_reset_optional(dev); if (ret) {
dev_err_probe(dev, ret, "failed to reset controller\n"); goto disable_clk;
}
hcd = usb_create_hcd(driver, dev, dev_name(dev)); if (!hcd) {
ret = -ENOMEM; goto disable_clk;
}
/* * USB 2.0 roothub is stored in the platform_device. * Swap it with mtk HCD.
*/
mtk->hcd = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, mtk);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mac");
hcd->regs = devm_ioremap_resource(dev, res); if (IS_ERR(hcd->regs)) {
ret = PTR_ERR(hcd->regs); goto put_usb2_hcd;
}
hcd->rsrc_start = res->start;
hcd->rsrc_len = resource_size(res);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ippc"); if (res) { /* ippc register is optional */
mtk->ippc_regs = devm_ioremap_resource(dev, res); if (IS_ERR(mtk->ippc_regs)) {
ret = PTR_ERR(mtk->ippc_regs); goto put_usb2_hcd;
}
mtk->has_ippc = true;
}
/* * imod_interval is the interrupt moderation value in nanoseconds. * The increment interval is 8 times as much as that defined in * the xHCI spec on MTK's controller.
*/
xhci->imod_interval = 5000;
device_property_read_u32(dev, "imod-interval-ns", &xhci->imod_interval);
ret = usb_add_hcd(hcd, irq, IRQF_SHARED); if (ret) goto disable_device_wakeup;
if (!xhci_has_one_roothub(xhci)) {
xhci->shared_hcd = usb_create_shared_hcd(driver, dev,
dev_name(dev), hcd); if (!xhci->shared_hcd) {
ret = -ENOMEM; goto dealloc_usb2_hcd;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.