blob: eb852de50dd277ed780eef2b57c412aaaf3f6b6a [file] [log] [blame]
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include <acpi/acpigen.h>
#include <intelblocks/acpi.h>
#include <soc/cpu.h>
#include <smp/spinlock.h>
#include <device/device.h>
#include <device/path.h>
#include <cpu/x86/lapic.h>
#include <cpu/x86/mp.h>
#define CPPC_NOM_FREQ_IDX 22
#define CPPC_NOM_PERF_IDX 3
enum cpu_perf_eff_type {
CPU_TYPE_EFF,
CPU_TYPE_PERF,
};
DECLARE_SPIN_LOCK(cpu_lock);
static u8 global_cpu_type[CONFIG_MAX_CPUS];
static bool is_perf_core(void)
{
return get_soc_cpu_type() == CPUID_CORE_TYPE_INTEL_CORE;
}
static struct device *get_cpu_bus_first_child(void)
{
struct device *dev = dev_find_path(NULL, DEVICE_PATH_CPU_CLUSTER);
assert(dev != NULL);
return (dev->link_list)->children;
}
static u32 get_cpu_index(void)
{
u32 cpu_index = 0;
struct device *dev;
u32 my_apic_id = lapicid();
for (dev = get_cpu_bus_first_child(); dev; dev = dev->sibling)
if (my_apic_id > dev->path.apic.apic_id)
cpu_index++;
return cpu_index;
}
/*
* This function determines the type (performance or efficient) of the CPU that
* is executing it and stores the information (in a thread-safe manner) in an
* global_cpu_type array.
* It requires the SoC to implement a function `get_soc_cpu_type()` which will be
* called in a critical section to determine the type of the executing CPU.
*/
static void set_cpu_type(void *unused)
{
spin_lock(&cpu_lock);
u8 cpu_index = get_cpu_index();
if (is_perf_core())
global_cpu_type[cpu_index] = CPU_TYPE_PERF;
spin_unlock(&cpu_lock);
}
static void run_set_cpu_type(void *unused)
{
if (mp_run_on_all_cpus(set_cpu_type, NULL) != CB_SUCCESS) {
printk(BIOS_ERR, "cpu_hybrid: Failed to set global_cpu_type with CPU type info\n");
return;
}
}
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 (global_cpu_type[core_id] == CPU_TYPE_PERF)
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),
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, run_set_cpu_type, NULL);