summaryrefslogtreecommitdiffstats
path: root/src/soc/intel/common/block/acpi/cpu_hybrid.c
blob: f52b68f7e222c7c1ce63aed91308b9c9d85791e8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
/* 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);