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