mirror of
				https://xff.cz/git/u-boot/
				synced 2025-10-31 02:15:45 +01:00 
			
		
		
		
	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;
 | |
| }
 |