1/* 2 * arch/ia64/kernel/cpufreq/acpi-cpufreq.c 3 * This file provides the ACPI based P-state support. This 4 * module works with generic cpufreq infrastructure. Most of 5 * the code is based on i386 version 6 * (arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c) 7 * 8 * Copyright (C) 2005 Intel Corp 9 * Venkatesh Pallipadi <venkatesh.pallipadi@intel.com> 10 */ 11 12#include <linux/kernel.h> 13#include <linux/module.h> 14#include <linux/init.h> 15#include <linux/cpufreq.h> 16#include <linux/proc_fs.h> 17#include <linux/seq_file.h> 18#include <asm/io.h> 19#include <asm/uaccess.h> 20#include <asm/pal.h> 21 22#include <linux/acpi.h> 23#include <acpi/processor.h> 24 25#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "acpi-cpufreq", msg) 26 27MODULE_AUTHOR("Venkatesh Pallipadi"); 28MODULE_DESCRIPTION("ACPI Processor P-States Driver"); 29MODULE_LICENSE("GPL"); 30 31 32struct cpufreq_acpi_io { 33 struct acpi_processor_performance acpi_data; 34 struct cpufreq_frequency_table *freq_table; 35 unsigned int resume; 36}; 37 38static struct cpufreq_acpi_io *acpi_io_data[NR_CPUS]; 39 40static struct cpufreq_driver acpi_cpufreq_driver; 41 42 43static int 44processor_set_pstate ( 45 u32 value) 46{ 47 s64 retval; 48 49 dprintk("processor_set_pstate\n"); 50 51 retval = ia64_pal_set_pstate((u64)value); 52 53 if (retval) { 54 dprintk("Failed to set freq to 0x%x, with error 0x%x\n", 55 value, retval); 56 return -ENODEV; 57 } 58 return (int)retval; 59} 60 61 62static int 63processor_get_pstate ( 64 u32 *value) 65{ 66 u64 pstate_index = 0; 67 s64 retval; 68 69 dprintk("processor_get_pstate\n"); 70 71 retval = ia64_pal_get_pstate(&pstate_index, 72 PAL_GET_PSTATE_TYPE_INSTANT); 73 *value = (u32) pstate_index; 74 75 if (retval) 76 dprintk("Failed to get current freq with " 77 "error 0x%x, idx 0x%x\n", retval, *value); 78 79 return (int)retval; 80} 81 82 83/* To be used only after data->acpi_data is initialized */ 84static unsigned 85extract_clock ( 86 struct cpufreq_acpi_io *data, 87 unsigned value, 88 unsigned int cpu) 89{ 90 unsigned long i; 91 92 dprintk("extract_clock\n"); 93 94 for (i = 0; i < data->acpi_data.state_count; i++) { 95 if (value == data->acpi_data.states[i].status) 96 return data->acpi_data.states[i].core_frequency; 97 } 98 return data->acpi_data.states[i-1].core_frequency; 99} 100 101 102static unsigned int 103processor_get_freq ( 104 struct cpufreq_acpi_io *data, 105 unsigned int cpu) 106{ 107 int ret = 0; 108 u32 value = 0; 109 cpumask_t saved_mask; 110 unsigned long clock_freq; 111 112 dprintk("processor_get_freq\n"); 113 114 saved_mask = current->cpus_allowed; 115 set_cpus_allowed(current, cpumask_of_cpu(cpu)); 116 if (smp_processor_id() != cpu) { 117 ret = -EAGAIN; 118 goto migrate_end; 119 } 120 121 /* processor_get_pstate gets the instantaneous frequency */ 122 ret = processor_get_pstate(&value); 123 124 if (ret) { 125 set_cpus_allowed(current, saved_mask); 126 printk(KERN_WARNING "get performance failed with error %d\n", 127 ret); 128 ret = -EAGAIN; 129 goto migrate_end; 130 } 131 clock_freq = extract_clock(data, value, cpu); 132 ret = (clock_freq*1000); 133 134migrate_end: 135 set_cpus_allowed(current, saved_mask); 136 return ret; 137} 138 139 140static int 141processor_set_freq ( 142 struct cpufreq_acpi_io *data, 143 unsigned int cpu, 144 int state) 145{ 146 int ret = 0; 147 u32 value = 0; 148 struct cpufreq_freqs cpufreq_freqs; 149 cpumask_t saved_mask; 150 int retval; 151 152 dprintk("processor_set_freq\n"); 153 154 saved_mask = current->cpus_allowed; 155 set_cpus_allowed(current, cpumask_of_cpu(cpu)); 156 if (smp_processor_id() != cpu) { 157 retval = -EAGAIN; 158 goto migrate_end; 159 } 160 161 if (state == data->acpi_data.state) { 162 if (unlikely(data->resume)) { 163 dprintk("Called after resume, resetting to P%d\n", state); 164 data->resume = 0; 165 } else { 166 dprintk("Already at target state (P%d)\n", state); 167 retval = 0; 168 goto migrate_end; 169 } 170 } 171 172 dprintk("Transitioning from P%d to P%d\n", 173 data->acpi_data.state, state); 174 175 /* cpufreq frequency struct */ 176 cpufreq_freqs.cpu = cpu; 177 cpufreq_freqs.old = data->freq_table[data->acpi_data.state].frequency; 178 cpufreq_freqs.new = data->freq_table[state].frequency; 179 180 /* notify cpufreq */ 181 cpufreq_notify_transition(&cpufreq_freqs, CPUFREQ_PRECHANGE); 182 183 /* 184 * First we write the target state's 'control' value to the 185 * control_register. 186 */ 187 188 value = (u32) data->acpi_data.states[state].control; 189 190 dprintk("Transitioning to state: 0x%08x\n", value); 191 192 ret = processor_set_pstate(value); 193 if (ret) { 194 unsigned int tmp = cpufreq_freqs.new; 195 cpufreq_notify_transition(&cpufreq_freqs, CPUFREQ_POSTCHANGE); 196 cpufreq_freqs.new = cpufreq_freqs.old; 197 cpufreq_freqs.old = tmp; 198 cpufreq_notify_transition(&cpufreq_freqs, CPUFREQ_PRECHANGE); 199 cpufreq_notify_transition(&cpufreq_freqs, CPUFREQ_POSTCHANGE); 200 printk(KERN_WARNING "Transition failed with error %d\n", ret); 201 retval = -ENODEV; 202 goto migrate_end; 203 } 204 205 cpufreq_notify_transition(&cpufreq_freqs, CPUFREQ_POSTCHANGE); 206 207 data->acpi_data.state = state; 208 209 retval = 0; 210 211migrate_end: 212 set_cpus_allowed(current, saved_mask); 213 return (retval); 214} 215 216 217static unsigned int 218acpi_cpufreq_get ( 219 unsigned int cpu) 220{ 221 struct cpufreq_acpi_io *data = acpi_io_data[cpu]; 222 223 dprintk("acpi_cpufreq_get\n"); 224 225 return processor_get_freq(data, cpu); 226} 227 228 229static int 230acpi_cpufreq_target ( 231 struct cpufreq_policy *policy, 232 unsigned int target_freq, 233 unsigned int relation) 234{ 235 struct cpufreq_acpi_io *data = acpi_io_data[policy->cpu]; 236 unsigned int next_state = 0; 237 unsigned int result = 0; 238 239 dprintk("acpi_cpufreq_setpolicy\n"); 240 241 result = cpufreq_frequency_table_target(policy, 242 data->freq_table, target_freq, relation, &next_state); 243 if (result) 244 return (result); 245 246 result = processor_set_freq(data, policy->cpu, next_state); 247 248 return (result); 249} 250 251 252static int 253acpi_cpufreq_verify ( 254 struct cpufreq_policy *policy) 255{ 256 unsigned int result = 0; 257 struct cpufreq_acpi_io *data = acpi_io_data[policy->cpu]; 258 259 dprintk("acpi_cpufreq_verify\n"); 260 261 result = cpufreq_frequency_table_verify(policy, 262 data->freq_table); 263 264 return (result); 265} 266 267 268static int 269acpi_cpufreq_cpu_init ( 270 struct cpufreq_policy *policy) 271{ 272 unsigned int i; 273 unsigned int cpu = policy->cpu; 274 struct cpufreq_acpi_io *data; 275 unsigned int result = 0; 276 277 dprintk("acpi_cpufreq_cpu_init\n"); 278 279 data = kzalloc(sizeof(struct cpufreq_acpi_io), GFP_KERNEL); 280 if (!data) 281 return (-ENOMEM); 282 283 acpi_io_data[cpu] = data; 284 285 result = acpi_processor_register_performance(&data->acpi_data, cpu); 286 287 if (result) 288 goto err_free; 289 290 /* capability check */ 291 if (data->acpi_data.state_count <= 1) { 292 dprintk("No P-States\n"); 293 result = -ENODEV; 294 goto err_unreg; 295 } 296 297 if ((data->acpi_data.control_register.space_id != 298 ACPI_ADR_SPACE_FIXED_HARDWARE) || 299 (data->acpi_data.status_register.space_id != 300 ACPI_ADR_SPACE_FIXED_HARDWARE)) { 301 dprintk("Unsupported address space [%d, %d]\n", 302 (u32) (data->acpi_data.control_register.space_id), 303 (u32) (data->acpi_data.status_register.space_id)); 304 result = -ENODEV; 305 goto err_unreg; 306 } 307 308 /* alloc freq_table */ 309 data->freq_table = kmalloc(sizeof(struct cpufreq_frequency_table) * 310 (data->acpi_data.state_count + 1), 311 GFP_KERNEL); 312 if (!data->freq_table) { 313 result = -ENOMEM; 314 goto err_unreg; 315 } 316 317 /* detect transition latency */ 318 policy->cpuinfo.transition_latency = 0; 319 for (i=0; i<data->acpi_data.state_count; i++) { 320 if ((data->acpi_data.states[i].transition_latency * 1000) > 321 policy->cpuinfo.transition_latency) { 322 policy->cpuinfo.transition_latency = 323 data->acpi_data.states[i].transition_latency * 1000; 324 } 325 } 326 policy->governor = CPUFREQ_DEFAULT_GOVERNOR; 327 328 policy->cur = processor_get_freq(data, policy->cpu); 329 330 /* table init */ 331 for (i = 0; i <= data->acpi_data.state_count; i++) 332 { 333 data->freq_table[i].index = i; 334 if (i < data->acpi_data.state_count) { 335 data->freq_table[i].frequency = 336 data->acpi_data.states[i].core_frequency * 1000; 337 } else { 338 data->freq_table[i].frequency = CPUFREQ_TABLE_END; 339 } 340 } 341 342 result = cpufreq_frequency_table_cpuinfo(policy, data->freq_table); 343 if (result) { 344 goto err_freqfree; 345 } 346 347 /* notify BIOS that we exist */ 348 acpi_processor_notify_smm(THIS_MODULE); 349 350 printk(KERN_INFO "acpi-cpufreq: CPU%u - ACPI performance management " 351 "activated.\n", cpu); 352 353 for (i = 0; i < data->acpi_data.state_count; i++) 354 dprintk(" %cP%d: %d MHz, %d mW, %d uS, %d uS, 0x%x 0x%x\n", 355 (i == data->acpi_data.state?'*':' '), i, 356 (u32) data->acpi_data.states[i].core_frequency, 357 (u32) data->acpi_data.states[i].power, 358 (u32) data->acpi_data.states[i].transition_latency, 359 (u32) data->acpi_data.states[i].bus_master_latency, 360 (u32) data->acpi_data.states[i].status, 361 (u32) data->acpi_data.states[i].control); 362 363 cpufreq_frequency_table_get_attr(data->freq_table, policy->cpu); 364 365 /* the first call to ->target() should result in us actually 366 * writing something to the appropriate registers. */ 367 data->resume = 1; 368 369 return (result); 370 371 err_freqfree: 372 kfree(data->freq_table); 373 err_unreg: 374 acpi_processor_unregister_performance(&data->acpi_data, cpu); 375 err_free: 376 kfree(data); 377 acpi_io_data[cpu] = NULL; 378 379 return (result); 380} 381 382 383static int 384acpi_cpufreq_cpu_exit ( 385 struct cpufreq_policy *policy) 386{ 387 struct cpufreq_acpi_io *data = acpi_io_data[policy->cpu]; 388 389 dprintk("acpi_cpufreq_cpu_exit\n"); 390 391 if (data) { 392 cpufreq_frequency_table_put_attr(policy->cpu); 393 acpi_io_data[policy->cpu] = NULL; 394 acpi_processor_unregister_performance(&data->acpi_data, 395 policy->cpu); 396 kfree(data); 397 } 398 399 return (0); 400} 401 402 403static struct freq_attr* acpi_cpufreq_attr[] = { 404 &cpufreq_freq_attr_scaling_available_freqs, 405 NULL, 406}; 407 408 409static struct cpufreq_driver acpi_cpufreq_driver = { 410 .verify = acpi_cpufreq_verify, 411 .target = acpi_cpufreq_target, 412 .get = acpi_cpufreq_get, 413 .init = acpi_cpufreq_cpu_init, 414 .exit = acpi_cpufreq_cpu_exit, 415 .name = "acpi-cpufreq", 416 .owner = THIS_MODULE, 417 .attr = acpi_cpufreq_attr, 418}; 419 420 421static int __init 422acpi_cpufreq_init (void) 423{ 424 dprintk("acpi_cpufreq_init\n"); 425 426 return cpufreq_register_driver(&acpi_cpufreq_driver); 427} 428 429 430static void __exit 431acpi_cpufreq_exit (void) 432{ 433 dprintk("acpi_cpufreq_exit\n"); 434 435 cpufreq_unregister_driver(&acpi_cpufreq_driver); 436 return; 437} 438 439 440late_initcall(acpi_cpufreq_init); 441module_exit(acpi_cpufreq_exit); 442