static int imx8m_ddrc_set_freq()

in imx8m-ddrc.c [128:228]


static int imx8m_ddrc_set_freq(struct device *dev, struct imx8m_ddrc_freq *freq)
{
	struct imx8m_ddrc *priv = dev_get_drvdata(dev);
	struct clk *new_dram_core_parent;
	struct clk *new_dram_alt_parent;
	struct clk *new_dram_apb_parent;
	int ret;

	/*
	 * Fetch new parents
	 *
	 * new_dram_alt_parent and new_dram_apb_parent are optional but
	 * new_dram_core_parent is not.
	 */
	new_dram_core_parent = clk_get_parent_by_index(
			priv->dram_core, freq->dram_core_parent_index - 1);
	if (!new_dram_core_parent) {
		dev_err(dev, "failed to fetch new dram_core parent\n");
		return -EINVAL;
	}
	if (freq->dram_alt_parent_index) {
		new_dram_alt_parent = clk_get_parent_by_index(
				priv->dram_alt,
				freq->dram_alt_parent_index - 1);
		if (!new_dram_alt_parent) {
			dev_err(dev, "failed to fetch new dram_alt parent\n");
			return -EINVAL;
		}
	} else
		new_dram_alt_parent = NULL;

	if (freq->dram_apb_parent_index) {
		new_dram_apb_parent = clk_get_parent_by_index(
				priv->dram_apb,
				freq->dram_apb_parent_index - 1);
		if (!new_dram_apb_parent) {
			dev_err(dev, "failed to fetch new dram_apb parent\n");
			return -EINVAL;
		}
	} else
		new_dram_apb_parent = NULL;

	/* increase reference counts and ensure clks are ON before switch */
	ret = clk_prepare_enable(new_dram_core_parent);
	if (ret) {
		dev_err(dev, "failed to enable new dram_core parent: %d\n",
			ret);
		goto out;
	}
	ret = clk_prepare_enable(new_dram_alt_parent);
	if (ret) {
		dev_err(dev, "failed to enable new dram_alt parent: %d\n",
			ret);
		goto out_disable_core_parent;
	}
	ret = clk_prepare_enable(new_dram_apb_parent);
	if (ret) {
		dev_err(dev, "failed to enable new dram_apb parent: %d\n",
			ret);
		goto out_disable_alt_parent;
	}

	imx8m_ddrc_smc_set_freq(freq->smcarg);

	/* update parents in clk tree after switch. */
	ret = clk_set_parent(priv->dram_core, new_dram_core_parent);
	if (ret)
		dev_warn(dev, "failed to set dram_core parent: %d\n", ret);
	if (new_dram_alt_parent) {
		ret = clk_set_parent(priv->dram_alt, new_dram_alt_parent);
		if (ret)
			dev_warn(dev, "failed to set dram_alt parent: %d\n",
				 ret);
	}
	if (new_dram_apb_parent) {
		ret = clk_set_parent(priv->dram_apb, new_dram_apb_parent);
		if (ret)
			dev_warn(dev, "failed to set dram_apb parent: %d\n",
				 ret);
	}

	/*
	 * Explicitly refresh dram PLL rate.
	 *
	 * Even if it's marked with CLK_GET_RATE_NOCACHE the rate will not be
	 * automatically refreshed when clk_get_rate is called on children.
	 */
	clk_get_rate(priv->dram_pll);

	/*
	 * clk_set_parent transfer the reference count from old parent.
	 * now we drop extra reference counts used during the switch
	 */
	clk_disable_unprepare(new_dram_apb_parent);
out_disable_alt_parent:
	clk_disable_unprepare(new_dram_alt_parent);
out_disable_core_parent:
	clk_disable_unprepare(new_dram_core_parent);
out:
	return ret;
}