2211 lines
62 KiB
C
Executable file
2211 lines
62 KiB
C
Executable file
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (c) 2021 Samsung Electronics Co., Ltd.
|
|
* http://www.samsung.com
|
|
*
|
|
* Author: Sung-Hyun Na <sunghyun.na@samsung.com>
|
|
*
|
|
* Chip Abstraction Layer for USB PHY
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*/
|
|
|
|
#include <linux/delay.h>
|
|
#include <linux/io.h>
|
|
#include <linux/kernel.h>
|
|
#include "phy-samsung-usb-cal.h"
|
|
#include "phy-exynos-usb3p1.h"
|
|
#include "phy-exynos-usb3p1-reg.h"
|
|
|
|
static int exynos_usb3p1_get_tune_param(struct exynos_usbphy_info *info,
|
|
char *param_name)
|
|
{
|
|
int cnt, ret;
|
|
char *name;
|
|
|
|
if (!info->tune_param)
|
|
return -1;
|
|
|
|
for (cnt = 0, ret = -1;
|
|
info->tune_param[cnt].value != EXYNOS_USB_TUNE_LAST;
|
|
cnt++) {
|
|
name = info->tune_param[cnt].name;
|
|
if (strcmp(name, param_name))
|
|
continue;
|
|
ret = info->tune_param[cnt].value;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void exynos_cal_usbphy_q_ch(void *regs_base, u8 enable)
|
|
{
|
|
u32 phy_resume;
|
|
|
|
if (enable) {
|
|
/* WA for Q-channel: disable all q-act from usb */
|
|
phy_resume = readl(regs_base + EXYNOS_USBCON_LINK_CTRL);
|
|
phy_resume |= LINKCTRL_DIS_QACT_ID0;
|
|
phy_resume |= LINKCTRL_DIS_QACT_VBUS_VALID;
|
|
phy_resume |= LINKCTRL_DIS_QACT_BVALID;
|
|
phy_resume |= LINKCTRL_DIS_QACT_LINKGATE;
|
|
phy_resume &= ~LINKCTRL_FORCE_QACT;
|
|
udelay(500);
|
|
writel(phy_resume, regs_base + EXYNOS_USBCON_LINK_CTRL);
|
|
udelay(500);
|
|
phy_resume = readl(regs_base + EXYNOS_USBCON_LINK_CTRL);
|
|
phy_resume |= LINKCTRL_FORCE_QACT;
|
|
udelay(500);
|
|
writel(phy_resume, regs_base + EXYNOS_USBCON_LINK_CTRL);
|
|
} else {
|
|
phy_resume = readl(regs_base + EXYNOS_USBCON_LINK_CTRL);
|
|
phy_resume &= ~LINKCTRL_FORCE_QACT;
|
|
phy_resume |= LINKCTRL_DIS_QACT_ID0;
|
|
phy_resume |= LINKCTRL_DIS_QACT_VBUS_VALID;
|
|
phy_resume |= LINKCTRL_DIS_QACT_BVALID;
|
|
phy_resume |= LINKCTRL_DIS_QACT_LINKGATE;
|
|
writel(phy_resume, regs_base + EXYNOS_USBCON_LINK_CTRL);
|
|
}
|
|
}
|
|
|
|
static void link_vbus_filter_en(struct exynos_usbphy_info *info,
|
|
u8 enable)
|
|
{
|
|
u32 phy_resume;
|
|
|
|
phy_resume = readl(info->regs_base + EXYNOS_USBCON_LINK_CTRL);
|
|
if (enable)
|
|
phy_resume &= ~LINKCTRL_BUS_FILTER_BYPASS_MASK;
|
|
else
|
|
phy_resume |= LINKCTRL_BUS_FILTER_BYPASS(0xf);
|
|
writel(phy_resume, info->regs_base + EXYNOS_USBCON_LINK_CTRL);
|
|
}
|
|
|
|
static void phy_power_en(struct exynos_usbphy_info *info, u8 en)
|
|
{
|
|
u32 reg;
|
|
bool ss_cap;
|
|
int main_version;
|
|
|
|
main_version = info->version & EXYNOS_USBCON_VER_MAJOR_VER_MASK;
|
|
ss_cap = (info->version & EXYNOS_USBCON_VER_SS_CAP) >> 6;
|
|
|
|
if ((main_version == EXYNOS_USBCON_VER_05_0_0) || (ss_cap)) {
|
|
void *__iomem reg_base;
|
|
|
|
if (info->used_phy_port == 1)
|
|
reg_base = info->regs_base_2nd;
|
|
else
|
|
reg_base = info->regs_base;
|
|
|
|
if (!ss_cap) {
|
|
/* 3.0 PHY Power Up or Down control */
|
|
reg = readl(reg_base + EXYNOS_USBCON_PWR);
|
|
|
|
if (en) {
|
|
/* apply to KC asb vector */
|
|
if (EXYNOS_USBCON_VER_MINOR(info->version) >= 0x1) {
|
|
reg &= ~(PWR_PIPE3_POWERDONW);
|
|
reg &= ~(PWR_FORCE_POWERDOWN_EN);
|
|
} else {
|
|
reg &= ~(PWR_TEST_POWERDOWN_HSP);
|
|
reg &= ~(PWR_TEST_POWERDOWN_SSP);
|
|
writel(reg, reg_base + EXYNOS_USBCON_PWR);
|
|
udelay(1000);
|
|
reg |= (PWR_TEST_POWERDOWN_HSP);
|
|
}
|
|
} else {
|
|
if (EXYNOS_USBCON_VER_MINOR(info->version) >= 0x1) {
|
|
reg |= (PWR_PIPE3_POWERDONW);
|
|
reg |= (PWR_FORCE_POWERDOWN_EN);
|
|
} else {
|
|
reg |= (PWR_TEST_POWERDOWN_SSP);
|
|
}
|
|
}
|
|
writel(reg, reg_base + EXYNOS_USBCON_PWR);
|
|
} else if (ss_cap) { //hs_cap: KITT Only Case
|
|
/* 3.0 PHY Power Up or Down Control (Only KITT) */
|
|
reg = readl(reg_base + EXYNOS_USBCON_G2PHY_CNTL0);
|
|
|
|
if (en) {
|
|
reg |= (G2PHY_CNTL0_UPCS_PWR_STABLE);
|
|
reg &= ~(G2PHY_CNTL0_TEST_POWERDOWN);
|
|
} else {
|
|
reg |= (G2PHY_CNTL0_TEST_POWERDOWN);
|
|
reg &= ~(G2PHY_CNTL0_UPCS_PWR_STABLE);
|
|
}
|
|
writel(reg, reg_base + EXYNOS_USBCON_G2PHY_CNTL0);
|
|
}
|
|
|
|
}
|
|
|
|
if (main_version == EXYNOS_USBCON_VER_03_0_0) {
|
|
/* 2.0 PHY Power Down Control */
|
|
reg = readl(info->regs_base + EXYNOS_USBCON_HSP_TEST);
|
|
if (en)
|
|
reg &= ~HSP_TEST_SIDDQ;
|
|
else
|
|
reg |= HSP_TEST_SIDDQ;
|
|
writel(reg, info->regs_base + EXYNOS_USBCON_HSP_TEST);
|
|
}
|
|
}
|
|
|
|
static void phy_sw_rst_high(struct exynos_usbphy_info *info)
|
|
{
|
|
void __iomem *regs_base = info->regs_base;
|
|
int main_version;
|
|
u32 clkrst;
|
|
|
|
main_version = info->version & EXYNOS_USBCON_VER_MAJOR_VER_MASK;
|
|
if ((main_version == EXYNOS_USBCON_VER_05_0_0) &&
|
|
(info->used_phy_port == 1)) {
|
|
regs_base = info->regs_base_2nd;
|
|
}
|
|
|
|
clkrst = readl(regs_base + EXYNOS_USBCON_CLKRST);
|
|
if (EXYNOS_USBCON_VER_MINOR(info->version) >= 0x1) {
|
|
clkrst |= CLKRST_PHY20_SW_RST;
|
|
clkrst |= CLKRST_PHY20_RST_SEL;
|
|
clkrst |= CLKRST_PHY30_SW_RST;
|
|
clkrst |= CLKRST_PHY30_RST_SEL;
|
|
} else {
|
|
clkrst |= CLKRST_PHY_SW_RST;
|
|
clkrst |= CLKRST_PHY_RST_SEL;
|
|
clkrst |= CLKRST_PORT_RST;
|
|
}
|
|
writel(clkrst, regs_base + EXYNOS_USBCON_CLKRST);
|
|
}
|
|
|
|
static void phy_sw_rst_low(struct exynos_usbphy_info *info)
|
|
{
|
|
void __iomem *regs_base = info->regs_base;
|
|
int main_version;
|
|
u32 clkrst;
|
|
|
|
main_version = info->version & EXYNOS_USBCON_VER_MAJOR_VER_MASK;
|
|
if ((main_version == EXYNOS_USBCON_VER_05_0_0) &&
|
|
(info->used_phy_port == 1))
|
|
regs_base = info->regs_base_2nd;
|
|
|
|
clkrst = readl(regs_base + EXYNOS_USBCON_CLKRST);
|
|
if (EXYNOS_USBCON_VER_MINOR(info->version) >= 0x1) {
|
|
clkrst |= CLKRST_PHY20_RST_SEL;
|
|
clkrst &= ~CLKRST_PHY20_SW_RST;
|
|
clkrst &= ~CLKRST_PHY30_SW_RST;
|
|
clkrst &= ~CLKRST_PORT_RST;
|
|
} else {
|
|
clkrst |= CLKRST_PHY_RST_SEL;
|
|
clkrst &= ~CLKRST_PHY_SW_RST;
|
|
clkrst &= ~CLKRST_PORT_RST;
|
|
}
|
|
writel(clkrst, regs_base + EXYNOS_USBCON_CLKRST);
|
|
}
|
|
|
|
void phy_exynos_usb_v3p1_pma_ready(struct exynos_usbphy_info *info)
|
|
{
|
|
void __iomem *regs_base = info->regs_base;
|
|
u32 reg;
|
|
|
|
reg = readl(regs_base + EXYNOS_USBCON_COMBO_PMA_CTRL);
|
|
reg &= ~PMA_LOW_PWR;
|
|
writel(reg, regs_base + EXYNOS_USBCON_COMBO_PMA_CTRL);
|
|
|
|
udelay(1);
|
|
|
|
reg |= PMA_APB_SW_RST;
|
|
reg |= PMA_INIT_SW_RST;
|
|
reg |= PMA_CMN_SW_RST;
|
|
reg |= PMA_TRSV_SW_RST;
|
|
writel(reg, regs_base + EXYNOS_USBCON_COMBO_PMA_CTRL);
|
|
|
|
udelay(1);
|
|
|
|
reg &= ~PMA_APB_SW_RST;
|
|
reg &= ~PMA_INIT_SW_RST;
|
|
reg &= ~PMA_PLL_REF_REQ_MASK;
|
|
reg &= ~PMA_REF_FREQ_SEL_MASK;
|
|
reg |= PMA_REF_FREQ_SEL_SET(0x1);
|
|
writel(reg, regs_base + EXYNOS_USBCON_COMBO_PMA_CTRL);
|
|
|
|
reg = readl(regs_base + EXYNOS_USBCON_LINK_CTRL);
|
|
reg &= ~LINKCTRL_PIPE3_FORCE_EN;
|
|
writel(reg, regs_base + EXYNOS_USBCON_LINK_CTRL);
|
|
}
|
|
|
|
void phy_exynos_usb_v3p1_g2_pma_ready(struct exynos_usbphy_info *info)
|
|
{
|
|
void __iomem *regs_base = info->regs_base;
|
|
u32 reg;
|
|
|
|
reg = readl(regs_base + EXYNOS_USBCON_COMBO_PMA_CTRL);
|
|
reg &= ~PMA_ROPLL_REF_CLK_SEL_MASK;
|
|
reg &= ~PMA_LCPLL_REF_CLK_SEL_MASK;
|
|
reg &= ~PMA_PLL_REF_REQ_MASK;
|
|
reg |= PMA_REF_FREQ_SEL_SET(1);
|
|
reg |= PMA_LOW_PWR;
|
|
reg |= PMA_TRSV_SW_RST;
|
|
reg |= PMA_CMN_SW_RST;
|
|
reg |= PMA_INIT_SW_RST;
|
|
reg |= PMA_APB_SW_RST;
|
|
writel(reg, regs_base + EXYNOS_USBCON_COMBO_PMA_CTRL);
|
|
|
|
udelay(1);
|
|
reg = readl(regs_base + EXYNOS_USBCON_COMBO_PMA_CTRL);
|
|
reg &= ~PMA_LOW_PWR;
|
|
writel(reg, regs_base + EXYNOS_USBCON_COMBO_PMA_CTRL);
|
|
udelay(1);
|
|
|
|
// release overide
|
|
reg = readl(regs_base + EXYNOS_USBCON_LINK_CTRL);
|
|
reg &= ~LINKCTRL_PIPE3_FORCE_EN;
|
|
writel(reg, regs_base + EXYNOS_USBCON_LINK_CTRL);
|
|
|
|
udelay(1);
|
|
|
|
reg = readl(regs_base + EXYNOS_USBCON_COMBO_PMA_CTRL);
|
|
reg &= ~PMA_APB_SW_RST;
|
|
writel(reg, regs_base + EXYNOS_USBCON_COMBO_PMA_CTRL);
|
|
}
|
|
|
|
void phy_exynos_usb_v3p1_g2_disable(struct exynos_usbphy_info *info)
|
|
{
|
|
void __iomem *regs_base = info->regs_base;
|
|
u32 reg;
|
|
|
|
/* Change pipe pclk to pipe3 */
|
|
reg = readl(regs_base + EXYNOS_USBCON_CLKRST);
|
|
reg &= ~CLKRST_LINK_PCLK_SEL;
|
|
writel(reg, regs_base + EXYNOS_USBCON_CLKRST);
|
|
|
|
reg = readl(regs_base + EXYNOS_USBCON_COMBO_PMA_CTRL);
|
|
reg |= PMA_LOW_PWR;
|
|
reg |= PMA_TRSV_SW_RST;
|
|
reg |= PMA_CMN_SW_RST;
|
|
reg |= PMA_INIT_SW_RST;
|
|
reg |= PMA_APB_SW_RST;
|
|
writel(reg, regs_base + EXYNOS_USBCON_COMBO_PMA_CTRL);
|
|
|
|
udelay(1);
|
|
|
|
reg &= ~PMA_TRSV_SW_RST;
|
|
reg &= ~PMA_CMN_SW_RST;
|
|
reg &= ~PMA_INIT_SW_RST;
|
|
reg &= ~PMA_APB_SW_RST;
|
|
writel(reg, regs_base + EXYNOS_USBCON_COMBO_PMA_CTRL);
|
|
}
|
|
|
|
void phy_exynos_usb_v3p1_pma_sw_rst_release(struct exynos_usbphy_info *info)
|
|
{
|
|
void __iomem *regs_base = info->regs_base;
|
|
u32 reg;
|
|
|
|
/* Reset Release for USB/DP PHY */
|
|
reg = readl(regs_base + EXYNOS_USBCON_COMBO_PMA_CTRL);
|
|
reg &= ~PMA_CMN_SW_RST;
|
|
reg &= ~PMA_TRSV_SW_RST;
|
|
writel(reg, regs_base + EXYNOS_USBCON_COMBO_PMA_CTRL);
|
|
|
|
udelay(1000);
|
|
|
|
/* Change pipe pclk to pipe3 */
|
|
reg = readl(regs_base + EXYNOS_USBCON_CLKRST);
|
|
reg |= CLKRST_LINK_PCLK_SEL;
|
|
writel(reg, regs_base + EXYNOS_USBCON_CLKRST);
|
|
}
|
|
|
|
void phy_exynos_usb_v3p1_g2_pma_sw_rst_release(struct exynos_usbphy_info *info)
|
|
{
|
|
void __iomem *regs_base = info->regs_base;
|
|
u32 reg;
|
|
|
|
/* Reset Release for USB/DP PHY */
|
|
reg = readl(regs_base + EXYNOS_USBCON_COMBO_PMA_CTRL);
|
|
reg &= ~PMA_INIT_SW_RST;
|
|
writel(reg, regs_base + EXYNOS_USBCON_COMBO_PMA_CTRL);
|
|
|
|
udelay(1); // Spec : wait for 200ns
|
|
|
|
/* run pll */
|
|
reg = readl(regs_base + EXYNOS_USBCON_COMBO_PMA_CTRL);
|
|
reg &= ~PMA_TRSV_SW_RST;
|
|
reg &= ~PMA_CMN_SW_RST;
|
|
writel(reg, regs_base + EXYNOS_USBCON_COMBO_PMA_CTRL);
|
|
|
|
udelay(10);
|
|
}
|
|
|
|
void phy_exynos_usb_v3p1_g2_link_pclk_sel(struct exynos_usbphy_info *info)
|
|
{
|
|
void __iomem *regs_base = info->regs_base;
|
|
u32 reg;
|
|
|
|
/* Change pipe pclk to pipe3 */
|
|
/* add by Makalu(evt1) case - 20180724 */
|
|
reg = readl(regs_base + EXYNOS_USBCON_CLKRST);
|
|
reg |= CLKRST_LINK_PCLK_SEL;
|
|
writel(reg, regs_base + EXYNOS_USBCON_CLKRST);
|
|
mdelay(3);
|
|
}
|
|
|
|
void phy_exynos_usb_v3p1_pipe_ready(struct exynos_usbphy_info *info)
|
|
{
|
|
void __iomem *regs_base = info->regs_base;
|
|
u32 reg;
|
|
|
|
reg = readl(regs_base + EXYNOS_USBCON_LINK_CTRL);
|
|
reg &= ~LINKCTRL_PIPE3_FORCE_EN;
|
|
writel(reg, regs_base + EXYNOS_USBCON_LINK_CTRL);
|
|
}
|
|
|
|
void phy_exynos_usb_v3p1_pipe_ovrd(struct exynos_usbphy_info *info)
|
|
{
|
|
void __iomem *regs_base = info->regs_base;
|
|
u32 reg;
|
|
bool ss_cap = 0;
|
|
|
|
//For KITT
|
|
ss_cap = (info->version & EXYNOS_USBCON_VER_SS_CAP) >> 6;
|
|
|
|
/* force pipe3 signal for link */
|
|
reg = readl(regs_base + EXYNOS_USBCON_LINK_CTRL);
|
|
reg |= LINKCTRL_PIPE3_FORCE_EN;
|
|
reg &= ~LINKCTRL_PIPE3_FORCE_PHY_STATUS;
|
|
reg |= LINKCTRL_PIPE3_FORCE_RX_ELEC_IDLE;
|
|
writel(reg, regs_base + EXYNOS_USBCON_LINK_CTRL);
|
|
|
|
/* PMA Disable */
|
|
if (!ss_cap) {
|
|
reg = readl(regs_base + EXYNOS_USBCON_COMBO_PMA_CTRL);
|
|
reg |= PMA_LOW_PWR;
|
|
writel(reg, regs_base + EXYNOS_USBCON_COMBO_PMA_CTRL);
|
|
}
|
|
}
|
|
|
|
void phy_exynos_usb3p1_rewa_ready(struct exynos_usbphy_info *info);
|
|
void phy_exynos_usb_v3p1_late_enable(struct exynos_usbphy_info *info);
|
|
|
|
void phy_exynos_usb_v3p1_link_sw_reset(struct exynos_usbphy_info *info)
|
|
{
|
|
void __iomem *regs_base = info->regs_base;
|
|
int main_version;
|
|
u32 reg;
|
|
|
|
main_version = info->version & EXYNOS_USBCON_VER_MAJOR_VER_MASK;
|
|
|
|
/* use link_sw_rst because it has functioning as Hreset_n
|
|
* for asb host/device role change, originally not recommend link_sw_rst by Foundry T.
|
|
* so that some of global register has cleard - 2018.11.12
|
|
*/
|
|
//#ifndef __BOOT__
|
|
/* Link Reset */
|
|
if (main_version == EXYNOS_USBCON_VER_03_0_0) {
|
|
reg = readl(info->regs_base + EXYNOS_USBCON_CLKRST);
|
|
reg |= CLKRST_LINK_SW_RST;
|
|
writel(reg, regs_base + EXYNOS_USBCON_CLKRST);
|
|
|
|
udelay(10);
|
|
|
|
reg &= ~CLKRST_LINK_SW_RST;
|
|
writel(reg, regs_base + EXYNOS_USBCON_CLKRST);
|
|
}
|
|
}
|
|
|
|
void phy_exynos_usb_v3p1_enable(struct exynos_usbphy_info *info)
|
|
{
|
|
void __iomem *regs_base = info->regs_base;
|
|
u32 reg;
|
|
u32 reg_hsp;
|
|
bool ss_only_cap, ss_cap;
|
|
int main_version;
|
|
|
|
main_version = info->version & EXYNOS_USBCON_VER_MAJOR_VER_MASK;
|
|
ss_only_cap = (info->version & EXYNOS_USBCON_VER_SS_ONLY_CAP) >> 4;
|
|
ss_cap = (info->version & EXYNOS_USBCON_VER_SS_CAP) >> 6;
|
|
|
|
if (main_version == EXYNOS_USBCON_VER_03_0_0) {
|
|
/* Set force q-channel */
|
|
exynos_cal_usbphy_q_ch(regs_base, 1);
|
|
}
|
|
|
|
//For KITT USB30 Gen2 PHY Power Enable (Set to 0x237)
|
|
if (ss_cap) {
|
|
reg = readl(info->regs_base + EXYNOS_USBCON_G2PHY_CNTL1);
|
|
reg |= G2PHY_CNTL1_ANA_PWR_EN;
|
|
reg |= G2PHY_CNTL1_PCS_PWR_STABLE;
|
|
reg |= G2PHY_CNTL1_PMA_PWR_STABLE;
|
|
writel(reg, regs_base + EXYNOS_USBCON_G2PHY_CNTL1);
|
|
}
|
|
|
|
/* Set PHY POR High */
|
|
phy_sw_rst_high(info);
|
|
|
|
if (!ss_only_cap) {
|
|
reg = readl(regs_base + EXYNOS_USBCON_UTMI);
|
|
reg &= ~UTMI_FORCE_SUSPEND;
|
|
reg &= ~UTMI_FORCE_SLEEP;
|
|
reg &= ~UTMI_DP_PULLDOWN;
|
|
reg &= ~UTMI_DM_PULLDOWN;
|
|
writel(reg, regs_base + EXYNOS_USBCON_UTMI);
|
|
|
|
/* set phy clock & control HS phy */
|
|
reg = readl(regs_base + EXYNOS_USBCON_HSP);
|
|
|
|
if (info->common_block_disable) {
|
|
reg |= HSP_EN_UTMISUSPEND;
|
|
reg |= HSP_COMMONONN;
|
|
} else {
|
|
reg &= ~HSP_COMMONONN;
|
|
}
|
|
writel(reg, regs_base + EXYNOS_USBCON_HSP);
|
|
}
|
|
|
|
if (ss_only_cap || ss_cap) {
|
|
void *ss_reg_base;
|
|
|
|
if (info->used_phy_port == 1)
|
|
ss_reg_base = info->regs_base_2nd;
|
|
else
|
|
ss_reg_base = info->regs_base;
|
|
/* Change pipe pclk to pipe3 */
|
|
reg = readl(ss_reg_base + EXYNOS_USBCON_CLKRST);
|
|
reg |= CLKRST_LINK_PCLK_SEL;
|
|
writel(reg, ss_reg_base + EXYNOS_USBCON_CLKRST);
|
|
}
|
|
udelay(100);
|
|
|
|
/* Follow setting sequence for USB Link */
|
|
/* 1. Set VBUS Valid and DP-Pull up control
|
|
* by VBUS pad usage
|
|
*/
|
|
link_vbus_filter_en(info, false);
|
|
reg = readl(regs_base + EXYNOS_USBCON_UTMI);
|
|
reg_hsp = readl(regs_base + EXYNOS_USBCON_HSP);
|
|
reg |= UTMI_FORCE_BVALID;
|
|
reg |= UTMI_FORCE_VBUSVALID;
|
|
reg_hsp |= HSP_VBUSVLDEXTSEL;
|
|
reg_hsp |= HSP_VBUSVLDEXT;
|
|
|
|
writel(reg, regs_base + EXYNOS_USBCON_UTMI);
|
|
writel(reg_hsp, regs_base + EXYNOS_USBCON_HSP);
|
|
|
|
/* Set PHY tune para */
|
|
phy_exynos_usb_v3p1_tune(info);
|
|
|
|
/* Enable PHY Power Mode */
|
|
phy_power_en(info, 1);
|
|
|
|
/* before POR low, 10us delay is needed. */
|
|
udelay(10);
|
|
|
|
/* Set PHY POR Low */
|
|
phy_sw_rst_low(info);
|
|
|
|
/* after POR low and delay 75us, PHYCLOCK is guaranteed. */
|
|
udelay(75);
|
|
|
|
if (ss_only_cap || ss_cap) {
|
|
phy_exynos_usb_v3p1_late_enable(info);
|
|
return;
|
|
}
|
|
|
|
/* Select PHY MUX */
|
|
if (info->dual_phy) {
|
|
u32 physel;
|
|
|
|
physel = readl(regs_base + EXYNOS_USBCON_DUALPHYSEL);
|
|
if (info->used_phy_port == 0) {
|
|
physel &= ~DUALPHYSEL_PHYSEL_CTRL;
|
|
physel &= ~DUALPHYSEL_PHYSEL_SSPHY;
|
|
physel &= ~DUALPHYSEL_PHYSEL_PIPECLK;
|
|
physel &= ~DUALPHYSEL_PHYSEL_PIPERST;
|
|
} else {
|
|
physel |= DUALPHYSEL_PHYSEL_CTRL;
|
|
physel |= DUALPHYSEL_PHYSEL_SSPHY;
|
|
physel |= DUALPHYSEL_PHYSEL_PIPECLK;
|
|
physel |= DUALPHYSEL_PHYSEL_PIPERST;
|
|
}
|
|
writel(physel, regs_base + EXYNOS_USBCON_DUALPHYSEL);
|
|
}
|
|
|
|
/* 2. OVC io usage */
|
|
reg = readl(regs_base + EXYNOS_USBCON_LINK_PORT);
|
|
if (info->use_io_for_ovc) {
|
|
reg &= ~LINKPORT_HUB_PORT_SEL_OCD_U3;
|
|
reg &= ~LINKPORT_HUB_PORT_SEL_OCD_U2;
|
|
} else {
|
|
reg |= LINKPORT_HUB_PORT_SEL_OCD_U3;
|
|
reg |= LINKPORT_HUB_PORT_SEL_OCD_U2;
|
|
}
|
|
writel(reg, regs_base + EXYNOS_USBCON_LINK_PORT);
|
|
|
|
/* Enable ReWA */
|
|
if (info->hs_rewa)
|
|
phy_exynos_usb3p1_rewa_ready(info);
|
|
}
|
|
|
|
enum exynos_usbcon_cr {
|
|
USBCON_CR_ADDR = 0,
|
|
USBCON_CR_DATA = 1,
|
|
USBCON_CR_READ = 18,
|
|
USBCON_CR_WRITE = 19,
|
|
};
|
|
|
|
//For KITT
|
|
enum exynos_usbcon_ssp_cr {
|
|
USBCON_CR_SSP_READ = 18,
|
|
USBCON_CR_SSP_WRITE = 19,
|
|
};
|
|
|
|
enum exynos_usbcon_ssp_cr_clk {
|
|
CR_CLK_HIGH = 0x1,
|
|
CR_CLK_LOW = 0x0,
|
|
};
|
|
|
|
static u16 phy_exynos_usb_v3p1_cr_access(struct exynos_usbphy_info *info,
|
|
enum exynos_usbcon_cr cr_bit, u16 data)
|
|
{
|
|
void __iomem *base;
|
|
|
|
u32 ssp_crctl0 = 0;
|
|
u32 ssp_crctl1 = 0;
|
|
u32 loop;
|
|
u32 loop_cnt;
|
|
|
|
if (info->used_phy_port != -1) {
|
|
if (info->used_phy_port == 0)
|
|
base = info->regs_base;
|
|
else
|
|
base = info->regs_base_2nd;
|
|
} else
|
|
base = info->regs_base;
|
|
|
|
/*Clear CR port register*/
|
|
ssp_crctl0 = readl(base + EXYNOS_USBCON_SSP_CRCTL0);
|
|
ssp_crctl0 &= ~(0xf);
|
|
ssp_crctl0 &= ~(0xffffU << 16);
|
|
writel(ssp_crctl0, base + EXYNOS_USBCON_SSP_CRCTL0);
|
|
|
|
/*Set Data for cr port*/
|
|
ssp_crctl0 &= ~SSP_CCTRL0_CR_DATA_IN_MASK;
|
|
ssp_crctl0 |= SSP_CCTRL0_CR_DATA_IN(data);
|
|
writel(ssp_crctl0, base + EXYNOS_USBCON_SSP_CRCTL0);
|
|
|
|
if (cr_bit == USBCON_CR_ADDR)
|
|
loop = 1;
|
|
else
|
|
loop = 2;
|
|
|
|
for (loop_cnt = 0; loop_cnt < loop; loop_cnt++) {
|
|
u32 trigger_bit = 0;
|
|
u32 handshake_cnt = 2;
|
|
/* Trigger cr port */
|
|
if (cr_bit == USBCON_CR_ADDR)
|
|
trigger_bit = SSP_CRCTRL0_CR_CAP_ADDR;
|
|
else {
|
|
if (loop_cnt == 0)
|
|
trigger_bit = SSP_CRCTRL0_CR_CAP_DATA;
|
|
else {
|
|
if (cr_bit == USBCON_CR_READ)
|
|
trigger_bit = SSP_CRCTRL0_CR_READ;
|
|
else
|
|
trigger_bit = SSP_CRCTRL0_CR_WRITE;
|
|
}
|
|
}
|
|
/* Handshake Procedure */
|
|
do {
|
|
u32 usec = 100;
|
|
if (handshake_cnt == 2)
|
|
ssp_crctl0 |= trigger_bit;
|
|
else
|
|
ssp_crctl0 &= ~trigger_bit;
|
|
writel(ssp_crctl0, base + EXYNOS_USBCON_SSP_CRCTL0);
|
|
|
|
/* Handshake */
|
|
do {
|
|
ssp_crctl1 = readl(base + EXYNOS_USBCON_SSP_CRCTL1);
|
|
if ((handshake_cnt == 2) && (ssp_crctl1 & SSP_CRCTL1_CR_ACK))
|
|
break;
|
|
else if ((handshake_cnt == 1) && !(ssp_crctl1 & SSP_CRCTL1_CR_ACK))
|
|
break;
|
|
else
|
|
udelay(1);
|
|
} while (usec-- > 0);
|
|
|
|
if (!usec)
|
|
pr_err("CRPORT handshake timeout1 (0x%08x)\n", ssp_crctl0);
|
|
|
|
udelay(5);
|
|
handshake_cnt--;
|
|
} while (handshake_cnt != 0);
|
|
udelay(50);
|
|
|
|
}
|
|
return (u16) ((ssp_crctl1 & SSP_CRCTL1_CR_DATA_OUT_MASK) >> 16);
|
|
}
|
|
|
|
//For KITT
|
|
static u32 phy_exynos_usb_v3p1_ssp_cr_clk_up(struct exynos_usbphy_info *info)
|
|
{
|
|
void __iomem *base = info->regs_base;
|
|
u32 g2phy_crparcon0 = 0;
|
|
|
|
udelay(10);
|
|
g2phy_crparcon0 = readl(base + EXYNOS_USBCON_G2PHY_CRPARCON0);
|
|
g2phy_crparcon0 |= G2PHY_CRPARCON0_CR_PARA_CLK;
|
|
writel(g2phy_crparcon0, base + EXYNOS_USBCON_G2PHY_CRPARCON0);
|
|
|
|
return CR_CLK_HIGH;
|
|
}
|
|
|
|
//For KITT
|
|
static u32 phy_exynos_usb_v3p1_ssp_cr_clk_down(struct exynos_usbphy_info *info)
|
|
{
|
|
void __iomem *base = info->regs_base;
|
|
u32 g2phy_crparcon0 = 0;
|
|
|
|
udelay(10);
|
|
g2phy_crparcon0 = readl(base + EXYNOS_USBCON_G2PHY_CRPARCON0);
|
|
g2phy_crparcon0 &= ~(G2PHY_CRPARCON0_CR_PARA_CLK);
|
|
writel(g2phy_crparcon0, base + EXYNOS_USBCON_G2PHY_CRPARCON0);
|
|
|
|
return CR_CLK_LOW;
|
|
}
|
|
|
|
//For KITT
|
|
static void phy_exynos_usb_v3p1_ssp_cr_clk(struct exynos_usbphy_info *info)
|
|
{
|
|
phy_exynos_usb_v3p1_ssp_cr_clk_up(info);
|
|
phy_exynos_usb_v3p1_ssp_cr_clk_down(info);
|
|
}
|
|
|
|
//For KITT------------------------------------------------------------------------
|
|
static u16 phy_exynos_usb_v3p1_ssp_cr_access(struct exynos_usbphy_info *info,
|
|
enum exynos_usbcon_cr cr_op, u16 addr, u16 data)
|
|
{
|
|
|
|
// DesignWare Cores USB 3.1 SS+ PHY for SS 10 LPP Databook
|
|
// 5.12 Control Register Interface
|
|
|
|
void __iomem *base;
|
|
u32 PreClkCnt = 0x1;
|
|
|
|
u32 g2phy_crparcon0 = 0;
|
|
u32 g2phy_crparcon1 = 0;
|
|
u32 g2phy_crparcon2 = 0;
|
|
u32 loop_cnt = 0;
|
|
|
|
base = info->regs_base;
|
|
|
|
/*Clear CR port register*/
|
|
/*Clear G2PHY_CRPARCON0*/
|
|
g2phy_crparcon0 = readl(base + EXYNOS_USBCON_G2PHY_CRPARCON0);
|
|
g2phy_crparcon0 &= ~(G2PHY_CRPARCON0_CR_PARA_ACK|G2PHY_CRPARCON0_CR_PARA_SEL
|
|
|G2PHY_CRPARCON0_CR_PARA_CLK);
|
|
g2phy_crparcon0 &= ~(0xffffU << 16);
|
|
writel(g2phy_crparcon0, base + EXYNOS_USBCON_G2PHY_CRPARCON0);
|
|
|
|
/*Clear G2PHY_CRPARCON1*/
|
|
g2phy_crparcon1 = readl(base + EXYNOS_USBCON_G2PHY_CRPARCON1);
|
|
g2phy_crparcon1 &= ~(G2PHY_CRPARCON1_CR_PARA_RD_EN);
|
|
g2phy_crparcon1 &= ~(0xffffU << 16);
|
|
writel(g2phy_crparcon1, base + EXYNOS_USBCON_G2PHY_CRPARCON1);
|
|
|
|
/*Clear G2PHY_CRPARCON2*/
|
|
g2phy_crparcon2 = readl(base + EXYNOS_USBCON_G2PHY_CRPARCON2);
|
|
g2phy_crparcon2 &= ~(G2PHY_CRPARCON2_CR_PARA_WR_EN);
|
|
g2phy_crparcon2 &= ~(0xffffU << 16);
|
|
writel(g2phy_crparcon2, base + EXYNOS_USBCON_G2PHY_CRPARCON2);
|
|
|
|
/*Set cr_para_sel to 0x1*/
|
|
g2phy_crparcon0 |= G2PHY_CRPARCON0_CR_PARA_SEL;
|
|
writel(g2phy_crparcon0, base + EXYNOS_USBCON_G2PHY_CRPARCON0);
|
|
|
|
/*Pre clocking to PHY*/
|
|
if (PreClkCnt != 0x0) {
|
|
do {
|
|
phy_exynos_usb_v3p1_ssp_cr_clk(info);
|
|
PreClkCnt--;
|
|
if (PreClkCnt == 0)
|
|
break;
|
|
} while (1);
|
|
}
|
|
|
|
do {
|
|
if (loop_cnt == 0)
|
|
phy_exynos_usb_v3p1_ssp_cr_clk(info);
|
|
else if (loop_cnt == 1) {
|
|
g2phy_crparcon0 = readl(base + EXYNOS_USBCON_G2PHY_CRPARCON0);
|
|
g2phy_crparcon0 |= (G2PHY_CRPARCON0_CR_PARA_ADDR(addr)|(G2PHY_CRPARCON0_CR_PARA_CLK));
|
|
|
|
if (cr_op == USBCON_CR_WRITE) {
|
|
g2phy_crparcon2 = readl(base + EXYNOS_USBCON_G2PHY_CRPARCON2);
|
|
g2phy_crparcon2 |= (G2PHY_CRPARCON2_CR_PARA_WR_DATA(data)|G2PHY_CRPARCON2_CR_PARA_WR_EN);
|
|
|
|
phy_exynos_usb_v3p1_ssp_cr_clk_up(info); // Toggle High
|
|
writel(g2phy_crparcon0, base + EXYNOS_USBCON_G2PHY_CRPARCON0); // write addr
|
|
writel(g2phy_crparcon2, base + EXYNOS_USBCON_G2PHY_CRPARCON2); // write Data + EN
|
|
|
|
} else {
|
|
// cr_op==USBCON_CR_READ
|
|
g2phy_crparcon1 = readl(base + EXYNOS_USBCON_G2PHY_CRPARCON1);
|
|
g2phy_crparcon1 |= G2PHY_CRPARCON1_CR_PARA_RD_EN;
|
|
|
|
g2phy_crparcon0 |= phy_exynos_usb_v3p1_ssp_cr_clk_up(info); // Toggle High
|
|
writel(g2phy_crparcon0, base + EXYNOS_USBCON_G2PHY_CRPARCON0); // read Addr
|
|
writel(g2phy_crparcon1, base + EXYNOS_USBCON_G2PHY_CRPARCON1); // read EN: High
|
|
}
|
|
|
|
g2phy_crparcon0 &= ~(0x1 << phy_exynos_usb_v3p1_ssp_cr_clk_down(info)); // Toggle Low
|
|
|
|
} else if (loop_cnt == 2) {
|
|
if (cr_op == USBCON_CR_WRITE) {
|
|
//g2phy_crparcon2 = readl(base + EXYNOS_USBCON_G2PHY_CRPARCON2);
|
|
//g2phy_crparcon2 &= ~(G2PHY_CRPARCON2_CR_PARA_WR_EN);
|
|
|
|
//phy_exynos_usb_v3p1_ssp_cr_clk_up(info); // Toggle High
|
|
//writel(g2phy_crparcon2, base + EXYNOS_USBCON_G2PHY_CRPARCON2);
|
|
} else {
|
|
// cr_op==USBCON_CR_READ
|
|
g2phy_crparcon1 = readl(base + EXYNOS_USBCON_G2PHY_CRPARCON1);
|
|
g2phy_crparcon1 &= ~(G2PHY_CRPARCON1_CR_PARA_RD_EN);
|
|
|
|
g2phy_crparcon0 |= phy_exynos_usb_v3p1_ssp_cr_clk_up(info); // Toggle High
|
|
writel(g2phy_crparcon1, base + EXYNOS_USBCON_G2PHY_CRPARCON1); // read EN: Low
|
|
}
|
|
|
|
g2phy_crparcon0 &= ~(0x1 << phy_exynos_usb_v3p1_ssp_cr_clk_down(info)); // Toggle Low
|
|
|
|
} else {
|
|
// loop_cnt > 2
|
|
g2phy_crparcon0 |= phy_exynos_usb_v3p1_ssp_cr_clk_up(info); // Toggle High and update
|
|
g2phy_crparcon0 = G2PHY_CRPARCON0_CR_PARA_ACK_GET(readl(base + EXYNOS_USBCON_G2PHY_CRPARCON0));
|
|
if (g2phy_crparcon0) {
|
|
g2phy_crparcon1 = readl(base + EXYNOS_USBCON_G2PHY_CRPARCON1);
|
|
break;
|
|
}
|
|
g2phy_crparcon0 &= ~(0x1 << phy_exynos_usb_v3p1_ssp_cr_clk_down(info)); // Toggle Low and update
|
|
}
|
|
|
|
loop_cnt++;
|
|
|
|
if (loop_cnt == 0x1000)
|
|
break;
|
|
} while (1);
|
|
|
|
return (u16) ((g2phy_crparcon1 & (0xffffU << 16)) >> 16);
|
|
}
|
|
|
|
void phy_exynos_usb_v3p1_cal_cr_write(struct exynos_usbphy_info *info, u16 addr, u16 data)
|
|
{
|
|
phy_exynos_usb_v3p1_cr_access(info, USBCON_CR_ADDR, addr);
|
|
phy_exynos_usb_v3p1_cr_access(info, USBCON_CR_WRITE, data);
|
|
}
|
|
|
|
u16 phy_exynos_usb_v3p1_cal_cr_read(struct exynos_usbphy_info *info, u16 addr)
|
|
{
|
|
phy_exynos_usb_v3p1_cr_access(info, USBCON_CR_ADDR, addr);
|
|
return phy_exynos_usb_v3p1_cr_access(info, USBCON_CR_READ, 0);
|
|
}
|
|
|
|
//For KITT ---------------------------------------------------------------------------------------
|
|
void phy_exynos_usb_v3p1_ssp_cal_cr_write(struct exynos_usbphy_info *info, u16 addr, u16 data)
|
|
{
|
|
phy_exynos_usb_v3p1_ssp_cr_access(info, USBCON_CR_WRITE, addr, data);
|
|
}
|
|
//--------------------------------------------------------------------------------------------
|
|
|
|
//For KITT ---------------------------------------------------------------------------------------
|
|
u16 phy_exynos_usb_v3p1_ssp_cal_cr_read(struct exynos_usbphy_info *info, u16 addr)
|
|
{
|
|
return phy_exynos_usb_v3p1_ssp_cr_access(info, USBCON_CR_READ, addr, 0);
|
|
|
|
}
|
|
//--------------------------------------------------------------------------------------------
|
|
|
|
void phy_exynos_usb_v3p1_cal_usb3phy_tune_fix_rxeq(struct exynos_usbphy_info *info)
|
|
{
|
|
u16 reg;
|
|
int rxeq_val;
|
|
|
|
rxeq_val = exynos_usb3p1_get_tune_param(info, "rx_eq_fix_val");
|
|
if (rxeq_val == -1)
|
|
return;
|
|
|
|
reg = phy_exynos_usb_v3p1_cal_cr_read(info, 0x1006);
|
|
reg &= ~(1 << 6);
|
|
phy_exynos_usb_v3p1_cal_cr_write(info, 0x1006, reg);
|
|
|
|
udelay(10);
|
|
|
|
reg |= (1 << 7);
|
|
phy_exynos_usb_v3p1_cal_cr_write(info, 0x1006, reg);
|
|
|
|
udelay(10);
|
|
|
|
reg &= ~(0x7 << 0x8);
|
|
reg |= ((rxeq_val & 0x7) << 8);
|
|
phy_exynos_usb_v3p1_cal_cr_write(info, 0x1006, reg);
|
|
|
|
udelay(10);
|
|
|
|
reg |= (1 << 11);
|
|
phy_exynos_usb_v3p1_cal_cr_write(info, 0x1006, reg);
|
|
|
|
pr_info("Reg RX_OVRD_IN_HI : 0x%x\n",
|
|
phy_exynos_usb_v3p1_cal_cr_read(info, 0x1006));
|
|
pr_info("Reg RX_CDR_CDR_FSM_DEBUG : 0x%x\n",
|
|
phy_exynos_usb_v3p1_cal_cr_read(info, 0x101c));
|
|
}
|
|
|
|
static void set_ss_tx_impedance(struct exynos_usbphy_info *info)
|
|
{
|
|
u16 rtune_debug, tx_ovrd_in_hi;
|
|
u8 tx_imp;
|
|
|
|
/* obtain calibration code for 45Ohm impedance */
|
|
rtune_debug = phy_exynos_usb_v3p1_cal_cr_read(info, 0x3);
|
|
/* set SUP.DIG.RTUNE_DEBUG.TYPE = 2 */
|
|
rtune_debug &= ~(0x3 << 3);
|
|
rtune_debug |= (0x2 << 3);
|
|
phy_exynos_usb_v3p1_cal_cr_write(info, 0x3, rtune_debug);
|
|
|
|
/* read SUP.DIG.RTUNE_STAT (0x0004[9:0]) */
|
|
tx_imp = phy_exynos_usb_v3p1_cal_cr_read(info, 0x4);
|
|
/* current_tx_cal_code[9:0] = SUP.DIG.RTUNE_STAT (0x0004[9:0]) */
|
|
tx_imp += 8;
|
|
/* tx_cal_code[9:0] = current_tx_cal_code[9:0] + 8(decimal)
|
|
* NOTE, max value is 63;
|
|
* i.e. if tx_cal_code[9:0] > 63, tx_cal_code[9:0]==63;
|
|
*/
|
|
if (tx_imp > 63)
|
|
tx_imp = 63;
|
|
|
|
/* set LANEX_DIG_TX_OVRD_IN_HI.TX_RESET_OVRD = 1 */
|
|
tx_ovrd_in_hi = phy_exynos_usb_v3p1_cal_cr_read(info, 0x1001);
|
|
tx_ovrd_in_hi |= (1 << 7);
|
|
phy_exynos_usb_v3p1_cal_cr_write(info, 0x1001, tx_ovrd_in_hi);
|
|
|
|
/* SUP.DIG.RTUNE_DEBUG.MAN_TUNE = 0 */
|
|
rtune_debug &= ~(1 << 1);
|
|
phy_exynos_usb_v3p1_cal_cr_write(info, 0x3, rtune_debug);
|
|
|
|
/* set SUP.DIG.RTUNE_DEBUG.VALUE = tx_cal_code[9:0] */
|
|
rtune_debug &= ~(0x1ff << 5);
|
|
rtune_debug |= (tx_imp << 5);
|
|
phy_exynos_usb_v3p1_cal_cr_write(info, 0x3, rtune_debug);
|
|
|
|
/* set SUP.DIG.RTUNE_DEBUG.SET_VAL = 1 */
|
|
rtune_debug |= (1 << 2);
|
|
phy_exynos_usb_v3p1_cal_cr_write(info, 0x3, rtune_debug);
|
|
|
|
/* set SUP.DIG.RTUNE_DEBUG.SET_VAL = 0 */
|
|
rtune_debug &= ~(1 << 2);
|
|
phy_exynos_usb_v3p1_cal_cr_write(info, 0x3, rtune_debug);
|
|
|
|
/* set LANEX_DIG_TX_OVRD_IN_HI.TX_RESET = 1 */
|
|
tx_ovrd_in_hi |= (1 << 6);
|
|
phy_exynos_usb_v3p1_cal_cr_write(info, 0x1001, tx_ovrd_in_hi);
|
|
|
|
/* set LANEX_DIG_TX_OVRD_IN_HI.TX_RESET = 0 */
|
|
tx_ovrd_in_hi &= ~(1 << 6);
|
|
phy_exynos_usb_v3p1_cal_cr_write(info, 0x1001, tx_ovrd_in_hi);
|
|
}
|
|
|
|
void phy_exynos_usb_v3p1_cal_usb3phy_tune_adaptive_eq(
|
|
struct exynos_usbphy_info *info, u8 eq_fix)
|
|
{
|
|
u16 reg;
|
|
|
|
reg = phy_exynos_usb_v3p1_cal_cr_read(info, 0x1006);
|
|
if (eq_fix) {
|
|
reg |= (1 << 6);
|
|
reg &= ~(1 << 7);
|
|
} else {
|
|
reg &= ~(1 << 6);
|
|
reg |= (1 << 7);
|
|
}
|
|
phy_exynos_usb_v3p1_cal_cr_write(info, 0x1006, reg);
|
|
}
|
|
|
|
void phy_exynos_usb_v3p1_usb3phy_tune_chg_rxeq(
|
|
struct exynos_usbphy_info *info, u8 eq_val)
|
|
{
|
|
u16 reg;
|
|
|
|
reg = phy_exynos_usb_v3p1_cal_cr_read(info, 0x1006);
|
|
reg &= ~(0x7 << 0x8);
|
|
reg |= ((eq_val & 0x7) << 8);
|
|
reg |= (1 << 11);
|
|
phy_exynos_usb_v3p1_cal_cr_write(info, 0x1006, reg);
|
|
}
|
|
|
|
enum exynos_usbphy_tif {
|
|
USBCON_TIF_RD_STS,
|
|
USBCON_TIF_RD_OVRD,
|
|
USBCON_TIF_WR_OVRD,
|
|
};
|
|
|
|
static u8 phy_exynos_usb_v3p1_tif_access(struct exynos_usbphy_info *info,
|
|
enum exynos_usbphy_tif access_type, u8 addr, u8 data)
|
|
{
|
|
void __iomem *base;
|
|
u32 hsp_test;
|
|
|
|
base = info->regs_base;
|
|
hsp_test = readl(base + EXYNOS_USBCON_HSP_TEST);
|
|
/* Set TEST DATA OUT SEL */
|
|
if (access_type == USBCON_TIF_RD_STS)
|
|
hsp_test &= ~HSP_TEST_DATA_OUT_SEL;
|
|
else
|
|
hsp_test |= HSP_TEST_DATA_OUT_SEL;
|
|
hsp_test &= ~HSP_TEST_DATA_IN_MASK;
|
|
hsp_test &= ~HSP_TEST_DATA_ADDR_MASK;
|
|
hsp_test |= HSP_TEST_DATA_ADDR_SET(addr);
|
|
writel(hsp_test, base + EXYNOS_USBCON_HSP_TEST);
|
|
|
|
udelay(10);
|
|
|
|
hsp_test = readl(base + EXYNOS_USBCON_HSP_TEST);
|
|
if (access_type != USBCON_TIF_WR_OVRD)
|
|
return HSP_TEST_DATA_OUT_GET(hsp_test);
|
|
|
|
hsp_test |= HSP_TEST_DATA_IN_SET((data | 0xf0));
|
|
hsp_test |= HSP_TEST_CLK;
|
|
writel(hsp_test, base + EXYNOS_USBCON_HSP_TEST);
|
|
|
|
udelay(10);
|
|
|
|
hsp_test = readl(base + EXYNOS_USBCON_HSP_TEST);
|
|
hsp_test &= ~HSP_TEST_CLK;
|
|
writel(hsp_test, base + EXYNOS_USBCON_HSP_TEST);
|
|
|
|
hsp_test = readl(base + EXYNOS_USBCON_HSP_TEST);
|
|
return HSP_TEST_DATA_OUT_GET(hsp_test);
|
|
}
|
|
|
|
u8 phy_exynos_usb_v3p1_tif_ov_rd(struct exynos_usbphy_info *info, u8 addr)
|
|
{
|
|
return phy_exynos_usb_v3p1_tif_access(info, USBCON_TIF_RD_OVRD, addr, 0x0);
|
|
}
|
|
|
|
u8 phy_exynos_usb_v3p1_tif_ov_wr(struct exynos_usbphy_info *info, u8 addr, u8 data)
|
|
{
|
|
return phy_exynos_usb_v3p1_tif_access(info, USBCON_TIF_WR_OVRD, addr, data);
|
|
}
|
|
|
|
u8 phy_exynos_usb_v3p1_tif_sts_rd(struct exynos_usbphy_info *info, u8 addr)
|
|
{
|
|
return phy_exynos_usb_v3p1_tif_access(info, USBCON_TIF_RD_STS, addr, 0x0);
|
|
}
|
|
|
|
void phy_exynos_usb_v3p1_late_enable(struct exynos_usbphy_info *info)
|
|
{
|
|
u32 version = info->version;
|
|
bool ss_cap;
|
|
|
|
ss_cap = (info->version & EXYNOS_USBCON_VER_SS_CAP) >> 6;
|
|
|
|
if ((version > EXYNOS_USBCON_VER_05_0_0) && (!ss_cap)) {
|
|
int tune_val;
|
|
u16 cr_reg;
|
|
|
|
/*Set RXDET_MEAS_TIME[11:4] each reference clock*/
|
|
phy_exynos_usb_v3p1_cal_cr_write(info, 0x1010, 0x80);
|
|
|
|
tune_val = exynos_usb3p1_get_tune_param(info, "rx_decode_mode");
|
|
if (tune_val != -1) {
|
|
phy_exynos_usb_v3p1_cal_cr_write(info, 0x1026, 0x1);
|
|
}
|
|
|
|
tune_val = exynos_usb3p1_get_tune_param(info, "cr_lvl_ctrl_en");
|
|
if (tune_val != -1) {
|
|
/* Enable override los_bias, los_level and
|
|
* tx_vboost_lvl, Set los_bias to 0x5 and
|
|
* los_level to 0x9
|
|
*/
|
|
phy_exynos_usb_v3p1_cal_cr_write(info, 0x15, 0xA409);
|
|
/* Set TX_VBOOST_LEVLE to tune->tx_boost_level */
|
|
tune_val = exynos_usb3p1_get_tune_param(info, "tx_vboost_lvl");
|
|
if (tune_val != -1) {
|
|
cr_reg = phy_exynos_usb_v3p1_cal_cr_read(info, 0x12);
|
|
cr_reg &= ~(0x7 << 13);
|
|
cr_reg |= ((tune_val & 0x7) << 13);
|
|
phy_exynos_usb_v3p1_cal_cr_write(info, 0x12, cr_reg);
|
|
}
|
|
/* Set swing_full to LANEN_DIG_TX_OVRD_DRV_LO */
|
|
cr_reg = 0x4000;
|
|
tune_val = exynos_usb3p1_get_tune_param(info, "pcs_tx_deemph_3p5db");
|
|
if (tune_val != -1) {
|
|
cr_reg |= (tune_val << 7);
|
|
tune_val = exynos_usb3p1_get_tune_param(info, "pcs_tx_swing_full");
|
|
if (tune_val != -1)
|
|
cr_reg |= tune_val;
|
|
else
|
|
cr_reg = 0;
|
|
} else
|
|
cr_reg = 0;
|
|
if (cr_reg)
|
|
phy_exynos_usb_v3p1_cal_cr_write(info, 0x1002, cr_reg);
|
|
}
|
|
/* to set the charge pump proportional current */
|
|
tune_val = exynos_usb3p1_get_tune_param(info, "mpll_charge_pump");
|
|
if (tune_val != -1)
|
|
phy_exynos_usb_v3p1_cal_cr_write(info, 0x30, 0xC0);
|
|
|
|
tune_val = exynos_usb3p1_get_tune_param(info, "rx_eq_fix_val");
|
|
if (tune_val != -1)
|
|
phy_exynos_usb_v3p1_cal_usb3phy_tune_fix_rxeq(info);
|
|
|
|
tune_val = exynos_usb3p1_get_tune_param(info, "decrese_ss_tx_imp");
|
|
if (tune_val != -1)
|
|
set_ss_tx_impedance(info);
|
|
}
|
|
|
|
if (ss_cap) { // KITT Gen2 PHY CR Control
|
|
|
|
//For KITT
|
|
// Read SUP_DIG_IDCODE_LO
|
|
//pr_info("SNPS Gen2 PHY: SUP_DIG_IDCODE_LO : 0x%x\n",
|
|
//phy_exynos_usb_v3p1_ssp_cal_cr_read(info, 0x0));
|
|
|
|
// TX_VBOOST_LVL: 0x7
|
|
phy_exynos_usb_v3p1_ssp_cal_cr_write(info, 0xf, 0x03d0);
|
|
|
|
// TX_VBOOST_LVL: 0x5
|
|
//phy_exynos_usb_v3p1_ssp_cal_cr_write(info, 0xf, 0x0350);
|
|
|
|
// TX_VBOOST_LVL: 0x3
|
|
//phy_exynos_usb_v3p1_ssp_cal_cr_write(info, 0xf, 0x02d0);
|
|
|
|
pr_info("[SNPS Gen2 PHY] SUP_DIG_LVL_OVRD_IN (0xf) : 0x%x\n",
|
|
phy_exynos_usb_v3p1_ssp_cal_cr_read(info, 0xf));
|
|
|
|
//pr_info("[SNPS Gen2 PHY] SUP_DIG_LVL_ASIC_IN (0x1a) : 0x%x\n",
|
|
//phy_exynos_usb_v3p1_ssp_cal_cr_read(info, 0x1a));
|
|
|
|
//pr_info("[SNPS Gen2 PHY] LANEN_DIG_ASIC_TX_ASIC_IN_1 (0x1012) : 0x%x\n",
|
|
//phy_exynos_usb_v3p1_ssp_cal_cr_read(info, 0x1012));
|
|
}
|
|
}
|
|
|
|
void phy_exynos_usb_v3p1_disable(struct exynos_usbphy_info *info)
|
|
{
|
|
u32 reg;
|
|
void __iomem *regs_base = info->regs_base;
|
|
|
|
/* set phy clock & control HS phy */
|
|
reg = readl(regs_base + EXYNOS_USBCON_UTMI);
|
|
reg &= ~UTMI_DP_PULLDOWN;
|
|
reg &= ~UTMI_DM_PULLDOWN;
|
|
reg |= UTMI_FORCE_SUSPEND;
|
|
reg |= UTMI_FORCE_SLEEP;
|
|
writel(reg, regs_base + EXYNOS_USBCON_UTMI);
|
|
|
|
/* Disable PHY Power Mode */
|
|
phy_power_en(info, 0);
|
|
|
|
/* clear force q-channel */
|
|
exynos_cal_usbphy_q_ch(regs_base, 0);
|
|
|
|
/* link sw reset is need for USB_DP/DM high-z in host mode: 2019.04.10 by daeman.ko*/
|
|
phy_exynos_usb_v3p1_link_sw_reset(info);
|
|
}
|
|
|
|
u64 phy_exynos_usb3p1_get_logic_trace(struct exynos_usbphy_info *info)
|
|
{
|
|
u64 ret;
|
|
void __iomem *regs_base = info->regs_base;
|
|
|
|
ret = readl(regs_base + EXYNOS_USBCON_LINK_DEBUG_L);
|
|
ret |= ((u64) readl(regs_base + EXYNOS_USBCON_LINK_DEBUG_H)) << 32;
|
|
|
|
return ret;
|
|
}
|
|
|
|
u8 phy_exynos_usb3p1_get_phyclkcnt(struct exynos_usbphy_info *info)
|
|
{
|
|
u32 hsp_test;
|
|
u8 phyclkcnt;
|
|
void __iomem *regs_base = info->regs_base;
|
|
|
|
/* phyclkcnt
|
|
* HSP_TEST.[31:26] phyclkcnt is top 6bit of [29:0] phyclkcnt.
|
|
* 1 count is about 280ms.
|
|
*/
|
|
hsp_test = readl(regs_base + EXYNOS_USBCON_HSP_TEST);
|
|
phyclkcnt = HSP_TEST_PHYCLKCNT_GET(hsp_test);
|
|
|
|
return phyclkcnt;
|
|
}
|
|
|
|
u8 phy_exynos_usb3p1_get_linestate(struct exynos_usbphy_info *info)
|
|
{
|
|
u32 hsp_test;
|
|
u8 linestate;
|
|
void __iomem *regs_base = info->regs_base;
|
|
|
|
hsp_test = readl(regs_base + EXYNOS_USBCON_HSP_TEST);
|
|
linestate = HSP_TEST_LINESTATE_GET(hsp_test);
|
|
|
|
return linestate;
|
|
}
|
|
|
|
void phy_exynos_usb3p1_pcs_reset(struct exynos_usbphy_info *info)
|
|
{
|
|
u32 clkrst;
|
|
void __iomem *regs_base = info->regs_base;
|
|
|
|
clkrst = readl(regs_base + EXYNOS_USBCON_CLKRST);
|
|
if (EXYNOS_USBCON_VER_MINOR(info->version) >= 0x1) {
|
|
/* TODO : How to pcs reset
|
|
* clkrst |= CLKRST_PHY30_RST_SEL;
|
|
* clkrst |= CLKRST_PHY30_SW_RST;
|
|
*/
|
|
} else {
|
|
clkrst |= CLKRST_PHY_SW_RST;
|
|
clkrst |= CLKRST_PHY_RST_SEL;
|
|
}
|
|
writel(clkrst, regs_base + EXYNOS_USBCON_CLKRST);
|
|
|
|
udelay(1);
|
|
|
|
clkrst = readl(regs_base + EXYNOS_USBCON_CLKRST);
|
|
if (EXYNOS_USBCON_VER_MINOR(info->version) >= 0x1) {
|
|
clkrst |= CLKRST_PHY30_RST_SEL;
|
|
clkrst &= ~CLKRST_PHY30_SW_RST;
|
|
} else {
|
|
clkrst |= CLKRST_PHY_RST_SEL;
|
|
clkrst &= ~CLKRST_PHY_SW_RST;
|
|
}
|
|
writel(clkrst, regs_base + EXYNOS_USBCON_CLKRST);
|
|
}
|
|
|
|
void phy_exynos_usb_v3p1_enable_dp_pullup(
|
|
struct exynos_usbphy_info *usbphy_info)
|
|
{
|
|
void __iomem *regs_base = usbphy_info->regs_base;
|
|
u32 phyutmi;
|
|
|
|
phyutmi = readl(regs_base + EXYNOS_USBCON_HSP);
|
|
phyutmi |= HSP_VBUSVLDEXT;
|
|
writel(phyutmi, regs_base + EXYNOS_USBCON_HSP);
|
|
}
|
|
|
|
void phy_exynos_usb_v3p1_disable_dp_pullup(
|
|
struct exynos_usbphy_info *usbphy_info)
|
|
{
|
|
void __iomem *regs_base = usbphy_info->regs_base;
|
|
u32 phyutmi;
|
|
|
|
phyutmi = readl(regs_base + EXYNOS_USBCON_HSP);
|
|
phyutmi &= ~HSP_VBUSVLDEXT;
|
|
writel(phyutmi, regs_base + EXYNOS_USBCON_HSP);
|
|
}
|
|
|
|
void phy_exynos_usb_v3p1_config_host_mode(struct exynos_usbphy_info *info)
|
|
{
|
|
u32 reg;
|
|
void __iomem *regs_base = info->regs_base;
|
|
|
|
/* DP/DM Pull Down Control */
|
|
reg = readl(regs_base + EXYNOS_USBCON_UTMI);
|
|
reg |= UTMI_DP_PULLDOWN;
|
|
reg |= UTMI_DM_PULLDOWN;
|
|
reg &= ~UTMI_FORCE_BVALID;
|
|
reg &= ~UTMI_FORCE_VBUSVALID;
|
|
writel(reg, regs_base + EXYNOS_USBCON_UTMI);
|
|
|
|
/* Disable Pull-up Register */
|
|
reg = readl(regs_base + EXYNOS_USBCON_HSP);
|
|
reg &= ~HSP_VBUSVLDEXTSEL;
|
|
reg &= ~HSP_VBUSVLDEXT;
|
|
writel(reg, regs_base + EXYNOS_USBCON_HSP);
|
|
}
|
|
|
|
void phy_exynos_usb_v3p1_tune(struct exynos_usbphy_info *info)
|
|
{
|
|
u32 hsp_tune, ssp_tune0, ssp_tune1, ssp_tune2, cnt;
|
|
|
|
bool ss_only_cap;
|
|
//For KITT
|
|
bool ss_cap;
|
|
|
|
ss_only_cap = (info->version & EXYNOS_USBCON_VER_SS_ONLY_CAP) >> 4;
|
|
//For KITT: Gen2 PHY
|
|
ss_cap = (info->version & EXYNOS_USBCON_VER_SS_CAP) >> 6;
|
|
|
|
if (!info->tune_param)
|
|
return;
|
|
if (!ss_only_cap) {
|
|
/* hsphy tuning */
|
|
void __iomem *regs_base = info->regs_base;
|
|
|
|
hsp_tune = readl(regs_base + EXYNOS_USBCON_HSP_TUNE);
|
|
|
|
cnt = 0;
|
|
for (; info->tune_param[cnt].value != EXYNOS_USB_TUNE_LAST; cnt++) {
|
|
char *para_name;
|
|
int val;
|
|
|
|
val = info->tune_param[cnt].value;
|
|
if (val == -1)
|
|
continue;
|
|
para_name = info->tune_param[cnt].name;
|
|
if (!strcmp(para_name, "compdis")) {
|
|
hsp_tune &= ~HSP_TUNE_COMPDIS_MASK;
|
|
hsp_tune |= HSP_TUNE_COMPDIS_SET(val);
|
|
} else if (!strcmp(para_name, "otg")) {
|
|
hsp_tune &= ~HSP_TUNE_OTG_MASK;
|
|
hsp_tune |= HSP_TUNE_OTG_SET(val);
|
|
} else if (!strcmp(para_name, "rx_sqrx")) {
|
|
hsp_tune &= ~HSP_TUNE_SQRX_MASK;
|
|
hsp_tune |= HSP_TUNE_SQRX_SET(val);
|
|
} else if (!strcmp(para_name, "tx_fsls")) {
|
|
hsp_tune &= ~HSP_TUNE_TXFSLS_MASK;
|
|
hsp_tune |= HSP_TUNE_TXFSLS_SET(val);
|
|
} else if (!strcmp(para_name, "tx_hsxv")) {
|
|
hsp_tune &= ~HSP_TUNE_HSXV_MASK;
|
|
hsp_tune |= HSP_TUNE_HSXV_SET(val);
|
|
} else if (!strcmp(para_name, "tx_pre_emp")) {
|
|
hsp_tune &= ~HSP_TUNE_TXPREEMPA_MASK;
|
|
hsp_tune |= HSP_TUNE_TXPREEMPA_SET(val);
|
|
} else if (!strcmp(para_name, "tx_pre_emp_plus")) {
|
|
if (val)
|
|
hsp_tune |= HSP_TUNE_TXPREEMPA_PLUS;
|
|
else
|
|
hsp_tune &= ~HSP_TUNE_TXPREEMPA_PLUS;
|
|
} else if (!strcmp(para_name, "tx_res")) {
|
|
hsp_tune &= ~HSP_TUNE_TXRES_MASK;
|
|
hsp_tune |= HSP_TUNE_TXRES_SET(val);
|
|
} else if (!strcmp(para_name, "tx_rise")) {
|
|
hsp_tune &= ~HSP_TUNE_TXRISE_MASK;
|
|
hsp_tune |= HSP_TUNE_TXRISE_SET(val);
|
|
} else if (!strcmp(para_name, "tx_vref")) {
|
|
hsp_tune &= ~HSP_TUNE_TXVREF_MASK;
|
|
hsp_tune |= HSP_TUNE_TXVREF_SET(val);
|
|
} else if (!strcmp(para_name, "tx_res_ovrd")) {
|
|
phy_exynos_usb_v3p1_tif_ov_wr(info, 0x6, val);
|
|
} else if (!strcmp(para_name, "tx_dis_inc")) {
|
|
phy_exynos_usb_v3p1_tif_ov_wr(info, 0xb, val);
|
|
}
|
|
}
|
|
writel(hsp_tune, regs_base + EXYNOS_USBCON_HSP_TUNE);
|
|
}
|
|
|
|
if (ss_only_cap) {
|
|
/* ssphy tuning */
|
|
void __iomem *ss_reg_base;
|
|
|
|
if (info->used_phy_port == 1)
|
|
ss_reg_base = info->regs_base_2nd;
|
|
else
|
|
ss_reg_base = info->regs_base;
|
|
|
|
ssp_tune0 = readl(ss_reg_base + EXYNOS_USBCON_SSP_PARACON0);
|
|
ssp_tune1 = readl(ss_reg_base + EXYNOS_USBCON_SSP_PARACON1);
|
|
ssp_tune2 = readl(ss_reg_base + EXYNOS_USBCON_SSP_TEST);
|
|
|
|
cnt = 0;
|
|
for (; info->tune_param[cnt].value != EXYNOS_USB_TUNE_LAST; cnt++) {
|
|
char *para_name;
|
|
int val;
|
|
|
|
val = info->tune_param[cnt].value;
|
|
if (val == -1)
|
|
continue;
|
|
para_name = info->tune_param[cnt].name;
|
|
if (!strcmp(para_name, "pcs_tx_swing_full")) {
|
|
ssp_tune0 &= ~SSP_PARACON0_PCS_TX_SWING_FULL_MASK;
|
|
ssp_tune0 |= SSP_PARACON0_PCS_TX_SWING_FULL(val);
|
|
} else if (!strcmp(para_name, "pcs_tx_deemph_6db")) {
|
|
ssp_tune0 &= ~SSP_PARACON0_PCS_TX_DEEMPH_6DB_MASK;
|
|
ssp_tune0 |= SSP_PARACON0_PCS_TX_DEEMPH_6DB(val);
|
|
} else if (!strcmp(para_name, "pcs_tx_deemph_3p5db")) {
|
|
ssp_tune0 &= ~SSP_PARACON0_PCS_TX_DEEMPH_3P5DB_MASK;
|
|
ssp_tune0 |= SSP_PARACON0_PCS_TX_DEEMPH_3P5DB(val);
|
|
} else if (!strcmp(para_name, "tx_vboost_lvl_sstx")) {
|
|
ssp_tune1 &= ~SSP_PARACON1_TX_VBOOST_LVL_SSTX_MASK;
|
|
ssp_tune1 |= SSP_PARACON1_TX_VBOOST_LVL_SSTX(val);
|
|
} else if (!strcmp(para_name, "tx_vboost_lvl")) {
|
|
ssp_tune1 &= ~SSP_PARACON1_TX_VBOOST_LVL_MASK;
|
|
ssp_tune1 |= SSP_PARACON1_TX_VBOOST_LVL(val);
|
|
} else if (!strcmp(para_name, "los_level")) {
|
|
ssp_tune1 &= ~SSP_PARACON1_LOS_LEVEL_MASK;
|
|
ssp_tune1 |= SSP_PARACON1_LOS_LEVEL(val);
|
|
} else if (!strcmp(para_name, "los_bias")) {
|
|
ssp_tune1 &= ~SSP_PARACON1_LOS_BIAS_MASK;
|
|
ssp_tune1 |= SSP_PARACON1_LOS_BIAS(val);
|
|
} else if (!strcmp(para_name, "pcs_rx_los_mask_val")) {
|
|
ssp_tune1 &= ~SSP_PARACON1_PCS_RX_LOS_MASK_VAL_MASK;
|
|
ssp_tune1 |= SSP_PARACON1_PCS_RX_LOS_MASK_VAL(val);
|
|
/* SSP TEST setting : 0x135e/f_0000 + 0x3c */
|
|
} else if (!strcmp(para_name, "tx_eye_height_cntl_en")) {
|
|
ssp_tune2 &= ~SSP_TEST_TX_EYE_HEIGHT_CNTL_EN_MASK;
|
|
ssp_tune2 |= SSP_TEST_TX_EYE_HEIGHT_CNTL_EN(val);
|
|
} else if (!strcmp(para_name, "pipe_tx_deemph_update_delay")) {
|
|
ssp_tune2 &= ~SSP_TEST_PIPE_TX_DEEMPH_UPDATE_DELAY_MASK;
|
|
ssp_tune2 |= SSP_TEST_PIPE_TX_DEEMPH_UPDATE_DELAY(val);
|
|
} else if (!strcmp(para_name, "pcs_tx_swing_full_sstx")) {
|
|
ssp_tune2 &= ~SSP_TEST_PCS_TX_SWING_FULL_SSTX_MASK;
|
|
ssp_tune2 |= SSP_TEST_PCS_TX_SWING_FULL_SSTX(val);
|
|
}
|
|
|
|
} /* for */
|
|
writel(ssp_tune0, ss_reg_base + EXYNOS_USBCON_SSP_PARACON0);
|
|
writel(ssp_tune1, ss_reg_base + EXYNOS_USBCON_SSP_PARACON1);
|
|
writel(ssp_tune2, ss_reg_base + EXYNOS_USBCON_SSP_TEST);
|
|
}
|
|
//For KITT: Gen2 PHY
|
|
if (ss_cap) {
|
|
|
|
/* ssphy tuning */
|
|
void __iomem *ss_reg_base;
|
|
|
|
if (info->used_phy_port == 1) {
|
|
ss_reg_base = info->regs_base_2nd;
|
|
} else {
|
|
ss_reg_base = info->regs_base;
|
|
}
|
|
|
|
ssp_tune0 = readl(ss_reg_base + EXYNOS_USBCON_G2PHY_PRTCL1EXT15);
|
|
ssp_tune1 = readl(ss_reg_base + EXYNOS_USBCON_G2PHY_CNTL0);
|
|
|
|
cnt = 0;
|
|
for (; info->tune_param[cnt].value != EXYNOS_USB_TUNE_LAST; cnt++) {
|
|
char *para_name;
|
|
int val;
|
|
|
|
val = info->tune_param[cnt].value;
|
|
if (val == -1) {
|
|
continue;
|
|
}
|
|
para_name = info->tune_param[cnt].name;
|
|
|
|
if (!strcmp(para_name, "tx_vboost_lvl")) {
|
|
ssp_tune0 &= ~G2PHY_PRTCL1EXT15_TX_VBOOST_LVL_MASK;
|
|
ssp_tune0 |= G2PHY_PRTCL1EXT15_TX_VBOOST_LVL(val);
|
|
ssp_tune1 |= G2PHY_CNTL0_EXT_CTRL_SEL;
|
|
writel(ssp_tune0, ss_reg_base + EXYNOS_USBCON_G2PHY_PRTCL1EXT15);
|
|
writel(ssp_tune1, ss_reg_base + EXYNOS_USBCON_G2PHY_CNTL0);
|
|
//writel(ssp_tune0, ss_reg_base + EXYNOS_USBCON_G2PHY_PRTCL1EXT15);
|
|
|
|
udelay(1);
|
|
|
|
ssp_tune1 &= (~G2PHY_CNTL0_EXT_CTRL_SEL);
|
|
writel(ssp_tune1, ss_reg_base + EXYNOS_USBCON_G2PHY_CNTL0);
|
|
} else if (!strcmp(para_name, "tx_iboost_lvl")) {
|
|
ssp_tune0 &= ~G2PHY_PRTCL1EXT15_TX_IBOOST_LVL_MASK;
|
|
ssp_tune0 |= G2PHY_PRTCL1EXT15_TX_IBOOST_LVL(val);
|
|
ssp_tune1 |= G2PHY_CNTL0_EXT_CTRL_SEL;
|
|
writel(ssp_tune0, ss_reg_base + EXYNOS_USBCON_G2PHY_PRTCL1EXT15);
|
|
writel(ssp_tune1, ss_reg_base + EXYNOS_USBCON_G2PHY_CNTL0);
|
|
//writel(ssp_tune0, ss_reg_base + EXYNOS_USBCON_G2PHY_PRTCL1EXT15);
|
|
|
|
udelay(1);
|
|
|
|
ssp_tune1 &= (~G2PHY_CNTL0_EXT_CTRL_SEL);
|
|
writel(ssp_tune1, ss_reg_base + EXYNOS_USBCON_G2PHY_CNTL0);
|
|
}
|
|
} /* for */
|
|
}
|
|
|
|
}
|
|
|
|
void phy_exynos_usb_v3p1_tune_each(struct exynos_usbphy_info *info,
|
|
char *para_name, int val)
|
|
{
|
|
u32 hsp_tune, ssp_tune0, ssp_tune1;
|
|
|
|
bool ss_only_cap;
|
|
//For KITT
|
|
bool ss_cap;
|
|
|
|
ss_only_cap = (info->version & EXYNOS_USBCON_VER_SS_CAP) >> 4;
|
|
ss_cap = (info->version & EXYNOS_USBCON_VER_SS_CAP) >> 6;
|
|
|
|
if (!info->tune_param)
|
|
return;
|
|
|
|
if (!ss_only_cap) {
|
|
/* hsphy tuning */
|
|
void __iomem *regs_base = info->regs_base;
|
|
|
|
hsp_tune = readl(regs_base + EXYNOS_USBCON_HSP_TUNE);
|
|
if (!strcmp(para_name, "compdis")) {
|
|
hsp_tune &= ~HSP_TUNE_COMPDIS_MASK;
|
|
hsp_tune |= HSP_TUNE_COMPDIS_SET(val);
|
|
} else if (!strcmp(para_name, "otg")) {
|
|
hsp_tune &= ~HSP_TUNE_OTG_MASK;
|
|
hsp_tune |= HSP_TUNE_OTG_SET(val);
|
|
} else if (!strcmp(para_name, "rx_sqrx")) {
|
|
hsp_tune &= ~HSP_TUNE_SQRX_MASK;
|
|
hsp_tune |= HSP_TUNE_SQRX_SET(val);
|
|
} else if (!strcmp(para_name, "tx_fsls")) {
|
|
hsp_tune &= ~HSP_TUNE_TXFSLS_MASK;
|
|
hsp_tune |= HSP_TUNE_TXFSLS_SET(val);
|
|
} else if (!strcmp(para_name, "tx_hsxv")) {
|
|
hsp_tune &= ~HSP_TUNE_HSXV_MASK;
|
|
hsp_tune |= HSP_TUNE_HSXV_SET(val);
|
|
} else if (!strcmp(para_name, "tx_pre_emp")) {
|
|
hsp_tune &= ~HSP_TUNE_TXPREEMPA_MASK;
|
|
hsp_tune |= HSP_TUNE_TXPREEMPA_SET(val);
|
|
} else if (!strcmp(para_name, "tx_pre_emp_plus")) {
|
|
if (val)
|
|
hsp_tune |= HSP_TUNE_TXPREEMPA_PLUS;
|
|
else
|
|
hsp_tune &= ~HSP_TUNE_TXPREEMPA_PLUS;
|
|
} else if (!strcmp(para_name, "tx_res")) {
|
|
hsp_tune &= ~HSP_TUNE_TXRES_MASK;
|
|
hsp_tune |= HSP_TUNE_TXRES_SET(val);
|
|
} else if (!strcmp(para_name, "tx_rise")) {
|
|
hsp_tune &= ~HSP_TUNE_TXRISE_MASK;
|
|
hsp_tune |= HSP_TUNE_TXRISE_SET(val);
|
|
} else if (!strcmp(para_name, "tx_vref")) {
|
|
hsp_tune &= ~HSP_TUNE_TXVREF_MASK;
|
|
hsp_tune |= HSP_TUNE_TXVREF_SET(val);
|
|
} else if (!strcmp(para_name, "tx_res_ovrd"))
|
|
phy_exynos_usb_v3p1_tif_ov_wr(info, 0x6, val);
|
|
writel(hsp_tune, regs_base + EXYNOS_USBCON_HSP_TUNE);
|
|
}
|
|
|
|
if (ss_only_cap) {
|
|
/* ssphy tuning */
|
|
void __iomem *ss_reg_base;
|
|
|
|
if (info->used_phy_port == 1)
|
|
ss_reg_base = info->regs_base_2nd;
|
|
else
|
|
ss_reg_base = info->regs_base;
|
|
|
|
ssp_tune0 = readl(ss_reg_base + EXYNOS_USBCON_SSP_PARACON0);
|
|
ssp_tune1 = readl(ss_reg_base + EXYNOS_USBCON_SSP_PARACON1);
|
|
|
|
if (!strcmp(para_name, "tx0_term_offset")) {
|
|
ssp_tune0 &= ~SSP_PARACON0_TX0_TERM_OFFSET_MASK;
|
|
ssp_tune0 |= SSP_PARACON0_TX0_TERM_OFFSET(val);
|
|
} else if (!strcmp(para_name, "pcs_tx_swing_full")) {
|
|
ssp_tune0 &= ~SSP_PARACON0_PCS_TX_SWING_FULL_MASK;
|
|
ssp_tune0 |= SSP_PARACON0_PCS_TX_SWING_FULL(val);
|
|
} else if (!strcmp(para_name, "pcs_tx_deemph_6db")) {
|
|
ssp_tune0 &= ~SSP_PARACON0_PCS_TX_DEEMPH_6DB_MASK;
|
|
ssp_tune0 |= SSP_PARACON0_PCS_TX_DEEMPH_6DB(val);
|
|
} else if (!strcmp(para_name, "pcs_tx_deemph_3p5db")) {
|
|
ssp_tune0 &= ~SSP_PARACON0_PCS_TX_DEEMPH_3P5DB_MASK;
|
|
ssp_tune0 |= SSP_PARACON0_PCS_TX_DEEMPH_3P5DB(val);
|
|
} else if (!strcmp(para_name, "tx_vboost_lvl")) {
|
|
ssp_tune1 &= ~SSP_PARACON1_TX_VBOOST_LVL_MASK;
|
|
ssp_tune1 |= SSP_PARACON1_TX_VBOOST_LVL(val);
|
|
} else if (!strcmp(para_name, "los_level")) {
|
|
ssp_tune1 &= ~SSP_PARACON1_LOS_LEVEL_MASK;
|
|
ssp_tune1 |= SSP_PARACON1_LOS_LEVEL(val);
|
|
} else if (!strcmp(para_name, "los_bias")) {
|
|
ssp_tune1 &= ~SSP_PARACON1_LOS_BIAS_MASK;
|
|
ssp_tune1 |= SSP_PARACON1_LOS_BIAS(val);
|
|
} else if (!strcmp(para_name, "pcs_rx_los_mask_val")) {
|
|
ssp_tune1 &= ~SSP_PARACON1_PCS_RX_LOS_MASK_VAL_MASK;
|
|
ssp_tune1 |= SSP_PARACON1_PCS_RX_LOS_MASK_VAL(val);
|
|
}
|
|
writel(ssp_tune0, ss_reg_base + EXYNOS_USBCON_SSP_PARACON0);
|
|
writel(ssp_tune1, ss_reg_base + EXYNOS_USBCON_SSP_PARACON1);
|
|
} /* ss_only_cap */
|
|
|
|
//For KITT: Gen2 PHY
|
|
if (ss_cap) {
|
|
/* ssphy tuning */
|
|
void __iomem *ss_reg_base;
|
|
|
|
if (info->used_phy_port == 1) {
|
|
ss_reg_base = info->regs_base_2nd;
|
|
} else {
|
|
ss_reg_base = info->regs_base;
|
|
}
|
|
|
|
ssp_tune0 = readl(ss_reg_base + EXYNOS_USBCON_G2PHY_PRTCL1EXT15);
|
|
ssp_tune1 = readl(ss_reg_base + EXYNOS_USBCON_G2PHY_CNTL0);
|
|
|
|
if (!strcmp(para_name, "tx_vboost_lvl")) {
|
|
ssp_tune0 &= ~G2PHY_PRTCL1EXT15_TX_VBOOST_LVL_MASK;
|
|
ssp_tune0 |= G2PHY_PRTCL1EXT15_TX_VBOOST_LVL(val);
|
|
ssp_tune1 |= G2PHY_CNTL0_EXT_CTRL_SEL;
|
|
writel(ssp_tune1, ss_reg_base + EXYNOS_USBCON_G2PHY_CNTL0);
|
|
writel(ssp_tune0, ss_reg_base + EXYNOS_USBCON_G2PHY_PRTCL1EXT15);
|
|
|
|
ssp_tune1 &= (~G2PHY_CNTL0_EXT_CTRL_SEL);
|
|
writel(ssp_tune1, ss_reg_base + EXYNOS_USBCON_G2PHY_CNTL0);
|
|
|
|
} else if (!strcmp(para_name, "tx_iboost_lvl")) {
|
|
ssp_tune0 &= ~G2PHY_PRTCL1EXT15_TX_IBOOST_LVL_MASK;
|
|
ssp_tune0 |= G2PHY_PRTCL1EXT15_TX_IBOOST_LVL(val);
|
|
ssp_tune1 |= G2PHY_CNTL0_EXT_CTRL_SEL;
|
|
writel(ssp_tune1, ss_reg_base + EXYNOS_USBCON_G2PHY_CNTL0);
|
|
writel(ssp_tune0, ss_reg_base + EXYNOS_USBCON_G2PHY_PRTCL1EXT15);
|
|
|
|
ssp_tune1 &= (~G2PHY_CNTL0_EXT_CTRL_SEL);
|
|
writel(ssp_tune1, ss_reg_base + EXYNOS_USBCON_G2PHY_CNTL0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void phy_exynos_usb_v3p1_rd_tune_each_from_reg(struct exynos_usbphy_info *info,
|
|
u32 tune, char *para_name, int *val)
|
|
{
|
|
bool ss_only_cap;
|
|
|
|
ss_only_cap = (info->version & EXYNOS_USBCON_VER_SS_CAP) >> 4;
|
|
|
|
if (!info->tune_param)
|
|
return;
|
|
|
|
if (!ss_only_cap) { /* hsphy tuning */
|
|
if (!strcmp(para_name, "compdis"))
|
|
*val = HSP_TUNE_COMPDIS_GET(tune);
|
|
else if (!strcmp(para_name, "otg"))
|
|
*val = HSP_TUNE_OTG_GET(tune);
|
|
else if (!strcmp(para_name, "rx_sqrx"))
|
|
*val = HSP_TUNE_SQRX_GET(tune);
|
|
else if (!strcmp(para_name, "tx_fsls"))
|
|
*val = HSP_TUNE_TXFSLS_GET(tune);
|
|
else if (!strcmp(para_name, "tx_hsxv"))
|
|
*val = HSP_TUNE_HSXV_GET(tune);
|
|
else if (!strcmp(para_name, "tx_pre_emp"))
|
|
*val = HSP_TUNE_TXPREEMPA_GET(tune);
|
|
else if (!strcmp(para_name, "tx_pre_emp_plus"))
|
|
*val = HSP_TUNE_TXPREEMPA_PLUS_GET(tune);
|
|
else if (!strcmp(para_name, "tx_res"))
|
|
*val = HSP_TUNE_TXRES_GET(tune);
|
|
else if (!strcmp(para_name, "tx_rise"))
|
|
*val = HSP_TUNE_TXRISE_GET(tune);
|
|
else if (!strcmp(para_name, "tx_vref"))
|
|
*val = HSP_TUNE_TXVREF_GET(tune);
|
|
else
|
|
*val = -1;
|
|
}
|
|
}
|
|
|
|
void phy_exynos_usb_v3p1_wr_tune_reg(struct exynos_usbphy_info *info, u32 val)
|
|
{
|
|
void __iomem *regs_base = info->regs_base;
|
|
|
|
writel(val, regs_base + EXYNOS_USBCON_HSP_TUNE);
|
|
}
|
|
|
|
void phy_exynos_usb_v3p1_rd_tune_reg(struct exynos_usbphy_info *info, u32 *val)
|
|
{
|
|
void __iomem *regs_base = info->regs_base;
|
|
|
|
if (!val)
|
|
return;
|
|
|
|
*val = readl(regs_base + EXYNOS_USBCON_HSP_TUNE);
|
|
}
|
|
|
|
void phy_exynos_usb3p1_rewa_ready(struct exynos_usbphy_info *info)
|
|
{
|
|
u32 reg;
|
|
void __iomem *regs_base = info->regs_base;
|
|
|
|
/* select rewa clock source :20180705 */
|
|
#if defined(CONFIG_SOC_EXYNOS9820) && !defined(CONFIG_SOC_EXYNOS9820_EVT0)
|
|
/*writel(info->hs_rewa_src, SYSREG_FSYS0_BASE + FSYS0_USB_REWACLK);*/
|
|
#endif
|
|
/* Disable ReWA */
|
|
reg = readl(regs_base + EXYNOS_USBCON_REWA_ENABLE);
|
|
reg &= ~REWA_ENABLE_HS_REWA_EN;
|
|
writel(reg, regs_base + EXYNOS_USBCON_REWA_ENABLE);
|
|
|
|
/* Config ReWA Operation */
|
|
reg = readl(regs_base + EXYNOS_USBCON_HSREWA_CTRL);
|
|
/* Select line state check circuit
|
|
* 0 : FSVPLUS/FSMINUS
|
|
* 1 : LINE STATE
|
|
* */
|
|
reg &= ~HSREWA_CTRL_DPDM_MON_SEL;
|
|
/* Select Drive K circuit
|
|
* 0 : Auto Resume in the PHY
|
|
* 1 : BYPASS mode by ReWA
|
|
* */
|
|
reg |= HSREWA_CTRL_DIG_BYPASS_CON_EN;
|
|
writel(reg, regs_base + EXYNOS_USBCON_HSREWA_CTRL);
|
|
|
|
/* Set Driver K Time */
|
|
reg = 0x1;
|
|
writel(reg, regs_base + EXYNOS_USBCON_HSREWA_HSTK);
|
|
|
|
/* Set Timeout counter Driver K Time */
|
|
reg = 0xff00;
|
|
writel(reg, regs_base + EXYNOS_USBCON_HSREWA_REFTO);
|
|
|
|
/* Disable All events source for abnormal event generation */
|
|
reg = readl(regs_base + EXYNOS_USBCON_HSREWA_INT1_MASK);
|
|
reg |= HSREWA_CTRL_HS_EVT_ERR_SUS |
|
|
HSREWA_CTRL_HS_EVT_ERR_DEV_K |
|
|
HSREWA_CTRL_HS_EVT_DISCON |
|
|
HSREWA_CTRL_HS_EVT_BYPASS_DIS |
|
|
HSREWA_CTRL_HS_EVT_RET_DIS |
|
|
HSREWA_CTRL_HS_EVT_RET_EN;
|
|
writel(reg, regs_base + EXYNOS_USBCON_HSREWA_INT1_MASK);
|
|
}
|
|
|
|
int phy_exynos_usb3p1_rewa_enable(struct exynos_usbphy_info *info)
|
|
{
|
|
int cnt;
|
|
u32 reg;
|
|
void __iomem *regs_base = info->regs_base;
|
|
|
|
/* Clear the system valid flag */
|
|
reg = readl(regs_base + EXYNOS_USBCON_HSREWA_CTRL);
|
|
reg &= ~HSREWA_CTRL_HS_SYS_VALID;
|
|
writel(reg, regs_base + EXYNOS_USBCON_HSREWA_CTRL);
|
|
|
|
/* Enable ReWA */
|
|
reg = readl(regs_base + EXYNOS_USBCON_REWA_ENABLE);
|
|
reg |= REWA_ENABLE_HS_REWA_EN;
|
|
writel(reg, regs_base + EXYNOS_USBCON_REWA_ENABLE);
|
|
|
|
/* Check Status : Wait ReWA Status is retention enabled */
|
|
for (cnt = 10000; cnt != 0; cnt--) {
|
|
|
|
reg = readl(regs_base + EXYNOS_USBCON_HSREWA_INT1_EVT);
|
|
|
|
/* non suspend status*/
|
|
if (reg & HSREWA_CTRL_HS_EVT_ERR_SUS)
|
|
return HS_REWA_EN_STS_NOT_SUSPEND;
|
|
/* Disconnect Status */
|
|
if (reg & HSREWA_CTRL_HS_EVT_DISCON)
|
|
return HS_REWA_EN_STS_DISCONNECT;
|
|
/* Success ReWA Enable */
|
|
if (reg & HSREWA_CTRL_HS_EVT_RET_EN)
|
|
break;
|
|
udelay(30);
|
|
}
|
|
|
|
if (!cnt)
|
|
return HS_REWA_EN_STS_NOT_SUSPEND;
|
|
|
|
/* Set the INT1 for detect K and Disconnect */
|
|
reg = readl(regs_base + EXYNOS_USBCON_HSREWA_INT1_MASK);
|
|
reg &= ~HSREWA_CTRL_HS_EVT_DISCON &
|
|
~HSREWA_CTRL_HS_EVT_ERR_DEV_K;
|
|
reg |= HSREWA_CTRL_HS_EVT_ERR_SUS |
|
|
HSREWA_CTRL_HS_EVT_BYPASS_DIS |
|
|
HSREWA_CTRL_HS_EVT_RET_DIS |
|
|
HSREWA_CTRL_HS_EVT_RET_EN;
|
|
writel(reg, regs_base + EXYNOS_USBCON_HSREWA_INT1_MASK);
|
|
|
|
/* Enable All interrupt source and disnable Wake-up event */
|
|
reg = readl(regs_base + EXYNOS_USBCON_HSREWA_INTR);
|
|
reg &= ~HSREWA_INTR_WAKEUP_REQ_MASK &
|
|
~HSREWA_INTR_EVT_MASK &
|
|
~HSREWA_INTR_WAKEUP_MASK &
|
|
~HSREWA_INTR_TIMEOUT_MASK;
|
|
writel(reg, regs_base + EXYNOS_USBCON_HSREWA_INTR);
|
|
|
|
udelay(100);
|
|
|
|
return HS_REWA_EN_STS_ENALBED;
|
|
}
|
|
|
|
int phy_exynos_usb3p1_rewa_req_sys_valid(struct exynos_usbphy_info *info)
|
|
{
|
|
int cnt;
|
|
u32 reg;
|
|
void __iomem *regs_base = info->regs_base;
|
|
|
|
/* Mask All Interrupt source for the INT1 */
|
|
reg = readl(regs_base + EXYNOS_USBCON_HSREWA_INT1_MASK);
|
|
reg &= ~HSREWA_CTRL_HS_EVT_DISCON &
|
|
~HSREWA_CTRL_HS_EVT_ERR_DEV_K &
|
|
~HSREWA_CTRL_HS_EVT_ERR_SUS &
|
|
~HSREWA_CTRL_HS_EVT_BYPASS_DIS &
|
|
~HSREWA_CTRL_HS_EVT_RET_DIS &
|
|
~HSREWA_CTRL_HS_EVT_RET_EN;
|
|
writel(reg, regs_base + EXYNOS_USBCON_HSREWA_INT1_MASK);
|
|
|
|
/* Enable All interrupt source and disnable Wake-up event */
|
|
reg = readl(regs_base + EXYNOS_USBCON_HSREWA_INTR);
|
|
reg |= HSREWA_INTR_WAKEUP_REQ_MASK;
|
|
reg |= HSREWA_INTR_TIMEOUT_MASK;
|
|
reg |= HSREWA_INTR_EVT_MASK;
|
|
reg |= HSREWA_INTR_WAKEUP_MASK;
|
|
writel(reg, regs_base + EXYNOS_USBCON_HSREWA_INTR);
|
|
|
|
/* Set the system valid flag */
|
|
reg = readl(regs_base + EXYNOS_USBCON_HSREWA_CTRL);
|
|
reg |= HSREWA_CTRL_HS_SYS_VALID;
|
|
writel(reg, regs_base + EXYNOS_USBCON_HSREWA_CTRL);
|
|
|
|
for (cnt = 10000; cnt != 0; cnt--) {
|
|
|
|
reg = readl(regs_base + EXYNOS_USBCON_HSREWA_INT1_EVT);
|
|
|
|
/* Disconnect Status */
|
|
if (reg & HSREWA_CTRL_HS_EVT_DISCON)
|
|
return HS_REWA_EN_STS_DISCONNECT;
|
|
/* Success ReWA Enable */
|
|
#if defined(CONFIG_OTG_CDP_SUPPORT)
|
|
if (reg & HSREWA_CTRL_HS_EVT_RET_DIS)
|
|
#else
|
|
if (reg & HSREWA_CTRL_HS_EVT_RET_EN)
|
|
#endif
|
|
break;
|
|
udelay(30);
|
|
}
|
|
|
|
return HS_REWA_EN_STS_DISABLED;
|
|
}
|
|
|
|
int phy_exynos_usb3p1_rewa_disable(struct exynos_usbphy_info *info)
|
|
{
|
|
int cnt;
|
|
u32 reg;
|
|
void __iomem *regs_base = info->regs_base;
|
|
|
|
/* Check ReWA Already diabled
|
|
* If ReWA was disabled states, disabled sequence is already done
|
|
*/
|
|
reg = readl(regs_base + EXYNOS_USBCON_REWA_ENABLE);
|
|
if (!(reg & REWA_ENABLE_HS_REWA_EN))
|
|
return 0;
|
|
|
|
/* Set Link ready to notify ReWA */
|
|
reg = readl(regs_base + EXYNOS_USBCON_HSREWA_CTRL);
|
|
reg |= HSREWA_CTRL_HS_LINK_READY;
|
|
writel(reg, regs_base + EXYNOS_USBCON_HSREWA_CTRL);
|
|
/* Wait Bypass Disable */
|
|
for (cnt = 10000; cnt != 0; cnt--) {
|
|
reg = readl(regs_base + EXYNOS_USBCON_HSREWA_INT1_EVT);
|
|
/* Success ReWA Enable */
|
|
if (reg & HSREWA_CTRL_HS_EVT_BYPASS_DIS)
|
|
break;
|
|
udelay(30);
|
|
}
|
|
if (!cnt)
|
|
return -1;
|
|
/* Wait ReWA Done */
|
|
for (cnt = 1000; cnt != 0; cnt--) {
|
|
reg = readl(regs_base + EXYNOS_USBCON_HSREWA_CTRL);
|
|
|
|
/* Success ReWA Enable */
|
|
if (reg & HSREWA_CTRL_HS_REWA_DONE)
|
|
break;
|
|
udelay(30);
|
|
}
|
|
if (!cnt)
|
|
return -1;
|
|
/* Disable ReWA */
|
|
reg = readl(regs_base + EXYNOS_USBCON_REWA_ENABLE);
|
|
reg &= ~REWA_ENABLE_HS_REWA_EN;
|
|
writel(reg, regs_base + EXYNOS_USBCON_REWA_ENABLE);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int phy_exynos_usb3p1_rewa_cancel(struct exynos_usbphy_info *info)
|
|
{
|
|
int ret = 0;
|
|
u32 reg;
|
|
void __iomem *regs_base = info->regs_base;
|
|
|
|
/* Check ReWA Already diabled
|
|
* If ReWA was disabled states, disabled sequence is already done
|
|
*/
|
|
reg = readl(regs_base + EXYNOS_USBCON_REWA_ENABLE);
|
|
if (!(reg & REWA_ENABLE_HS_REWA_EN))
|
|
return 0;
|
|
|
|
#if !defined(CONFIG_OTG_CDP_SUPPORT)
|
|
ret = phy_exynos_usb3p1_rewa_req_sys_valid(info);
|
|
#endif
|
|
|
|
/* Disable ReWA */
|
|
reg = readl(regs_base + EXYNOS_USBCON_REWA_ENABLE);
|
|
reg &= ~REWA_ENABLE_HS_REWA_EN;
|
|
writel(reg, regs_base + EXYNOS_USBCON_REWA_ENABLE);
|
|
|
|
udelay(100);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void phy_exynos_usb3p1_u3_rewa_enable(struct exynos_usbphy_info *info, int lfps_overlap_mode)
|
|
{
|
|
void __iomem *regs_base = info->regs_base;
|
|
u32 reg;
|
|
u32 lfpsresp_limit_cnt = 0x39219; // 9ms
|
|
|
|
if (!lfps_overlap_mode) {
|
|
/* Disable overlap_lfps */
|
|
reg = readl(regs_base + EXYNOS_USBCON_U3REWA_CTRL);
|
|
reg &= ~U3REWA_CTRL_OVERLAP_LFPS;
|
|
reg |= U3REWA_CTRL_U3REWA_BLK_EN;
|
|
writel(reg, regs_base + EXYNOS_USBCON_U3REWA_CTRL);
|
|
|
|
} else {
|
|
/* Enable overlap_lfps */
|
|
reg = readl(regs_base + EXYNOS_USBCON_U3REWA_CTRL);
|
|
reg |= U3REWA_CTRL_OVERLAP_LFPS;
|
|
reg |= U3REWA_CTRL_U3REWA_BLK_EN;
|
|
writel(reg, regs_base + EXYNOS_USBCON_U3REWA_CTRL);
|
|
|
|
/* Set lfpsresp_limit_cnt */
|
|
writel(lfpsresp_limit_cnt, regs_base + EXYNOS_USBCON_U3REWA_CTRL);
|
|
}
|
|
}
|
|
|
|
void phy_exynos_usb3p1_u3_rewa_disable(struct exynos_usbphy_info *info)
|
|
{
|
|
void __iomem *regs_base = info->regs_base;
|
|
u32 reg;
|
|
|
|
reg = readl(regs_base + EXYNOS_USBCON_U3REWA_CTRL);
|
|
reg &= ~U3REWA_CTRL_U3REWA_BLK_EN;
|
|
writel(reg, regs_base + EXYNOS_USBCON_U3REWA_CTRL);
|
|
}
|
|
|
|
void phy_exynos_usb3p1_usb3phy_dp_altmode_set_ss_disable(
|
|
struct exynos_usbphy_info *usbphy_info, int dp_phy_port)
|
|
{
|
|
void __iomem *regs_base;
|
|
u32 reg;
|
|
|
|
if (dp_phy_port == 0)
|
|
regs_base = usbphy_info->regs_base;
|
|
else
|
|
regs_base = usbphy_info->regs_base_2nd;
|
|
|
|
/* Reset Mux Select */
|
|
/* Assert phy_reset */
|
|
if (usbphy_info->version == EXYNOS_USBCON_VER_05_3_0) {
|
|
// only for Ramen
|
|
reg = readl(regs_base + EXYNOS_USBCON_CLKRST);
|
|
reg |= CLKRST_PHY_SW_RST;
|
|
reg |= CLKRST_PHY_RST_SEL;
|
|
writel(reg, regs_base + EXYNOS_USBCON_CLKRST);
|
|
}
|
|
|
|
udelay(100);
|
|
}
|
|
|
|
void phy_exynos_usb3p1_usb3phy_dp_altmode_clear_ss_disable(
|
|
struct exynos_usbphy_info *usbphy_info, int dp_phy_port)
|
|
{
|
|
void __iomem *regs_base;
|
|
u32 reg;
|
|
|
|
if (dp_phy_port == 0)
|
|
regs_base = usbphy_info->regs_base;
|
|
else
|
|
regs_base = usbphy_info->regs_base_2nd;
|
|
|
|
|
|
if (usbphy_info->version == EXYNOS_USBCON_VER_05_3_0) {
|
|
// only for Ramen
|
|
reg = readl(regs_base + EXYNOS_USBCON_CLKRST);
|
|
reg |= CLKRST_PHY_RST_SEL;
|
|
reg &= ~CLKRST_PHY_SW_RST;
|
|
writel(reg, regs_base + EXYNOS_USBCON_CLKRST);
|
|
}
|
|
|
|
udelay(100);
|
|
}
|
|
|
|
void phy_exynos_usb3p1_set_fs_vplus_vminus(
|
|
struct exynos_usbphy_info *usbphy_info, u32 fsls_speed_sel, u32 fsv_out_en)
|
|
{
|
|
void __iomem *regs_base = usbphy_info->regs_base;
|
|
u32 hsp_ctrl;
|
|
|
|
if (fsv_out_en) {
|
|
hsp_ctrl = readl(regs_base + EXYNOS_USBCON_HSP);
|
|
if (fsls_speed_sel)
|
|
hsp_ctrl |= HSP_FSLS_SPEED_SEL;
|
|
else
|
|
hsp_ctrl &= ~HSP_FSLS_SPEED_SEL;
|
|
hsp_ctrl |= HSP_FSVP_OUT_EN;
|
|
hsp_ctrl |= HSP_FSVM_OUT_EN;
|
|
writel(hsp_ctrl, regs_base + EXYNOS_USBCON_HSP);
|
|
} else {
|
|
hsp_ctrl = readl(regs_base + EXYNOS_USBCON_HSP);
|
|
hsp_ctrl &= ~HSP_FSLS_SPEED_SEL;
|
|
hsp_ctrl &= ~HSP_FSVP_OUT_EN;
|
|
hsp_ctrl &= ~HSP_FSVM_OUT_EN;
|
|
writel(hsp_ctrl, regs_base + EXYNOS_USBCON_HSP);
|
|
}
|
|
|
|
}
|
|
|
|
u8 phy_exynos_usb3p1_get_fs_vplus_vminus(struct exynos_usbphy_info *info)
|
|
{
|
|
void __iomem *regs_base = info->regs_base;
|
|
u32 hsp_ctrl;
|
|
u32 fsvplus, fsvminus;
|
|
|
|
hsp_ctrl = readl(regs_base + EXYNOS_USBCON_HSP);
|
|
fsvplus = HSP_FSVPLUS_GET(hsp_ctrl);
|
|
fsvminus = HSP_FSVMINUS_GET(hsp_ctrl);
|
|
|
|
return ((fsvminus & 0x1) << 1) | (fsvplus & 0x1);
|
|
}
|
|
|
|
void phy_exynos_usb3p1_set_fsv_out_en(
|
|
struct exynos_usbphy_info *usbphy_info, u32 fsv_out_en)
|
|
{
|
|
void __iomem *regs_base = usbphy_info->regs_base;
|
|
u32 hsp_ctrl;
|
|
|
|
hsp_ctrl = readl(regs_base + EXYNOS_USBCON_HSP);
|
|
if (fsv_out_en) {
|
|
hsp_ctrl |= HSP_FSVP_OUT_EN;
|
|
hsp_ctrl |= HSP_FSVM_OUT_EN;
|
|
} else {
|
|
hsp_ctrl &= ~HSP_FSVP_OUT_EN;
|
|
hsp_ctrl &= ~HSP_FSVM_OUT_EN;
|
|
}
|
|
writel(hsp_ctrl, regs_base + EXYNOS_USBCON_HSP);
|
|
}
|
|
|
|
u8 phy_exynos_usb3p1_bc_data_contact_detect(struct exynos_usbphy_info *usbphy_info)
|
|
{
|
|
bool ret = false;
|
|
u32 utmi_ctrl, hsp_ctrl;
|
|
u32 cnt;
|
|
u32 fsvplus;
|
|
void __iomem *regs_base = usbphy_info->regs_base;
|
|
|
|
// set UTMI_CTRL
|
|
utmi_ctrl = readl(regs_base + EXYNOS_USBCON_UTMI);
|
|
utmi_ctrl |= UTMI_OPMODE_CTRL_EN;
|
|
utmi_ctrl &= ~UTMI_FORCE_OPMODE_MASK;
|
|
utmi_ctrl |= UTMI_FORCE_OPMODE_SET(1);
|
|
utmi_ctrl &= ~UTMI_DP_PULLDOWN;
|
|
utmi_ctrl |= UTMI_DM_PULLDOWN;
|
|
utmi_ctrl |= UTMI_FORCE_SUSPEND;
|
|
writel(utmi_ctrl, regs_base + EXYNOS_USBCON_UTMI);
|
|
|
|
// Data contact Detection Enable
|
|
hsp_ctrl = readl(regs_base + EXYNOS_USBCON_HSP);
|
|
hsp_ctrl &= ~HSP_VDATSRCENB;
|
|
hsp_ctrl &= ~HSP_VDATDETENB;
|
|
hsp_ctrl |= HSP_DCDENB;
|
|
writel(hsp_ctrl, regs_base + EXYNOS_USBCON_HSP);
|
|
|
|
for (cnt = 8; cnt != 0; cnt--) {
|
|
// TDCD_TIMEOUT, 300ms~900ms
|
|
mdelay(40);
|
|
|
|
hsp_ctrl = readl(regs_base + EXYNOS_USBCON_HSP);
|
|
fsvplus = HSP_FSVPLUS_GET(hsp_ctrl);
|
|
|
|
if (!fsvplus)
|
|
break;
|
|
}
|
|
|
|
if (fsvplus == 1 && cnt == 0)
|
|
ret = false;
|
|
else {
|
|
mdelay(10); // TDCD_DBNC, 10ms
|
|
|
|
hsp_ctrl = readl(regs_base + EXYNOS_USBCON_HSP);
|
|
fsvplus = HSP_FSVPLUS_GET(hsp_ctrl);
|
|
|
|
if (!fsvplus)
|
|
ret = true;
|
|
else
|
|
ret = false;
|
|
}
|
|
|
|
hsp_ctrl &= ~HSP_DCDENB;
|
|
writel(hsp_ctrl, regs_base + EXYNOS_USBCON_HSP);
|
|
|
|
// restore UTMI_CTRL
|
|
utmi_ctrl = readl(regs_base + EXYNOS_USBCON_UTMI);
|
|
utmi_ctrl &= ~UTMI_OPMODE_CTRL_EN;
|
|
utmi_ctrl &= ~UTMI_FORCE_OPMODE_MASK;
|
|
utmi_ctrl &= ~UTMI_DM_PULLDOWN;
|
|
utmi_ctrl &= ~UTMI_FORCE_SUSPEND;
|
|
writel(utmi_ctrl, regs_base + EXYNOS_USBCON_UTMI);
|
|
|
|
return ret;
|
|
}
|
|
|
|
enum exynos_usb_bc phy_exynos_usb3p1_bc_battery_charger_detection(struct exynos_usbphy_info *usbphy_info)
|
|
{
|
|
u32 utmi_ctrl, hsp_ctrl;
|
|
u32 chgdet;
|
|
enum exynos_usb_bc chg_port = BC_SDP;
|
|
void __iomem *regs_base = usbphy_info->regs_base;
|
|
|
|
/** Step 1. Primary Detection :: SDP / DCP or CDP
|
|
* voltage sourcing on the D+ line and sensing on the D- line
|
|
**/
|
|
// set UTMI_CTRL
|
|
utmi_ctrl = readl(regs_base + EXYNOS_USBCON_UTMI);
|
|
utmi_ctrl |= UTMI_OPMODE_CTRL_EN;
|
|
utmi_ctrl &= ~UTMI_FORCE_OPMODE_MASK;
|
|
utmi_ctrl |= UTMI_FORCE_OPMODE_SET(1);
|
|
utmi_ctrl &= ~UTMI_DP_PULLDOWN;
|
|
utmi_ctrl &= ~UTMI_DM_PULLDOWN;
|
|
utmi_ctrl |= UTMI_FORCE_SUSPEND;
|
|
writel(utmi_ctrl, regs_base + EXYNOS_USBCON_UTMI);
|
|
|
|
hsp_ctrl = readl(regs_base + EXYNOS_USBCON_HSP);
|
|
hsp_ctrl &= ~HSP_CHRGSEL;
|
|
hsp_ctrl |= HSP_VDATSRCENB;
|
|
hsp_ctrl |= HSP_VDATDETENB;
|
|
writel(hsp_ctrl, regs_base + EXYNOS_USBCON_HSP);
|
|
|
|
mdelay(40); // TVDMSRC_ON, 40ms
|
|
|
|
hsp_ctrl = readl(regs_base + EXYNOS_USBCON_HSP);
|
|
chgdet = HSP_CHGDET_GET(hsp_ctrl);
|
|
if (!chgdet) {
|
|
/** IF CHGDET pin is not set,
|
|
* Standard Downstream Port
|
|
*/
|
|
chg_port = BC_SDP;
|
|
} else {
|
|
hsp_ctrl = readl(regs_base + EXYNOS_USBCON_HSP);
|
|
hsp_ctrl &= ~HSP_VDATSRCENB;
|
|
hsp_ctrl &= ~HSP_VDATDETENB;
|
|
writel(hsp_ctrl, regs_base + EXYNOS_USBCON_HSP);
|
|
|
|
mdelay(20);
|
|
|
|
/** ELSE Maybe DCP or CDP but DCP is primary charger */
|
|
|
|
/** Step 2.1 Secondary Detection :: DCP or CDP
|
|
* voltage sourcing on the D- line and sensing on the D+ line
|
|
*/
|
|
hsp_ctrl |= HSP_CHRGSEL;
|
|
hsp_ctrl |= HSP_VDATSRCENB;
|
|
hsp_ctrl |= HSP_VDATDETENB;
|
|
writel(hsp_ctrl, regs_base + EXYNOS_USBCON_HSP);
|
|
|
|
mdelay(40); // TVDMSRC_ON, 40ms
|
|
|
|
hsp_ctrl = readl(regs_base + EXYNOS_USBCON_HSP);
|
|
chgdet = HSP_CHGDET_GET(hsp_ctrl);
|
|
|
|
if (!chgdet)
|
|
chg_port = BC_CDP;
|
|
else
|
|
chg_port = BC_DCP;
|
|
}
|
|
|
|
hsp_ctrl = readl(regs_base + EXYNOS_USBCON_HSP);
|
|
hsp_ctrl &= ~HSP_VDATSRCENB;
|
|
hsp_ctrl &= ~HSP_VDATDETENB;
|
|
writel(hsp_ctrl, regs_base + EXYNOS_USBCON_HSP);
|
|
|
|
// restore UTMI_CTRL
|
|
utmi_ctrl = readl(regs_base + EXYNOS_USBCON_UTMI);
|
|
utmi_ctrl &= ~UTMI_OPMODE_CTRL_EN;
|
|
utmi_ctrl &= ~UTMI_FORCE_OPMODE_MASK;
|
|
utmi_ctrl &= ~UTMI_FORCE_SUSPEND;
|
|
writel(utmi_ctrl, regs_base + EXYNOS_USBCON_UTMI);
|
|
|
|
return chg_port;
|
|
}
|
|
|
|
#if defined(CONFIG_OTG_CDP_SUPPORT)
|
|
u8 phy_exynos_usb3p1_bc_operate_cdp(struct exynos_usbphy_info *usbphy_info)
|
|
{
|
|
void __iomem *regs_base = usbphy_info->regs_base;
|
|
u32 utmi_ctrl, hsp_ctrl;
|
|
u32 chgdet, fsvplus, fsvminus;
|
|
u8 bc_support = false;
|
|
u8 timeout = false;
|
|
u32 cnt = 0;
|
|
|
|
// set UTMI_CTRL
|
|
utmi_ctrl = readl(regs_base + EXYNOS_USBCON_UTMI);
|
|
utmi_ctrl |= UTMI_OPMODE_CTRL_EN;
|
|
utmi_ctrl &= ~UTMI_FORCE_OPMODE_MASK;
|
|
utmi_ctrl |= UTMI_FORCE_OPMODE_SET(1);
|
|
utmi_ctrl |= UTMI_DP_PULLDOWN;
|
|
utmi_ctrl |= UTMI_DM_PULLDOWN;
|
|
utmi_ctrl |= UTMI_FORCE_SUSPEND;
|
|
writel(utmi_ctrl, regs_base + EXYNOS_USBCON_UTMI);
|
|
|
|
/* Step 1.1. Primary Detection voltage sensing on the D+ line */
|
|
hsp_ctrl = readl(regs_base + EXYNOS_USBCON_HSP);
|
|
hsp_ctrl |= HSP_CHRGSEL;
|
|
hsp_ctrl &= ~HSP_VDATSRCENB;
|
|
hsp_ctrl |= HSP_VDATDETENB;
|
|
hsp_ctrl &= ~HSP_DCDENB;
|
|
writel(hsp_ctrl, regs_base + EXYNOS_USBCON_HSP);
|
|
|
|
while (1) {
|
|
hsp_ctrl = readl(regs_base + EXYNOS_USBCON_HSP);
|
|
chgdet = HSP_CHGDET_GET(hsp_ctrl);
|
|
fsvplus = HSP_FSVPLUS_GET(hsp_ctrl);
|
|
fsvminus = HSP_FSVMINUS_GET(hsp_ctrl);
|
|
|
|
mdelay(1);
|
|
cnt++;
|
|
|
|
if (cnt >= 1000) // TSVLD_CON_PWD, max 1s
|
|
timeout = true;
|
|
|
|
if (chgdet || fsvplus || fsvminus || timeout)
|
|
break;
|
|
}
|
|
pr_info("chgdet = %d, fsvplus = %d, fsvminus = %d\n", chgdet, fsvplus, fsvminus);
|
|
pr_info("timeout = %d\n", timeout);
|
|
pr_info("cnt = %d, Primary DP on\n", cnt);
|
|
|
|
if (chgdet && !fsvplus && !fsvminus && !timeout) {
|
|
/* Step 1.2. Primary Detection, voltage sourcing on the D- line */
|
|
hsp_ctrl = readl(regs_base + EXYNOS_USBCON_HSP);
|
|
hsp_ctrl |= HSP_VDATSRCENB;
|
|
writel(hsp_ctrl, regs_base + EXYNOS_USBCON_HSP);
|
|
|
|
cnt = 0;
|
|
while (1) {
|
|
mdelay(1);
|
|
cnt++;
|
|
|
|
hsp_ctrl = readl(regs_base + EXYNOS_USBCON_HSP);
|
|
chgdet = HSP_CHGDET_GET(hsp_ctrl);
|
|
|
|
if (!chgdet)
|
|
break;
|
|
}
|
|
pr_info("cnt = %d, Primary DP off\n", cnt);
|
|
|
|
/* Step 1.3. Primary Detection, turn off D- line */
|
|
hsp_ctrl = readl(regs_base + EXYNOS_USBCON_HSP);
|
|
hsp_ctrl &= ~HSP_VDATSRCENB;
|
|
writel(hsp_ctrl, regs_base + EXYNOS_USBCON_HSP);
|
|
|
|
if (cnt > 36) // TVDPSRC_ON, min 40ms
|
|
bc_support = 1;
|
|
}
|
|
|
|
// restore HSP_CTRL
|
|
hsp_ctrl = readl(regs_base + EXYNOS_USBCON_HSP);
|
|
hsp_ctrl &= ~HSP_CHRGSEL;
|
|
hsp_ctrl &= ~HSP_VDATSRCENB;
|
|
hsp_ctrl &= ~HSP_VDATDETENB;
|
|
writel(hsp_ctrl, regs_base + EXYNOS_USBCON_HSP);
|
|
|
|
// restore UTMI_CTRL
|
|
utmi_ctrl = readl(regs_base + EXYNOS_USBCON_UTMI);
|
|
utmi_ctrl &= ~UTMI_OPMODE_CTRL_EN;
|
|
utmi_ctrl &= ~UTMI_FORCE_OPMODE_MASK;
|
|
utmi_ctrl &= ~UTMI_FORCE_SUSPEND;
|
|
writel(utmi_ctrl, regs_base + EXYNOS_USBCON_UTMI);
|
|
|
|
pr_info("bc_support = %d\n", bc_support);
|
|
|
|
return bc_support;
|
|
}
|
|
#endif
|