0001
0002
0003
0004
0005
0006 #include <linux/module.h>
0007 #include <linux/device.h>
0008 #include <linux/of_device.h>
0009 #include <linux/platform_device.h>
0010 #include <linux/devfreq.h>
0011 #include <linux/pm_opp.h>
0012 #include <linux/clk.h>
0013 #include <linux/clk-provider.h>
0014 #include <linux/arm-smccc.h>
0015
0016 #define IMX_SIP_DDR_DVFS 0xc2000004
0017
0018
0019 #define IMX_SIP_DDR_DVFS_GET_FREQ_COUNT 0x10
0020 #define IMX_SIP_DDR_DVFS_GET_FREQ_INFO 0x11
0021
0022
0023
0024
0025
0026 struct imx8m_ddrc_freq {
0027 unsigned long rate;
0028 unsigned long smcarg;
0029 int dram_core_parent_index;
0030 int dram_alt_parent_index;
0031 int dram_apb_parent_index;
0032 };
0033
0034
0035 #define IMX8M_DDRC_MAX_FREQ_COUNT 4
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059
0060
0061
0062
0063
0064
0065 struct imx8m_ddrc {
0066 struct devfreq_dev_profile profile;
0067 struct devfreq *devfreq;
0068
0069
0070 struct clk *dram_core;
0071 struct clk *dram_pll;
0072 struct clk *dram_alt;
0073 struct clk *dram_apb;
0074
0075 int freq_count;
0076 struct imx8m_ddrc_freq freq_table[IMX8M_DDRC_MAX_FREQ_COUNT];
0077 };
0078
0079 static struct imx8m_ddrc_freq *imx8m_ddrc_find_freq(struct imx8m_ddrc *priv,
0080 unsigned long rate)
0081 {
0082 struct imx8m_ddrc_freq *freq;
0083 int i;
0084
0085
0086
0087
0088
0089 rate = DIV_ROUND_CLOSEST(rate, 250000);
0090 for (i = 0; i < priv->freq_count; ++i) {
0091 freq = &priv->freq_table[i];
0092 if (freq->rate == rate ||
0093 freq->rate + 1 == rate ||
0094 freq->rate - 1 == rate)
0095 return freq;
0096 }
0097
0098 return NULL;
0099 }
0100
0101 static void imx8m_ddrc_smc_set_freq(int target_freq)
0102 {
0103 struct arm_smccc_res res;
0104 u32 online_cpus = 0;
0105 int cpu;
0106
0107 local_irq_disable();
0108
0109 for_each_online_cpu(cpu)
0110 online_cpus |= (1 << (cpu * 8));
0111
0112
0113 arm_smccc_smc(IMX_SIP_DDR_DVFS, target_freq, online_cpus,
0114 0, 0, 0, 0, 0, &res);
0115
0116 local_irq_enable();
0117 }
0118
0119 static struct clk *clk_get_parent_by_index(struct clk *clk, int index)
0120 {
0121 struct clk_hw *hw;
0122
0123 hw = clk_hw_get_parent_by_index(__clk_get_hw(clk), index);
0124
0125 return hw ? hw->clk : NULL;
0126 }
0127
0128 static int imx8m_ddrc_set_freq(struct device *dev, struct imx8m_ddrc_freq *freq)
0129 {
0130 struct imx8m_ddrc *priv = dev_get_drvdata(dev);
0131 struct clk *new_dram_core_parent;
0132 struct clk *new_dram_alt_parent;
0133 struct clk *new_dram_apb_parent;
0134 int ret;
0135
0136
0137
0138
0139
0140
0141
0142 new_dram_core_parent = clk_get_parent_by_index(
0143 priv->dram_core, freq->dram_core_parent_index - 1);
0144 if (!new_dram_core_parent) {
0145 dev_err(dev, "failed to fetch new dram_core parent\n");
0146 return -EINVAL;
0147 }
0148 if (freq->dram_alt_parent_index) {
0149 new_dram_alt_parent = clk_get_parent_by_index(
0150 priv->dram_alt,
0151 freq->dram_alt_parent_index - 1);
0152 if (!new_dram_alt_parent) {
0153 dev_err(dev, "failed to fetch new dram_alt parent\n");
0154 return -EINVAL;
0155 }
0156 } else
0157 new_dram_alt_parent = NULL;
0158
0159 if (freq->dram_apb_parent_index) {
0160 new_dram_apb_parent = clk_get_parent_by_index(
0161 priv->dram_apb,
0162 freq->dram_apb_parent_index - 1);
0163 if (!new_dram_apb_parent) {
0164 dev_err(dev, "failed to fetch new dram_apb parent\n");
0165 return -EINVAL;
0166 }
0167 } else
0168 new_dram_apb_parent = NULL;
0169
0170
0171 ret = clk_prepare_enable(new_dram_core_parent);
0172 if (ret) {
0173 dev_err(dev, "failed to enable new dram_core parent: %d\n",
0174 ret);
0175 goto out;
0176 }
0177 ret = clk_prepare_enable(new_dram_alt_parent);
0178 if (ret) {
0179 dev_err(dev, "failed to enable new dram_alt parent: %d\n",
0180 ret);
0181 goto out_disable_core_parent;
0182 }
0183 ret = clk_prepare_enable(new_dram_apb_parent);
0184 if (ret) {
0185 dev_err(dev, "failed to enable new dram_apb parent: %d\n",
0186 ret);
0187 goto out_disable_alt_parent;
0188 }
0189
0190 imx8m_ddrc_smc_set_freq(freq->smcarg);
0191
0192
0193 ret = clk_set_parent(priv->dram_core, new_dram_core_parent);
0194 if (ret)
0195 dev_warn(dev, "failed to set dram_core parent: %d\n", ret);
0196 if (new_dram_alt_parent) {
0197 ret = clk_set_parent(priv->dram_alt, new_dram_alt_parent);
0198 if (ret)
0199 dev_warn(dev, "failed to set dram_alt parent: %d\n",
0200 ret);
0201 }
0202 if (new_dram_apb_parent) {
0203 ret = clk_set_parent(priv->dram_apb, new_dram_apb_parent);
0204 if (ret)
0205 dev_warn(dev, "failed to set dram_apb parent: %d\n",
0206 ret);
0207 }
0208
0209
0210
0211
0212
0213
0214
0215 clk_get_rate(priv->dram_pll);
0216
0217
0218
0219
0220
0221 clk_disable_unprepare(new_dram_apb_parent);
0222 out_disable_alt_parent:
0223 clk_disable_unprepare(new_dram_alt_parent);
0224 out_disable_core_parent:
0225 clk_disable_unprepare(new_dram_core_parent);
0226 out:
0227 return ret;
0228 }
0229
0230 static int imx8m_ddrc_target(struct device *dev, unsigned long *freq, u32 flags)
0231 {
0232 struct imx8m_ddrc *priv = dev_get_drvdata(dev);
0233 struct imx8m_ddrc_freq *freq_info;
0234 struct dev_pm_opp *new_opp;
0235 unsigned long old_freq, new_freq;
0236 int ret;
0237
0238 new_opp = devfreq_recommended_opp(dev, freq, flags);
0239 if (IS_ERR(new_opp)) {
0240 ret = PTR_ERR(new_opp);
0241 dev_err(dev, "failed to get recommended opp: %d\n", ret);
0242 return ret;
0243 }
0244 dev_pm_opp_put(new_opp);
0245
0246 old_freq = clk_get_rate(priv->dram_core);
0247 if (*freq == old_freq)
0248 return 0;
0249
0250 freq_info = imx8m_ddrc_find_freq(priv, *freq);
0251 if (!freq_info)
0252 return -EINVAL;
0253
0254
0255
0256
0257
0258 ret = imx8m_ddrc_set_freq(dev, freq_info);
0259
0260 new_freq = clk_get_rate(priv->dram_core);
0261 if (ret)
0262 dev_err(dev, "ddrc failed freq switch to %lu from %lu: error %d. now at %lu\n",
0263 *freq, old_freq, ret, new_freq);
0264 else if (*freq != new_freq)
0265 dev_err(dev, "ddrc failed freq update to %lu from %lu, now at %lu\n",
0266 *freq, old_freq, new_freq);
0267 else
0268 dev_dbg(dev, "ddrc freq set to %lu (was %lu)\n",
0269 *freq, old_freq);
0270
0271 return ret;
0272 }
0273
0274 static int imx8m_ddrc_get_cur_freq(struct device *dev, unsigned long *freq)
0275 {
0276 struct imx8m_ddrc *priv = dev_get_drvdata(dev);
0277
0278 *freq = clk_get_rate(priv->dram_core);
0279
0280 return 0;
0281 }
0282
0283 static int imx8m_ddrc_init_freq_info(struct device *dev)
0284 {
0285 struct imx8m_ddrc *priv = dev_get_drvdata(dev);
0286 struct arm_smccc_res res;
0287 int index;
0288
0289
0290 arm_smccc_smc(IMX_SIP_DDR_DVFS, IMX_SIP_DDR_DVFS_GET_FREQ_COUNT,
0291 0, 0, 0, 0, 0, 0, &res);
0292 priv->freq_count = res.a0;
0293 if (priv->freq_count <= 0 ||
0294 priv->freq_count > IMX8M_DDRC_MAX_FREQ_COUNT)
0295 return -ENODEV;
0296
0297 for (index = 0; index < priv->freq_count; ++index) {
0298 struct imx8m_ddrc_freq *freq = &priv->freq_table[index];
0299
0300 arm_smccc_smc(IMX_SIP_DDR_DVFS, IMX_SIP_DDR_DVFS_GET_FREQ_INFO,
0301 index, 0, 0, 0, 0, 0, &res);
0302
0303 if ((long)res.a0 <= 0)
0304 return -ENODEV;
0305
0306 freq->rate = res.a0;
0307 freq->smcarg = index;
0308 freq->dram_core_parent_index = res.a1;
0309 freq->dram_alt_parent_index = res.a2;
0310 freq->dram_apb_parent_index = res.a3;
0311
0312
0313 if (freq->dram_core_parent_index != 1 &&
0314 freq->dram_core_parent_index != 2)
0315 return -ENODEV;
0316
0317 if (freq->dram_alt_parent_index > 8 ||
0318 freq->dram_apb_parent_index > 8)
0319 return -ENODEV;
0320
0321 if (freq->dram_core_parent_index == 2 &&
0322 freq->dram_alt_parent_index == 0)
0323 return -ENODEV;
0324 }
0325
0326 return 0;
0327 }
0328
0329 static int imx8m_ddrc_check_opps(struct device *dev)
0330 {
0331 struct imx8m_ddrc *priv = dev_get_drvdata(dev);
0332 struct imx8m_ddrc_freq *freq_info;
0333 struct dev_pm_opp *opp;
0334 unsigned long freq;
0335 int i, opp_count;
0336
0337
0338 opp_count = dev_pm_opp_get_opp_count(dev);
0339 if (opp_count < 0)
0340 return opp_count;
0341 for (i = 0, freq = 0; i < opp_count; ++i, ++freq) {
0342 opp = dev_pm_opp_find_freq_ceil(dev, &freq);
0343 if (IS_ERR(opp)) {
0344 dev_err(dev, "Failed enumerating OPPs: %ld\n",
0345 PTR_ERR(opp));
0346 return PTR_ERR(opp);
0347 }
0348 dev_pm_opp_put(opp);
0349
0350 freq_info = imx8m_ddrc_find_freq(priv, freq);
0351 if (!freq_info) {
0352 dev_info(dev, "Disable unsupported OPP %luHz %luMT/s\n",
0353 freq, DIV_ROUND_CLOSEST(freq, 250000));
0354 dev_pm_opp_disable(dev, freq);
0355 }
0356 }
0357
0358 return 0;
0359 }
0360
0361 static void imx8m_ddrc_exit(struct device *dev)
0362 {
0363 dev_pm_opp_of_remove_table(dev);
0364 }
0365
0366 static int imx8m_ddrc_probe(struct platform_device *pdev)
0367 {
0368 struct device *dev = &pdev->dev;
0369 struct imx8m_ddrc *priv;
0370 const char *gov = DEVFREQ_GOV_USERSPACE;
0371 int ret;
0372
0373 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
0374 if (!priv)
0375 return -ENOMEM;
0376
0377 platform_set_drvdata(pdev, priv);
0378
0379 ret = imx8m_ddrc_init_freq_info(dev);
0380 if (ret) {
0381 dev_err(dev, "failed to init firmware freq info: %d\n", ret);
0382 return ret;
0383 }
0384
0385 priv->dram_core = devm_clk_get(dev, "core");
0386 if (IS_ERR(priv->dram_core)) {
0387 ret = PTR_ERR(priv->dram_core);
0388 dev_err(dev, "failed to fetch core clock: %d\n", ret);
0389 return ret;
0390 }
0391 priv->dram_pll = devm_clk_get(dev, "pll");
0392 if (IS_ERR(priv->dram_pll)) {
0393 ret = PTR_ERR(priv->dram_pll);
0394 dev_err(dev, "failed to fetch pll clock: %d\n", ret);
0395 return ret;
0396 }
0397 priv->dram_alt = devm_clk_get(dev, "alt");
0398 if (IS_ERR(priv->dram_alt)) {
0399 ret = PTR_ERR(priv->dram_alt);
0400 dev_err(dev, "failed to fetch alt clock: %d\n", ret);
0401 return ret;
0402 }
0403 priv->dram_apb = devm_clk_get(dev, "apb");
0404 if (IS_ERR(priv->dram_apb)) {
0405 ret = PTR_ERR(priv->dram_apb);
0406 dev_err(dev, "failed to fetch apb clock: %d\n", ret);
0407 return ret;
0408 }
0409
0410 ret = dev_pm_opp_of_add_table(dev);
0411 if (ret < 0) {
0412 dev_err(dev, "failed to get OPP table\n");
0413 return ret;
0414 }
0415
0416 ret = imx8m_ddrc_check_opps(dev);
0417 if (ret < 0)
0418 goto err;
0419
0420 priv->profile.target = imx8m_ddrc_target;
0421 priv->profile.exit = imx8m_ddrc_exit;
0422 priv->profile.get_cur_freq = imx8m_ddrc_get_cur_freq;
0423 priv->profile.initial_freq = clk_get_rate(priv->dram_core);
0424
0425 priv->devfreq = devm_devfreq_add_device(dev, &priv->profile,
0426 gov, NULL);
0427 if (IS_ERR(priv->devfreq)) {
0428 ret = PTR_ERR(priv->devfreq);
0429 dev_err(dev, "failed to add devfreq device: %d\n", ret);
0430 goto err;
0431 }
0432
0433 return 0;
0434
0435 err:
0436 dev_pm_opp_of_remove_table(dev);
0437 return ret;
0438 }
0439
0440 static const struct of_device_id imx8m_ddrc_of_match[] = {
0441 { .compatible = "fsl,imx8m-ddrc", },
0442 { },
0443 };
0444 MODULE_DEVICE_TABLE(of, imx8m_ddrc_of_match);
0445
0446 static struct platform_driver imx8m_ddrc_platdrv = {
0447 .probe = imx8m_ddrc_probe,
0448 .driver = {
0449 .name = "imx8m-ddrc-devfreq",
0450 .of_match_table = imx8m_ddrc_of_match,
0451 },
0452 };
0453 module_platform_driver(imx8m_ddrc_platdrv);
0454
0455 MODULE_DESCRIPTION("i.MX8M DDR Controller frequency driver");
0456 MODULE_AUTHOR("Leonard Crestez <leonard.crestez@nxp.com>");
0457 MODULE_LICENSE("GPL v2");