/** * acpi_cppc_processor_probe - Search for per CPU _CPC objects. * @pr: Ptr to acpi_processor containing this CPUs logical Id. * * Return: 0 for success or negative value for err. */ int acpi_cppc_processor_probe(struct acpi_processor *pr) { struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL}; union acpi_object *out_obj, *cpc_obj; struct cpc_desc *cpc_ptr; struct cpc_reg *gas_t; acpi_handle handle = pr->handle; unsigned int num_ent, i, cpc_rev; acpi_status status; int ret = -EFAULT; /* Parse the ACPI _CPC table for this cpu. */ status = acpi_evaluate_object_typed(handle, "_CPC", NULL, &output, ACPI_TYPE_PACKAGE); if (ACPI_FAILURE(status)) { ret = -ENODEV; goto out_buf_free; } out_obj = (union acpi_object *) output.pointer; cpc_ptr = kzalloc(sizeof(struct cpc_desc), GFP_KERNEL); if (!cpc_ptr) { ret = -ENOMEM; goto out_buf_free; } /* First entry is NumEntries. */ cpc_obj = &out_obj->package.elements[0]; if (cpc_obj->type == ACPI_TYPE_INTEGER) { num_ent = cpc_obj->integer.value; } else { pr_debug("Unexpected entry type(%d) for NumEntries\n", cpc_obj->type); goto out_free; } /* Only support CPPCv2. Bail otherwise. */ if (num_ent != CPPC_NUM_ENT) { pr_debug("Firmware exports %d entries. Expected: %d\n", num_ent, CPPC_NUM_ENT); goto out_free; } /* Second entry should be revision. */ cpc_obj = &out_obj->package.elements[1]; if (cpc_obj->type == ACPI_TYPE_INTEGER) { cpc_rev = cpc_obj->integer.value; } else { pr_debug("Unexpected entry type(%d) for Revision\n", cpc_obj->type); goto out_free; } if (cpc_rev != CPPC_REV) { pr_debug("Firmware exports revision:%d. Expected:%d\n", cpc_rev, CPPC_REV); goto out_free; } /* Iterate through remaining entries in _CPC */ for (i = 2; i < num_ent; i++) { cpc_obj = &out_obj->package.elements[i]; if (cpc_obj->type == ACPI_TYPE_INTEGER) { cpc_ptr->cpc_regs[i-2].type = ACPI_TYPE_INTEGER; cpc_ptr->cpc_regs[i-2].cpc_entry.int_value = cpc_obj->integer.value; } else if (cpc_obj->type == ACPI_TYPE_BUFFER) { gas_t = (struct cpc_reg *) cpc_obj->buffer.pointer; /* * The PCC Subspace index is encoded inside * the CPC table entries. The same PCC index * will be used for all the PCC entries, * so extract it only once. */ if (gas_t->space_id == ACPI_ADR_SPACE_PLATFORM_COMM) { if (pcc_subspace_idx < 0) pcc_subspace_idx = gas_t->access_width; else if (pcc_subspace_idx != gas_t->access_width) { pr_debug("Mismatched PCC ids.\n"); goto out_free; } } else if (gas_t->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) { /* Support only PCC and SYS MEM type regs */ pr_debug("Unsupported register type: %d\n", gas_t->space_id); goto out_free; } cpc_ptr->cpc_regs[i-2].type = ACPI_TYPE_BUFFER; memcpy(&cpc_ptr->cpc_regs[i-2].cpc_entry.reg, gas_t, sizeof(*gas_t)); } else { pr_debug("Err in entry:%d in CPC table of CPU:%d \n", i, pr->id); goto out_free; } } /* Store CPU Logical ID */ cpc_ptr->cpu_id = pr->id; /* Parse PSD data for this CPU */ ret = acpi_get_psd(cpc_ptr, handle); if (ret) goto out_free; /* Register PCC channel once for all CPUs. */ if (!pcc_channel_acquired) { ret = register_pcc_channel(pcc_subspace_idx); if (ret) goto out_free; } /* Plug PSD data into this CPUs CPC descriptor. */ per_cpu(cpc_desc_ptr, pr->id) = cpc_ptr; /* Everything looks okay */ pr_debug("Parsed CPC struct for CPU: %d\n", pr->id); kfree(output.pointer); return 0; out_free: kfree(cpc_ptr); out_buf_free: kfree(output.pointer); return ret; }
/** * acpi_cppc_processor_probe - Search for per CPU _CPC objects. * @pr: Ptr to acpi_processor containing this CPUs logical Id. * * Return: 0 for success or negative value for err. */ int acpi_cppc_processor_probe(struct acpi_processor *pr) { struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL}; union acpi_object *out_obj, *cpc_obj; struct cpc_desc *cpc_ptr; struct cpc_reg *gas_t; struct device *cpu_dev; acpi_handle handle = pr->handle; unsigned int num_ent, i, cpc_rev; int pcc_subspace_id = -1; acpi_status status; int ret = -EFAULT; /* Parse the ACPI _CPC table for this cpu. */ status = acpi_evaluate_object_typed(handle, "_CPC", NULL, &output, ACPI_TYPE_PACKAGE); if (ACPI_FAILURE(status)) { ret = -ENODEV; goto out_buf_free; } out_obj = (union acpi_object *) output.pointer; cpc_ptr = kzalloc(sizeof(struct cpc_desc), GFP_KERNEL); if (!cpc_ptr) { ret = -ENOMEM; goto out_buf_free; } /* First entry is NumEntries. */ cpc_obj = &out_obj->package.elements[0]; if (cpc_obj->type == ACPI_TYPE_INTEGER) { num_ent = cpc_obj->integer.value; } else { pr_debug("Unexpected entry type(%d) for NumEntries\n", cpc_obj->type); goto out_free; } /* Only support CPPCv2. Bail otherwise. */ if (num_ent != CPPC_NUM_ENT) { pr_debug("Firmware exports %d entries. Expected: %d\n", num_ent, CPPC_NUM_ENT); goto out_free; } cpc_ptr->num_entries = num_ent; /* Second entry should be revision. */ cpc_obj = &out_obj->package.elements[1]; if (cpc_obj->type == ACPI_TYPE_INTEGER) { cpc_rev = cpc_obj->integer.value; } else { pr_debug("Unexpected entry type(%d) for Revision\n", cpc_obj->type); goto out_free; } if (cpc_rev != CPPC_REV) { pr_debug("Firmware exports revision:%d. Expected:%d\n", cpc_rev, CPPC_REV); goto out_free; } /* Iterate through remaining entries in _CPC */ for (i = 2; i < num_ent; i++) { cpc_obj = &out_obj->package.elements[i]; if (cpc_obj->type == ACPI_TYPE_INTEGER) { cpc_ptr->cpc_regs[i-2].type = ACPI_TYPE_INTEGER; cpc_ptr->cpc_regs[i-2].cpc_entry.int_value = cpc_obj->integer.value; } else if (cpc_obj->type == ACPI_TYPE_BUFFER) { gas_t = (struct cpc_reg *) cpc_obj->buffer.pointer; /* * The PCC Subspace index is encoded inside * the CPC table entries. The same PCC index * will be used for all the PCC entries, * so extract it only once. */ if (gas_t->space_id == ACPI_ADR_SPACE_PLATFORM_COMM) { if (pcc_subspace_id < 0) { pcc_subspace_id = gas_t->access_width; if (pcc_data_alloc(pcc_subspace_id)) goto out_free; } else if (pcc_subspace_id != gas_t->access_width) { pr_debug("Mismatched PCC ids.\n"); goto out_free; } } else if (gas_t->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) { if (gas_t->address) { void __iomem *addr; addr = ioremap(gas_t->address, gas_t->bit_width/8); if (!addr) goto out_free; cpc_ptr->cpc_regs[i-2].sys_mem_vaddr = addr; } } else { if (gas_t->space_id != ACPI_ADR_SPACE_FIXED_HARDWARE || !cpc_ffh_supported()) { /* Support only PCC ,SYS MEM and FFH type regs */ pr_debug("Unsupported register type: %d\n", gas_t->space_id); goto out_free; } } cpc_ptr->cpc_regs[i-2].type = ACPI_TYPE_BUFFER; memcpy(&cpc_ptr->cpc_regs[i-2].cpc_entry.reg, gas_t, sizeof(*gas_t)); } else { pr_debug("Err in entry:%d in CPC table of CPU:%d \n", i, pr->id); goto out_free; } } per_cpu(cpu_pcc_subspace_idx, pr->id) = pcc_subspace_id; /* Store CPU Logical ID */ cpc_ptr->cpu_id = pr->id; /* Parse PSD data for this CPU */ ret = acpi_get_psd(cpc_ptr, handle); if (ret) goto out_free; /* Register PCC channel once for all PCC subspace id. */ if (pcc_subspace_id >= 0 && !pcc_data[pcc_subspace_id]->pcc_channel_acquired) { ret = register_pcc_channel(pcc_subspace_id); if (ret) goto out_free; init_rwsem(&pcc_data[pcc_subspace_id]->pcc_lock); init_waitqueue_head(&pcc_data[pcc_subspace_id]->pcc_write_wait_q); } /* Everything looks okay */ pr_debug("Parsed CPC struct for CPU: %d\n", pr->id); /* Add per logical CPU nodes for reading its feedback counters. */ cpu_dev = get_cpu_device(pr->id); if (!cpu_dev) { ret = -EINVAL; goto out_free; } /* Plug PSD data into this CPUs CPC descriptor. */ per_cpu(cpc_desc_ptr, pr->id) = cpc_ptr; ret = kobject_init_and_add(&cpc_ptr->kobj, &cppc_ktype, &cpu_dev->kobj, "acpi_cppc"); if (ret) { per_cpu(cpc_desc_ptr, pr->id) = NULL; goto out_free; } kfree(output.pointer); return 0; out_free: /* Free all the mapped sys mem areas for this CPU */ for (i = 2; i < cpc_ptr->num_entries; i++) { void __iomem *addr = cpc_ptr->cpc_regs[i-2].sys_mem_vaddr; if (addr) iounmap(addr); } kfree(cpc_ptr); out_buf_free: kfree(output.pointer); return ret; }