123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334 |
- /*
- * Copyright (C) 2014, Red Hat Inc, Andrew Jones <drjones@redhat.com>
- *
- * This work is licensed under the terms of the GNU LGPL, version 2.
- */
- #include "libcflat.h"
- #include "libfdt/libfdt.h"
- #include "devicetree.h"
- static const void *fdt;
- const void *dt_fdt(void)
- {
- return fdt;
- }
- bool dt_available(void)
- {
- return fdt_check_header(fdt) == 0;
- }
- int dt_get_nr_cells(int fdtnode, u32 *nr_address_cells, u32 *nr_size_cells)
- {
- const struct fdt_property *prop;
- u32 *nr_cells;
- int len, nac, nsc;
- prop = fdt_get_property(fdt, fdtnode, "#address-cells", &len);
- if (prop == NULL)
- return len;
- nr_cells = (u32 *)prop->data;
- nac = fdt32_to_cpu(*nr_cells);
- prop = fdt_get_property(fdt, fdtnode, "#size-cells", &len);
- if (prop == NULL)
- return len;
- nr_cells = (u32 *)prop->data;
- nsc = fdt32_to_cpu(*nr_cells);
- *nr_address_cells = nac;
- *nr_size_cells = nsc;
- return 0;
- }
- void dt_reg_init(struct dt_reg *reg, u32 nr_address_cells, u32 nr_size_cells)
- {
- memset(reg, 0, sizeof(struct dt_reg));
- reg->nr_address_cells = nr_address_cells;
- reg->nr_size_cells = nr_size_cells;
- }
- int dt_get_reg(int fdtnode, int regidx, struct dt_reg *reg)
- {
- const struct fdt_property *prop;
- u32 *cells, i;
- unsigned nr_tuple_cells;
- int len;
- prop = fdt_get_property(fdt, fdtnode, "reg", &len);
- if (prop == NULL)
- return len;
- cells = (u32 *)prop->data;
- nr_tuple_cells = reg->nr_address_cells + reg->nr_size_cells;
- regidx *= nr_tuple_cells;
- if (regidx + nr_tuple_cells > len/sizeof(u32))
- return -FDT_ERR_NOTFOUND;
- for (i = 0; i < reg->nr_address_cells; ++i)
- reg->address_cells[i] = fdt32_to_cpu(cells[regidx + i]);
- regidx += reg->nr_address_cells;
- for (i = 0; i < reg->nr_size_cells; ++i)
- reg->size_cells[i] = fdt32_to_cpu(cells[regidx + i]);
- return 0;
- }
- int dt_pbus_translate_node(int fdtnode, int regidx,
- struct dt_pbus_reg *pbus_reg)
- {
- struct dt_reg raw_reg;
- u32 nac, nsc;
- int parent, ret;
- parent = fdt_parent_offset(fdt, fdtnode);
- if (parent < 0)
- return parent;
- ret = dt_get_nr_cells(parent, &nac, &nsc);
- if (ret != 0)
- return ret;
- dt_reg_init(&raw_reg, nac, nsc);
- ret = dt_get_reg(fdtnode, regidx, &raw_reg);
- if (ret < 0)
- return ret;
- pbus_reg->addr = dt_pbus_read_cells(raw_reg.nr_address_cells,
- raw_reg.address_cells);
- pbus_reg->size = dt_pbus_read_cells(raw_reg.nr_size_cells,
- raw_reg.size_cells);
- return 0;
- }
- int dt_pbus_translate(const struct dt_device *dev, int regidx,
- void *reg)
- {
- return dt_pbus_translate_node(dev->fdtnode, regidx, reg);
- }
- int dt_bus_match_any(const struct dt_device *dev __unused, int fdtnode)
- {
- /* matches any device with a valid node */
- return fdtnode < 0 ? fdtnode : 1;
- }
- static const struct dt_bus dt_default_bus = {
- .match = dt_bus_match_any,
- .translate = dt_pbus_translate,
- };
- void dt_bus_init_defaults(struct dt_bus *bus)
- {
- memcpy(bus, &dt_default_bus, sizeof(struct dt_bus));
- }
- void dt_device_init(struct dt_device *dev, const struct dt_bus *bus,
- void *info)
- {
- memset(dev, 0, sizeof(struct dt_device));
- dev->bus = bus;
- dev->info = info;
- }
- int dt_device_find_compatible(const struct dt_device *dev,
- const char *compatible)
- {
- int node, ret;
- node = fdt_node_offset_by_compatible(fdt, -1, compatible);
- while (node >= 0) {
- ret = dev->bus->match(dev, node);
- if (ret < 0)
- return ret;
- else if (ret)
- break;
- node = fdt_node_offset_by_compatible(fdt, node, compatible);
- }
- return node;
- }
- int dt_pbus_get_base_compatible(const char *compatible,
- struct dt_pbus_reg *base)
- {
- struct dt_device dev;
- int node;
- dt_device_init(&dev, &dt_default_bus, NULL);
- node = dt_device_find_compatible(&dev, compatible);
- if (node < 0)
- return node;
- dt_device_bind_node(&dev, node);
- return dt_pbus_get_base(&dev, base);
- }
- int dt_get_memory_params(struct dt_pbus_reg *regs, int nr_regs)
- {
- const char *pn = "device_type", *pv = "memory";
- int node, ret, reg_idx, pl = strlen(pv) + 1, nr = 0;
- struct dt_pbus_reg reg;
- node = fdt_node_offset_by_prop_value(fdt, -1, pn, pv, pl);
- while (node >= 0) {
- reg_idx = 0;
- while (nr < nr_regs) {
- ret = dt_pbus_translate_node(node, reg_idx, ®);
- if (ret == -FDT_ERR_NOTFOUND)
- break;
- if (ret < 0)
- return ret;
- regs[nr].addr = reg.addr;
- regs[nr].size = reg.size;
- ++nr, ++reg_idx;
- }
- node = fdt_node_offset_by_prop_value(fdt, node, pn, pv, pl);
- }
- return node != -FDT_ERR_NOTFOUND ? node : nr;
- }
- int dt_for_each_cpu_node(void (*func)(int fdtnode, u64 regval, void *info),
- void *info)
- {
- const struct fdt_property *prop;
- int cpus, cpu, ret, len;
- struct dt_reg raw_reg;
- u32 nac, nsc;
- u64 regval;
- cpus = fdt_path_offset(fdt, "/cpus");
- if (cpus < 0)
- return cpus;
- ret = dt_get_nr_cells(cpus, &nac, &nsc);
- if (ret < 0)
- return ret;
- dt_reg_init(&raw_reg, nac, nsc);
- dt_for_each_subnode(cpus, cpu) {
- prop = fdt_get_property(fdt, cpu, "device_type", &len);
- if (prop == NULL)
- return len;
- if (len != 4 || strcmp((char *)prop->data, "cpu"))
- continue;
- ret = dt_get_reg(cpu, 0, &raw_reg);
- if (ret < 0)
- return ret;
- regval = raw_reg.address_cells[0];
- if (nac == 2)
- regval = (regval << 32) | raw_reg.address_cells[1];
- func(cpu, regval, info);
- }
- return 0;
- }
- int dt_get_bootargs(const char **bootargs)
- {
- const struct fdt_property *prop;
- int node, len;
- *bootargs = NULL;
- node = fdt_path_offset(fdt, "/chosen");
- if (node < 0)
- return node;
- prop = fdt_get_property(fdt, node, "bootargs", &len);
- if (!prop)
- return len;
- *bootargs = prop->data;
- return 0;
- }
- int dt_get_default_console_node(void)
- {
- const struct fdt_property *prop;
- int node, len;
- node = fdt_path_offset(fdt, "/chosen");
- if (node < 0)
- return node;
- prop = fdt_get_property(fdt, node, "stdout-path", &len);
- if (!prop) {
- prop = fdt_get_property(fdt, node, "linux,stdout-path", &len);
- if (!prop)
- return len;
- }
- return fdt_path_offset(fdt, prop->data);
- }
- int dt_get_initrd(const char **initrd, u32 *size)
- {
- const struct fdt_property *prop;
- const char *start, *end;
- int node, len;
- u32 *data;
- *initrd = NULL;
- *size = 0;
- node = fdt_path_offset(fdt, "/chosen");
- if (node < 0)
- return node;
- prop = fdt_get_property(fdt, node, "linux,initrd-start", &len);
- if (!prop)
- return len;
- data = (u32 *)prop->data;
- start = (const char *)(unsigned long)fdt32_to_cpu(*data);
- prop = fdt_get_property(fdt, node, "linux,initrd-end", &len);
- if (!prop) {
- assert(len != -FDT_ERR_NOTFOUND);
- return len;
- }
- data = (u32 *)prop->data;
- end = (const char *)(unsigned long)fdt32_to_cpu(*data);
- *initrd = start;
- *size = (unsigned long)end - (unsigned long)start;
- return 0;
- }
- int dt_init(const void *fdt_ptr)
- {
- int ret;
- ret = fdt_check_header(fdt_ptr);
- if (ret < 0)
- return ret;
- /* Sanity check the path. */
- ret = fdt_path_offset(fdt_ptr, "/");
- if (ret < 0)
- return ret;
- fdt = fdt_ptr;
- return 0;
- }
|