brintos

brintos / linux-shallow public Read only

0
0
Text · 15.9 KiB · 771efbf Raw
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