static int qusb2_phy_init()

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;
}