Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
elanfreq.c
Go to the documentation of this file.
1 /*
2  * elanfreq: cpufreq driver for the AMD ELAN family
3  *
4  * (c) Copyright 2002 Robert Schwebel <[email protected]>
5  *
6  * Parts of this code are (c) Sven Geggus <[email protected]>
7  *
8  * All Rights Reserved.
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version
13  * 2 of the License, or (at your option) any later version.
14  *
15  * 2002-02-13: - initial revision for 2.4.18-pre9 by Robert Schwebel
16  *
17  */
18 
19 #include <linux/kernel.h>
20 #include <linux/module.h>
21 #include <linux/init.h>
22 
23 #include <linux/delay.h>
24 #include <linux/cpufreq.h>
25 
26 #include <asm/cpu_device_id.h>
27 #include <asm/msr.h>
28 #include <linux/timex.h>
29 #include <linux/io.h>
30 
31 #define REG_CSCIR 0x22 /* Chip Setup and Control Index Register */
32 #define REG_CSCDR 0x23 /* Chip Setup and Control Data Register */
33 
34 /* Module parameter */
35 static int max_freq;
36 
38  int clock; /* frequency in kHz */
39  int val40h; /* PMU Force Mode register */
40  int val80h; /* CPU Clock Speed Register */
41 };
42 
43 /*
44  * It is important that the frequencies
45  * are listed in ascending order here!
46  */
47 static struct s_elan_multiplier elan_multiplier[] = {
48  {1000, 0x02, 0x18},
49  {2000, 0x02, 0x10},
50  {4000, 0x02, 0x08},
51  {8000, 0x00, 0x00},
52  {16000, 0x00, 0x02},
53  {33000, 0x00, 0x04},
54  {66000, 0x01, 0x04},
55  {99000, 0x01, 0x05}
56 };
57 
58 static struct cpufreq_frequency_table elanfreq_table[] = {
59  {0, 1000},
60  {1, 2000},
61  {2, 4000},
62  {3, 8000},
63  {4, 16000},
64  {5, 33000},
65  {6, 66000},
66  {7, 99000},
67  {0, CPUFREQ_TABLE_END},
68 };
69 
70 
80 static unsigned int elanfreq_get_cpu_frequency(unsigned int cpu)
81 {
82  u8 clockspeed_reg; /* Clock Speed Register */
83 
85  outb_p(0x80, REG_CSCIR);
86  clockspeed_reg = inb_p(REG_CSCDR);
88 
89  if ((clockspeed_reg & 0xE0) == 0xE0)
90  return 0;
91 
92  /* Are we in CPU clock multiplied mode (66/99 MHz)? */
93  if ((clockspeed_reg & 0xE0) == 0xC0) {
94  if ((clockspeed_reg & 0x01) == 0)
95  return 66000;
96  else
97  return 99000;
98  }
99 
100  /* 33 MHz is not 32 MHz... */
101  if ((clockspeed_reg & 0xE0) == 0xA0)
102  return 33000;
103 
104  return (1<<((clockspeed_reg & 0xE0) >> 5)) * 1000;
105 }
106 
107 
120 static void elanfreq_set_cpu_state(unsigned int state)
121 {
122  struct cpufreq_freqs freqs;
123 
124  freqs.old = elanfreq_get_cpu_frequency(0);
125  freqs.new = elan_multiplier[state].clock;
126  freqs.cpu = 0; /* elanfreq.c is UP only driver */
127 
129 
130  printk(KERN_INFO "elanfreq: attempting to set frequency to %i kHz\n",
131  elan_multiplier[state].clock);
132 
133 
134  /*
135  * Access to the Elan's internal registers is indexed via
136  * 0x22: Chip Setup & Control Register Index Register (CSCI)
137  * 0x23: Chip Setup & Control Register Data Register (CSCD)
138  *
139  */
140 
141  /*
142  * 0x40 is the Power Management Unit's Force Mode Register.
143  * Bit 6 enables Hyperspeed Mode (66/100 MHz core frequency)
144  */
145 
147  outb_p(0x40, REG_CSCIR); /* Disable hyperspeed mode */
148  outb_p(0x00, REG_CSCDR);
149  local_irq_enable(); /* wait till internal pipelines and */
150  udelay(1000); /* buffers have cleaned up */
151 
153 
154  /* now, set the CPU clock speed register (0x80) */
155  outb_p(0x80, REG_CSCIR);
156  outb_p(elan_multiplier[state].val80h, REG_CSCDR);
157 
158  /* now, the hyperspeed bit in PMU Force Mode Register (0x40) */
159  outb_p(0x40, REG_CSCIR);
160  outb_p(elan_multiplier[state].val40h, REG_CSCDR);
161  udelay(10000);
163 
165 };
166 
167 
176 static int elanfreq_verify(struct cpufreq_policy *policy)
177 {
178  return cpufreq_frequency_table_verify(policy, &elanfreq_table[0]);
179 }
180 
181 static int elanfreq_target(struct cpufreq_policy *policy,
182  unsigned int target_freq,
183  unsigned int relation)
184 {
185  unsigned int newstate = 0;
186 
187  if (cpufreq_frequency_table_target(policy, &elanfreq_table[0],
188  target_freq, relation, &newstate))
189  return -EINVAL;
190 
191  elanfreq_set_cpu_state(newstate);
192 
193  return 0;
194 }
195 
196 
197 /*
198  * Module init and exit code
199  */
200 
201 static int elanfreq_cpu_init(struct cpufreq_policy *policy)
202 {
203  struct cpuinfo_x86 *c = &cpu_data(0);
204  unsigned int i;
205  int result;
206 
207  /* capability check */
208  if ((c->x86_vendor != X86_VENDOR_AMD) ||
209  (c->x86 != 4) || (c->x86_model != 10))
210  return -ENODEV;
211 
212  /* max freq */
213  if (!max_freq)
214  max_freq = elanfreq_get_cpu_frequency(0);
215 
216  /* table init */
217  for (i = 0; (elanfreq_table[i].frequency != CPUFREQ_TABLE_END); i++) {
218  if (elanfreq_table[i].frequency > max_freq)
219  elanfreq_table[i].frequency = CPUFREQ_ENTRY_INVALID;
220  }
221 
222  /* cpuinfo and default policy values */
223  policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
224  policy->cur = elanfreq_get_cpu_frequency(0);
225 
226  result = cpufreq_frequency_table_cpuinfo(policy, elanfreq_table);
227  if (result)
228  return result;
229 
230  cpufreq_frequency_table_get_attr(elanfreq_table, policy->cpu);
231  return 0;
232 }
233 
234 
235 static int elanfreq_cpu_exit(struct cpufreq_policy *policy)
236 {
238  return 0;
239 }
240 
241 
242 #ifndef MODULE
243 
254 static int __init elanfreq_setup(char *str)
255 {
256  max_freq = simple_strtoul(str, &str, 0);
257  printk(KERN_WARNING "You're using the deprecated elanfreq command line option. Use elanfreq.max_freq instead, please!\n");
258  return 1;
259 }
260 __setup("elanfreq=", elanfreq_setup);
261 #endif
262 
263 
264 static struct freq_attr *elanfreq_attr[] = {
266  NULL,
267 };
268 
269 
270 static struct cpufreq_driver elanfreq_driver = {
271  .get = elanfreq_get_cpu_frequency,
272  .verify = elanfreq_verify,
273  .target = elanfreq_target,
274  .init = elanfreq_cpu_init,
275  .exit = elanfreq_cpu_exit,
276  .name = "elanfreq",
277  .owner = THIS_MODULE,
278  .attr = elanfreq_attr,
279 };
280 
281 static const struct x86_cpu_id elan_id[] = {
282  { X86_VENDOR_AMD, 4, 10, },
283  {}
284 };
285 MODULE_DEVICE_TABLE(x86cpu, elan_id);
286 
287 static int __init elanfreq_init(void)
288 {
289  if (!x86_match_cpu(elan_id))
290  return -ENODEV;
291  return cpufreq_register_driver(&elanfreq_driver);
292 }
293 
294 
295 static void __exit elanfreq_exit(void)
296 {
297  cpufreq_unregister_driver(&elanfreq_driver);
298 }
299 
300 
301 module_param(max_freq, int, 0444);
302 
303 MODULE_LICENSE("GPL");
304 MODULE_AUTHOR("Robert Schwebel <[email protected]>, "
305  "Sven Geggus <[email protected]>");
306 MODULE_DESCRIPTION("cpufreq driver for AMD's Elan CPUs");
307 
308 module_init(elanfreq_init);
309 module_exit(elanfreq_exit);