in pcc-cpufreq.c [387:535]
static int __init pcc_cpufreq_probe(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;
}
mem_resource = (struct pcc_memory_resource *)member->buffer.pointer;
pr_debug("probe: mem_resource descriptor: 0x%x,"
" length: %d, space_id: %d, resource_usage: %d,"
" type_specific: %d, granularity: 0x%llx,"
" minimum: 0x%llx, maximum: 0x%llx,"
" translation_offset: 0x%llx, address_length: 0x%llx\n",
mem_resource->descriptor, mem_resource->length,
mem_resource->space_id, mem_resource->resource_usage,
mem_resource->type_specific, mem_resource->granularity,
mem_resource->minimum, mem_resource->maximum,
mem_resource->translation_offset,
mem_resource->address_length);
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: PCCH header (virtual) addr: 0x%p\n", pcch_hdr);
pr_debug("probe: PCCH header is at physical address: 0x%llx,"
" signature: 0x%x, length: %d bytes, major: %d, minor: %d,"
" supported features: 0x%x, command field: 0x%x,"
" status field: 0x%x, nominal latency: %d us\n",
mem_resource->minimum, ioread32(&pcch_hdr->signature),
ioread16(&pcch_hdr->length), ioread8(&pcch_hdr->major),
ioread8(&pcch_hdr->minor), ioread32(&pcch_hdr->features),
ioread16(&pcch_hdr->command), ioread16(&pcch_hdr->status),
ioread32(&pcch_hdr->latency));
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;
}
reg_resource = (struct pcc_register_resource *)member->buffer.pointer;
doorbell.space_id = reg_resource->space_id;
doorbell.bit_width = reg_resource->bit_width;
doorbell.bit_offset = reg_resource->bit_offset;
doorbell.access_width = 4;
doorbell.address = reg_resource->address;
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;
}
doorbell_write = member->integer.value;
pr_debug("probe: doorbell_preserve: 0x%llx,"
" doorbell_write: 0x%llx\n",
doorbell_preserve, doorbell_write);
pcc_cpu_info = alloc_percpu(struct pcc_cpu);
if (!pcc_cpu_info) {
ret = -ENOMEM;
goto pcch_free;
}
printk(KERN_DEBUG "pcc-cpufreq: (v%s) driver loaded with frequency"
" limits: %d MHz, %d MHz\n", PCC_VERSION,
ioread32(&pcch_hdr->minimum_frequency),
ioread32(&pcch_hdr->nominal));
kfree(output.pointer);
return ret;
pcch_free:
pcc_clear_mapping();
out_free:
kfree(output.pointer);
return ret;
}