speedstep-smi.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465
  1. /*
  2. * Intel SpeedStep SMI driver.
  3. *
  4. * (C) 2003 Hiroshi Miura <miura@da-cha.org>
  5. *
  6. * Licensed under the terms of the GNU GPL License version 2.
  7. *
  8. */
  9. /*********************************************************************
  10. * SPEEDSTEP - DEFINITIONS *
  11. *********************************************************************/
  12. #include <linux/kernel.h>
  13. #include <linux/module.h>
  14. #include <linux/moduleparam.h>
  15. #include <linux/init.h>
  16. #include <linux/cpufreq.h>
  17. #include <linux/delay.h>
  18. #include <linux/io.h>
  19. #include <asm/ist.h>
  20. #include "speedstep-lib.h"
  21. /* speedstep system management interface port/command.
  22. *
  23. * These parameters are got from IST-SMI BIOS call.
  24. * If user gives it, these are used.
  25. *
  26. */
  27. static int smi_port;
  28. static int smi_cmd;
  29. static unsigned int smi_sig;
  30. /* info about the processor */
  31. static enum speedstep_processor speedstep_processor;
  32. /*
  33. * There are only two frequency states for each processor. Values
  34. * are in kHz for the time being.
  35. */
  36. static struct cpufreq_frequency_table speedstep_freqs[] = {
  37. {SPEEDSTEP_HIGH, 0},
  38. {SPEEDSTEP_LOW, 0},
  39. {0, CPUFREQ_TABLE_END},
  40. };
  41. #define GET_SPEEDSTEP_OWNER 0
  42. #define GET_SPEEDSTEP_STATE 1
  43. #define SET_SPEEDSTEP_STATE 2
  44. #define GET_SPEEDSTEP_FREQS 4
  45. /* how often shall the SMI call be tried if it failed, e.g. because
  46. * of DMA activity going on? */
  47. #define SMI_TRIES 5
  48. /**
  49. * speedstep_smi_ownership
  50. */
  51. static int speedstep_smi_ownership(void)
  52. {
  53. u32 command, result, magic, dummy;
  54. u32 function = GET_SPEEDSTEP_OWNER;
  55. unsigned char magic_data[] = "Copyright (c) 1999 Intel Corporation";
  56. command = (smi_sig & 0xffffff00) | (smi_cmd & 0xff);
  57. magic = virt_to_phys(magic_data);
  58. pr_debug("trying to obtain ownership with command %x at port %x\n",
  59. command, smi_port);
  60. __asm__ __volatile__(
  61. "push %%ebp\n"
  62. "out %%al, (%%dx)\n"
  63. "pop %%ebp\n"
  64. : "=D" (result),
  65. "=a" (dummy), "=b" (dummy), "=c" (dummy), "=d" (dummy),
  66. "=S" (dummy)
  67. : "a" (command), "b" (function), "c" (0), "d" (smi_port),
  68. "D" (0), "S" (magic)
  69. : "memory"
  70. );
  71. pr_debug("result is %x\n", result);
  72. return result;
  73. }
  74. /**
  75. * speedstep_smi_get_freqs - get SpeedStep preferred & current freq.
  76. * @low: the low frequency value is placed here
  77. * @high: the high frequency value is placed here
  78. *
  79. * Only available on later SpeedStep-enabled systems, returns false results or
  80. * even hangs [cf. bugme.osdl.org # 1422] on earlier systems. Empirical testing
  81. * shows that the latter occurs if !(ist_info.event & 0xFFFF).
  82. */
  83. static int speedstep_smi_get_freqs(unsigned int *low, unsigned int *high)
  84. {
  85. u32 command, result = 0, edi, high_mhz, low_mhz, dummy;
  86. u32 state = 0;
  87. u32 function = GET_SPEEDSTEP_FREQS;
  88. if (!(ist_info.event & 0xFFFF)) {
  89. pr_debug("bug #1422 -- can't read freqs from BIOS\n");
  90. return -ENODEV;
  91. }
  92. command = (smi_sig & 0xffffff00) | (smi_cmd & 0xff);
  93. pr_debug("trying to determine frequencies with command %x at port %x\n",
  94. command, smi_port);
  95. __asm__ __volatile__(
  96. "push %%ebp\n"
  97. "out %%al, (%%dx)\n"
  98. "pop %%ebp"
  99. : "=a" (result),
  100. "=b" (high_mhz),
  101. "=c" (low_mhz),
  102. "=d" (state), "=D" (edi), "=S" (dummy)
  103. : "a" (command),
  104. "b" (function),
  105. "c" (state),
  106. "d" (smi_port), "S" (0), "D" (0)
  107. );
  108. pr_debug("result %x, low_freq %u, high_freq %u\n",
  109. result, low_mhz, high_mhz);
  110. /* abort if results are obviously incorrect... */
  111. if ((high_mhz + low_mhz) < 600)
  112. return -EINVAL;
  113. *high = high_mhz * 1000;
  114. *low = low_mhz * 1000;
  115. return result;
  116. }
  117. /**
  118. * speedstep_get_state - set the SpeedStep state
  119. * @state: processor frequency state (SPEEDSTEP_LOW or SPEEDSTEP_HIGH)
  120. *
  121. */
  122. static int speedstep_get_state(void)
  123. {
  124. u32 function = GET_SPEEDSTEP_STATE;
  125. u32 result, state, edi, command, dummy;
  126. command = (smi_sig & 0xffffff00) | (smi_cmd & 0xff);
  127. pr_debug("trying to determine current setting with command %x "
  128. "at port %x\n", command, smi_port);
  129. __asm__ __volatile__(
  130. "push %%ebp\n"
  131. "out %%al, (%%dx)\n"
  132. "pop %%ebp\n"
  133. : "=a" (result),
  134. "=b" (state), "=D" (edi),
  135. "=c" (dummy), "=d" (dummy), "=S" (dummy)
  136. : "a" (command), "b" (function), "c" (0),
  137. "d" (smi_port), "S" (0), "D" (0)
  138. );
  139. pr_debug("state is %x, result is %x\n", state, result);
  140. return state & 1;
  141. }
  142. /**
  143. * speedstep_set_state - set the SpeedStep state
  144. * @state: new processor frequency state (SPEEDSTEP_LOW or SPEEDSTEP_HIGH)
  145. *
  146. */
  147. static void speedstep_set_state(unsigned int state)
  148. {
  149. unsigned int result = 0, command, new_state, dummy;
  150. unsigned long flags;
  151. unsigned int function = SET_SPEEDSTEP_STATE;
  152. unsigned int retry = 0;
  153. if (state > 0x1)
  154. return;
  155. /* Disable IRQs */
  156. local_irq_save(flags);
  157. command = (smi_sig & 0xffffff00) | (smi_cmd & 0xff);
  158. pr_debug("trying to set frequency to state %u "
  159. "with command %x at port %x\n",
  160. state, command, smi_port);
  161. do {
  162. if (retry) {
  163. pr_debug("retry %u, previous result %u, waiting...\n",
  164. retry, result);
  165. mdelay(retry * 50);
  166. }
  167. retry++;
  168. __asm__ __volatile__(
  169. "push %%ebp\n"
  170. "out %%al, (%%dx)\n"
  171. "pop %%ebp"
  172. : "=b" (new_state), "=D" (result),
  173. "=c" (dummy), "=a" (dummy),
  174. "=d" (dummy), "=S" (dummy)
  175. : "a" (command), "b" (function), "c" (state),
  176. "d" (smi_port), "S" (0), "D" (0)
  177. );
  178. } while ((new_state != state) && (retry <= SMI_TRIES));
  179. /* enable IRQs */
  180. local_irq_restore(flags);
  181. if (new_state == state)
  182. pr_debug("change to %u MHz succeeded after %u tries "
  183. "with result %u\n",
  184. (speedstep_freqs[new_state].frequency / 1000),
  185. retry, result);
  186. else
  187. printk(KERN_ERR "cpufreq: change to state %u "
  188. "failed with new_state %u and result %u\n",
  189. state, new_state, result);
  190. return;
  191. }
  192. /**
  193. * speedstep_target - set a new CPUFreq policy
  194. * @policy: new policy
  195. * @target_freq: new freq
  196. * @relation:
  197. *
  198. * Sets a new CPUFreq policy/freq.
  199. */
  200. static int speedstep_target(struct cpufreq_policy *policy,
  201. unsigned int target_freq, unsigned int relation)
  202. {
  203. unsigned int newstate = 0;
  204. struct cpufreq_freqs freqs;
  205. if (cpufreq_frequency_table_target(policy, &speedstep_freqs[0],
  206. target_freq, relation, &newstate))
  207. return -EINVAL;
  208. freqs.old = speedstep_freqs[speedstep_get_state()].frequency;
  209. freqs.new = speedstep_freqs[newstate].frequency;
  210. freqs.cpu = 0; /* speedstep.c is UP only driver */
  211. if (freqs.old == freqs.new)
  212. return 0;
  213. cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
  214. speedstep_set_state(newstate);
  215. cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
  216. return 0;
  217. }
  218. /**
  219. * speedstep_verify - verifies a new CPUFreq policy
  220. * @policy: new policy
  221. *
  222. * Limit must be within speedstep_low_freq and speedstep_high_freq, with
  223. * at least one border included.
  224. */
  225. static int speedstep_verify(struct cpufreq_policy *policy)
  226. {
  227. return cpufreq_frequency_table_verify(policy, &speedstep_freqs[0]);
  228. }
  229. static int speedstep_cpu_init(struct cpufreq_policy *policy)
  230. {
  231. int result;
  232. unsigned int speed, state;
  233. unsigned int *low, *high;
  234. /* capability check */
  235. if (policy->cpu != 0)
  236. return -ENODEV;
  237. result = speedstep_smi_ownership();
  238. if (result) {
  239. pr_debug("fails in acquiring ownership of a SMI interface.\n");
  240. return -EINVAL;
  241. }
  242. /* detect low and high frequency */
  243. low = &speedstep_freqs[SPEEDSTEP_LOW].frequency;
  244. high = &speedstep_freqs[SPEEDSTEP_HIGH].frequency;
  245. result = speedstep_smi_get_freqs(low, high);
  246. if (result) {
  247. /* fall back to speedstep_lib.c dection mechanism:
  248. * try both states out */
  249. pr_debug("could not detect low and high frequencies "
  250. "by SMI call.\n");
  251. result = speedstep_get_freqs(speedstep_processor,
  252. low, high,
  253. NULL,
  254. &speedstep_set_state);
  255. if (result) {
  256. pr_debug("could not detect two different speeds"
  257. " -- aborting.\n");
  258. return result;
  259. } else
  260. pr_debug("workaround worked.\n");
  261. }
  262. /* get current speed setting */
  263. state = speedstep_get_state();
  264. speed = speedstep_freqs[state].frequency;
  265. pr_debug("currently at %s speed setting - %i MHz\n",
  266. (speed == speedstep_freqs[SPEEDSTEP_LOW].frequency)
  267. ? "low" : "high",
  268. (speed / 1000));
  269. /* cpuinfo and default policy values */
  270. policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
  271. policy->cur = speed;
  272. result = cpufreq_frequency_table_cpuinfo(policy, speedstep_freqs);
  273. if (result)
  274. return result;
  275. cpufreq_frequency_table_get_attr(speedstep_freqs, policy->cpu);
  276. return 0;
  277. }
  278. static int speedstep_cpu_exit(struct cpufreq_policy *policy)
  279. {
  280. cpufreq_frequency_table_put_attr(policy->cpu);
  281. return 0;
  282. }
  283. static unsigned int speedstep_get(unsigned int cpu)
  284. {
  285. if (cpu)
  286. return -ENODEV;
  287. return speedstep_get_frequency(speedstep_processor);
  288. }
  289. static int speedstep_resume(struct cpufreq_policy *policy)
  290. {
  291. int result = speedstep_smi_ownership();
  292. if (result)
  293. pr_debug("fails in re-acquiring ownership of a SMI interface.\n");
  294. return result;
  295. }
  296. static struct freq_attr *speedstep_attr[] = {
  297. &cpufreq_freq_attr_scaling_available_freqs,
  298. NULL,
  299. };
  300. static struct cpufreq_driver speedstep_driver = {
  301. .name = "speedstep-smi",
  302. .verify = speedstep_verify,
  303. .target = speedstep_target,
  304. .init = speedstep_cpu_init,
  305. .exit = speedstep_cpu_exit,
  306. .get = speedstep_get,
  307. .resume = speedstep_resume,
  308. .owner = THIS_MODULE,
  309. .attr = speedstep_attr,
  310. };
  311. /**
  312. * speedstep_init - initializes the SpeedStep CPUFreq driver
  313. *
  314. * Initializes the SpeedStep support. Returns -ENODEV on unsupported
  315. * BIOS, -EINVAL on problems during initiatization, and zero on
  316. * success.
  317. */
  318. static int __init speedstep_init(void)
  319. {
  320. speedstep_processor = speedstep_detect_processor();
  321. switch (speedstep_processor) {
  322. case SPEEDSTEP_CPU_PIII_T:
  323. case SPEEDSTEP_CPU_PIII_C:
  324. case SPEEDSTEP_CPU_PIII_C_EARLY:
  325. break;
  326. default:
  327. speedstep_processor = 0;
  328. }
  329. if (!speedstep_processor) {
  330. pr_debug("No supported Intel CPU detected.\n");
  331. return -ENODEV;
  332. }
  333. pr_debug("signature:0x%.8ulx, command:0x%.8ulx, "
  334. "event:0x%.8ulx, perf_level:0x%.8ulx.\n",
  335. ist_info.signature, ist_info.command,
  336. ist_info.event, ist_info.perf_level);
  337. /* Error if no IST-SMI BIOS or no PARM
  338. sig= 'ISGE' aka 'Intel Speedstep Gate E' */
  339. if ((ist_info.signature != 0x47534943) && (
  340. (smi_port == 0) || (smi_cmd == 0)))
  341. return -ENODEV;
  342. if (smi_sig == 1)
  343. smi_sig = 0x47534943;
  344. else
  345. smi_sig = ist_info.signature;
  346. /* setup smi_port from MODLULE_PARM or BIOS */
  347. if ((smi_port > 0xff) || (smi_port < 0))
  348. return -EINVAL;
  349. else if (smi_port == 0)
  350. smi_port = ist_info.command & 0xff;
  351. if ((smi_cmd > 0xff) || (smi_cmd < 0))
  352. return -EINVAL;
  353. else if (smi_cmd == 0)
  354. smi_cmd = (ist_info.command >> 16) & 0xff;
  355. return cpufreq_register_driver(&speedstep_driver);
  356. }
  357. /**
  358. * speedstep_exit - unregisters SpeedStep support
  359. *
  360. * Unregisters SpeedStep support.
  361. */
  362. static void __exit speedstep_exit(void)
  363. {
  364. cpufreq_unregister_driver(&speedstep_driver);
  365. }
  366. module_param(smi_port, int, 0444);
  367. module_param(smi_cmd, int, 0444);
  368. module_param(smi_sig, uint, 0444);
  369. MODULE_PARM_DESC(smi_port, "Override the BIOS-given IST port with this value "
  370. "-- Intel's default setting is 0xb2");
  371. MODULE_PARM_DESC(smi_cmd, "Override the BIOS-given IST command with this value "
  372. "-- Intel's default setting is 0x82");
  373. MODULE_PARM_DESC(smi_sig, "Set to 1 to fake the IST signature when using the "
  374. "SMI interface.");
  375. MODULE_AUTHOR("Hiroshi Miura");
  376. MODULE_DESCRIPTION("Speedstep driver for IST applet SMI interface.");
  377. MODULE_LICENSE("GPL");
  378. module_init(speedstep_init);
  379. module_exit(speedstep_exit);