23dd0ea4c7
This function currently accepts the IRQ-polarity type. Fix it to use the GPIO type instead. Signed-off-by: Simon Glass <sjg@chromium.org>
403 lines
8.1 KiB
C
403 lines
8.1 KiB
C
// 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_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;
|
|
}
|