tegra124-cpufreq.c 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Tegra 124 cpufreq driver
  4. */
  5. #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  6. #include <linux/clk.h>
  7. #include <linux/cpufreq.h>
  8. #include <linux/err.h>
  9. #include <linux/init.h>
  10. #include <linux/kernel.h>
  11. #include <linux/module.h>
  12. #include <linux/of_device.h>
  13. #include <linux/of.h>
  14. #include <linux/platform_device.h>
  15. #include <linux/pm_opp.h>
  16. #include <linux/types.h>
  17. struct tegra124_cpufreq_priv {
  18. struct clk *cpu_clk;
  19. struct clk *pllp_clk;
  20. struct clk *pllx_clk;
  21. struct clk *dfll_clk;
  22. struct platform_device *cpufreq_dt_pdev;
  23. };
  24. static int tegra124_cpu_switch_to_dfll(struct tegra124_cpufreq_priv *priv)
  25. {
  26. struct clk *orig_parent;
  27. int ret;
  28. ret = clk_set_rate(priv->dfll_clk, clk_get_rate(priv->cpu_clk));
  29. if (ret)
  30. return ret;
  31. orig_parent = clk_get_parent(priv->cpu_clk);
  32. clk_set_parent(priv->cpu_clk, priv->pllp_clk);
  33. ret = clk_prepare_enable(priv->dfll_clk);
  34. if (ret)
  35. goto out;
  36. clk_set_parent(priv->cpu_clk, priv->dfll_clk);
  37. return 0;
  38. out:
  39. clk_set_parent(priv->cpu_clk, orig_parent);
  40. return ret;
  41. }
  42. static int tegra124_cpufreq_probe(struct platform_device *pdev)
  43. {
  44. struct tegra124_cpufreq_priv *priv;
  45. struct device_node *np;
  46. struct device *cpu_dev;
  47. struct platform_device_info cpufreq_dt_devinfo = {};
  48. int ret;
  49. priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
  50. if (!priv)
  51. return -ENOMEM;
  52. cpu_dev = get_cpu_device(0);
  53. if (!cpu_dev)
  54. return -ENODEV;
  55. np = of_cpu_device_node_get(0);
  56. if (!np)
  57. return -ENODEV;
  58. priv->cpu_clk = of_clk_get_by_name(np, "cpu_g");
  59. if (IS_ERR(priv->cpu_clk)) {
  60. ret = PTR_ERR(priv->cpu_clk);
  61. goto out_put_np;
  62. }
  63. priv->dfll_clk = of_clk_get_by_name(np, "dfll");
  64. if (IS_ERR(priv->dfll_clk)) {
  65. ret = PTR_ERR(priv->dfll_clk);
  66. goto out_put_cpu_clk;
  67. }
  68. priv->pllx_clk = of_clk_get_by_name(np, "pll_x");
  69. if (IS_ERR(priv->pllx_clk)) {
  70. ret = PTR_ERR(priv->pllx_clk);
  71. goto out_put_dfll_clk;
  72. }
  73. priv->pllp_clk = of_clk_get_by_name(np, "pll_p");
  74. if (IS_ERR(priv->pllp_clk)) {
  75. ret = PTR_ERR(priv->pllp_clk);
  76. goto out_put_pllx_clk;
  77. }
  78. ret = tegra124_cpu_switch_to_dfll(priv);
  79. if (ret)
  80. goto out_put_pllp_clk;
  81. cpufreq_dt_devinfo.name = "cpufreq-dt";
  82. cpufreq_dt_devinfo.parent = &pdev->dev;
  83. priv->cpufreq_dt_pdev =
  84. platform_device_register_full(&cpufreq_dt_devinfo);
  85. if (IS_ERR(priv->cpufreq_dt_pdev)) {
  86. ret = PTR_ERR(priv->cpufreq_dt_pdev);
  87. goto out_put_pllp_clk;
  88. }
  89. platform_set_drvdata(pdev, priv);
  90. of_node_put(np);
  91. return 0;
  92. out_put_pllp_clk:
  93. clk_put(priv->pllp_clk);
  94. out_put_pllx_clk:
  95. clk_put(priv->pllx_clk);
  96. out_put_dfll_clk:
  97. clk_put(priv->dfll_clk);
  98. out_put_cpu_clk:
  99. clk_put(priv->cpu_clk);
  100. out_put_np:
  101. of_node_put(np);
  102. return ret;
  103. }
  104. static int __maybe_unused tegra124_cpufreq_suspend(struct device *dev)
  105. {
  106. struct tegra124_cpufreq_priv *priv = dev_get_drvdata(dev);
  107. int err;
  108. /*
  109. * PLLP rate 408Mhz is below the CPU Fmax at Vmin and is safe to
  110. * use during suspend and resume. So, switch the CPU clock source
  111. * to PLLP and disable DFLL.
  112. */
  113. err = clk_set_parent(priv->cpu_clk, priv->pllp_clk);
  114. if (err < 0) {
  115. dev_err(dev, "failed to reparent to PLLP: %d\n", err);
  116. return err;
  117. }
  118. clk_disable_unprepare(priv->dfll_clk);
  119. return 0;
  120. }
  121. static int __maybe_unused tegra124_cpufreq_resume(struct device *dev)
  122. {
  123. struct tegra124_cpufreq_priv *priv = dev_get_drvdata(dev);
  124. int err;
  125. /*
  126. * Warmboot code powers up the CPU with PLLP clock source.
  127. * Enable DFLL clock and switch CPU clock source back to DFLL.
  128. */
  129. err = clk_prepare_enable(priv->dfll_clk);
  130. if (err < 0) {
  131. dev_err(dev, "failed to enable DFLL clock for CPU: %d\n", err);
  132. goto disable_cpufreq;
  133. }
  134. err = clk_set_parent(priv->cpu_clk, priv->dfll_clk);
  135. if (err < 0) {
  136. dev_err(dev, "failed to reparent to DFLL clock: %d\n", err);
  137. goto disable_dfll;
  138. }
  139. return 0;
  140. disable_dfll:
  141. clk_disable_unprepare(priv->dfll_clk);
  142. disable_cpufreq:
  143. disable_cpufreq();
  144. return err;
  145. }
  146. static const struct dev_pm_ops tegra124_cpufreq_pm_ops = {
  147. SET_SYSTEM_SLEEP_PM_OPS(tegra124_cpufreq_suspend,
  148. tegra124_cpufreq_resume)
  149. };
  150. static struct platform_driver tegra124_cpufreq_platdrv = {
  151. .driver.name = "cpufreq-tegra124",
  152. .driver.pm = &tegra124_cpufreq_pm_ops,
  153. .probe = tegra124_cpufreq_probe,
  154. };
  155. static int __init tegra_cpufreq_init(void)
  156. {
  157. int ret;
  158. struct platform_device *pdev;
  159. if (!(of_machine_is_compatible("nvidia,tegra124") ||
  160. of_machine_is_compatible("nvidia,tegra210")))
  161. return -ENODEV;
  162. /*
  163. * Platform driver+device required for handling EPROBE_DEFER with
  164. * the regulator and the DFLL clock
  165. */
  166. ret = platform_driver_register(&tegra124_cpufreq_platdrv);
  167. if (ret)
  168. return ret;
  169. pdev = platform_device_register_simple("cpufreq-tegra124", -1, NULL, 0);
  170. if (IS_ERR(pdev)) {
  171. platform_driver_unregister(&tegra124_cpufreq_platdrv);
  172. return PTR_ERR(pdev);
  173. }
  174. return 0;
  175. }
  176. module_init(tegra_cpufreq_init);
  177. MODULE_AUTHOR("Tuomas Tynkkynen <ttynkkynen@nvidia.com>");
  178. MODULE_DESCRIPTION("cpufreq driver for NVIDIA Tegra124");
  179. MODULE_LICENSE("GPL v2");