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