summaryrefslogtreecommitdiff
path: root/lib/acpi/acpi_dp.c
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2020-07-17 08:04:48 -0400
committerTom Rini <trini@konsulko.com>2020-07-17 08:04:48 -0400
commit7c3cc6f106ed1ca13b0ff6eea9f8e1473240aef3 (patch)
tree8c67a8ed3ab24b1421161960103d8614cbde659a /lib/acpi/acpi_dp.c
parent42e7659db0ac7089d3a2f80ee1c3b8eb64d84706 (diff)
parentd40d2c570600396b54dece16429727ef50cfeef0 (diff)
Merge https://gitlab.denx.de/u-boot/custodians/u-boot-x86
- New timer API to allow delays with a 32-bit microsecond timer - Add dynamic ACPI structs (DSDT/SSDT) generations to the DM core - x86: Enable ACPI table generation by default - x86: Enable the copy framebuffer on Coral - x86: A few fixes to FSP2 with ApolloLake - x86: Drop setup_pcat_compatibility() - x86: Primary-to-Sideband Bus minor fixes
Diffstat (limited to 'lib/acpi/acpi_dp.c')
-rw-r--r--lib/acpi/acpi_dp.c402
1 files changed, 402 insertions, 0 deletions
diff --git a/lib/acpi/acpi_dp.c b/lib/acpi/acpi_dp.c
new file mode 100644
index 0000000000..579cab4771
--- /dev/null
+++ b/lib/acpi/acpi_dp.c
@@ -0,0 +1,402 @@
+// 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;
+}
+
+struct acpi_dp *acpi_dp_add_gpio(struct acpi_dp *dp, const char *name,
+ const char *ref, int index, int pin,
+ enum acpi_irq_polarity polarity)
+{
+ struct acpi_dp *gpio;
+
+ assert(dp);
+ gpio = acpi_dp_new_table(name);
+ if (!gpio)
+ return NULL;
+
+ if (!acpi_dp_add_reference(gpio, NULL, ref) ||
+ !acpi_dp_add_integer(gpio, NULL, index) ||
+ !acpi_dp_add_integer(gpio, NULL, pin) ||
+ !acpi_dp_add_integer(gpio, NULL, polarity == ACPI_IRQ_ACTIVE_LOW))
+ return NULL;
+
+ if (!acpi_dp_add_array(dp, gpio))
+ return NULL;
+
+ return gpio;
+}
+
+int acpi_dp_ofnode_copy_int(ofnode node, struct acpi_dp *dp, const char *prop)
+{
+ int ret;
+ u32 val = 0;
+
+ ret = ofnode_read_u32(node, prop, &val);
+ if (ret)
+ return ret;
+ if (!acpi_dp_add_integer(dp, prop, val))
+ return log_ret(-ENOMEM);
+
+ return 0;
+}
+
+int acpi_dp_ofnode_copy_str(ofnode node, struct acpi_dp *dp, const char *prop)
+{
+ const char *val;
+
+ val = ofnode_read_string(node, prop);
+ if (!val)
+ return -EINVAL;
+ if (!acpi_dp_add_string(dp, prop, val))
+ return log_ret(-ENOMEM);
+
+ return 0;
+}
+
+int acpi_dp_dev_copy_int(const struct udevice *dev, struct acpi_dp *dp,
+ const char *prop)
+{
+ int ret;
+ u32 val = 0;
+
+ ret = dev_read_u32(dev, prop, &val);
+ if (ret)
+ return ret;
+ if (!acpi_dp_add_integer(dp, prop, val))
+ return log_ret(-ENOMEM);
+
+ return ret;
+}
+
+int acpi_dp_dev_copy_str(const struct udevice *dev, struct acpi_dp *dp,
+ const char *prop)
+{
+ const char *val;
+
+ val = dev_read_string(dev, prop);
+ if (!val)
+ return -EINVAL;
+ if (!acpi_dp_add_string(dp, prop, val))
+ return log_ret(-ENOMEM);
+
+ return 0;
+}