diff options
author | Simon Glass | 2020-07-07 13:11:56 -0600 |
---|---|---|
committer | Bin Meng | 2020-07-17 14:32:24 +0800 |
commit | 0e5a0a00d6e44dc0c7e1466ceb3e452b43ceeb1b (patch) | |
tree | df19c9ee9a3d00dc9acb646541dcd7ad7d250287 /lib/acpi | |
parent | 29df845204e6b67583491b3c9883432c3a74d923 (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/Makefile | 1 | ||||
-rw-r--r-- | lib/acpi/acpi_dp.c | 323 |
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; +} |