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