Newer
Older
void (*count_cells)(void *blob, int parentoffset,
int *addrc, int *sizec);
u64 (*map)(fdt32_t *addr, const fdt32_t *range,
int na, int ns, int pna);
int (*translate)(fdt32_t *addr, u64 offset, int na);
};
/* Default translator (generic bus) */
void of_bus_default_count_cells(void *blob, int parentoffset,
int *addrc, int *sizec)
{
if (addrc)
*addrc = fdt_address_cells(blob, parentoffset);
if (sizec) {
prop = fdt_getprop(blob, parentoffset, "#size-cells", NULL);
if (prop)
else
*sizec = 1;
}
}
static u64 of_bus_default_map(fdt32_t *addr, const fdt32_t *range,
int na, int ns, int pna)
{
u64 cp, s, da;
cp = of_read_number(range, na);
s = of_read_number(range + na + pna, ns);
da = of_read_number(addr, na);
debug("OF: default map, cp=%" PRIu64 ", s=%" PRIu64
", da=%" PRIu64 "\n", cp, s, da);
if (da < cp || da >= (cp + s))
return OF_BAD_ADDR;
return da - cp;
}
static int of_bus_default_translate(fdt32_t *addr, u64 offset, int na)
{
u64 a = of_read_number(addr, na);
memset(addr, 0, na * 4);
a += offset;
if (na > 1)
addr[na - 2] = cpu_to_fdt32(a >> 32);
addr[na - 1] = cpu_to_fdt32(a & 0xffffffffu);
return 0;
}
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
#ifdef CONFIG_OF_ISA_BUS
/* ISA bus translator */
static int of_bus_isa_match(void *blob, int parentoffset)
{
const char *name;
name = fdt_get_name(blob, parentoffset, NULL);
if (!name)
return 0;
return !strcmp(name, "isa");
}
static void of_bus_isa_count_cells(void *blob, int parentoffset,
int *addrc, int *sizec)
{
if (addrc)
*addrc = 2;
if (sizec)
*sizec = 1;
}
static u64 of_bus_isa_map(fdt32_t *addr, const fdt32_t *range,
int na, int ns, int pna)
{
u64 cp, s, da;
/* Check address type match */
if ((addr[0] ^ range[0]) & cpu_to_be32(1))
return OF_BAD_ADDR;
cp = of_read_number(range + 1, na - 1);
s = of_read_number(range + na + pna, ns);
da = of_read_number(addr + 1, na - 1);
debug("OF: ISA map, cp=%" PRIu64 ", s=%" PRIu64
", da=%" PRIu64 "\n", cp, s, da);
if (da < cp || da >= (cp + s))
return OF_BAD_ADDR;
return da - cp;
}
static int of_bus_isa_translate(fdt32_t *addr, u64 offset, int na)
{
return of_bus_default_translate(addr + 1, offset, na - 1);
}
#endif /* CONFIG_OF_ISA_BUS */
/* Array of bus specific translators */
static struct of_bus of_busses[] = {
#ifdef CONFIG_OF_ISA_BUS
/* ISA */
{
.name = "isa",
.addresses = "reg",
.match = of_bus_isa_match,
.count_cells = of_bus_isa_count_cells,
.map = of_bus_isa_map,
.translate = of_bus_isa_translate,
},
#endif /* CONFIG_OF_ISA_BUS */
/* Default */
{
.name = "default",
.addresses = "reg",
.count_cells = of_bus_default_count_cells,
.map = of_bus_default_map,
.translate = of_bus_default_translate,
},
};
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
static struct of_bus *of_match_bus(void *blob, int parentoffset)
{
struct of_bus *bus;
if (ARRAY_SIZE(of_busses) == 1)
return of_busses;
for (bus = of_busses; bus; bus++) {
if (!bus->match || bus->match(blob, parentoffset))
return bus;
}
/*
* We should always have matched the default bus at least, since
* it has a NULL match field. If we didn't then it somehow isn't
* in the of_busses array or something equally catastrophic has
* gone wrong.
*/
assert(0);
return NULL;
}
static int of_translate_one(void * blob, int parent, struct of_bus *bus,
int na, int ns, int pna, const char *rprop)
{
int rlen;
int rone;
u64 offset = OF_BAD_ADDR;
/* Normally, an absence of a "ranges" property means we are
* crossing a non-translatable boundary, and thus the addresses
* below the current not cannot be converted to CPU physical ones.
* Unfortunately, while this is very clear in the spec, it's not
* what Apple understood, and they do have things like /uni-n or
* /ht nodes with no "ranges" property and a lot of perfectly
* useable mapped devices below them. Thus we treat the absence of
* "ranges" as equivalent to an empty "ranges" property which means
* a 1:1 translation at that level. It's up to the caller not to try
* to translate addresses that aren't supposed to be translated in
* the first place. --BenH.
*/
ranges = fdt_getprop(blob, parent, rprop, &rlen);
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
if (ranges == NULL || rlen == 0) {
offset = of_read_number(addr, na);
memset(addr, 0, pna * 4);
debug("OF: no ranges, 1:1 translation\n");
goto finish;
}
debug("OF: walking ranges...\n");
/* Now walk through the ranges */
rlen /= 4;
rone = na + pna + ns;
for (; rlen >= rone; rlen -= rone, ranges += rone) {
offset = bus->map(addr, ranges, na, ns, pna);
if (offset != OF_BAD_ADDR)
break;
}
if (offset == OF_BAD_ADDR) {
debug("OF: not found !\n");
return 1;
}
memcpy(addr, ranges + na, 4 * pna);
finish:
of_dump_addr("OF: parent translation for:", addr, pna);
debug("OF: with offset: %" PRIu64 "\n", offset);
/* Translate it into parent bus space */
return pbus->translate(addr, offset, pna);
}
/*
* Translate an address from the device-tree into a CPU physical address,
* this walks up the tree and applies the various bus mappings on the
* way.
*
* Note: We consider that crossing any level with #size-cells == 0 to mean
* that translation is impossible (that is we are not dealing with a value
* that can be mapped to a cpu physical address). This is not really specified
* that way, but this is traditionally the way IBM at least do things
*/
static u64 __of_translate_address(void *blob, int node_offset, const fdt32_t *in_addr,
const char *rprop)
{
int parent;
struct of_bus *bus, *pbus;
int na, ns, pna, pns;
u64 result = OF_BAD_ADDR;
debug("OF: ** translation for device %s **\n",
fdt_get_name(blob, node_offset, NULL));
/* Get parent & match bus type */
parent = fdt_parent_offset(blob, node_offset);
if (parent < 0)
goto bail;
/* Cound address cells & copy address locally */
bus->count_cells(blob, parent, &na, &ns);
Przemyslaw Marczak
committed
if (!OF_CHECK_COUNTS(na, ns)) {
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
printf("%s: Bad cell count for %s\n", __FUNCTION__,
fdt_get_name(blob, node_offset, NULL));
goto bail;
}
memcpy(addr, in_addr, na * 4);
debug("OF: bus is %s (na=%d, ns=%d) on %s\n",
bus->name, na, ns, fdt_get_name(blob, parent, NULL));
of_dump_addr("OF: translating address:", addr, na);
/* Translate */
for (;;) {
/* Switch to parent bus */
node_offset = parent;
parent = fdt_parent_offset(blob, node_offset);
/* If root, we have finished */
if (parent < 0) {
debug("OF: reached root node\n");
result = of_read_number(addr, na);
break;
}
/* Get new parent bus and counts */
pbus->count_cells(blob, parent, &pna, &pns);
Przemyslaw Marczak
committed
if (!OF_CHECK_COUNTS(pna, pns)) {
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
printf("%s: Bad cell count for %s\n", __FUNCTION__,
fdt_get_name(blob, node_offset, NULL));
break;
}
debug("OF: parent bus is %s (na=%d, ns=%d) on %s\n",
pbus->name, pna, pns, fdt_get_name(blob, parent, NULL));
/* Apply bus translation */
if (of_translate_one(blob, node_offset, bus, pbus,
addr, na, ns, pna, rprop))
break;
/* Complete the move up one level */
na = pna;
ns = pns;
bus = pbus;
of_dump_addr("OF: one level translation:", addr, na);
}
bail:
return result;
}
u64 fdt_translate_address(void *blob, int node_offset, const fdt32_t *in_addr)
{
return __of_translate_address(blob, node_offset, in_addr, "ranges");
}
/**
* fdt_node_offset_by_compat_reg: Find a node that matches compatiable and
* who's reg property matches a physical cpu address
*
* @blob: ptr to device tree
* @compat: compatiable string to match
* @compat_off: property name
*
*/
int fdt_node_offset_by_compat_reg(void *blob, const char *compat,
phys_addr_t compat_off)
{
int len, off = fdt_node_offset_by_compatible(blob, -1, compat);
while (off != -FDT_ERR_NOTFOUND) {
const fdt32_t *reg = fdt_getprop(blob, off, "reg", &len);
if (reg) {
if (compat_off == fdt_translate_address(blob, off, reg))
return off;
}
off = fdt_node_offset_by_compatible(blob, off, compat);
}
return -FDT_ERR_NOTFOUND;
}
/**
* fdt_alloc_phandle: Return next free phandle value
*
* @blob: ptr to device tree
*/
int fdt_alloc_phandle(void *blob)
{
int offset;
uint32_t phandle = 0;
for (offset = fdt_next_node(blob, -1, NULL); offset >= 0;
offset = fdt_next_node(blob, offset, NULL)) {
phandle = max(phandle, fdt_get_phandle(blob, offset));
* fdt_set_phandle: Create a phandle property for the given node
*
* @fdt: ptr to device tree
* @nodeoffset: node to update
* @phandle: phandle value to set (must be unique)
*/
int fdt_set_phandle(void *fdt, int nodeoffset, uint32_t phandle)
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
{
int ret;
#ifdef DEBUG
int off = fdt_node_offset_by_phandle(fdt, phandle);
if ((off >= 0) && (off != nodeoffset)) {
char buf[64];
fdt_get_path(fdt, nodeoffset, buf, sizeof(buf));
printf("Trying to update node %s with phandle %u ",
buf, phandle);
fdt_get_path(fdt, off, buf, sizeof(buf));
printf("that already exists in node %s.\n", buf);
return -FDT_ERR_BADPHANDLE;
}
#endif
ret = fdt_setprop_cell(fdt, nodeoffset, "phandle", phandle);
if (ret < 0)
return ret;
/*
* For now, also set the deprecated "linux,phandle" property, so that we
* don't break older kernels.
*/
ret = fdt_setprop_cell(fdt, nodeoffset, "linux,phandle", phandle);
return ret;
}
/*
* fdt_create_phandle: Create a phandle property for the given node
*
* @fdt: ptr to device tree
* @nodeoffset: node to update
*/
unsigned int fdt_create_phandle(void *fdt, int nodeoffset)
{
/* see if there is a phandle already */
int phandle = fdt_get_phandle(fdt, nodeoffset);
/* if we got 0, means no phandle so create one */
if (phandle == 0) {
ret = fdt_set_phandle(fdt, nodeoffset, phandle);
if (ret < 0) {
printf("Can't set phandle %u: %s\n", phandle,
fdt_strerror(ret));
return 0;
}
}
return phandle;
}
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
/*
* fdt_set_node_status: Set status for the given node
*
* @fdt: ptr to device tree
* @nodeoffset: node to update
* @status: FDT_STATUS_OKAY, FDT_STATUS_DISABLED,
* FDT_STATUS_FAIL, FDT_STATUS_FAIL_ERROR_CODE
* @error_code: optional, only used if status is FDT_STATUS_FAIL_ERROR_CODE
*/
int fdt_set_node_status(void *fdt, int nodeoffset,
enum fdt_status status, unsigned int error_code)
{
char buf[16];
int ret = 0;
if (nodeoffset < 0)
return nodeoffset;
switch (status) {
case FDT_STATUS_OKAY:
ret = fdt_setprop_string(fdt, nodeoffset, "status", "okay");
break;
case FDT_STATUS_DISABLED:
ret = fdt_setprop_string(fdt, nodeoffset, "status", "disabled");
break;
case FDT_STATUS_FAIL:
ret = fdt_setprop_string(fdt, nodeoffset, "status", "fail");
break;
case FDT_STATUS_FAIL_ERROR_CODE:
sprintf(buf, "fail-%d", error_code);
ret = fdt_setprop_string(fdt, nodeoffset, "status", buf);
break;
default:
printf("Invalid fdt status: %x\n", status);
ret = -1;
break;
}
return ret;
}
/*
* fdt_set_status_by_alias: Set status for the given node given an alias
*
* @fdt: ptr to device tree
* @alias: alias of node to update
* @status: FDT_STATUS_OKAY, FDT_STATUS_DISABLED,
* FDT_STATUS_FAIL, FDT_STATUS_FAIL_ERROR_CODE
* @error_code: optional, only used if status is FDT_STATUS_FAIL_ERROR_CODE
*/
int fdt_set_status_by_alias(void *fdt, const char* alias,
enum fdt_status status, unsigned int error_code)
{
int offset = fdt_path_offset(fdt, alias);
return fdt_set_node_status(fdt, offset, status, error_code);
}
#if defined(CONFIG_VIDEO) || defined(CONFIG_LCD)
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
int fdt_add_edid(void *blob, const char *compat, unsigned char *edid_buf)
{
int noff;
int ret;
noff = fdt_node_offset_by_compatible(blob, -1, compat);
if (noff != -FDT_ERR_NOTFOUND) {
debug("%s: %s\n", fdt_get_name(blob, noff, 0), compat);
add_edid:
ret = fdt_setprop(blob, noff, "edid", edid_buf, 128);
if (ret == -FDT_ERR_NOSPACE) {
ret = fdt_increase_size(blob, 512);
if (!ret)
goto add_edid;
else
goto err_size;
} else if (ret < 0) {
printf("Can't add property: %s\n", fdt_strerror(ret));
return ret;
}
}
return 0;
err_size:
printf("Can't increase blob size: %s\n", fdt_strerror(ret));
return ret;
}
#endif
/*
* Verify the physical address of device tree node for a given alias
*
* This function locates the device tree node of a given alias, and then
* verifies that the physical address of that device matches the given
* parameter. It displays a message if there is a mismatch.
*
* Returns 1 on success, 0 on failure
*/
int fdt_verify_alias_address(void *fdt, int anode, const char *alias, u64 addr)
{
const char *path;
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
int node, len;
u64 dt_addr;
path = fdt_getprop(fdt, anode, alias, NULL);
if (!path) {
/* If there's no such alias, then it's not a failure */
return 1;
}
node = fdt_path_offset(fdt, path);
if (node < 0) {
printf("Warning: device tree alias '%s' points to invalid "
"node %s.\n", alias, path);
return 0;
}
reg = fdt_getprop(fdt, node, "reg", &len);
if (!reg) {
printf("Warning: device tree node '%s' has no address.\n",
path);
return 0;
}
dt_addr = fdt_translate_address(fdt, node, reg);
if (addr != dt_addr) {
printf("Warning: U-Boot configured device %s at address %"
PRIx64 ",\n but the device tree has it address %"
PRIx64 ".\n", alias, addr, dt_addr);
return 0;
}
return 1;
}
/*
* Returns the base address of an SOC or PCI node
*/
u64 fdt_get_base_address(void *fdt, int node)
{
int size;
u32 naddr;
naddr = fdt_address_cells(fdt, node);
prop = fdt_getprop(fdt, node, "ranges", &size);
return prop ? fdt_translate_address(fdt, node, prop + naddr) : 0;
}
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
/*
* Read a property of size <prop_len>. Currently only supports 1 or 2 cells.
*/
static int fdt_read_prop(const fdt32_t *prop, int prop_len, int cell_off,
uint64_t *val, int cells)
{
const fdt32_t *prop32 = &prop[cell_off];
const fdt64_t *prop64 = (const fdt64_t *)&prop[cell_off];
if ((cell_off + cells) > prop_len)
return -FDT_ERR_NOSPACE;
switch (cells) {
case 1:
*val = fdt32_to_cpu(*prop32);
break;
case 2:
*val = fdt64_to_cpu(*prop64);
break;
default:
return -FDT_ERR_NOSPACE;
}
return 0;
}
/**
* fdt_read_range - Read a node's n'th range property
*
* @fdt: ptr to device tree
* @node: offset of node
* @n: range index
* @child_addr: pointer to storage for the "child address" field
* @addr: pointer to storage for the CPU view translated physical start
* @len: pointer to storage for the range length
*
* Convenience function that reads and interprets a specific range out of
* a number of the "ranges" property array.
*/
int fdt_read_range(void *fdt, int node, int n, uint64_t *child_addr,
uint64_t *addr, uint64_t *len)
{
int pnode = fdt_parent_offset(fdt, node);
const fdt32_t *ranges;
int pacells;
int acells;
int scells;
int ranges_len;
int cell = 0;
int r = 0;
/*
* The "ranges" property is an array of
* { <child address> <parent address> <size in child address space> }
*
* All 3 elements can span a diffent number of cells. Fetch their size.
*/
pacells = fdt_getprop_u32_default_node(fdt, pnode, 0, "#address-cells", 1);
acells = fdt_getprop_u32_default_node(fdt, node, 0, "#address-cells", 1);
scells = fdt_getprop_u32_default_node(fdt, node, 0, "#size-cells", 1);
/* Now try to get the ranges property */
ranges = fdt_getprop(fdt, node, "ranges", &ranges_len);
if (!ranges)
return -FDT_ERR_NOTFOUND;
ranges_len /= sizeof(uint32_t);
/* Jump to the n'th entry */
cell = n * (pacells + acells + scells);
/* Read <child address> */
if (child_addr) {
r = fdt_read_prop(ranges, ranges_len, cell, child_addr,
acells);
if (r)
return r;
}
cell += acells;
/* Read <parent address> */
if (addr)
*addr = fdt_translate_address(fdt, node, ranges + cell);
cell += pacells;
/* Read <size in child address space> */
if (len) {
r = fdt_read_prop(ranges, ranges_len, cell, len, scells);
if (r)
return r;
}
return 0;
}
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
/**
* fdt_setup_simplefb_node - Fill and enable a simplefb node
*
* @fdt: ptr to device tree
* @node: offset of the simplefb node
* @base_address: framebuffer base address
* @width: width in pixels
* @height: height in pixels
* @stride: bytes per line
* @format: pixel format string
*
* Convenience function to fill and enable a simplefb node.
*/
int fdt_setup_simplefb_node(void *fdt, int node, u64 base_address, u32 width,
u32 height, u32 stride, const char *format)
{
char name[32];
fdt32_t cells[4];
int i, addrc, sizec, ret;
of_bus_default_count_cells(fdt, fdt_parent_offset(fdt, node),
&addrc, &sizec);
i = 0;
if (addrc == 2)
cells[i++] = cpu_to_fdt32(base_address >> 32);
cells[i++] = cpu_to_fdt32(base_address);
if (sizec == 2)
cells[i++] = 0;
cells[i++] = cpu_to_fdt32(height * stride);
ret = fdt_setprop(fdt, node, "reg", cells, sizeof(cells[0]) * i);
if (ret < 0)
return ret;
snprintf(name, sizeof(name), "framebuffer@%" PRIx64, base_address);
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
ret = fdt_set_name(fdt, node, name);
if (ret < 0)
return ret;
ret = fdt_setprop_u32(fdt, node, "width", width);
if (ret < 0)
return ret;
ret = fdt_setprop_u32(fdt, node, "height", height);
if (ret < 0)
return ret;
ret = fdt_setprop_u32(fdt, node, "stride", stride);
if (ret < 0)
return ret;
ret = fdt_setprop_string(fdt, node, "format", format);
if (ret < 0)
return ret;
ret = fdt_setprop_string(fdt, node, "status", "okay");
if (ret < 0)
return ret;
return 0;
}
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
/*
* Update native-mode in display-timings from display environment variable.
* The node to update are specified by path.
*/
int fdt_fixup_display(void *blob, const char *path, const char *display)
{
int off, toff;
if (!display || !path)
return -FDT_ERR_NOTFOUND;
toff = fdt_path_offset(blob, path);
if (toff >= 0)
toff = fdt_subnode_offset(blob, toff, "display-timings");
if (toff < 0)
return toff;
for (off = fdt_first_subnode(blob, toff);
off >= 0;
off = fdt_next_subnode(blob, off)) {
uint32_t h = fdt_get_phandle(blob, off);
debug("%s:0x%x\n", fdt_get_name(blob, off, NULL),
fdt32_to_cpu(h));
if (strcasecmp(fdt_get_name(blob, off, NULL), display) == 0)
return fdt_setprop_u32(blob, toff, "native-mode", h);
}
return toff;
}