sparc-us3-cpufreq.c 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. /* us3_cpufreq.c: UltraSPARC-III cpu frequency support
  2. *
  3. * Copyright (C) 2003 David S. Miller (davem@redhat.com)
  4. *
  5. * Many thanks to Dominik Brodowski for fixing up the cpufreq
  6. * infrastructure in order to make this driver easier to implement.
  7. */
  8. #include <linux/kernel.h>
  9. #include <linux/module.h>
  10. #include <linux/sched.h>
  11. #include <linux/smp.h>
  12. #include <linux/cpufreq.h>
  13. #include <linux/threads.h>
  14. #include <linux/slab.h>
  15. #include <linux/init.h>
  16. #include <asm/head.h>
  17. #include <asm/timer.h>
  18. static struct cpufreq_driver *cpufreq_us3_driver;
  19. struct us3_freq_percpu_info {
  20. struct cpufreq_frequency_table table[4];
  21. };
  22. /* Indexed by cpu number. */
  23. static struct us3_freq_percpu_info *us3_freq_table;
  24. /* UltraSPARC-III has three dividers: 1, 2, and 32. These are controlled
  25. * in the Safari config register.
  26. */
  27. #define SAFARI_CFG_DIV_1 0x0000000000000000UL
  28. #define SAFARI_CFG_DIV_2 0x0000000040000000UL
  29. #define SAFARI_CFG_DIV_32 0x0000000080000000UL
  30. #define SAFARI_CFG_DIV_MASK 0x00000000C0000000UL
  31. static void read_safari_cfg(void *arg)
  32. {
  33. unsigned long ret, *val = arg;
  34. __asm__ __volatile__("ldxa [%%g0] %1, %0"
  35. : "=&r" (ret)
  36. : "i" (ASI_SAFARI_CONFIG));
  37. *val = ret;
  38. }
  39. static void update_safari_cfg(void *arg)
  40. {
  41. unsigned long reg, *new_bits = arg;
  42. read_safari_cfg(&reg);
  43. reg &= ~SAFARI_CFG_DIV_MASK;
  44. reg |= *new_bits;
  45. __asm__ __volatile__("stxa %0, [%%g0] %1\n\t"
  46. "membar #Sync"
  47. : /* no outputs */
  48. : "r" (reg), "i" (ASI_SAFARI_CONFIG)
  49. : "memory");
  50. }
  51. static unsigned long get_current_freq(unsigned int cpu, unsigned long safari_cfg)
  52. {
  53. unsigned long clock_tick = sparc64_get_clock_tick(cpu) / 1000;
  54. unsigned long ret;
  55. switch (safari_cfg & SAFARI_CFG_DIV_MASK) {
  56. case SAFARI_CFG_DIV_1:
  57. ret = clock_tick / 1;
  58. break;
  59. case SAFARI_CFG_DIV_2:
  60. ret = clock_tick / 2;
  61. break;
  62. case SAFARI_CFG_DIV_32:
  63. ret = clock_tick / 32;
  64. break;
  65. default:
  66. BUG();
  67. }
  68. return ret;
  69. }
  70. static unsigned int us3_freq_get(unsigned int cpu)
  71. {
  72. unsigned long reg;
  73. if (smp_call_function_single(cpu, read_safari_cfg, &reg, 1))
  74. return 0;
  75. return get_current_freq(cpu, reg);
  76. }
  77. static int us3_freq_target(struct cpufreq_policy *policy, unsigned int index)
  78. {
  79. unsigned int cpu = policy->cpu;
  80. unsigned long new_bits, new_freq;
  81. new_freq = sparc64_get_clock_tick(cpu) / 1000;
  82. switch (index) {
  83. case 0:
  84. new_bits = SAFARI_CFG_DIV_1;
  85. new_freq /= 1;
  86. break;
  87. case 1:
  88. new_bits = SAFARI_CFG_DIV_2;
  89. new_freq /= 2;
  90. break;
  91. case 2:
  92. new_bits = SAFARI_CFG_DIV_32;
  93. new_freq /= 32;
  94. break;
  95. default:
  96. BUG();
  97. }
  98. return smp_call_function_single(cpu, update_safari_cfg, &new_bits, 1);
  99. }
  100. static int __init us3_freq_cpu_init(struct cpufreq_policy *policy)
  101. {
  102. unsigned int cpu = policy->cpu;
  103. unsigned long clock_tick = sparc64_get_clock_tick(cpu) / 1000;
  104. struct cpufreq_frequency_table *table =
  105. &us3_freq_table[cpu].table[0];
  106. table[0].driver_data = 0;
  107. table[0].frequency = clock_tick / 1;
  108. table[1].driver_data = 1;
  109. table[1].frequency = clock_tick / 2;
  110. table[2].driver_data = 2;
  111. table[2].frequency = clock_tick / 32;
  112. table[3].driver_data = 0;
  113. table[3].frequency = CPUFREQ_TABLE_END;
  114. policy->cpuinfo.transition_latency = 0;
  115. policy->cur = clock_tick;
  116. policy->freq_table = table;
  117. return 0;
  118. }
  119. static int us3_freq_cpu_exit(struct cpufreq_policy *policy)
  120. {
  121. if (cpufreq_us3_driver)
  122. us3_freq_target(policy, 0);
  123. return 0;
  124. }
  125. static int __init us3_freq_init(void)
  126. {
  127. unsigned long manuf, impl, ver;
  128. int ret;
  129. if (tlb_type != cheetah && tlb_type != cheetah_plus)
  130. return -ENODEV;
  131. __asm__("rdpr %%ver, %0" : "=r" (ver));
  132. manuf = ((ver >> 48) & 0xffff);
  133. impl = ((ver >> 32) & 0xffff);
  134. if (manuf == CHEETAH_MANUF &&
  135. (impl == CHEETAH_IMPL ||
  136. impl == CHEETAH_PLUS_IMPL ||
  137. impl == JAGUAR_IMPL ||
  138. impl == PANTHER_IMPL)) {
  139. struct cpufreq_driver *driver;
  140. ret = -ENOMEM;
  141. driver = kzalloc(sizeof(*driver), GFP_KERNEL);
  142. if (!driver)
  143. goto err_out;
  144. us3_freq_table = kzalloc((NR_CPUS * sizeof(*us3_freq_table)),
  145. GFP_KERNEL);
  146. if (!us3_freq_table)
  147. goto err_out;
  148. driver->init = us3_freq_cpu_init;
  149. driver->verify = cpufreq_generic_frequency_table_verify;
  150. driver->target_index = us3_freq_target;
  151. driver->get = us3_freq_get;
  152. driver->exit = us3_freq_cpu_exit;
  153. strcpy(driver->name, "UltraSPARC-III");
  154. cpufreq_us3_driver = driver;
  155. ret = cpufreq_register_driver(driver);
  156. if (ret)
  157. goto err_out;
  158. return 0;
  159. err_out:
  160. if (driver) {
  161. kfree(driver);
  162. cpufreq_us3_driver = NULL;
  163. }
  164. kfree(us3_freq_table);
  165. us3_freq_table = NULL;
  166. return ret;
  167. }
  168. return -ENODEV;
  169. }
  170. static void __exit us3_freq_exit(void)
  171. {
  172. if (cpufreq_us3_driver) {
  173. cpufreq_unregister_driver(cpufreq_us3_driver);
  174. kfree(cpufreq_us3_driver);
  175. cpufreq_us3_driver = NULL;
  176. kfree(us3_freq_table);
  177. us3_freq_table = NULL;
  178. }
  179. }
  180. MODULE_AUTHOR("David S. Miller <davem@redhat.com>");
  181. MODULE_DESCRIPTION("cpufreq driver for UltraSPARC-III");
  182. MODULE_LICENSE("GPL");
  183. module_init(us3_freq_init);
  184. module_exit(us3_freq_exit);