blob: 5714acce08821a856ee6cb1bfb1c1bb831bdbea1 [file] [log] [blame]
Simon Glassc993ff72020-07-07 13:11:56 -06001// 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
Simon Glassc993ff72020-07-07 13:11:56 -06009#include <dm.h>
10#include <log.h>
11#include <malloc.h>
Caleb Connolly29cab7c2024-08-30 13:34:37 +010012#include <u-boot/uuid.h>
Simon Glassc993ff72020-07-07 13:11:56 -060013#include <acpi/acpigen.h>
14#include <acpi/acpi_dp.h>
15#include <dm/acpi.h>
16
17static void acpi_dp_write_array(struct acpi_ctx *ctx,
18 const struct acpi_dp *array);
19
20static 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 } */
43static 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 */
53static 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
75static 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
97static 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
183int 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
198static 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
221struct acpi_dp *acpi_dp_new_table(const char *name)
222{
223 return acpi_dp_new(NULL, ACPI_DP_TYPE_TABLE, name);
224}
225
226struct 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
240struct 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
253struct 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
266struct 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
284struct 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
300struct 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}
Simon Glass59534492020-07-07 13:11:57 -0600323
324struct acpi_dp *acpi_dp_add_gpio(struct acpi_dp *dp, const char *name,
325 const char *ref, int index, int pin,
Simon Glass2f3c6ba2020-09-22 12:44:59 -0600326 enum acpi_gpio_polarity polarity)
Simon Glass59534492020-07-07 13:11:57 -0600327{
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) ||
Simon Glass2f3c6ba2020-09-22 12:44:59 -0600338 !acpi_dp_add_integer(gpio, NULL, polarity == ACPI_GPIO_ACTIVE_LOW))
Simon Glass59534492020-07-07 13:11:57 -0600339 return NULL;
340
341 if (!acpi_dp_add_array(dp, gpio))
342 return NULL;
343
344 return gpio;
345}
Simon Glassdd0ed902020-07-07 13:11:58 -0600346
347int 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
361int 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
374int 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
389int 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}