Spaces:
Runtime error
Runtime error
File size: 6,867 Bytes
8df6da4 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 |
/*
* 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;
}
|