640 lines · c
1/*2 * pcc-cpufreq.c - Processor Clocking Control firmware cpufreq interface3 *4 * Copyright (C) 2009 Red Hat, Matthew Garrett <mjg@redhat.com>5 * Copyright (C) 2009 Hewlett-Packard Development Company, L.P.6 * Nagananda Chumbalkar <nagananda.chumbalkar@hp.com>7 *8 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~9 *10 * This program is free software; you can redistribute it and/or modify11 * it under the terms of the GNU General Public License as published by12 * the Free Software Foundation; version 2 of the License.13 *14 * This program is distributed in the hope that it will be useful, but15 * WITHOUT ANY WARRANTY; without even the implied warranty of16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or NON17 * INFRINGEMENT. See the GNU General Public License for more details.18 *19 * You should have received a copy of the GNU General Public License along20 * with this program; if not, write to the Free Software Foundation, Inc.,21 * 675 Mass Ave, Cambridge, MA 02139, USA.22 *23 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~24 */25 26#include <linux/kernel.h>27#include <linux/module.h>28#include <linux/init.h>29#include <linux/smp.h>30#include <linux/sched.h>31#include <linux/cpufreq.h>32#include <linux/compiler.h>33#include <linux/slab.h>34#include <linux/platform_device.h>35 36#include <linux/acpi.h>37#include <linux/io.h>38#include <linux/spinlock.h>39#include <linux/uaccess.h>40 41#include <acpi/processor.h>42 43#define PCC_VERSION "1.10.00"44#define POLL_LOOPS 30045 46#define CMD_COMPLETE 0x147#define CMD_GET_FREQ 0x048#define CMD_SET_FREQ 0x149 50#define BUF_SZ 451 52struct pcc_register_resource {53 u8 descriptor;54 u16 length;55 u8 space_id;56 u8 bit_width;57 u8 bit_offset;58 u8 access_size;59 u64 address;60} __attribute__ ((packed));61 62struct pcc_memory_resource {63 u8 descriptor;64 u16 length;65 u8 space_id;66 u8 resource_usage;67 u8 type_specific;68 u64 granularity;69 u64 minimum;70 u64 maximum;71 u64 translation_offset;72 u64 address_length;73} __attribute__ ((packed));74 75static struct cpufreq_driver pcc_cpufreq_driver;76 77struct pcc_header {78 u32 signature;79 u16 length;80 u8 major;81 u8 minor;82 u32 features;83 u16 command;84 u16 status;85 u32 latency;86 u32 minimum_time;87 u32 maximum_time;88 u32 nominal;89 u32 throttled_frequency;90 u32 minimum_frequency;91};92 93static void __iomem *pcch_virt_addr;94static struct pcc_header __iomem *pcch_hdr;95 96static DEFINE_SPINLOCK(pcc_lock);97 98static struct acpi_generic_address doorbell;99 100static u64 doorbell_preserve;101static u64 doorbell_write;102 103static u8 OSC_UUID[16] = {0x9F, 0x2C, 0x9B, 0x63, 0x91, 0x70, 0x1f, 0x49,104 0xBB, 0x4F, 0xA5, 0x98, 0x2F, 0xA1, 0xB5, 0x46};105 106struct pcc_cpu {107 u32 input_offset;108 u32 output_offset;109};110 111static struct pcc_cpu __percpu *pcc_cpu_info;112 113static int pcc_cpufreq_verify(struct cpufreq_policy_data *policy)114{115 cpufreq_verify_within_cpu_limits(policy);116 return 0;117}118 119static inline void pcc_cmd(void)120{121 u64 doorbell_value;122 int i;123 124 acpi_read(&doorbell_value, &doorbell);125 acpi_write((doorbell_value & doorbell_preserve) | doorbell_write,126 &doorbell);127 128 for (i = 0; i < POLL_LOOPS; i++) {129 if (ioread16(&pcch_hdr->status) & CMD_COMPLETE)130 break;131 }132}133 134static inline void pcc_clear_mapping(void)135{136 if (pcch_virt_addr)137 iounmap(pcch_virt_addr);138 pcch_virt_addr = NULL;139}140 141static unsigned int pcc_get_freq(unsigned int cpu)142{143 struct pcc_cpu *pcc_cpu_data;144 unsigned int curr_freq;145 unsigned int freq_limit;146 u16 status;147 u32 input_buffer;148 u32 output_buffer;149 150 spin_lock(&pcc_lock);151 152 pr_debug("get: get_freq for CPU %d\n", cpu);153 pcc_cpu_data = per_cpu_ptr(pcc_cpu_info, cpu);154 155 input_buffer = 0x1;156 iowrite32(input_buffer,157 (pcch_virt_addr + pcc_cpu_data->input_offset));158 iowrite16(CMD_GET_FREQ, &pcch_hdr->command);159 160 pcc_cmd();161 162 output_buffer =163 ioread32(pcch_virt_addr + pcc_cpu_data->output_offset);164 165 /* Clear the input buffer - we are done with the current command */166 memset_io((pcch_virt_addr + pcc_cpu_data->input_offset), 0, BUF_SZ);167 168 status = ioread16(&pcch_hdr->status);169 if (status != CMD_COMPLETE) {170 pr_debug("get: FAILED: for CPU %d, status is %d\n",171 cpu, status);172 goto cmd_incomplete;173 }174 iowrite16(0, &pcch_hdr->status);175 curr_freq = (((ioread32(&pcch_hdr->nominal) * (output_buffer & 0xff))176 / 100) * 1000);177 178 pr_debug("get: SUCCESS: (virtual) output_offset for cpu %d is "179 "0x%p, contains a value of: 0x%x. Speed is: %d MHz\n",180 cpu, (pcch_virt_addr + pcc_cpu_data->output_offset),181 output_buffer, curr_freq);182 183 freq_limit = (output_buffer >> 8) & 0xff;184 if (freq_limit != 0xff) {185 pr_debug("get: frequency for cpu %d is being temporarily"186 " capped at %d\n", cpu, curr_freq);187 }188 189 spin_unlock(&pcc_lock);190 return curr_freq;191 192cmd_incomplete:193 iowrite16(0, &pcch_hdr->status);194 spin_unlock(&pcc_lock);195 return 0;196}197 198static int pcc_cpufreq_target(struct cpufreq_policy *policy,199 unsigned int target_freq,200 unsigned int relation)201{202 struct pcc_cpu *pcc_cpu_data;203 struct cpufreq_freqs freqs;204 u16 status;205 u32 input_buffer;206 int cpu;207 208 cpu = policy->cpu;209 pcc_cpu_data = per_cpu_ptr(pcc_cpu_info, cpu);210 211 pr_debug("target: CPU %d should go to target freq: %d "212 "(virtual) input_offset is 0x%p\n",213 cpu, target_freq,214 (pcch_virt_addr + pcc_cpu_data->input_offset));215 216 freqs.old = policy->cur;217 freqs.new = target_freq;218 cpufreq_freq_transition_begin(policy, &freqs);219 spin_lock(&pcc_lock);220 221 input_buffer = 0x1 | (((target_freq * 100)222 / (ioread32(&pcch_hdr->nominal) * 1000)) << 8);223 iowrite32(input_buffer,224 (pcch_virt_addr + pcc_cpu_data->input_offset));225 iowrite16(CMD_SET_FREQ, &pcch_hdr->command);226 227 pcc_cmd();228 229 /* Clear the input buffer - we are done with the current command */230 memset_io((pcch_virt_addr + pcc_cpu_data->input_offset), 0, BUF_SZ);231 232 status = ioread16(&pcch_hdr->status);233 iowrite16(0, &pcch_hdr->status);234 235 spin_unlock(&pcc_lock);236 cpufreq_freq_transition_end(policy, &freqs, status != CMD_COMPLETE);237 238 if (status != CMD_COMPLETE) {239 pr_debug("target: FAILED for cpu %d, with status: 0x%x\n",240 cpu, status);241 return -EINVAL;242 }243 244 pr_debug("target: was SUCCESSFUL for cpu %d\n", cpu);245 246 return 0;247}248 249static int pcc_get_offset(int cpu)250{251 acpi_status status;252 struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};253 union acpi_object *pccp, *offset;254 struct pcc_cpu *pcc_cpu_data;255 struct acpi_processor *pr;256 int ret = 0;257 258 pr = per_cpu(processors, cpu);259 pcc_cpu_data = per_cpu_ptr(pcc_cpu_info, cpu);260 261 if (!pr)262 return -ENODEV;263 264 status = acpi_evaluate_object(pr->handle, "PCCP", NULL, &buffer);265 if (ACPI_FAILURE(status))266 return -ENODEV;267 268 pccp = buffer.pointer;269 if (!pccp || pccp->type != ACPI_TYPE_PACKAGE) {270 ret = -ENODEV;271 goto out_free;272 }273 274 offset = &(pccp->package.elements[0]);275 if (!offset || offset->type != ACPI_TYPE_INTEGER) {276 ret = -ENODEV;277 goto out_free;278 }279 280 pcc_cpu_data->input_offset = offset->integer.value;281 282 offset = &(pccp->package.elements[1]);283 if (!offset || offset->type != ACPI_TYPE_INTEGER) {284 ret = -ENODEV;285 goto out_free;286 }287 288 pcc_cpu_data->output_offset = offset->integer.value;289 290 memset_io((pcch_virt_addr + pcc_cpu_data->input_offset), 0, BUF_SZ);291 memset_io((pcch_virt_addr + pcc_cpu_data->output_offset), 0, BUF_SZ);292 293 pr_debug("pcc_get_offset: for CPU %d: pcc_cpu_data "294 "input_offset: 0x%x, pcc_cpu_data output_offset: 0x%x\n",295 cpu, pcc_cpu_data->input_offset, pcc_cpu_data->output_offset);296out_free:297 kfree(buffer.pointer);298 return ret;299}300 301static int __init pcc_cpufreq_do_osc(acpi_handle *handle)302{303 acpi_status status;304 struct acpi_object_list input;305 struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL};306 union acpi_object in_params[4];307 union acpi_object *out_obj;308 u32 capabilities[2];309 u32 errors;310 u32 supported;311 int ret = 0;312 313 input.count = 4;314 input.pointer = in_params;315 in_params[0].type = ACPI_TYPE_BUFFER;316 in_params[0].buffer.length = 16;317 in_params[0].buffer.pointer = OSC_UUID;318 in_params[1].type = ACPI_TYPE_INTEGER;319 in_params[1].integer.value = 1;320 in_params[2].type = ACPI_TYPE_INTEGER;321 in_params[2].integer.value = 2;322 in_params[3].type = ACPI_TYPE_BUFFER;323 in_params[3].buffer.length = 8;324 in_params[3].buffer.pointer = (u8 *)&capabilities;325 326 capabilities[0] = OSC_QUERY_ENABLE;327 capabilities[1] = 0x1;328 329 status = acpi_evaluate_object(*handle, "_OSC", &input, &output);330 if (ACPI_FAILURE(status))331 return -ENODEV;332 333 if (!output.length)334 return -ENODEV;335 336 out_obj = output.pointer;337 if (out_obj->type != ACPI_TYPE_BUFFER) {338 ret = -ENODEV;339 goto out_free;340 }341 342 errors = *((u32 *)out_obj->buffer.pointer) & ~(1 << 0);343 if (errors) {344 ret = -ENODEV;345 goto out_free;346 }347 348 supported = *((u32 *)(out_obj->buffer.pointer + 4));349 if (!(supported & 0x1)) {350 ret = -ENODEV;351 goto out_free;352 }353 354 kfree(output.pointer);355 capabilities[0] = 0x0;356 capabilities[1] = 0x1;357 358 status = acpi_evaluate_object(*handle, "_OSC", &input, &output);359 if (ACPI_FAILURE(status))360 return -ENODEV;361 362 if (!output.length)363 return -ENODEV;364 365 out_obj = output.pointer;366 if (out_obj->type != ACPI_TYPE_BUFFER) {367 ret = -ENODEV;368 goto out_free;369 }370 371 errors = *((u32 *)out_obj->buffer.pointer) & ~(1 << 0);372 if (errors) {373 ret = -ENODEV;374 goto out_free;375 }376 377 supported = *((u32 *)(out_obj->buffer.pointer + 4));378 if (!(supported & 0x1)) {379 ret = -ENODEV;380 goto out_free;381 }382 383out_free:384 kfree(output.pointer);385 return ret;386}387 388static int __init pcc_cpufreq_evaluate(void)389{390 acpi_status status;391 struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL};392 struct pcc_memory_resource *mem_resource;393 struct pcc_register_resource *reg_resource;394 union acpi_object *out_obj, *member;395 acpi_handle handle, osc_handle;396 int ret = 0;397 398 status = acpi_get_handle(NULL, "\\_SB", &handle);399 if (ACPI_FAILURE(status))400 return -ENODEV;401 402 if (!acpi_has_method(handle, "PCCH"))403 return -ENODEV;404 405 status = acpi_get_handle(handle, "_OSC", &osc_handle);406 if (ACPI_SUCCESS(status)) {407 ret = pcc_cpufreq_do_osc(&osc_handle);408 if (ret)409 pr_debug("probe: _OSC evaluation did not succeed\n");410 /* Firmware's use of _OSC is optional */411 ret = 0;412 }413 414 status = acpi_evaluate_object(handle, "PCCH", NULL, &output);415 if (ACPI_FAILURE(status))416 return -ENODEV;417 418 out_obj = output.pointer;419 if (out_obj->type != ACPI_TYPE_PACKAGE) {420 ret = -ENODEV;421 goto out_free;422 }423 424 member = &out_obj->package.elements[0];425 if (member->type != ACPI_TYPE_BUFFER) {426 ret = -ENODEV;427 goto out_free;428 }429 430 mem_resource = (struct pcc_memory_resource *)member->buffer.pointer;431 432 pr_debug("probe: mem_resource descriptor: 0x%x,"433 " length: %d, space_id: %d, resource_usage: %d,"434 " type_specific: %d, granularity: 0x%llx,"435 " minimum: 0x%llx, maximum: 0x%llx,"436 " translation_offset: 0x%llx, address_length: 0x%llx\n",437 mem_resource->descriptor, mem_resource->length,438 mem_resource->space_id, mem_resource->resource_usage,439 mem_resource->type_specific, mem_resource->granularity,440 mem_resource->minimum, mem_resource->maximum,441 mem_resource->translation_offset,442 mem_resource->address_length);443 444 if (mem_resource->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) {445 ret = -ENODEV;446 goto out_free;447 }448 449 pcch_virt_addr = ioremap(mem_resource->minimum,450 mem_resource->address_length);451 if (pcch_virt_addr == NULL) {452 pr_debug("probe: could not map shared mem region\n");453 ret = -ENOMEM;454 goto out_free;455 }456 pcch_hdr = pcch_virt_addr;457 458 pr_debug("probe: PCCH header (virtual) addr: 0x%p\n", pcch_hdr);459 pr_debug("probe: PCCH header is at physical address: 0x%llx,"460 " signature: 0x%x, length: %d bytes, major: %d, minor: %d,"461 " supported features: 0x%x, command field: 0x%x,"462 " status field: 0x%x, nominal latency: %d us\n",463 mem_resource->minimum, ioread32(&pcch_hdr->signature),464 ioread16(&pcch_hdr->length), ioread8(&pcch_hdr->major),465 ioread8(&pcch_hdr->minor), ioread32(&pcch_hdr->features),466 ioread16(&pcch_hdr->command), ioread16(&pcch_hdr->status),467 ioread32(&pcch_hdr->latency));468 469 pr_debug("probe: min time between commands: %d us,"470 " max time between commands: %d us,"471 " nominal CPU frequency: %d MHz,"472 " minimum CPU frequency: %d MHz,"473 " minimum CPU frequency without throttling: %d MHz\n",474 ioread32(&pcch_hdr->minimum_time),475 ioread32(&pcch_hdr->maximum_time),476 ioread32(&pcch_hdr->nominal),477 ioread32(&pcch_hdr->throttled_frequency),478 ioread32(&pcch_hdr->minimum_frequency));479 480 member = &out_obj->package.elements[1];481 if (member->type != ACPI_TYPE_BUFFER) {482 ret = -ENODEV;483 goto pcch_free;484 }485 486 reg_resource = (struct pcc_register_resource *)member->buffer.pointer;487 488 doorbell.space_id = reg_resource->space_id;489 doorbell.bit_width = reg_resource->bit_width;490 doorbell.bit_offset = reg_resource->bit_offset;491 doorbell.access_width = 4;492 doorbell.address = reg_resource->address;493 494 pr_debug("probe: doorbell: space_id is %d, bit_width is %d, "495 "bit_offset is %d, access_width is %d, address is 0x%llx\n",496 doorbell.space_id, doorbell.bit_width, doorbell.bit_offset,497 doorbell.access_width, reg_resource->address);498 499 member = &out_obj->package.elements[2];500 if (member->type != ACPI_TYPE_INTEGER) {501 ret = -ENODEV;502 goto pcch_free;503 }504 505 doorbell_preserve = member->integer.value;506 507 member = &out_obj->package.elements[3];508 if (member->type != ACPI_TYPE_INTEGER) {509 ret = -ENODEV;510 goto pcch_free;511 }512 513 doorbell_write = member->integer.value;514 515 pr_debug("probe: doorbell_preserve: 0x%llx,"516 " doorbell_write: 0x%llx\n",517 doorbell_preserve, doorbell_write);518 519 pcc_cpu_info = alloc_percpu(struct pcc_cpu);520 if (!pcc_cpu_info) {521 ret = -ENOMEM;522 goto pcch_free;523 }524 525 printk(KERN_DEBUG "pcc-cpufreq: (v%s) driver loaded with frequency"526 " limits: %d MHz, %d MHz\n", PCC_VERSION,527 ioread32(&pcch_hdr->minimum_frequency),528 ioread32(&pcch_hdr->nominal));529 kfree(output.pointer);530 return ret;531pcch_free:532 pcc_clear_mapping();533out_free:534 kfree(output.pointer);535 return ret;536}537 538static int pcc_cpufreq_cpu_init(struct cpufreq_policy *policy)539{540 unsigned int cpu = policy->cpu;541 unsigned int result = 0;542 543 if (!pcch_virt_addr) {544 result = -1;545 goto out;546 }547 548 result = pcc_get_offset(cpu);549 if (result) {550 pr_debug("init: PCCP evaluation failed\n");551 goto out;552 }553 554 policy->max = policy->cpuinfo.max_freq =555 ioread32(&pcch_hdr->nominal) * 1000;556 policy->min = policy->cpuinfo.min_freq =557 ioread32(&pcch_hdr->minimum_frequency) * 1000;558 559 pr_debug("init: policy->max is %d, policy->min is %d\n",560 policy->max, policy->min);561out:562 return result;563}564 565static struct cpufreq_driver pcc_cpufreq_driver = {566 .flags = CPUFREQ_CONST_LOOPS,567 .get = pcc_get_freq,568 .verify = pcc_cpufreq_verify,569 .target = pcc_cpufreq_target,570 .init = pcc_cpufreq_cpu_init,571 .name = "pcc-cpufreq",572};573 574static int __init pcc_cpufreq_probe(struct platform_device *pdev)575{576 int ret;577 578 /* Skip initialization if another cpufreq driver is there. */579 if (cpufreq_get_current_driver())580 return -ENODEV;581 582 if (acpi_disabled)583 return -ENODEV;584 585 ret = pcc_cpufreq_evaluate();586 if (ret) {587 pr_debug("pcc_cpufreq_probe: PCCH evaluation failed\n");588 return ret;589 }590 591 if (num_present_cpus() > 4) {592 pcc_cpufreq_driver.flags |= CPUFREQ_NO_AUTO_DYNAMIC_SWITCHING;593 pr_err("%s: Too many CPUs, dynamic performance scaling disabled\n",594 __func__);595 pr_err("%s: Try to enable another scaling driver through BIOS settings\n",596 __func__);597 pr_err("%s: and complain to the system vendor\n", __func__);598 }599 600 ret = cpufreq_register_driver(&pcc_cpufreq_driver);601 602 return ret;603}604 605static void pcc_cpufreq_remove(struct platform_device *pdev)606{607 cpufreq_unregister_driver(&pcc_cpufreq_driver);608 609 pcc_clear_mapping();610 611 free_percpu(pcc_cpu_info);612}613 614static struct platform_driver pcc_cpufreq_platdrv = {615 .driver = {616 .name = "pcc-cpufreq",617 },618 .remove_new = pcc_cpufreq_remove,619};620 621static int __init pcc_cpufreq_init(void)622{623 return platform_driver_probe(&pcc_cpufreq_platdrv, pcc_cpufreq_probe);624}625 626static void __exit pcc_cpufreq_exit(void)627{628 platform_driver_unregister(&pcc_cpufreq_platdrv);629}630 631MODULE_ALIAS("platform:pcc-cpufreq");632 633MODULE_AUTHOR("Matthew Garrett, Naga Chumbalkar");634MODULE_VERSION(PCC_VERSION);635MODULE_DESCRIPTION("Processor Clocking Control interface driver");636MODULE_LICENSE("GPL");637 638late_initcall(pcc_cpufreq_init);639module_exit(pcc_cpufreq_exit);640