1 /* SPDX-License-Identifier: GPL-2.0-only */
2
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>
7 #include <stdio.h>
8
9 #include "chip.h"
10 /*
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.
15 */
16
17 /* DPTF Event types */
18 enum {
19 TRIP_POINTS_CHANGED_EVENT = 0x81,
20 THERMAL_EVENT = 0x90,
21 };
22
23 /* EC constants */
24 enum {
25 EC_FAN_DUTY_AUTO = 0xFF,
26 };
27
28 /* Return the fan number as a string for the FAN participant */
fan_num_namestring_of(enum dptf_participant participant)29 static const char *fan_num_namestring_of(enum dptf_participant participant)
30 {
31 switch (participant) {
32 case DPTF_FAN:
33 return "FAN0";
34 case DPTF_FAN_2:
35 return "FAN1";
36 default:
37 return "";
38 }
39 }
40
write_charger_PPPC(const struct device * ec)41 static void write_charger_PPPC(const struct device *ec)
42 {
43 acpigen_write_method_serialized("PPPC", 0);
44
45 /*
46 * Convert size of PPSS table to index
47 *
48 * Store (SizeOf (PPSS), Local0)
49 * Decrement (Local0)
50 */
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);
57
58 /*
59 * Check if charging is disabled (AC removed)
60 *
61 * If (\_SB.PCI0.LPCB.EC0.ACEX () = Zero) {
62 * Return (Local0)
63 * }
64 */
65 acpigen_write_if();
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 */
71
72 /* Return highest power state (index 0) */
73 acpigen_write_return_op(ZERO_OP);
74
75 acpigen_pop_len(); /* Method */
76 }
77
write_charger_SPPC(const struct device * ec)78 static void write_charger_SPPC(const struct device *ec)
79 {
80 /*
81 * SPPC - Set charger current limit
82 * Method(SPPC, 1) {
83 * Store (DeRefOf (Index (DeRefOf (Index
84 * (PPSS, ToInteger (Arg0))), 4)), Local0)
85 * \_SB.PCI0.LPCB.EC0.CHGS (Local0)
86 * }
87 */
88
89 acpigen_write_method_serialized("SPPC", 1);
90
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);
103
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 */
108 }
109
write_fan_fst(const struct device * ec,int participant)110 static void write_fan_fst(const struct device *ec, int participant)
111 {
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 */
119
120 /* _FST */
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 */
137 }
138
write_fan_fsl(const struct device * ec)139 static void write_fan_fsl(const struct device *ec)
140 {
141 /* _FSL */
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 */
147 }
148
149 /*
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!
152 */
write_is_policy_enabled(bool enabled)153 static void write_is_policy_enabled(bool enabled)
154 {
155 /*
156 * Local0 = SizeOf (IDSP)
157 * Local1 = 0
158 * Local2 = 0
159 *
160 * While (Local1 < Local0) {
161 * If (IDSP[Local1] == Arg0 && Arg1 == enabled) {
162 * Local2 = 1
163 * }
164 * Local1++
165 * }
166 *
167 * If (Local2 == 1) {
168 * ..........
169 */
170
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);
176
177 /* Local1 = 0 (index variable) */
178 acpigen_write_store();
179 acpigen_write_zero();
180 acpigen_emit_byte(LOCAL1_OP);
181
182 /* Local2 = 0 (out variable, 1=found, 0=not found) */
183 acpigen_write_store();
184 acpigen_write_zero();
185 acpigen_emit_byte(LOCAL2_OP);
186
187 /*
188 * While (Local1 < Local0) {
189 */
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);
195
196 /* If (IDSP[Local1] == Arg0 && Arg1 == 1) { */
197 acpigen_write_if();
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);
209
210 /* { Local2 = 1 } */
211 acpigen_write_store();
212 acpigen_write_one();
213 acpigen_emit_byte(LOCAL2_OP);
214 acpigen_pop_len(); /* If */
215
216 /*
217 * Local1++
218 * } # End of While
219 */
220 acpigen_emit_byte(INCREMENT_OP);
221 acpigen_emit_byte(LOCAL1_OP);
222 acpigen_pop_len(); /* While */
223
224 /*
225 * If (Local2 == 1)
226 */
227 acpigen_write_if();
228 acpigen_emit_byte(LEQUAL_OP);
229 acpigen_emit_byte(LOCAL2_OP);
230 acpigen_write_one();
231
232 /* caller must insert acpigen_pop_len() ! */
233 }
234
write_dptf_OSC(const struct device * ec)235 static void write_dptf_OSC(const struct device *ec)
236 {
237 char name[16];
238 int i;
239
240 /*
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
247 */
248 acpigen_write_method_serialized("_OSC", 4);
249
250 /*
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)
254 */
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);
259 }
260
261 acpigen_emit_namestring(acpi_device_path_join(ec, "CHGD"));
262 acpigen_pop_len(); /* If (from write_is_policy_enabled) */
263
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) */
270
271 acpigen_write_return_op(ARG3_OP);
272 acpigen_pop_len(); /* Method _OSC */
273 }
274
write_dppm_methods(const struct device * ec)275 static void write_dppm_methods(const struct device *ec)
276 {
277 enum dptf_participant p;
278 char name[16];
279 int i;
280
281 acpigen_write_scope("\\_SB.DPTF");
282 write_dptf_OSC(ec);
283
284 /* TEVT */
285 if (CONFIG(EC_SUPPORTS_DPTF_TEVT)) {
286 acpigen_write_method("TEVT", 1);
287
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 */
295 }
296
297 acpigen_pop_len(); /* Method */
298 }
299
300 /* TPET */
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);
305 }
306
307 acpigen_pop_len(); /* Method */
308 acpigen_pop_len(); /* Scope */
309 }
310
write_charger_methods(const struct device * ec)311 static void write_charger_methods(const struct device *ec)
312 {
313 dptf_write_scope(DPTF_CHARGER);
314 write_charger_PPPC(ec);
315 write_charger_SPPC(ec);
316 acpigen_pop_len(); /* Scope */
317 }
318
write_fan_methods(const struct device * ec,int participant)319 static void write_fan_methods(const struct device *ec, int participant)
320 {
321 dptf_write_scope(participant);
322 write_fan_fsl(ec);
323 write_fan_fst(ec, participant);
324 acpigen_pop_len(); /* Scope */
325 }
326
write_thermal_methods(const struct device * ec,enum dptf_participant participant,int tsr_index)327 static void write_thermal_methods(const struct device *ec, enum dptf_participant participant,
328 int tsr_index)
329 {
330 dptf_write_scope(participant);
331
332 /*
333 * GTSH - Amount of hysteresis inherent in temperature reading (2 degrees, in units of
334 * 1/10th degree K)
335 */
336 acpigen_write_name_integer("GTSH", 20);
337
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 */
344
345 /* PATC - Aux trip point count */
346 acpigen_write_name_integer("PATC", 2);
347
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 */
354
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 */
361
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 */
367
368 acpigen_pop_len(); /* Scope */
369 }
370
ec_fill_dptf_helpers(const struct device * ec,const struct device * fan_dev)371 void ec_fill_dptf_helpers(const struct device *ec, const struct device *fan_dev)
372 {
373 enum dptf_participant p;
374 int i;
375 struct ec_google_chromeec_config *config = fan_dev->chip_info;
376
377 write_dppm_methods(ec);
378 write_charger_methods(ec);
379
380 if (config->ec_multifan_support) {
381 for (p = DPTF_FAN; p <= DPTF_FAN_2; ++p)
382 write_fan_methods(ec, p);
383 } else
384 write_fan_methods(ec, DPTF_FAN);
385
386 for (p = DPTF_TEMP_SENSOR_0, i = 0; p <= DPTF_TEMP_SENSOR_4; ++p, ++i)
387 write_thermal_methods(ec, p, i);
388 }
389