bmips-cpufreq.c 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. /*
  2. * CPU frequency scaling for Broadcom BMIPS SoCs
  3. *
  4. * Copyright (c) 2017 Broadcom
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU General Public License as
  8. * published by the Free Software Foundation version 2.
  9. *
  10. * This program is distributed "as is" WITHOUT ANY WARRANTY of any
  11. * kind, whether express or implied; without even the implied warranty
  12. * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. */
  15. #include <linux/cpufreq.h>
  16. #include <linux/module.h>
  17. #include <linux/of_address.h>
  18. #include <linux/slab.h>
  19. /* for mips_hpt_frequency */
  20. #include <asm/time.h>
  21. #define BMIPS_CPUFREQ_PREFIX "bmips"
  22. #define BMIPS_CPUFREQ_NAME BMIPS_CPUFREQ_PREFIX "-cpufreq"
  23. #define TRANSITION_LATENCY (25 * 1000) /* 25 us */
  24. #define BMIPS5_CLK_DIV_SET_SHIFT 0x7
  25. #define BMIPS5_CLK_DIV_SHIFT 0x4
  26. #define BMIPS5_CLK_DIV_MASK 0xf
  27. enum bmips_type {
  28. BMIPS5000,
  29. BMIPS5200,
  30. };
  31. struct cpufreq_compat {
  32. const char *compatible;
  33. unsigned int bmips_type;
  34. unsigned int clk_mult;
  35. unsigned int max_freqs;
  36. };
  37. #define BMIPS(c, t, m, f) { \
  38. .compatible = c, \
  39. .bmips_type = (t), \
  40. .clk_mult = (m), \
  41. .max_freqs = (f), \
  42. }
  43. static struct cpufreq_compat bmips_cpufreq_compat[] = {
  44. BMIPS("brcm,bmips5000", BMIPS5000, 8, 4),
  45. BMIPS("brcm,bmips5200", BMIPS5200, 8, 4),
  46. { }
  47. };
  48. static struct cpufreq_compat *priv;
  49. static int htp_freq_to_cpu_freq(unsigned int clk_mult)
  50. {
  51. return mips_hpt_frequency * clk_mult / 1000;
  52. }
  53. static struct cpufreq_frequency_table *
  54. bmips_cpufreq_get_freq_table(const struct cpufreq_policy *policy)
  55. {
  56. struct cpufreq_frequency_table *table;
  57. unsigned long cpu_freq;
  58. int i;
  59. cpu_freq = htp_freq_to_cpu_freq(priv->clk_mult);
  60. table = kmalloc_array(priv->max_freqs + 1, sizeof(*table), GFP_KERNEL);
  61. if (!table)
  62. return ERR_PTR(-ENOMEM);
  63. for (i = 0; i < priv->max_freqs; i++) {
  64. table[i].frequency = cpu_freq / (1 << i);
  65. table[i].driver_data = i;
  66. }
  67. table[i].frequency = CPUFREQ_TABLE_END;
  68. return table;
  69. }
  70. static unsigned int bmips_cpufreq_get(unsigned int cpu)
  71. {
  72. unsigned int div;
  73. uint32_t mode;
  74. switch (priv->bmips_type) {
  75. case BMIPS5200:
  76. case BMIPS5000:
  77. mode = read_c0_brcm_mode();
  78. div = ((mode >> BMIPS5_CLK_DIV_SHIFT) & BMIPS5_CLK_DIV_MASK);
  79. break;
  80. default:
  81. div = 0;
  82. }
  83. return htp_freq_to_cpu_freq(priv->clk_mult) / (1 << div);
  84. }
  85. static int bmips_cpufreq_target_index(struct cpufreq_policy *policy,
  86. unsigned int index)
  87. {
  88. unsigned int div = policy->freq_table[index].driver_data;
  89. switch (priv->bmips_type) {
  90. case BMIPS5200:
  91. case BMIPS5000:
  92. change_c0_brcm_mode(BMIPS5_CLK_DIV_MASK << BMIPS5_CLK_DIV_SHIFT,
  93. (1 << BMIPS5_CLK_DIV_SET_SHIFT) |
  94. (div << BMIPS5_CLK_DIV_SHIFT));
  95. break;
  96. default:
  97. return -ENOTSUPP;
  98. }
  99. return 0;
  100. }
  101. static int bmips_cpufreq_exit(struct cpufreq_policy *policy)
  102. {
  103. kfree(policy->freq_table);
  104. return 0;
  105. }
  106. static int bmips_cpufreq_init(struct cpufreq_policy *policy)
  107. {
  108. struct cpufreq_frequency_table *freq_table;
  109. freq_table = bmips_cpufreq_get_freq_table(policy);
  110. if (IS_ERR(freq_table)) {
  111. pr_err("%s: couldn't determine frequency table (%ld).\n",
  112. BMIPS_CPUFREQ_NAME, PTR_ERR(freq_table));
  113. return PTR_ERR(freq_table);
  114. }
  115. cpufreq_generic_init(policy, freq_table, TRANSITION_LATENCY);
  116. pr_info("%s: registered\n", BMIPS_CPUFREQ_NAME);
  117. return 0;
  118. }
  119. static struct cpufreq_driver bmips_cpufreq_driver = {
  120. .flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK,
  121. .verify = cpufreq_generic_frequency_table_verify,
  122. .target_index = bmips_cpufreq_target_index,
  123. .get = bmips_cpufreq_get,
  124. .init = bmips_cpufreq_init,
  125. .exit = bmips_cpufreq_exit,
  126. .attr = cpufreq_generic_attr,
  127. .name = BMIPS_CPUFREQ_PREFIX,
  128. };
  129. static int __init bmips_cpufreq_probe(void)
  130. {
  131. struct cpufreq_compat *cc;
  132. struct device_node *np;
  133. for (cc = bmips_cpufreq_compat; cc->compatible; cc++) {
  134. np = of_find_compatible_node(NULL, "cpu", cc->compatible);
  135. if (np) {
  136. of_node_put(np);
  137. priv = cc;
  138. break;
  139. }
  140. }
  141. /* We hit the guard element of the array. No compatible CPU found. */
  142. if (!cc->compatible)
  143. return -ENODEV;
  144. return cpufreq_register_driver(&bmips_cpufreq_driver);
  145. }
  146. device_initcall(bmips_cpufreq_probe);
  147. MODULE_AUTHOR("Markus Mayer <mmayer@broadcom.com>");
  148. MODULE_DESCRIPTION("CPUfreq driver for Broadcom BMIPS SoCs");
  149. MODULE_LICENSE("GPL");