// 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 #include #include #include #include #include #include #include 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_gpio_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_GPIO_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; }