kernel_samsung_a53x/drivers/phy/samsung/phy-exynos-usb3p1.c
2024-06-15 16:02:09 -03:00

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