uboot/drivers/core/acpi.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Core driver model support for ACPI table generation
   4 *
   5 * Copyright 2019 Google LLC
   6 * Written by Simon Glass <sjg@chromium.org>
   7 */
   8
   9#define LOG_CATEOGRY    LOGC_ACPI
  10
  11#include <common.h>
  12#include <display_options.h>
  13#include <dm.h>
  14#include <log.h>
  15#include <malloc.h>
  16#include <mapmem.h>
  17#include <acpi/acpi_device.h>
  18#include <dm/acpi.h>
  19#include <dm/device-internal.h>
  20#include <dm/root.h>
  21
  22#define MAX_ACPI_ITEMS  100
  23
  24/**
  25 * Type of table that we collected
  26 *
  27 * @TYPE_NONE: Not yet known
  28 * @TYPE_SSDT: Items in the Secondary System Description Table
  29 * @TYPE_DSDT: Items in the Differentiated System Description Table
  30 * @TYPE_OTHER: Other (whole)
  31 */
  32enum gen_type_t {
  33        TYPE_NONE,
  34        TYPE_SSDT,
  35        TYPE_DSDT,
  36        TYPE_OTHER,
  37};
  38
  39const char *gen_type_str[] = {
  40        "-",
  41        "ssdt",
  42        "dsdt",
  43        "other",
  44};
  45
  46/* Type of method to call */
  47enum method_t {
  48        METHOD_WRITE_TABLES,
  49        METHOD_FILL_SSDT,
  50        METHOD_INJECT_DSDT,
  51        METHOD_SETUP_NHLT,
  52};
  53
  54/* Prototype for all methods */
  55typedef int (*acpi_method)(const struct udevice *dev, struct acpi_ctx *ctx);
  56
  57/**
  58 * struct acpi_item - Holds info about ACPI data generated by a driver method
  59 *
  60 * @dev: Device that generated this data
  61 * @type: Table type it refers to
  62 * @writer: Writer that wrote this table
  63 * @base: Pointer to base of table in its original location
  64 * @buf: Buffer allocated to contain the data (NULL if not allocated)
  65 * @size: Size of the data in bytes
  66 */
  67struct acpi_item {
  68        struct udevice *dev;
  69        const struct acpi_writer *writer;
  70        enum gen_type_t type;
  71        const char *base;
  72        char *buf;
  73        int size;
  74};
  75
  76/* List of ACPI items collected */
  77static struct acpi_item acpi_item[MAX_ACPI_ITEMS];
  78static int item_count;
  79
  80int acpi_copy_name(char *out_name, const char *name)
  81{
  82        strncpy(out_name, name, ACPI_NAME_LEN);
  83        out_name[ACPI_NAME_LEN] = '\0';
  84
  85        return 0;
  86}
  87
  88int acpi_get_name(const struct udevice *dev, char *out_name)
  89{
  90        struct acpi_ops *aops;
  91        const char *name;
  92        int ret;
  93
  94        aops = device_get_acpi_ops(dev);
  95        if (aops && aops->get_name)
  96                return aops->get_name(dev, out_name);
  97        name = dev_read_string(dev, "acpi,name");
  98        if (name)
  99                return acpi_copy_name(out_name, name);
 100        ret = acpi_device_infer_name(dev, out_name);
 101        if (ret)
 102                return log_msg_ret("dev", ret);
 103
 104        return 0;
 105}
 106
 107int acpi_get_path(const struct udevice *dev, char *out_path, int maxlen)
 108{
 109        const char *path;
 110        int ret;
 111
 112        path = dev_read_string(dev, "acpi,path");
 113        if (path) {
 114                if (strlen(path) >= maxlen)
 115                        return -ENOSPC;
 116                strcpy(out_path, path);
 117                return 0;
 118        }
 119        ret = acpi_device_path(dev, out_path, maxlen);
 120        if (ret)
 121                return log_msg_ret("dev", ret);
 122
 123        return 0;
 124}
 125
 126/**
 127 * add_item() - Add a new item to the list of data collected
 128 *
 129 * @ctx: ACPI context
 130 * @dev: Device that generated the data, if type != TYPE_OTHER
 131 * @writer: Writer entry that generated the data, if type == TYPE_OTHER
 132 * @type: Table type it refers to
 133 * @start: The start of the data (the end is obtained from ctx->current)
 134 * Return: 0 if OK, -ENOSPC if too many items, -ENOMEM if out of memory
 135 */
 136static int add_item(struct acpi_ctx *ctx, struct udevice *dev,
 137                    const struct acpi_writer *writer, enum gen_type_t type,
 138                    void *start)
 139{
 140        struct acpi_item *item;
 141        void *end = ctx->current;
 142
 143        if (item_count == MAX_ACPI_ITEMS) {
 144                log_err("Too many items\n");
 145                return log_msg_ret("mem", -ENOSPC);
 146        }
 147
 148        item = &acpi_item[item_count];
 149        item->dev = dev;
 150        item->writer = writer;
 151        item->type = type;
 152        item->size = end - start;
 153        item->base = start;
 154        if (!item->size)
 155                return 0;
 156        if (type != TYPE_OTHER) {
 157                item->buf = malloc(item->size);
 158                if (!item->buf)
 159                        return log_msg_ret("mem", -ENOMEM);
 160                memcpy(item->buf, start, item->size);
 161        }
 162        item_count++;
 163        log_debug("* %s: Added type %d, %p, size %x\n",
 164                  dev ? dev->name : "other", type, start, item->size);
 165
 166        return 0;
 167}
 168
 169int acpi_add_other_item(struct acpi_ctx *ctx, const struct acpi_writer *writer,
 170                        void *start)
 171{
 172        return add_item(ctx, NULL, writer, TYPE_OTHER, start);
 173}
 174
 175void acpi_dump_items(enum acpi_dump_option option)
 176{
 177        int i;
 178
 179        printf("Seq  Type       Base   Size  Device/Writer\n");
 180        printf("---  -----  --------   ----  -------------\n");
 181        for (i = 0; i < item_count; i++) {
 182                struct acpi_item *item = &acpi_item[i];
 183
 184                printf("%3x  %-5s  %8lx  %5x  %s\n", i,
 185                       gen_type_str[item->type],
 186                       (ulong)map_to_sysmem(item->base), item->size,
 187                       item->dev ? item->dev->name : item->writer->name);
 188                if (option == ACPI_DUMP_CONTENTS) {
 189                        print_buffer(0, item->buf ? item->buf : item->base, 1,
 190                                     item->size, 0);
 191                        printf("\n");
 192                }
 193        }
 194}
 195
 196static struct acpi_item *find_acpi_item(const char *devname)
 197{
 198        int i;
 199
 200        for (i = 0; i < item_count; i++) {
 201                struct acpi_item *item = &acpi_item[i];
 202
 203                if (item->dev && !strcmp(devname, item->dev->name))
 204                        return item;
 205        }
 206
 207        return NULL;
 208}
 209
 210/**
 211 * sort_acpi_item_type - Sort the ACPI items into the desired order
 212 *
 213 * This looks up the ordering in the device tree and then adds each item one by
 214 * one into the supplied buffer
 215 *
 216 * @ctx: ACPI context
 217 * @start: Start position to put the sorted items. The items will follow each
 218 *      other in sorted order
 219 * @type: Type of items to sort
 220 * Return: 0 if OK, -ve on error
 221 */
 222static int sort_acpi_item_type(struct acpi_ctx *ctx, void *start,
 223                               enum gen_type_t type)
 224{
 225        const u32 *order;
 226        int size;
 227        int count;
 228        void *ptr;
 229        void *end = ctx->current;
 230
 231        ptr = start;
 232        order = ofnode_read_chosen_prop(type == TYPE_DSDT ?
 233                                        "u-boot,acpi-dsdt-order" :
 234                                        "u-boot,acpi-ssdt-order", &size);
 235        if (!order) {
 236                log_debug("Failed to find ordering, leaving as is\n");
 237                return 0;
 238        }
 239
 240        /*
 241         * This algorithm rewrites the context buffer without changing its
 242         * length. So there is no need to update ctx-current
 243         */
 244        count = size / sizeof(u32);
 245        while (count--) {
 246                struct acpi_item *item;
 247                const char *name;
 248                ofnode node;
 249
 250                node = ofnode_get_by_phandle(fdt32_to_cpu(*order++));
 251                name = ofnode_get_name(node);
 252                item = find_acpi_item(name);
 253                if (!item) {
 254                        log_err("Failed to find item '%s'\n", name);
 255                        return log_msg_ret("find", -ENOENT);
 256                }
 257                if (item->type == type) {
 258                        log_debug("   - add %s\n", item->dev->name);
 259                        memcpy(ptr, item->buf, item->size);
 260                        ptr += item->size;
 261                }
 262        }
 263
 264        /*
 265         * If the sort order is missing an item then the output will be too
 266         * small. Report this error since the item needs to be added to the
 267         * ordering for the ACPI tables to be complete.
 268         */
 269        if (ptr != end) {
 270                log_warning("*** Missing bytes: ptr=%p, end=%p\n", ptr, end);
 271                return -ENXIO;
 272        }
 273
 274        return 0;
 275}
 276
 277acpi_method acpi_get_method(struct udevice *dev, enum method_t method)
 278{
 279        struct acpi_ops *aops;
 280
 281        aops = device_get_acpi_ops(dev);
 282        if (aops) {
 283                switch (method) {
 284                case METHOD_WRITE_TABLES:
 285                        return aops->write_tables;
 286                case METHOD_FILL_SSDT:
 287                        return aops->fill_ssdt;
 288                case METHOD_INJECT_DSDT:
 289                        return aops->inject_dsdt;
 290                case METHOD_SETUP_NHLT:
 291                        return aops->setup_nhlt;
 292                }
 293        }
 294
 295        return NULL;
 296}
 297
 298int acpi_recurse_method(struct acpi_ctx *ctx, struct udevice *parent,
 299                        enum method_t method, enum gen_type_t type)
 300{
 301        struct udevice *dev;
 302        acpi_method func;
 303        int ret;
 304
 305        func = acpi_get_method(parent, method);
 306        if (func) {
 307                log_debug("- method %d, %s %p\n", method, parent->name, func);
 308                ret = device_of_to_plat(parent);
 309                if (ret)
 310                        return log_msg_ret("ofdata", ret);
 311                ctx->tab_start = ctx->current;
 312                ret = func(parent, ctx);
 313                if (ret)
 314                        return log_msg_ret("func", ret);
 315
 316                /* Add the item to the internal list */
 317                if (type != TYPE_NONE) {
 318                        ret = add_item(ctx, parent, NULL, type, ctx->tab_start);
 319                        if (ret)
 320                                return log_msg_ret("add", ret);
 321                }
 322        }
 323        device_foreach_child(dev, parent) {
 324                ret = acpi_recurse_method(ctx, dev, method, type);
 325                if (ret)
 326                        return log_msg_ret("recurse", ret);
 327        }
 328
 329        return 0;
 330}
 331
 332int acpi_fill_ssdt(struct acpi_ctx *ctx)
 333{
 334        void *start = ctx->current;
 335        int ret;
 336
 337        log_debug("Writing SSDT tables\n");
 338        ret = acpi_recurse_method(ctx, dm_root(), METHOD_FILL_SSDT, TYPE_SSDT);
 339        log_debug("Writing SSDT finished, err=%d\n", ret);
 340        ret = sort_acpi_item_type(ctx, start, TYPE_SSDT);
 341        if (ret)
 342                return log_msg_ret("build", ret);
 343
 344        return ret;
 345}
 346
 347int acpi_inject_dsdt(struct acpi_ctx *ctx)
 348{
 349        void *start = ctx->current;
 350        int ret;
 351
 352        log_debug("Writing DSDT tables\n");
 353        ret = acpi_recurse_method(ctx, dm_root(), METHOD_INJECT_DSDT,
 354                                  TYPE_DSDT);
 355        log_debug("Writing DSDT finished, err=%d\n", ret);
 356        ret = sort_acpi_item_type(ctx, start, TYPE_DSDT);
 357        if (ret)
 358                return log_msg_ret("build", ret);
 359
 360        return ret;
 361}
 362
 363void acpi_reset_items(void)
 364{
 365        item_count = 0;
 366}
 367
 368int acpi_write_dev_tables(struct acpi_ctx *ctx)
 369{
 370        int ret;
 371
 372        log_debug("Writing device tables\n");
 373        ret = acpi_recurse_method(ctx, dm_root(), METHOD_WRITE_TABLES,
 374                                  TYPE_NONE);
 375        log_debug("Writing finished, err=%d\n", ret);
 376
 377        return ret;
 378}
 379
 380int acpi_setup_nhlt(struct acpi_ctx *ctx, struct nhlt *nhlt)
 381{
 382        int ret;
 383
 384        log_debug("Setup NHLT\n");
 385        ctx->nhlt = nhlt;
 386        ret = acpi_recurse_method(ctx, dm_root(), METHOD_SETUP_NHLT, TYPE_NONE);
 387        log_debug("Setup finished, err=%d\n", ret);
 388
 389        return ret;
 390}
 391