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