/* * Copyright 2017 NXP * * NXP i.MX8 USB HOST xHCI Controller (Cadence IP) * * Author: Peter Chen * * SPDX-License-Identifier: GPL-2.0+ */ #include #include #include #include #include "xhci.h" #include #include #include #include /* Declare global data pointer */ DECLARE_GLOBAL_DATA_PTR; /* According to UG CH 3.1.1 Bring-up Sequence */ static void imx_usb3_phy_init(void) { writel(0x0830, PHY_PMA_CMN_CTRL1); writel(0x10, TB_ADDR_CMN_DIAG_HSCLK_SEL); writel(0x00F0, TB_ADDR_CMN_PLL0_VCOCAL_INIT_TMR); writel(0x0018, TB_ADDR_CMN_PLL0_VCOCAL_ITER_TMR); writel(0x00D0, TB_ADDR_CMN_PLL0_INTDIV); writel(0x4aaa, TB_ADDR_CMN_PLL0_FRACDIV); writel(0x0034, TB_ADDR_CMN_PLL0_HIGH_THR); writel(0x1ee, TB_ADDR_CMN_PLL0_SS_CTRL1); writel(0x7F03, TB_ADDR_CMN_PLL0_SS_CTRL2); writel(0x0020, TB_ADDR_CMN_PLL0_DSM_DIAG); writel(0x0000, TB_ADDR_CMN_DIAG_PLL0_OVRD); writel(0x0000, TB_ADDR_CMN_DIAG_PLL0_FBH_OVRD); writel(0x0000, TB_ADDR_CMN_DIAG_PLL0_FBL_OVRD); writel(0x0007, TB_ADDR_CMN_DIAG_PLL0_V2I_TUNE); writel(0x0027, TB_ADDR_CMN_DIAG_PLL0_CP_TUNE); writel(0x0008, TB_ADDR_CMN_DIAG_PLL0_LF_PROG); writel(0x0022, TB_ADDR_CMN_DIAG_PLL0_TEST_MODE); writel(0x000a, TB_ADDR_CMN_PSM_CLK_CTRL); writel(0x139, TB_ADDR_XCVR_DIAG_RX_LANE_CAL_RST_TMR); writel(0xbefc, TB_ADDR_XCVR_PSM_RCTRL); writel(0x7799, TB_ADDR_TX_PSC_A0); writel(0x7798, TB_ADDR_TX_PSC_A1); writel(0x509b, TB_ADDR_TX_PSC_A2); writel(0x3, TB_ADDR_TX_DIAG_ECTRL_OVRD); writel(0x5098, TB_ADDR_TX_PSC_A3); writel(0x2090, TB_ADDR_TX_PSC_CAL); writel(0x2090, TB_ADDR_TX_PSC_RDY); writel(0xA6FD, TB_ADDR_RX_PSC_A0); writel(0xA6FD, TB_ADDR_RX_PSC_A1); writel(0xA410, TB_ADDR_RX_PSC_A2); writel(0x2410, TB_ADDR_RX_PSC_A3); writel(0x23FF, TB_ADDR_RX_PSC_CAL); writel(0x2010, TB_ADDR_RX_PSC_RDY); writel(0x0020, TB_ADDR_TX_TXCC_MGNLS_MULT_000); writel(0x00ff, TB_ADDR_TX_DIAG_BGREF_PREDRV_DELAY); writel(0x0002, TB_ADDR_RX_SLC_CU_ITER_TMR); writel(0x0013, TB_ADDR_RX_SIGDET_HL_FILT_TMR); writel(0x0000, TB_ADDR_RX_SAMP_DAC_CTRL); writel(0x1004, TB_ADDR_RX_DIAG_SIGDET_TUNE); writel(0x4041, TB_ADDR_RX_DIAG_LFPSDET_TUNE2); writel(0x0480, TB_ADDR_RX_DIAG_BS_TM); writel(0x8006, TB_ADDR_RX_DIAG_DFE_CTRL1); writel(0x003f, TB_ADDR_RX_DIAG_ILL_IQE_TRIM4); writel(0x543f, TB_ADDR_RX_DIAG_ILL_E_TRIM0); writel(0x543f, TB_ADDR_RX_DIAG_ILL_IQ_TRIM0); writel(0x0000, TB_ADDR_RX_DIAG_ILL_IQE_TRIM6); writel(0x8000, TB_ADDR_RX_DIAG_RXFE_TM3); writel(0x0003, TB_ADDR_RX_DIAG_RXFE_TM4); writel(0x2408, TB_ADDR_RX_DIAG_LFPSDET_TUNE); writel(0x05ca, TB_ADDR_RX_DIAG_DFE_CTRL3); writel(0x0258, TB_ADDR_RX_DIAG_SC2C_DELAY); writel(0x1fff, TB_ADDR_RX_REE_VGA_GAIN_NODFE); writel(0x02c6, TB_ADDR_XCVR_PSM_CAL_TMR); writel(0x0002, TB_ADDR_XCVR_PSM_A0BYP_TMR); writel(0x02c6, TB_ADDR_XCVR_PSM_A0IN_TMR); writel(0x0010, TB_ADDR_XCVR_PSM_A1IN_TMR); writel(0x0010, TB_ADDR_XCVR_PSM_A2IN_TMR); writel(0x0010, TB_ADDR_XCVR_PSM_A3IN_TMR); writel(0x0010, TB_ADDR_XCVR_PSM_A4IN_TMR); writel(0x0010, TB_ADDR_XCVR_PSM_A5IN_TMR); writel(0x0002, TB_ADDR_XCVR_PSM_A0OUT_TMR); writel(0x0002, TB_ADDR_XCVR_PSM_A1OUT_TMR); writel(0x0002, TB_ADDR_XCVR_PSM_A2OUT_TMR); writel(0x0002, TB_ADDR_XCVR_PSM_A3OUT_TMR); writel(0x0002, TB_ADDR_XCVR_PSM_A4OUT_TMR); writel(0x0002, TB_ADDR_XCVR_PSM_A5OUT_TMR); /* Change rx detect parameter */ writel(0x960, TB_ADDR_TX_RCVDET_EN_TMR); writel(0x01e0, TB_ADDR_TX_RCVDET_ST_TMR); writel(0x0090, TB_ADDR_XCVR_DIAG_LANE_FCM_EN_MGN_TMR); udelay(10); /* force rx detect */ /* writel(0xc000, TB_ADDR_TX_RCVDET_OVRD); */ } void imx8_xhci_init(void) { u32 tmp_data; tmp_data = readl(USB3_SSPHY_STATUS); writel(tmp_data, USB3_SSPHY_STATUS); tmp_data = readl(USB3_SSPHY_STATUS); while ((tmp_data & 0xf0000000) != 0xf0000000) { printf("clkvld is incorrect = 0x%x\n", tmp_data); udelay(10); tmp_data = readl(USB3_SSPHY_STATUS); } tmp_data = readl(USB3_CORE_CTRL1); tmp_data = (tmp_data & 0xfffffff8) | 0x202; writel(tmp_data, USB3_CORE_CTRL1); tmp_data &= ~0x04000000; /* clear PHY apb reset */ writel(tmp_data, USB3_CORE_CTRL1); imx_usb3_phy_init(); tmp_data = readl(USB3_CORE_CTRL1); tmp_data &= ~0xfc000000; /* clear all sw_rst */ writel(tmp_data, USB3_CORE_CTRL1); debug("wait xhci_power_on_ready\n"); tmp_data = readl(USB3_CORE_STATUS); while (!(tmp_data & 0x1000)) tmp_data = readl(USB3_CORE_STATUS); debug("xhci_power_on_ready\n"); tmp_data = readl(USBSTS); debug("waiting CNR 0x%x\n", tmp_data); while (tmp_data & 0x800) tmp_data = readl(USBSTS); debug("check CNR has finished\n"); } void imx8_xhci_reset(void) { /* Set CORE ctrl to default value, that all rst are hold */ writel(0xfc000001, USB3_CORE_CTRL1); } #ifdef CONFIG_DM_USB static int xhci_imx8_probe(struct udevice *dev) { struct xhci_hccr *hccr; struct xhci_hcor *hcor; int ret = 0; int len; /* Need to power on the PHY before access it */ #if CONFIG_IS_ENABLED(POWER_DOMAIN) struct udevice phy_dev; struct power_domain pd; const void *blob = gd->fdt_blob; int offset = dev_of_offset(dev), phy_off; phy_off = fdtdec_lookup_phandle(blob, offset, "fsl,usbphy"); if (phy_off < 0) return -EINVAL; phy_dev.node = offset_to_ofnode(phy_off); if (!power_domain_get(&phy_dev, &pd)) { if (power_domain_on(&pd)) return -EINVAL; } #endif ret = board_usb_init(dev->seq, USB_INIT_HOST); if (ret != 0) { printf("Failed to initialize board for USB\n"); return ret; } init_clk_usb3(dev->seq); imx8_xhci_init(); hccr = (struct xhci_hccr *)HCIVERSION_CAPLENGTH; len = HC_LENGTH(xhci_readl(&hccr->cr_capbase)); hcor = (struct xhci_hcor *)((uintptr_t) hccr + len); printf("XHCI-imx8 init hccr 0x%p and hcor 0x%p hc_length %d\n", (uint32_t *)hccr, (uint32_t *)hcor, len); return xhci_register(dev, hccr, hcor); } static int xhci_imx8_remove(struct udevice *dev) { int ret = xhci_deregister(dev); if (!ret) imx8_xhci_reset(); board_usb_cleanup(dev->seq, USB_INIT_HOST); return ret; } static const struct udevice_id xhci_usb_ids[] = { { .compatible = "fsl,imx8-usb3", }, { } }; U_BOOT_DRIVER(xhci_imx8) = { .name = "xhci_imx8", .id = UCLASS_USB, .of_match = xhci_usb_ids, .probe = xhci_imx8_probe, .remove = xhci_imx8_remove, .ops = &xhci_usb_ops, .platdata_auto_alloc_size = sizeof(struct usb_platdata), .priv_auto_alloc_size = sizeof(struct xhci_ctrl), .flags = DM_FLAG_ALLOC_PRIV_DMA, }; #else int xhci_hcd_init(int index, struct xhci_hccr **ret_hccr, struct xhci_hcor **ret_hcor) { struct xhci_hccr *hccr; struct xhci_hcor *hcor; int len, ret; ret = board_usb_init(index, USB_INIT_HOST); if (ret != 0) { printf("Failed to initialize board for USB\n"); return ret; } init_clk_usb3(index); imx8_xhci_init(); hccr = (struct xhci_hccr *)HCIVERSION_CAPLENGTH; len = HC_LENGTH(xhci_readl(&hccr->cr_capbase)); hcor = (struct xhci_hcor *)((uintptr_t) hccr + len); printf("XHCI-imx8 init hccr 0x%p and hcor 0x%p hc_length %d\n", (uint32_t *)hccr, (uint32_t *)hcor, len); *ret_hccr = hccr; *ret_hcor = hcor; return 0; } void xhci_hcd_stop(int index) { imx8_xhci_reset(); board_usb_cleanup(index, USB_INIT_HOST); } #endif