in qualcomm/phy-qcom-qusb2.c [726:875]
static int qusb2_phy_init(struct phy *phy)
{
struct qusb2_phy *qphy = phy_get_drvdata(phy);
const struct qusb2_phy_cfg *cfg = qphy->cfg;
unsigned int val = 0;
unsigned int clk_scheme;
int ret;
dev_vdbg(&phy->dev, "%s(): Initializing QUSB2 phy\n", __func__);
/* turn on regulator supplies */
ret = regulator_bulk_enable(ARRAY_SIZE(qphy->vregs), qphy->vregs);
if (ret)
return ret;
ret = clk_prepare_enable(qphy->iface_clk);
if (ret) {
dev_err(&phy->dev, "failed to enable iface_clk, %d\n", ret);
goto poweroff_phy;
}
/* enable ahb interface clock to program phy */
ret = clk_prepare_enable(qphy->cfg_ahb_clk);
if (ret) {
dev_err(&phy->dev, "failed to enable cfg ahb clock, %d\n", ret);
goto disable_iface_clk;
}
/* Perform phy reset */
ret = reset_control_assert(qphy->phy_reset);
if (ret) {
dev_err(&phy->dev, "failed to assert phy_reset, %d\n", ret);
goto disable_ahb_clk;
}
/* 100 us delay to keep PHY in reset mode */
usleep_range(100, 150);
ret = reset_control_deassert(qphy->phy_reset);
if (ret) {
dev_err(&phy->dev, "failed to de-assert phy_reset, %d\n", ret);
goto disable_ahb_clk;
}
/* Disable the PHY */
qusb2_setbits(qphy->base, cfg->regs[QUSB2PHY_PORT_POWERDOWN],
qphy->cfg->disable_ctrl);
if (cfg->has_pll_test) {
/* save reset value to override reference clock scheme later */
val = readl(qphy->base + QUSB2PHY_PLL_TEST);
}
qcom_qusb2_phy_configure(qphy->base, cfg->regs, cfg->tbl,
cfg->tbl_num);
/* Override board specific PHY tuning values */
qusb2_phy_override_phy_params(qphy);
/* Set efuse value for tuning the PHY */
qusb2_phy_set_tune2_param(qphy);
/* Enable the PHY */
qusb2_clrbits(qphy->base, cfg->regs[QUSB2PHY_PORT_POWERDOWN],
POWER_DOWN);
/* Required to get phy pll lock successfully */
usleep_range(150, 160);
/*
* Not all the SoCs have got a readable TCSR_PHY_CLK_SCHEME
* register in the TCSR so, if there's none, use the default
* value hardcoded in the configuration.
*/
qphy->has_se_clk_scheme = cfg->se_clk_scheme_default;
/*
* read TCSR_PHY_CLK_SCHEME register to check if single-ended
* clock scheme is selected. If yes, then disable differential
* ref_clk and use single-ended clock, otherwise use differential
* ref_clk only.
*/
if (qphy->tcsr) {
ret = regmap_read(qphy->tcsr, qphy->cfg->clk_scheme_offset,
&clk_scheme);
if (ret) {
dev_err(&phy->dev, "failed to read clk scheme reg\n");
goto assert_phy_reset;
}
/* is it a differential clock scheme ? */
if (!(clk_scheme & PHY_CLK_SCHEME_SEL)) {
dev_vdbg(&phy->dev, "%s(): select differential clk\n",
__func__);
qphy->has_se_clk_scheme = false;
} else {
dev_vdbg(&phy->dev, "%s(): select single-ended clk\n",
__func__);
}
}
if (!qphy->has_se_clk_scheme) {
ret = clk_prepare_enable(qphy->ref_clk);
if (ret) {
dev_err(&phy->dev, "failed to enable ref clk, %d\n",
ret);
goto assert_phy_reset;
}
}
if (cfg->has_pll_test) {
if (!qphy->has_se_clk_scheme)
val &= ~CLK_REF_SEL;
else
val |= CLK_REF_SEL;
writel(val, qphy->base + QUSB2PHY_PLL_TEST);
/* ensure above write is through */
readl(qphy->base + QUSB2PHY_PLL_TEST);
}
/* Required to get phy pll lock successfully */
usleep_range(100, 110);
val = readb(qphy->base + cfg->regs[QUSB2PHY_PLL_STATUS]);
if (!(val & cfg->mask_core_ready)) {
dev_err(&phy->dev,
"QUSB2PHY pll lock failed: status reg = %x\n", val);
ret = -EBUSY;
goto disable_ref_clk;
}
qphy->phy_initialized = true;
return 0;
disable_ref_clk:
if (!qphy->has_se_clk_scheme)
clk_disable_unprepare(qphy->ref_clk);
assert_phy_reset:
reset_control_assert(qphy->phy_reset);
disable_ahb_clk:
clk_disable_unprepare(qphy->cfg_ahb_clk);
disable_iface_clk:
clk_disable_unprepare(qphy->iface_clk);
poweroff_phy:
regulator_bulk_disable(ARRAY_SIZE(qphy->vregs), qphy->vregs);
return ret;
}