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