Lines Matching +full:clk +full:- +full:out +full:- +full:frequency +full:- +full:hz

1 // SPDX-License-Identifier: GPL-2.0
12 #include <linux/clk.h>
13 #include <linux/clk-provider.h>
14 #include <linux/arm-smccc.h>
40 * +----------+ |\ +------+
41 * | dram_pll |-------|M| dram_core | |
42 * +----------+ |U|---------->| D |
43 * /--|X| | D |
46 * +---------+ | |
48 * +---------+ | |
50 * +----------+ | | |
51 * | dram_alt |----/ | |
52 * +----------+ | |
53 * | dram_apb |-------------------->| |
54 * +----------+ +------+
58 * Frequency switching is implemented in TF-A (via SMC call) and can change the
69 /* For frequency switching: */
70 struct clk *dram_core;
71 struct clk *dram_pll;
72 struct clk *dram_alt;
73 struct clk *dram_apb;
86 * Firmware reports values in MT/s, so we round-down from Hz in imx8m_ddrc_find_freq()
90 for (i = 0; i < priv->freq_count; ++i) { in imx8m_ddrc_find_freq()
91 freq = &priv->freq_table[i]; in imx8m_ddrc_find_freq()
92 if (freq->rate == rate || in imx8m_ddrc_find_freq()
93 freq->rate + 1 == rate || in imx8m_ddrc_find_freq()
94 freq->rate - 1 == rate) in imx8m_ddrc_find_freq()
119 static struct clk *clk_get_parent_by_index(struct clk *clk, int index) in clk_get_parent_by_index() argument
123 hw = clk_hw_get_parent_by_index(__clk_get_hw(clk), index); in clk_get_parent_by_index()
125 return hw ? hw->clk : NULL; in clk_get_parent_by_index()
131 struct clk *new_dram_core_parent; in imx8m_ddrc_set_freq()
132 struct clk *new_dram_alt_parent; in imx8m_ddrc_set_freq()
133 struct clk *new_dram_apb_parent; in imx8m_ddrc_set_freq()
143 priv->dram_core, freq->dram_core_parent_index - 1); in imx8m_ddrc_set_freq()
146 return -EINVAL; in imx8m_ddrc_set_freq()
148 if (freq->dram_alt_parent_index) { in imx8m_ddrc_set_freq()
150 priv->dram_alt, in imx8m_ddrc_set_freq()
151 freq->dram_alt_parent_index - 1); in imx8m_ddrc_set_freq()
154 return -EINVAL; in imx8m_ddrc_set_freq()
159 if (freq->dram_apb_parent_index) { in imx8m_ddrc_set_freq()
161 priv->dram_apb, in imx8m_ddrc_set_freq()
162 freq->dram_apb_parent_index - 1); in imx8m_ddrc_set_freq()
165 return -EINVAL; in imx8m_ddrc_set_freq()
175 goto out; in imx8m_ddrc_set_freq()
190 imx8m_ddrc_smc_set_freq(freq->smcarg); in imx8m_ddrc_set_freq()
192 /* update parents in clk tree after switch. */ in imx8m_ddrc_set_freq()
193 ret = clk_set_parent(priv->dram_core, new_dram_core_parent); in imx8m_ddrc_set_freq()
197 ret = clk_set_parent(priv->dram_alt, new_dram_alt_parent); in imx8m_ddrc_set_freq()
203 ret = clk_set_parent(priv->dram_apb, new_dram_apb_parent); in imx8m_ddrc_set_freq()
215 clk_get_rate(priv->dram_pll); in imx8m_ddrc_set_freq()
226 out: in imx8m_ddrc_set_freq()
246 old_freq = clk_get_rate(priv->dram_core); in imx8m_ddrc_target()
252 return -EINVAL; in imx8m_ddrc_target()
255 * Read back the clk rate to verify switch was correct and so that in imx8m_ddrc_target()
260 new_freq = clk_get_rate(priv->dram_core); in imx8m_ddrc_target()
278 *freq = clk_get_rate(priv->dram_core); in imx8m_ddrc_get_cur_freq()
292 priv->freq_count = res.a0; in imx8m_ddrc_init_freq_info()
293 if (priv->freq_count <= 0 || in imx8m_ddrc_init_freq_info()
294 priv->freq_count > IMX8M_DDRC_MAX_FREQ_COUNT) in imx8m_ddrc_init_freq_info()
295 return -ENODEV; in imx8m_ddrc_init_freq_info()
297 for (index = 0; index < priv->freq_count; ++index) { in imx8m_ddrc_init_freq_info()
298 struct imx8m_ddrc_freq *freq = &priv->freq_table[index]; in imx8m_ddrc_init_freq_info()
304 return -ENODEV; in imx8m_ddrc_init_freq_info()
306 freq->rate = res.a0; in imx8m_ddrc_init_freq_info()
307 freq->smcarg = index; in imx8m_ddrc_init_freq_info()
308 freq->dram_core_parent_index = res.a1; in imx8m_ddrc_init_freq_info()
309 freq->dram_alt_parent_index = res.a2; in imx8m_ddrc_init_freq_info()
310 freq->dram_apb_parent_index = res.a3; in imx8m_ddrc_init_freq_info()
313 if (freq->dram_core_parent_index != 1 && in imx8m_ddrc_init_freq_info()
314 freq->dram_core_parent_index != 2) in imx8m_ddrc_init_freq_info()
315 return -ENODEV; in imx8m_ddrc_init_freq_info()
317 if (freq->dram_alt_parent_index > 8 || in imx8m_ddrc_init_freq_info()
318 freq->dram_apb_parent_index > 8) in imx8m_ddrc_init_freq_info()
319 return -ENODEV; in imx8m_ddrc_init_freq_info()
321 if (freq->dram_core_parent_index == 2 && in imx8m_ddrc_init_freq_info()
322 freq->dram_alt_parent_index == 0) in imx8m_ddrc_init_freq_info()
323 return -ENODEV; in imx8m_ddrc_init_freq_info()
368 struct device *dev = &pdev->dev; in imx8m_ddrc_probe()
375 return -ENOMEM; in imx8m_ddrc_probe()
385 priv->dram_core = devm_clk_get(dev, "core"); in imx8m_ddrc_probe()
386 if (IS_ERR(priv->dram_core)) { in imx8m_ddrc_probe()
387 ret = PTR_ERR(priv->dram_core); in imx8m_ddrc_probe()
391 priv->dram_pll = devm_clk_get(dev, "pll"); in imx8m_ddrc_probe()
392 if (IS_ERR(priv->dram_pll)) { in imx8m_ddrc_probe()
393 ret = PTR_ERR(priv->dram_pll); in imx8m_ddrc_probe()
397 priv->dram_alt = devm_clk_get(dev, "alt"); in imx8m_ddrc_probe()
398 if (IS_ERR(priv->dram_alt)) { in imx8m_ddrc_probe()
399 ret = PTR_ERR(priv->dram_alt); in imx8m_ddrc_probe()
403 priv->dram_apb = devm_clk_get(dev, "apb"); in imx8m_ddrc_probe()
404 if (IS_ERR(priv->dram_apb)) { in imx8m_ddrc_probe()
405 ret = PTR_ERR(priv->dram_apb); in imx8m_ddrc_probe()
420 priv->profile.target = imx8m_ddrc_target; in imx8m_ddrc_probe()
421 priv->profile.exit = imx8m_ddrc_exit; in imx8m_ddrc_probe()
422 priv->profile.get_cur_freq = imx8m_ddrc_get_cur_freq; in imx8m_ddrc_probe()
423 priv->profile.initial_freq = clk_get_rate(priv->dram_core); in imx8m_ddrc_probe()
425 priv->devfreq = devm_devfreq_add_device(dev, &priv->profile, in imx8m_ddrc_probe()
427 if (IS_ERR(priv->devfreq)) { in imx8m_ddrc_probe()
428 ret = PTR_ERR(priv->devfreq); in imx8m_ddrc_probe()
441 { .compatible = "fsl,imx8m-ddrc", },
449 .name = "imx8m-ddrc-devfreq",
455 MODULE_DESCRIPTION("i.MX8M DDR Controller frequency driver");