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