blob: d1a79d18e2ca42e032c6aa5d3ba2db977fd0235e [file] [log] [blame]
Tim Wawrzynczakc41f7f12020-05-29 13:56:37 -06001/* SPDX-License-Identifier: GPL-2.0-only */
2
3#include <acpi/acpigen.h>
4#include <acpi/acpigen_dptf.h>
Elyes HAOUAS441e1912020-07-27 09:37:08 +02005#include <stdbool.h>
6#include <stdint.h>
Tim Wawrzynczakc41f7f12020-05-29 13:56:37 -06007
Tim Wawrzynczakc41f7f12020-05-29 13:56:37 -06008/* Defaults */
Tim Wawrzynczak46f6fcf2020-05-29 14:29:53 -06009#define DEFAULT_RAW_UNIT "ma"
10
Tim Wawrzynczak03465f42020-06-03 16:27:30 -060011/* 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"
15
Tim Wawrzynczakc41f7f12020-05-29 13:56:37 -060016enum {
17 ART_REVISION = 0,
Tim Wawrzynczak7eb11362020-05-29 14:10:53 -060018 DEFAULT_PRIORITY = 100,
Tim Wawrzynczak2ad8ffe2020-05-29 14:39:02 -060019 DEFAULT_TRIP_POINT = 0xFFFFFFFFull,
Tim Wawrzynczakc41f7f12020-05-29 13:56:37 -060020 DEFAULT_WEIGHT = 100,
21 DPTF_MAX_ART_THRESHOLDS = 10,
Tim Wawrzynczaka9d3e652020-07-27 13:51:04 -060022 FPS_REVISION = 0,
Tim Wawrzynczakbb5c2552020-05-29 14:46:19 -060023 PPCC_REVISION = 2,
24 RAPL_PL1_INDEX = 0,
25 RAPL_PL2_INDEX = 1,
Tim Wawrzynczakc41f7f12020-05-29 13:56:37 -060026};
27
28/* Convert degrees C to 1/10 degree Kelvin for ACPI */
29static int to_acpi_temp(int deg_c)
30{
31 return deg_c * 10 + 2732;
32}
33
Tim Wawrzynczak7eb11362020-05-29 14:10:53 -060034/* Converts ms to 1/10th second for ACPI */
35static int to_acpi_time(int ms)
36{
37 return ms / 100;
38}
39
Tim Wawrzynczakc41f7f12020-05-29 13:56:37 -060040/* Writes out a 0-argument non-Serialized Method that returns an Integer */
41static void write_simple_return_method(const char *name, int value)
42{
43 acpigen_write_method(name, 0);
44 acpigen_write_return_integer(value);
45 acpigen_pop_len(); /* Method */
46}
47
Tim Wawrzynczak7eb11362020-05-29 14:10:53 -060048/* Writes out 'count' ZEROs in a row */
49static void write_zeros(int count)
50{
51 for (; count; --count)
52 acpigen_write_integer(0);
53}
54
Tim Wawrzynczakc41f7f12020-05-29 13:56:37 -060055/* Return the assigned namestring of any participant */
56static const char *namestring_of(enum dptf_participant participant)
57{
58 switch (participant) {
59 case DPTF_CPU:
60 return "TCPU";
61 case DPTF_CHARGER:
62 return "TCHG";
63 case DPTF_FAN:
64 return "TFN1";
Sumeet Pawnikar2f7fa552022-06-08 17:43:36 +053065 case DPTF_FAN_2:
66 return "TFN2";
Tim Wawrzynczakc41f7f12020-05-29 13:56:37 -060067 case DPTF_TEMP_SENSOR_0:
68 return "TSR0";
69 case DPTF_TEMP_SENSOR_1:
70 return "TSR1";
71 case DPTF_TEMP_SENSOR_2:
72 return "TSR2";
73 case DPTF_TEMP_SENSOR_3:
74 return "TSR3";
Tim Wawrzynczak40713aa2021-11-24 09:18:44 -070075 case DPTF_TEMP_SENSOR_4:
76 return "TSR4";
Sumeet Pawnikare2e0a6b2021-09-24 18:39:50 +053077 case DPTF_TPCH:
78 return "TPCH";
Varshit B Pandyae7d3a1a2022-03-20 20:39:51 +053079 case DPTF_POWER:
80 return "TPWR";
Varshit B Pandya170a76c2022-04-02 15:11:36 +053081 case DPTF_BATTERY:
82 return "TBAT";
Tim Wawrzynczakc41f7f12020-05-29 13:56:37 -060083 default:
84 return "";
85 }
86}
87
88/* Helper to get Scope for participants underneath \_SB.DPTF */
89static const char *scope_of(enum dptf_participant participant)
90{
91 static char scope[16];
92
93 if (participant == DPTF_CPU)
Tim Wawrzynczak5212ece62020-07-16 11:54:04 -060094 snprintf(scope, sizeof(scope), TCPU_SCOPE ".%s", namestring_of(participant));
Tim Wawrzynczakc41f7f12020-05-29 13:56:37 -060095 else
Tim Wawrzynczak5212ece62020-07-16 11:54:04 -060096 snprintf(scope, sizeof(scope), DPTF_DEVICE_PATH ".%s",
Tim Wawrzynczakc41f7f12020-05-29 13:56:37 -060097 namestring_of(participant));
98
99 return scope;
100}
101
Tim Wawrzynczak5212ece62020-07-16 11:54:04 -0600102/*
103 * Most of the DPTF participants are underneath the \_SB.DPTF scope, so we can just get away
104 * with using the simple namestring for references, but the TCPU has a different scope, so
105 * either an absolute or relative path must be used instead.
106 */
107static const char *path_of(enum dptf_participant participant)
108{
109 if (participant == DPTF_CPU)
110 return scope_of(participant);
111 else
112 return namestring_of(participant);
113}
114
Tim Wawrzynczakc41f7f12020-05-29 13:56:37 -0600115/* Write out scope of a participant */
116void dptf_write_scope(enum dptf_participant participant)
117{
118 acpigen_write_scope(scope_of(participant));
119}
120
121/*
122 * This table describes active cooling relationships between the system's fan and the
123 * temperature sensors that it can have an effect on. As ever-increasing temperature thresholds
124 * are crossed (_AC9.._AC0, low to high), the corresponding fan percentages listed in this table
125 * are used to increase the speed of the fan in order to speed up cooling.
126 */
127static void write_active_relationship_table(const struct dptf_active_policy *policies,
Sumeet Pawnikar2f7fa552022-06-08 17:43:36 +0530128 int max_count, bool dptf_multifan_support)
Tim Wawrzynczakc41f7f12020-05-29 13:56:37 -0600129{
130 char *pkg_count;
131 int i, j;
132
133 /* Nothing to do */
134 if (!max_count || policies[0].target == DPTF_NONE)
135 return;
136
Tim Wawrzynczak5212ece62020-07-16 11:54:04 -0600137 acpigen_write_scope(DPTF_DEVICE_PATH);
Tim Wawrzynczakc41f7f12020-05-29 13:56:37 -0600138 acpigen_write_method("_ART", 0);
139
140 /* Return this package */
141 acpigen_emit_byte(RETURN_OP);
142
143 /* Keep track of items added to the package */
144 pkg_count = acpigen_write_package(1); /* The '1' here is for the revision */
145 acpigen_write_integer(ART_REVISION);
146
147 for (i = 0; i < max_count; ++i) {
148 /*
149 * These have to be filled out from AC0 down to AC9, filling in only as many
150 * as are used. As soon as one isn't filled in, we're done.
151 */
152 if (policies[i].target == DPTF_NONE)
153 break;
154
155 (*pkg_count)++;
156
157 /* Source, Target, Percent, Fan % for each of _AC0 ... _AC9 */
158 acpigen_write_package(13);
Sumeet Pawnikar2f7fa552022-06-08 17:43:36 +0530159 if (dptf_multifan_support)
160 acpigen_emit_namestring(path_of(policies[i].source));
161 else
162 acpigen_emit_namestring(path_of(DPTF_FAN));
163
Tim Wawrzynczak5212ece62020-07-16 11:54:04 -0600164 acpigen_emit_namestring(path_of(policies[i].target));
Tim Wawrzynczakc41f7f12020-05-29 13:56:37 -0600165 acpigen_write_integer(DEFAULT_IF_0(policies[i].weight, DEFAULT_WEIGHT));
166
167 /* Write out fan %; corresponds with target's _ACx methods */
168 for (j = 0; j < DPTF_MAX_ART_THRESHOLDS; ++j)
169 acpigen_write_integer(policies[i].thresholds[j].fan_pct);
170
171 acpigen_pop_len(); /* inner Package */
172 }
173
174 acpigen_pop_len(); /* outer Package */
175 acpigen_pop_len(); /* Method _ART */
176 acpigen_pop_len(); /* Scope */
177}
178
179/*
180 * _AC9 through _AC0 represent temperature thresholds, in increasing order, defined from _AC0
181 * down, that, when reached, DPTF will activate TFN1 in order to actively cool the temperature
182 * sensor(s). As increasing thresholds are reached, the fan is spun faster.
183 */
184static void write_active_cooling_methods(const struct dptf_active_policy *policies,
185 int max_count)
186{
187 char name[5];
188 int i, j;
189
190 /* Nothing to do */
191 if (!max_count || policies[0].target == DPTF_NONE)
192 return;
193
194 for (i = 0; i < max_count; ++i) {
195 if (policies[i].target == DPTF_NONE)
196 break;
197
198 dptf_write_scope(policies[i].target);
199
200 /* Write out as many of _AC0 through _AC9 that are applicable */
201 for (j = 0; j < DPTF_MAX_ACX; ++j) {
202 if (!policies[i].thresholds[j].temp)
203 break;
204
205 snprintf(name, sizeof(name), "_AC%1X", j);
206 write_simple_return_method(name, to_acpi_temp(
207 policies[i].thresholds[j].temp));
208 }
209
210 acpigen_pop_len(); /* Scope */
211 }
212}
213
Sumeet Pawnikar2f7fa552022-06-08 17:43:36 +0530214void dptf_write_active_policies(const struct dptf_active_policy *policies,
215 int max_count, bool dptf_multifan_support)
Tim Wawrzynczakc41f7f12020-05-29 13:56:37 -0600216{
Sumeet Pawnikar2f7fa552022-06-08 17:43:36 +0530217 write_active_relationship_table(policies, max_count, dptf_multifan_support);
Tim Wawrzynczakc41f7f12020-05-29 13:56:37 -0600218 write_active_cooling_methods(policies, max_count);
219}
Tim Wawrzynczak7eb11362020-05-29 14:10:53 -0600220
221/*
222 * This writes out the Thermal Relationship Table, which describes the thermal relationships
223 * between participants in a thermal zone. This information is used to passively cool (i.e.,
224 * throttle) the Source (source of heat), in order to indirectly cool the Target (temperature
225 * sensor).
226 */
227static void write_thermal_relationship_table(const struct dptf_passive_policy *policies,
228 int max_count)
229{
230 char *pkg_count;
231 int i;
232
233 /* Nothing to do */
234 if (!max_count || policies[0].source == DPTF_NONE)
235 return;
236
Tim Wawrzynczak5212ece62020-07-16 11:54:04 -0600237 acpigen_write_scope(DPTF_DEVICE_PATH);
Tim Wawrzynczak7eb11362020-05-29 14:10:53 -0600238
239 /*
240 * A _TRT Revision (TRTR) of 1 means that the 'Priority' field is an arbitrary priority
241 * value to be used for this specific relationship. The priority value determines the
242 * order in which various sources are used in a passive thermal action for a given
243 * target.
244 */
245 acpigen_write_name_integer("TRTR", 1);
246
247 /* Thermal Relationship Table */
248 acpigen_write_method("_TRT", 0);
249
250 /* Return this package */
251 acpigen_emit_byte(RETURN_OP);
252 pkg_count = acpigen_write_package(0);
253
254 for (i = 0; i < max_count; ++i) {
255 /* Stop writing the table once an entry is empty */
256 if (policies[i].source == DPTF_NONE)
257 break;
258
259 /* Keep track of outer package item count */
260 (*pkg_count)++;
261
262 acpigen_write_package(8);
263
264 /* Source, Target, Priority, Sampling Period */
Tim Wawrzynczak5212ece62020-07-16 11:54:04 -0600265 acpigen_emit_namestring(path_of(policies[i].source));
266 acpigen_emit_namestring(path_of(policies[i].target));
Tim Wawrzynczak7eb11362020-05-29 14:10:53 -0600267 acpigen_write_integer(DEFAULT_IF_0(policies[i].priority, DEFAULT_PRIORITY));
268 acpigen_write_integer(to_acpi_time(policies[i].period));
269
270 /* Reserved */
271 write_zeros(4);
272
273 acpigen_pop_len(); /* Package */
274 }
275
276 acpigen_pop_len(); /* Package */
277 acpigen_pop_len(); /* Method */
278 acpigen_pop_len(); /* Scope */
279}
280
281/*
282 * When a temperature sensor measures above its the temperature returned in its _PSV Method,
283 * DPTF will begin throttling Sources in order to indirectly cool the sensor.
284 */
285static void write_all_PSV(const struct dptf_passive_policy *policies, int max_count)
286{
287 int i;
288
289 for (i = 0; i < max_count; ++i) {
290 if (policies[i].source == DPTF_NONE)
291 break;
292
293 dptf_write_scope(policies[i].target);
294 write_simple_return_method("_PSV", to_acpi_temp(policies[i].temp));
295 acpigen_pop_len(); /* Scope */
296 }
297}
298
299void dptf_write_passive_policies(const struct dptf_passive_policy *policies, int max_count)
300{
301 write_thermal_relationship_table(policies, max_count);
302 write_all_PSV(policies, max_count);
303}
Tim Wawrzynczak3a9cde92020-05-29 14:19:15 -0600304
305void dptf_write_critical_policies(const struct dptf_critical_policy *policies, int max_count)
306{
307 int i;
308
309 for (i = 0; i < max_count; ++i) {
310 if (policies[i].source == DPTF_NONE)
311 break;
312
313 dptf_write_scope(policies[i].source);
314
315 /* Choose _CRT or _HOT */
316 write_simple_return_method(policies[i].type == DPTF_CRITICAL_SHUTDOWN ?
317 "_CRT" : "_HOT", to_acpi_temp(policies[i].temp));
318
319 acpigen_pop_len(); /* Scope */
320 }
321}
Tim Wawrzynczak46f6fcf2020-05-29 14:29:53 -0600322
323void dptf_write_charger_perf(const struct dptf_charger_perf *states, int max_count)
324{
325 char *pkg_count;
326 int i;
327
328 if (!max_count || !states[0].control)
329 return;
330
331 dptf_write_scope(DPTF_CHARGER);
332
333 /* PPSS - Participant Performance Supported States */
334 acpigen_write_method("PPSS", 0);
335 acpigen_emit_byte(RETURN_OP);
336
337 pkg_count = acpigen_write_package(0);
338 for (i = 0; i < max_count; ++i) {
339 if (!states[i].control)
340 break;
341
342 (*pkg_count)++;
343
344 /*
345 * 0, 0, 0, 0, # Reserved
346 * Control, Raw Performance, Raw Unit, 0 # Reserved
347 */
348 acpigen_write_package(8);
349 write_zeros(4);
350 acpigen_write_integer(states[i].control);
351 acpigen_write_integer(states[i].raw_perf);
352 acpigen_write_string(DEFAULT_RAW_UNIT);
353 acpigen_write_integer(0);
354 acpigen_pop_len(); /* inner Package */
355 }
356
357 acpigen_pop_len(); /* outer Package */
358 acpigen_pop_len(); /* Method PPSS */
359 acpigen_pop_len(); /* Scope */
360}
Tim Wawrzynczak2ad8ffe2020-05-29 14:39:02 -0600361
Sumeet Pawnikar2f7fa552022-06-08 17:43:36 +0530362int dptf_write_fan_perf_fps(uint8_t percent, uint16_t power, uint16_t speed,
363 uint16_t noise_level)
364{
365 /*
366 * Some _FPS tables do include a last entry where Percent is 0, but Power is
367 * called out, so this table is finished when both are zero.
368 */
369 if (!percent && !power)
370 return 1;
371
372 acpigen_write_package(5);
373 acpigen_write_integer(percent);
374 acpigen_write_integer(DEFAULT_TRIP_POINT);
375 acpigen_write_integer(speed);
376 acpigen_write_integer(noise_level);
377 acpigen_write_integer(power);
378 acpigen_pop_len(); /* inner Package */
379
380 return 0;
381}
382
383void dptf_write_fan_perf(const struct dptf_fan_perf *states, int max_count,
384 enum dptf_participant participant)
Tim Wawrzynczak2ad8ffe2020-05-29 14:39:02 -0600385{
386 char *pkg_count;
387 int i;
388
389 if (!max_count || !states[0].percent)
390 return;
391
Sumeet Pawnikar2f7fa552022-06-08 17:43:36 +0530392 dptf_write_scope(participant);
Tim Wawrzynczak2ad8ffe2020-05-29 14:39:02 -0600393
394 /* _FPS - Fan Performance States */
395 acpigen_write_name("_FPS");
Sumeet Pawnikar2f7fa552022-06-08 17:43:36 +0530396
Tim Wawrzynczaka9d3e652020-07-27 13:51:04 -0600397 pkg_count = acpigen_write_package(1); /* 1 for Revision */
398 acpigen_write_integer(FPS_REVISION); /* revision */
Tim Wawrzynczak2ad8ffe2020-05-29 14:39:02 -0600399
400 for (i = 0; i < max_count; ++i) {
Sumeet Pawnikar672bd9b2022-06-08 17:43:36 +0530401 (*pkg_count)++;
Sumeet Pawnikar2f7fa552022-06-08 17:43:36 +0530402 if (dptf_write_fan_perf_fps(states[i].percent, states[i].power,
403 states[i].speed, states[i].noise_level))
404 break;
405 }
406
407 acpigen_pop_len(); /* Package */
408 acpigen_pop_len(); /* Scope */
409}
410
411void dptf_write_multifan_perf(
412 const struct dptf_multifan_perf
413 states[DPTF_MAX_FAN_PARTICIPANTS][DPTF_MAX_FAN_PERF_STATES],
414 int max_count, enum dptf_participant participant, int fan_num)
415{
416 char *pkg_count;
417 int i;
418
419 if (!max_count || !states[fan_num][0].percent)
420 return;
421
422 dptf_write_scope(participant);
423
424 /* _FPS - Fan Performance States */
425 acpigen_write_name("_FPS");
426
427 pkg_count = acpigen_write_package(1); /* 1 for Revision */
428 acpigen_write_integer(FPS_REVISION); /* revision */
429
430 for (i = 0; i < max_count; ++i) {
431 (*pkg_count)++;
432 if (dptf_write_fan_perf_fps(states[fan_num][i].percent, states[fan_num][i].power,
433 states[fan_num][i].speed, states[fan_num][i].noise_level))
434 break;
Tim Wawrzynczak2ad8ffe2020-05-29 14:39:02 -0600435 }
436
437 acpigen_pop_len(); /* Package */
438 acpigen_pop_len(); /* Scope */
439}
Tim Wawrzynczakbb5c2552020-05-29 14:46:19 -0600440
441void dptf_write_power_limits(const struct dptf_power_limits *limits)
442{
443 char *pkg_count;
444
445 /* Nothing to do */
446 if (!limits->pl1.min_power && !limits->pl2.min_power)
447 return;
448
449 dptf_write_scope(DPTF_CPU);
450 acpigen_write_method("PPCC", 0);
451
CoolStare145c2f2023-02-09 22:44:09 -0800452 acpigen_emit_byte(RETURN_OP);
453
Tim Wawrzynczakbb5c2552020-05-29 14:46:19 -0600454 pkg_count = acpigen_write_package(1); /* 1 for the Revision */
455 acpigen_write_integer(PPCC_REVISION); /* revision */
456
457 if (limits->pl1.min_power) {
458 (*pkg_count)++;
459 acpigen_write_package(6);
460 acpigen_write_integer(RAPL_PL1_INDEX);
461 acpigen_write_integer(limits->pl1.min_power);
462 acpigen_write_integer(limits->pl1.max_power);
463 acpigen_write_integer(limits->pl1.time_window_min);
464 acpigen_write_integer(limits->pl1.time_window_max);
465 acpigen_write_integer(limits->pl1.granularity);
466 acpigen_pop_len(); /* inner Package */
467 }
468
469 if (limits->pl2.min_power) {
470 (*pkg_count)++;
471 acpigen_write_package(6);
472 acpigen_write_integer(RAPL_PL2_INDEX);
473 acpigen_write_integer(limits->pl2.min_power);
474 acpigen_write_integer(limits->pl2.max_power);
475 acpigen_write_integer(limits->pl2.time_window_min);
476 acpigen_write_integer(limits->pl2.time_window_max);
477 acpigen_write_integer(limits->pl2.granularity);
478 acpigen_pop_len(); /* inner Package */
479 }
480
481 acpigen_pop_len(); /* outer Package */
482 acpigen_pop_len(); /* Method */
483 acpigen_pop_len(); /* Scope */
484}
Tim Wawrzynczake4d8ebc2020-05-29 14:58:16 -0600485
486void dptf_write_STR(const char *str)
487{
488 if (!str)
489 return;
490
491 acpigen_write_name_string("_STR", str);
492}
493
494void dptf_write_fan_options(bool fine_grained, int step_size, bool low_speed_notify)
495{
496 acpigen_write_name("_FIF");
497 acpigen_write_package(4);
498
499 acpigen_write_integer(0); /* Revision */
500 acpigen_write_integer(fine_grained);
501 acpigen_write_integer(step_size);
502 acpigen_write_integer(low_speed_notify);
503 acpigen_pop_len(); /* Package */
504}
505
506void dptf_write_tsr_hysteresis(uint8_t hysteresis)
507{
508 if (!hysteresis)
509 return;
510
511 acpigen_write_name_integer("GTSH", hysteresis);
512}
Tim Wawrzynczak03465f42020-06-03 16:27:30 -0600513
514void dptf_write_enabled_policies(const struct dptf_active_policy *active_policies,
515 int active_count,
516 const struct dptf_passive_policy *passive_policies,
517 int passive_count,
518 const struct dptf_critical_policy *critical_policies,
519 int critical_count)
520{
521 bool is_active_used;
522 bool is_passive_used;
523 bool is_critical_used;
524 int pkg_count;
525
526 is_active_used = (active_count && active_policies[0].target != DPTF_NONE);
527 is_passive_used = (passive_count && passive_policies[0].target != DPTF_NONE);
528 is_critical_used = (critical_count && critical_policies[0].source != DPTF_NONE);
529 pkg_count = is_active_used + is_passive_used + is_critical_used;
530
531 if (!pkg_count)
532 return;
533
Tim Wawrzynczak5212ece62020-07-16 11:54:04 -0600534 acpigen_write_scope(DPTF_DEVICE_PATH);
Tim Wawrzynczak03465f42020-06-03 16:27:30 -0600535 acpigen_write_name("IDSP");
536 acpigen_write_package(pkg_count);
537
538 if (is_active_used)
539 acpigen_write_uuid(DPTF_ACTIVE_POLICY_UUID);
540
541 if (is_passive_used)
542 acpigen_write_uuid(DPTF_PASSIVE_POLICY_1_0_UUID);
543
544 if (is_critical_used)
545 acpigen_write_uuid(DPTF_CRITICAL_POLICY_UUID);
546
547 acpigen_pop_len(); /* Package */
548 acpigen_pop_len(); /* Scope */
549}