acpi.c 8.8 KB

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