|
@@ -553,6 +553,234 @@ static void acpi_cpufreq_remove_file(struct acpi_processor *pr)
|
|
}
|
|
}
|
|
#endif /* CONFIG_X86_ACPI_CPUFREQ_PROC_INTF */
|
|
#endif /* CONFIG_X86_ACPI_CPUFREQ_PROC_INTF */
|
|
|
|
|
|
|
|
+static int acpi_processor_get_psd(struct acpi_processor *pr)
|
|
|
|
+{
|
|
|
|
+ int result = 0;
|
|
|
|
+ acpi_status status = AE_OK;
|
|
|
|
+ struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
|
|
|
|
+ struct acpi_buffer format = {sizeof("NNNNN"), "NNNNN"};
|
|
|
|
+ struct acpi_buffer state = {0, NULL};
|
|
|
|
+ union acpi_object *psd = NULL;
|
|
|
|
+ struct acpi_psd_package *pdomain;
|
|
|
|
+
|
|
|
|
+ ACPI_FUNCTION_TRACE("acpi_processor_get_psd");
|
|
|
|
+
|
|
|
|
+ status = acpi_evaluate_object(pr->handle, "_PSD", NULL, &buffer);
|
|
|
|
+ if (ACPI_FAILURE(status)) {
|
|
|
|
+ return_VALUE(-ENODEV);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ psd = (union acpi_object *) buffer.pointer;
|
|
|
|
+ if (!psd || (psd->type != ACPI_TYPE_PACKAGE)) {
|
|
|
|
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid _PSD data\n"));
|
|
|
|
+ result = -EFAULT;
|
|
|
|
+ goto end;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (psd->package.count != 1) {
|
|
|
|
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid _PSD data\n"));
|
|
|
|
+ result = -EFAULT;
|
|
|
|
+ goto end;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ pdomain = &(pr->performance->domain_info);
|
|
|
|
+
|
|
|
|
+ state.length = sizeof(struct acpi_psd_package);
|
|
|
|
+ state.pointer = pdomain;
|
|
|
|
+
|
|
|
|
+ status = acpi_extract_package(&(psd->package.elements[0]),
|
|
|
|
+ &format, &state);
|
|
|
|
+ if (ACPI_FAILURE(status)) {
|
|
|
|
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid _PSD data\n"));
|
|
|
|
+ result = -EFAULT;
|
|
|
|
+ goto end;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (pdomain->num_entries != ACPI_PSD_REV0_ENTRIES) {
|
|
|
|
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unknown _PSD:num_entries\n"));
|
|
|
|
+ result = -EFAULT;
|
|
|
|
+ goto end;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (pdomain->revision != ACPI_PSD_REV0_REVISION) {
|
|
|
|
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unknown _PSD:revision\n"));
|
|
|
|
+ result = -EFAULT;
|
|
|
|
+ goto end;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+end:
|
|
|
|
+ acpi_os_free(buffer.pointer);
|
|
|
|
+ return_VALUE(result);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+int acpi_processor_preregister_performance(
|
|
|
|
+ struct acpi_processor_performance **performance)
|
|
|
|
+{
|
|
|
|
+ int count, count_target;
|
|
|
|
+ int retval = 0;
|
|
|
|
+ unsigned int i, j;
|
|
|
|
+ cpumask_t covered_cpus;
|
|
|
|
+ struct acpi_processor *pr;
|
|
|
|
+ struct acpi_psd_package *pdomain;
|
|
|
|
+ struct acpi_processor *match_pr;
|
|
|
|
+ struct acpi_psd_package *match_pdomain;
|
|
|
|
+
|
|
|
|
+ ACPI_FUNCTION_TRACE("acpi_processor_preregister_performance");
|
|
|
|
+
|
|
|
|
+ down(&performance_sem);
|
|
|
|
+
|
|
|
|
+ retval = 0;
|
|
|
|
+
|
|
|
|
+ /* Call _PSD for all CPUs */
|
|
|
|
+ for_each_cpu(i) {
|
|
|
|
+ pr = processors[i];
|
|
|
|
+ if (!pr) {
|
|
|
|
+ /* Look only at processors in ACPI namespace */
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (pr->performance) {
|
|
|
|
+ retval = -EBUSY;
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!performance || !performance[i]) {
|
|
|
|
+ retval = -EINVAL;
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ pr->performance = performance[i];
|
|
|
|
+ cpu_set(i, pr->performance->shared_cpu_map);
|
|
|
|
+ if (acpi_processor_get_psd(pr)) {
|
|
|
|
+ retval = -EINVAL;
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if (retval)
|
|
|
|
+ goto err_ret;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Now that we have _PSD data from all CPUs, lets setup P-state
|
|
|
|
+ * domain info.
|
|
|
|
+ */
|
|
|
|
+ for_each_cpu(i) {
|
|
|
|
+ pr = processors[i];
|
|
|
|
+ if (!pr)
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ /* Basic validity check for domain info */
|
|
|
|
+ pdomain = &(pr->performance->domain_info);
|
|
|
|
+ if ((pdomain->revision != ACPI_PSD_REV0_REVISION) ||
|
|
|
|
+ (pdomain->num_entries != ACPI_PSD_REV0_ENTRIES)) {
|
|
|
|
+ retval = -EINVAL;
|
|
|
|
+ goto err_ret;
|
|
|
|
+ }
|
|
|
|
+ if (pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ALL &&
|
|
|
|
+ pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ANY &&
|
|
|
|
+ pdomain->coord_type != DOMAIN_COORD_TYPE_HW_ALL) {
|
|
|
|
+ retval = -EINVAL;
|
|
|
|
+ goto err_ret;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ cpus_clear(covered_cpus);
|
|
|
|
+ for_each_cpu(i) {
|
|
|
|
+ pr = processors[i];
|
|
|
|
+ if (!pr)
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ if (cpu_isset(i, covered_cpus))
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ pdomain = &(pr->performance->domain_info);
|
|
|
|
+ cpu_set(i, pr->performance->shared_cpu_map);
|
|
|
|
+ cpu_set(i, covered_cpus);
|
|
|
|
+ if (pdomain->num_processors <= 1)
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ /* Validate the Domain info */
|
|
|
|
+ count_target = pdomain->num_processors;
|
|
|
|
+ count = 1;
|
|
|
|
+ if (pdomain->coord_type == DOMAIN_COORD_TYPE_SW_ALL ||
|
|
|
|
+ pdomain->coord_type == DOMAIN_COORD_TYPE_HW_ALL) {
|
|
|
|
+ pr->performance->shared_type = CPUFREQ_SHARED_TYPE_ALL;
|
|
|
|
+ } else if (pdomain->coord_type == DOMAIN_COORD_TYPE_SW_ANY) {
|
|
|
|
+ pr->performance->shared_type = CPUFREQ_SHARED_TYPE_ANY;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ for_each_cpu(j) {
|
|
|
|
+ if (i == j)
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ match_pr = processors[j];
|
|
|
|
+ if (!match_pr)
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ match_pdomain = &(match_pr->performance->domain_info);
|
|
|
|
+ if (match_pdomain->domain != pdomain->domain)
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ /* Here i and j are in the same domain */
|
|
|
|
+
|
|
|
|
+ if (match_pdomain->num_processors != count_target) {
|
|
|
|
+ retval = -EINVAL;
|
|
|
|
+ goto err_ret;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (pdomain->coord_type != match_pdomain->coord_type) {
|
|
|
|
+ retval = -EINVAL;
|
|
|
|
+ goto err_ret;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ cpu_set(j, covered_cpus);
|
|
|
|
+ cpu_set(j, pr->performance->shared_cpu_map);
|
|
|
|
+ count++;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ for_each_cpu(j) {
|
|
|
|
+ if (i == j)
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ match_pr = processors[j];
|
|
|
|
+ if (!match_pr)
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ match_pdomain = &(match_pr->performance->domain_info);
|
|
|
|
+ if (match_pdomain->domain != pdomain->domain)
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ match_pr->performance->shared_type =
|
|
|
|
+ pr->performance->shared_type;
|
|
|
|
+ match_pr->performance->shared_cpu_map =
|
|
|
|
+ pr->performance->shared_cpu_map;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+err_ret:
|
|
|
|
+ if (retval) {
|
|
|
|
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error while parsing _PSD domain information. Assuming no coordination\n"));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ for_each_cpu(i) {
|
|
|
|
+ pr = processors[i];
|
|
|
|
+ if (!pr || !pr->performance)
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ /* Assume no coordination on any error parsing domain info */
|
|
|
|
+ if (retval) {
|
|
|
|
+ cpus_clear(pr->performance->shared_cpu_map);
|
|
|
|
+ cpu_set(i, pr->performance->shared_cpu_map);
|
|
|
|
+ pr->performance->shared_type = CPUFREQ_SHARED_TYPE_ALL;
|
|
|
|
+ }
|
|
|
|
+ pr->performance = NULL; /* Will be set for real in register */
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ up(&performance_sem);
|
|
|
|
+ return_VALUE(retval);
|
|
|
|
+}
|
|
|
|
+EXPORT_SYMBOL(acpi_processor_preregister_performance);
|
|
|
|
+
|
|
|
|
+
|
|
int
|
|
int
|
|
acpi_processor_register_performance(struct acpi_processor_performance
|
|
acpi_processor_register_performance(struct acpi_processor_performance
|
|
*performance, unsigned int cpu)
|
|
*performance, unsigned int cpu)
|