1 /* SPDX-License-Identifier: GPL-2.0-only */
3 #include <acpi/acpigen.h>
4 #include <acpi/acpigen_dptf.h>
5 #include <ec/google/common/dptf.h>
6 #include <drivers/intel/dptf/chip.h>
11 * The Chrome EC is typically in charge of many system functions, including battery charging and
12 * fan PWM control. This places it in the middle of a DPTF implementation and therefore, many of
13 * the "helper" ACPI Methods themselves call EC Methods. Because of that, the responsibility for
14 * producing the corresponding AML lies here.
17 /* DPTF Event types */
19 TRIP_POINTS_CHANGED_EVENT
= 0x81,
25 EC_FAN_DUTY_AUTO
= 0xFF,
28 /* Return the fan number as a string for the FAN participant */
29 static const char *fan_num_namestring_of(enum dptf_participant participant
)
31 switch (participant
) {
41 static void write_charger_PPPC(const struct device
*ec
)
43 acpigen_write_method_serialized("PPPC", 0);
46 * Convert size of PPSS table to index
48 * Store (SizeOf (PPSS), Local0)
51 acpigen_write_store();
52 acpigen_emit_byte(SIZEOF_OP
);
53 acpigen_emit_namestring("PPSS");
54 acpigen_emit_byte(LOCAL0_OP
);
55 acpigen_emit_byte(DECREMENT_OP
);
56 acpigen_emit_byte(LOCAL0_OP
);
59 * Check if charging is disabled (AC removed)
61 * If (\_SB.PCI0.LPCB.EC0.ACEX () = Zero) {
66 acpigen_emit_byte(LEQUAL_OP
);
67 acpigen_emit_namestring(acpi_device_path_join(ec
, "ACEX"));
68 acpigen_emit_byte(ZERO_OP
);
69 acpigen_write_return_op(LOCAL0_OP
);
70 acpigen_pop_len(); /* If */
72 /* Return highest power state (index 0) */
73 acpigen_write_return_op(ZERO_OP
);
75 acpigen_pop_len(); /* Method */
78 static void write_charger_SPPC(const struct device
*ec
)
81 * SPPC - Set charger current limit
83 * Store (DeRefOf (Index (DeRefOf (Index
84 * (PPSS, ToInteger (Arg0))), 4)), Local0)
85 * \_SB.PCI0.LPCB.EC0.CHGS (Local0)
89 acpigen_write_method_serialized("SPPC", 1);
91 /* Retrieve Control (index 4) for specified PPSS level */
92 acpigen_emit_byte(STORE_OP
);
93 acpigen_emit_byte(DEREF_OP
);
94 acpigen_emit_byte(INDEX_OP
);
95 acpigen_emit_byte(DEREF_OP
);
96 acpigen_emit_byte(INDEX_OP
);
97 acpigen_emit_namestring("PPSS");
98 acpigen_write_to_integer(ARG0_OP
, ZERO_OP
);
99 acpigen_emit_byte(ZERO_OP
); /* 3rd arg to Index */
100 acpigen_write_integer(4); /* Index */
101 acpigen_emit_byte(ZERO_OP
); /* 3rd arg to Index */
102 acpigen_emit_byte(LOCAL0_OP
);
104 /* Pass Control value to EC to limit charging */
105 acpigen_emit_namestring(acpi_device_path_join(ec
, "CHGS"));
106 acpigen_emit_byte(LOCAL0_OP
);
107 acpigen_pop_len(); /* Method */
110 static void write_fan_fst(const struct device
*ec
, int participant
)
112 /* TFST is a package that is used to store data from FAND */
113 acpigen_write_name("TFST");
114 acpigen_write_package(3);
115 acpigen_write_integer(0); /* Revision */
116 acpigen_write_integer(0); /* Control */
117 acpigen_write_integer(0); /* Speed */
118 acpigen_pop_len(); /* Package */
121 acpigen_write_method_serialized("_FST", 0);
122 acpigen_write_store();
123 acpigen_emit_namestring(acpi_device_path_join(ec
, "FAND"));
124 acpigen_emit_byte(INDEX_OP
);
125 acpigen_emit_namestring("TFST");
126 acpigen_write_integer(1);
127 acpigen_emit_byte(ZERO_OP
); /* 3rd arg to Index */
128 acpigen_write_store();
129 acpigen_emit_namestring(acpi_device_path_join(ec
, fan_num_namestring_of(participant
)));
130 acpigen_emit_byte(INDEX_OP
);
131 acpigen_emit_namestring("TFST");
132 acpigen_write_integer(2);
133 acpigen_emit_byte(ZERO_OP
);
134 acpigen_emit_byte(RETURN_OP
);
135 acpigen_emit_namestring("TFST");
136 acpigen_pop_len(); /* Method _FST */
139 static void write_fan_fsl(const struct device
*ec
)
142 acpigen_write_method_serialized("_FSL", 1);
143 acpigen_write_store();
144 acpigen_emit_byte(ARG0_OP
);
145 acpigen_emit_namestring(acpi_device_path_join(ec
, "FAND"));
146 acpigen_pop_len(); /* Method _FSL */
150 * Emit code to execute if the policy is enabled after this function is called, and also
151 * remember to manually add a acpigen_pop_len() afterwards!
153 static void write_is_policy_enabled(bool enabled
)
156 * Local0 = SizeOf (IDSP)
160 * While (Local1 < Local0) {
161 * If (IDSP[Local1] == Arg0 && Arg1 == enabled) {
171 /* Local0 = SizeOf (IDSP) */
172 acpigen_write_store();
173 acpigen_emit_byte(SIZEOF_OP
);
174 acpigen_emit_namestring("IDSP");
175 acpigen_emit_byte(LOCAL0_OP
);
177 /* Local1 = 0 (index variable) */
178 acpigen_write_store();
179 acpigen_write_zero();
180 acpigen_emit_byte(LOCAL1_OP
);
182 /* Local2 = 0 (out variable, 1=found, 0=not found) */
183 acpigen_write_store();
184 acpigen_write_zero();
185 acpigen_emit_byte(LOCAL2_OP
);
188 * While (Local1 < Local0) {
190 acpigen_emit_byte(WHILE_OP
);
191 acpigen_write_len_f();
192 acpigen_emit_byte(LLESS_OP
);
193 acpigen_emit_byte(LOCAL1_OP
);
194 acpigen_emit_byte(LOCAL0_OP
);
196 /* If (IDSP[Local1] == Arg0 && Arg1 == 1) { */
198 acpigen_emit_byte(LAND_OP
);
199 acpigen_emit_byte(LEQUAL_OP
);
200 acpigen_emit_byte(DEREF_OP
);
201 acpigen_emit_byte(INDEX_OP
);
202 acpigen_emit_namestring("IDSP");
203 acpigen_emit_byte(LOCAL1_OP
);
204 acpigen_emit_byte(ZERO_OP
); /* 3rd arg of index - unused */
205 acpigen_emit_byte(ARG0_OP
); /* end lequal */
206 acpigen_emit_byte(LEQUAL_OP
);
207 acpigen_emit_byte(ARG1_OP
);
208 acpigen_write_integer(enabled
? 1 : 0);
211 acpigen_write_store();
213 acpigen_emit_byte(LOCAL2_OP
);
214 acpigen_pop_len(); /* If */
220 acpigen_emit_byte(INCREMENT_OP
);
221 acpigen_emit_byte(LOCAL1_OP
);
222 acpigen_pop_len(); /* While */
228 acpigen_emit_byte(LEQUAL_OP
);
229 acpigen_emit_byte(LOCAL2_OP
);
232 /* caller must insert acpigen_pop_len() ! */
235 static void write_dptf_OSC(const struct device
*ec
)
241 * Arg0: Buffer containing UUID
242 * Arg1: "Integer containing Revision ID of buffer format", but Linux passes whether
243 * it is enabling (1) or disabling (0) the policy in Arg1.
244 * Arg2: Integer containing count of entries in Arg3
245 * Arg3: Buffer containing list of DWORD capabilities
246 * Return: Buffer containing list of DWORD capabilities
248 acpigen_write_method_serialized("_OSC", 4);
251 * If the Passive Policy is enabled:
252 * 1) Disable temperature sensor trip points in the EC (replaces TINI)
253 * 2) Disable the charge limit in the EC (replaces TCHG.INIT)
255 write_is_policy_enabled(true);
256 for (i
= 0; i
< DPTF_MAX_TSR
; ++i
) {
257 snprintf(name
, sizeof(name
), "^TSR%1d.PATD", i
);
258 acpigen_emit_namestring(name
);
261 acpigen_emit_namestring(acpi_device_path_join(ec
, "CHGD"));
262 acpigen_pop_len(); /* If (from write_is_policy_enabled) */
264 /* If the Active Policy is disabled, disable DPTF fan control in the EC */
265 write_is_policy_enabled(false);
266 acpigen_write_store();
267 acpigen_write_integer(EC_FAN_DUTY_AUTO
);
268 acpigen_emit_namestring(acpi_device_path_join(ec
, "FAND"));
269 acpigen_pop_len(); /* If (from write_is_policy_enabled) */
271 acpigen_write_return_op(ARG3_OP
);
272 acpigen_pop_len(); /* Method _OSC */
275 static void write_dppm_methods(const struct device
*ec
)
277 enum dptf_participant p
;
281 acpigen_write_scope("\\_SB.DPTF");
285 if (CONFIG(EC_SUPPORTS_DPTF_TEVT
)) {
286 acpigen_write_method("TEVT", 1);
288 /* Local0 = ToInteger(Arg0) */
289 acpigen_write_to_integer(ARG0_OP
, LOCAL0_OP
);
290 for (p
= DPTF_TEMP_SENSOR_0
, i
= 0; p
<= DPTF_TEMP_SENSOR_4
; ++p
, ++i
) {
291 snprintf(name
, sizeof(name
), "^TSR%1d", i
);
292 acpigen_write_if_lequal_op_int(LOCAL0_OP
, i
);
293 acpigen_notify(name
, THERMAL_EVENT
);
294 acpigen_pop_len(); /* If */
297 acpigen_pop_len(); /* Method */
301 acpigen_write_method("TPET", 0);
302 for (p
= DPTF_TEMP_SENSOR_0
, i
= 0; p
<= DPTF_TEMP_SENSOR_4
; ++p
, ++i
) {
303 snprintf(name
, sizeof(name
), "^TSR%1d", i
);
304 acpigen_notify(name
, TRIP_POINTS_CHANGED_EVENT
);
307 acpigen_pop_len(); /* Method */
308 acpigen_pop_len(); /* Scope */
311 static void write_charger_methods(const struct device
*ec
)
313 dptf_write_scope(DPTF_CHARGER
);
314 write_charger_PPPC(ec
);
315 write_charger_SPPC(ec
);
316 acpigen_pop_len(); /* Scope */
319 static void write_fan_methods(const struct device
*ec
, int participant
)
321 dptf_write_scope(participant
);
323 write_fan_fst(ec
, participant
);
324 acpigen_pop_len(); /* Scope */
327 static void write_thermal_methods(const struct device
*ec
, enum dptf_participant participant
,
330 dptf_write_scope(participant
);
333 * GTSH - Amount of hysteresis inherent in temperature reading (2 degrees, in units of
336 acpigen_write_name_integer("GTSH", 20);
338 /* _TMP - read temperature from EC */
339 acpigen_write_method_serialized("_TMP", 0);
340 acpigen_emit_byte(RETURN_OP
);
341 acpigen_emit_namestring(acpi_device_path_join(ec
, "TSRD"));
342 acpigen_write_integer(tsr_index
);
343 acpigen_pop_len(); /* Method _TMP */
345 /* PATC - Aux trip point count */
346 acpigen_write_name_integer("PATC", 2);
348 /* PAT0 - Set Aux trip point 0 */
349 acpigen_write_method_serialized("PAT0", 1);
350 acpigen_emit_namestring(acpi_device_path_join(ec
, "PAT0"));
351 acpigen_write_integer(tsr_index
);
352 acpigen_emit_byte(ARG0_OP
);
353 acpigen_pop_len(); /* Method PAT0 */
355 /* PAT1 - Set Aux trip point 1 */
356 acpigen_write_method_serialized("PAT1", 1);
357 acpigen_emit_namestring(acpi_device_path_join(ec
, "PAT1"));
358 acpigen_write_integer(tsr_index
);
359 acpigen_emit_byte(ARG0_OP
);
360 acpigen_pop_len(); /* Method PAT0 */
362 /* PATD - Disable Aux trip point */
363 acpigen_write_method_serialized("PATD", 0);
364 acpigen_emit_namestring(acpi_device_path_join(ec
, "PATD"));
365 acpigen_write_integer(tsr_index
);
366 acpigen_pop_len(); /* Method PAT0 */
368 acpigen_pop_len(); /* Scope */
371 void ec_fill_dptf_helpers(const struct device
*ec
, const struct device
*fan_dev
)
373 enum dptf_participant p
;
375 struct ec_google_chromeec_config
*config
= fan_dev
->chip_info
;
377 write_dppm_methods(ec
);
378 write_charger_methods(ec
);
380 if (config
->ec_multifan_support
) {
381 for (p
= DPTF_FAN
; p
<= DPTF_FAN_2
; ++p
)
382 write_fan_methods(ec
, p
);
384 write_fan_methods(ec
, DPTF_FAN
);
386 for (p
= DPTF_TEMP_SENSOR_0
, i
= 0; p
<= DPTF_TEMP_SENSOR_4
; ++p
, ++i
)
387 write_thermal_methods(ec
, p
, i
);