root/drivers/cpufreq/mediatek-cpufreq.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. mtk_cpu_dvfs_info_lookup
  2. mtk_cpufreq_voltage_tracking
  3. mtk_cpufreq_set_voltage
  4. mtk_cpufreq_set_target
  5. mtk_cpu_dvfs_info_init
  6. mtk_cpu_dvfs_info_release
  7. mtk_cpufreq_init
  8. mtk_cpufreq_exit
  9. mtk_cpufreq_probe
  10. mtk_cpufreq_driver_init

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * Copyright (c) 2015 Linaro Ltd.
   4  * Author: Pi-Cheng Chen <pi-cheng.chen@linaro.org>
   5  */
   6 
   7 #include <linux/clk.h>
   8 #include <linux/cpu.h>
   9 #include <linux/cpufreq.h>
  10 #include <linux/cpumask.h>
  11 #include <linux/module.h>
  12 #include <linux/of.h>
  13 #include <linux/platform_device.h>
  14 #include <linux/pm_opp.h>
  15 #include <linux/regulator/consumer.h>
  16 #include <linux/slab.h>
  17 #include <linux/thermal.h>
  18 
  19 #define MIN_VOLT_SHIFT          (100000)
  20 #define MAX_VOLT_SHIFT          (200000)
  21 #define MAX_VOLT_LIMIT          (1150000)
  22 #define VOLT_TOL                (10000)
  23 
  24 /*
  25  * The struct mtk_cpu_dvfs_info holds necessary information for doing CPU DVFS
  26  * on each CPU power/clock domain of Mediatek SoCs. Each CPU cluster in
  27  * Mediatek SoCs has two voltage inputs, Vproc and Vsram. In some cases the two
  28  * voltage inputs need to be controlled under a hardware limitation:
  29  * 100mV < Vsram - Vproc < 200mV
  30  *
  31  * When scaling the clock frequency of a CPU clock domain, the clock source
  32  * needs to be switched to another stable PLL clock temporarily until
  33  * the original PLL becomes stable at target frequency.
  34  */
  35 struct mtk_cpu_dvfs_info {
  36         struct cpumask cpus;
  37         struct device *cpu_dev;
  38         struct regulator *proc_reg;
  39         struct regulator *sram_reg;
  40         struct clk *cpu_clk;
  41         struct clk *inter_clk;
  42         struct list_head list_head;
  43         int intermediate_voltage;
  44         bool need_voltage_tracking;
  45 };
  46 
  47 static LIST_HEAD(dvfs_info_list);
  48 
  49 static struct mtk_cpu_dvfs_info *mtk_cpu_dvfs_info_lookup(int cpu)
  50 {
  51         struct mtk_cpu_dvfs_info *info;
  52 
  53         list_for_each_entry(info, &dvfs_info_list, list_head) {
  54                 if (cpumask_test_cpu(cpu, &info->cpus))
  55                         return info;
  56         }
  57 
  58         return NULL;
  59 }
  60 
  61 static int mtk_cpufreq_voltage_tracking(struct mtk_cpu_dvfs_info *info,
  62                                         int new_vproc)
  63 {
  64         struct regulator *proc_reg = info->proc_reg;
  65         struct regulator *sram_reg = info->sram_reg;
  66         int old_vproc, old_vsram, new_vsram, vsram, vproc, ret;
  67 
  68         old_vproc = regulator_get_voltage(proc_reg);
  69         if (old_vproc < 0) {
  70                 pr_err("%s: invalid Vproc value: %d\n", __func__, old_vproc);
  71                 return old_vproc;
  72         }
  73         /* Vsram should not exceed the maximum allowed voltage of SoC. */
  74         new_vsram = min(new_vproc + MIN_VOLT_SHIFT, MAX_VOLT_LIMIT);
  75 
  76         if (old_vproc < new_vproc) {
  77                 /*
  78                  * When scaling up voltages, Vsram and Vproc scale up step
  79                  * by step. At each step, set Vsram to (Vproc + 200mV) first,
  80                  * then set Vproc to (Vsram - 100mV).
  81                  * Keep doing it until Vsram and Vproc hit target voltages.
  82                  */
  83                 do {
  84                         old_vsram = regulator_get_voltage(sram_reg);
  85                         if (old_vsram < 0) {
  86                                 pr_err("%s: invalid Vsram value: %d\n",
  87                                        __func__, old_vsram);
  88                                 return old_vsram;
  89                         }
  90                         old_vproc = regulator_get_voltage(proc_reg);
  91                         if (old_vproc < 0) {
  92                                 pr_err("%s: invalid Vproc value: %d\n",
  93                                        __func__, old_vproc);
  94                                 return old_vproc;
  95                         }
  96 
  97                         vsram = min(new_vsram, old_vproc + MAX_VOLT_SHIFT);
  98 
  99                         if (vsram + VOLT_TOL >= MAX_VOLT_LIMIT) {
 100                                 vsram = MAX_VOLT_LIMIT;
 101 
 102                                 /*
 103                                  * If the target Vsram hits the maximum voltage,
 104                                  * try to set the exact voltage value first.
 105                                  */
 106                                 ret = regulator_set_voltage(sram_reg, vsram,
 107                                                             vsram);
 108                                 if (ret)
 109                                         ret = regulator_set_voltage(sram_reg,
 110                                                         vsram - VOLT_TOL,
 111                                                         vsram);
 112 
 113                                 vproc = new_vproc;
 114                         } else {
 115                                 ret = regulator_set_voltage(sram_reg, vsram,
 116                                                             vsram + VOLT_TOL);
 117 
 118                                 vproc = vsram - MIN_VOLT_SHIFT;
 119                         }
 120                         if (ret)
 121                                 return ret;
 122 
 123                         ret = regulator_set_voltage(proc_reg, vproc,
 124                                                     vproc + VOLT_TOL);
 125                         if (ret) {
 126                                 regulator_set_voltage(sram_reg, old_vsram,
 127                                                       old_vsram);
 128                                 return ret;
 129                         }
 130                 } while (vproc < new_vproc || vsram < new_vsram);
 131         } else if (old_vproc > new_vproc) {
 132                 /*
 133                  * When scaling down voltages, Vsram and Vproc scale down step
 134                  * by step. At each step, set Vproc to (Vsram - 200mV) first,
 135                  * then set Vproc to (Vproc + 100mV).
 136                  * Keep doing it until Vsram and Vproc hit target voltages.
 137                  */
 138                 do {
 139                         old_vproc = regulator_get_voltage(proc_reg);
 140                         if (old_vproc < 0) {
 141                                 pr_err("%s: invalid Vproc value: %d\n",
 142                                        __func__, old_vproc);
 143                                 return old_vproc;
 144                         }
 145                         old_vsram = regulator_get_voltage(sram_reg);
 146                         if (old_vsram < 0) {
 147                                 pr_err("%s: invalid Vsram value: %d\n",
 148                                        __func__, old_vsram);
 149                                 return old_vsram;
 150                         }
 151 
 152                         vproc = max(new_vproc, old_vsram - MAX_VOLT_SHIFT);
 153                         ret = regulator_set_voltage(proc_reg, vproc,
 154                                                     vproc + VOLT_TOL);
 155                         if (ret)
 156                                 return ret;
 157 
 158                         if (vproc == new_vproc)
 159                                 vsram = new_vsram;
 160                         else
 161                                 vsram = max(new_vsram, vproc + MIN_VOLT_SHIFT);
 162 
 163                         if (vsram + VOLT_TOL >= MAX_VOLT_LIMIT) {
 164                                 vsram = MAX_VOLT_LIMIT;
 165 
 166                                 /*
 167                                  * If the target Vsram hits the maximum voltage,
 168                                  * try to set the exact voltage value first.
 169                                  */
 170                                 ret = regulator_set_voltage(sram_reg, vsram,
 171                                                             vsram);
 172                                 if (ret)
 173                                         ret = regulator_set_voltage(sram_reg,
 174                                                         vsram - VOLT_TOL,
 175                                                         vsram);
 176                         } else {
 177                                 ret = regulator_set_voltage(sram_reg, vsram,
 178                                                             vsram + VOLT_TOL);
 179                         }
 180 
 181                         if (ret) {
 182                                 regulator_set_voltage(proc_reg, old_vproc,
 183                                                       old_vproc);
 184                                 return ret;
 185                         }
 186                 } while (vproc > new_vproc + VOLT_TOL ||
 187                          vsram > new_vsram + VOLT_TOL);
 188         }
 189 
 190         return 0;
 191 }
 192 
 193 static int mtk_cpufreq_set_voltage(struct mtk_cpu_dvfs_info *info, int vproc)
 194 {
 195         if (info->need_voltage_tracking)
 196                 return mtk_cpufreq_voltage_tracking(info, vproc);
 197         else
 198                 return regulator_set_voltage(info->proc_reg, vproc,
 199                                              vproc + VOLT_TOL);
 200 }
 201 
 202 static int mtk_cpufreq_set_target(struct cpufreq_policy *policy,
 203                                   unsigned int index)
 204 {
 205         struct cpufreq_frequency_table *freq_table = policy->freq_table;
 206         struct clk *cpu_clk = policy->clk;
 207         struct clk *armpll = clk_get_parent(cpu_clk);
 208         struct mtk_cpu_dvfs_info *info = policy->driver_data;
 209         struct device *cpu_dev = info->cpu_dev;
 210         struct dev_pm_opp *opp;
 211         long freq_hz, old_freq_hz;
 212         int vproc, old_vproc, inter_vproc, target_vproc, ret;
 213 
 214         inter_vproc = info->intermediate_voltage;
 215 
 216         old_freq_hz = clk_get_rate(cpu_clk);
 217         old_vproc = regulator_get_voltage(info->proc_reg);
 218         if (old_vproc < 0) {
 219                 pr_err("%s: invalid Vproc value: %d\n", __func__, old_vproc);
 220                 return old_vproc;
 221         }
 222 
 223         freq_hz = freq_table[index].frequency * 1000;
 224 
 225         opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_hz);
 226         if (IS_ERR(opp)) {
 227                 pr_err("cpu%d: failed to find OPP for %ld\n",
 228                        policy->cpu, freq_hz);
 229                 return PTR_ERR(opp);
 230         }
 231         vproc = dev_pm_opp_get_voltage(opp);
 232         dev_pm_opp_put(opp);
 233 
 234         /*
 235          * If the new voltage or the intermediate voltage is higher than the
 236          * current voltage, scale up voltage first.
 237          */
 238         target_vproc = (inter_vproc > vproc) ? inter_vproc : vproc;
 239         if (old_vproc < target_vproc) {
 240                 ret = mtk_cpufreq_set_voltage(info, target_vproc);
 241                 if (ret) {
 242                         pr_err("cpu%d: failed to scale up voltage!\n",
 243                                policy->cpu);
 244                         mtk_cpufreq_set_voltage(info, old_vproc);
 245                         return ret;
 246                 }
 247         }
 248 
 249         /* Reparent the CPU clock to intermediate clock. */
 250         ret = clk_set_parent(cpu_clk, info->inter_clk);
 251         if (ret) {
 252                 pr_err("cpu%d: failed to re-parent cpu clock!\n",
 253                        policy->cpu);
 254                 mtk_cpufreq_set_voltage(info, old_vproc);
 255                 WARN_ON(1);
 256                 return ret;
 257         }
 258 
 259         /* Set the original PLL to target rate. */
 260         ret = clk_set_rate(armpll, freq_hz);
 261         if (ret) {
 262                 pr_err("cpu%d: failed to scale cpu clock rate!\n",
 263                        policy->cpu);
 264                 clk_set_parent(cpu_clk, armpll);
 265                 mtk_cpufreq_set_voltage(info, old_vproc);
 266                 return ret;
 267         }
 268 
 269         /* Set parent of CPU clock back to the original PLL. */
 270         ret = clk_set_parent(cpu_clk, armpll);
 271         if (ret) {
 272                 pr_err("cpu%d: failed to re-parent cpu clock!\n",
 273                        policy->cpu);
 274                 mtk_cpufreq_set_voltage(info, inter_vproc);
 275                 WARN_ON(1);
 276                 return ret;
 277         }
 278 
 279         /*
 280          * If the new voltage is lower than the intermediate voltage or the
 281          * original voltage, scale down to the new voltage.
 282          */
 283         if (vproc < inter_vproc || vproc < old_vproc) {
 284                 ret = mtk_cpufreq_set_voltage(info, vproc);
 285                 if (ret) {
 286                         pr_err("cpu%d: failed to scale down voltage!\n",
 287                                policy->cpu);
 288                         clk_set_parent(cpu_clk, info->inter_clk);
 289                         clk_set_rate(armpll, old_freq_hz);
 290                         clk_set_parent(cpu_clk, armpll);
 291                         return ret;
 292                 }
 293         }
 294 
 295         return 0;
 296 }
 297 
 298 #define DYNAMIC_POWER "dynamic-power-coefficient"
 299 
 300 static int mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info *info, int cpu)
 301 {
 302         struct device *cpu_dev;
 303         struct regulator *proc_reg = ERR_PTR(-ENODEV);
 304         struct regulator *sram_reg = ERR_PTR(-ENODEV);
 305         struct clk *cpu_clk = ERR_PTR(-ENODEV);
 306         struct clk *inter_clk = ERR_PTR(-ENODEV);
 307         struct dev_pm_opp *opp;
 308         unsigned long rate;
 309         int ret;
 310 
 311         cpu_dev = get_cpu_device(cpu);
 312         if (!cpu_dev) {
 313                 pr_err("failed to get cpu%d device\n", cpu);
 314                 return -ENODEV;
 315         }
 316 
 317         cpu_clk = clk_get(cpu_dev, "cpu");
 318         if (IS_ERR(cpu_clk)) {
 319                 if (PTR_ERR(cpu_clk) == -EPROBE_DEFER)
 320                         pr_warn("cpu clk for cpu%d not ready, retry.\n", cpu);
 321                 else
 322                         pr_err("failed to get cpu clk for cpu%d\n", cpu);
 323 
 324                 ret = PTR_ERR(cpu_clk);
 325                 return ret;
 326         }
 327 
 328         inter_clk = clk_get(cpu_dev, "intermediate");
 329         if (IS_ERR(inter_clk)) {
 330                 if (PTR_ERR(inter_clk) == -EPROBE_DEFER)
 331                         pr_warn("intermediate clk for cpu%d not ready, retry.\n",
 332                                 cpu);
 333                 else
 334                         pr_err("failed to get intermediate clk for cpu%d\n",
 335                                cpu);
 336 
 337                 ret = PTR_ERR(inter_clk);
 338                 goto out_free_resources;
 339         }
 340 
 341         proc_reg = regulator_get_optional(cpu_dev, "proc");
 342         if (IS_ERR(proc_reg)) {
 343                 if (PTR_ERR(proc_reg) == -EPROBE_DEFER)
 344                         pr_warn("proc regulator for cpu%d not ready, retry.\n",
 345                                 cpu);
 346                 else
 347                         pr_err("failed to get proc regulator for cpu%d\n",
 348                                cpu);
 349 
 350                 ret = PTR_ERR(proc_reg);
 351                 goto out_free_resources;
 352         }
 353 
 354         /* Both presence and absence of sram regulator are valid cases. */
 355         sram_reg = regulator_get_exclusive(cpu_dev, "sram");
 356 
 357         /* Get OPP-sharing information from "operating-points-v2" bindings */
 358         ret = dev_pm_opp_of_get_sharing_cpus(cpu_dev, &info->cpus);
 359         if (ret) {
 360                 pr_err("failed to get OPP-sharing information for cpu%d\n",
 361                        cpu);
 362                 goto out_free_resources;
 363         }
 364 
 365         ret = dev_pm_opp_of_cpumask_add_table(&info->cpus);
 366         if (ret) {
 367                 pr_warn("no OPP table for cpu%d\n", cpu);
 368                 goto out_free_resources;
 369         }
 370 
 371         /* Search a safe voltage for intermediate frequency. */
 372         rate = clk_get_rate(inter_clk);
 373         opp = dev_pm_opp_find_freq_ceil(cpu_dev, &rate);
 374         if (IS_ERR(opp)) {
 375                 pr_err("failed to get intermediate opp for cpu%d\n", cpu);
 376                 ret = PTR_ERR(opp);
 377                 goto out_free_opp_table;
 378         }
 379         info->intermediate_voltage = dev_pm_opp_get_voltage(opp);
 380         dev_pm_opp_put(opp);
 381 
 382         info->cpu_dev = cpu_dev;
 383         info->proc_reg = proc_reg;
 384         info->sram_reg = IS_ERR(sram_reg) ? NULL : sram_reg;
 385         info->cpu_clk = cpu_clk;
 386         info->inter_clk = inter_clk;
 387 
 388         /*
 389          * If SRAM regulator is present, software "voltage tracking" is needed
 390          * for this CPU power domain.
 391          */
 392         info->need_voltage_tracking = !IS_ERR(sram_reg);
 393 
 394         return 0;
 395 
 396 out_free_opp_table:
 397         dev_pm_opp_of_cpumask_remove_table(&info->cpus);
 398 
 399 out_free_resources:
 400         if (!IS_ERR(proc_reg))
 401                 regulator_put(proc_reg);
 402         if (!IS_ERR(sram_reg))
 403                 regulator_put(sram_reg);
 404         if (!IS_ERR(cpu_clk))
 405                 clk_put(cpu_clk);
 406         if (!IS_ERR(inter_clk))
 407                 clk_put(inter_clk);
 408 
 409         return ret;
 410 }
 411 
 412 static void mtk_cpu_dvfs_info_release(struct mtk_cpu_dvfs_info *info)
 413 {
 414         if (!IS_ERR(info->proc_reg))
 415                 regulator_put(info->proc_reg);
 416         if (!IS_ERR(info->sram_reg))
 417                 regulator_put(info->sram_reg);
 418         if (!IS_ERR(info->cpu_clk))
 419                 clk_put(info->cpu_clk);
 420         if (!IS_ERR(info->inter_clk))
 421                 clk_put(info->inter_clk);
 422 
 423         dev_pm_opp_of_cpumask_remove_table(&info->cpus);
 424 }
 425 
 426 static int mtk_cpufreq_init(struct cpufreq_policy *policy)
 427 {
 428         struct mtk_cpu_dvfs_info *info;
 429         struct cpufreq_frequency_table *freq_table;
 430         int ret;
 431 
 432         info = mtk_cpu_dvfs_info_lookup(policy->cpu);
 433         if (!info) {
 434                 pr_err("dvfs info for cpu%d is not initialized.\n",
 435                        policy->cpu);
 436                 return -EINVAL;
 437         }
 438 
 439         ret = dev_pm_opp_init_cpufreq_table(info->cpu_dev, &freq_table);
 440         if (ret) {
 441                 pr_err("failed to init cpufreq table for cpu%d: %d\n",
 442                        policy->cpu, ret);
 443                 return ret;
 444         }
 445 
 446         cpumask_copy(policy->cpus, &info->cpus);
 447         policy->freq_table = freq_table;
 448         policy->driver_data = info;
 449         policy->clk = info->cpu_clk;
 450 
 451         dev_pm_opp_of_register_em(policy->cpus);
 452 
 453         return 0;
 454 }
 455 
 456 static int mtk_cpufreq_exit(struct cpufreq_policy *policy)
 457 {
 458         struct mtk_cpu_dvfs_info *info = policy->driver_data;
 459 
 460         dev_pm_opp_free_cpufreq_table(info->cpu_dev, &policy->freq_table);
 461 
 462         return 0;
 463 }
 464 
 465 static struct cpufreq_driver mtk_cpufreq_driver = {
 466         .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK |
 467                  CPUFREQ_HAVE_GOVERNOR_PER_POLICY |
 468                  CPUFREQ_IS_COOLING_DEV,
 469         .verify = cpufreq_generic_frequency_table_verify,
 470         .target_index = mtk_cpufreq_set_target,
 471         .get = cpufreq_generic_get,
 472         .init = mtk_cpufreq_init,
 473         .exit = mtk_cpufreq_exit,
 474         .name = "mtk-cpufreq",
 475         .attr = cpufreq_generic_attr,
 476 };
 477 
 478 static int mtk_cpufreq_probe(struct platform_device *pdev)
 479 {
 480         struct mtk_cpu_dvfs_info *info, *tmp;
 481         int cpu, ret;
 482 
 483         for_each_possible_cpu(cpu) {
 484                 info = mtk_cpu_dvfs_info_lookup(cpu);
 485                 if (info)
 486                         continue;
 487 
 488                 info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
 489                 if (!info) {
 490                         ret = -ENOMEM;
 491                         goto release_dvfs_info_list;
 492                 }
 493 
 494                 ret = mtk_cpu_dvfs_info_init(info, cpu);
 495                 if (ret) {
 496                         dev_err(&pdev->dev,
 497                                 "failed to initialize dvfs info for cpu%d\n",
 498                                 cpu);
 499                         goto release_dvfs_info_list;
 500                 }
 501 
 502                 list_add(&info->list_head, &dvfs_info_list);
 503         }
 504 
 505         ret = cpufreq_register_driver(&mtk_cpufreq_driver);
 506         if (ret) {
 507                 dev_err(&pdev->dev, "failed to register mtk cpufreq driver\n");
 508                 goto release_dvfs_info_list;
 509         }
 510 
 511         return 0;
 512 
 513 release_dvfs_info_list:
 514         list_for_each_entry_safe(info, tmp, &dvfs_info_list, list_head) {
 515                 mtk_cpu_dvfs_info_release(info);
 516                 list_del(&info->list_head);
 517         }
 518 
 519         return ret;
 520 }
 521 
 522 static struct platform_driver mtk_cpufreq_platdrv = {
 523         .driver = {
 524                 .name   = "mtk-cpufreq",
 525         },
 526         .probe          = mtk_cpufreq_probe,
 527 };
 528 
 529 /* List of machines supported by this driver */
 530 static const struct of_device_id mtk_cpufreq_machines[] __initconst = {
 531         { .compatible = "mediatek,mt2701", },
 532         { .compatible = "mediatek,mt2712", },
 533         { .compatible = "mediatek,mt7622", },
 534         { .compatible = "mediatek,mt7623", },
 535         { .compatible = "mediatek,mt817x", },
 536         { .compatible = "mediatek,mt8173", },
 537         { .compatible = "mediatek,mt8176", },
 538         { .compatible = "mediatek,mt8183", },
 539         { .compatible = "mediatek,mt8516", },
 540 
 541         { }
 542 };
 543 
 544 static int __init mtk_cpufreq_driver_init(void)
 545 {
 546         struct device_node *np;
 547         const struct of_device_id *match;
 548         struct platform_device *pdev;
 549         int err;
 550 
 551         np = of_find_node_by_path("/");
 552         if (!np)
 553                 return -ENODEV;
 554 
 555         match = of_match_node(mtk_cpufreq_machines, np);
 556         of_node_put(np);
 557         if (!match) {
 558                 pr_debug("Machine is not compatible with mtk-cpufreq\n");
 559                 return -ENODEV;
 560         }
 561 
 562         err = platform_driver_register(&mtk_cpufreq_platdrv);
 563         if (err)
 564                 return err;
 565 
 566         /*
 567          * Since there's no place to hold device registration code and no
 568          * device tree based way to match cpufreq driver yet, both the driver
 569          * and the device registration codes are put here to handle defer
 570          * probing.
 571          */
 572         pdev = platform_device_register_simple("mtk-cpufreq", -1, NULL, 0);
 573         if (IS_ERR(pdev)) {
 574                 pr_err("failed to register mtk-cpufreq platform device\n");
 575                 return PTR_ERR(pdev);
 576         }
 577 
 578         return 0;
 579 }
 580 device_initcall(mtk_cpufreq_driver_init);
 581 
 582 MODULE_DESCRIPTION("MediaTek CPUFreq driver");
 583 MODULE_AUTHOR("Pi-Cheng Chen <pi-cheng.chen@linaro.org>");
 584 MODULE_LICENSE("GPL v2");

/* [<][>][^][v][top][bottom][index][help] */