mirror of
https://xff.cz/git/u-boot/
synced 2025-09-01 08:42:12 +02:00
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>
This commit is contained in:
@@ -3,4 +3,5 @@
|
||||
|
||||
obj-y += acpigen.o
|
||||
obj-y += acpi_device.o
|
||||
obj-y += acpi_dp.o
|
||||
obj-y += acpi_table.o
|
||||
|
323
lib/acpi/acpi_dp.c
Normal file
323
lib/acpi/acpi_dp.c
Normal file
@@ -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;
|
||||
}
|
Reference in New Issue
Block a user