soc/intel/alderlake: Add ADL-P 4+4 with 28W TDP
[coreboot.git] / src / acpi / acpigen_dptf.c
bloba458a5599b54934a28f0c42334f7e6a8139d68c8
1 /* SPDX-License-Identifier: GPL-2.0-only */
3 #include <acpi/acpigen.h>
4 #include <acpi/acpigen_dptf.h>
5 #include <stdbool.h>
6 #include <stdint.h>
8 /* Defaults */
9 #define DEFAULT_RAW_UNIT "ma"
11 /* DPTF-specific UUIDs */
12 #define DPTF_PASSIVE_POLICY_1_0_UUID "42A441D6-AE6A-462B-A84B-4A8CE79027D3"
13 #define DPTF_CRITICAL_POLICY_UUID "97C68AE7-15FA-499c-B8C9-5DA81D606E0A"
14 #define DPTF_ACTIVE_POLICY_UUID "3A95C389-E4B8-4629-A526-C52C88626BAE"
16 enum {
17 ART_REVISION = 0,
18 DEFAULT_PRIORITY = 100,
19 DEFAULT_TRIP_POINT = 0xFFFFFFFFull,
20 DEFAULT_WEIGHT = 100,
21 DPTF_MAX_ART_THRESHOLDS = 10,
22 FPS_REVISION = 0,
23 PPCC_REVISION = 2,
24 RAPL_PL1_INDEX = 0,
25 RAPL_PL2_INDEX = 1,
28 /* Convert degrees C to 1/10 degree Kelvin for ACPI */
29 static int to_acpi_temp(int deg_c)
31 return deg_c * 10 + 2732;
34 /* Converts ms to 1/10th second for ACPI */
35 static int to_acpi_time(int ms)
37 return ms / 100;
40 /* Writes out a 0-argument non-Serialized Method that returns an Integer */
41 static void write_simple_return_method(const char *name, int value)
43 acpigen_write_method(name, 0);
44 acpigen_write_return_integer(value);
45 acpigen_pop_len(); /* Method */
48 /* Writes out 'count' ZEROs in a row */
49 static void write_zeros(int count)
51 for (; count; --count)
52 acpigen_write_integer(0);
55 /* Return the assigned namestring of any participant */
56 static const char *namestring_of(enum dptf_participant participant)
58 switch (participant) {
59 case DPTF_CPU:
60 return "TCPU";
61 case DPTF_CHARGER:
62 return "TCHG";
63 case DPTF_FAN:
64 return "TFN1";
65 case DPTF_TEMP_SENSOR_0:
66 return "TSR0";
67 case DPTF_TEMP_SENSOR_1:
68 return "TSR1";
69 case DPTF_TEMP_SENSOR_2:
70 return "TSR2";
71 case DPTF_TEMP_SENSOR_3:
72 return "TSR3";
73 case DPTF_TEMP_SENSOR_4:
74 return "TSR4";
75 case DPTF_TPCH:
76 return "TPCH";
77 case DPTF_POWER:
78 return "TPWR";
79 case DPTF_BATTERY:
80 return "TBAT";
81 default:
82 return "";
86 /* Helper to get Scope for participants underneath \_SB.DPTF */
87 static const char *scope_of(enum dptf_participant participant)
89 static char scope[16];
91 if (participant == DPTF_CPU)
92 snprintf(scope, sizeof(scope), TCPU_SCOPE ".%s", namestring_of(participant));
93 else
94 snprintf(scope, sizeof(scope), DPTF_DEVICE_PATH ".%s",
95 namestring_of(participant));
97 return scope;
101 * Most of the DPTF participants are underneath the \_SB.DPTF scope, so we can just get away
102 * with using the simple namestring for references, but the TCPU has a different scope, so
103 * either an absolute or relative path must be used instead.
105 static const char *path_of(enum dptf_participant participant)
107 if (participant == DPTF_CPU)
108 return scope_of(participant);
109 else
110 return namestring_of(participant);
113 /* Write out scope of a participant */
114 void dptf_write_scope(enum dptf_participant participant)
116 acpigen_write_scope(scope_of(participant));
120 * This table describes active cooling relationships between the system's fan and the
121 * temperature sensors that it can have an effect on. As ever-increasing temperature thresholds
122 * are crossed (_AC9.._AC0, low to high), the corresponding fan percentages listed in this table
123 * are used to increase the speed of the fan in order to speed up cooling.
125 static void write_active_relationship_table(const struct dptf_active_policy *policies,
126 int max_count)
128 char *pkg_count;
129 int i, j;
131 /* Nothing to do */
132 if (!max_count || policies[0].target == DPTF_NONE)
133 return;
135 acpigen_write_scope(DPTF_DEVICE_PATH);
136 acpigen_write_method("_ART", 0);
138 /* Return this package */
139 acpigen_emit_byte(RETURN_OP);
141 /* Keep track of items added to the package */
142 pkg_count = acpigen_write_package(1); /* The '1' here is for the revision */
143 acpigen_write_integer(ART_REVISION);
145 for (i = 0; i < max_count; ++i) {
147 * These have to be filled out from AC0 down to AC9, filling in only as many
148 * as are used. As soon as one isn't filled in, we're done.
150 if (policies[i].target == DPTF_NONE)
151 break;
153 (*pkg_count)++;
155 /* Source, Target, Percent, Fan % for each of _AC0 ... _AC9 */
156 acpigen_write_package(13);
157 acpigen_emit_namestring(path_of(DPTF_FAN));
158 acpigen_emit_namestring(path_of(policies[i].target));
159 acpigen_write_integer(DEFAULT_IF_0(policies[i].weight, DEFAULT_WEIGHT));
161 /* Write out fan %; corresponds with target's _ACx methods */
162 for (j = 0; j < DPTF_MAX_ART_THRESHOLDS; ++j)
163 acpigen_write_integer(policies[i].thresholds[j].fan_pct);
165 acpigen_pop_len(); /* inner Package */
168 acpigen_pop_len(); /* outer Package */
169 acpigen_pop_len(); /* Method _ART */
170 acpigen_pop_len(); /* Scope */
174 * _AC9 through _AC0 represent temperature thresholds, in increasing order, defined from _AC0
175 * down, that, when reached, DPTF will activate TFN1 in order to actively cool the temperature
176 * sensor(s). As increasing thresholds are reached, the fan is spun faster.
178 static void write_active_cooling_methods(const struct dptf_active_policy *policies,
179 int max_count)
181 char name[5];
182 int i, j;
184 /* Nothing to do */
185 if (!max_count || policies[0].target == DPTF_NONE)
186 return;
188 for (i = 0; i < max_count; ++i) {
189 if (policies[i].target == DPTF_NONE)
190 break;
192 dptf_write_scope(policies[i].target);
194 /* Write out as many of _AC0 through _AC9 that are applicable */
195 for (j = 0; j < DPTF_MAX_ACX; ++j) {
196 if (!policies[i].thresholds[j].temp)
197 break;
199 snprintf(name, sizeof(name), "_AC%1X", j);
200 write_simple_return_method(name, to_acpi_temp(
201 policies[i].thresholds[j].temp));
204 acpigen_pop_len(); /* Scope */
208 void dptf_write_active_policies(const struct dptf_active_policy *policies, int max_count)
210 write_active_relationship_table(policies, max_count);
211 write_active_cooling_methods(policies, max_count);
215 * This writes out the Thermal Relationship Table, which describes the thermal relationships
216 * between participants in a thermal zone. This information is used to passively cool (i.e.,
217 * throttle) the Source (source of heat), in order to indirectly cool the Target (temperature
218 * sensor).
220 static void write_thermal_relationship_table(const struct dptf_passive_policy *policies,
221 int max_count)
223 char *pkg_count;
224 int i;
226 /* Nothing to do */
227 if (!max_count || policies[0].source == DPTF_NONE)
228 return;
230 acpigen_write_scope(DPTF_DEVICE_PATH);
233 * A _TRT Revision (TRTR) of 1 means that the 'Priority' field is an arbitrary priority
234 * value to be used for this specific relationship. The priority value determines the
235 * order in which various sources are used in a passive thermal action for a given
236 * target.
238 acpigen_write_name_integer("TRTR", 1);
240 /* Thermal Relationship Table */
241 acpigen_write_method("_TRT", 0);
243 /* Return this package */
244 acpigen_emit_byte(RETURN_OP);
245 pkg_count = acpigen_write_package(0);
247 for (i = 0; i < max_count; ++i) {
248 /* Stop writing the table once an entry is empty */
249 if (policies[i].source == DPTF_NONE)
250 break;
252 /* Keep track of outer package item count */
253 (*pkg_count)++;
255 acpigen_write_package(8);
257 /* Source, Target, Priority, Sampling Period */
258 acpigen_emit_namestring(path_of(policies[i].source));
259 acpigen_emit_namestring(path_of(policies[i].target));
260 acpigen_write_integer(DEFAULT_IF_0(policies[i].priority, DEFAULT_PRIORITY));
261 acpigen_write_integer(to_acpi_time(policies[i].period));
263 /* Reserved */
264 write_zeros(4);
266 acpigen_pop_len(); /* Package */
269 acpigen_pop_len(); /* Package */
270 acpigen_pop_len(); /* Method */
271 acpigen_pop_len(); /* Scope */
275 * When a temperature sensor measures above its the temperature returned in its _PSV Method,
276 * DPTF will begin throttling Sources in order to indirectly cool the sensor.
278 static void write_all_PSV(const struct dptf_passive_policy *policies, int max_count)
280 int i;
282 for (i = 0; i < max_count; ++i) {
283 if (policies[i].source == DPTF_NONE)
284 break;
286 dptf_write_scope(policies[i].target);
287 write_simple_return_method("_PSV", to_acpi_temp(policies[i].temp));
288 acpigen_pop_len(); /* Scope */
292 void dptf_write_passive_policies(const struct dptf_passive_policy *policies, int max_count)
294 write_thermal_relationship_table(policies, max_count);
295 write_all_PSV(policies, max_count);
298 void dptf_write_critical_policies(const struct dptf_critical_policy *policies, int max_count)
300 int i;
302 for (i = 0; i < max_count; ++i) {
303 if (policies[i].source == DPTF_NONE)
304 break;
306 dptf_write_scope(policies[i].source);
308 /* Choose _CRT or _HOT */
309 write_simple_return_method(policies[i].type == DPTF_CRITICAL_SHUTDOWN ?
310 "_CRT" : "_HOT", to_acpi_temp(policies[i].temp));
312 acpigen_pop_len(); /* Scope */
316 void dptf_write_charger_perf(const struct dptf_charger_perf *states, int max_count)
318 char *pkg_count;
319 int i;
321 if (!max_count || !states[0].control)
322 return;
324 dptf_write_scope(DPTF_CHARGER);
326 /* PPSS - Participant Performance Supported States */
327 acpigen_write_method("PPSS", 0);
328 acpigen_emit_byte(RETURN_OP);
330 pkg_count = acpigen_write_package(0);
331 for (i = 0; i < max_count; ++i) {
332 if (!states[i].control)
333 break;
335 (*pkg_count)++;
338 * 0, 0, 0, 0, # Reserved
339 * Control, Raw Performance, Raw Unit, 0 # Reserved
341 acpigen_write_package(8);
342 write_zeros(4);
343 acpigen_write_integer(states[i].control);
344 acpigen_write_integer(states[i].raw_perf);
345 acpigen_write_string(DEFAULT_RAW_UNIT);
346 acpigen_write_integer(0);
347 acpigen_pop_len(); /* inner Package */
350 acpigen_pop_len(); /* outer Package */
351 acpigen_pop_len(); /* Method PPSS */
352 acpigen_pop_len(); /* Scope */
355 void dptf_write_fan_perf(const struct dptf_fan_perf *states, int max_count)
357 char *pkg_count;
358 int i;
360 if (!max_count || !states[0].percent)
361 return;
363 dptf_write_scope(DPTF_FAN);
365 /* _FPS - Fan Performance States */
366 acpigen_write_name("_FPS");
367 pkg_count = acpigen_write_package(1); /* 1 for Revision */
368 acpigen_write_integer(FPS_REVISION); /* revision */
370 for (i = 0; i < max_count; ++i) {
372 * Some _FPS tables do include a last entry where Percent is 0, but Power is
373 * called out, so this table is finished when both are zero.
375 if (!states[i].percent && !states[i].power)
376 break;
378 (*pkg_count)++;
379 acpigen_write_package(5);
380 acpigen_write_integer(states[i].percent);
381 acpigen_write_integer(DEFAULT_TRIP_POINT);
382 acpigen_write_integer(states[i].speed);
383 acpigen_write_integer(states[i].noise_level);
384 acpigen_write_integer(states[i].power);
385 acpigen_pop_len(); /* inner Package */
388 acpigen_pop_len(); /* Package */
389 acpigen_pop_len(); /* Scope */
392 void dptf_write_power_limits(const struct dptf_power_limits *limits)
394 char *pkg_count;
396 /* Nothing to do */
397 if (!limits->pl1.min_power && !limits->pl2.min_power)
398 return;
400 dptf_write_scope(DPTF_CPU);
401 acpigen_write_method("PPCC", 0);
403 acpigen_emit_byte(RETURN_OP);
405 pkg_count = acpigen_write_package(1); /* 1 for the Revision */
406 acpigen_write_integer(PPCC_REVISION); /* revision */
408 if (limits->pl1.min_power) {
409 (*pkg_count)++;
410 acpigen_write_package(6);
411 acpigen_write_integer(RAPL_PL1_INDEX);
412 acpigen_write_integer(limits->pl1.min_power);
413 acpigen_write_integer(limits->pl1.max_power);
414 acpigen_write_integer(limits->pl1.time_window_min);
415 acpigen_write_integer(limits->pl1.time_window_max);
416 acpigen_write_integer(limits->pl1.granularity);
417 acpigen_pop_len(); /* inner Package */
420 if (limits->pl2.min_power) {
421 (*pkg_count)++;
422 acpigen_write_package(6);
423 acpigen_write_integer(RAPL_PL2_INDEX);
424 acpigen_write_integer(limits->pl2.min_power);
425 acpigen_write_integer(limits->pl2.max_power);
426 acpigen_write_integer(limits->pl2.time_window_min);
427 acpigen_write_integer(limits->pl2.time_window_max);
428 acpigen_write_integer(limits->pl2.granularity);
429 acpigen_pop_len(); /* inner Package */
432 acpigen_pop_len(); /* outer Package */
433 acpigen_pop_len(); /* Method */
434 acpigen_pop_len(); /* Scope */
437 void dptf_write_STR(const char *str)
439 if (!str)
440 return;
442 acpigen_write_name_string("_STR", str);
445 void dptf_write_fan_options(bool fine_grained, int step_size, bool low_speed_notify)
447 acpigen_write_name("_FIF");
448 acpigen_write_package(4);
450 acpigen_write_integer(0); /* Revision */
451 acpigen_write_integer(fine_grained);
452 acpigen_write_integer(step_size);
453 acpigen_write_integer(low_speed_notify);
454 acpigen_pop_len(); /* Package */
457 void dptf_write_tsr_hysteresis(uint8_t hysteresis)
459 if (!hysteresis)
460 return;
462 acpigen_write_name_integer("GTSH", hysteresis);
465 void dptf_write_enabled_policies(const struct dptf_active_policy *active_policies,
466 int active_count,
467 const struct dptf_passive_policy *passive_policies,
468 int passive_count,
469 const struct dptf_critical_policy *critical_policies,
470 int critical_count)
472 bool is_active_used;
473 bool is_passive_used;
474 bool is_critical_used;
475 int pkg_count;
477 is_active_used = (active_count && active_policies[0].target != DPTF_NONE);
478 is_passive_used = (passive_count && passive_policies[0].target != DPTF_NONE);
479 is_critical_used = (critical_count && critical_policies[0].source != DPTF_NONE);
480 pkg_count = is_active_used + is_passive_used + is_critical_used;
482 if (!pkg_count)
483 return;
485 acpigen_write_scope(DPTF_DEVICE_PATH);
486 acpigen_write_name("IDSP");
487 acpigen_write_package(pkg_count);
489 if (is_active_used)
490 acpigen_write_uuid(DPTF_ACTIVE_POLICY_UUID);
492 if (is_passive_used)
493 acpigen_write_uuid(DPTF_PASSIVE_POLICY_1_0_UUID);
495 if (is_critical_used)
496 acpigen_write_uuid(DPTF_CRITICAL_POLICY_UUID);
498 acpigen_pop_len(); /* Package */
499 acpigen_pop_len(); /* Scope */