GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)

This commit is contained in:
lai
2026-09-06 03:52:57 +08:00
commit b1928b41c0
21813 changed files with 4413081 additions and 0 deletions
@@ -0,0 +1,2 @@
obj-$(CONFIG_TARGET_XMFALCON) += phy-xmfalcon-usb.o
obj-$(CONFIG_TARGET_XMORCA) += phy-xmorca-usb.o
@@ -0,0 +1,31 @@
/*
* Copyright (c) LOTUS. All rights reserved.
*/
#ifndef PHY_LOTUS_USB
#define PHY_LOTUS_USB
#define U_LEVEL1 10
#define U_LEVEL2 20
#define U_LEVEL3 30
#define U_LEVEL4 50
#define U_LEVEL5 100
#define U_LEVEL6 200
#define U_LEVEL7 300
#define U_LEVEL8 500
#define U_LEVEL9 1000
#define U_LEVEL10 2000
enum usb_phy_width_mode {
PHY_WIDTH_8BIT = 0,
PHY_WIDTH_16BIT,
};
enum usb_scenes {
/* U3 CTRL connect U3/U2 PHY */
LOTUS_USB_SCENE2 = 0,
/* U3 CTRL connect U3 PHY, U2 CTRL connect U2 PHY */
LOTUS_USB_SCENE1 = 1,
};
#endif // PHY_LOTUS_USB
@@ -0,0 +1,699 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) LOTUS. All rights reserved.
*/
#include <asm/arch/platform.h>
#include <dm.h>
#include <linux/lotus/usb.h>
#include <usb.h>
#include <usb/xhci.h>
#include <linux/lotus/chip.h>
#include "phy-usb.h"
#ifdef __USB_DEBUG
#define USB_DEBUG(...) printf(__VA_ARGS__)
#else
#define USB_DEBUG(...)
#endif
/* CRG PERI_CRG80: usb clk & reset, offset 0x140 */
#define USB2_CTRL 0x140
#define OUTER_PHY_CRG_DEF_VAL 0x130d
#define LOTUS_PHY_CRG_DEF_VAL 0x3130d
#define LOTUS_PHY_REF_CKEN (0x1 << 17)
#define LOTUS_PHY_APB_CKEN (0x1 << 16)
#define USB2_UTMI_CKEN (0x1 << 12)
#define USB2_PHY_APB_CKEN (0x1 << 11)
#define USB2_REF_CKEN (0x1 << 9)
#define USB2_BUS_CKEN (0x1 << 8)
#define USB2_PHY_PLL_CKEN (0x1 << 4)
#define USB2_PHY_XTAL_CKEN (0x1 << 2)
#define USB2_FREECLK_CKSEL (0x1 << 13)
#define USB2_PHY_APB_RST (0x1 << 10)
#define USB2_VCC_SRST_REQ (0x1 << 3)
#define USB2_PHY_REQ (0x1 << 0)
#define USB2_PHY_PORT_TREQ (0x1 << 1)
#define USB3_CTRL 0x13c
#define USB3_PHY_SRST (0x1 << 8)
#define USB3_PHY_XTAL_CKEN (0x1 << 7)
#define USB3_VCC_SRST (0x1 << 6)
#define USB3_PIPE3_RX_CKEN (0x1 << 5)
#define USB3_PIPE3_TX_CKEN (0x1 << 4)
#define USB3_UTMI_CKEN (0x1 << 3)
#define USB3_SUSPEND_CKEN (0x1 << 2)
#define USB3_REF_CKEN (0x1 << 1)
#define USB3_BUS_CKEN (0x1 << 0)
#define GTXTHRCFG 0xc108
#define GRXTHRCFG 0xc10c
#define REG_GCTL 0xc110
#define GUSB2PHYCFG 0xc200
#define REG_GUSB3PIPECTL0 0xc2c0
#define PCS_SSP_SOFT_RESET (0x1 << 31)
#define USBTRDTIM_MASK 0x00003c00
#define USBTRDTIM_16BIT (0x5 << 10)
#define USBTRDTIM_8BIT (0x9 << 10)
#define PHYIF (0x1 << 3)
#define SUSPENDUSB20 (0x1 << 6)
#define ENBLSLPM (0x1 << 8)
#define LSIPD_MASK (0x7 << 19)
#define LSIPD_3_BIT (0x2 << 19)
#define U2_FREECLK_EXISTS (0x1 << 30)
#define PORT_CAP_DIR (0x3 << 12)
#define PORT_SET_HOST (0x1 << 12)
#define PORT_DISABLE_SUSPEND (0x1 << 17)
#define USB2_G_TXTHRCFG 0x23100000
#define USB2_G_RXTHRCFG 0x23100000
/* Lotus phy use reg, reg's misc_base=0x12028000 */
#define MISC_LOTUS_PHY2_CTRL_REG 0x28
#define MISC_USB_SEL_SCENE1 (0x0 << 0)
#define MISC_USB_SEL_SCENE2 (0x1 << 0)
#define MISC_USB_SCENE_MASK (0x1 << 0)
#define USB2_UTMI_REG_PROTECT1 (0x1 << 8)
#define USB2_UTMI_REG_PROTECT2 (0x1 << 9)
#define USB2_UTMI_REG_PROTECT3 (0x1 << 10)
#define USB2_HW_CHIRP (0x1 << 14)
#define USB2_UTMI_IDPULLUP (0x1 << 15)
/* Outer phy use reg, reg's misc_base=0x12028000 */
#define MISC_OUT_PHY2_CTRL_REG 0x2C
#define USB2_OSCOUTEN (0x1 << 8)
#define USB2_PLL_EN (0x1 << 12)
#define USB2_UTMI_DATABUS16_8 (0x1 << 16)
#define MISC_OUT_PHY2_XCFGI_63TO32 0x988
#define CFG_OUT_PHY2_EYE_VAL (0x1 << 5)
/* reg base(misc_base): 0x12028000 */
#define MISC_USB2_CTRL_REG 0x24
#define U2_BUS_FILTER_BYPASS (0xf << 0)
#define U2_PWREN_MODE_MASK (0x1 << 4)
#define U2_PWREN_MODE_MISC (0x0 << 4)
#define U2_PWREN_MODE_CTRL (0x1 << 4)
#define U2_OVERCURR_MODE_MASK (0x1 << 5)
#define U2_OVERCURR_MODE_DISABLE (0x0 << 5)
#define U2_OVERCURR_MODE_ENABLE (0x1 << 5)
#define U2_PWREN_MASK (0x1 << 6)
#define U2_PWREN_ON (0x0 << 6)
#define U2_PWREN_OFF (0x1 << 6)
#define U2_OVERCURR_MASK (0x1 << 7)
#define U2_OVERCURR_ON (0x0 << 7)
#define U2_OVERCURR_OFF (0x1 << 7)
#define U2_FLADJ_30MHZ (0x20 << 13)
#define MISC_USB3_CTRL_REG 0x1110
#define U3_BUS_FILTER_BYPASS (0xf << 0)
#define U3_FLADJ_30MHZ (0x20 << 4)
#define U3_PWREN_MODE_MASK (0x1 << 11)
#define U3_PWREN_MODE_MISC (0x0 << 11)
#define U3_PWREN_MODE_CTRL (0x1 << 11)
#define U3_OVERCURR_MODE_MASK (0x1 << 12)
#define U3_OVERCURR_MODE_DISABLE (0x0 << 12)
#define U3_OVERCURR_MODE_ENABLE (0x1 << 12)
#define U3_PWREN_MASK (0x1 << 13)
#define U3_PWREN_ON (0x0 << 13)
#define U3_PWREN_OFF (0x1 << 13)
#define U3_OVERCURR_MASK (0x1 << 14)
#define U3_OVERCURR_ON (0x0 << 14)
#define U3_OVERCURR_OFF (0x1 << 14)
#define HOST_U2_PORT_MASK (0xf << 20)
#define HOST_U2_PORT(n) (n << 20)
#define HOST_U3_PORT_MASK (0xf << 24)
#define HOST_U3_PORT(n) (n << 24)
#define USB2_HOST_VBUS_EN (0x1 << 30)
#define USB3_HOST_VBUS_EN (0x1 << 31)
#define MISC_USB3_PHY_REG 0x112c
/* SYSBOOT1[2:2]: usb scene select */
#define SYSBOOT1 0x12020134
#define SYSBOOT1_SEL_USB_SCENE_MASK (1 << 2)
static uintptr_t xhci_base;
/**
* lotus_chip_u3_ability - whether current chip support u3.
*
* @return:
* 1: support u3.
* 0: only support u2.
*/
static int lotus_chip_u3_ability(void)
{
u32 id = get_chipid();
USB_DEBUG("%s: chid id 0x%x\n", __func__, id);
if (id == 0x76050110 || id == 0x06050110)
return 0;
/* if id == 0, consider chip support U3. */
return 1;
}
int xhci_hcd_init(int index, struct xhci_hccr **hccr, struct xhci_hcor **hcor)
{
#ifdef CONFIG_USB_DUAL_PORT_SUPPORT
unsigned int reg;
reg = readl(MISC_REG_BASE + MISC_LOTUS_PHY2_CTRL_REG);
reg &= MISC_USB_SCENE_MASK;
if (index >= LOTUS_USB_CTRL_UNKNOWN)
return -ENODEV;
/* 7605v11 only support u2 host */
if (!lotus_chip_u3_ability() && index == LOTUS_DWC_U3)
return -ENODEV;
/* Scene2 don't init U2 host. */
if (index == LOTUS_DWC_U2 && reg)
return -ENODEV;
#endif
if ((hccr == NULL) || (hcor == NULL))
return -EINVAL;
#ifdef CONFIG_USB_DUAL_PORT_SUPPORT
if (index == LOTUS_DWC_U3)
xhci_base = USB3_CTRL_REG_BASE;
else if (index == LOTUS_DWC_U2)
xhci_base = USB2_CTRL_REG_BASE;
#else
/* single usb port, U3 is default */
if (index == LOTUS_DWC_U3) {
/* 7605v11 only support U2 host */
if (!lotus_chip_u3_ability())
xhci_base = USB2_CTRL_REG_BASE;
else
xhci_base = USB3_CTRL_REG_BASE;
} else
return -ENODEV;
#endif
*hccr = (struct xhci_hccr *)(xhci_base);
*hcor = (struct xhci_hcor *)((uintptr_t) *hccr +
HC_LENGTH(xhci_readl(&(*hccr)->cr_capbase)));
USB_DEBUG("hccr:0x%x, hcor:0x%x.\n", (unsigned int)*hccr, (unsigned int)*hcor);
return 0;
}
/* Get usb scene from SYSBOOT1 and config to MISC
* usb_scene: 0-scene1, 1-scene2.
* scene1:U3 only + U2.
* scene2:U3(compatible to U2).
*/
#ifdef CONFIG_USB_DUAL_PORT_SUPPORT
static void usb_scene_select(int index, int type)
#else
static void usb_scene_select(int index)
#endif
{
unsigned int reg, usb_scene = LOTUS_USB_SCENE2;
#ifdef CONFIG_USB_DUAL_PORT_SUPPORT
reg = readl(SYSBOOT1);
if (reg & SYSBOOT1_SEL_USB_SCENE_MASK)
usb_scene = LOTUS_USB_SCENE1;
/* used by usb download */
if (index == LOTUS_DWC_U2 && type == USB_INIT_DEVICE)
usb_scene = LOTUS_USB_SCENE1;
#else
/* used by usb download */
if ((index == LOTUS_DWC_U2) ||
(index == LOTUS_DWC_U3 && !lotus_chip_u3_ability()))
usb_scene = LOTUS_USB_SCENE1;
#endif
reg = 0;
if (usb_scene == LOTUS_USB_SCENE1) {
/* u3 ctrl connect u3 phy, u2 ctrl connect u2 phy */
reg |= MISC_USB_SEL_SCENE1;
printf("%s:select U3 only + U2.\n", __func__);
} else if (usb_scene == LOTUS_USB_SCENE2) {
/* u3 ctrl connect u3 phy and u2 phy */
reg |= MISC_USB_SEL_SCENE2;
printf("%s:select U3(compatible to U2).\n", __func__);
}
writel(reg, MISC_REG_BASE + MISC_LOTUS_PHY2_CTRL_REG);
USB_DEBUG("MISC_LOTUS_PHY2_CTRL_REG(0x12028028):0x%X\n",
readl(MISC_REG_BASE + MISC_LOTUS_PHY2_CTRL_REG));
}
static void lotus_u2_phy_eye_config(void)
{
unsigned int reg;
/* outer phy eye diagram config */
reg = readl(MISC_REG_BASE + MISC_OUT_PHY2_XCFGI_63TO32);
reg |= CFG_OUT_PHY2_EYE_VAL;
writel(reg, MISC_REG_BASE + MISC_OUT_PHY2_XCFGI_63TO32);
}
static void lotus_dwc_data_width_config(int index,
enum usb_phy_width_mode mode)
{
unsigned int reg, base = 0;
/* GUSB2PHYCFG register: config val 0x40001548(HS/FS) or
* 0x00381548(LS) according to IC.
*
* USBTRDTIM: USB 2.0 Turnaround Time in PHY clocks.
* PHYIF: 1 UTMI+ PHY is 16-bit, 0 UTMI+ PHY is 8-bit.
*/
reg = 0;
switch (mode) {
case PHY_WIDTH_8BIT:
/* 8-bits usb phy configure */
reg |= USBTRDTIM_8BIT;
reg &= ~PHYIF;
break;
case PHY_WIDTH_16BIT:
/* 16-bits usb phy configure */
reg |= (USBTRDTIM_16BIT | PHYIF);
break;
default:
printf("usb phy width config val error.\n");
return;
}
#ifdef CONFIG_USB_DUAL_PORT_SUPPORT
if (index == LOTUS_DWC_U3)
base = USB3_CTRL_REG_BASE;
else
base = USB2_CTRL_REG_BASE;
#else
if (index == LOTUS_DWC_U3 && lotus_chip_u3_ability())
base = USB3_CTRL_REG_BASE;
else
base = USB2_CTRL_REG_BASE;
#endif
writel(reg, base + GUSB2PHYCFG);
}
static inline void lotus_usb2_vbus_valid_en(void)
{
unsigned int reg;
/* enable U2 vbus valid */
reg = readl(MISC_REG_BASE + MISC_USB3_CTRL_REG);
reg |= USB2_HOST_VBUS_EN;
writel(reg, MISC_REG_BASE + MISC_USB3_CTRL_REG);
}
static inline void lotus_usb3_vbus_valid_en(void)
{
unsigned int reg;
/* enable U3 vbus valid */
reg = readl(MISC_REG_BASE + MISC_USB3_CTRL_REG);
reg |= USB3_HOST_VBUS_EN;
writel(reg, MISC_REG_BASE + MISC_USB3_CTRL_REG);
}
static void lotus_u2_phy_clk_init(void)
{
unsigned int reg;
/* enable u2 phy clk and do reset */
reg = (USB2_PHY_REQ | USB2_PHY_XTAL_CKEN);
writel(reg, CRG_REG_BASE + USB2_CTRL);
udelay(U_LEVEL6);
/* undo phy reset */
reg = readl(CRG_REG_BASE + USB2_CTRL);
reg &= ~USB2_PHY_REQ;
writel(reg, CRG_REG_BASE + USB2_CTRL);
USB_DEBUG("USB2_CRG(0x12010140), expext:0x4,"
"acutal:0x%X\n", readl(CRG_REG_BASE + USB2_CTRL));
}
static void lotus_u2_ctrl_clk_init(void)
{
unsigned int reg;
/* enable u2 ctrl clk and do reset */
reg = readl(CRG_REG_BASE + USB2_CTRL);
reg |= (USB2_VCC_SRST_REQ | USB2_BUS_CKEN | USB2_REF_CKEN |
USB2_UTMI_CKEN);
writel(reg, CRG_REG_BASE + USB2_CTRL);
udelay(U_LEVEL6);
/* undo ctrl vcc reset */
reg = readl(CRG_REG_BASE + USB2_CTRL);
reg &= ~USB2_VCC_SRST_REQ;
writel(reg, CRG_REG_BASE + USB2_CTRL);
}
static void inline lotus_usb2_clk_init(void)
{
/* write CRG default value 0x130d, including:
*
* USB2_PHY_REQ: reset PHY UTMI
* USB2_PHY_XTAL_CKEN: open phy xtal clk
* USB2_VCC_SRST_REQ: reset ctrl VCC
* USB2_BUS_CKEN: open bus clk
* USB2_REF_CKEN: open ctrl ref clk
* USB2_UTMI_CKEN: open UTMI clk
* LOTUS_PHY_APB_CKEN: open LOTUS phy apb clk
* USB2_UTMI_CKEN: open LOTUS phy ref clk
*/
lotus_u2_phy_clk_init();
lotus_u2_ctrl_clk_init();
USB_DEBUG("USB2_CRG(0x12010140), expext:0x1304,"
"acutal:0x%X\n", readl(CRG_REG_BASE + USB2_CTRL));
}
static void inline lotus_usb2_clk_exit(void)
{
unsigned int reg;
/* disable usb2 clk and do reset */
reg = 0;
reg |= (USB2_VCC_SRST_REQ | USB2_PHY_REQ);
writel(reg, CRG_REG_BASE + USB2_CTRL);
USB_DEBUG("USB2_CRG(0x12010140), expext:0x9,"
"acutal:0x%X\n", readl(CRG_REG_BASE + USB2_CTRL));
}
static void lotus_usb3_clk_init(void)
{
unsigned int reg;
/* write CRG default value 0x1ff, including:
*
* USB3_PHY_SRST: reset U3 PHY UTMI
* USB3_PHY_XTAL_CKEN: open U3 phy xtal clk
* USB3_VCC_SRST: reset U3 ctrl VCC
* USB3_PIPE3_RX_CKEN: open PIPE3 RX clk
* USB3_PIPE3_TX_CKEN: open PIPE3 TX clk
* USB3_UTMI_CKEN: open UTMI clk
* USB3_SUSPEND_CKEN: open U3 ctrl suspend clk
* USB3_REF_CKEN: open U3 ctrl ref clk
* USB3_BUS_CKEN: open U3 ctrl bus clk
*/
reg = 0;
reg |= (USB3_PHY_SRST | USB3_PHY_XTAL_CKEN | USB3_VCC_SRST |
USB3_PIPE3_RX_CKEN | USB3_PIPE3_TX_CKEN | USB3_UTMI_CKEN |
USB3_SUSPEND_CKEN | USB3_REF_CKEN | USB3_BUS_CKEN);
writel(reg, CRG_REG_BASE + USB3_CTRL);
USB_DEBUG("USB3_CRG(0x1201013c), expext:0x1ff,"
"acutal:0x%X\n", readl(CRG_REG_BASE + USB3_CTRL));
udelay(U_LEVEL6);
/* undo usb phy and ctrl reset */
reg = readl(CRG_REG_BASE + USB3_CTRL);
reg &= ~USB3_PHY_SRST;
reg &= ~USB3_VCC_SRST;
writel(reg, CRG_REG_BASE + USB3_CTRL);
USB_DEBUG("USB3_CRG(0x1201013c), expext:0xbf,"
"acutal:0x%X\n", readl(CRG_REG_BASE + USB3_CTRL));
}
static void lotus_usb3_clk_exit(void)
{
unsigned int reg;
/* disable usb3 clk and do reset */
reg = 0;
reg |= (USB3_PHY_SRST | USB3_VCC_SRST);
writel(reg, CRG_REG_BASE + USB3_CTRL);
USB_DEBUG("USB3_CRG(0x1201013c), expext:0x140,"
"acutal:0x%X\n", readl(CRG_REG_BASE + USB3_CTRL));
}
static void lotus_u2_phy_misc_config(void)
{
unsigned int reg;
/* MISC_OUT_PHY2_CTRL: config 0x11100 according to IC. */
reg = 0;
reg |= (USB2_OSCOUTEN | USB2_PLL_EN | USB2_UTMI_DATABUS16_8);
writel(reg, MISC_REG_BASE + MISC_OUT_PHY2_CTRL_REG);
USB_DEBUG("MISC_OUT_PHY2_CTRL_REG(0x1202802c),"
"expext:0x11100, acutal:0x%X\n",
readl(MISC_REG_BASE + MISC_OUT_PHY2_CTRL_REG));
}
static void lotus_u2_ctrl_misc_config(void)
{
unsigned int reg;
/* MISC_USB2_CTRL_REG: config 0x400ff according to IC. */
reg = 0;
reg |= (U2_BUS_FILTER_BYPASS | U2_PWREN_MODE_CTRL | U2_OVERCURR_MODE_ENABLE |\
U2_PWREN_OFF | U2_OVERCURR_OFF | U2_FLADJ_30MHZ);
writel(reg, MISC_REG_BASE + MISC_USB2_CTRL_REG);
USB_DEBUG("MISC_USB2_CTRL_REG(0x12028024),"
"expext:0x400ff, acutal:0x%X\n",
readl(MISC_REG_BASE + MISC_USB2_CTRL_REG));
}
static void lotus_u3_ctrl_misc_config(void)
{
unsigned int reg;
/* MISC_USB3_CTRL_REG: config 0xc1107a0f according to IC. */
reg = 0;
reg |= HOST_U3_PORT(1);
reg |= HOST_U2_PORT(1);
reg |= (U3_OVERCURR_OFF | U3_PWREN_OFF | U3_OVERCURR_MODE_ENABLE |\
U3_PWREN_MODE_CTRL | U3_FLADJ_30MHZ | U3_BUS_FILTER_BYPASS);
writel(reg, MISC_REG_BASE + MISC_USB3_CTRL_REG);
USB_DEBUG("MISC_USB3_CTRL_REG(0x12029110), acutal:0x%X\n",
readl(MISC_REG_BASE + MISC_USB3_CTRL_REG));
}
/* config U3 phy inner reg, insure U3 phy CRG already open. */
static void lotus_u3_phy_misc_config(void)
{
/* adjust pi_current_trim */
writel(0x11, MISC_REG_BASE + MISC_USB3_PHY_REG);
udelay(1); /* asic say delay 10ns, we delay 1us */
writel(0x4211, MISC_REG_BASE + MISC_USB3_PHY_REG);
udelay(1);
writel(0x11, MISC_REG_BASE + MISC_USB3_PHY_REG);
udelay(1);
/* close slew_assist_dis */
writel(0x2, MISC_REG_BASE + MISC_USB3_PHY_REG);
udelay(1);
writel(0x100202, MISC_REG_BASE + MISC_USB3_PHY_REG);
udelay(1);
writel(0x2, MISC_REG_BASE + MISC_USB3_PHY_REG);
udelay(1);
USB_DEBUG("MISC_USB3_PHY_REG(0x1202912c):0x%X\n",
readl(MISC_REG_BASE + MISC_USB3_PHY_REG));
}
static void lotus_dwc_usb_config(int index)
{
unsigned int reg, base = 0;
#ifdef CONFIG_USB_DUAL_PORT_SUPPORT
if (index == LOTUS_DWC_U2)
base = USB2_CTRL_REG_BASE;
else
base = USB3_CTRL_REG_BASE;
#else
if (index == LOTUS_DWC_U2 ||
(index == LOTUS_DWC_U3 && !lotus_chip_u3_ability())) {
/* USB2 Controller configs */
base = USB2_CTRL_REG_BASE;
}
if ((index == LOTUS_DWC_U3 && lotus_chip_u3_ability())) {
/* USB3 Controller configs */
base = USB3_CTRL_REG_BASE;
}
#endif
reg = readl(base + REG_GUSB3PIPECTL0);
reg |= PCS_SSP_SOFT_RESET;
writel(reg, base + REG_GUSB3PIPECTL0);
udelay(U_LEVEL2);
reg = readl(base + REG_GCTL);
reg &= ~PORT_CAP_DIR;
reg |= PORT_SET_HOST; /* [13:12] 01: Host; 10: Device; 11: OTG */
writel(reg, base + REG_GCTL);
udelay(U_LEVEL2);
reg = readl(base + REG_GUSB3PIPECTL0);
reg &= ~PCS_SSP_SOFT_RESET;
reg &= ~PORT_DISABLE_SUSPEND; /* disable suspend */
writel(reg, base + REG_GUSB3PIPECTL0);
udelay(U_LEVEL2);
writel(USB2_G_TXTHRCFG, base + GTXTHRCFG);
writel(USB2_G_RXTHRCFG, base + GRXTHRCFG);
udelay(U_LEVEL2);
lotus_dwc_data_width_config(index, PHY_WIDTH_16BIT);
reg = readl(base + GUSB2PHYCFG);
reg &= ~SUSPENDUSB20; /* disable suspend */
reg &= ~ENBLSLPM; /* suspend not transfer to external PHY */
reg &= ~U2_FREECLK_EXISTS;
reg &= ~LSIPD_MASK;
reg |= LSIPD_3_BIT;
writel(reg, base + GUSB2PHYCFG);
#ifdef CONFIG_USB_DUAL_PORT_SUPPORT
if (index == LOTUS_DWC_U2) {
#else
if (index == LOTUS_DWC_U2 ||
(index == LOTUS_DWC_U3 && !lotus_chip_u3_ability())) {
#endif
USB_DEBUG("U2 GUSB2PHYCFG(0xc200):0x%X\n",
readl(base + GUSB2PHYCFG));
USB_DEBUG("U2 GUSB3PIPECTL0(0xc2c0):0x%X\n",
readl(base + REG_GUSB3PIPECTL0));
USB_DEBUG("U2 GCTL(0xc110):0x%X\n",
readl(base + REG_GCTL));
#ifdef CONFIG_USB_DUAL_PORT_SUPPORT
} else if (index == LOTUS_DWC_U3) {
#else
} else if ((index == LOTUS_DWC_U3 && lotus_chip_u3_ability())) {
#endif
USB_DEBUG("U3 GUSB2PHYCFG(0xc200):0x%X\n",
readl(base + GUSB2PHYCFG));
USB_DEBUG("U3 GUSB3PIPECTL0(0xc2c0):0x%X\n",
readl(base + REG_GUSB3PIPECTL0));
USB_DEBUG("U3 GCTL(0xc110):0x%X\n",
readl(base + REG_GCTL));
}
}
#ifdef CONFIG_USB_DUAL_PORT_SUPPORT
void phy_usb_init(int index, int type)
#else
void phy_usb_init(int index)
#endif
{
#ifdef CONFIG_USB_DUAL_PORT_SUPPORT
unsigned int reg;
/* 7605v11 only support u2 host */
if (!lotus_chip_u3_ability() && index == LOTUS_DWC_U3)
return;
usb_scene_select(index, type);
#else
usb_scene_select(index);
#endif
#ifdef CONFIG_USB_DUAL_PORT_SUPPORT
reg = readl(MISC_REG_BASE + MISC_LOTUS_PHY2_CTRL_REG);
reg &= MISC_USB_SCENE_MASK;
/* Scene2 don't init U2 host. */
if (index == LOTUS_DWC_U2 && reg)
return;
if (index == LOTUS_DWC_U3) {
#else
if (index == LOTUS_DWC_U3 && lotus_chip_u3_ability()) {
#endif
printf("USB3 init...\n");
/* config u3 ctrl/u3 phy/u2 phy */
lotus_u3_ctrl_misc_config();
lotus_usb3_vbus_valid_en();
lotus_u2_phy_misc_config();
lotus_usb3_clk_init();
lotus_u3_phy_misc_config();
lotus_u2_phy_clk_init();
lotus_dwc_usb_config(index);
#ifdef CONFIG_USB_DUAL_PORT_SUPPORT
} else if (index == LOTUS_DWC_U2) {
#else
} else if ((index == LOTUS_DWC_U2) ||
(index == LOTUS_DWC_U3 && !lotus_chip_u3_ability())) {
#endif
printf("USB2 init...\n");
/* config u2 ctrl and u2 phy */
lotus_u2_ctrl_misc_config();
lotus_usb2_vbus_valid_en();
lotus_u2_phy_misc_config();
lotus_usb2_clk_init();
lotus_dwc_usb_config(index);
/* u2 phy eye diagram config */
lotus_u2_phy_eye_config();
}
/* According to IC, usb phy clk init need 1ms delay */
mdelay(1);
}
EXPORT_SYMBOL(phy_usb_init);
void xhci_hcd_stop(int index)
{
if (index == LOTUS_DWC_U3 && lotus_chip_u3_ability())
lotus_usb3_clk_exit();
else if ((index == LOTUS_DWC_U2) ||
(index == LOTUS_DWC_U3 && !lotus_chip_u3_ability()))
lotus_usb2_clk_exit();
}
EXPORT_SYMBOL(xhci_hcd_stop);
/* Some usb device can't be recognize in U-boot because
* of error state, so we power off them and wait a delay
* to restart them, similar to unplug and then insert.
*/
void lotus_usb_pwren_control(void)
{
u32 reg, default_val;
int delay __attribute__((unused));
#ifndef CONFIG_USB_DUAL_PORT_SUPPORT
delay = CONFIG_USB_PWREN_OFF_DELAY ?
CONFIG_USB_PWREN_OFF_DELAY : 100;
#endif
reg = readl(MISC_REG_BASE + MISC_USB2_CTRL_REG);
default_val = reg;
reg &= ~U2_PWREN_MODE_MASK; /* set PWREN_MISC mode */
reg |= U2_PWREN_OFF; /* turn off PWREN */
writel(reg, MISC_REG_BASE + MISC_USB2_CTRL_REG);
#ifndef CONFIG_USB_DUAL_PORT_SUPPORT
mdelay(delay);
#endif
reg = readl(MISC_REG_BASE + MISC_USB2_CTRL_REG);
reg &= ~U2_PWREN_MASK; /* turn on PWREN */
writel(reg, MISC_REG_BASE + MISC_USB2_CTRL_REG);
reg = default_val; /* set PWREN_CTRL mode(default val) */
writel(reg, MISC_REG_BASE + MISC_USB2_CTRL_REG);
/* U3 CTRL PWREN control */
reg = readl(MISC_REG_BASE + MISC_USB3_CTRL_REG);
default_val = reg;
reg &= ~U3_PWREN_MODE_MASK;
reg |= U3_PWREN_OFF; /* turn off PWREN */
writel(reg, MISC_REG_BASE + MISC_USB3_CTRL_REG);
USB_DEBUG("U3 MISC GCTL(0x1110):0x%X\n",
readl(MISC_REG_BASE + MISC_USB3_CTRL_REG));
#ifndef CONFIG_USB_DUAL_PORT_SUPPORT
mdelay(delay);
#endif
reg = readl(MISC_REG_BASE + MISC_USB3_CTRL_REG);
reg &= ~U3_PWREN_MASK; /* turn on PWREN */
writel(reg, MISC_REG_BASE + MISC_USB3_CTRL_REG);
USB_DEBUG("U3 MISC GCTL(0x1110):0x%X\n",
readl(MISC_REG_BASE + MISC_USB3_CTRL_REG));
reg = default_val; /* set PWREN_CTRL mode(default val) */
writel(reg, MISC_REG_BASE + MISC_USB3_CTRL_REG);
USB_DEBUG("U3 MISC GCTL(0x1110):0x%X\n",
readl(MISC_REG_BASE + MISC_USB3_CTRL_REG));
}
@@ -0,0 +1,307 @@
/*
* Copyright (c) LOTUS. All rights reserved.
*/
#ifdef CONFIG_PHY_LOTUS_USB
#include <asm/arch/platform.h>
#include <dm.h>
#include <linux/lotus/usb.h>
#include <usb/xhci.h>
#include "phy-usb.h"
/* offset 0x140 */
#define USB2_CTRL 0x140
#define USB2_CRG_DEFAULT_VAL 0x3b2f
#define USB2_UTMI_CKEN (0x1 << 12)
#define USB2_PHY_APB_CKEN (0x1 << 11)
#define USB2_REF_CKEN (0x1 << 9)
#define USB2_BUS_CKEN (0x1 << 8)
#define USB2_PHY_PLL_CKEN (0x1 << 4)
#define USB2_PHY_XTAL_CKEN (0x1 << 2)
#define USB2_FREECLK_CKSEL (0x1 << 13)
#define USB2_PHY_APB_RST (0x1 << 10)
#define USB2_VCC_SRST_REQ (0x1 << 3)
#define USB2_PHY_REQ (0x1 << 0)
#define USB2_PHY_PORT_TREQ (0x1 << 1)
#define GTXTHRCFG 0xc108
#define GRXTHRCFG 0xc10c
#define REG_GCTL 0xc110
#define REG_GUSB3PIPECTL0 0xc2c0
#define PCS_SSP_SOFT_RESET (0x1 << 31)
#define PORT_CAP_DIR (0x3 << 12)
#define PORT_SET_HOST (0x1 << 12)
#define PORT_DISABLE_SUSPEND (0x1 << 17)
#define USB2_G_TXTHRCFG 0x23100000
#define USB2_G_RXTHRCFG 0x23100000
/* PHY base register */
#define USB2_PHY_BASE_REG 0x100D0000
#define RG_PLL_EN_MASK 0x0003
#define RG_PLL_EN_VAL 0x0003
#define PHY_PLL_OFFSET 0x0014
#define USB_VBUS_IO_CONFIG_REG 0x100c007C
#define USB_VBUS_IO_CONFIG_VAL 0x0531
#define USB_PWREN_CONFIG_REG 0x100c0080
#define USB_PWREN_CONFIG_VAL 0x1
/* PHY eye config */
#define PHY_ANA_CFG_0_OFFSET 0x00
#define PHY_PRE_DRIVE_MASK (0xf << 24)
#define PHY_PRE_DRIVE_VAL (0x4 << 24)
#define PHY_HSTX_DE_MASK (0xf << 8)
#define PHY_HSTX_DE_VAL (0xc << 8)
#define PHY_HSTX_DEEN_BIT (0x1 << 5)
#define PHY_ANA_CFG_2_OFFSET 0x08
#define PHY_TX_TEST_BIT (0x1 << 20)
#define PHY_HALF_DEEM_BIT (0x1 << 21)
#define PHY_DISCONNECT_REFERENCE_MASK (0x7 << 16)
#define PHY_DISCONNECT_REFERENCE_VAL (0x2 << 16)
#define PHY_ANA_CFG_4_OFFSET 0x10
#define PHY_TX_REFERENCE_MASK (0x7 << 4)
#define PHY_TX_REFERENCE_VAL (0x5 << 4)
#define PHY_SQUELCH_REFERENCE_MASK (0x7 << 0)
#define PHY_SQUELCH_REFERENCE_VAL (0x5 << 0)
/* PHY trim config */
#define USB_TRIM_BASE_REG 0x12028004
#define USB_TRIM_VAL_MASK 0x001F
#define USB_TRIM_VAL_MIN 0x0009
#define USB_TRIM_VAL_MAX 0x001D
#define USB2_TRIM_OFFSET 0x0008
#define USB2_TRIM_MASK 0x1f00
#define usb2_trim_val(a) (((a) << 8) & USB2_TRIM_MASK)
#define USB2_TRIM_DEFAULT_VAL 0x000F
static uintptr_t xhci_base = 0;
int xhci_hcd_init(int index, struct xhci_hccr **hccr, struct xhci_hcor **hcor)
{
if ((hccr == NULL) || (hcor == NULL))
return -EINVAL;
xhci_base = USB3_CTRL_REG_BASE;
*hccr = (struct xhci_hccr *)(xhci_base);
*hcor = (struct xhci_hcor *)((uintptr_t) *hccr +
HC_LENGTH(xhci_readl(&(*hccr)->cr_capbase)));
return 0;
}
void usb2_eye_config(void)
{
unsigned int reg;
/* HSTX pre-drive strength */
reg = readl(USB2_PHY_BASE_REG + PHY_ANA_CFG_0_OFFSET);
reg &= ~PHY_PRE_DRIVE_MASK;
reg |= PHY_PRE_DRIVE_VAL;
writel(reg, USB2_PHY_BASE_REG + PHY_ANA_CFG_0_OFFSET);
/* HSTX de-emphasis strength */
reg = readl(USB2_PHY_BASE_REG + PHY_ANA_CFG_0_OFFSET);
reg &= ~PHY_HSTX_DE_MASK;
reg |= PHY_HSTX_DE_VAL;
writel(reg, USB2_PHY_BASE_REG + PHY_ANA_CFG_0_OFFSET);
/* HSTX de-emphasis enable */
reg = readl(USB2_PHY_BASE_REG + PHY_ANA_CFG_0_OFFSET);
reg |= PHY_HSTX_DEEN_BIT;
writel(reg, USB2_PHY_BASE_REG + PHY_ANA_CFG_0_OFFSET);
/* TX test bit */
reg = readl(USB2_PHY_BASE_REG + PHY_ANA_CFG_2_OFFSET);
reg |= PHY_TX_TEST_BIT;
writel(reg, USB2_PHY_BASE_REG + PHY_ANA_CFG_2_OFFSET);
/* TX half de-emphasis bit */
reg = readl(USB2_PHY_BASE_REG + PHY_ANA_CFG_2_OFFSET);
reg |= PHY_HALF_DEEM_BIT;
writel(reg, USB2_PHY_BASE_REG + PHY_ANA_CFG_2_OFFSET);
/* Disconnect reference voltage sel */
reg = readl(USB2_PHY_BASE_REG + PHY_ANA_CFG_2_OFFSET);
reg &= ~PHY_DISCONNECT_REFERENCE_MASK;
reg |= PHY_DISCONNECT_REFERENCE_VAL;
writel(reg, USB2_PHY_BASE_REG + PHY_ANA_CFG_2_OFFSET);
/* TX reference voltage sel */
reg = readl(USB2_PHY_BASE_REG + PHY_ANA_CFG_4_OFFSET);
reg &= ~PHY_TX_REFERENCE_MASK;
reg |= PHY_TX_REFERENCE_VAL;
writel(reg, USB2_PHY_BASE_REG + PHY_ANA_CFG_4_OFFSET);
/* Squlech reference voltage sel */
reg = readl(USB2_PHY_BASE_REG + PHY_ANA_CFG_4_OFFSET);
reg &= ~PHY_SQUELCH_REFERENCE_MASK;
reg |= PHY_SQUELCH_REFERENCE_VAL;
writel(reg, USB2_PHY_BASE_REG + PHY_ANA_CFG_4_OFFSET);
}
void usb2_trim_config(void)
{
unsigned int ret;
unsigned int reg;
unsigned int trim_val;
ret = readl(USB_TRIM_BASE_REG);
trim_val = (ret & USB_TRIM_VAL_MASK); /* get usb trim value */
reg = readl(USB2_PHY_BASE_REG + USB2_TRIM_OFFSET);
reg &= ~USB2_TRIM_MASK;
if ((trim_val >= USB_TRIM_VAL_MIN) && (trim_val <= USB_TRIM_VAL_MAX))
reg |= usb2_trim_val(trim_val);
else
reg |= usb2_trim_val(USB2_TRIM_DEFAULT_VAL);
writel(reg, USB2_PHY_BASE_REG + USB2_TRIM_OFFSET);
}
void phy_usb_init_crg_clk(int index)
{
unsigned int reg;
/* set usb2 CRG default val */
reg = USB2_CRG_DEFAULT_VAL;
writel(reg, CRG_REG_BASE + USB2_CTRL);
udelay(U_LEVEL6);
/* open UTMI clk */
reg = readl(CRG_REG_BASE + USB2_CTRL);
reg |= USB2_UTMI_CKEN;
writel(reg, CRG_REG_BASE + USB2_CTRL);
/* open phy apb clk */
reg = readl(CRG_REG_BASE + USB2_CTRL);
reg |= USB2_PHY_APB_CKEN;
writel(reg, CRG_REG_BASE + USB2_CTRL);
/* open ctrl ref clk */
reg = readl(CRG_REG_BASE + USB2_CTRL);
reg |= USB2_REF_CKEN;
writel(reg, CRG_REG_BASE + USB2_CTRL);
/* open bus clk */
reg = readl(CRG_REG_BASE + USB2_CTRL);
reg |= USB2_BUS_CKEN;
writel(reg, CRG_REG_BASE + USB2_CTRL);
/* open phy pll clk */
reg = readl(CRG_REG_BASE + USB2_CTRL);
reg |= USB2_PHY_PLL_CKEN;
writel(reg, CRG_REG_BASE + USB2_CTRL);
/* open phy xtal clk */
reg = readl(CRG_REG_BASE + USB2_CTRL);
reg |= USB2_PHY_XTAL_CKEN;
writel(reg, CRG_REG_BASE + USB2_CTRL);
/* freeclk_cksel_free */
reg = readl(CRG_REG_BASE + USB2_CTRL);
reg |= USB2_FREECLK_CKSEL;
writel(reg, CRG_REG_BASE + USB2_CTRL);
udelay(U_LEVEL5);
}
void phy_usb_init(int index)
{
unsigned int reg;
/* VBUS config */
reg = USB_VBUS_IO_CONFIG_VAL;
writel(reg, USB_VBUS_IO_CONFIG_REG);
reg = USB_PWREN_CONFIG_VAL;
writel(reg, USB_PWREN_CONFIG_REG);
/* init crg and clk */
phy_usb_init_crg_clk(index);
/* release phy apb */
reg = readl(CRG_REG_BASE + USB2_CTRL);
reg &= ~USB2_PHY_APB_RST;
writel(reg, CRG_REG_BASE + USB2_CTRL);
udelay(U_LEVEL5);
/* por noreset */
reg = readl(CRG_REG_BASE + USB2_CTRL);
reg &= ~USB2_PHY_REQ;
writel(reg, CRG_REG_BASE + USB2_CTRL);
reg = readl(USB2_PHY_BASE_REG + PHY_PLL_OFFSET);
reg &= ~RG_PLL_EN_MASK;
reg |= RG_PLL_EN_VAL;
writel(reg, USB2_PHY_BASE_REG + PHY_PLL_OFFSET);
udelay(U_LEVEL10);
/* cancel TPOR */
reg = readl(CRG_REG_BASE + USB2_CTRL);
reg &= ~USB2_PHY_PORT_TREQ;
writel(reg, CRG_REG_BASE + USB2_CTRL);
udelay(U_LEVEL6);
/* vcc reset */
reg = readl(CRG_REG_BASE + USB2_CTRL);
reg &= ~USB2_VCC_SRST_REQ;
writel(reg, CRG_REG_BASE + USB2_CTRL);
/* USB2 Controller configs */
reg = readl(USB3_CTRL_REG_BASE + REG_GUSB3PIPECTL0);
reg |= PCS_SSP_SOFT_RESET;
writel(reg, USB3_CTRL_REG_BASE + REG_GUSB3PIPECTL0);
udelay(U_LEVEL2);
reg = readl(USB3_CTRL_REG_BASE + REG_GCTL);
reg &= ~PORT_CAP_DIR;
reg |= PORT_SET_HOST; /* [13:12] 01: Host; 10: Device; 11: OTG */
writel(reg, USB3_CTRL_REG_BASE + REG_GCTL);
udelay(U_LEVEL2);
reg = readl(USB3_CTRL_REG_BASE + REG_GUSB3PIPECTL0);
reg &= ~PCS_SSP_SOFT_RESET;
reg &= ~PORT_DISABLE_SUSPEND; /* disable suspend */
writel(reg, USB3_CTRL_REG_BASE + REG_GUSB3PIPECTL0);
udelay(U_LEVEL2);
writel(USB2_G_TXTHRCFG, USB3_CTRL_REG_BASE + GTXTHRCFG);
writel(USB2_G_RXTHRCFG, USB3_CTRL_REG_BASE + GRXTHRCFG);
udelay(U_LEVEL2);
/* USB2 eye config */
usb2_eye_config();
/* USB2 trim config */
usb2_trim_config();
}
EXPORT_SYMBOL(phy_usb_init);
void xhci_hcd_stop(int index)
{
unsigned int reg;
/* por noreset */
reg = readl(CRG_REG_BASE + USB2_CTRL);
reg |= USB2_PHY_REQ;
writel(reg, CRG_REG_BASE + USB2_CTRL);
udelay(U_LEVEL10);
/* cancel TPOR */
reg = readl(CRG_REG_BASE + USB2_CTRL);
reg |= USB2_PHY_PORT_TREQ;
writel(reg, CRG_REG_BASE + USB2_CTRL);
udelay(U_LEVEL6);
/* vcc reset */
reg = readl(CRG_REG_BASE + USB2_CTRL);
reg |= USB2_VCC_SRST_REQ;
writel(reg, CRG_REG_BASE + USB2_CTRL);
}
EXPORT_SYMBOL(xhci_hcd_stop);
#endif
@@ -0,0 +1,313 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) LOTUS. All rights reserved.
*/
#include <asm/arch/platform.h>
#include <dm.h>
#include <linux/lotus/usb.h>
#include <usb.h>
#include <usb/xhci.h>
#include "phy-usb.h"
#ifdef __USB_DEBUG
#define USB_DEBUG(...) printf(__VA_ARGS__)
#else
#define USB_DEBUG(...)
#endif
#define GTXTHRCFG 0xc108
#define GRXTHRCFG 0xc10c
#define REG_GCTL 0xc110
#define GUSB2PHYCFG 0xc200
#define REG_GUSB3PIPECTL0 0xc2c0
#define PCS_SSP_SOFT_RESET (0x1 << 31)
#define USBTRDTIM_MASK 0x00003c00
#define USBTRDTIM_16BIT (0x5 << 10)
#define USBTRDTIM_8BIT (0x9 << 10)
#define PHYIF (0x1 << 3)
#define SUSPENDUSB20 (0x1 << 6)
#define ENBLSLPM (0x1 << 8)
#define LSIPD_MASK (0x7 << 19)
#define LSIPD_3_BIT (0x2 << 19)
#define U2_FREECLK_EXISTS (0x1 << 30)
#define PORT_CAP_DIR (0x3 << 12)
#define PORT_SET_HOST (0x1 << 12)
#define PORT_DISABLE_SUSPEND (0x1 << 17)
#define USB2_G_TXTHRCFG 0x23100000
#define USB2_G_RXTHRCFG 0x23100000
/* CRG PERI_CRG80: usb clk & reset, offset 0x140 */
#define USB2_CTRL 0x140
#define USB2_CRG_DEF_VAL 0x1307
#define USB2_PHY_CFG_REQ (0x1 << 0)
#define USB2_PHY_PORT_SRST (0x1 << 1)
#define USB2_PHY_XTAL_CKEN (0x1 << 2)
#define USB2_VCC_SRST_REQ (0x1 << 3)
#define USB2_BUS_CKEN (0x1 << 8)
#define USB2_REF_CKEN (0x1 << 9)
#define USB2_UTMI_CKEN (0x1 << 12)
#define USB2_FREECLK_CKSEL (0x1 << 13)
/* u2 ctrl use reg, reg's misc_base=0x12028000 */
#define MISC_USB2_CTRL_REG 0x24
#define U2_BUS_FILTER_BYPASS (0xf << 0)
#define U2_PWREN_MODE_MASK (0x1 << 4)
#define U2_PWREN_MODE_MISC (0x0 << 4)
#define U2_PWREN_MODE_CTRL (0x1 << 4)
#define U2_OVERCURR_MODE_MASK (0x1 << 5)
#define U2_OVERCURR_MODE_DISABLE (0x0 << 5)
#define U2_OVERCURR_MODE_ENABLE (0x1 << 5)
#define U2_PWREN_MASK (0x1 << 6)
#define U2_PWREN_ON (0x0 << 6)
#define U2_PWREN_OFF (0x1 << 6)
#define U2_OVERCURR_MASK (0x1 << 7)
#define U2_OVERCURR_ON (0x0 << 7)
#define U2_OVERCURR_OFF (0x1 << 7)
#define U2_FLADJ_30MHZ (0x20 << 13)
/* Outer phy use reg, reg's misc_base=0x12028000 */
#define MISC_OUT_PHY2_CTRL_REG 0x2C
#define USB2_PLL_EN (0x1 << 0)
#define USB2_UTMI_DATABUS16_8 (0x1 << 1)
#define MISC_USB2_VBUS_REG 0x980
#define U2_VBUS_EN (0x1 << 0)
static uintptr_t xhci_base;
int xhci_hcd_init(int index, struct xhci_hccr **hccr, struct xhci_hcor **hcor)
{
if ((hccr == NULL) || (hcor == NULL))
return -EINVAL;
xhci_base = USB2_CTRL_REG_BASE;
*hccr = (struct xhci_hccr *)(xhci_base);
*hcor = (struct xhci_hcor *)((uintptr_t) *hccr +
HC_LENGTH(xhci_readl(&(*hccr)->cr_capbase)));
return 0;
}
void xhci_hcd_stop(int index)
{
unsigned int reg;
/* por reset */
reg = readl(CRG_REG_BASE + USB2_CTRL);
reg |= USB2_PHY_CFG_REQ;
reg |= USB2_PHY_PORT_SRST;
writel(reg, CRG_REG_BASE + USB2_CTRL);
udelay(U_LEVEL10);
/* vcc reset */
reg = readl(CRG_REG_BASE + USB2_CTRL);
reg |= USB2_VCC_SRST_REQ;
writel(reg, CRG_REG_BASE + USB2_CTRL);
}
EXPORT_SYMBOL(xhci_hcd_stop);
static void usb2_eye_config(void)
{
}
static void usb2_trim_config(void)
{
}
static void lotus_u2_phy_clk_init(void)
{
unsigned int reg;
/* enable u2 phy clk and do reset */
reg = (USB2_PHY_CFG_REQ | USB2_PHY_PORT_SRST | \
USB2_PHY_XTAL_CKEN);
writel(reg, CRG_REG_BASE + USB2_CTRL);
USB_DEBUG("USB2_CRG(0x12010140), expext:0x7,"
"acutal:0x%X\n", readl(CRG_REG_BASE + USB2_CTRL));
udelay(1);
/* undo phy reset */
reg = readl(CRG_REG_BASE + USB2_CTRL);
reg &= ~USB2_PHY_CFG_REQ;
reg &= ~USB2_PHY_PORT_SRST;
writel(reg, CRG_REG_BASE + USB2_CTRL);
USB_DEBUG("USB2_CRG(0x12010140), expext:0x4,"
"acutal:0x%X\n", readl(CRG_REG_BASE + USB2_CTRL));
}
static void lotus_u2_ctrl_clk_init(void)
{
unsigned int reg;
/* enable u2 ctrl clk and do reset */
reg = readl(CRG_REG_BASE + USB2_CTRL);
reg |= (USB2_VCC_SRST_REQ | USB2_BUS_CKEN | USB2_REF_CKEN |
USB2_UTMI_CKEN);
writel(reg, CRG_REG_BASE + USB2_CTRL);
USB_DEBUG("USB2_CRG(0x12010140), expext:0x130c,"
"acutal:0x%X\n", readl(CRG_REG_BASE + USB2_CTRL));
udelay(1);
/* undo ctrl vcc reset */
reg = readl(CRG_REG_BASE + USB2_CTRL);
reg &= ~USB2_VCC_SRST_REQ;
writel(reg, CRG_REG_BASE + USB2_CTRL);
USB_DEBUG("USB2_CRG(0x12010140), expext:0x1304,"
"acutal:0x%X\n", readl(CRG_REG_BASE + USB2_CTRL));
}
static void lotus_usb2_clk_init(void)
{
lotus_u2_phy_clk_init();
lotus_u2_ctrl_clk_init();
}
static void lotus_u2_phy_misc_config(void)
{
unsigned int reg;
/* MISC_CTRL11: config 0x3 according to IC. */
reg = 0;
reg |= (USB2_PLL_EN | USB2_UTMI_DATABUS16_8);
writel(reg, MISC_REG_BASE + MISC_OUT_PHY2_CTRL_REG);
USB_DEBUG("MISC_OUT_PHY2_CTRL_REG(0x1202802c),"
"expext:0x3, acutal:0x%X\n",
readl(MISC_REG_BASE + MISC_OUT_PHY2_CTRL_REG));
}
static inline void lotus_usb2_vbus_valid_en(void)
{
unsigned int reg;
/* enable U2 vbus valid */
reg = readl(MISC_REG_BASE + MISC_USB2_VBUS_REG);
reg |= U2_VBUS_EN;
writel(reg, MISC_REG_BASE + MISC_USB2_VBUS_REG);
}
static void lotus_dwc_data_width_config(enum usb_phy_width_mode mode)
{
unsigned int reg, base = USB2_CTRL_REG_BASE;
/* GUSB2PHYCFG register: config val 0x40001548(HS/FS) or
* 0x00381548(LS) according to IC.
*
* USBTRDTIM: USB 2.0 Turnaround Time in PHY clocks.
* PHYIF: 1 UTMI+ PHY is 16-bit, 0 UTMI+ PHY is 8-bit.
*/
reg = 0;
switch (mode) {
case PHY_WIDTH_8BIT:
/* 8-bits usb phy configure */
reg |= USBTRDTIM_8BIT;
reg &= ~PHYIF;
break;
case PHY_WIDTH_16BIT:
/* 16-bits usb phy configure */
reg |= (USBTRDTIM_16BIT | PHYIF);
break;
default:
printf("usb phy width config val error.\n");
return;
}
writel(reg, base + GUSB2PHYCFG);
}
static void lotus_dwc_usb_config(void)
{
unsigned int reg, base = USB2_CTRL_REG_BASE;
reg = readl(base + REG_GUSB3PIPECTL0);
reg |= PCS_SSP_SOFT_RESET;
writel(reg, base + REG_GUSB3PIPECTL0);
udelay(U_LEVEL2);
reg = readl(base + REG_GCTL);
reg &= ~PORT_CAP_DIR;
reg |= PORT_SET_HOST; /* host mode is default. */
writel(reg, base + REG_GCTL);
udelay(U_LEVEL2);
reg = readl(base + REG_GUSB3PIPECTL0);
reg &= ~PCS_SSP_SOFT_RESET;
reg &= ~PORT_DISABLE_SUSPEND; /* disable suspend */
writel(reg, base + REG_GUSB3PIPECTL0);
udelay(U_LEVEL2);
writel(USB2_G_TXTHRCFG, base + GTXTHRCFG);
writel(USB2_G_RXTHRCFG, base + GRXTHRCFG);
udelay(U_LEVEL2);
lotus_dwc_data_width_config(PHY_WIDTH_16BIT);
reg = readl(base + GUSB2PHYCFG);
reg &= ~SUSPENDUSB20; /* disable suspend */
reg &= ~ENBLSLPM; /* suspend not transfer to external PHY */
reg &= ~U2_FREECLK_EXISTS;
reg &= ~LSIPD_MASK;
reg |= LSIPD_3_BIT;
writel(reg, base + GUSB2PHYCFG);
USB_DEBUG("GUSB2PHYCFG(0xc200):0x%X\n",
readl(base + GUSB2PHYCFG));
USB_DEBUG("GUSB3PIPECTL0(0xc2c0):0x%X\n",
readl(base + REG_GUSB3PIPECTL0));
USB_DEBUG("GCTL(0xc110):0x%X\n",
readl(base + REG_GCTL));
}
void phy_usb_init(int index)
{
/* u2 utmi data bus config, u2 phy pll en */
lotus_u2_phy_misc_config();
/* init crg and clk */
lotus_usb2_clk_init();
/* enable u2 vbusvalid */
lotus_usb2_vbus_valid_en();
/* USB2 Controller configs */
lotus_dwc_usb_config();
/* lotus phy eye diagram config */
usb2_eye_config();
/* lotus phy trim config */
usb2_trim_config();
}
EXPORT_SYMBOL(phy_usb_init);
/* Some usb device can't be recognize in U-boot because
* of error state, so we power off them and wait a delay
* to restart them, similar to unplug and then insert.
*/
void lotus_usb_pwren_control(void)
{
u32 reg, default_val, delay;
delay = CONFIG_USB_PWREN_OFF_DELAY ?
CONFIG_USB_PWREN_OFF_DELAY : 100;
reg = readl(MISC_REG_BASE + MISC_USB2_CTRL_REG);
default_val = reg;
reg &= ~U2_PWREN_MODE_MASK; /* set PWREN_MISC mode */
reg |= U2_PWREN_OFF; /* turn off PWREN */
writel(reg, MISC_REG_BASE + MISC_USB2_CTRL_REG);
mdelay(delay);
reg = readl(MISC_REG_BASE + MISC_USB2_CTRL_REG);
reg &= ~U2_PWREN_MASK; /* turn on PWREN */
writel(reg, MISC_REG_BASE + MISC_USB2_CTRL_REG);
reg = default_val; /* set PWREN_CTRL mode(default val) */
writel(reg, MISC_REG_BASE + MISC_USB2_CTRL_REG);
}
@@ -0,0 +1,307 @@
/*
* Copyright (c) LOTUS. All rights reserved.
*/
#ifdef CONFIG_PHY_LOTUS_USB
#include <asm/arch/platform.h>
#include <dm.h>
#include <linux/lotus/usb.h>
#include <usb/xhci.h>
#include "phy-usb.h"
/* offset 0x140 */
#define USB2_CTRL 0x140
#define USB2_CRG_DEFAULT_VAL 0x3b2f
#define USB2_UTMI_CKEN (0x1 << 12)
#define USB2_PHY_APB_CKEN (0x1 << 11)
#define USB2_REF_CKEN (0x1 << 9)
#define USB2_BUS_CKEN (0x1 << 8)
#define USB2_PHY_PLL_CKEN (0x1 << 4)
#define USB2_PHY_XTAL_CKEN (0x1 << 2)
#define USB2_FREECLK_CKSEL (0x1 << 13)
#define USB2_PHY_APB_RST (0x1 << 10)
#define USB2_VCC_SRST_REQ (0x1 << 3)
#define USB2_PHY_REQ (0x1 << 0)
#define USB2_PHY_PORT_TREQ (0x1 << 1)
#define GTXTHRCFG 0xc108
#define GRXTHRCFG 0xc10c
#define REG_GCTL 0xc110
#define REG_GUSB3PIPECTL0 0xc2c0
#define PCS_SSP_SOFT_RESET (0x1 << 31)
#define PORT_CAP_DIR (0x3 << 12)
#define PORT_SET_HOST (0x1 << 12)
#define PORT_DISABLE_SUSPEND (0x1 << 17)
#define USB2_G_TXTHRCFG 0x23100000
#define USB2_G_RXTHRCFG 0x23100000
/* PHY base register */
#define USB2_PHY_BASE_REG 0x100D0000
#define RG_PLL_EN_MASK 0x0003
#define RG_PLL_EN_VAL 0x0003
#define PHY_PLL_OFFSET 0x0014
#define USB_VBUS_IO_CONFIG_REG 0x100c007C
#define USB_VBUS_IO_CONFIG_VAL 0x0531
#define USB_PWREN_CONFIG_REG 0x100c0080
#define USB_PWREN_CONFIG_VAL 0x1
/* PHY eye config */
#define PHY_ANA_CFG_0_OFFSET 0x00
#define PHY_PRE_DRIVE_MASK (0xf << 24)
#define PHY_PRE_DRIVE_VAL (0x4 << 24)
#define PHY_HSTX_DE_MASK (0xf << 8)
#define PHY_HSTX_DE_VAL (0xc << 8)
#define PHY_HSTX_DEEN_BIT (0x1 << 5)
#define PHY_ANA_CFG_2_OFFSET 0x08
#define PHY_TX_TEST_BIT (0x1 << 20)
#define PHY_HALF_DEEM_BIT (0x1 << 21)
#define PHY_DISCONNECT_REFERENCE_MASK (0x7 << 16)
#define PHY_DISCONNECT_REFERENCE_VAL (0x2 << 16)
#define PHY_ANA_CFG_4_OFFSET 0x10
#define PHY_TX_REFERENCE_MASK (0x7 << 4)
#define PHY_TX_REFERENCE_VAL (0x5 << 4)
#define PHY_SQUELCH_REFERENCE_MASK (0x7 << 0)
#define PHY_SQUELCH_REFERENCE_VAL (0x5 << 0)
/* PHY trim config */
#define USB_TRIM_BASE_REG 0x12028004
#define USB_TRIM_VAL_MASK 0x001F
#define USB_TRIM_VAL_MIN 0x0009
#define USB_TRIM_VAL_MAX 0x001D
#define USB2_TRIM_OFFSET 0x0008
#define USB2_TRIM_MASK 0x1f00
#define usb2_trim_val(a) (((a) << 8) & USB2_TRIM_MASK)
#define USB2_TRIM_DEFAULT_VAL 0x000F
static uintptr_t xhci_base = 0;
int xhci_hcd_init(int index, struct xhci_hccr **hccr, struct xhci_hcor **hcor)
{
if ((hccr == NULL) || (hcor == NULL))
return -EINVAL;
xhci_base = USB3_CTRL_REG_BASE;
*hccr = (struct xhci_hccr *)(xhci_base);
*hcor = (struct xhci_hcor *)((uintptr_t) *hccr +
HC_LENGTH(xhci_readl(&(*hccr)->cr_capbase)));
return 0;
}
void usb2_eye_config(void)
{
unsigned int reg;
/* HSTX pre-drive strength */
reg = readl(USB2_PHY_BASE_REG + PHY_ANA_CFG_0_OFFSET);
reg &= ~PHY_PRE_DRIVE_MASK;
reg |= PHY_PRE_DRIVE_VAL;
writel(reg, USB2_PHY_BASE_REG + PHY_ANA_CFG_0_OFFSET);
/* HSTX de-emphasis strength */
reg = readl(USB2_PHY_BASE_REG + PHY_ANA_CFG_0_OFFSET);
reg &= ~PHY_HSTX_DE_MASK;
reg |= PHY_HSTX_DE_VAL;
writel(reg, USB2_PHY_BASE_REG + PHY_ANA_CFG_0_OFFSET);
/* HSTX de-emphasis enable */
reg = readl(USB2_PHY_BASE_REG + PHY_ANA_CFG_0_OFFSET);
reg |= PHY_HSTX_DEEN_BIT;
writel(reg, USB2_PHY_BASE_REG + PHY_ANA_CFG_0_OFFSET);
/* TX test bit */
reg = readl(USB2_PHY_BASE_REG + PHY_ANA_CFG_2_OFFSET);
reg |= PHY_TX_TEST_BIT;
writel(reg, USB2_PHY_BASE_REG + PHY_ANA_CFG_2_OFFSET);
/* TX half de-emphasis bit */
reg = readl(USB2_PHY_BASE_REG + PHY_ANA_CFG_2_OFFSET);
reg |= PHY_HALF_DEEM_BIT;
writel(reg, USB2_PHY_BASE_REG + PHY_ANA_CFG_2_OFFSET);
/* Disconnect reference voltage sel */
reg = readl(USB2_PHY_BASE_REG + PHY_ANA_CFG_2_OFFSET);
reg &= ~PHY_DISCONNECT_REFERENCE_MASK;
reg |= PHY_DISCONNECT_REFERENCE_VAL;
writel(reg, USB2_PHY_BASE_REG + PHY_ANA_CFG_2_OFFSET);
/* TX reference voltage sel */
reg = readl(USB2_PHY_BASE_REG + PHY_ANA_CFG_4_OFFSET);
reg &= ~PHY_TX_REFERENCE_MASK;
reg |= PHY_TX_REFERENCE_VAL;
writel(reg, USB2_PHY_BASE_REG + PHY_ANA_CFG_4_OFFSET);
/* Squlech reference voltage sel */
reg = readl(USB2_PHY_BASE_REG + PHY_ANA_CFG_4_OFFSET);
reg &= ~PHY_SQUELCH_REFERENCE_MASK;
reg |= PHY_SQUELCH_REFERENCE_VAL;
writel(reg, USB2_PHY_BASE_REG + PHY_ANA_CFG_4_OFFSET);
}
void usb2_trim_config(void)
{
unsigned int ret;
unsigned int reg;
unsigned int trim_val;
ret = readl(USB_TRIM_BASE_REG);
trim_val = (ret & USB_TRIM_VAL_MASK); /* get usb trim value */
reg = readl(USB2_PHY_BASE_REG + USB2_TRIM_OFFSET);
reg &= ~USB2_TRIM_MASK;
if ((trim_val >= USB_TRIM_VAL_MIN) && (trim_val <= USB_TRIM_VAL_MAX))
reg |= usb2_trim_val(trim_val);
else
reg |= usb2_trim_val(USB2_TRIM_DEFAULT_VAL);
writel(reg, USB2_PHY_BASE_REG + USB2_TRIM_OFFSET);
}
void phy_usb_init_crg_clk(int index)
{
unsigned int reg;
/* set usb2 CRG default val */
reg = USB2_CRG_DEFAULT_VAL;
writel(reg, CRG_REG_BASE + USB2_CTRL);
udelay(U_LEVEL6);
/* open UTMI clk */
reg = readl(CRG_REG_BASE + USB2_CTRL);
reg |= USB2_UTMI_CKEN;
writel(reg, CRG_REG_BASE + USB2_CTRL);
/* open phy apb clk */
reg = readl(CRG_REG_BASE + USB2_CTRL);
reg |= USB2_PHY_APB_CKEN;
writel(reg, CRG_REG_BASE + USB2_CTRL);
/* open ctrl ref clk */
reg = readl(CRG_REG_BASE + USB2_CTRL);
reg |= USB2_REF_CKEN;
writel(reg, CRG_REG_BASE + USB2_CTRL);
/* open bus clk */
reg = readl(CRG_REG_BASE + USB2_CTRL);
reg |= USB2_BUS_CKEN;
writel(reg, CRG_REG_BASE + USB2_CTRL);
/* open phy pll clk */
reg = readl(CRG_REG_BASE + USB2_CTRL);
reg |= USB2_PHY_PLL_CKEN;
writel(reg, CRG_REG_BASE + USB2_CTRL);
/* open phy xtal clk */
reg = readl(CRG_REG_BASE + USB2_CTRL);
reg |= USB2_PHY_XTAL_CKEN;
writel(reg, CRG_REG_BASE + USB2_CTRL);
/* freeclk_cksel_free */
reg = readl(CRG_REG_BASE + USB2_CTRL);
reg |= USB2_FREECLK_CKSEL;
writel(reg, CRG_REG_BASE + USB2_CTRL);
udelay(U_LEVEL5);
}
void phy_usb_init(int index)
{
unsigned int reg;
/* VBUS config */
reg = USB_VBUS_IO_CONFIG_VAL;
writel(reg, USB_VBUS_IO_CONFIG_REG);
reg = USB_PWREN_CONFIG_VAL;
writel(reg, USB_PWREN_CONFIG_REG);
/* init crg and clk */
phy_usb_init_crg_clk(index);
/* release phy apb */
reg = readl(CRG_REG_BASE + USB2_CTRL);
reg &= ~USB2_PHY_APB_RST;
writel(reg, CRG_REG_BASE + USB2_CTRL);
udelay(U_LEVEL5);
/* por noreset */
reg = readl(CRG_REG_BASE + USB2_CTRL);
reg &= ~USB2_PHY_REQ;
writel(reg, CRG_REG_BASE + USB2_CTRL);
reg = readl(USB2_PHY_BASE_REG + PHY_PLL_OFFSET);
reg &= ~RG_PLL_EN_MASK;
reg |= RG_PLL_EN_VAL;
writel(reg, USB2_PHY_BASE_REG + PHY_PLL_OFFSET);
udelay(U_LEVEL10);
/* cancel TPOR */
reg = readl(CRG_REG_BASE + USB2_CTRL);
reg &= ~USB2_PHY_PORT_TREQ;
writel(reg, CRG_REG_BASE + USB2_CTRL);
udelay(U_LEVEL6);
/* vcc reset */
reg = readl(CRG_REG_BASE + USB2_CTRL);
reg &= ~USB2_VCC_SRST_REQ;
writel(reg, CRG_REG_BASE + USB2_CTRL);
/* USB2 Controller configs */
reg = readl(USB3_CTRL_REG_BASE + REG_GUSB3PIPECTL0);
reg |= PCS_SSP_SOFT_RESET;
writel(reg, USB3_CTRL_REG_BASE + REG_GUSB3PIPECTL0);
udelay(U_LEVEL2);
reg = readl(USB3_CTRL_REG_BASE + REG_GCTL);
reg &= ~PORT_CAP_DIR;
reg |= PORT_SET_HOST; /* [13:12] 01: Host; 10: Device; 11: OTG */
writel(reg, USB3_CTRL_REG_BASE + REG_GCTL);
udelay(U_LEVEL2);
reg = readl(USB3_CTRL_REG_BASE + REG_GUSB3PIPECTL0);
reg &= ~PCS_SSP_SOFT_RESET;
reg &= ~PORT_DISABLE_SUSPEND; /* disable suspend */
writel(reg, USB3_CTRL_REG_BASE + REG_GUSB3PIPECTL0);
udelay(U_LEVEL2);
writel(USB2_G_TXTHRCFG, USB3_CTRL_REG_BASE + GTXTHRCFG);
writel(USB2_G_RXTHRCFG, USB3_CTRL_REG_BASE + GRXTHRCFG);
udelay(U_LEVEL2);
/* USB2 eye config */
usb2_eye_config();
/* USB2 trim config */
usb2_trim_config();
}
EXPORT_SYMBOL(phy_usb_init);
void xhci_hcd_stop(int index)
{
unsigned int reg;
/* por noreset */
reg = readl(CRG_REG_BASE + USB2_CTRL);
reg |= USB2_PHY_REQ;
writel(reg, CRG_REG_BASE + USB2_CTRL);
udelay(U_LEVEL10);
/* cancel TPOR */
reg = readl(CRG_REG_BASE + USB2_CTRL);
reg |= USB2_PHY_PORT_TREQ;
writel(reg, CRG_REG_BASE + USB2_CTRL);
udelay(U_LEVEL6);
/* vcc reset */
reg = readl(CRG_REG_BASE + USB2_CTRL);
reg |= USB2_VCC_SRST_REQ;
writel(reg, CRG_REG_BASE + USB2_CTRL);
}
EXPORT_SYMBOL(xhci_hcd_stop);
#endif