davinci-cpufreq.c 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * CPU frequency scaling for DaVinci
  4. *
  5. * Copyright (C) 2009 Texas Instruments Incorporated - https://www.ti.com/
  6. *
  7. * Based on linux/arch/arm/plat-omap/cpu-omap.c. Original Copyright follows:
  8. *
  9. * Copyright (C) 2005 Nokia Corporation
  10. * Written by Tony Lindgren <tony@atomide.com>
  11. *
  12. * Based on cpu-sa1110.c, Copyright (C) 2001 Russell King
  13. *
  14. * Copyright (C) 2007-2008 Texas Instruments, Inc.
  15. * Updated to support OMAP3
  16. * Rajendra Nayak <rnayak@ti.com>
  17. */
  18. #include <linux/types.h>
  19. #include <linux/cpufreq.h>
  20. #include <linux/init.h>
  21. #include <linux/err.h>
  22. #include <linux/clk.h>
  23. #include <linux/platform_data/davinci-cpufreq.h>
  24. #include <linux/platform_device.h>
  25. #include <linux/export.h>
  26. struct davinci_cpufreq {
  27. struct device *dev;
  28. struct clk *armclk;
  29. struct clk *asyncclk;
  30. unsigned long asyncrate;
  31. };
  32. static struct davinci_cpufreq cpufreq;
  33. static int davinci_target(struct cpufreq_policy *policy, unsigned int idx)
  34. {
  35. struct davinci_cpufreq_config *pdata = cpufreq.dev->platform_data;
  36. struct clk *armclk = cpufreq.armclk;
  37. unsigned int old_freq, new_freq;
  38. int ret = 0;
  39. old_freq = policy->cur;
  40. new_freq = pdata->freq_table[idx].frequency;
  41. /* if moving to higher frequency, up the voltage beforehand */
  42. if (pdata->set_voltage && new_freq > old_freq) {
  43. ret = pdata->set_voltage(idx);
  44. if (ret)
  45. return ret;
  46. }
  47. ret = clk_set_rate(armclk, new_freq * 1000);
  48. if (ret)
  49. return ret;
  50. if (cpufreq.asyncclk) {
  51. ret = clk_set_rate(cpufreq.asyncclk, cpufreq.asyncrate);
  52. if (ret)
  53. return ret;
  54. }
  55. /* if moving to lower freq, lower the voltage after lowering freq */
  56. if (pdata->set_voltage && new_freq < old_freq)
  57. pdata->set_voltage(idx);
  58. return 0;
  59. }
  60. static int davinci_cpu_init(struct cpufreq_policy *policy)
  61. {
  62. int result = 0;
  63. struct davinci_cpufreq_config *pdata = cpufreq.dev->platform_data;
  64. struct cpufreq_frequency_table *freq_table = pdata->freq_table;
  65. if (policy->cpu != 0)
  66. return -EINVAL;
  67. /* Finish platform specific initialization */
  68. if (pdata->init) {
  69. result = pdata->init();
  70. if (result)
  71. return result;
  72. }
  73. policy->clk = cpufreq.armclk;
  74. /*
  75. * Time measurement across the target() function yields ~1500-1800us
  76. * time taken with no drivers on notification list.
  77. * Setting the latency to 2000 us to accommodate addition of drivers
  78. * to pre/post change notification list.
  79. */
  80. cpufreq_generic_init(policy, freq_table, 2000 * 1000);
  81. return 0;
  82. }
  83. static struct cpufreq_driver davinci_driver = {
  84. .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK,
  85. .verify = cpufreq_generic_frequency_table_verify,
  86. .target_index = davinci_target,
  87. .get = cpufreq_generic_get,
  88. .init = davinci_cpu_init,
  89. .name = "davinci",
  90. .attr = cpufreq_generic_attr,
  91. };
  92. static int __init davinci_cpufreq_probe(struct platform_device *pdev)
  93. {
  94. struct davinci_cpufreq_config *pdata = pdev->dev.platform_data;
  95. struct clk *asyncclk;
  96. if (!pdata)
  97. return -EINVAL;
  98. if (!pdata->freq_table)
  99. return -EINVAL;
  100. cpufreq.dev = &pdev->dev;
  101. cpufreq.armclk = clk_get(NULL, "arm");
  102. if (IS_ERR(cpufreq.armclk)) {
  103. dev_err(cpufreq.dev, "Unable to get ARM clock\n");
  104. return PTR_ERR(cpufreq.armclk);
  105. }
  106. asyncclk = clk_get(cpufreq.dev, "async");
  107. if (!IS_ERR(asyncclk)) {
  108. cpufreq.asyncclk = asyncclk;
  109. cpufreq.asyncrate = clk_get_rate(asyncclk);
  110. }
  111. return cpufreq_register_driver(&davinci_driver);
  112. }
  113. static int __exit davinci_cpufreq_remove(struct platform_device *pdev)
  114. {
  115. clk_put(cpufreq.armclk);
  116. if (cpufreq.asyncclk)
  117. clk_put(cpufreq.asyncclk);
  118. return cpufreq_unregister_driver(&davinci_driver);
  119. }
  120. static struct platform_driver davinci_cpufreq_driver = {
  121. .driver = {
  122. .name = "cpufreq-davinci",
  123. },
  124. .remove = __exit_p(davinci_cpufreq_remove),
  125. };
  126. int __init davinci_cpufreq_init(void)
  127. {
  128. return platform_driver_probe(&davinci_cpufreq_driver,
  129. davinci_cpufreq_probe);
  130. }