blob: a4b3b09ba1e594e835da932700389bf26a1b67bc [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";
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";
Tim Wawrzynczak40713aa2021-11-24 09:18:44 -070073 case DPTF_TEMP_SENSOR_4:
74 return "TSR4";
Sumeet Pawnikare2e0a6b2021-09-24 18:39:50 +053075 case DPTF_TPCH:
76 return "TPCH";
Varshit B Pandyae7d3a1a2022-03-20 20:39:51 +053077 case DPTF_POWER:
78 return "TPWR";
Varshit B Pandya170a76c2022-04-02 15:11:36 +053079 case DPTF_BATTERY:
80 return "TBAT";
Tim Wawrzynczakc41f7f12020-05-29 13:56:37 -060081 default:
82 return "";
83 }
84}
85
86/* Helper to get Scope for participants underneath \_SB.DPTF */
87static const char *scope_of(enum dptf_participant participant)
88{
89 static char scope[16];
90
91 if (participant == DPTF_CPU)
Tim Wawrzynczak5212ece62020-07-16 11:54:04 -060092 snprintf(scope, sizeof(scope), TCPU_SCOPE ".%s", namestring_of(participant));
Tim Wawrzynczakc41f7f12020-05-29 13:56:37 -060093 else
Tim Wawrzynczak5212ece62020-07-16 11:54:04 -060094 snprintf(scope, sizeof(scope), DPTF_DEVICE_PATH ".%s",
Tim Wawrzynczakc41f7f12020-05-29 13:56:37 -060095 namestring_of(participant));
96
97 return scope;
98}
99
Tim Wawrzynczak5212ece62020-07-16 11:54:04 -0600100/*
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.
104 */
105static const char *path_of(enum dptf_participant participant)
106{
107 if (participant == DPTF_CPU)
108 return scope_of(participant);
109 else
110 return namestring_of(participant);
111}
112
Tim Wawrzynczakc41f7f12020-05-29 13:56:37 -0600113/* Write out scope of a participant */
114void dptf_write_scope(enum dptf_participant participant)
115{
116 acpigen_write_scope(scope_of(participant));
117}
118
119/*
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.
124 */
125static void write_active_relationship_table(const struct dptf_active_policy *policies,
Sumeet Pawnikar4dba71f2022-10-19 15:15:54 +0530126 int max_count)
Tim Wawrzynczakc41f7f12020-05-29 13:56:37 -0600127{
128 char *pkg_count;
129 int i, j;
130
131 /* Nothing to do */
132 if (!max_count || policies[0].target == DPTF_NONE)
133 return;
134
Tim Wawrzynczak5212ece62020-07-16 11:54:04 -0600135 acpigen_write_scope(DPTF_DEVICE_PATH);
Tim Wawrzynczakc41f7f12020-05-29 13:56:37 -0600136 acpigen_write_method("_ART", 0);
137
138 /* Return this package */
139 acpigen_emit_byte(RETURN_OP);
140
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);
144
145 for (i = 0; i < max_count; ++i) {
146 /*
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.
149 */
150 if (policies[i].target == DPTF_NONE)
151 break;
152
153 (*pkg_count)++;
154
155 /* Source, Target, Percent, Fan % for each of _AC0 ... _AC9 */
156 acpigen_write_package(13);
Sumeet Pawnikar4dba71f2022-10-19 15:15:54 +0530157 acpigen_emit_namestring(path_of(DPTF_FAN));
Tim Wawrzynczak5212ece62020-07-16 11:54:04 -0600158 acpigen_emit_namestring(path_of(policies[i].target));
Tim Wawrzynczakc41f7f12020-05-29 13:56:37 -0600159 acpigen_write_integer(DEFAULT_IF_0(policies[i].weight, DEFAULT_WEIGHT));
160
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);
164
165 acpigen_pop_len(); /* inner Package */
166 }
167
168 acpigen_pop_len(); /* outer Package */
169 acpigen_pop_len(); /* Method _ART */
170 acpigen_pop_len(); /* Scope */
171}
172
173/*
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.
177 */
178static void write_active_cooling_methods(const struct dptf_active_policy *policies,
179 int max_count)
180{
181 char name[5];
182 int i, j;
183
184 /* Nothing to do */
185 if (!max_count || policies[0].target == DPTF_NONE)
186 return;
187
188 for (i = 0; i < max_count; ++i) {
189 if (policies[i].target == DPTF_NONE)
190 break;
191
192 dptf_write_scope(policies[i].target);
193
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;
198
199 snprintf(name, sizeof(name), "_AC%1X", j);
200 write_simple_return_method(name, to_acpi_temp(
201 policies[i].thresholds[j].temp));
202 }
203
204 acpigen_pop_len(); /* Scope */
205 }
206}
207
Sumeet Pawnikar4dba71f2022-10-19 15:15:54 +0530208void dptf_write_active_policies(const struct dptf_active_policy *policies, int max_count)
Tim Wawrzynczakc41f7f12020-05-29 13:56:37 -0600209{
Sumeet Pawnikar4dba71f2022-10-19 15:15:54 +0530210 write_active_relationship_table(policies, max_count);
Tim Wawrzynczakc41f7f12020-05-29 13:56:37 -0600211 write_active_cooling_methods(policies, max_count);
212}
Tim Wawrzynczak7eb11362020-05-29 14:10:53 -0600213
214/*
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).
219 */
220static void write_thermal_relationship_table(const struct dptf_passive_policy *policies,
221 int max_count)
222{
223 char *pkg_count;
224 int i;
225
226 /* Nothing to do */
227 if (!max_count || policies[0].source == DPTF_NONE)
228 return;
229
Tim Wawrzynczak5212ece62020-07-16 11:54:04 -0600230 acpigen_write_scope(DPTF_DEVICE_PATH);
Tim Wawrzynczak7eb11362020-05-29 14:10:53 -0600231
232 /*
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.
237 */
238 acpigen_write_name_integer("TRTR", 1);
239
240 /* Thermal Relationship Table */
241 acpigen_write_method("_TRT", 0);
242
243 /* Return this package */
244 acpigen_emit_byte(RETURN_OP);
245 pkg_count = acpigen_write_package(0);
246
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;
251
252 /* Keep track of outer package item count */
253 (*pkg_count)++;
254
255 acpigen_write_package(8);
256
257 /* Source, Target, Priority, Sampling Period */
Tim Wawrzynczak5212ece62020-07-16 11:54:04 -0600258 acpigen_emit_namestring(path_of(policies[i].source));
259 acpigen_emit_namestring(path_of(policies[i].target));
Tim Wawrzynczak7eb11362020-05-29 14:10:53 -0600260 acpigen_write_integer(DEFAULT_IF_0(policies[i].priority, DEFAULT_PRIORITY));
261 acpigen_write_integer(to_acpi_time(policies[i].period));
262
263 /* Reserved */
264 write_zeros(4);
265
266 acpigen_pop_len(); /* Package */
267 }
268
269 acpigen_pop_len(); /* Package */
270 acpigen_pop_len(); /* Method */
271 acpigen_pop_len(); /* Scope */
272}
273
274/*
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.
277 */
278static void write_all_PSV(const struct dptf_passive_policy *policies, int max_count)
279{
280 int i;
281
282 for (i = 0; i < max_count; ++i) {
283 if (policies[i].source == DPTF_NONE)
284 break;
285
286 dptf_write_scope(policies[i].target);
287 write_simple_return_method("_PSV", to_acpi_temp(policies[i].temp));
288 acpigen_pop_len(); /* Scope */
289 }
290}
291
292void dptf_write_passive_policies(const struct dptf_passive_policy *policies, int max_count)
293{
294 write_thermal_relationship_table(policies, max_count);
295 write_all_PSV(policies, max_count);
296}
Tim Wawrzynczak3a9cde92020-05-29 14:19:15 -0600297
298void dptf_write_critical_policies(const struct dptf_critical_policy *policies, int max_count)
299{
300 int i;
301
302 for (i = 0; i < max_count; ++i) {
303 if (policies[i].source == DPTF_NONE)
304 break;
305
306 dptf_write_scope(policies[i].source);
307
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));
311
312 acpigen_pop_len(); /* Scope */
313 }
314}
Tim Wawrzynczak46f6fcf2020-05-29 14:29:53 -0600315
316void dptf_write_charger_perf(const struct dptf_charger_perf *states, int max_count)
317{
318 char *pkg_count;
319 int i;
320
321 if (!max_count || !states[0].control)
322 return;
323
324 dptf_write_scope(DPTF_CHARGER);
325
326 /* PPSS - Participant Performance Supported States */
327 acpigen_write_method("PPSS", 0);
328 acpigen_emit_byte(RETURN_OP);
329
330 pkg_count = acpigen_write_package(0);
331 for (i = 0; i < max_count; ++i) {
332 if (!states[i].control)
333 break;
334
335 (*pkg_count)++;
336
337 /*
338 * 0, 0, 0, 0, # Reserved
339 * Control, Raw Performance, Raw Unit, 0 # Reserved
340 */
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 */
348 }
349
350 acpigen_pop_len(); /* outer Package */
351 acpigen_pop_len(); /* Method PPSS */
352 acpigen_pop_len(); /* Scope */
353}
Tim Wawrzynczak2ad8ffe2020-05-29 14:39:02 -0600354
Sumeet Pawnikar4dba71f2022-10-19 15:15:54 +0530355void dptf_write_fan_perf(const struct dptf_fan_perf *states, int max_count)
Tim Wawrzynczak2ad8ffe2020-05-29 14:39:02 -0600356{
357 char *pkg_count;
358 int i;
359
360 if (!max_count || !states[0].percent)
361 return;
362
Sumeet Pawnikar4dba71f2022-10-19 15:15:54 +0530363 dptf_write_scope(DPTF_FAN);
Tim Wawrzynczak2ad8ffe2020-05-29 14:39:02 -0600364
365 /* _FPS - Fan Performance States */
366 acpigen_write_name("_FPS");
Tim Wawrzynczaka9d3e652020-07-27 13:51:04 -0600367 pkg_count = acpigen_write_package(1); /* 1 for Revision */
368 acpigen_write_integer(FPS_REVISION); /* revision */
Tim Wawrzynczak2ad8ffe2020-05-29 14:39:02 -0600369
370 for (i = 0; i < max_count; ++i) {
Sumeet Pawnikar4dba71f2022-10-19 15:15:54 +0530371 /*
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.
374 */
375 if (!states[i].percent && !states[i].power)
Sumeet Pawnikar672bd9b2022-06-08 17:43:36 +0530376 break;
Sumeet Pawnikar672bd9b2022-06-08 17:43:36 +0530377
Sumeet Pawnikar672bd9b2022-06-08 17:43:36 +0530378 (*pkg_count)++;
Sumeet Pawnikar4dba71f2022-10-19 15:15:54 +0530379 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 */
Tim Wawrzynczak2ad8ffe2020-05-29 14:39:02 -0600386 }
387
388 acpigen_pop_len(); /* Package */
389 acpigen_pop_len(); /* Scope */
390}
Tim Wawrzynczakbb5c2552020-05-29 14:46:19 -0600391
392void dptf_write_power_limits(const struct dptf_power_limits *limits)
393{
394 char *pkg_count;
395
396 /* Nothing to do */
397 if (!limits->pl1.min_power && !limits->pl2.min_power)
398 return;
399
400 dptf_write_scope(DPTF_CPU);
401 acpigen_write_method("PPCC", 0);
402
403 pkg_count = acpigen_write_package(1); /* 1 for the Revision */
404 acpigen_write_integer(PPCC_REVISION); /* revision */
405
406 if (limits->pl1.min_power) {
407 (*pkg_count)++;
408 acpigen_write_package(6);
409 acpigen_write_integer(RAPL_PL1_INDEX);
410 acpigen_write_integer(limits->pl1.min_power);
411 acpigen_write_integer(limits->pl1.max_power);
412 acpigen_write_integer(limits->pl1.time_window_min);
413 acpigen_write_integer(limits->pl1.time_window_max);
414 acpigen_write_integer(limits->pl1.granularity);
415 acpigen_pop_len(); /* inner Package */
416 }
417
418 if (limits->pl2.min_power) {
419 (*pkg_count)++;
420 acpigen_write_package(6);
421 acpigen_write_integer(RAPL_PL2_INDEX);
422 acpigen_write_integer(limits->pl2.min_power);
423 acpigen_write_integer(limits->pl2.max_power);
424 acpigen_write_integer(limits->pl2.time_window_min);
425 acpigen_write_integer(limits->pl2.time_window_max);
426 acpigen_write_integer(limits->pl2.granularity);
427 acpigen_pop_len(); /* inner Package */
428 }
429
430 acpigen_pop_len(); /* outer Package */
431 acpigen_pop_len(); /* Method */
432 acpigen_pop_len(); /* Scope */
433}
Tim Wawrzynczake4d8ebc2020-05-29 14:58:16 -0600434
435void dptf_write_STR(const char *str)
436{
437 if (!str)
438 return;
439
440 acpigen_write_name_string("_STR", str);
441}
442
443void dptf_write_fan_options(bool fine_grained, int step_size, bool low_speed_notify)
444{
445 acpigen_write_name("_FIF");
446 acpigen_write_package(4);
447
448 acpigen_write_integer(0); /* Revision */
449 acpigen_write_integer(fine_grained);
450 acpigen_write_integer(step_size);
451 acpigen_write_integer(low_speed_notify);
452 acpigen_pop_len(); /* Package */
453}
454
455void dptf_write_tsr_hysteresis(uint8_t hysteresis)
456{
457 if (!hysteresis)
458 return;
459
460 acpigen_write_name_integer("GTSH", hysteresis);
461}
Tim Wawrzynczak03465f42020-06-03 16:27:30 -0600462
463void dptf_write_enabled_policies(const struct dptf_active_policy *active_policies,
464 int active_count,
465 const struct dptf_passive_policy *passive_policies,
466 int passive_count,
467 const struct dptf_critical_policy *critical_policies,
468 int critical_count)
469{
470 bool is_active_used;
471 bool is_passive_used;
472 bool is_critical_used;
473 int pkg_count;
474
475 is_active_used = (active_count && active_policies[0].target != DPTF_NONE);
476 is_passive_used = (passive_count && passive_policies[0].target != DPTF_NONE);
477 is_critical_used = (critical_count && critical_policies[0].source != DPTF_NONE);
478 pkg_count = is_active_used + is_passive_used + is_critical_used;
479
480 if (!pkg_count)
481 return;
482
Tim Wawrzynczak5212ece62020-07-16 11:54:04 -0600483 acpigen_write_scope(DPTF_DEVICE_PATH);
Tim Wawrzynczak03465f42020-06-03 16:27:30 -0600484 acpigen_write_name("IDSP");
485 acpigen_write_package(pkg_count);
486
487 if (is_active_used)
488 acpigen_write_uuid(DPTF_ACTIVE_POLICY_UUID);
489
490 if (is_passive_used)
491 acpigen_write_uuid(DPTF_PASSIVE_POLICY_1_0_UUID);
492
493 if (is_critical_used)
494 acpigen_write_uuid(DPTF_CRITICAL_POLICY_UUID);
495
496 acpigen_pop_len(); /* Package */
497 acpigen_pop_len(); /* Scope */
498}