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

320 lines
10 KiB
C
Executable file
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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) (&reg))->b.phy_reset = 1;
((EUSBPHY_REG_RST_CTRL_p) (&reg))->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) (&reg))->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) (&reg))->b.phy_reset = 1;
((EUSBPHY_REG_RST_CTRL_p) (&reg))->b.phy_reset_ovrd_en = 1;
((EUSBPHY_REG_RST_CTRL_p) (&reg))->b.utmi_port_reset = 1;
((EUSBPHY_REG_RST_CTRL_p) (&reg))->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) (&reg))->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) (&reg))->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) (&reg))->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) (&reg))->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) (&reg))->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) (&reg))->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) (&reg))->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) (&reg))->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) (&reg))->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) (&reg))->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) (&reg))->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) (&reg))->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) (&reg))->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) (&reg))->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) (&reg))->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) (&reg))->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) (&reg))->b.test_iddq = 0;
writel(reg, base + EUSBCON_REG_TESTSE);
reg = readl(base + EUSBCON_REG_CMN_CTRL);
((EUSBPHY_REG_CMN_CTRL_p) (&reg))->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 1b0,
* otherwise keep phy_enable at 1b1 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) (&reg))->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
* 1b1.
*/
reg = readl(base + EUSBCON_REG_CMN_CTRL);
((EUSBPHY_REG_CMN_CTRL_p) (&reg))->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) (&reg))->b.utmi_port_reset = 0;
((EUSBPHY_REG_RST_CTRL_p) (&reg))->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) (&reg))->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;
}