tegra124-cpufreq.c 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. /*
  2. * Tegra 124 cpufreq driver
  3. *
  4. * This software is licensed under the terms of the GNU General Public
  5. * License version 2, as published by the Free Software Foundation, and
  6. * may be copied, distributed, and modified under those terms.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. */
  13. #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  14. #include <linux/clk.h>
  15. #include <linux/err.h>
  16. #include <linux/init.h>
  17. #include <linux/kernel.h>
  18. #include <linux/module.h>
  19. #include <linux/of_device.h>
  20. #include <linux/of.h>
  21. #include <linux/platform_device.h>
  22. #include <linux/pm_opp.h>
  23. #include <linux/regulator/consumer.h>
  24. #include <linux/types.h>
  25. struct tegra124_cpufreq_priv {
  26. struct regulator *vdd_cpu_reg;
  27. struct clk *cpu_clk;
  28. struct clk *pllp_clk;
  29. struct clk *pllx_clk;
  30. struct clk *dfll_clk;
  31. struct platform_device *cpufreq_dt_pdev;
  32. };
  33. static int tegra124_cpu_switch_to_dfll(struct tegra124_cpufreq_priv *priv)
  34. {
  35. struct clk *orig_parent;
  36. int ret;
  37. ret = clk_set_rate(priv->dfll_clk, clk_get_rate(priv->cpu_clk));
  38. if (ret)
  39. return ret;
  40. orig_parent = clk_get_parent(priv->cpu_clk);
  41. clk_set_parent(priv->cpu_clk, priv->pllp_clk);
  42. ret = clk_prepare_enable(priv->dfll_clk);
  43. if (ret)
  44. goto out;
  45. clk_set_parent(priv->cpu_clk, priv->dfll_clk);
  46. return 0;
  47. out:
  48. clk_set_parent(priv->cpu_clk, orig_parent);
  49. return ret;
  50. }
  51. static void tegra124_cpu_switch_to_pllx(struct tegra124_cpufreq_priv *priv)
  52. {
  53. clk_set_parent(priv->cpu_clk, priv->pllp_clk);
  54. clk_disable_unprepare(priv->dfll_clk);
  55. regulator_sync_voltage(priv->vdd_cpu_reg);
  56. clk_set_parent(priv->cpu_clk, priv->pllx_clk);
  57. }
  58. static int tegra124_cpufreq_probe(struct platform_device *pdev)
  59. {
  60. struct tegra124_cpufreq_priv *priv;
  61. struct device_node *np;
  62. struct device *cpu_dev;
  63. struct platform_device_info cpufreq_dt_devinfo = {};
  64. int ret;
  65. priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
  66. if (!priv)
  67. return -ENOMEM;
  68. cpu_dev = get_cpu_device(0);
  69. if (!cpu_dev)
  70. return -ENODEV;
  71. np = of_cpu_device_node_get(0);
  72. if (!np)
  73. return -ENODEV;
  74. priv->vdd_cpu_reg = regulator_get(cpu_dev, "vdd-cpu");
  75. if (IS_ERR(priv->vdd_cpu_reg)) {
  76. ret = PTR_ERR(priv->vdd_cpu_reg);
  77. goto out_put_np;
  78. }
  79. priv->cpu_clk = of_clk_get_by_name(np, "cpu_g");
  80. if (IS_ERR(priv->cpu_clk)) {
  81. ret = PTR_ERR(priv->cpu_clk);
  82. goto out_put_vdd_cpu_reg;
  83. }
  84. priv->dfll_clk = of_clk_get_by_name(np, "dfll");
  85. if (IS_ERR(priv->dfll_clk)) {
  86. ret = PTR_ERR(priv->dfll_clk);
  87. goto out_put_cpu_clk;
  88. }
  89. priv->pllx_clk = of_clk_get_by_name(np, "pll_x");
  90. if (IS_ERR(priv->pllx_clk)) {
  91. ret = PTR_ERR(priv->pllx_clk);
  92. goto out_put_dfll_clk;
  93. }
  94. priv->pllp_clk = of_clk_get_by_name(np, "pll_p");
  95. if (IS_ERR(priv->pllp_clk)) {
  96. ret = PTR_ERR(priv->pllp_clk);
  97. goto out_put_pllx_clk;
  98. }
  99. ret = tegra124_cpu_switch_to_dfll(priv);
  100. if (ret)
  101. goto out_put_pllp_clk;
  102. cpufreq_dt_devinfo.name = "cpufreq-dt";
  103. cpufreq_dt_devinfo.parent = &pdev->dev;
  104. priv->cpufreq_dt_pdev =
  105. platform_device_register_full(&cpufreq_dt_devinfo);
  106. if (IS_ERR(priv->cpufreq_dt_pdev)) {
  107. ret = PTR_ERR(priv->cpufreq_dt_pdev);
  108. goto out_switch_to_pllx;
  109. }
  110. platform_set_drvdata(pdev, priv);
  111. return 0;
  112. out_switch_to_pllx:
  113. tegra124_cpu_switch_to_pllx(priv);
  114. out_put_pllp_clk:
  115. clk_put(priv->pllp_clk);
  116. out_put_pllx_clk:
  117. clk_put(priv->pllx_clk);
  118. out_put_dfll_clk:
  119. clk_put(priv->dfll_clk);
  120. out_put_cpu_clk:
  121. clk_put(priv->cpu_clk);
  122. out_put_vdd_cpu_reg:
  123. regulator_put(priv->vdd_cpu_reg);
  124. out_put_np:
  125. of_node_put(np);
  126. return ret;
  127. }
  128. static int tegra124_cpufreq_remove(struct platform_device *pdev)
  129. {
  130. struct tegra124_cpufreq_priv *priv = platform_get_drvdata(pdev);
  131. platform_device_unregister(priv->cpufreq_dt_pdev);
  132. tegra124_cpu_switch_to_pllx(priv);
  133. clk_put(priv->pllp_clk);
  134. clk_put(priv->pllx_clk);
  135. clk_put(priv->dfll_clk);
  136. clk_put(priv->cpu_clk);
  137. regulator_put(priv->vdd_cpu_reg);
  138. return 0;
  139. }
  140. static struct platform_driver tegra124_cpufreq_platdrv = {
  141. .driver.name = "cpufreq-tegra124",
  142. .probe = tegra124_cpufreq_probe,
  143. .remove = tegra124_cpufreq_remove,
  144. };
  145. static int __init tegra_cpufreq_init(void)
  146. {
  147. int ret;
  148. struct platform_device *pdev;
  149. if (!of_machine_is_compatible("nvidia,tegra124"))
  150. return -ENODEV;
  151. /*
  152. * Platform driver+device required for handling EPROBE_DEFER with
  153. * the regulator and the DFLL clock
  154. */
  155. ret = platform_driver_register(&tegra124_cpufreq_platdrv);
  156. if (ret)
  157. return ret;
  158. pdev = platform_device_register_simple("cpufreq-tegra124", -1, NULL, 0);
  159. if (IS_ERR(pdev)) {
  160. platform_driver_unregister(&tegra124_cpufreq_platdrv);
  161. return PTR_ERR(pdev);
  162. }
  163. return 0;
  164. }
  165. module_init(tegra_cpufreq_init);
  166. MODULE_AUTHOR("Tuomas Tynkkynen <ttynkkynen@nvidia.com>");
  167. MODULE_DESCRIPTION("cpufreq driver for NVIDIA Tegra124");
  168. MODULE_LICENSE("GPL v2");