soc/intel/alderlake: Add ADL-P 4+4 with 28W TDP
[coreboot.git] / src / cpu / intel / speedstep / speedstep.c
blobb1b300d9033e5b948f2528edc630c48a407cc0d9
1 /* SPDX-License-Identifier: GPL-2.0-only */
3 #include <console/console.h>
4 #include <cpu/cpu.h>
5 #include <cpu/intel/speedstep.h>
6 #include <cpu/x86/msr.h>
7 #include <string.h>
8 #include <types.h>
10 /**
11 * @brief Gather speedstep limits for current processor
13 * At least power limits are processor type specific. Penryn introduced half
14 * steps in bus ratios. Don't know about Atom processors.
16 static void speedstep_get_limits(sst_params_t *const params)
18 msr_t msr;
20 const uint16_t cpu_id = (cpuid_eax(1) >> 4) & 0xffff;
21 const uint32_t state_mask =
22 /* Penryn supports non integer (i.e. half) ratios. */
23 ((cpu_id == 0x1067) ? SPEEDSTEP_RATIO_NONINT : 0)
24 | SPEEDSTEP_RATIO_VALUE_MASK | SPEEDSTEP_VID_MASK;
26 /* Initialize params to zero. */
27 memset(params, '\0', sizeof(*params));
29 /* Read Super-LFM parameters. */
30 if (((rdmsr(MSR_EXTENDED_CONFIG).lo >> 27) & 3) == 3) {/*supported and
31 enabled bits */
32 msr = rdmsr(MSR_FSB_CLOCK_VCC);
33 params->slfm = SPEEDSTEP_STATE_FROM_MSR(msr.lo, state_mask);
34 params->slfm.dynfsb = 1;
35 params->slfm.is_slfm = 1;
38 /* Read normal minimum parameters. */
39 msr = rdmsr(MSR_THERM2_CTL);
40 params->min = SPEEDSTEP_STATE_FROM_MSR(msr.lo, state_mask);
42 /* Read normal maximum parameters. */
43 /* Newer CPUs provide the normal maximum settings in
44 IA32_PLATFORM_ID. The values in IA32_PERF_STATUS change
45 when using turbo mode. */
46 msr = rdmsr(IA32_PLATFORM_ID);
47 params->max = SPEEDSTEP_STATE_FROM_MSR(msr.lo, state_mask);
48 if (cpu_id == 0x006e) {
49 /* Looks like Yonah CPUs don't have the frequency ratio in
50 IA32_PLATFORM_ID. Use IA32_PERF_STATUS instead, the reading
51 should be reliable as those CPUs don't have turbo mode. */
52 msr = rdmsr(IA32_PERF_STATUS);
53 params->max.ratio = (msr.hi & SPEEDSTEP_RATIO_VALUE_MASK)
54 >> SPEEDSTEP_RATIO_SHIFT;
57 /* Read turbo parameters. */
58 msr = rdmsr(MSR_FSB_CLOCK_VCC);
59 if ((msr.hi & (1 << (63 - 32))) &&
60 /* supported and */
61 !(rdmsr(IA32_MISC_ENABLE).hi & (1 << (38 - 32)))) {
62 /* not disabled */
63 params->turbo = SPEEDSTEP_STATE_FROM_MSR(msr.hi, state_mask);
64 params->turbo.is_turbo = 1;
67 /* Set power limits by processor type. */
68 /* Defined values match the normal voltage versions only. But
69 they are only a hint for OSPM, so this should not hurt much. */
70 switch (cpu_id) {
71 case 0x006e:
72 /* Yonah */
73 params->min.power = SPEEDSTEP_MIN_POWER_YONAH;
74 params->max.power = SPEEDSTEP_MAX_POWER_YONAH;
75 break;
76 case 0x1067:
77 /* Penryn */
78 params->slfm.power = SPEEDSTEP_SLFM_POWER_PENRYN;
79 params->min.power = SPEEDSTEP_MIN_POWER_PENRYN;
80 params->max.power = SPEEDSTEP_MAX_POWER_PENRYN;
81 params->turbo.power = SPEEDSTEP_MAX_POWER_PENRYN;
82 break;
83 case 0x006f:
84 /* Merom */
85 default:
86 /* Use Merom values by default (as before). */
87 params->slfm.power = SPEEDSTEP_SLFM_POWER_MEROM;
88 params->min.power = SPEEDSTEP_MIN_POWER_MEROM;
89 params->max.power = SPEEDSTEP_MAX_POWER_MEROM;
90 params->turbo.power = SPEEDSTEP_MAX_POWER_MEROM;
91 break;
95 /**
96 * @brief Generate full p-states table from processor parameters
98 * This is generic code and should work at least for Merom and Penryn
99 * processors. It is used to generate ACPI tables and configure EMTTM.
101 void speedstep_gen_pstates(sst_table_t *const table)
103 sst_params_t params;
104 /* Gather speedstep limits. */
105 speedstep_get_limits(&params);
107 /*\ First, find the number of normal states: \*/
109 /* Calculate with doubled values to work
110 around non-integer (.5) bus ratios. */
111 const int power_diff2 = (params.max.power - params.min.power) * 2;
112 const int vid_diff2 = (params.max.vid - params.min.vid) * 2;
113 const int max_ratio2 = SPEEDSTEP_DOUBLE_RATIO(params.max);
114 const int min_ratio2 = SPEEDSTEP_DOUBLE_RATIO(params.min);
115 const int ratio_diff2 = max_ratio2 - min_ratio2;
116 /* Calculate number of normal states (LFM to HFM, min to max). */
117 /* Increase step size, until all states fit into the table.
118 (Note: First try should always work, if
119 SPEEDSTEP_MAX_NORMAL_STATES is set correctly.) */
120 int states, step2 = 0;
121 do {
122 step2 += 2 * 2; /* Must be a multiple of 2 (doubled). */
123 states = ratio_diff2 / step2 + 1;
124 } while (states > SPEEDSTEP_MAX_NORMAL_STATES);
125 if (step2 > 4)
126 printk(BIOS_INFO, "Enhanced Speedstep processor with "
127 "more than %d possible p-states.\n",
128 SPEEDSTEP_MAX_NORMAL_STATES);
129 if (states < 2) /* Report at least two normal states. */
130 states = 2;
132 /*\ Now, fill the table: \*/
134 table->num_states = 0;
136 /* Add turbo state if supported. */
137 if (params.turbo.is_turbo)
138 table->states[table->num_states++] = params.turbo;
140 /* Add HFM first. */
141 table->states[table->num_states] = params.max;
142 /* Work around HFM and LFM having the same bus ratio. */
143 if ((params.max.dynfsb == params.min.dynfsb) &&
144 (params.max.nonint == params.min.nonint) &&
145 (params.max.ratio == params.min.ratio))
146 table->states[table->num_states].vid = params.min.vid;
147 ++table->num_states;
148 --states;
150 /* Now, add all other normal states based on LFM (min). */
151 const int power_step = (power_diff2 / states) / 2;
152 const int vid_step = (vid_diff2 / states) / 2;
153 const int ratio_step = step2 / 2;
154 int power = params.min.power + (states - 1) * power_step;
155 int vid = params.min.vid + (states - 1) * vid_step;
156 int ratio = params.min.ratio + (states - 1) * ratio_step;
157 for (; states > 0; --states) {
158 table->states[table->num_states++] =
159 (sst_state_t){ 0, 0, ratio, vid, 0, 0, power };
160 power -= power_step;
161 vid -= vid_step;
162 ratio -= ratio_step;
165 /* At last, add Super-LFM state if supported. */
166 if (params.slfm.is_slfm)
167 table->states[table->num_states++] = params.slfm;