320 lines
10 KiB
C
Executable file
320 lines
10 KiB
C
Executable file
// SPDX-License-Identifier: GPL-2.0
|
||
/**
|
||
*
|
||
* Copyright (c) 2021 Samsung Electronics Co., Ltd.
|
||
* http://www.samsung.com
|
||
*
|
||
*
|
||
* This program is free software: you can redistribute it and/or modify
|
||
* it under the terms of the GNU General Public License version 2 of
|
||
* the License 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 <string.h>
|
||
//#include <lk/reg.h>
|
||
#include <linux/types.h>
|
||
#include <linux/delay.h>
|
||
#include <linux/io.h>
|
||
|
||
#include "phy-samsung-usb-cal.h"
|
||
|
||
#include "eusb-con-reg.h"
|
||
#include "eusb-phy-reg.h"
|
||
|
||
void phy_exynos_eusb_tune(struct exynos_usbphy_info *info)
|
||
{
|
||
void *base;
|
||
u32 phy_cfg_tx, phy_cfg_rx, cnt;
|
||
|
||
if (!info->tune_param) {
|
||
return;
|
||
}
|
||
|
||
/* eusb phy tuning */
|
||
base = info->regs_base;
|
||
|
||
phy_cfg_tx = readl(base + EUSBCON_REG_TXTUNE);
|
||
phy_cfg_rx = readl(base + EUSBCON_REG_RXTUNE);
|
||
|
||
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_fsls_slew_rate")) {
|
||
((EUSBPHY_REG_TXTUNE_p) (&phy_cfg_tx))->b.fsls_slew_rate = val;
|
||
} else if (!strcmp(para_name, "tx_fsls_vref_tune")) {
|
||
((EUSBPHY_REG_TXTUNE_p) (&phy_cfg_tx))->b.fsls_vref_tune = val;
|
||
} else if (!strcmp(para_name, "tx_fsls_vreg_bypass")) {
|
||
((EUSBPHY_REG_TXTUNE_p) (&phy_cfg_tx))->b.fsls_vreg_bypass = val;
|
||
} else if (!strcmp(para_name, "tx_hs_vref_tune")) {
|
||
((EUSBPHY_REG_TXTUNE_p) (&phy_cfg_tx))->b.hs_vref_tune = val;
|
||
} else if (!strcmp(para_name, "tx_hs_xv")) {
|
||
((EUSBPHY_REG_TXTUNE_p) (&phy_cfg_tx))->b.hs_xv = val;
|
||
} else if (!strcmp(para_name, "tx_preemp")) {
|
||
((EUSBPHY_REG_TXTUNE_p) (&phy_cfg_tx))->b.preemp = val;
|
||
} else if (!strcmp(para_name, "tx_res")) {
|
||
((EUSBPHY_REG_TXTUNE_p) (&phy_cfg_tx))->b.res = val;
|
||
} else if (!strcmp(para_name, "tx_rise")) {
|
||
((EUSBPHY_REG_TXTUNE_p) (&phy_cfg_tx))->b.rise = val;
|
||
} else if (!strcmp(para_name, "rx_eq_ctle")) {
|
||
((EUSBPHY_REG_RXTUNE_p) (&phy_cfg_rx))->b.eq_ctle = val;
|
||
} else if (!strcmp(para_name, "rx_hs_term_en")) {
|
||
((EUSBPHY_REG_RXTUNE_p) (&phy_cfg_rx))->b.hs_term_en = val;
|
||
} else if (!strcmp(para_name, "rx_hs_tune")) {
|
||
((EUSBPHY_REG_RXTUNE_p) (&phy_cfg_rx))->b.hs_tune = val;
|
||
} else if (!strcmp(para_name, "reg_direct") && (val != -1)) {
|
||
phy_cfg_tx = val & 0xffff;
|
||
phy_cfg_rx = (val >> 16) & 0xffff;
|
||
break;
|
||
}
|
||
}
|
||
writel(phy_cfg_tx, base + EUSBCON_REG_TXTUNE);
|
||
writel(phy_cfg_rx, base + EUSBCON_REG_RXTUNE);
|
||
}
|
||
|
||
void phy_exynos_eusb_reset(struct exynos_usbphy_info *info)
|
||
{
|
||
u32 reg;
|
||
void *base;
|
||
|
||
base = info->regs_base;
|
||
|
||
|
||
reg = readl(base + EUSBCON_REG_RST_CTRL);
|
||
((EUSBPHY_REG_RST_CTRL_p) (®))->b.phy_reset = 1;
|
||
((EUSBPHY_REG_RST_CTRL_p) (®))->b.phy_reset_ovrd_en = 1;
|
||
writel(reg, base + EUSBCON_REG_RST_CTRL);
|
||
|
||
reg = readl(base + EUSBCON_REG_CMN_CTRL);
|
||
((EUSBPHY_REG_CMN_CTRL_p) (®))->b.phy_enable = 0;
|
||
writel(reg, base + EUSBCON_REG_CMN_CTRL);
|
||
|
||
|
||
}
|
||
|
||
void phy_exynos_eusb_initiate(struct exynos_usbphy_info *info)
|
||
{
|
||
u32 reg, value;
|
||
void *base;
|
||
|
||
base = info->regs_base;
|
||
|
||
|
||
|
||
/* Refer to the eUSB PHY databook 4.1.2.1 */
|
||
/* 1. Set the phy_reset signal to 1'b1 to hold the eUSB 2.0 PHY
|
||
* in reset */
|
||
reg = readl(base + EUSBCON_REG_RST_CTRL);
|
||
((EUSBPHY_REG_RST_CTRL_p) (®))->b.phy_reset = 1;
|
||
((EUSBPHY_REG_RST_CTRL_p) (®))->b.phy_reset_ovrd_en = 1;
|
||
((EUSBPHY_REG_RST_CTRL_p) (®))->b.utmi_port_reset = 1;
|
||
((EUSBPHY_REG_RST_CTRL_p) (®))->b.utmi_port_reset_ovrd_en = 1;
|
||
writel(reg, base + EUSBCON_REG_RST_CTRL);
|
||
|
||
/* 2. Set all strapping options as described in Chapter 3,
|
||
* “Signal Descriptions”. Strapping signals include:
|
||
* ❑ ref_freq_sel[2:0]
|
||
* ❑ phy_cfg_rcal_bypass
|
||
* ❑ phy_cfg_rcal_code[3:0]
|
||
* ❑ phy_cfg_rptr_mode
|
||
* ❑ phy_cfg_rx_hs_term_en
|
||
* ❑ “Parameter Override Signals”
|
||
*/
|
||
|
||
/* phy_cfg_rptr_mode */
|
||
reg = readl(base + EUSBCON_REG_CMN_CTRL);
|
||
((EUSBPHY_REG_CMN_CTRL_p) (®))->b.phy_cfg_rptr_mode = 1;
|
||
|
||
if ((info->refclk == USBPHY_REFCLK_DIFF_19_2MHZ) ||
|
||
(info->refclk == USBPHY_REFCLK_EXT_19P2MHZ)) {
|
||
/* Reference Clock Frequency : 19.2MHz
|
||
* ref_freq_sel[2:0] : 0
|
||
* phy_cfg_pll_fb_div[11:0] : 368
|
||
* phy_cfg_pll_ref_div[3:0] : 0
|
||
*/
|
||
/* ref_freq_sel */
|
||
((EUSBPHY_REG_CMN_CTRL_p) (®))->b.ref_freq_sel = 0;
|
||
writel(reg, base + EUSBCON_REG_CMN_CTRL);
|
||
/* phy_cfg_pll_fb_div[11:0] */
|
||
reg = readl(base + EUSBCON_REG_PLLCFG0);
|
||
((EUSBPHY_REG_PLLCFG0_p) (®))->b.pll_fb_div = 368;
|
||
writel(reg, base + EUSBCON_REG_PLLCFG0);
|
||
/* phy_cfg_pll_ref_div[3:0] */
|
||
reg = readl(base + EUSBCON_REG_PLLCFG1);
|
||
((EUSBPHY_REG_PLLCFG1_p) (®))->b.pll_ref_div = 0;
|
||
writel(reg, base + EUSBCON_REG_PLLCFG1);
|
||
pr_info("%s: 19.2\n", __func__);
|
||
} else if ((info->refclk == USBPHY_REFCLK_DIFF_20MHZ) ||
|
||
(info->refclk == USBPHY_REFCLK_EXT_20MHZ)) {
|
||
/* Reference Clock Frequency : 20MHz
|
||
* ref_freq_sel[2:0] : 1
|
||
* phy_cfg_pll_fb_div[11:0] : 352
|
||
* phy_cfg_pll_ref_div[3:0] : 0
|
||
*/
|
||
/* ref_freq_sel */
|
||
((EUSBPHY_REG_CMN_CTRL_p) (®))->b.ref_freq_sel = 1;
|
||
writel(reg, base + EUSBCON_REG_CMN_CTRL);
|
||
/* phy_cfg_pll_fb_div[11:0] */
|
||
reg = readl(base + EUSBCON_REG_PLLCFG0);
|
||
((EUSBPHY_REG_PLLCFG0_p) (®))->b.pll_fb_div = 352;
|
||
writel(reg, base + EUSBCON_REG_PLLCFG0);
|
||
/* phy_cfg_pll_ref_div[3:0] */
|
||
reg = readl(base + EUSBCON_REG_PLLCFG1);
|
||
((EUSBPHY_REG_PLLCFG1_p) (®))->b.pll_ref_div = 0;
|
||
writel(reg, base + EUSBCON_REG_PLLCFG1);
|
||
pr_info("%s: 20\n", __func__);
|
||
} else if ((info->refclk == USBPHY_REFCLK_DIFF_24MHZ) ||
|
||
(info->refclk == USBPHY_REFCLK_EXT_24MHZ)) {
|
||
/* Reference Clock Frequency : 24MHz
|
||
* ref_freq_sel[2:0] : 2
|
||
* phy_cfg_pll_fb_div[11:0] : 288
|
||
* phy_cfg_pll_ref_div[3:0] : 0
|
||
*/
|
||
/* ref_freq_sel */
|
||
((EUSBPHY_REG_CMN_CTRL_p) (®))->b.ref_freq_sel = 2;
|
||
writel(reg, base + EUSBCON_REG_CMN_CTRL);
|
||
/* phy_cfg_pll_fb_div[11:0] */
|
||
reg = readl(base + EUSBCON_REG_PLLCFG0);
|
||
((EUSBPHY_REG_PLLCFG0_p) (®))->b.pll_fb_div = 288;
|
||
writel(reg, base + EUSBCON_REG_PLLCFG0);
|
||
/* phy_cfg_pll_ref_div[3:0] */
|
||
reg = readl(base + EUSBCON_REG_PLLCFG1);
|
||
((EUSBPHY_REG_PLLCFG1_p) (®))->b.pll_ref_div = 0;
|
||
writel(reg, base + EUSBCON_REG_PLLCFG1);
|
||
pr_info("%s: 24\n", __func__);
|
||
} else if ((info->refclk == USBPHY_REFCLK_DIFF_26MHZ) ||
|
||
(info->refclk == USBPHY_REFCLK_EXT_26MHZ)) {
|
||
/* Reference Clock Frequency : 26MHz
|
||
* ref_freq_sel[2:0] : 3
|
||
* phy_cfg_pll_fb_div[11:0] : 263
|
||
* phy_cfg_pll_ref_div[3:0] : 0
|
||
*/
|
||
/* ref_freq_sel */
|
||
((EUSBPHY_REG_CMN_CTRL_p) (®))->b.ref_freq_sel = 3;
|
||
writel(reg, base + EUSBCON_REG_CMN_CTRL);
|
||
/* phy_cfg_pll_fb_div[11:0] */
|
||
reg = readl(base + EUSBCON_REG_PLLCFG0);
|
||
((EUSBPHY_REG_PLLCFG0_p) (®))->b.pll_fb_div = 263;
|
||
writel(reg, base + EUSBCON_REG_PLLCFG0);
|
||
/* phy_cfg_pll_ref_div[3:0] */
|
||
reg = readl(base + EUSBCON_REG_PLLCFG1);
|
||
((EUSBPHY_REG_PLLCFG1_p) (®))->b.pll_ref_div = 0;
|
||
writel(reg, base + EUSBCON_REG_PLLCFG1);
|
||
pr_info("%s: 26\n", __func__);
|
||
} else if ((info->refclk == USBPHY_REFCLK_DIFF_48MHZ) ||
|
||
(info->refclk == USBPHY_REFCLK_EXT_48MHZ)) {
|
||
/* Reference Clock Frequency : 48MHz
|
||
* ref_freq_sel[2:0] : 2
|
||
* phy_cfg_pll_fb_div[11:0] : 288
|
||
* phy_cfg_pll_ref_div[3:0] : 1
|
||
*/
|
||
/* ref_freq_sel */
|
||
((EUSBPHY_REG_CMN_CTRL_p) (®))->b.ref_freq_sel = 2;
|
||
writel(reg, base + EUSBCON_REG_CMN_CTRL);
|
||
/* phy_cfg_pll_fb_div[11:0] */
|
||
reg = readl(base + EUSBCON_REG_PLLCFG0);
|
||
((EUSBPHY_REG_PLLCFG0_p) (®))->b.pll_fb_div = 288;
|
||
writel(reg, base + EUSBCON_REG_PLLCFG0);
|
||
/* phy_cfg_pll_ref_div[3:0] */
|
||
reg = readl(base + EUSBCON_REG_PLLCFG1);
|
||
((EUSBPHY_REG_PLLCFG1_p) (®))->b.pll_ref_div = 1;
|
||
writel(reg, base + EUSBCON_REG_PLLCFG1);
|
||
} else {
|
||
/* Not supported clock, so skip */
|
||
}
|
||
|
||
phy_exynos_eusb_tune(info);
|
||
|
||
/* 3. Set all inputs to a default state as necessary for
|
||
* the eUSB 2.0 PHY application.
|
||
*/
|
||
reg = readl(base + EUSBCON_REG_TESTSE);
|
||
((EUSBPHY_REG_TESTSE_p) (®))->b.test_iddq = 0;
|
||
writel(reg, base + EUSBCON_REG_TESTSE);
|
||
|
||
reg = readl(base + EUSBCON_REG_CMN_CTRL);
|
||
((EUSBPHY_REG_CMN_CTRL_p) (®))->b.phy_enable = 0;
|
||
writel(reg, base + EUSBCON_REG_CMN_CTRL);
|
||
|
||
/* 5. If some registers programming is needed before
|
||
* power-up sequence starts, keep phy_enable at 1’b0,
|
||
* otherwise keep phy_enable at 1’b1 and skip step 7.
|
||
*/
|
||
|
||
/* After releasing test_iddq to 1'b0,
|
||
* phy_reset should be held at 1'b1 for at least an additional 10us.
|
||
*/
|
||
udelay(10);
|
||
|
||
/* 6. Release phy_reset from 1'b1 to 1'b0 */
|
||
reg = readl(base + EUSBCON_REG_RST_CTRL);
|
||
((EUSBPHY_REG_RST_CTRL_p) (®))->b.phy_reset = 0;
|
||
writel(reg, base + EUSBCON_REG_RST_CTRL);
|
||
|
||
/* additional delay for REXT calibration */
|
||
udelay(10);
|
||
|
||
/* 7. Perform all necessary registers programming
|
||
* (configuration settings) and then set phy_enable at
|
||
* 1’b1.
|
||
*/
|
||
|
||
reg = readl(base + EUSBCON_REG_CMN_CTRL);
|
||
((EUSBPHY_REG_CMN_CTRL_p) (®))->b.phy_enable = 1;
|
||
writel(reg, base + EUSBCON_REG_CMN_CTRL);
|
||
|
||
/* additional delay for REXT calibration */
|
||
value = 1000;
|
||
udelay(value);
|
||
|
||
/* T4 indicates when the PHY analog and digital circuit has powered up and
|
||
* is ready to perform an eUSB protocol Port Reset (ESE1). The utmi_clk
|
||
* output clock starts toggling.
|
||
*/
|
||
udelay(28);
|
||
|
||
/* T5 indicates when the PHY (eDSPr/eDSPn/eUSPr) has completed the
|
||
* transmission of Port Reset (ESE1) on eUSB lines.
|
||
*/
|
||
udelay(2500);
|
||
|
||
reg = readl(base + EUSBCON_REG_RST_CTRL);
|
||
((EUSBPHY_REG_RST_CTRL_p) (®))->b.utmi_port_reset = 0;
|
||
((EUSBPHY_REG_RST_CTRL_p) (®))->b.utmi_port_reset_ovrd_en = 0;
|
||
writel(reg, base + EUSBCON_REG_RST_CTRL);
|
||
|
||
|
||
}
|
||
|
||
void phy_exynos_eusb_terminate(struct exynos_usbphy_info *info)
|
||
{
|
||
u32 reg;
|
||
void *base;
|
||
|
||
base = info->regs_base;
|
||
|
||
reg = readl(base + EUSBCON_REG_RST_CTRL);
|
||
((EUSBPHY_REG_RST_CTRL_p) (®))->b.phy_reset = 1;
|
||
writel(reg, base + EUSBCON_REG_RST_CTRL);
|
||
}
|
||
|
||
u8 phy_exynos_eusb_get_eusb_state(struct exynos_usbphy_info *info)
|
||
{
|
||
EUSBPHY_REG_PHY_CR_XCVR0_DIG_CTL_EUSB_STATE_JMP_DBG_OUT_o reg;
|
||
void *base;
|
||
|
||
base = info->regs_base_2nd;
|
||
reg.data = readl(base + EUSBPHY_REG_PHY_CR_XCVR0_DIG_CTL_EUSB_STATE_JMP_DBG_OUT) & 0xffff;
|
||
return reg.b.eusb_state_jmp_dbg_out;
|
||
}
|