static int qcom_qmp_phy_power_on()

in qualcomm/phy-qcom-qmp.c [5171:5300]


static int qcom_qmp_phy_power_on(struct phy *phy)
{
	struct qmp_phy *qphy = phy_get_drvdata(phy);
	struct qcom_qmp *qmp = qphy->qmp;
	const struct qmp_phy_cfg *cfg = qphy->cfg;
	void __iomem *tx = qphy->tx;
	void __iomem *rx = qphy->rx;
	void __iomem *pcs = qphy->pcs;
	void __iomem *pcs_misc = qphy->pcs_misc;
	void __iomem *status;
	unsigned int mask, val, ready;
	int ret;

	qcom_qmp_phy_serdes_init(qphy);

	if (cfg->has_lane_rst) {
		ret = reset_control_deassert(qphy->lane_rst);
		if (ret) {
			dev_err(qmp->dev, "lane%d reset deassert failed\n",
				qphy->index);
			goto err_lane_rst;
		}
	}

	ret = clk_prepare_enable(qphy->pipe_clk);
	if (ret) {
		dev_err(qmp->dev, "pipe_clk enable failed err=%d\n", ret);
		goto err_clk_enable;
	}

	/* Tx, Rx, and PCS configurations */
	qcom_qmp_phy_configure_lane(tx, cfg->regs,
				    cfg->tx_tbl, cfg->tx_tbl_num, 1);
	if (cfg->tx_tbl_sec)
		qcom_qmp_phy_configure_lane(tx, cfg->regs, cfg->tx_tbl_sec,
					    cfg->tx_tbl_num_sec, 1);

	/* Configuration for other LANE for USB-DP combo PHY */
	if (cfg->is_dual_lane_phy) {
		qcom_qmp_phy_configure_lane(qphy->tx2, cfg->regs,
					    cfg->tx_tbl, cfg->tx_tbl_num, 2);
		if (cfg->tx_tbl_sec)
			qcom_qmp_phy_configure_lane(qphy->tx2, cfg->regs,
						    cfg->tx_tbl_sec,
						    cfg->tx_tbl_num_sec, 2);
	}

	/* Configure special DP tx tunings */
	if (cfg->type == PHY_TYPE_DP)
		cfg->configure_dp_tx(qphy);

	qcom_qmp_phy_configure_lane(rx, cfg->regs,
				    cfg->rx_tbl, cfg->rx_tbl_num, 1);
	if (cfg->rx_tbl_sec)
		qcom_qmp_phy_configure_lane(rx, cfg->regs,
					    cfg->rx_tbl_sec, cfg->rx_tbl_num_sec, 1);

	if (cfg->is_dual_lane_phy) {
		qcom_qmp_phy_configure_lane(qphy->rx2, cfg->regs,
					    cfg->rx_tbl, cfg->rx_tbl_num, 2);
		if (cfg->rx_tbl_sec)
			qcom_qmp_phy_configure_lane(qphy->rx2, cfg->regs,
						    cfg->rx_tbl_sec,
						    cfg->rx_tbl_num_sec, 2);
	}

	/* Configure link rate, swing, etc. */
	if (cfg->type == PHY_TYPE_DP) {
		cfg->configure_dp_phy(qphy);
	} else {
		qcom_qmp_phy_configure(pcs, cfg->regs, cfg->pcs_tbl, cfg->pcs_tbl_num);
		if (cfg->pcs_tbl_sec)
			qcom_qmp_phy_configure(pcs, cfg->regs, cfg->pcs_tbl_sec,
					       cfg->pcs_tbl_num_sec);
	}

	ret = reset_control_deassert(qmp->ufs_reset);
	if (ret)
		goto err_lane_rst;

	qcom_qmp_phy_configure(pcs_misc, cfg->regs, cfg->pcs_misc_tbl,
			       cfg->pcs_misc_tbl_num);
	if (cfg->pcs_misc_tbl_sec)
		qcom_qmp_phy_configure(pcs_misc, cfg->regs, cfg->pcs_misc_tbl_sec,
				       cfg->pcs_misc_tbl_num_sec);

	/*
	 * Pull out PHY from POWER DOWN state.
	 * This is active low enable signal to power-down PHY.
	 */
	if(cfg->type == PHY_TYPE_PCIE)
		qphy_setbits(pcs, QPHY_POWER_DOWN_CONTROL, cfg->pwrdn_ctrl);

	if (cfg->has_pwrdn_delay)
		usleep_range(cfg->pwrdn_delay_min, cfg->pwrdn_delay_max);

	if (cfg->type != PHY_TYPE_DP) {
		/* Pull PHY out of reset state */
		if (!cfg->no_pcs_sw_reset)
			qphy_clrbits(pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
		/* start SerDes and Phy-Coding-Sublayer */
		qphy_setbits(pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl);

		if (cfg->type == PHY_TYPE_UFS) {
			status = pcs + cfg->regs[QPHY_PCS_READY_STATUS];
			mask = PCS_READY;
			ready = PCS_READY;
		} else {
			status = pcs + cfg->regs[QPHY_PCS_STATUS];
			mask = cfg->phy_status;
			ready = 0;
		}

		ret = readl_poll_timeout(status, val, (val & mask) == ready, 10,
					 PHY_INIT_COMPLETE_TIMEOUT);
		if (ret) {
			dev_err(qmp->dev, "phy initialization timed-out\n");
			goto err_pcs_ready;
		}
	}
	return 0;

err_pcs_ready:
	clk_disable_unprepare(qphy->pipe_clk);
err_clk_enable:
	if (cfg->has_lane_rst)
		reset_control_assert(qphy->lane_rst);
err_lane_rst:
	return ret;
}