sc520_freq.c 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. /*
  2. * sc520_freq.c: cpufreq driver for the AMD Elan sc520
  3. *
  4. * Copyright (C) 2005 Sean Young <sean@mess.org>
  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
  8. * as published by the Free Software Foundation; either version
  9. * 2 of the License, or (at your option) any later version.
  10. *
  11. * Based on elanfreq.c
  12. *
  13. * 2005-03-30: - initial revision
  14. */
  15. #include <linux/kernel.h>
  16. #include <linux/module.h>
  17. #include <linux/init.h>
  18. #include <linux/delay.h>
  19. #include <linux/cpufreq.h>
  20. #include <linux/timex.h>
  21. #include <linux/io.h>
  22. #include <asm/msr.h>
  23. #define MMCR_BASE 0xfffef000 /* The default base address */
  24. #define OFFS_CPUCTL 0x2 /* CPU Control Register */
  25. static __u8 __iomem *cpuctl;
  26. #define PFX "sc520_freq: "
  27. static struct cpufreq_frequency_table sc520_freq_table[] = {
  28. {0x01, 100000},
  29. {0x02, 133000},
  30. {0, CPUFREQ_TABLE_END},
  31. };
  32. static unsigned int sc520_freq_get_cpu_frequency(unsigned int cpu)
  33. {
  34. u8 clockspeed_reg = *cpuctl;
  35. switch (clockspeed_reg & 0x03) {
  36. default:
  37. printk(KERN_ERR PFX "error: cpuctl register has unexpected "
  38. "value %02x\n", clockspeed_reg);
  39. case 0x01:
  40. return 100000;
  41. case 0x02:
  42. return 133000;
  43. }
  44. }
  45. static void sc520_freq_set_cpu_state(unsigned int state)
  46. {
  47. struct cpufreq_freqs freqs;
  48. u8 clockspeed_reg;
  49. freqs.old = sc520_freq_get_cpu_frequency(0);
  50. freqs.new = sc520_freq_table[state].frequency;
  51. freqs.cpu = 0; /* AMD Elan is UP */
  52. cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
  53. pr_debug("attempting to set frequency to %i kHz\n",
  54. sc520_freq_table[state].frequency);
  55. local_irq_disable();
  56. clockspeed_reg = *cpuctl & ~0x03;
  57. *cpuctl = clockspeed_reg | sc520_freq_table[state].index;
  58. local_irq_enable();
  59. cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
  60. };
  61. static int sc520_freq_verify(struct cpufreq_policy *policy)
  62. {
  63. return cpufreq_frequency_table_verify(policy, &sc520_freq_table[0]);
  64. }
  65. static int sc520_freq_target(struct cpufreq_policy *policy,
  66. unsigned int target_freq,
  67. unsigned int relation)
  68. {
  69. unsigned int newstate = 0;
  70. if (cpufreq_frequency_table_target(policy, sc520_freq_table,
  71. target_freq, relation, &newstate))
  72. return -EINVAL;
  73. sc520_freq_set_cpu_state(newstate);
  74. return 0;
  75. }
  76. /*
  77. * Module init and exit code
  78. */
  79. static int sc520_freq_cpu_init(struct cpufreq_policy *policy)
  80. {
  81. struct cpuinfo_x86 *c = &cpu_data(0);
  82. int result;
  83. /* capability check */
  84. if (c->x86_vendor != X86_VENDOR_AMD ||
  85. c->x86 != 4 || c->x86_model != 9)
  86. return -ENODEV;
  87. /* cpuinfo and default policy values */
  88. policy->cpuinfo.transition_latency = 1000000; /* 1ms */
  89. policy->cur = sc520_freq_get_cpu_frequency(0);
  90. result = cpufreq_frequency_table_cpuinfo(policy, sc520_freq_table);
  91. if (result)
  92. return result;
  93. cpufreq_frequency_table_get_attr(sc520_freq_table, policy->cpu);
  94. return 0;
  95. }
  96. static int sc520_freq_cpu_exit(struct cpufreq_policy *policy)
  97. {
  98. cpufreq_frequency_table_put_attr(policy->cpu);
  99. return 0;
  100. }
  101. static struct freq_attr *sc520_freq_attr[] = {
  102. &cpufreq_freq_attr_scaling_available_freqs,
  103. NULL,
  104. };
  105. static struct cpufreq_driver sc520_freq_driver = {
  106. .get = sc520_freq_get_cpu_frequency,
  107. .verify = sc520_freq_verify,
  108. .target = sc520_freq_target,
  109. .init = sc520_freq_cpu_init,
  110. .exit = sc520_freq_cpu_exit,
  111. .name = "sc520_freq",
  112. .owner = THIS_MODULE,
  113. .attr = sc520_freq_attr,
  114. };
  115. static int __init sc520_freq_init(void)
  116. {
  117. struct cpuinfo_x86 *c = &cpu_data(0);
  118. int err;
  119. /* Test if we have the right hardware */
  120. if (c->x86_vendor != X86_VENDOR_AMD ||
  121. c->x86 != 4 || c->x86_model != 9) {
  122. pr_debug("no Elan SC520 processor found!\n");
  123. return -ENODEV;
  124. }
  125. cpuctl = ioremap((unsigned long)(MMCR_BASE + OFFS_CPUCTL), 1);
  126. if (!cpuctl) {
  127. printk(KERN_ERR "sc520_freq: error: failed to remap memory\n");
  128. return -ENOMEM;
  129. }
  130. err = cpufreq_register_driver(&sc520_freq_driver);
  131. if (err)
  132. iounmap(cpuctl);
  133. return err;
  134. }
  135. static void __exit sc520_freq_exit(void)
  136. {
  137. cpufreq_unregister_driver(&sc520_freq_driver);
  138. iounmap(cpuctl);
  139. }
  140. MODULE_LICENSE("GPL");
  141. MODULE_AUTHOR("Sean Young <sean@mess.org>");
  142. MODULE_DESCRIPTION("cpufreq driver for AMD's Elan sc520 CPU");
  143. module_init(sc520_freq_init);
  144. module_exit(sc520_freq_exit);