acpi.c 7.7 KB

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