blob: f023752644a84a2cf887918831ec97e3a7a3b065 [file] [log] [blame]
/*
* Copyright (c) 2017 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;
int main_version;
main_version = info->version & EXYNOS_USBCON_VER_MAJOR_VER_MASK;
if (main_version == EXYNOS_USBCON_VER_05_0_0) {
void *__iomem reg_base;
if (info->used_phy_port == 1)
reg_base = info->regs_base_2nd;
else
reg_base = info->regs_base;
/* 3.0 PHY Power 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 (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_PWRN;
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_MASK;
reg |= PMA_REF_FREQ_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;
udelay(100);
reg = readl(regs_base + EXYNOS_USBCON_COMBO_PMA_CTRL);
reg &= ~PMA_ROPLL_REF_REQ_MASK;
reg &= ~PMA_ROPLL_REF_REQ_MASK;
reg &= ~PMA_PLL_REF_REQ_MASK;
reg |= PMA_REF_FREQ_SET(1);
reg |= PMA_LOW_PWRN;
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_PWRN;
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_PWRN;
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(1000);
}
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;
/* 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 */
reg = readl(regs_base + EXYNOS_USBCON_COMBO_PMA_CTRL);
reg |= PMA_LOW_PWRN;
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_rst(struct exynos_usbphy_info *info)
{
void __iomem *regs_base = info->regs_base;
u32 reg;
bool ss_only_cap;
int main_version;
main_version = info->version & EXYNOS_USBCON_VER_MAJOR_VER_MASK;
ss_only_cap = (info->version & EXYNOS_USBCON_VER_SS_CAP) >> 4;
if (main_version == EXYNOS_USBCON_VER_03_0_0) {
/* 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;
int main_version;
main_version = info->version & EXYNOS_USBCON_VER_MAJOR_VER_MASK;
ss_only_cap = (info->version & EXYNOS_USBCON_VER_SS_CAP) >> 4;
if (main_version == EXYNOS_USBCON_VER_03_0_0) {
/* Set force q-channel */
exynos_cal_usbphy_q_ch(regs_base, 1);
}
/* 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);
} else {
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) {
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,
};
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;
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);
}
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);
}
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;
if (version > EXYNOS_USBCON_VER_05_0_0) {
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);
}
}
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_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);
}
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;
}
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;
ss_only_cap = (info->version & EXYNOS_USBCON_VER_SS_CAP) >> 4;
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);
}
writel(hsp_tune, regs_base + EXYNOS_USBCON_HSP_TUNE);
} else {
/* 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);
} /* else */
}
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;
ss_only_cap = (info->version & EXYNOS_USBCON_VER_SS_CAP) >> 4;
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);
} else {
/* 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);
} /* else */
}
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 = 15; 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 = 15; 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 (reg & HSREWA_CTRL_HS_EVT_RET_EN)
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 = 15; 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 = 15; 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;
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;
ret = phy_exynos_usb3p1_rewa_req_sys_valid(info);
/* 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_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 */
reg = readl(regs_base + EXYNOS_USBCON_CLKRST);
#if defined(CONFIG_USB_DP_COMBO_GEN2)
reg |= CLKRST_PHY20_SW_RST;
reg |= CLKRST_PHY20_RST_SEL;
#else
reg |= CLKRST_PHY_SW_RST;
reg |= CLKRST_PHY_RST_SEL;
#endif
writel(reg, regs_base + EXYNOS_USBCON_CLKRST);
udelay(100);
/* Deassert test_powerdown_ssp */
/* Deassert test_powerdown_hsp */
reg = readl(regs_base + EXYNOS_USBCON_PWR);
#if !defined(CONFIG_USB_DP_COMBO_GEN2)
reg &= ~(PWR_TEST_POWERDOWN_HSP);
reg &= ~(PWR_TEST_POWERDOWN_SSP);
#endif
writel(reg, regs_base + EXYNOS_USBCON_PWR);
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;
/* Assert test_powerdown_ssp */
/* Assert test_powerdown_hsp */
reg = readl(regs_base + EXYNOS_USBCON_PWR);
#if !defined(CONFIG_USB_DP_COMBO_GEN2)
reg |= (PWR_TEST_POWERDOWN_HSP);
reg |= (PWR_TEST_POWERDOWN_SSP);
#endif
writel(reg, regs_base + EXYNOS_USBCON_PWR);
udelay(100);
reg = readl(regs_base + EXYNOS_USBCON_CLKRST);
#if defined(CONFIG_USB_DP_COMBO_GEN2)
reg |= CLKRST_PHY20_RST_SEL;
reg &= ~CLKRST_PHY20_SW_RST;
#else
reg |= CLKRST_PHY_RST_SEL;
reg &= ~CLKRST_PHY_SW_RST;
#endif
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_FSV_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_FSV_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;
}