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 10 static const void *fdt; 11 12 const void *dt_fdt(void) 13 { 14 return fdt; 15 } 16 17 bool dt_available(void) 18 { 19 return fdt_check_header(fdt) == 0; 20 } 21 22 int dt_get_nr_cells(int fdtnode, u32 *nr_address_cells, u32 *nr_size_cells) 23 { 24 const struct fdt_property *prop; 25 u32 *nr_cells; 26 int len, nac, nsc; 27 28 prop = fdt_get_property(fdt, fdtnode, "#address-cells", &len); 29 if (prop == NULL) 30 return len; 31 32 nr_cells = (u32 *)prop->data; 33 nac = fdt32_to_cpu(*nr_cells); 34 35 prop = fdt_get_property(fdt, fdtnode, "#size-cells", &len); 36 if (prop == NULL) 37 return len; 38 39 nr_cells = (u32 *)prop->data; 40 nsc = fdt32_to_cpu(*nr_cells); 41 42 *nr_address_cells = nac; 43 *nr_size_cells = nsc; 44 45 return 0; 46 } 47 48 void dt_reg_init(struct dt_reg *reg, u32 nr_address_cells, u32 nr_size_cells) 49 { 50 memset(reg, 0, sizeof(struct dt_reg)); 51 reg->nr_address_cells = nr_address_cells; 52 reg->nr_size_cells = nr_size_cells; 53 } 54 55 int dt_get_reg(int fdtnode, int regidx, struct dt_reg *reg) 56 { 57 const struct fdt_property *prop; 58 u32 *cells, i; 59 unsigned nr_tuple_cells; 60 int len; 61 62 prop = fdt_get_property(fdt, fdtnode, "reg", &len); 63 if (prop == NULL) 64 return len; 65 66 cells = (u32 *)prop->data; 67 nr_tuple_cells = reg->nr_address_cells + reg->nr_size_cells; 68 regidx *= nr_tuple_cells; 69 70 if (regidx + nr_tuple_cells > len/sizeof(u32)) 71 return -FDT_ERR_NOTFOUND; 72 73 for (i = 0; i < reg->nr_address_cells; ++i) 74 reg->address_cells[i] = fdt32_to_cpu(cells[regidx + i]); 75 76 regidx += reg->nr_address_cells; 77 for (i = 0; i < reg->nr_size_cells; ++i) 78 reg->size_cells[i] = fdt32_to_cpu(cells[regidx + i]); 79 80 return 0; 81 } 82 83 int dt_pbus_translate_node(int fdtnode, int regidx, 84 struct dt_pbus_reg *pbus_reg) 85 { 86 struct dt_reg raw_reg; 87 u32 nac, nsc; 88 int parent, ret; 89 90 parent = fdt_parent_offset(fdt, fdtnode); 91 if (parent < 0) 92 return parent; 93 94 ret = dt_get_nr_cells(parent, &nac, &nsc); 95 if (ret != 0) 96 return ret; 97 98 dt_reg_init(&raw_reg, nac, nsc); 99 100 ret = dt_get_reg(fdtnode, regidx, &raw_reg); 101 if (ret < 0) 102 return ret; 103 104 pbus_reg->addr = dt_pbus_read_cells(raw_reg.nr_address_cells, 105 raw_reg.address_cells); 106 pbus_reg->size = dt_pbus_read_cells(raw_reg.nr_size_cells, 107 raw_reg.size_cells); 108 109 return 0; 110 } 111 112 int dt_pbus_translate(const struct dt_device *dev, int regidx, 113 void *reg) 114 { 115 return dt_pbus_translate_node(dev->fdtnode, regidx, reg); 116 } 117 118 int dt_bus_match_any(const struct dt_device *dev __unused, int fdtnode) 119 { 120 /* matches any device with a valid node */ 121 return fdtnode < 0 ? fdtnode : 1; 122 } 123 124 static const struct dt_bus dt_default_bus = { 125 .match = dt_bus_match_any, 126 .translate = dt_pbus_translate, 127 }; 128 129 void dt_bus_init_defaults(struct dt_bus *bus) 130 { 131 memcpy(bus, &dt_default_bus, sizeof(struct dt_bus)); 132 } 133 134 void dt_device_init(struct dt_device *dev, const struct dt_bus *bus, 135 void *info) 136 { 137 memset(dev, 0, sizeof(struct dt_device)); 138 dev->bus = bus; 139 dev->info = info; 140 } 141 142 int dt_device_find_compatible(const struct dt_device *dev, 143 const char *compatible) 144 { 145 int node, ret; 146 147 node = fdt_node_offset_by_compatible(fdt, -1, compatible); 148 while (node >= 0) { 149 ret = dev->bus->match(dev, node); 150 if (ret < 0) 151 return ret; 152 else if (ret) 153 break; 154 node = fdt_node_offset_by_compatible(fdt, node, compatible); 155 } 156 return node; 157 } 158 159 int dt_pbus_get_base_compatible(const char *compatible, 160 struct dt_pbus_reg *base) 161 { 162 struct dt_device dev; 163 int node; 164 165 dt_device_init(&dev, &dt_default_bus, NULL); 166 167 node = dt_device_find_compatible(&dev, compatible); 168 if (node < 0) 169 return node; 170 171 dt_device_bind_node(&dev, node); 172 173 return dt_pbus_get_base(&dev, base); 174 } 175 176 int dt_get_memory_params(struct dt_pbus_reg *regs, int nr_regs) 177 { 178 const char *pn = "device_type", *pv = "memory"; 179 int node, ret, reg_idx, pl = strlen(pv) + 1, nr = 0; 180 struct dt_pbus_reg reg; 181 182 node = fdt_node_offset_by_prop_value(fdt, -1, pn, pv, pl); 183 184 while (node >= 0) { 185 186 reg_idx = 0; 187 188 while (nr < nr_regs) { 189 ret = dt_pbus_translate_node(node, reg_idx, ®); 190 if (ret == -FDT_ERR_NOTFOUND) 191 break; 192 if (ret < 0) 193 return ret; 194 regs[nr].addr = reg.addr; 195 regs[nr].size = reg.size; 196 ++nr, ++reg_idx; 197 } 198 199 node = fdt_node_offset_by_prop_value(fdt, node, pn, pv, pl); 200 } 201 202 return node != -FDT_ERR_NOTFOUND ? node : nr; 203 } 204 205 int dt_for_each_cpu_node(void (*func)(int fdtnode, u64 regval, void *info), 206 void *info) 207 { 208 const struct fdt_property *prop; 209 int cpus, cpu, ret, len; 210 struct dt_reg raw_reg; 211 u32 nac, nsc; 212 u64 regval; 213 214 cpus = fdt_path_offset(fdt, "/cpus"); 215 if (cpus < 0) 216 return cpus; 217 218 ret = dt_get_nr_cells(cpus, &nac, &nsc); 219 if (ret < 0) 220 return ret; 221 222 dt_reg_init(&raw_reg, nac, nsc); 223 224 dt_for_each_subnode(cpus, cpu) { 225 226 prop = fdt_get_property(fdt, cpu, "device_type", &len); 227 if (prop == NULL) 228 continue; 229 230 if (len != 4 || strcmp((char *)prop->data, "cpu")) 231 continue; 232 233 ret = dt_get_reg(cpu, 0, &raw_reg); 234 if (ret < 0) 235 return ret; 236 237 regval = raw_reg.address_cells[0]; 238 if (nac == 2) 239 regval = (regval << 32) | raw_reg.address_cells[1]; 240 241 func(cpu, regval, info); 242 } 243 244 return 0; 245 } 246 247 int dt_get_bootargs(const char **bootargs) 248 { 249 const struct fdt_property *prop; 250 int node, len; 251 252 *bootargs = NULL; 253 254 node = fdt_path_offset(fdt, "/chosen"); 255 if (node < 0) 256 return node; 257 258 prop = fdt_get_property(fdt, node, "bootargs", &len); 259 if (!prop) 260 return len; 261 262 *bootargs = prop->data; 263 return 0; 264 } 265 266 int dt_get_default_console_node(void) 267 { 268 const char *p, *q; 269 int node, len; 270 271 node = fdt_path_offset(fdt, "/chosen"); 272 if (node < 0) 273 return node; 274 275 p = fdt_getprop(fdt, node, "stdout-path", &len); 276 if (!p) { 277 p = fdt_getprop(fdt, node, "linux,stdout-path", &len); 278 if (!p) 279 return len; 280 } 281 282 q = strchrnul(p, ':'); 283 len = q - p; 284 285 return fdt_path_offset_namelen(fdt, p, len); 286 } 287 288 int dt_get_initrd(const char **initrd, u32 *size) 289 { 290 const struct fdt_property *prop; 291 const char *start, *end; 292 int node, len; 293 u32 *data; 294 295 *initrd = NULL; 296 *size = 0; 297 298 node = fdt_path_offset(fdt, "/chosen"); 299 if (node < 0) 300 return node; 301 302 prop = fdt_get_property(fdt, node, "linux,initrd-start", &len); 303 if (!prop) 304 return len; 305 data = (u32 *)prop->data; 306 start = (const char *)(unsigned long)fdt32_to_cpu(*data); 307 308 prop = fdt_get_property(fdt, node, "linux,initrd-end", &len); 309 if (!prop) { 310 assert(len != -FDT_ERR_NOTFOUND); 311 return len; 312 } 313 data = (u32 *)prop->data; 314 end = (const char *)(unsigned long)fdt32_to_cpu(*data); 315 316 *initrd = start; 317 *size = (unsigned long)end - (unsigned long)start; 318 319 return 0; 320 } 321 322 int dt_init(const void *fdt_ptr) 323 { 324 int ret; 325 326 ret = fdt_check_header(fdt_ptr); 327 if (ret < 0) 328 return ret; 329 330 /* Sanity check the path. */ 331 ret = fdt_path_offset(fdt_ptr, "/"); 332 if (ret < 0) 333 return ret; 334 335 fdt = fdt_ptr; 336 return 0; 337 } 338