aboutsummaryrefslogtreecommitdiff
path: root/lib/acpi
diff options
context:
space:
mode:
authorSimon Glass2020-07-07 13:11:56 -0600
committerBin Meng2020-07-17 14:32:24 +0800
commit0e5a0a00d6e44dc0c7e1466ceb3e452b43ceeb1b (patch)
treedf19c9ee9a3d00dc9acb646541dcd7ad7d250287 /lib/acpi
parent29df845204e6b67583491b3c9883432c3a74d923 (diff)
acpi: Support writing Device Properties objects via _DSD
More complex device properties can be provided to drivers via a device-specific data (_DSD) object. To create this we need to build it up in a separate data structure and then generate the ACPI code, due to its recursive nature. Add an implementation of this. Signed-off-by: Simon Glass <sjg@chromium.org> Reviewed-by: Wolfgang Wallner <wolfgang.wallner@br-automation.com> Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Diffstat (limited to 'lib/acpi')
-rw-r--r--lib/acpi/Makefile1
-rw-r--r--lib/acpi/acpi_dp.c323
2 files changed, 324 insertions, 0 deletions
diff --git a/lib/acpi/Makefile b/lib/acpi/Makefile
index 85a1f774ad4..5c2f793701f 100644
--- a/lib/acpi/Makefile
+++ b/lib/acpi/Makefile
@@ -3,4 +3,5 @@
obj-y += acpigen.o
obj-y += acpi_device.o
+obj-y += acpi_dp.o
obj-y += acpi_table.o
diff --git a/lib/acpi/acpi_dp.c b/lib/acpi/acpi_dp.c
new file mode 100644
index 00000000000..32528e1ec42
--- /dev/null
+++ b/lib/acpi/acpi_dp.c
@@ -0,0 +1,323 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Generation of tables for particular device types
+ *
+ * Copyright 2019 Google LLC
+ * Mostly taken from coreboot file acpi_device.c
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <log.h>
+#include <malloc.h>
+#include <uuid.h>
+#include <acpi/acpigen.h>
+#include <acpi/acpi_dp.h>
+#include <dm/acpi.h>
+
+static void acpi_dp_write_array(struct acpi_ctx *ctx,
+ const struct acpi_dp *array);
+
+static void acpi_dp_write_value(struct acpi_ctx *ctx,
+ const struct acpi_dp *prop)
+{
+ switch (prop->type) {
+ case ACPI_DP_TYPE_INTEGER:
+ acpigen_write_integer(ctx, prop->integer);
+ break;
+ case ACPI_DP_TYPE_STRING:
+ case ACPI_DP_TYPE_CHILD:
+ acpigen_write_string(ctx, prop->string);
+ break;
+ case ACPI_DP_TYPE_REFERENCE:
+ acpigen_emit_namestring(ctx, prop->string);
+ break;
+ case ACPI_DP_TYPE_ARRAY:
+ acpi_dp_write_array(ctx, prop->array);
+ break;
+ default:
+ break;
+ }
+}
+
+/* Package (2) { "prop->name", VALUE } */
+static void acpi_dp_write_property(struct acpi_ctx *ctx,
+ const struct acpi_dp *prop)
+{
+ acpigen_write_package(ctx, 2);
+ acpigen_write_string(ctx, prop->name);
+ acpi_dp_write_value(ctx, prop);
+ acpigen_pop_len(ctx);
+}
+
+/* Write array of Device Properties */
+static void acpi_dp_write_array(struct acpi_ctx *ctx,
+ const struct acpi_dp *array)
+{
+ const struct acpi_dp *dp;
+ char *pkg_count;
+
+ /* Package element count determined as it is populated */
+ pkg_count = acpigen_write_package(ctx, 0);
+
+ /*
+ * Only acpi_dp of type DP_TYPE_TABLE is allowed to be an array.
+ * DP_TYPE_TABLE does not have a value to be written. Thus, start
+ * the loop from next type in the array.
+ */
+ for (dp = array->next; dp; dp = dp->next) {
+ acpi_dp_write_value(ctx, dp);
+ (*pkg_count)++;
+ }
+
+ acpigen_pop_len(ctx);
+}
+
+static void acpi_dp_free(struct acpi_dp *dp)
+{
+ assert(dp);
+ while (dp) {
+ struct acpi_dp *p = dp->next;
+
+ switch (dp->type) {
+ case ACPI_DP_TYPE_CHILD:
+ acpi_dp_free(dp->child);
+ break;
+ case ACPI_DP_TYPE_ARRAY:
+ acpi_dp_free(dp->array);
+ break;
+ default:
+ break;
+ }
+
+ free(dp);
+ dp = p;
+ }
+}
+
+static int acpi_dp_write_internal(struct acpi_ctx *ctx, struct acpi_dp *table)
+{
+ struct acpi_dp *dp, *prop;
+ char *dp_count, *prop_count = NULL;
+ int child_count = 0;
+ int ret;
+
+ assert(table);
+ if (table->type != ACPI_DP_TYPE_TABLE)
+ return 0;
+
+ /* Name (name) */
+ acpigen_write_name(ctx, table->name);
+
+ /* Device Property list starts with the next entry */
+ prop = table->next;
+
+ /* Package (DP), default to assuming no properties or children */
+ dp_count = acpigen_write_package(ctx, 0);
+
+ /* Print base properties */
+ for (dp = prop; dp; dp = dp->next) {
+ if (dp->type == ACPI_DP_TYPE_CHILD) {
+ child_count++;
+ } else {
+ /*
+ * The UUID and package is only added when
+ * we come across the first property. This
+ * is to avoid creating a zero-length package
+ * in situations where there are only children.
+ */
+ if (!prop_count) {
+ *dp_count += 2;
+ /* ToUUID (ACPI_DP_UUID) */
+ ret = acpigen_write_uuid(ctx, ACPI_DP_UUID);
+ if (ret)
+ return log_msg_ret("touuid", ret);
+ /*
+ * Package (PROP), element count determined as
+ * it is populated
+ */
+ prop_count = acpigen_write_package(ctx, 0);
+ }
+ (*prop_count)++;
+ acpi_dp_write_property(ctx, dp);
+ }
+ }
+
+ if (prop_count) {
+ /* Package (PROP) length, if a package was written */
+ acpigen_pop_len(ctx);
+ }
+
+ if (child_count) {
+ /* Update DP package count to 2 or 4 */
+ *dp_count += 2;
+ /* ToUUID (ACPI_DP_CHILD_UUID) */
+ ret = acpigen_write_uuid(ctx, ACPI_DP_CHILD_UUID);
+ if (ret)
+ return log_msg_ret("child uuid", ret);
+
+ /* Print child pointer properties */
+ acpigen_write_package(ctx, child_count);
+
+ for (dp = prop; dp; dp = dp->next)
+ if (dp->type == ACPI_DP_TYPE_CHILD)
+ acpi_dp_write_property(ctx, dp);
+ /* Package (CHILD) length */
+ acpigen_pop_len(ctx);
+ }
+
+ /* Package (DP) length */
+ acpigen_pop_len(ctx);
+
+ /* Recursively parse children into separate tables */
+ for (dp = prop; dp; dp = dp->next) {
+ if (dp->type == ACPI_DP_TYPE_CHILD) {
+ ret = acpi_dp_write_internal(ctx, dp->child);
+ if (ret)
+ return log_msg_ret("dp child", ret);
+ }
+ }
+
+ return 0;
+}
+
+int acpi_dp_write(struct acpi_ctx *ctx, struct acpi_dp *table)
+{
+ int ret;
+
+ ret = acpi_dp_write_internal(ctx, table);
+
+ /* Clean up */
+ acpi_dp_free(table);
+
+ if (ret)
+ return log_msg_ret("write", ret);
+
+ return 0;
+}
+
+static struct acpi_dp *acpi_dp_new(struct acpi_dp *dp, enum acpi_dp_type type,
+ const char *name)
+{
+ struct acpi_dp *new;
+
+ new = malloc(sizeof(struct acpi_dp));
+ if (!new)
+ return NULL;
+
+ memset(new, '\0', sizeof(*new));
+ new->type = type;
+ new->name = name;
+
+ if (dp) {
+ /* Add to end of property list */
+ while (dp->next)
+ dp = dp->next;
+ dp->next = new;
+ }
+
+ return new;
+}
+
+struct acpi_dp *acpi_dp_new_table(const char *name)
+{
+ return acpi_dp_new(NULL, ACPI_DP_TYPE_TABLE, name);
+}
+
+struct acpi_dp *acpi_dp_add_integer(struct acpi_dp *dp, const char *name,
+ u64 value)
+{
+ struct acpi_dp *new;
+
+ assert(dp);
+ new = acpi_dp_new(dp, ACPI_DP_TYPE_INTEGER, name);
+
+ if (new)
+ new->integer = value;
+
+ return new;
+}
+
+struct acpi_dp *acpi_dp_add_string(struct acpi_dp *dp, const char *name,
+ const char *string)
+{
+ struct acpi_dp *new;
+
+ assert(dp);
+ new = acpi_dp_new(dp, ACPI_DP_TYPE_STRING, name);
+ if (new)
+ new->string = string;
+
+ return new;
+}
+
+struct acpi_dp *acpi_dp_add_reference(struct acpi_dp *dp, const char *name,
+ const char *reference)
+{
+ struct acpi_dp *new;
+
+ assert(dp);
+ new = acpi_dp_new(dp, ACPI_DP_TYPE_REFERENCE, name);
+ if (new)
+ new->string = reference;
+
+ return new;
+}
+
+struct acpi_dp *acpi_dp_add_child(struct acpi_dp *dp, const char *name,
+ struct acpi_dp *child)
+{
+ struct acpi_dp *new;
+
+ assert(dp);
+ if (child->type != ACPI_DP_TYPE_TABLE)
+ return NULL;
+
+ new = acpi_dp_new(dp, ACPI_DP_TYPE_CHILD, name);
+ if (new) {
+ new->child = child;
+ new->string = child->name;
+ }
+
+ return new;
+}
+
+struct acpi_dp *acpi_dp_add_array(struct acpi_dp *dp, struct acpi_dp *array)
+{
+ struct acpi_dp *new;
+
+ assert(dp);
+ assert(array);
+ if (array->type != ACPI_DP_TYPE_TABLE)
+ return NULL;
+
+ new = acpi_dp_new(dp, ACPI_DP_TYPE_ARRAY, array->name);
+ if (new)
+ new->array = array;
+
+ return new;
+}
+
+struct acpi_dp *acpi_dp_add_integer_array(struct acpi_dp *dp, const char *name,
+ u64 *array, int len)
+{
+ struct acpi_dp *dp_array;
+ int i;
+
+ assert(dp);
+ if (len <= 0)
+ return NULL;
+
+ dp_array = acpi_dp_new_table(name);
+ if (!dp_array)
+ return NULL;
+
+ for (i = 0; i < len; i++)
+ if (!acpi_dp_add_integer(dp_array, NULL, array[i]))
+ break;
+
+ if (!acpi_dp_add_array(dp, dp_array))
+ return NULL;
+
+ return dp_array;
+}