/* * pcc-cpufreq.c - Processor Clocking Control firmware cpufreq interface * * Copyright (C) 2009 Red Hat, Matthew Garrett <mjg@redhat.com> * Copyright (C) 2009 Hewlett-Packard Development Company, L.P. * Nagananda Chumbalkar <nagananda.chumbalkar@hp.com> * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or NON * INFRINGEMENT. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 675 Mass Ave, Cambridge, MA 02139, USA. * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
/* Clear the input buffer - we are done with the current command */
memset_io((pcch_virt_addr + pcc_cpu_data->input_offset), 0, BUF_SZ);
status = ioread16(&pcch_hdr->status); if (status != CMD_COMPLETE) {
pr_debug("get: FAILED: for CPU %d, status is %d\n",
cpu, status); goto cmd_incomplete;
}
iowrite16(0, &pcch_hdr->status);
curr_freq = (((ioread32(&pcch_hdr->nominal) * (output_buffer & 0xff))
/ 100) * 1000);
pr_debug("get: SUCCESS: (virtual) output_offset for cpu %d is " "0x%p, contains a value of: 0x%x. Speed is: %d MHz\n",
cpu, (pcch_virt_addr + pcc_cpu_data->output_offset),
output_buffer, curr_freq);
freq_limit = (output_buffer >> 8) & 0xff; if (freq_limit != 0xff) {
pr_debug("get: frequency for cpu %d is being temporarily" " capped at %d\n", cpu, curr_freq);
}
cpu = policy->cpu;
pcc_cpu_data = per_cpu_ptr(pcc_cpu_info, cpu);
pr_debug("target: CPU %d should go to target freq: %d " "(virtual) input_offset is 0x%p\n",
cpu, target_freq,
(pcch_virt_addr + pcc_cpu_data->input_offset));
status = acpi_evaluate_object(*handle, "_OSC", &input, &output); if (ACPI_FAILURE(status)) return -ENODEV;
if (!output.length) return -ENODEV;
out_obj = output.pointer; if (out_obj->type != ACPI_TYPE_BUFFER) {
ret = -ENODEV; goto out_free;
}
errors = *((u32 *)out_obj->buffer.pointer) & ~(1 << 0); if (errors) {
ret = -ENODEV; goto out_free;
}
supported = *((u32 *)(out_obj->buffer.pointer + 4)); if (!(supported & 0x1)) {
ret = -ENODEV; goto out_free;
}
out_free:
kfree(output.pointer); return ret;
}
staticint __init pcc_cpufreq_evaluate(void)
{
acpi_status status; struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL}; struct pcc_memory_resource *mem_resource; struct pcc_register_resource *reg_resource; union acpi_object *out_obj, *member;
acpi_handle handle, osc_handle; int ret = 0;
status = acpi_get_handle(NULL, "\\_SB", &handle); if (ACPI_FAILURE(status)) return -ENODEV;
if (!acpi_has_method(handle, "PCCH")) return -ENODEV;
status = acpi_get_handle(handle, "_OSC", &osc_handle); if (ACPI_SUCCESS(status)) {
ret = pcc_cpufreq_do_osc(&osc_handle); if (ret)
pr_debug("probe: _OSC evaluation did not succeed\n"); /* Firmware's use of _OSC is optional */
ret = 0;
}
status = acpi_evaluate_object(handle, "PCCH", NULL, &output); if (ACPI_FAILURE(status)) return -ENODEV;
out_obj = output.pointer; if (out_obj->type != ACPI_TYPE_PACKAGE) {
ret = -ENODEV; goto out_free;
}
member = &out_obj->package.elements[0]; if (member->type != ACPI_TYPE_BUFFER) {
ret = -ENODEV; goto out_free;
}
if (mem_resource->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) {
ret = -ENODEV; goto out_free;
}
pcch_virt_addr = ioremap(mem_resource->minimum,
mem_resource->address_length); if (pcch_virt_addr == NULL) {
pr_debug("probe: could not map shared mem region\n");
ret = -ENOMEM; goto out_free;
}
pcch_hdr = pcch_virt_addr;
pr_debug("probe: min time between commands: %d us," " max time between commands: %d us," " nominal CPU frequency: %d MHz," " minimum CPU frequency: %d MHz," " minimum CPU frequency without throttling: %d MHz\n",
ioread32(&pcch_hdr->minimum_time),
ioread32(&pcch_hdr->maximum_time),
ioread32(&pcch_hdr->nominal),
ioread32(&pcch_hdr->throttled_frequency),
ioread32(&pcch_hdr->minimum_frequency));
member = &out_obj->package.elements[1]; if (member->type != ACPI_TYPE_BUFFER) {
ret = -ENODEV; goto pcch_free;
}
pr_debug("probe: doorbell: space_id is %d, bit_width is %d, " "bit_offset is %d, access_width is %d, address is 0x%llx\n",
doorbell.space_id, doorbell.bit_width, doorbell.bit_offset,
doorbell.access_width, reg_resource->address);
member = &out_obj->package.elements[2]; if (member->type != ACPI_TYPE_INTEGER) {
ret = -ENODEV; goto pcch_free;
}
doorbell_preserve = member->integer.value;
member = &out_obj->package.elements[3]; if (member->type != ACPI_TYPE_INTEGER) {
ret = -ENODEV; goto pcch_free;
}
staticint __init pcc_cpufreq_probe(struct platform_device *pdev)
{ int ret;
/* Skip initialization if another cpufreq driver is there. */ if (cpufreq_get_current_driver()) return -ENODEV;
if (acpi_disabled) return -ENODEV;
ret = pcc_cpufreq_evaluate(); if (ret) {
pr_debug("pcc_cpufreq_probe: PCCH evaluation failed\n"); return ret;
}
if (num_present_cpus() > 4) {
pcc_cpufreq_driver.flags |= CPUFREQ_NO_AUTO_DYNAMIC_SWITCHING;
pr_err("%s: Too many CPUs, dynamic performance scaling disabled\n",
__func__);
pr_err("%s: Try to enable another scaling driver through BIOS settings\n",
__func__);
pr_err("%s: and complain to the system vendor\n", __func__);
}
ret = cpufreq_register_driver(&pcc_cpufreq_driver);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.