Blame view
drivers/cpufreq/speedstep-ich.c
9.2 KB
1da177e4c Linux-2.6.12-rc2 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
/* * (C) 2001 Dave Jones, Arjan van de ven. * (C) 2002 - 2003 Dominik Brodowski <linux@brodo.de> * * Licensed under the terms of the GNU GPL License version 2. * Based upon reverse engineered information, and on Intel documentation * for chipsets ICH2-M and ICH3-M. * * Many thanks to Ducrot Bruno for finding and fixing the last * "missing link" for ICH2-M/ICH3-M support, and to Thomas Winkler * for extensive testing. * * BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous* */ /********************************************************************* * SPEEDSTEP - DEFINITIONS * *********************************************************************/ |
1c5864e26 cpufreq: Use cons... |
20 |
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
1da177e4c Linux-2.6.12-rc2 |
21 22 23 24 25 |
#include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> #include <linux/cpufreq.h> #include <linux/pci.h> |
e8edc6e03 Detach sched.h fr... |
26 |
#include <linux/sched.h> |
1da177e4c Linux-2.6.12-rc2 |
27 |
|
fa8031aef cpufreq: Add supp... |
28 |
#include <asm/cpu_device_id.h> |
1da177e4c Linux-2.6.12-rc2 |
29 30 31 32 33 34 35 36 37 38 39 40 41 |
#include "speedstep-lib.h" /* speedstep_chipset: * It is necessary to know which chipset is used. As accesses to * this device occur at various places in this module, we need a * static struct pci_dev * pointing to that device. */ static struct pci_dev *speedstep_chipset_dev; /* speedstep_processor */ |
1cce76c2a [CPUFREQ] use an ... |
42 |
static enum speedstep_processor speedstep_processor; |
1da177e4c Linux-2.6.12-rc2 |
43 |
|
9a7d82a89 [CPUFREQ] Move PM... |
44 |
static u32 pmbase; |
1da177e4c Linux-2.6.12-rc2 |
45 46 47 48 49 50 |
/* * There are only two frequency states for each processor. Values * are in kHz for the time being. */ static struct cpufreq_frequency_table speedstep_freqs[] = { |
7f4b04614 cpufreq: create a... |
51 52 53 |
{0, SPEEDSTEP_HIGH, 0}, {0, SPEEDSTEP_LOW, 0}, {0, 0, CPUFREQ_TABLE_END}, |
1da177e4c Linux-2.6.12-rc2 |
54 |
}; |
1da177e4c Linux-2.6.12-rc2 |
55 |
/** |
9a7d82a89 [CPUFREQ] Move PM... |
56 |
* speedstep_find_register - read the PMBASE address |
1da177e4c Linux-2.6.12-rc2 |
57 |
* |
9a7d82a89 [CPUFREQ] Move PM... |
58 |
* Returns: -ENODEV if no register could be found |
1da177e4c Linux-2.6.12-rc2 |
59 |
*/ |
bbfebd665 [CPUFREQ] checkpa... |
60 |
static int speedstep_find_register(void) |
1da177e4c Linux-2.6.12-rc2 |
61 |
{ |
9a7d82a89 [CPUFREQ] Move PM... |
62 63 |
if (!speedstep_chipset_dev) return -ENODEV; |
1da177e4c Linux-2.6.12-rc2 |
64 65 66 67 |
/* get PMBASE */ pci_read_config_dword(speedstep_chipset_dev, 0x40, &pmbase); if (!(pmbase & 0x01)) { |
1c5864e26 cpufreq: Use cons... |
68 69 |
pr_err("could not find speedstep register "); |
9a7d82a89 [CPUFREQ] Move PM... |
70 |
return -ENODEV; |
1da177e4c Linux-2.6.12-rc2 |
71 72 73 74 |
} pmbase &= 0xFFFFFFFE; if (!pmbase) { |
1c5864e26 cpufreq: Use cons... |
75 76 |
pr_err("could not find speedstep register "); |
9a7d82a89 [CPUFREQ] Move PM... |
77 |
return -ENODEV; |
1da177e4c Linux-2.6.12-rc2 |
78 |
} |
2d06d8c49 [CPUFREQ] use dyn... |
79 80 |
pr_debug("pmbase is 0x%x ", pmbase); |
9a7d82a89 [CPUFREQ] Move PM... |
81 82 83 84 85 86 87 |
return 0; } /** * speedstep_set_state - set the SpeedStep state * @state: new processor frequency state (SPEEDSTEP_LOW or SPEEDSTEP_HIGH) * |
394122ab1 [CPUFREQ] cpumask... |
88 89 |
* Tries to change the SpeedStep state. Can be called from * smp_call_function_single. |
9a7d82a89 [CPUFREQ] Move PM... |
90 |
*/ |
bbfebd665 [CPUFREQ] checkpa... |
91 |
static void speedstep_set_state(unsigned int state) |
9a7d82a89 [CPUFREQ] Move PM... |
92 93 94 95 96 97 98 |
{ u8 pm2_blk; u8 value; unsigned long flags; if (state > 0x1) return; |
1da177e4c Linux-2.6.12-rc2 |
99 100 101 102 103 |
/* Disable IRQs */ local_irq_save(flags); /* read state */ value = inb(pmbase + 0x50); |
2d06d8c49 [CPUFREQ] use dyn... |
104 105 |
pr_debug("read at pmbase 0x%x + 0x50 returned 0x%x ", pmbase, value); |
1da177e4c Linux-2.6.12-rc2 |
106 107 108 109 |
/* write new state */ value &= 0xFE; value |= state; |
2d06d8c49 [CPUFREQ] use dyn... |
110 111 |
pr_debug("writing 0x%x to pmbase 0x%x + 0x50 ", value, pmbase); |
1da177e4c Linux-2.6.12-rc2 |
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
/* Disable bus master arbitration */ pm2_blk = inb(pmbase + 0x20); pm2_blk |= 0x01; outb(pm2_blk, (pmbase + 0x20)); /* Actual transition */ outb(value, (pmbase + 0x50)); /* Restore bus master arbitration */ pm2_blk &= 0xfe; outb(pm2_blk, (pmbase + 0x20)); /* check if transition was successful */ value = inb(pmbase + 0x50); /* Enable IRQs */ local_irq_restore(flags); |
2d06d8c49 [CPUFREQ] use dyn... |
130 131 |
pr_debug("read at pmbase 0x%x + 0x50 returned 0x%x ", pmbase, value); |
1da177e4c Linux-2.6.12-rc2 |
132 |
|
bbfebd665 [CPUFREQ] checkpa... |
133 |
if (state == (value & 0x1)) |
2d06d8c49 [CPUFREQ] use dyn... |
134 135 |
pr_debug("change to %u MHz succeeded ", |
bbfebd665 [CPUFREQ] checkpa... |
136 137 |
speedstep_get_frequency(speedstep_processor) / 1000); else |
1c5864e26 cpufreq: Use cons... |
138 139 |
pr_err("change failed - I/O error "); |
1da177e4c Linux-2.6.12-rc2 |
140 141 142 |
return; } |
394122ab1 [CPUFREQ] cpumask... |
143 144 145 146 147 |
/* Wrapper for smp_call_function_single. */ static void _speedstep_set_state(void *_state) { speedstep_set_state(*(unsigned int *)_state); } |
1da177e4c Linux-2.6.12-rc2 |
148 149 150 151 152 153 154 |
/** * speedstep_activate - activate SpeedStep control in the chipset * * Tries to activate the SpeedStep status and control registers. * Returns -EINVAL on an unsupported chipset, and zero on success. */ |
bbfebd665 [CPUFREQ] checkpa... |
155 |
static int speedstep_activate(void) |
1da177e4c Linux-2.6.12-rc2 |
156 157 158 159 160 161 162 163 164 |
{ u16 value = 0; if (!speedstep_chipset_dev) return -EINVAL; pci_read_config_word(speedstep_chipset_dev, 0x00A0, &value); if (!(value & 0x08)) { value |= 0x08; |
2d06d8c49 [CPUFREQ] use dyn... |
165 166 |
pr_debug("activating SpeedStep (TM) registers "); |
1da177e4c Linux-2.6.12-rc2 |
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 |
pci_write_config_word(speedstep_chipset_dev, 0x00A0, value); } return 0; } /** * speedstep_detect_chipset - detect the Southbridge which contains SpeedStep logic * * Detects ICH2-M, ICH3-M and ICH4-M so far. The pci_dev points to * the LPC bridge / PM module which contains all power-management * functions. Returns the SPEEDSTEP_CHIPSET_-number for the detected * chipset, or zero on failure. */ |
bbfebd665 [CPUFREQ] checkpa... |
182 |
static unsigned int speedstep_detect_chipset(void) |
1da177e4c Linux-2.6.12-rc2 |
183 184 185 |
{ speedstep_chipset_dev = pci_get_subsys(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_12, |
bbfebd665 [CPUFREQ] checkpa... |
186 |
PCI_ANY_ID, PCI_ANY_ID, |
1da177e4c Linux-2.6.12-rc2 |
187 188 189 190 191 192 |
NULL); if (speedstep_chipset_dev) return 4; /* 4-M */ speedstep_chipset_dev = pci_get_subsys(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12, |
bbfebd665 [CPUFREQ] checkpa... |
193 |
PCI_ANY_ID, PCI_ANY_ID, |
1da177e4c Linux-2.6.12-rc2 |
194 195 196 197 198 199 200 |
NULL); if (speedstep_chipset_dev) return 3; /* 3-M */ speedstep_chipset_dev = pci_get_subsys(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_10, |
bbfebd665 [CPUFREQ] checkpa... |
201 |
PCI_ANY_ID, PCI_ANY_ID, |
1da177e4c Linux-2.6.12-rc2 |
202 203 204 205 |
NULL); if (speedstep_chipset_dev) { /* speedstep.c causes lockups on Dell Inspirons 8000 and * 8100 which use a pretty old revision of the 82815 |
c03c30137 cpufreq: Fix typo... |
206 |
* host bridge. Abort on these systems. |
1da177e4c Linux-2.6.12-rc2 |
207 208 |
*/ static struct pci_dev *hostbridge; |
1da177e4c Linux-2.6.12-rc2 |
209 210 211 |
hostbridge = pci_get_subsys(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82815_MC, |
bbfebd665 [CPUFREQ] checkpa... |
212 |
PCI_ANY_ID, PCI_ANY_ID, |
1da177e4c Linux-2.6.12-rc2 |
213 214 215 216 |
NULL); if (!hostbridge) return 2; /* 2-M */ |
44c10138f PCI: Change all d... |
217 |
if (hostbridge->revision < 5) { |
2d06d8c49 [CPUFREQ] use dyn... |
218 219 |
pr_debug("hostbridge does not support speedstep "); |
1da177e4c Linux-2.6.12-rc2 |
220 221 222 223 224 225 226 227 228 229 230 |
speedstep_chipset_dev = NULL; pci_dev_put(hostbridge); return 0; } pci_dev_put(hostbridge); return 2; /* 2-M */ } return 0; } |
8dca15e40 [CPUFREQ] speedst... |
231 |
static void get_freq_data(void *_speed) |
394122ab1 [CPUFREQ] cpumask... |
232 |
{ |
8dca15e40 [CPUFREQ] speedst... |
233 |
unsigned int *speed = _speed; |
394122ab1 [CPUFREQ] cpumask... |
234 |
|
8dca15e40 [CPUFREQ] speedst... |
235 |
*speed = speedstep_get_frequency(speedstep_processor); |
1da177e4c Linux-2.6.12-rc2 |
236 237 238 239 |
} static unsigned int speedstep_get(unsigned int cpu) { |
8dca15e40 [CPUFREQ] speedst... |
240 |
unsigned int speed; |
394122ab1 [CPUFREQ] cpumask... |
241 242 |
/* You're supposed to ensure CPU is online. */ |
8dca15e40 [CPUFREQ] speedst... |
243 |
if (smp_call_function_single(cpu, get_freq_data, &speed, 1) != 0) |
394122ab1 [CPUFREQ] cpumask... |
244 |
BUG(); |
2d06d8c49 [CPUFREQ] use dyn... |
245 246 |
pr_debug("detected %u kHz as current frequency ", speed); |
8dca15e40 [CPUFREQ] speedst... |
247 |
return speed; |
1da177e4c Linux-2.6.12-rc2 |
248 249 250 251 252 |
} /** * speedstep_target - set a new CPUFreq policy * @policy: new policy |
9c0ebcf78 cpufreq: Implemen... |
253 |
* @index: index of target frequency |
1da177e4c Linux-2.6.12-rc2 |
254 255 256 |
* * Sets a new CPUFreq policy. */ |
9c0ebcf78 cpufreq: Implemen... |
257 |
static int speedstep_target(struct cpufreq_policy *policy, unsigned int index) |
1da177e4c Linux-2.6.12-rc2 |
258 |
{ |
9c0ebcf78 cpufreq: Implemen... |
259 |
unsigned int policy_cpu; |
1da177e4c Linux-2.6.12-rc2 |
260 |
|
394122ab1 [CPUFREQ] cpumask... |
261 |
policy_cpu = cpumask_any_and(policy->cpus, cpu_online_mask); |
1da177e4c Linux-2.6.12-rc2 |
262 |
|
9c0ebcf78 cpufreq: Implemen... |
263 |
smp_call_function_single(policy_cpu, _speedstep_set_state, &index, |
394122ab1 [CPUFREQ] cpumask... |
264 |
true); |
1da177e4c Linux-2.6.12-rc2 |
265 |
|
1da177e4c Linux-2.6.12-rc2 |
266 267 |
return 0; } |
394122ab1 [CPUFREQ] cpumask... |
268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 |
struct get_freqs { struct cpufreq_policy *policy; int ret; }; static void get_freqs_on_cpu(void *_get_freqs) { struct get_freqs *get_freqs = _get_freqs; get_freqs->ret = speedstep_get_freqs(speedstep_processor, &speedstep_freqs[SPEEDSTEP_LOW].frequency, &speedstep_freqs[SPEEDSTEP_HIGH].frequency, &get_freqs->policy->cpuinfo.transition_latency, &speedstep_set_state); } |
1da177e4c Linux-2.6.12-rc2 |
284 285 286 |
static int speedstep_cpu_init(struct cpufreq_policy *policy) { |
4b1576832 cpufreq: speedste... |
287 |
unsigned int policy_cpu; |
394122ab1 [CPUFREQ] cpumask... |
288 |
struct get_freqs gf; |
1da177e4c Linux-2.6.12-rc2 |
289 290 291 |
/* only run on CPU to be set, or on its sibling */ #ifdef CONFIG_SMP |
265ea6248 speedstep-ich: Re... |
292 |
cpumask_copy(policy->cpus, topology_sibling_cpumask(policy->cpu)); |
1da177e4c Linux-2.6.12-rc2 |
293 |
#endif |
394122ab1 [CPUFREQ] cpumask... |
294 |
policy_cpu = cpumask_any_and(policy->cpus, cpu_online_mask); |
1da177e4c Linux-2.6.12-rc2 |
295 |
|
1a10760c9 [CPUFREQ] Measure... |
296 |
/* detect low and high frequency and transition latency */ |
394122ab1 [CPUFREQ] cpumask... |
297 298 299 300 |
gf.policy = policy; smp_call_function_single(policy_cpu, get_freqs_on_cpu, &gf, 1); if (gf.ret) return gf.ret; |
1da177e4c Linux-2.6.12-rc2 |
301 |
|
5f3a2d39b cpufreq: speedste... |
302 |
return cpufreq_table_validate_and_show(policy, speedstep_freqs); |
1da177e4c Linux-2.6.12-rc2 |
303 |
} |
221dee285 Revert "[CPUFREQ]... |
304 |
static struct cpufreq_driver speedstep_driver = { |
1da177e4c Linux-2.6.12-rc2 |
305 |
.name = "speedstep-ich", |
3be1394a6 cpufreq: speedste... |
306 |
.verify = cpufreq_generic_frequency_table_verify, |
9c0ebcf78 cpufreq: Implemen... |
307 |
.target_index = speedstep_target, |
1da177e4c Linux-2.6.12-rc2 |
308 |
.init = speedstep_cpu_init, |
1da177e4c Linux-2.6.12-rc2 |
309 |
.get = speedstep_get, |
3be1394a6 cpufreq: speedste... |
310 |
.attr = cpufreq_generic_attr, |
1da177e4c Linux-2.6.12-rc2 |
311 |
}; |
fa8031aef cpufreq: Add supp... |
312 313 314 315 316 317 318 319 320 321 |
static const struct x86_cpu_id ss_smi_ids[] = { { X86_VENDOR_INTEL, 6, 0xb, }, { X86_VENDOR_INTEL, 6, 0x8, }, { X86_VENDOR_INTEL, 15, 2 }, {} }; #if 0 /* Autoload or not? Do not for now. */ MODULE_DEVICE_TABLE(x86cpu, ss_smi_ids); #endif |
1da177e4c Linux-2.6.12-rc2 |
322 323 324 325 326 327 328 329 330 331 |
/** * speedstep_init - initializes the SpeedStep CPUFreq driver * * Initializes the SpeedStep support. Returns -ENODEV on unsupported * devices, -EINVAL on problems during initiatization, and zero on * success. */ static int __init speedstep_init(void) { |
fa8031aef cpufreq: Add supp... |
332 333 |
if (!x86_match_cpu(ss_smi_ids)) return -ENODEV; |
1da177e4c Linux-2.6.12-rc2 |
334 335 336 |
/* detect processor */ speedstep_processor = speedstep_detect_processor(); if (!speedstep_processor) { |
2d06d8c49 [CPUFREQ] use dyn... |
337 |
pr_debug("Intel(R) SpeedStep(TM) capable processor " |
bbfebd665 [CPUFREQ] checkpa... |
338 339 |
"not found "); |
1da177e4c Linux-2.6.12-rc2 |
340 341 342 343 344 |
return -ENODEV; } /* detect chipset */ if (!speedstep_detect_chipset()) { |
2d06d8c49 [CPUFREQ] use dyn... |
345 |
pr_debug("Intel(R) SpeedStep(TM) for this chipset not " |
bbfebd665 [CPUFREQ] checkpa... |
346 347 |
"(yet) available. "); |
1da177e4c Linux-2.6.12-rc2 |
348 349 350 351 352 353 354 355 |
return -ENODEV; } /* activate speedstep support */ if (speedstep_activate()) { pci_dev_put(speedstep_chipset_dev); return -EINVAL; } |
9a7d82a89 [CPUFREQ] Move PM... |
356 357 |
if (speedstep_find_register()) return -ENODEV; |
1da177e4c Linux-2.6.12-rc2 |
358 359 360 361 362 363 364 365 366 367 368 369 370 371 |
return cpufreq_register_driver(&speedstep_driver); } /** * speedstep_exit - unregisters SpeedStep support * * Unregisters SpeedStep support. */ static void __exit speedstep_exit(void) { pci_dev_put(speedstep_chipset_dev); cpufreq_unregister_driver(&speedstep_driver); } |
d5e80b4b1 Update/Remove soo... |
372 |
MODULE_AUTHOR("Dave Jones, Dominik Brodowski <linux@brodo.de>"); |
bbfebd665 [CPUFREQ] checkpa... |
373 374 375 |
MODULE_DESCRIPTION("Speedstep driver for Intel mobile processors on chipsets " "with ICH-M southbridges."); MODULE_LICENSE("GPL"); |
1da177e4c Linux-2.6.12-rc2 |
376 377 378 |
module_init(speedstep_init); module_exit(speedstep_exit); |