]>
Commit | Line | Data |
---|---|---|
0e5a0a00 SG |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Generation of tables for particular device types | |
4 | * | |
5 | * Copyright 2019 Google LLC | |
6 | * Mostly taken from coreboot file acpi_device.c | |
7 | */ | |
8 | ||
0e5a0a00 SG |
9 | #include <dm.h> |
10 | #include <log.h> | |
11 | #include <malloc.h> | |
58d825fb | 12 | #include <u-boot/uuid.h> |
0e5a0a00 SG |
13 | #include <acpi/acpigen.h> |
14 | #include <acpi/acpi_dp.h> | |
15 | #include <dm/acpi.h> | |
16 | ||
17 | static void acpi_dp_write_array(struct acpi_ctx *ctx, | |
18 | const struct acpi_dp *array); | |
19 | ||
20 | static void acpi_dp_write_value(struct acpi_ctx *ctx, | |
21 | const struct acpi_dp *prop) | |
22 | { | |
23 | switch (prop->type) { | |
24 | case ACPI_DP_TYPE_INTEGER: | |
25 | acpigen_write_integer(ctx, prop->integer); | |
26 | break; | |
27 | case ACPI_DP_TYPE_STRING: | |
28 | case ACPI_DP_TYPE_CHILD: | |
29 | acpigen_write_string(ctx, prop->string); | |
30 | break; | |
31 | case ACPI_DP_TYPE_REFERENCE: | |
32 | acpigen_emit_namestring(ctx, prop->string); | |
33 | break; | |
34 | case ACPI_DP_TYPE_ARRAY: | |
35 | acpi_dp_write_array(ctx, prop->array); | |
36 | break; | |
37 | default: | |
38 | break; | |
39 | } | |
40 | } | |
41 | ||
42 | /* Package (2) { "prop->name", VALUE } */ | |
43 | static void acpi_dp_write_property(struct acpi_ctx *ctx, | |
44 | const struct acpi_dp *prop) | |
45 | { | |
46 | acpigen_write_package(ctx, 2); | |
47 | acpigen_write_string(ctx, prop->name); | |
48 | acpi_dp_write_value(ctx, prop); | |
49 | acpigen_pop_len(ctx); | |
50 | } | |
51 | ||
52 | /* Write array of Device Properties */ | |
53 | static void acpi_dp_write_array(struct acpi_ctx *ctx, | |
54 | const struct acpi_dp *array) | |
55 | { | |
56 | const struct acpi_dp *dp; | |
57 | char *pkg_count; | |
58 | ||
59 | /* Package element count determined as it is populated */ | |
60 | pkg_count = acpigen_write_package(ctx, 0); | |
61 | ||
62 | /* | |
63 | * Only acpi_dp of type DP_TYPE_TABLE is allowed to be an array. | |
64 | * DP_TYPE_TABLE does not have a value to be written. Thus, start | |
65 | * the loop from next type in the array. | |
66 | */ | |
67 | for (dp = array->next; dp; dp = dp->next) { | |
68 | acpi_dp_write_value(ctx, dp); | |
69 | (*pkg_count)++; | |
70 | } | |
71 | ||
72 | acpigen_pop_len(ctx); | |
73 | } | |
74 | ||
75 | static void acpi_dp_free(struct acpi_dp *dp) | |
76 | { | |
77 | assert(dp); | |
78 | while (dp) { | |
79 | struct acpi_dp *p = dp->next; | |
80 | ||
81 | switch (dp->type) { | |
82 | case ACPI_DP_TYPE_CHILD: | |
83 | acpi_dp_free(dp->child); | |
84 | break; | |
85 | case ACPI_DP_TYPE_ARRAY: | |
86 | acpi_dp_free(dp->array); | |
87 | break; | |
88 | default: | |
89 | break; | |
90 | } | |
91 | ||
92 | free(dp); | |
93 | dp = p; | |
94 | } | |
95 | } | |
96 | ||
97 | static int acpi_dp_write_internal(struct acpi_ctx *ctx, struct acpi_dp *table) | |
98 | { | |
99 | struct acpi_dp *dp, *prop; | |
100 | char *dp_count, *prop_count = NULL; | |
101 | int child_count = 0; | |
102 | int ret; | |
103 | ||
104 | assert(table); | |
105 | if (table->type != ACPI_DP_TYPE_TABLE) | |
106 | return 0; | |
107 | ||
108 | /* Name (name) */ | |
109 | acpigen_write_name(ctx, table->name); | |
110 | ||
111 | /* Device Property list starts with the next entry */ | |
112 | prop = table->next; | |
113 | ||
114 | /* Package (DP), default to assuming no properties or children */ | |
115 | dp_count = acpigen_write_package(ctx, 0); | |
116 | ||
117 | /* Print base properties */ | |
118 | for (dp = prop; dp; dp = dp->next) { | |
119 | if (dp->type == ACPI_DP_TYPE_CHILD) { | |
120 | child_count++; | |
121 | } else { | |
122 | /* | |
123 | * The UUID and package is only added when | |
124 | * we come across the first property. This | |
125 | * is to avoid creating a zero-length package | |
126 | * in situations where there are only children. | |
127 | */ | |
128 | if (!prop_count) { | |
129 | *dp_count += 2; | |
130 | /* ToUUID (ACPI_DP_UUID) */ | |
131 | ret = acpigen_write_uuid(ctx, ACPI_DP_UUID); | |
132 | if (ret) | |
133 | return log_msg_ret("touuid", ret); | |
134 | /* | |
135 | * Package (PROP), element count determined as | |
136 | * it is populated | |
137 | */ | |
138 | prop_count = acpigen_write_package(ctx, 0); | |
139 | } | |
140 | (*prop_count)++; | |
141 | acpi_dp_write_property(ctx, dp); | |
142 | } | |
143 | } | |
144 | ||
145 | if (prop_count) { | |
146 | /* Package (PROP) length, if a package was written */ | |
147 | acpigen_pop_len(ctx); | |
148 | } | |
149 | ||
150 | if (child_count) { | |
151 | /* Update DP package count to 2 or 4 */ | |
152 | *dp_count += 2; | |
153 | /* ToUUID (ACPI_DP_CHILD_UUID) */ | |
154 | ret = acpigen_write_uuid(ctx, ACPI_DP_CHILD_UUID); | |
155 | if (ret) | |
156 | return log_msg_ret("child uuid", ret); | |
157 | ||
158 | /* Print child pointer properties */ | |
159 | acpigen_write_package(ctx, child_count); | |
160 | ||
161 | for (dp = prop; dp; dp = dp->next) | |
162 | if (dp->type == ACPI_DP_TYPE_CHILD) | |
163 | acpi_dp_write_property(ctx, dp); | |
164 | /* Package (CHILD) length */ | |
165 | acpigen_pop_len(ctx); | |
166 | } | |
167 | ||
168 | /* Package (DP) length */ | |
169 | acpigen_pop_len(ctx); | |
170 | ||
171 | /* Recursively parse children into separate tables */ | |
172 | for (dp = prop; dp; dp = dp->next) { | |
173 | if (dp->type == ACPI_DP_TYPE_CHILD) { | |
174 | ret = acpi_dp_write_internal(ctx, dp->child); | |
175 | if (ret) | |
176 | return log_msg_ret("dp child", ret); | |
177 | } | |
178 | } | |
179 | ||
180 | return 0; | |
181 | } | |
182 | ||
183 | int acpi_dp_write(struct acpi_ctx *ctx, struct acpi_dp *table) | |
184 | { | |
185 | int ret; | |
186 | ||
187 | ret = acpi_dp_write_internal(ctx, table); | |
188 | ||
189 | /* Clean up */ | |
190 | acpi_dp_free(table); | |
191 | ||
192 | if (ret) | |
193 | return log_msg_ret("write", ret); | |
194 | ||
195 | return 0; | |
196 | } | |
197 | ||
198 | static struct acpi_dp *acpi_dp_new(struct acpi_dp *dp, enum acpi_dp_type type, | |
199 | const char *name) | |
200 | { | |
201 | struct acpi_dp *new; | |
202 | ||
203 | new = malloc(sizeof(struct acpi_dp)); | |
204 | if (!new) | |
205 | return NULL; | |
206 | ||
207 | memset(new, '\0', sizeof(*new)); | |
208 | new->type = type; | |
209 | new->name = name; | |
210 | ||
211 | if (dp) { | |
212 | /* Add to end of property list */ | |
213 | while (dp->next) | |
214 | dp = dp->next; | |
215 | dp->next = new; | |
216 | } | |
217 | ||
218 | return new; | |
219 | } | |
220 | ||
221 | struct acpi_dp *acpi_dp_new_table(const char *name) | |
222 | { | |
223 | return acpi_dp_new(NULL, ACPI_DP_TYPE_TABLE, name); | |
224 | } | |
225 | ||
226 | struct acpi_dp *acpi_dp_add_integer(struct acpi_dp *dp, const char *name, | |
227 | u64 value) | |
228 | { | |
229 | struct acpi_dp *new; | |
230 | ||
231 | assert(dp); | |
232 | new = acpi_dp_new(dp, ACPI_DP_TYPE_INTEGER, name); | |
233 | ||
234 | if (new) | |
235 | new->integer = value; | |
236 | ||
237 | return new; | |
238 | } | |
239 | ||
240 | struct acpi_dp *acpi_dp_add_string(struct acpi_dp *dp, const char *name, | |
241 | const char *string) | |
242 | { | |
243 | struct acpi_dp *new; | |
244 | ||
245 | assert(dp); | |
246 | new = acpi_dp_new(dp, ACPI_DP_TYPE_STRING, name); | |
247 | if (new) | |
248 | new->string = string; | |
249 | ||
250 | return new; | |
251 | } | |
252 | ||
253 | struct acpi_dp *acpi_dp_add_reference(struct acpi_dp *dp, const char *name, | |
254 | const char *reference) | |
255 | { | |
256 | struct acpi_dp *new; | |
257 | ||
258 | assert(dp); | |
259 | new = acpi_dp_new(dp, ACPI_DP_TYPE_REFERENCE, name); | |
260 | if (new) | |
261 | new->string = reference; | |
262 | ||
263 | return new; | |
264 | } | |
265 | ||
266 | struct acpi_dp *acpi_dp_add_child(struct acpi_dp *dp, const char *name, | |
267 | struct acpi_dp *child) | |
268 | { | |
269 | struct acpi_dp *new; | |
270 | ||
271 | assert(dp); | |
272 | if (child->type != ACPI_DP_TYPE_TABLE) | |
273 | return NULL; | |
274 | ||
275 | new = acpi_dp_new(dp, ACPI_DP_TYPE_CHILD, name); | |
276 | if (new) { | |
277 | new->child = child; | |
278 | new->string = child->name; | |
279 | } | |
280 | ||
281 | return new; | |
282 | } | |
283 | ||
284 | struct acpi_dp *acpi_dp_add_array(struct acpi_dp *dp, struct acpi_dp *array) | |
285 | { | |
286 | struct acpi_dp *new; | |
287 | ||
288 | assert(dp); | |
289 | assert(array); | |
290 | if (array->type != ACPI_DP_TYPE_TABLE) | |
291 | return NULL; | |
292 | ||
293 | new = acpi_dp_new(dp, ACPI_DP_TYPE_ARRAY, array->name); | |
294 | if (new) | |
295 | new->array = array; | |
296 | ||
297 | return new; | |
298 | } | |
299 | ||
300 | struct acpi_dp *acpi_dp_add_integer_array(struct acpi_dp *dp, const char *name, | |
301 | u64 *array, int len) | |
302 | { | |
303 | struct acpi_dp *dp_array; | |
304 | int i; | |
305 | ||
306 | assert(dp); | |
307 | if (len <= 0) | |
308 | return NULL; | |
309 | ||
310 | dp_array = acpi_dp_new_table(name); | |
311 | if (!dp_array) | |
312 | return NULL; | |
313 | ||
314 | for (i = 0; i < len; i++) | |
315 | if (!acpi_dp_add_integer(dp_array, NULL, array[i])) | |
316 | break; | |
317 | ||
318 | if (!acpi_dp_add_array(dp, dp_array)) | |
319 | return NULL; | |
320 | ||
321 | return dp_array; | |
322 | } | |
23572346 SG |
323 | |
324 | struct acpi_dp *acpi_dp_add_gpio(struct acpi_dp *dp, const char *name, | |
325 | const char *ref, int index, int pin, | |
23dd0ea4 | 326 | enum acpi_gpio_polarity polarity) |
23572346 SG |
327 | { |
328 | struct acpi_dp *gpio; | |
329 | ||
330 | assert(dp); | |
331 | gpio = acpi_dp_new_table(name); | |
332 | if (!gpio) | |
333 | return NULL; | |
334 | ||
335 | if (!acpi_dp_add_reference(gpio, NULL, ref) || | |
336 | !acpi_dp_add_integer(gpio, NULL, index) || | |
337 | !acpi_dp_add_integer(gpio, NULL, pin) || | |
23dd0ea4 | 338 | !acpi_dp_add_integer(gpio, NULL, polarity == ACPI_GPIO_ACTIVE_LOW)) |
23572346 SG |
339 | return NULL; |
340 | ||
341 | if (!acpi_dp_add_array(dp, gpio)) | |
342 | return NULL; | |
343 | ||
344 | return gpio; | |
345 | } | |
06679000 SG |
346 | |
347 | int acpi_dp_ofnode_copy_int(ofnode node, struct acpi_dp *dp, const char *prop) | |
348 | { | |
349 | int ret; | |
350 | u32 val = 0; | |
351 | ||
352 | ret = ofnode_read_u32(node, prop, &val); | |
353 | if (ret) | |
354 | return ret; | |
355 | if (!acpi_dp_add_integer(dp, prop, val)) | |
356 | return log_ret(-ENOMEM); | |
357 | ||
358 | return 0; | |
359 | } | |
360 | ||
361 | int acpi_dp_ofnode_copy_str(ofnode node, struct acpi_dp *dp, const char *prop) | |
362 | { | |
363 | const char *val; | |
364 | ||
365 | val = ofnode_read_string(node, prop); | |
366 | if (!val) | |
367 | return -EINVAL; | |
368 | if (!acpi_dp_add_string(dp, prop, val)) | |
369 | return log_ret(-ENOMEM); | |
370 | ||
371 | return 0; | |
372 | } | |
373 | ||
374 | int acpi_dp_dev_copy_int(const struct udevice *dev, struct acpi_dp *dp, | |
375 | const char *prop) | |
376 | { | |
377 | int ret; | |
378 | u32 val = 0; | |
379 | ||
380 | ret = dev_read_u32(dev, prop, &val); | |
381 | if (ret) | |
382 | return ret; | |
383 | if (!acpi_dp_add_integer(dp, prop, val)) | |
384 | return log_ret(-ENOMEM); | |
385 | ||
386 | return ret; | |
387 | } | |
388 | ||
389 | int acpi_dp_dev_copy_str(const struct udevice *dev, struct acpi_dp *dp, | |
390 | const char *prop) | |
391 | { | |
392 | const char *val; | |
393 | ||
394 | val = dev_read_string(dev, prop); | |
395 | if (!val) | |
396 | return -EINVAL; | |
397 | if (!acpi_dp_add_string(dp, prop, val)) | |
398 | return log_ret(-ENOMEM); | |
399 | ||
400 | return 0; | |
401 | } |