devicetree.c 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. /*
  2. * Copyright (C) 2014, Red Hat Inc, Andrew Jones <drjones@redhat.com>
  3. *
  4. * This work is licensed under the terms of the GNU LGPL, version 2.
  5. */
  6. #include "libcflat.h"
  7. #include "libfdt/libfdt.h"
  8. #include "devicetree.h"
  9. static const void *fdt;
  10. const void *dt_fdt(void)
  11. {
  12. return fdt;
  13. }
  14. bool dt_available(void)
  15. {
  16. return fdt_check_header(fdt) == 0;
  17. }
  18. int dt_get_nr_cells(int fdtnode, u32 *nr_address_cells, u32 *nr_size_cells)
  19. {
  20. const struct fdt_property *prop;
  21. u32 *nr_cells;
  22. int len, nac, nsc;
  23. prop = fdt_get_property(fdt, fdtnode, "#address-cells", &len);
  24. if (prop == NULL)
  25. return len;
  26. nr_cells = (u32 *)prop->data;
  27. nac = fdt32_to_cpu(*nr_cells);
  28. prop = fdt_get_property(fdt, fdtnode, "#size-cells", &len);
  29. if (prop == NULL)
  30. return len;
  31. nr_cells = (u32 *)prop->data;
  32. nsc = fdt32_to_cpu(*nr_cells);
  33. *nr_address_cells = nac;
  34. *nr_size_cells = nsc;
  35. return 0;
  36. }
  37. void dt_reg_init(struct dt_reg *reg, u32 nr_address_cells, u32 nr_size_cells)
  38. {
  39. memset(reg, 0, sizeof(struct dt_reg));
  40. reg->nr_address_cells = nr_address_cells;
  41. reg->nr_size_cells = nr_size_cells;
  42. }
  43. int dt_get_reg(int fdtnode, int regidx, struct dt_reg *reg)
  44. {
  45. const struct fdt_property *prop;
  46. u32 *cells, i;
  47. unsigned nr_tuple_cells;
  48. int len;
  49. prop = fdt_get_property(fdt, fdtnode, "reg", &len);
  50. if (prop == NULL)
  51. return len;
  52. cells = (u32 *)prop->data;
  53. nr_tuple_cells = reg->nr_address_cells + reg->nr_size_cells;
  54. regidx *= nr_tuple_cells;
  55. if (regidx + nr_tuple_cells > len/sizeof(u32))
  56. return -FDT_ERR_NOTFOUND;
  57. for (i = 0; i < reg->nr_address_cells; ++i)
  58. reg->address_cells[i] = fdt32_to_cpu(cells[regidx + i]);
  59. regidx += reg->nr_address_cells;
  60. for (i = 0; i < reg->nr_size_cells; ++i)
  61. reg->size_cells[i] = fdt32_to_cpu(cells[regidx + i]);
  62. return 0;
  63. }
  64. int dt_pbus_translate_node(int fdtnode, int regidx,
  65. struct dt_pbus_reg *pbus_reg)
  66. {
  67. struct dt_reg raw_reg;
  68. u32 nac, nsc;
  69. int parent, ret;
  70. parent = fdt_parent_offset(fdt, fdtnode);
  71. if (parent < 0)
  72. return parent;
  73. ret = dt_get_nr_cells(parent, &nac, &nsc);
  74. if (ret != 0)
  75. return ret;
  76. dt_reg_init(&raw_reg, nac, nsc);
  77. ret = dt_get_reg(fdtnode, regidx, &raw_reg);
  78. if (ret < 0)
  79. return ret;
  80. pbus_reg->addr = dt_pbus_read_cells(raw_reg.nr_address_cells,
  81. raw_reg.address_cells);
  82. pbus_reg->size = dt_pbus_read_cells(raw_reg.nr_size_cells,
  83. raw_reg.size_cells);
  84. return 0;
  85. }
  86. int dt_pbus_translate(const struct dt_device *dev, int regidx,
  87. void *reg)
  88. {
  89. return dt_pbus_translate_node(dev->fdtnode, regidx, reg);
  90. }
  91. int dt_bus_match_any(const struct dt_device *dev __unused, int fdtnode)
  92. {
  93. /* matches any device with a valid node */
  94. return fdtnode < 0 ? fdtnode : 1;
  95. }
  96. static const struct dt_bus dt_default_bus = {
  97. .match = dt_bus_match_any,
  98. .translate = dt_pbus_translate,
  99. };
  100. void dt_bus_init_defaults(struct dt_bus *bus)
  101. {
  102. memcpy(bus, &dt_default_bus, sizeof(struct dt_bus));
  103. }
  104. void dt_device_init(struct dt_device *dev, const struct dt_bus *bus,
  105. void *info)
  106. {
  107. memset(dev, 0, sizeof(struct dt_device));
  108. dev->bus = bus;
  109. dev->info = info;
  110. }
  111. int dt_device_find_compatible(const struct dt_device *dev,
  112. const char *compatible)
  113. {
  114. int node, ret;
  115. node = fdt_node_offset_by_compatible(fdt, -1, compatible);
  116. while (node >= 0) {
  117. ret = dev->bus->match(dev, node);
  118. if (ret < 0)
  119. return ret;
  120. else if (ret)
  121. break;
  122. node = fdt_node_offset_by_compatible(fdt, node, compatible);
  123. }
  124. return node;
  125. }
  126. int dt_pbus_get_base_compatible(const char *compatible,
  127. struct dt_pbus_reg *base)
  128. {
  129. struct dt_device dev;
  130. int node;
  131. dt_device_init(&dev, &dt_default_bus, NULL);
  132. node = dt_device_find_compatible(&dev, compatible);
  133. if (node < 0)
  134. return node;
  135. dt_device_bind_node(&dev, node);
  136. return dt_pbus_get_base(&dev, base);
  137. }
  138. int dt_get_memory_params(struct dt_pbus_reg *regs, int nr_regs)
  139. {
  140. const char *pn = "device_type", *pv = "memory";
  141. int node, ret, reg_idx, pl = strlen(pv) + 1, nr = 0;
  142. struct dt_pbus_reg reg;
  143. node = fdt_node_offset_by_prop_value(fdt, -1, pn, pv, pl);
  144. while (node >= 0) {
  145. reg_idx = 0;
  146. while (nr < nr_regs) {
  147. ret = dt_pbus_translate_node(node, reg_idx, &reg);
  148. if (ret == -FDT_ERR_NOTFOUND)
  149. break;
  150. if (ret < 0)
  151. return ret;
  152. regs[nr].addr = reg.addr;
  153. regs[nr].size = reg.size;
  154. ++nr, ++reg_idx;
  155. }
  156. node = fdt_node_offset_by_prop_value(fdt, node, pn, pv, pl);
  157. }
  158. return node != -FDT_ERR_NOTFOUND ? node : nr;
  159. }
  160. int dt_for_each_cpu_node(void (*func)(int fdtnode, u64 regval, void *info),
  161. void *info)
  162. {
  163. const struct fdt_property *prop;
  164. int cpus, cpu, ret, len;
  165. struct dt_reg raw_reg;
  166. u32 nac, nsc;
  167. u64 regval;
  168. cpus = fdt_path_offset(fdt, "/cpus");
  169. if (cpus < 0)
  170. return cpus;
  171. ret = dt_get_nr_cells(cpus, &nac, &nsc);
  172. if (ret < 0)
  173. return ret;
  174. dt_reg_init(&raw_reg, nac, nsc);
  175. dt_for_each_subnode(cpus, cpu) {
  176. prop = fdt_get_property(fdt, cpu, "device_type", &len);
  177. if (prop == NULL)
  178. return len;
  179. if (len != 4 || strcmp((char *)prop->data, "cpu"))
  180. continue;
  181. ret = dt_get_reg(cpu, 0, &raw_reg);
  182. if (ret < 0)
  183. return ret;
  184. regval = raw_reg.address_cells[0];
  185. if (nac == 2)
  186. regval = (regval << 32) | raw_reg.address_cells[1];
  187. func(cpu, regval, info);
  188. }
  189. return 0;
  190. }
  191. int dt_get_bootargs(const char **bootargs)
  192. {
  193. const struct fdt_property *prop;
  194. int node, len;
  195. *bootargs = NULL;
  196. node = fdt_path_offset(fdt, "/chosen");
  197. if (node < 0)
  198. return node;
  199. prop = fdt_get_property(fdt, node, "bootargs", &len);
  200. if (!prop)
  201. return len;
  202. *bootargs = prop->data;
  203. return 0;
  204. }
  205. int dt_get_default_console_node(void)
  206. {
  207. const struct fdt_property *prop;
  208. int node, len;
  209. node = fdt_path_offset(fdt, "/chosen");
  210. if (node < 0)
  211. return node;
  212. prop = fdt_get_property(fdt, node, "stdout-path", &len);
  213. if (!prop) {
  214. prop = fdt_get_property(fdt, node, "linux,stdout-path", &len);
  215. if (!prop)
  216. return len;
  217. }
  218. return fdt_path_offset(fdt, prop->data);
  219. }
  220. int dt_get_initrd(const char **initrd, u32 *size)
  221. {
  222. const struct fdt_property *prop;
  223. const char *start, *end;
  224. int node, len;
  225. u32 *data;
  226. *initrd = NULL;
  227. *size = 0;
  228. node = fdt_path_offset(fdt, "/chosen");
  229. if (node < 0)
  230. return node;
  231. prop = fdt_get_property(fdt, node, "linux,initrd-start", &len);
  232. if (!prop)
  233. return len;
  234. data = (u32 *)prop->data;
  235. start = (const char *)(unsigned long)fdt32_to_cpu(*data);
  236. prop = fdt_get_property(fdt, node, "linux,initrd-end", &len);
  237. if (!prop) {
  238. assert(len != -FDT_ERR_NOTFOUND);
  239. return len;
  240. }
  241. data = (u32 *)prop->data;
  242. end = (const char *)(unsigned long)fdt32_to_cpu(*data);
  243. *initrd = start;
  244. *size = (unsigned long)end - (unsigned long)start;
  245. return 0;
  246. }
  247. int dt_init(const void *fdt_ptr)
  248. {
  249. int ret;
  250. ret = fdt_check_header(fdt_ptr);
  251. if (ret < 0)
  252. return ret;
  253. /* Sanity check the path. */
  254. ret = fdt_path_offset(fdt_ptr, "/");
  255. if (ret < 0)
  256. return ret;
  257. fdt = fdt_ptr;
  258. return 0;
  259. }