123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353 |
- // SPDX-License-Identifier: GPL-2.0+
- /*
- * Core driver model support for ACPI table generation
- *
- * Copyright 2019 Google LLC
- * Written by Simon Glass <sjg@chromium.org>
- */
- #define LOG_CATEOGRY LOGC_ACPI
- #include <common.h>
- #include <dm.h>
- #include <log.h>
- #include <malloc.h>
- #include <acpi/acpi_device.h>
- #include <dm/acpi.h>
- #include <dm/device-internal.h>
- #include <dm/root.h>
- #define MAX_ACPI_ITEMS 100
- /* Type of table that we collected */
- enum gen_type_t {
- TYPE_NONE,
- TYPE_SSDT,
- TYPE_DSDT,
- };
- /* Type of method to call */
- enum method_t {
- METHOD_WRITE_TABLES,
- METHOD_FILL_SSDT,
- METHOD_INJECT_DSDT,
- METHOD_SETUP_NHLT,
- };
- /* Prototype for all methods */
- typedef int (*acpi_method)(const struct udevice *dev, struct acpi_ctx *ctx);
- /**
- * struct acpi_item - Holds info about ACPI data generated by a driver method
- *
- * @dev: Device that generated this data
- * @type: Table type it refers to
- * @buf: Buffer containing the data
- * @size: Size of the data in bytes
- */
- struct acpi_item {
- struct udevice *dev;
- enum gen_type_t type;
- char *buf;
- int size;
- };
- /* List of ACPI items collected */
- static struct acpi_item acpi_item[MAX_ACPI_ITEMS];
- static int item_count;
- int acpi_copy_name(char *out_name, const char *name)
- {
- strncpy(out_name, name, ACPI_NAME_LEN);
- out_name[ACPI_NAME_LEN] = '\0';
- return 0;
- }
- int acpi_get_name(const struct udevice *dev, char *out_name)
- {
- struct acpi_ops *aops;
- const char *name;
- int ret;
- aops = device_get_acpi_ops(dev);
- if (aops && aops->get_name)
- return aops->get_name(dev, out_name);
- name = dev_read_string(dev, "acpi,name");
- if (name)
- return acpi_copy_name(out_name, name);
- ret = acpi_device_infer_name(dev, out_name);
- if (ret)
- return log_msg_ret("dev", ret);
- return 0;
- }
- int acpi_get_path(const struct udevice *dev, char *out_path, int maxlen)
- {
- const char *path;
- int ret;
- path = dev_read_string(dev, "acpi,path");
- if (path) {
- if (strlen(path) >= maxlen)
- return -ENOSPC;
- strcpy(out_path, path);
- return 0;
- }
- ret = acpi_device_path(dev, out_path, maxlen);
- if (ret)
- return log_msg_ret("dev", ret);
- return 0;
- }
- /**
- * acpi_add_item() - Add a new item to the list of data collected
- *
- * @ctx: ACPI context
- * @dev: Device that generated the data
- * @type: Table type it refers to
- * @start: The start of the data (the end is obtained from ctx->current)
- * @return 0 if OK, -ENOSPC if too many items, -ENOMEM if out of memory
- */
- static int acpi_add_item(struct acpi_ctx *ctx, struct udevice *dev,
- enum gen_type_t type, void *start)
- {
- struct acpi_item *item;
- void *end = ctx->current;
- if (item_count == MAX_ACPI_ITEMS) {
- log_err("Too many items\n");
- return log_msg_ret("mem", -ENOSPC);
- }
- item = &acpi_item[item_count];
- item->dev = dev;
- item->type = type;
- item->size = end - start;
- if (!item->size)
- return 0;
- item->buf = malloc(item->size);
- if (!item->buf)
- return log_msg_ret("mem", -ENOMEM);
- memcpy(item->buf, start, item->size);
- item_count++;
- log_debug("* %s: Added type %d, %p, size %x\n", dev->name, type, start,
- item->size);
- return 0;
- }
- void acpi_dump_items(enum acpi_dump_option option)
- {
- int i;
- for (i = 0; i < item_count; i++) {
- struct acpi_item *item = &acpi_item[i];
- printf("dev '%s', type %d, size %x\n", item->dev->name,
- item->type, item->size);
- if (option == ACPI_DUMP_CONTENTS) {
- print_buffer(0, item->buf, 1, item->size, 0);
- printf("\n");
- }
- }
- }
- static struct acpi_item *find_acpi_item(const char *devname)
- {
- int i;
- for (i = 0; i < item_count; i++) {
- struct acpi_item *item = &acpi_item[i];
- if (!strcmp(devname, item->dev->name))
- return item;
- }
- return NULL;
- }
- /**
- * sort_acpi_item_type - Sort the ACPI items into the desired order
- *
- * This looks up the ordering in the device tree and then adds each item one by
- * one into the supplied buffer
- *
- * @ctx: ACPI context
- * @start: Start position to put the sorted items. The items will follow each
- * other in sorted order
- * @type: Type of items to sort
- * @return 0 if OK, -ve on error
- */
- static int sort_acpi_item_type(struct acpi_ctx *ctx, void *start,
- enum gen_type_t type)
- {
- const u32 *order;
- int size;
- int count;
- void *ptr;
- void *end = ctx->current;
- ptr = start;
- order = ofnode_read_chosen_prop(type == TYPE_DSDT ?
- "u-boot,acpi-dsdt-order" :
- "u-boot,acpi-ssdt-order", &size);
- if (!order) {
- log_debug("Failed to find ordering, leaving as is\n");
- return 0;
- }
- /*
- * This algorithm rewrites the context buffer without changing its
- * length. So there is no need to update ctx-current
- */
- count = size / sizeof(u32);
- while (count--) {
- struct acpi_item *item;
- const char *name;
- ofnode node;
- node = ofnode_get_by_phandle(fdt32_to_cpu(*order++));
- name = ofnode_get_name(node);
- item = find_acpi_item(name);
- if (!item) {
- log_err("Failed to find item '%s'\n", name);
- return log_msg_ret("find", -ENOENT);
- }
- if (item->type == type) {
- log_debug(" - add %s\n", item->dev->name);
- memcpy(ptr, item->buf, item->size);
- ptr += item->size;
- }
- }
- /*
- * If the sort order is missing an item then the output will be too
- * small. Report this error since the item needs to be added to the
- * ordering for the ACPI tables to be complete.
- */
- if (ptr != end) {
- log_warning("*** Missing bytes: ptr=%p, end=%p\n", ptr, end);
- return -ENXIO;
- }
- return 0;
- }
- acpi_method acpi_get_method(struct udevice *dev, enum method_t method)
- {
- struct acpi_ops *aops;
- aops = device_get_acpi_ops(dev);
- if (aops) {
- switch (method) {
- case METHOD_WRITE_TABLES:
- return aops->write_tables;
- case METHOD_FILL_SSDT:
- return aops->fill_ssdt;
- case METHOD_INJECT_DSDT:
- return aops->inject_dsdt;
- case METHOD_SETUP_NHLT:
- return aops->setup_nhlt;
- }
- }
- return NULL;
- }
- int acpi_recurse_method(struct acpi_ctx *ctx, struct udevice *parent,
- enum method_t method, enum gen_type_t type)
- {
- struct udevice *dev;
- acpi_method func;
- int ret;
- func = acpi_get_method(parent, method);
- if (func) {
- void *start = ctx->current;
- log_debug("- method %d, %s %p\n", method, parent->name, func);
- ret = device_of_to_plat(parent);
- if (ret)
- return log_msg_ret("ofdata", ret);
- ret = func(parent, ctx);
- if (ret)
- return log_msg_ret("func", ret);
- /* Add the item to the internal list */
- if (type != TYPE_NONE) {
- ret = acpi_add_item(ctx, parent, type, start);
- if (ret)
- return log_msg_ret("add", ret);
- }
- }
- device_foreach_child(dev, parent) {
- ret = acpi_recurse_method(ctx, dev, method, type);
- if (ret)
- return log_msg_ret("recurse", ret);
- }
- return 0;
- }
- int acpi_fill_ssdt(struct acpi_ctx *ctx)
- {
- void *start = ctx->current;
- int ret;
- log_debug("Writing SSDT tables\n");
- ret = acpi_recurse_method(ctx, dm_root(), METHOD_FILL_SSDT, TYPE_SSDT);
- log_debug("Writing SSDT finished, err=%d\n", ret);
- ret = sort_acpi_item_type(ctx, start, TYPE_SSDT);
- if (ret)
- return log_msg_ret("build", ret);
- return ret;
- }
- int acpi_inject_dsdt(struct acpi_ctx *ctx)
- {
- void *start = ctx->current;
- int ret;
- log_debug("Writing DSDT tables\n");
- ret = acpi_recurse_method(ctx, dm_root(), METHOD_INJECT_DSDT,
- TYPE_DSDT);
- log_debug("Writing DSDT finished, err=%d\n", ret);
- ret = sort_acpi_item_type(ctx, start, TYPE_DSDT);
- if (ret)
- return log_msg_ret("build", ret);
- return ret;
- }
- void acpi_reset_items(void)
- {
- item_count = 0;
- }
- int acpi_write_dev_tables(struct acpi_ctx *ctx)
- {
- int ret;
- log_debug("Writing device tables\n");
- ret = acpi_recurse_method(ctx, dm_root(), METHOD_WRITE_TABLES,
- TYPE_NONE);
- log_debug("Writing finished, err=%d\n", ret);
- return ret;
- }
- int acpi_setup_nhlt(struct acpi_ctx *ctx, struct nhlt *nhlt)
- {
- int ret;
- log_debug("Setup NHLT\n");
- ctx->nhlt = nhlt;
- ret = acpi_recurse_method(ctx, dm_root(), METHOD_SETUP_NHLT, TYPE_NONE);
- log_debug("Setup finished, err=%d\n", ret);
- return ret;
- }
|