| /* SPDX-License-Identifier: GPL-2.0-or-later */ |
| #include <acpi/acpi.h> |
| #include <acpi/acpigen.h> |
| #include <bootstate.h> |
| #include <commonlib/sort.h> |
| #include <cpu/x86/lapic.h> |
| #include <cpu/x86/mp.h> |
| #include <cpu/cpu.h> |
| #include <device/path.h> |
| #include <intelblocks/acpi.h> |
| #include <soc/cpu.h> |
| #include <types.h> |
| |
| #define CPPC_NOM_FREQ_IDX 22 |
| #define CPPC_NOM_PERF_IDX 3 |
| |
| enum cpu_perf_eff_type { |
| CPU_TYPE_EFF, |
| CPU_TYPE_PERF, |
| }; |
| |
| struct cpu_apic_info_type { |
| /* |
| * Ordered APIC IDs based on core type. |
| * Array begins with Performance Cores' APIC IDs, |
| * then followed by Efficeint Cores's APIC IDs. |
| */ |
| int32_t apic_ids[CONFIG_MAX_CPUS]; |
| |
| /* Total CPU count */ |
| uint16_t total_cpu_cnt; |
| |
| /* |
| * Total Performance core count. This will be used |
| * to identify the start of Efficient Cores's |
| * APIC ID list |
| */ |
| uint16_t perf_cpu_cnt; |
| }; |
| |
| static struct cpu_apic_info_type cpu_apic_info; |
| |
| /* |
| * The function orders APIC IDs such that orders first Performance cores and then |
| * Efficient cores' APIC IDs in ascending order. Also calculates total number of |
| * Performance cores and all cores count in the system and populates the information |
| * in the cpu_apic_info sturct. |
| */ |
| static void acpi_set_hybrid_cpu_apicid_order(void *unused) |
| { |
| size_t perf_core_cnt = 0, eff_core_cnt = 0; |
| int32_t eff_apic_ids[CONFIG_MAX_CPUS] = {0}; |
| extern struct cpu_info cpu_infos[]; |
| uint32_t i, j = 0; |
| |
| for (i = 0; i < ARRAY_SIZE(cpu_apic_info.apic_ids); i++) { |
| if (!cpu_infos[i].cpu) |
| continue; |
| if (cpu_infos[i].cpu->path.apic.core_type == CPU_TYPE_PERF) |
| cpu_apic_info.apic_ids[perf_core_cnt++] = |
| cpu_infos[i].cpu->path.apic.apic_id; |
| else |
| eff_apic_ids[eff_core_cnt++] = |
| cpu_infos[i].cpu->path.apic.apic_id; |
| } |
| |
| if (perf_core_cnt > 1) |
| bubblesort(cpu_apic_info.apic_ids, perf_core_cnt, NUM_ASCENDING); |
| |
| for (i = perf_core_cnt; j < eff_core_cnt; i++, j++) |
| cpu_apic_info.apic_ids[i] = eff_apic_ids[j]; |
| |
| if (eff_core_cnt > 1) |
| bubblesort(&cpu_apic_info.apic_ids[perf_core_cnt], eff_core_cnt, NUM_ASCENDING); |
| |
| /* Populate total core count */ |
| cpu_apic_info.total_cpu_cnt = perf_core_cnt + eff_core_cnt; |
| |
| cpu_apic_info.perf_cpu_cnt = perf_core_cnt; |
| } |
| |
| static unsigned long acpi_create_madt_lapics_hybrid(unsigned long current) |
| { |
| size_t index; |
| |
| for (index = 0; index < cpu_apic_info.total_cpu_cnt; index++) |
| current = acpi_create_madt_one_lapic(current, index, |
| cpu_apic_info.apic_ids[index]); |
| |
| return current; |
| } |
| |
| unsigned long acpi_create_madt_lapics_with_nmis_hybrid(unsigned long current) |
| { |
| current = acpi_create_madt_lapics_hybrid(current); |
| current = acpi_create_madt_lapic_nmis(current); |
| return current; |
| } |
| |
| static enum cpu_perf_eff_type get_core_type(void) |
| { |
| return (get_soc_cpu_type() == CPUID_CORE_TYPE_INTEL_CORE) ? |
| CPU_TYPE_PERF : CPU_TYPE_EFF; |
| } |
| |
| void set_dev_core_type(void) |
| { |
| struct cpu_info *info = cpu_info(); |
| info->cpu->path.apic.core_type = get_core_type(); |
| } |
| |
| static void acpi_get_cpu_nomi_perf(u16 *eff_core_nom_perf, u16 *perf_core_nom_perf) |
| { |
| u8 max_non_turbo_ratio = cpu_get_max_non_turbo_ratio(); |
| |
| _Static_assert(CONFIG_SOC_INTEL_PERFORMANCE_CORE_SCALE_FACTOR != 0, |
| "CONFIG_SOC_INTEL_PERFORMANCE_CORE_SCALE_FACTOR must not be zero"); |
| |
| _Static_assert(CONFIG_SOC_INTEL_EFFICIENT_CORE_SCALE_FACTOR != 0, |
| "CONFIG_SOC_INTEL_EFFICIENT_CORE_SCALE_FACTOR must not be zero"); |
| |
| *perf_core_nom_perf = (u16)((max_non_turbo_ratio * |
| CONFIG_SOC_INTEL_PERFORMANCE_CORE_SCALE_FACTOR) / 100); |
| |
| *eff_core_nom_perf = (u16)((max_non_turbo_ratio * |
| CONFIG_SOC_INTEL_EFFICIENT_CORE_SCALE_FACTOR) / 100); |
| } |
| |
| static u16 acpi_get_cpu_nominal_freq(void) |
| { |
| return cpu_get_max_non_turbo_ratio() * cpu_get_bus_frequency(); |
| } |
| |
| /* Updates Nominal Frequency and Nominal Performance */ |
| static void acpigen_cppc_update_nominal_freq_perf(const char *pkg_path, s32 core_id) |
| { |
| u16 eff_core_nom_perf, perf_core_nom_perf; |
| |
| if (!soc_is_nominal_freq_supported()) |
| return; |
| |
| acpi_get_cpu_nomi_perf(&eff_core_nom_perf, &perf_core_nom_perf); |
| |
| if (core_id < cpu_apic_info.perf_cpu_cnt) |
| acpigen_set_package_element_int(pkg_path, CPPC_NOM_PERF_IDX, perf_core_nom_perf); |
| else |
| acpigen_set_package_element_int(pkg_path, CPPC_NOM_PERF_IDX, |
| eff_core_nom_perf); |
| |
| /* Update CPU's nominal frequency */ |
| acpigen_set_package_element_int(pkg_path, CPPC_NOM_FREQ_IDX, |
| acpi_get_cpu_nominal_freq()); |
| } |
| |
| void acpigen_write_CPPC_hybrid_method(s32 core_id) |
| { |
| char pkg_path[16]; |
| |
| if (core_id == 0) |
| snprintf(pkg_path, sizeof(pkg_path), CPPC_PACKAGE_NAME, 0); |
| else |
| snprintf(pkg_path, sizeof(pkg_path), |
| "\\_SB." CONFIG_ACPI_CPU_STRING "." CPPC_PACKAGE_NAME, 0); |
| |
| acpigen_write_method("_CPC", 0); |
| |
| /* Update nominal performance and nominal frequency */ |
| acpigen_cppc_update_nominal_freq_perf(pkg_path, core_id); |
| acpigen_emit_byte(RETURN_OP); |
| acpigen_emit_namestring(pkg_path); |
| acpigen_pop_len(); |
| } |
| |
| BOOT_STATE_INIT_ENTRY(BS_DEV_INIT_CHIPS, BS_ON_EXIT, acpi_set_hybrid_cpu_apicid_order, NULL); |