From bc0e8d7c5d189c1566a73affad0087ccbe511bc9 Mon Sep 17 00:00:00 2001 From: WingMan Kwok Date: Fri, 5 Sep 2014 22:26:23 +0300 Subject: [PATCH] keystone: usb: add support of usb xhci MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Add support of usb xhci. xHCI controls all USB speeds of the Host mode, that is, the SS through the SS PHY, as well as the HS, FS, and LS through the USB2 PHY. xHCI replaces and supersedes all previous host HCIs (HS-only EHCI, FS/LS OHCI and UHCI), and is therefore not backwards compatible with any of them. The USB3SS’s USB Controller is fully compliant with xHC. Acked-by: Vitaly Andrianov Signed-off-by: WingMan Kwok Signed-off-by: Ivan Khoronzhuk --- arch/arm/include/asm/arch-keystone/hardware.h | 6 + .../include/asm/arch-keystone/xhci-keystone.h | 21 ++ drivers/usb/host/Makefile | 1 + drivers/usb/host/xhci-keystone.c | 329 ++++++++++++++++++ include/configs/ks2_evm.h | 15 + include/linux/usb/dwc3.h | 8 +- 6 files changed, 379 insertions(+), 1 deletion(-) create mode 100644 arch/arm/include/asm/arch-keystone/xhci-keystone.h create mode 100644 drivers/usb/host/xhci-keystone.c diff --git a/arch/arm/include/asm/arch-keystone/hardware.h b/arch/arm/include/asm/arch-keystone/hardware.h index ddc4be3b2f..c1642a59a0 100644 --- a/arch/arm/include/asm/arch-keystone/hardware.h +++ b/arch/arm/include/asm/arch-keystone/hardware.h @@ -163,6 +163,12 @@ typedef volatile unsigned int *dv_reg_p; /* MSMC control */ #define KS2_MSMC_CTRL_BASE 0x0bc00000 +/* USB */ +#define KS2_USB_SS_BASE 0x02680000 +#define KS2_USB_HOST_XHCI_BASE (KS2_USB_SS_BASE + 0x10000) +#define KS2_DEV_USB_PHY_BASE 0x02620738 +#define KS2_USB_PHY_CFG_BASE 0x02630000 + #ifdef CONFIG_SOC_K2HK #include #endif diff --git a/arch/arm/include/asm/arch-keystone/xhci-keystone.h b/arch/arm/include/asm/arch-keystone/xhci-keystone.h new file mode 100644 index 0000000000..3aab4e045f --- /dev/null +++ b/arch/arm/include/asm/arch-keystone/xhci-keystone.h @@ -0,0 +1,21 @@ +/* + * USB 3.0 DRD Controller + * + * (C) Copyright 2012-2014 + * Texas Instruments Incorporated, + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#define USB3_PHY_REF_SSP_EN BIT(29) +#define USB3_PHY_OTG_VBUSVLDECTSEL BIT(16) + +/* KEYSTONE2 XHCI PHY register structure */ +struct keystone_xhci_phy { + unsigned int phy_utmi; /* ctl0 */ + unsigned int phy_pipe; /* ctl1 */ + unsigned int phy_param_ctrl_1; /* ctl2 */ + unsigned int phy_param_ctrl_2; /* ctl3 */ + unsigned int phy_clock; /* ctl4 */ + unsigned int phy_pll; /* ctl5 */ +}; diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index c4f5157a84..3d3a0c42a5 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -43,5 +43,6 @@ obj-$(CONFIG_USB_EHCI_ZYNQ) += ehci-zynq.o # xhci obj-$(CONFIG_USB_XHCI) += xhci.o xhci-mem.o xhci-ring.o +obj-$(CONFIG_USB_XHCI_KEYSTONE) += xhci-keystone.o obj-$(CONFIG_USB_XHCI_EXYNOS) += xhci-exynos5.o obj-$(CONFIG_USB_XHCI_OMAP) += xhci-omap.o diff --git a/drivers/usb/host/xhci-keystone.c b/drivers/usb/host/xhci-keystone.c new file mode 100644 index 0000000000..05d338f261 --- /dev/null +++ b/drivers/usb/host/xhci-keystone.c @@ -0,0 +1,329 @@ +/* + * USB 3.0 DRD Controller + * + * (C) Copyright 2012-2014 + * Texas Instruments Incorporated, + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "xhci.h" + +struct kdwc3_irq_regs { + u32 revision; /* 0x000 */ + u32 rsvd0[3]; + u32 sysconfig; /* 0x010 */ + u32 rsvd1[1]; + u32 irq_eoi; + u32 rsvd2[1]; + struct { + u32 raw_status; + u32 status; + u32 enable_set; + u32 enable_clr; + } irqs[16]; +}; + +struct keystone_xhci { + struct xhci_hccr *hcd; + struct dwc3 *dwc3_reg; + struct xhci_hcor *hcor; + struct kdwc3_irq_regs *usbss; + struct keystone_xhci_phy *phy; +}; + +struct keystone_xhci keystone; + +static void keystone_xhci_phy_set(struct keystone_xhci_phy *phy) +{ + u32 val; + + /* + * VBUSVLDEXTSEL has a default value of 1 in BootCfg but shouldn't. + * It should always be cleared because our USB PHY has an onchip VBUS + * analog comparator. + */ + val = readl(&phy->phy_clock); + /* quit selecting the vbusvldextsel by default! */ + val &= ~USB3_PHY_OTG_VBUSVLDECTSEL; + writel(val, &phy->phy_clock); +} + +static void keystone_xhci_phy_unset(struct keystone_xhci_phy *phy) +{ + u32 val; + + /* Disable the PHY REFCLK clock gate */ + val = readl(&phy->phy_clock); + val &= ~USB3_PHY_REF_SSP_EN; + writel(val, &phy->phy_clock); +} + +static void dwc3_set_mode(struct dwc3 *dwc3_reg, u32 mode) +{ + clrsetbits_le32(&dwc3_reg->g_ctl, + DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG), + DWC3_GCTL_PRTCAPDIR(mode)); +} + +static void dwc3_core_soft_reset(struct dwc3 *dwc3_reg) +{ + /* Before Resetting PHY, put Core in Reset */ + setbits_le32(&dwc3_reg->g_ctl, DWC3_GCTL_CORESOFTRESET); + + /* Assert USB3 PHY reset */ + setbits_le32(&dwc3_reg->g_usb3pipectl[0], DWC3_GUSB3PIPECTL_PHYSOFTRST); + + /* Assert USB2 PHY reset */ + setbits_le32(&dwc3_reg->g_usb2phycfg[0], DWC3_GUSB2PHYCFG_PHYSOFTRST); + + mdelay(100); + + /* Clear USB3 PHY reset */ + clrbits_le32(&dwc3_reg->g_usb3pipectl[0], DWC3_GUSB3PIPECTL_PHYSOFTRST); + + /* Clear USB2 PHY reset */ + clrbits_le32(&dwc3_reg->g_usb2phycfg[0], DWC3_GUSB2PHYCFG_PHYSOFTRST); + + /* After PHYs are stable we can take Core out of reset state */ + clrbits_le32(&dwc3_reg->g_ctl, DWC3_GCTL_CORESOFTRESET); +} + +static int dwc3_core_init(struct dwc3 *dwc3_reg) +{ + u32 revision, val; + unsigned long t_rst; + unsigned int dwc3_hwparams1; + + revision = readl(&dwc3_reg->g_snpsid); + /* This should read as U3 followed by revision number */ + if ((revision & DWC3_GSNPSID_MASK) != 0x55330000) { + puts("this is not a DesignWare USB3 DRD Core\n"); + return -EINVAL; + } + + /* issue device SoftReset too */ + writel(DWC3_DCTL_CSFTRST, &dwc3_reg->d_ctl); + + t_rst = get_timer(0); + do { + val = readl(&dwc3_reg->d_ctl); + if (!(val & DWC3_DCTL_CSFTRST)) + break; + WATCHDOG_RESET(); + } while (get_timer(t_rst) < 500); + + if (val & DWC3_DCTL_CSFTRST) { + debug("Reset timed out\n"); + return -2; + } + + dwc3_core_soft_reset(dwc3_reg); + + dwc3_hwparams1 = readl(&dwc3_reg->g_hwparams1); + + val = readl(&dwc3_reg->g_ctl); + val &= ~DWC3_GCTL_SCALEDOWN_MASK; + val &= ~DWC3_GCTL_DISSCRAMBLE; + switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc3_hwparams1)) { + case DWC3_GHWPARAMS1_EN_PWROPT_CLK: + val &= ~DWC3_GCTL_DSBLCLKGTNG; + break; + default: + printf("No power optimization available\n"); + } + + /* + * WORKAROUND: DWC3 revisions <1.90a have a bug + * where the device can fail to connect at SuperSpeed + * and falls back to high-speed mode which causes + * the device to enter a Connect/Disconnect loop + */ + if ((revision & DWC3_REVISION_MASK) < 0x190a) + val |= DWC3_GCTL_U2RSTECN; + + writel(val, &dwc3_reg->g_ctl); + + return 0; +} + +static int keystone_xhci_core_init(struct dwc3 *dwc3_reg) +{ + int ret; + + ret = dwc3_core_init(dwc3_reg); + if (ret) { + debug("failed to initialize core\n"); + return -EINVAL; + } + + /* We are hard-coding DWC3 core to Host Mode */ + dwc3_set_mode(dwc3_reg, DWC3_GCTL_PRTCAP_HOST); + + return 0; +} + +int xhci_hcd_init(int index, + struct xhci_hccr **ret_hccr, struct xhci_hcor **ret_hcor) +{ + u32 val; + int ret; + struct xhci_hccr *hcd; + struct xhci_hcor *hcor; + struct kdwc3_irq_regs *usbss; + struct keystone_xhci_phy *phy; + + usbss = (struct kdwc3_irq_regs *)CONFIG_USB_SS_BASE; + phy = (struct keystone_xhci_phy *)CONFIG_DEV_USB_PHY_BASE; + + /* Enable the PHY REFCLK clock gate with phy_ref_ssp_en = 1 */ + val = readl(&(phy->phy_clock)); + val |= USB3_PHY_REF_SSP_EN; + writel(val, &phy->phy_clock); + + mdelay(100); + + /* Release USB from reset */ + ret = psc_enable_module(KS2_LPSC_USB); + if (ret) { + puts("Cannot enable USB module"); + return -1; + } + + mdelay(100); + + /* Initialize usb phy */ + keystone_xhci_phy_set(phy); + + /* soft reset usbss */ + writel(1, &usbss->sysconfig); + while (readl(&usbss->sysconfig) & 1) + ; + + val = readl(&usbss->revision); + debug("usbss revision %x\n", val); + + /* Initialize usb core */ + hcd = (struct xhci_hccr *)CONFIG_USB_HOST_XHCI_BASE; + keystone.dwc3_reg = (struct dwc3 *)(CONFIG_USB_HOST_XHCI_BASE + + DWC3_REG_OFFSET); + + keystone_xhci_core_init(keystone.dwc3_reg); + + /* set register addresses */ + hcor = (struct xhci_hcor *)((uint32_t)hcd + + HC_LENGTH(readl(&hcd->cr_capbase))); + + debug("Keystone2-xhci: init hccr %08x and hcor %08x hc_length %d\n", + (u32)hcd, (u32)hcor, + (u32)HC_LENGTH(xhci_readl(&hcd->cr_capbase))); + + keystone.usbss = usbss; + keystone.phy = phy; + keystone.hcd = hcd; + keystone.hcor = hcor; + + *ret_hccr = hcd; + *ret_hcor = hcor; + + return 0; +} + +static int keystone_xhci_phy_suspend(void) +{ + int loop_cnt = 0; + struct xhci_hcor *hcor; + uint32_t *portsc_1 = NULL; + uint32_t *portsc_2 = NULL; + u32 val, usb2_pls, usb3_pls, event_q; + struct dwc3 *dwc3_reg = keystone.dwc3_reg; + + /* set register addresses */ + hcor = keystone.hcor; + + /* Bypass Scrambling and Set Shorter Training sequence for simulation */ + val = DWC3_GCTL_PWRDNSCALE(0x4b0) | DWC3_GCTL_PRTCAPDIR(0x2); + writel(val, &dwc3_reg->g_ctl); + + /* GUSB2PHYCFG */ + val = readl(&dwc3_reg->g_usb2phycfg[0]); + + /* assert bit 6 (SusPhy) */ + val |= DWC3_GUSB2PHYCFG_SUSPHY; + writel(val, &dwc3_reg->g_usb2phycfg[0]); + + /* GUSB3PIPECTL */ + val = readl(&dwc3_reg->g_usb3pipectl[0]); + + /* + * assert bit 29 to allow PHY to go to suspend when idle + * and cause the USB3 SS PHY to enter suspend mode + */ + val |= (BIT(29) | DWC3_GUSB3PIPECTL_SUSPHY); + writel(val, &dwc3_reg->g_usb3pipectl[0]); + + /* + * Steps necessary to allow controller to suspend even when + * VBUS is HIGH: + * - Init DCFG[2:0] (DevSpd) to: 1=FS + * - Init GEVNTADR0 to point to an eventQ + * - Init GEVNTSIZ0 to 0x0100 to specify the size of the eventQ + * - Init DCTL::Run_nStop = 1 + */ + writel(0x00020001, &dwc3_reg->d_cfg); + /* TODO: local2global( (Uint32) eventQ )? */ + writel((u32)&event_q, &dwc3_reg->g_evnt_buf[0].g_evntadrlo); + writel(0, &dwc3_reg->g_evnt_buf[0].g_evntadrhi); + writel(0x4, &dwc3_reg->g_evnt_buf[0].g_evntsiz); + /* Run */ + writel(DWC3_DCTL_RUN_STOP, &dwc3_reg->d_ctl); + + mdelay(100); + + /* Wait for USB2 & USB3 PORTSC::PortLinkState to indicate suspend */ + portsc_1 = (uint32_t *)(&hcor->portregs[0].or_portsc); + portsc_2 = (uint32_t *)(&hcor->portregs[1].or_portsc); + usb2_pls = 0; + usb3_pls = 0; + do { + ++loop_cnt; + usb2_pls = (readl(portsc_1) & PORT_PLS_MASK) >> 5; + usb3_pls = (readl(portsc_2) & PORT_PLS_MASK) >> 5; + } while (((usb2_pls != 0x4) || (usb3_pls != 0x4)) && loop_cnt < 1000); + + if (usb2_pls != 0x4 || usb3_pls != 0x4) { + debug("USB suspend failed - PLS USB2=%02x, USB3=%02x\n", + usb2_pls, usb3_pls); + return -1; + } + + debug("USB2 and USB3 PLS - Disabled, loop_cnt=%d\n", loop_cnt); + return 0; +} + +void xhci_hcd_stop(int index) +{ + /* Disable USB */ + if (keystone_xhci_phy_suspend()) + return; + + if (psc_disable_module(KS2_LPSC_USB)) { + debug("PSC disable module USB failed!\n"); + return; + } + + /* Disable PHY */ + keystone_xhci_phy_unset(keystone.phy); + +/* memset(&keystone, 0, sizeof(struct keystone_xhci)); */ + debug("xhci_hcd_stop OK.\n"); +} diff --git a/include/configs/ks2_evm.h b/include/configs/ks2_evm.h index 60f8ffd562..2164617584 100644 --- a/include/configs/ks2_evm.h +++ b/include/configs/ks2_evm.h @@ -183,6 +183,20 @@ "1024k(bootloader)ro,512k(params)ro," \ "-(ubifs)" +/* USB Configuration */ +#define CONFIG_USB_XHCI +#define CONFIG_USB_XHCI_KEYSTONE +#define CONFIG_SYS_USB_XHCI_MAX_ROOT_PORTS 2 +#define CONFIG_USB_STORAGE +#define CONFIG_DOS_PARTITION +#define CONFIG_EFI_PARTITION +#define CONFIG_FS_FAT +#define CONFIG_SYS_CACHELINE_SIZE 64 +#define CONFIG_USB_SS_BASE KS2_USB_SS_BASE +#define CONFIG_USB_HOST_XHCI_BASE KS2_USB_HOST_XHCI_BASE +#define CONFIG_DEV_USB_PHY_BASE KS2_DEV_USB_PHY_BASE +#define CONFIG_USB_PHY_CFG_BASE KS2_USB_PHY_CFG_BASE + /* U-Boot command configuration */ #include #define CONFIG_CMD_ASKENV @@ -196,6 +210,7 @@ #define CONFIG_CMD_UBIFS #define CONFIG_CMD_SF #define CONFIG_CMD_EEPROM +#define CONFIG_CMD_USB /* U-Boot general configuration */ #define CONFIG_SYS_GENERIC_BOARD diff --git a/include/linux/usb/dwc3.h b/include/linux/usb/dwc3.h index 97d179a6e9..7edc760c7b 100644 --- a/include/linux/usb/dwc3.h +++ b/include/linux/usb/dwc3.h @@ -41,7 +41,8 @@ #define DWC3_REG_OFFSET 0xC100 struct g_event_buffer { - u64 g_evntadr; + u32 g_evntadrlo; + u32 g_evntadrhi; u32 g_evntsiz; u32 g_evntcount; }; @@ -185,4 +186,9 @@ struct dwc3 { /* offset: 0xC100 */ #define DWC3_GTXFIFOSIZ_TXFDEF(n) ((n) & 0xffff) #define DWC3_GTXFIFOSIZ_TXFSTADDR(n) ((n) & 0xffff0000) +/* Device Control Register */ +#define DWC3_DCTL_RUN_STOP (1 << 31) +#define DWC3_DCTL_CSFTRST (1 << 30) +#define DWC3_DCTL_LSFTRST (1 << 29) + #endif /* __DWC3_H_ */ -- 2.30.2