/* * Copyright (c) 2021, Novatek Microelectronics Corp. * All rights reserved. * * SPDX-License-Identifier: GPL-2.0 */ #include #include #include #include #include "xhci.h" #ifdef CONFIG_DM_USB #include #include #endif #include #include #include #include #include #include #define DRV_VERSION "1.00.00" #ifdef CONFIG_DM_USB /* Declare global data pointer */ DECLARE_GLOBAL_DATA_PTR; struct uboot_xhci_priv { struct xhci_ctrl ctrl; /* Needed by EHCI */ struct xhci_hccr *hccr; struct xhci_hcor *hcor; }; #endif #define USB3PHY_SETREG(ofs,value) writel((value), (volatile void __iomem *)(IOADDR_USB3CTRL_REG_BASE+0x2000+((ofs)<<2))) #define USB3PHY_GETREG(ofs) readl((volatile void __iomem *)(IOADDR_USB3CTRL_REG_BASE+0x2000+((ofs)<<2))) #define USB3U2PHY_SETREG(ofs,value) writel((value), (volatile void __iomem *)(IOADDR_USB3CTRL_REG_BASE+0x1000+((ofs)<<2))) #define USB3U2PHY_GETREG(ofs) readl((volatile void __iomem *)(IOADDR_USB3CTRL_REG_BASE+0x1000+((ofs)<<2))) static void nvtim_set_gpio_power(void) { /* Enable it after dts parsing ready*/ ulong fdt_addr = nvt_readl((ulong)nvt_shminfo_boot_fdt_addr); int nodeoffset; u32 *cell = NULL; char path[20] = {0}; int gpio_pin, gpio_value, err; sprintf(path,"/u3hst@2,%lx",(IOADDR_USB3_REG_BASE & 0xFFFFFFFF)); nodeoffset = fdt_path_offset((const void*)fdt_addr, path); cell = (u32*)fdt_getprop((const void*)fdt_addr, nodeoffset, "power_gpio", NULL); if (cell > 0) { gpio_pin = __be32_to_cpu(cell[0]); gpio_value = __be32_to_cpu(cell[1]); #if defined(CONFIG_TARGET_NA51090_A64) if ((gpio_pin >= J_GPIO(0)) && (gpio_pin <= J_GPIO(4))) { u32 pinmux_value; unsigned long tmpval; sprintf(path,"/top@%lx/misc",(IOADDR_TOP_REG_BASE & 0xFFFFFFFF)); nodeoffset = fdt_path_offset((const void*)fdt_addr, path); cell = (u32*)fdt_getprop((const void*)fdt_addr, nodeoffset, "pinmux", NULL); pinmux_value = __be32_to_cpu(cell[0]); if ((pinmux_value & 0x1) == 0x0) { tmpval = readl((volatile unsigned long *)(IOADDR_TOP_REG_BASE+0x04)); tmpval &= ~(0x1 << 16); writel(tmpval, (volatile unsigned long *)(IOADDR_TOP_REG_BASE+0x04)); tmpval = readl((volatile unsigned long *)(IOADDR_TOP_REG_BASE+0xA4)); tmpval |= (0x1 << (gpio_pin & 0xF)); writel(tmpval, (volatile unsigned long *)(IOADDR_TOP_REG_BASE+0xA4)); } } #endif err = gpio_request(gpio_pin, "vbus"); if (err) { printf("#### failed to request vbus, %d\n", gpio_pin); return; } gpio_direction_output(gpio_pin, gpio_value); gpio_free(gpio_pin); } } #ifndef CONFIG_DM_USB /* * Create the appropriate control structures to manage a new XHCI host * controller. */ 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; unsigned long tmpval, temp; u8 u3_trim_rint_sel=8,u3_trim_swctrl=4, u3_trim_sqsel=4,u3_trim_icdr=0xB; /* init OTG controller */ /* Enable phy clk*/ tmpval = readl((volatile unsigned long *)IOADDR_CG_REG_BASE); tmpval |= (0x1<<20); writel(tmpval, (volatile unsigned long *)IOADDR_CG_REG_BASE); /* Enable Reset*/ tmpval = readl((volatile unsigned long *)(IOADDR_CG_REG_BASE+0x98)); tmpval &= ~(0x1<<18); writel(tmpval, (volatile unsigned long *)(IOADDR_CG_REG_BASE+0x98)); tmpval = readl((volatile unsigned long *)(IOADDR_CG_REG_BASE+0x9C)); tmpval &= ~(0x1<<15); writel(tmpval, (volatile unsigned long *)(IOADDR_CG_REG_BASE+0x9C)); udelay(10); /* Disable Reset*/ tmpval = readl((volatile unsigned long *)(IOADDR_CG_REG_BASE+0x98)); tmpval |= 0x1<<18; writel(tmpval, (volatile unsigned long *)(IOADDR_CG_REG_BASE+0x98)); tmpval = readl((volatile unsigned long *)(IOADDR_CG_REG_BASE+0x9C)); tmpval |= 0x1<<15; writel(tmpval, (volatile unsigned long *)(IOADDR_CG_REG_BASE+0x9C)); /* Release sram shutdown*/ tmpval = readl((volatile unsigned long *)(IOADDR_TOP_REG_BASE+0x1000)); tmpval &= ~(0x1<<7); writel(tmpval, (volatile unsigned long *)(IOADDR_TOP_REG_BASE+0x1000)); /* Enable clock*/ tmpval = readl((volatile unsigned long *)(IOADDR_CG_REG_BASE+0x74)); tmpval |= (0x1<<26); writel(tmpval, (volatile unsigned long *)(IOADDR_CG_REG_BASE+0x74)); mdelay(10); tmpval = readl((volatile unsigned long *)(IOADDR_USB3CTRL_REG_BASE+0x28)); tmpval = 0x1; writel(tmpval, (volatile unsigned long *)(IOADDR_USB3CTRL_REG_BASE+0x28)); tmpval = readl((volatile unsigned long *)(IOADDR_USB3CTRL_REG_BASE+0x10)); tmpval &= ~0x2; tmpval &= ~(0x1 << 11); tmpval |= (0x1 << 0); tmpval &= ~((0x7 << 16) | (0x7 << 24) | (0x1 << 4)); tmpval |= ((0x4 << 16) | (0x4 << 24)); writel(tmpval, (volatile unsigned long *)(IOADDR_USB3CTRL_REG_BASE+0x10)); USB3PHY_SETREG(0x0D, 0x01); { // UINT16 data=0; // INT32 result; // result= efuse_readParamOps(EFUSE_USBC_TRIM_DATA, &data); // if(result >= 0) { // u3_trim_swctrl = data&0x7; // u3_trim_sqsel = (data>>3)&0x7; // u3_trim_rint_sel = (data>>6)&0x1F; // } //usb3_validateTrim(); USB3PHY_SETREG(0x1A0, 0x40+u3_trim_rint_sel); USB3PHY_SETREG(0x1A3, 0x60); temp = USB3U2PHY_GETREG(0x06); temp &= ~(0x7<<1); temp |= (u3_trim_swctrl<<1); USB3U2PHY_SETREG(0x06, temp); temp = USB3U2PHY_GETREG(0x05); temp &= ~(0x7<<2); temp |= (u3_trim_sqsel<<2); USB3U2PHY_SETREG(0x05, temp); } /* asd_mode=1 (toggle rate) */ USB3PHY_SETREG(0x198, 0x04); /* RX_ICTRL's offset = 0 */ USB3PHY_SETREG(0x1BF, 0x40); /* TX_AMP_CTL=2, TX_DEC_EM_CTL=8 */ USB3PHY_SETREG(0x014, 0x8a); /* TX_LFPS_AMP_CTL = 1 */ USB3PHY_SETREG(0x034, 0xfc);//default 0xfc /* PHY Power Mode Change ready reponse time. (3 is 1ms.)(4 is 1.3ms.) */ USB3PHY_SETREG(0x114, 0x0B); USB3PHY_SETREG(0x152, 0x2E); USB3PHY_SETREG(0x153, 0x01); USB3PHY_SETREG(0x1B0, 0xC0); USB3PHY_SETREG(0x1B1, 0x91); USB3PHY_SETREG(0x1B2, 0x00); USB3PHY_SETREG(0x135, 0x88); USB3PHY_SETREG(0x12A, 0x50); USB3PHY_SETREG(0x1F0, 0x80); USB3PHY_SETREG(0x1F5, 0x01|(u3_trim_icdr<<4));//0xB1 USB3PHY_SETREG(0x105, 0x01); USB3PHY_SETREG(0x056, 0x74);//disconnect level to 0x3 udelay(2); USB3PHY_SETREG(0x102, 0x20); udelay(10); USB3PHY_SETREG(0x102, 0x00); udelay(300); USB3PHY_SETREG(0x103, 0x01); udelay(100); USB3U2PHY_SETREG(0x51, 0x20); udelay(2); USB3U2PHY_SETREG(0x51, 0x00); udelay(2); if(1) { temp = USB3PHY_GETREG(0x011); temp |= 0x1; USB3PHY_SETREG(0x011, temp); temp = USB3PHY_GETREG(0x031); temp |= 0x1; USB3PHY_SETREG(0x031, temp); } USB3PHY_SETREG(0x029, 0x1); hccr = (struct xhci_hccr *)IOADDR_USB3_REG_BASE; len = HC_LENGTH(xhci_readl(&hccr->cr_capbase)); hcor = (struct xhci_hcor *)((unsigned long)hccr + len); printk("XHCI init hccr 0x%lx and hcor 0x%lx hc_length 0x%x ver %s\n", (unsigned long)hccr, (unsigned long)hcor, len, DRV_VERSION); *ret_hccr = hccr; *ret_hcor = hcor; nvtim_set_gpio_power(); return 0; } /* * Destroy the appropriate control structures corresponding * to the XHCI host * controller */ void xhci_hcd_stop(int index) { } #else /* * Create the appropriate control structures to manage a new XHCI host * controller. */ int dm_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; unsigned long tmpval = 0, temp = 0; u8 u3_trim_rint_sel=8,u3_trim_swctrl=4, u3_trim_sqsel=4,u3_trim_icdr=0xB; /* init OTG controller */ /* Enable phy clk*/ tmpval = readl((volatile unsigned long *)IOADDR_CG_REG_BASE); tmpval |= (0x1<<20); writel(tmpval, (volatile unsigned long *)IOADDR_CG_REG_BASE); /* Enable Reset*/ tmpval = readl((volatile unsigned long *)(IOADDR_CG_REG_BASE+0x98)); tmpval &= ~(0x1<<18); writel(tmpval, (volatile unsigned long *)(IOADDR_CG_REG_BASE+0x98)); tmpval = readl((volatile unsigned long *)(IOADDR_CG_REG_BASE+0x9C)); tmpval &= ~(0x1<<15); writel(tmpval, (volatile unsigned long *)(IOADDR_CG_REG_BASE+0x9C)); udelay(10); /* Disable Reset*/ tmpval = readl((volatile unsigned long *)(IOADDR_CG_REG_BASE+0x98)); tmpval |= 0x1<<18; writel(tmpval, (volatile unsigned long *)(IOADDR_CG_REG_BASE+0x98)); tmpval = readl((volatile unsigned long *)(IOADDR_CG_REG_BASE+0x9C)); tmpval |= 0x1<<15; writel(tmpval, (volatile unsigned long *)(IOADDR_CG_REG_BASE+0x9C)); /* Release sram shutdown*/ tmpval = readl((volatile unsigned long *)(IOADDR_TOP_REG_BASE+0x1000)); tmpval &= ~(0x1<<25); writel(tmpval, (volatile unsigned long *)(IOADDR_TOP_REG_BASE+0x1000)); /* Enable clock*/ tmpval = readl((volatile unsigned long *)(IOADDR_CG_REG_BASE+0x74)); tmpval |= (0x1<<26); writel(tmpval, (volatile unsigned long *)(IOADDR_CG_REG_BASE+0x74)); mdelay(10); tmpval = readl((volatile unsigned long *)(IOADDR_USB3CTRL_REG_BASE+0x10)); tmpval &= ~0x2; tmpval &= ~(0x1 << 11); tmpval |= (0x1 << 0); tmpval &= ~((0x7 << 16) | (0x7 << 24) | (0x1 << 4)); tmpval |= ((0x4 << 16) | (0x4 << 24)); writel(tmpval, (volatile unsigned long *)(IOADDR_USB3CTRL_REG_BASE+0x10)); udelay(100); USB3PHY_SETREG(0x0D, 0x01); { // UINT16 data=0; // INT32 result; // result= efuse_readParamOps(EFUSE_USBC_TRIM_DATA, &data); // if(result >= 0) { // u3_trim_swctrl = data&0x7; // u3_trim_sqsel = (data>>3)&0x7; // u3_trim_rint_sel = (data>>6)&0x1F; // } //usb3_validateTrim(); USB3PHY_SETREG(0x1A0, 0x40+u3_trim_rint_sel); USB3PHY_SETREG(0x1A3, 0x60); temp = USB3U2PHY_GETREG(0x06); temp &= ~(0x7<<1); temp |= (u3_trim_swctrl<<1); USB3U2PHY_SETREG(0x06, temp); temp = USB3U2PHY_GETREG(0x05); temp &= ~(0x7<<2); temp |= (u3_trim_sqsel<<2); USB3U2PHY_SETREG(0x05, temp); } /* asd_mode=1 (toggle rate) */ USB3PHY_SETREG(0x198, 0x04); /* RX_ICTRL's offset = 0 */ USB3PHY_SETREG(0x1BF, 0x40); /* TX_AMP_CTL=2, TX_DEC_EM_CTL=8 */ USB3PHY_SETREG(0x014, 0x8a); /* TX_LFPS_AMP_CTL = 1 */ USB3PHY_SETREG(0x034, 0xfc);//default 0xfc /* PHY Power Mode Change ready reponse time. (3 is 1ms.)(4 is 1.3ms.) */ USB3PHY_SETREG(0x114, 0x0B); USB3PHY_SETREG(0x152, 0x2E); USB3PHY_SETREG(0x153, 0x01); USB3PHY_SETREG(0x1B0, 0xC0); USB3PHY_SETREG(0x1B1, 0x91); USB3PHY_SETREG(0x1B2, 0x00); USB3PHY_SETREG(0x135, 0x88); USB3PHY_SETREG(0x12A, 0x50); USB3PHY_SETREG(0x1F0, 0x80); USB3PHY_SETREG(0x1F5, 0x01|(u3_trim_icdr<<4));//0xB1 USB3PHY_SETREG(0x105, 0x01); USB3PHY_SETREG(0x056, 0x74);//disconnect level to 0x3 udelay(2); USB3PHY_SETREG(0x102, 0x20); udelay(10); USB3PHY_SETREG(0x102, 0x00); udelay(300); USB3PHY_SETREG(0x103, 0x01); udelay(100); USB3U2PHY_SETREG(0x51, 0x20); udelay(2); USB3U2PHY_SETREG(0x51, 0x00); udelay(2); if(1) { temp = USB3PHY_GETREG(0x011); temp |= 0x1; USB3PHY_SETREG(0x011, temp); temp = USB3PHY_GETREG(0x031); temp |= 0x1; USB3PHY_SETREG(0x031, temp); } USB3PHY_SETREG(0x029, 0x1); hccr = (struct xhci_hccr *)IOADDR_USB3_REG_BASE; len = HC_LENGTH(xhci_readl(&hccr->cr_capbase)); hcor = (struct xhci_hcor *)((unsigned long)hccr + len); printk("XHCI init hccr 0x%lx and hcor 0x%lx hc_length 0x%x ver %s\n", (unsigned long)hccr, (unsigned long)hcor, len, DRV_VERSION); *ret_hccr = hccr; *ret_hcor = hcor; return 0; } static int xhci_usb_probe(struct udevice *dev) { struct uboot_xhci_priv *ctx = dev_get_priv(dev); int ret = 0; ret = dm_xhci_hcd_init(0 , &ctx->hccr, &ctx->hcor); if (ret) { puts("XHCI: failed to initialize controller\n"); return -EINVAL; } nvtim_set_gpio_power(); debug("%s hccr 0x%lx and hcor 0x%lx hc_length %ld\n", __func__, (unsigned long)ctx->hccr, (unsigned long)ctx->hcor, (unsigned long)HC_LENGTH(xhci_readl(&ctx->hccr->cr_capbase))); return xhci_register(dev, ctx->hccr, ctx->hcor); } static int xhci_usb_remove(struct udevice *dev) { int ret; ret = xhci_deregister(dev); if (ret) return ret; return 0; } static const struct udevice_id xhci_usb_ids[] = { { .compatible = "nvt,nvt_usb3xhci" }, { } }; U_BOOT_DRIVER(usb_xhci) = { .name = "xhci_nvtivot", .id = UCLASS_USB, .of_match = xhci_usb_ids, .probe = xhci_usb_probe, .remove = xhci_usb_remove, .ops = &xhci_usb_ops, .platdata_auto_alloc_size = sizeof(struct usb_platdata), .priv_auto_alloc_size = sizeof(struct uboot_xhci_priv), .flags = DM_FLAG_ALLOC_PRIV_DMA, }; #endif