blob: b9c476f68eb765038f8b4684a099991df76670da [file] [log] [blame]
/*
* Copyright 2017 NXP
*
* NXP i.MX8 USB HOST xHCI Controller (Cadence IP)
*
* Author: Peter Chen <peter.chen@nxp.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <usb.h>
#include <malloc.h>
#include <linux/compat.h>
#include "xhci.h"
#include <usb/imx8_usb3_reg_def.h>
#include <dm.h>
#include <power-domain.h>
#include <asm/arch/clock.h>
/* 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,
"cdns3,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 = "Cadence,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