458 lines
12 KiB
C
Executable File
458 lines
12 KiB
C
Executable File
/*
|
|
* Copyright (c) 2021, Novatek Microelectronics Corp.
|
|
* All rights reserved.
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <asm/io.h>
|
|
#include <errno.h>
|
|
#include <usb.h>
|
|
|
|
#include "xhci.h"
|
|
#ifdef CONFIG_DM_USB
|
|
#include <dm.h>
|
|
#include <fdtdec.h>
|
|
#endif
|
|
#include <asm/arch/IOAddress.h>
|
|
#include <asm/arch/hardware.h>
|
|
#include <asm/gpio.h>
|
|
#include <asm/nvt-common/nvt_types.h>
|
|
#include <asm/nvt-common/nvt_common.h>
|
|
#include <linux/libfdt.h>
|
|
|
|
#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 |