mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-18 10:48:51 +00:00
d64b9cdcd4
The __of_translate_address routine translates an address from the device tree into a CPU physical address. A note in the description of the routine explains that the crossing of any level with since inherited from IBM. This does not happen for Texas Instruments, or at least for the beaglebone device tree. Without this patch, in fact, the translation into physical addresses of the registers contained in the am33xx-clocks.dtsi nodes would not be possible. They all have a parent with #size-cells = <0>. The CONFIG_OF_TRANSLATE_ZERO_SIZE_CELLS symbol makes translation possible even in the case of crossing levels with #size-cells = <0>. The patch acts conservatively on address translation, except for removing a check within the of_translate_one function in the drivers/core/of_addr.c file: + ranges = of_get_property(parent, rprop, &rlen); - if (ranges == NULL && !of_empty_ranges_quirk(parent)) { - debug("no ranges; cannot translate\n"); - return 1; - } if (ranges == NULL || rlen == 0) { offset = of_read_number(addr, na); memset(addr, 0, pna * 4); debug("empty ranges; 1:1 translation\n"); There are two reasons: 1 The function of_empty_ranges_quirk always returns false, invalidating the following if statement in case of null ranges. Therefore one of the two checks is useless. 2 The implementation of the of_translate_one function found in the common/fdt_support.c file has removed this check while keeping the one about the 1:1 translation. The patch adds a test and modifies a check for the correctness of an address in the case of enabling translation also for zero size cells. The added test checks translations of addresses generated by nodes of a device tree similar to those you can find in the files am33xx.dtsi and am33xx-clocks.dtsi for which the patch was created. The patch was also tested on a beaglebone black board. The addresses generated for the registers of the loaded drivers are those specified by the AM335x reference manual. Signed-off-by: Dario Binacchi <dariobin@libero.it> Tested-by: Dario Binacchi <dariobin@libero.it> Reviewed-by: Simon Glass <sjg@chromium.org>
1036 lines
23 KiB
C
1036 lines
23 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* Copyright (c) 2017 Google, Inc
|
|
* Written by Simon Glass <sjg@chromium.org>
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <dm.h>
|
|
#include <fdtdec.h>
|
|
#include <fdt_support.h>
|
|
#include <log.h>
|
|
#include <malloc.h>
|
|
#include <linux/libfdt.h>
|
|
#include <dm/of_access.h>
|
|
#include <dm/of_addr.h>
|
|
#include <dm/ofnode.h>
|
|
#include <linux/err.h>
|
|
#include <linux/ioport.h>
|
|
|
|
int ofnode_read_u32(ofnode node, const char *propname, u32 *outp)
|
|
{
|
|
return ofnode_read_u32_index(node, propname, 0, outp);
|
|
}
|
|
|
|
u32 ofnode_read_u32_default(ofnode node, const char *propname, u32 def)
|
|
{
|
|
assert(ofnode_valid(node));
|
|
ofnode_read_u32_index(node, propname, 0, &def);
|
|
|
|
return def;
|
|
}
|
|
|
|
int ofnode_read_u32_index(ofnode node, const char *propname, int index,
|
|
u32 *outp)
|
|
{
|
|
const fdt32_t *cell;
|
|
int len;
|
|
|
|
assert(ofnode_valid(node));
|
|
debug("%s: %s: ", __func__, propname);
|
|
|
|
if (ofnode_is_np(node))
|
|
return of_read_u32_index(ofnode_to_np(node), propname, index,
|
|
outp);
|
|
|
|
cell = fdt_getprop(gd->fdt_blob, ofnode_to_offset(node), propname,
|
|
&len);
|
|
if (!cell) {
|
|
debug("(not found)\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (len < (sizeof(int) * (index + 1))) {
|
|
debug("(not large enough)\n");
|
|
return -EOVERFLOW;
|
|
}
|
|
|
|
*outp = fdt32_to_cpu(cell[index]);
|
|
debug("%#x (%d)\n", *outp, *outp);
|
|
|
|
return 0;
|
|
}
|
|
|
|
u32 ofnode_read_u32_index_default(ofnode node, const char *propname, int index,
|
|
u32 def)
|
|
{
|
|
assert(ofnode_valid(node));
|
|
ofnode_read_u32_index(node, propname, index, &def);
|
|
|
|
return def;
|
|
}
|
|
|
|
int ofnode_read_s32_default(ofnode node, const char *propname, s32 def)
|
|
{
|
|
assert(ofnode_valid(node));
|
|
ofnode_read_u32(node, propname, (u32 *)&def);
|
|
|
|
return def;
|
|
}
|
|
|
|
int ofnode_read_u64(ofnode node, const char *propname, u64 *outp)
|
|
{
|
|
const unaligned_fdt64_t *cell;
|
|
int len;
|
|
|
|
assert(ofnode_valid(node));
|
|
debug("%s: %s: ", __func__, propname);
|
|
|
|
if (ofnode_is_np(node))
|
|
return of_read_u64(ofnode_to_np(node), propname, outp);
|
|
|
|
cell = fdt_getprop(gd->fdt_blob, ofnode_to_offset(node), propname,
|
|
&len);
|
|
if (!cell || len < sizeof(*cell)) {
|
|
debug("(not found)\n");
|
|
return -EINVAL;
|
|
}
|
|
*outp = fdt64_to_cpu(cell[0]);
|
|
debug("%#llx (%lld)\n", (unsigned long long)*outp,
|
|
(unsigned long long)*outp);
|
|
|
|
return 0;
|
|
}
|
|
|
|
u64 ofnode_read_u64_default(ofnode node, const char *propname, u64 def)
|
|
{
|
|
assert(ofnode_valid(node));
|
|
ofnode_read_u64(node, propname, &def);
|
|
|
|
return def;
|
|
}
|
|
|
|
bool ofnode_read_bool(ofnode node, const char *propname)
|
|
{
|
|
const void *prop;
|
|
|
|
assert(ofnode_valid(node));
|
|
debug("%s: %s: ", __func__, propname);
|
|
|
|
prop = ofnode_get_property(node, propname, NULL);
|
|
|
|
debug("%s\n", prop ? "true" : "false");
|
|
|
|
return prop ? true : false;
|
|
}
|
|
|
|
const void *ofnode_read_prop(ofnode node, const char *propname, int *sizep)
|
|
{
|
|
const char *val = NULL;
|
|
int len;
|
|
|
|
assert(ofnode_valid(node));
|
|
debug("%s: %s: ", __func__, propname);
|
|
|
|
if (ofnode_is_np(node)) {
|
|
struct property *prop = of_find_property(
|
|
ofnode_to_np(node), propname, &len);
|
|
|
|
if (prop) {
|
|
val = prop->value;
|
|
len = prop->length;
|
|
}
|
|
} else {
|
|
val = fdt_getprop(gd->fdt_blob, ofnode_to_offset(node),
|
|
propname, &len);
|
|
}
|
|
if (!val) {
|
|
debug("<not found>\n");
|
|
if (sizep)
|
|
*sizep = -FDT_ERR_NOTFOUND;
|
|
return NULL;
|
|
}
|
|
if (sizep)
|
|
*sizep = len;
|
|
|
|
return val;
|
|
}
|
|
|
|
const char *ofnode_read_string(ofnode node, const char *propname)
|
|
{
|
|
const char *str;
|
|
int len;
|
|
|
|
str = ofnode_read_prop(node, propname, &len);
|
|
if (!str)
|
|
return NULL;
|
|
|
|
if (strnlen(str, len) >= len) {
|
|
debug("<invalid>\n");
|
|
return NULL;
|
|
}
|
|
debug("%s\n", str);
|
|
|
|
return str;
|
|
}
|
|
|
|
int ofnode_read_size(ofnode node, const char *propname)
|
|
{
|
|
int len;
|
|
|
|
if (!ofnode_read_prop(node, propname, &len))
|
|
return -EINVAL;
|
|
|
|
return len;
|
|
}
|
|
|
|
ofnode ofnode_find_subnode(ofnode node, const char *subnode_name)
|
|
{
|
|
ofnode subnode;
|
|
|
|
assert(ofnode_valid(node));
|
|
debug("%s: %s: ", __func__, subnode_name);
|
|
|
|
if (ofnode_is_np(node)) {
|
|
const struct device_node *np = ofnode_to_np(node);
|
|
|
|
for (np = np->child; np; np = np->sibling) {
|
|
if (!strcmp(subnode_name, np->name))
|
|
break;
|
|
}
|
|
subnode = np_to_ofnode(np);
|
|
} else {
|
|
int ooffset = fdt_subnode_offset(gd->fdt_blob,
|
|
ofnode_to_offset(node), subnode_name);
|
|
subnode = offset_to_ofnode(ooffset);
|
|
}
|
|
debug("%s\n", ofnode_valid(subnode) ?
|
|
ofnode_get_name(subnode) : "<none>");
|
|
|
|
return subnode;
|
|
}
|
|
|
|
int ofnode_read_u32_array(ofnode node, const char *propname,
|
|
u32 *out_values, size_t sz)
|
|
{
|
|
assert(ofnode_valid(node));
|
|
debug("%s: %s: ", __func__, propname);
|
|
|
|
if (ofnode_is_np(node)) {
|
|
return of_read_u32_array(ofnode_to_np(node), propname,
|
|
out_values, sz);
|
|
} else {
|
|
return fdtdec_get_int_array(gd->fdt_blob,
|
|
ofnode_to_offset(node), propname,
|
|
out_values, sz);
|
|
}
|
|
}
|
|
|
|
#if !CONFIG_IS_ENABLED(DM_INLINE_OFNODE)
|
|
bool ofnode_is_enabled(ofnode node)
|
|
{
|
|
if (ofnode_is_np(node)) {
|
|
return of_device_is_available(ofnode_to_np(node));
|
|
} else {
|
|
return fdtdec_get_is_enabled(gd->fdt_blob,
|
|
ofnode_to_offset(node));
|
|
}
|
|
}
|
|
|
|
ofnode ofnode_first_subnode(ofnode node)
|
|
{
|
|
assert(ofnode_valid(node));
|
|
if (ofnode_is_np(node))
|
|
return np_to_ofnode(node.np->child);
|
|
|
|
return offset_to_ofnode(
|
|
fdt_first_subnode(gd->fdt_blob, ofnode_to_offset(node)));
|
|
}
|
|
|
|
ofnode ofnode_next_subnode(ofnode node)
|
|
{
|
|
assert(ofnode_valid(node));
|
|
if (ofnode_is_np(node))
|
|
return np_to_ofnode(node.np->sibling);
|
|
|
|
return offset_to_ofnode(
|
|
fdt_next_subnode(gd->fdt_blob, ofnode_to_offset(node)));
|
|
}
|
|
#endif /* !DM_INLINE_OFNODE */
|
|
|
|
ofnode ofnode_get_parent(ofnode node)
|
|
{
|
|
ofnode parent;
|
|
|
|
assert(ofnode_valid(node));
|
|
if (ofnode_is_np(node))
|
|
parent = np_to_ofnode(of_get_parent(ofnode_to_np(node)));
|
|
else
|
|
parent.of_offset = fdt_parent_offset(gd->fdt_blob,
|
|
ofnode_to_offset(node));
|
|
|
|
return parent;
|
|
}
|
|
|
|
const char *ofnode_get_name(ofnode node)
|
|
{
|
|
if (!ofnode_valid(node)) {
|
|
debug("%s node not valid\n", __func__);
|
|
return NULL;
|
|
}
|
|
|
|
if (ofnode_is_np(node))
|
|
return strrchr(node.np->full_name, '/') + 1;
|
|
|
|
return fdt_get_name(gd->fdt_blob, ofnode_to_offset(node), NULL);
|
|
}
|
|
|
|
ofnode ofnode_get_by_phandle(uint phandle)
|
|
{
|
|
ofnode node;
|
|
|
|
if (of_live_active())
|
|
node = np_to_ofnode(of_find_node_by_phandle(phandle));
|
|
else
|
|
node.of_offset = fdt_node_offset_by_phandle(gd->fdt_blob,
|
|
phandle);
|
|
|
|
return node;
|
|
}
|
|
|
|
fdt_addr_t ofnode_get_addr_size_index(ofnode node, int index, fdt_size_t *size)
|
|
{
|
|
int na, ns;
|
|
|
|
if (ofnode_is_np(node)) {
|
|
const __be32 *prop_val;
|
|
u64 size64;
|
|
uint flags;
|
|
|
|
prop_val = of_get_address(ofnode_to_np(node), index, &size64,
|
|
&flags);
|
|
if (!prop_val)
|
|
return FDT_ADDR_T_NONE;
|
|
if (size)
|
|
*size = size64;
|
|
|
|
ns = of_n_size_cells(ofnode_to_np(node));
|
|
|
|
if (IS_ENABLED(CONFIG_OF_TRANSLATE) &&
|
|
(ns > 0 || gd_size_cells_0())) {
|
|
return of_translate_address(ofnode_to_np(node), prop_val);
|
|
} else {
|
|
na = of_n_addr_cells(ofnode_to_np(node));
|
|
return of_read_number(prop_val, na);
|
|
}
|
|
} else {
|
|
na = ofnode_read_simple_addr_cells(ofnode_get_parent(node));
|
|
ns = ofnode_read_simple_size_cells(ofnode_get_parent(node));
|
|
return fdtdec_get_addr_size_fixed(gd->fdt_blob,
|
|
ofnode_to_offset(node), "reg",
|
|
index, na, ns, size, true);
|
|
}
|
|
|
|
return FDT_ADDR_T_NONE;
|
|
}
|
|
|
|
fdt_addr_t ofnode_get_addr_index(ofnode node, int index)
|
|
{
|
|
fdt_size_t size;
|
|
|
|
return ofnode_get_addr_size_index(node, index, &size);
|
|
}
|
|
|
|
fdt_addr_t ofnode_get_addr(ofnode node)
|
|
{
|
|
return ofnode_get_addr_index(node, 0);
|
|
}
|
|
|
|
int ofnode_stringlist_search(ofnode node, const char *property,
|
|
const char *string)
|
|
{
|
|
if (ofnode_is_np(node)) {
|
|
return of_property_match_string(ofnode_to_np(node),
|
|
property, string);
|
|
} else {
|
|
int ret;
|
|
|
|
ret = fdt_stringlist_search(gd->fdt_blob,
|
|
ofnode_to_offset(node), property,
|
|
string);
|
|
if (ret == -FDT_ERR_NOTFOUND)
|
|
return -ENODATA;
|
|
else if (ret < 0)
|
|
return -EINVAL;
|
|
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
int ofnode_read_string_index(ofnode node, const char *property, int index,
|
|
const char **outp)
|
|
{
|
|
if (ofnode_is_np(node)) {
|
|
return of_property_read_string_index(ofnode_to_np(node),
|
|
property, index, outp);
|
|
} else {
|
|
int len;
|
|
|
|
*outp = fdt_stringlist_get(gd->fdt_blob, ofnode_to_offset(node),
|
|
property, index, &len);
|
|
if (len < 0)
|
|
return -EINVAL;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int ofnode_read_string_count(ofnode node, const char *property)
|
|
{
|
|
if (ofnode_is_np(node)) {
|
|
return of_property_count_strings(ofnode_to_np(node), property);
|
|
} else {
|
|
return fdt_stringlist_count(gd->fdt_blob,
|
|
ofnode_to_offset(node), property);
|
|
}
|
|
}
|
|
|
|
static void ofnode_from_fdtdec_phandle_args(struct fdtdec_phandle_args *in,
|
|
struct ofnode_phandle_args *out)
|
|
{
|
|
assert(OF_MAX_PHANDLE_ARGS == MAX_PHANDLE_ARGS);
|
|
out->node = offset_to_ofnode(in->node);
|
|
out->args_count = in->args_count;
|
|
memcpy(out->args, in->args, sizeof(out->args));
|
|
}
|
|
|
|
static void ofnode_from_of_phandle_args(struct of_phandle_args *in,
|
|
struct ofnode_phandle_args *out)
|
|
{
|
|
assert(OF_MAX_PHANDLE_ARGS == MAX_PHANDLE_ARGS);
|
|
out->node = np_to_ofnode(in->np);
|
|
out->args_count = in->args_count;
|
|
memcpy(out->args, in->args, sizeof(out->args));
|
|
}
|
|
|
|
int ofnode_parse_phandle_with_args(ofnode node, const char *list_name,
|
|
const char *cells_name, int cell_count,
|
|
int index,
|
|
struct ofnode_phandle_args *out_args)
|
|
{
|
|
if (ofnode_is_np(node)) {
|
|
struct of_phandle_args args;
|
|
int ret;
|
|
|
|
ret = of_parse_phandle_with_args(ofnode_to_np(node),
|
|
list_name, cells_name,
|
|
cell_count, index,
|
|
&args);
|
|
if (ret)
|
|
return ret;
|
|
ofnode_from_of_phandle_args(&args, out_args);
|
|
} else {
|
|
struct fdtdec_phandle_args args;
|
|
int ret;
|
|
|
|
ret = fdtdec_parse_phandle_with_args(gd->fdt_blob,
|
|
ofnode_to_offset(node),
|
|
list_name, cells_name,
|
|
cell_count, index, &args);
|
|
if (ret)
|
|
return ret;
|
|
ofnode_from_fdtdec_phandle_args(&args, out_args);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ofnode_count_phandle_with_args(ofnode node, const char *list_name,
|
|
const char *cells_name, int cell_count)
|
|
{
|
|
if (ofnode_is_np(node))
|
|
return of_count_phandle_with_args(ofnode_to_np(node),
|
|
list_name, cells_name, cell_count);
|
|
else
|
|
return fdtdec_parse_phandle_with_args(gd->fdt_blob,
|
|
ofnode_to_offset(node), list_name, cells_name,
|
|
cell_count, -1, NULL);
|
|
}
|
|
|
|
ofnode ofnode_path(const char *path)
|
|
{
|
|
if (of_live_active())
|
|
return np_to_ofnode(of_find_node_by_path(path));
|
|
else
|
|
return offset_to_ofnode(fdt_path_offset(gd->fdt_blob, path));
|
|
}
|
|
|
|
const void *ofnode_read_chosen_prop(const char *propname, int *sizep)
|
|
{
|
|
ofnode chosen_node;
|
|
|
|
chosen_node = ofnode_path("/chosen");
|
|
|
|
return ofnode_read_prop(chosen_node, propname, sizep);
|
|
}
|
|
|
|
const char *ofnode_read_chosen_string(const char *propname)
|
|
{
|
|
return ofnode_read_chosen_prop(propname, NULL);
|
|
}
|
|
|
|
ofnode ofnode_get_chosen_node(const char *name)
|
|
{
|
|
const char *prop;
|
|
|
|
prop = ofnode_read_chosen_prop(name, NULL);
|
|
if (!prop)
|
|
return ofnode_null();
|
|
|
|
return ofnode_path(prop);
|
|
}
|
|
|
|
const void *ofnode_read_aliases_prop(const char *propname, int *sizep)
|
|
{
|
|
ofnode node;
|
|
|
|
node = ofnode_path("/aliases");
|
|
|
|
return ofnode_read_prop(node, propname, sizep);
|
|
}
|
|
|
|
ofnode ofnode_get_aliases_node(const char *name)
|
|
{
|
|
const char *prop;
|
|
|
|
prop = ofnode_read_aliases_prop(name, NULL);
|
|
if (!prop)
|
|
return ofnode_null();
|
|
|
|
debug("%s: node_path: %s\n", __func__, prop);
|
|
|
|
return ofnode_path(prop);
|
|
}
|
|
|
|
int ofnode_get_child_count(ofnode parent)
|
|
{
|
|
ofnode child;
|
|
int num = 0;
|
|
|
|
ofnode_for_each_subnode(child, parent)
|
|
num++;
|
|
|
|
return num;
|
|
}
|
|
|
|
static int decode_timing_property(ofnode node, const char *name,
|
|
struct timing_entry *result)
|
|
{
|
|
int length, ret = 0;
|
|
|
|
length = ofnode_read_size(node, name);
|
|
if (length < 0) {
|
|
debug("%s: could not find property %s\n",
|
|
ofnode_get_name(node), name);
|
|
return length;
|
|
}
|
|
|
|
if (length == sizeof(u32)) {
|
|
result->typ = ofnode_read_u32_default(node, name, 0);
|
|
result->min = result->typ;
|
|
result->max = result->typ;
|
|
} else {
|
|
ret = ofnode_read_u32_array(node, name, &result->min, 3);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int ofnode_decode_display_timing(ofnode parent, int index,
|
|
struct display_timing *dt)
|
|
{
|
|
int i;
|
|
ofnode timings, node;
|
|
u32 val = 0;
|
|
int ret = 0;
|
|
|
|
timings = ofnode_find_subnode(parent, "display-timings");
|
|
if (!ofnode_valid(timings))
|
|
return -EINVAL;
|
|
|
|
i = 0;
|
|
ofnode_for_each_subnode(node, timings) {
|
|
if (i++ == index)
|
|
break;
|
|
}
|
|
|
|
if (!ofnode_valid(node))
|
|
return -EINVAL;
|
|
|
|
memset(dt, 0, sizeof(*dt));
|
|
|
|
ret |= decode_timing_property(node, "hback-porch", &dt->hback_porch);
|
|
ret |= decode_timing_property(node, "hfront-porch", &dt->hfront_porch);
|
|
ret |= decode_timing_property(node, "hactive", &dt->hactive);
|
|
ret |= decode_timing_property(node, "hsync-len", &dt->hsync_len);
|
|
ret |= decode_timing_property(node, "vback-porch", &dt->vback_porch);
|
|
ret |= decode_timing_property(node, "vfront-porch", &dt->vfront_porch);
|
|
ret |= decode_timing_property(node, "vactive", &dt->vactive);
|
|
ret |= decode_timing_property(node, "vsync-len", &dt->vsync_len);
|
|
ret |= decode_timing_property(node, "clock-frequency", &dt->pixelclock);
|
|
|
|
dt->flags = 0;
|
|
val = ofnode_read_u32_default(node, "vsync-active", -1);
|
|
if (val != -1) {
|
|
dt->flags |= val ? DISPLAY_FLAGS_VSYNC_HIGH :
|
|
DISPLAY_FLAGS_VSYNC_LOW;
|
|
}
|
|
val = ofnode_read_u32_default(node, "hsync-active", -1);
|
|
if (val != -1) {
|
|
dt->flags |= val ? DISPLAY_FLAGS_HSYNC_HIGH :
|
|
DISPLAY_FLAGS_HSYNC_LOW;
|
|
}
|
|
val = ofnode_read_u32_default(node, "de-active", -1);
|
|
if (val != -1) {
|
|
dt->flags |= val ? DISPLAY_FLAGS_DE_HIGH :
|
|
DISPLAY_FLAGS_DE_LOW;
|
|
}
|
|
val = ofnode_read_u32_default(node, "pixelclk-active", -1);
|
|
if (val != -1) {
|
|
dt->flags |= val ? DISPLAY_FLAGS_PIXDATA_POSEDGE :
|
|
DISPLAY_FLAGS_PIXDATA_NEGEDGE;
|
|
}
|
|
|
|
if (ofnode_read_bool(node, "interlaced"))
|
|
dt->flags |= DISPLAY_FLAGS_INTERLACED;
|
|
if (ofnode_read_bool(node, "doublescan"))
|
|
dt->flags |= DISPLAY_FLAGS_DOUBLESCAN;
|
|
if (ofnode_read_bool(node, "doubleclk"))
|
|
dt->flags |= DISPLAY_FLAGS_DOUBLECLK;
|
|
|
|
return ret;
|
|
}
|
|
|
|
const void *ofnode_get_property(ofnode node, const char *propname, int *lenp)
|
|
{
|
|
if (ofnode_is_np(node))
|
|
return of_get_property(ofnode_to_np(node), propname, lenp);
|
|
else
|
|
return fdt_getprop(gd->fdt_blob, ofnode_to_offset(node),
|
|
propname, lenp);
|
|
}
|
|
|
|
int ofnode_get_first_property(ofnode node, struct ofprop *prop)
|
|
{
|
|
prop->node = node;
|
|
|
|
if (ofnode_is_np(node)) {
|
|
prop->prop = of_get_first_property(ofnode_to_np(prop->node));
|
|
if (!prop->prop)
|
|
return -FDT_ERR_NOTFOUND;
|
|
} else {
|
|
prop->offset =
|
|
fdt_first_property_offset(gd->fdt_blob,
|
|
ofnode_to_offset(prop->node));
|
|
if (prop->offset < 0)
|
|
return prop->offset;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ofnode_get_next_property(struct ofprop *prop)
|
|
{
|
|
if (ofnode_is_np(prop->node)) {
|
|
prop->prop = of_get_next_property(ofnode_to_np(prop->node),
|
|
prop->prop);
|
|
if (!prop->prop)
|
|
return -FDT_ERR_NOTFOUND;
|
|
} else {
|
|
prop->offset = fdt_next_property_offset(gd->fdt_blob,
|
|
prop->offset);
|
|
if (prop->offset < 0)
|
|
return prop->offset;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
const void *ofnode_get_property_by_prop(const struct ofprop *prop,
|
|
const char **propname, int *lenp)
|
|
{
|
|
if (ofnode_is_np(prop->node))
|
|
return of_get_property_by_prop(ofnode_to_np(prop->node),
|
|
prop->prop, propname, lenp);
|
|
else
|
|
return fdt_getprop_by_offset(gd->fdt_blob,
|
|
prop->offset,
|
|
propname, lenp);
|
|
}
|
|
|
|
bool ofnode_is_available(ofnode node)
|
|
{
|
|
if (ofnode_is_np(node))
|
|
return of_device_is_available(ofnode_to_np(node));
|
|
else
|
|
return fdtdec_get_is_enabled(gd->fdt_blob,
|
|
ofnode_to_offset(node));
|
|
}
|
|
|
|
fdt_addr_t ofnode_get_addr_size(ofnode node, const char *property,
|
|
fdt_size_t *sizep)
|
|
{
|
|
if (ofnode_is_np(node)) {
|
|
int na, ns;
|
|
int psize;
|
|
const struct device_node *np = ofnode_to_np(node);
|
|
const __be32 *prop = of_get_property(np, property, &psize);
|
|
|
|
if (!prop)
|
|
return FDT_ADDR_T_NONE;
|
|
na = of_n_addr_cells(np);
|
|
ns = of_n_size_cells(np);
|
|
*sizep = of_read_number(prop + na, ns);
|
|
|
|
if (CONFIG_IS_ENABLED(OF_TRANSLATE) &&
|
|
(ns > 0 || gd_size_cells_0())) {
|
|
return of_translate_address(np, prop);
|
|
}
|
|
else
|
|
return of_read_number(prop, na);
|
|
} else {
|
|
return fdtdec_get_addr_size(gd->fdt_blob,
|
|
ofnode_to_offset(node), property,
|
|
sizep);
|
|
}
|
|
}
|
|
|
|
const uint8_t *ofnode_read_u8_array_ptr(ofnode node, const char *propname,
|
|
size_t sz)
|
|
{
|
|
if (ofnode_is_np(node)) {
|
|
const struct device_node *np = ofnode_to_np(node);
|
|
int psize;
|
|
const __be32 *prop = of_get_property(np, propname, &psize);
|
|
|
|
if (!prop || sz != psize)
|
|
return NULL;
|
|
return (uint8_t *)prop;
|
|
|
|
} else {
|
|
return fdtdec_locate_byte_array(gd->fdt_blob,
|
|
ofnode_to_offset(node), propname, sz);
|
|
}
|
|
}
|
|
|
|
int ofnode_read_pci_addr(ofnode node, enum fdt_pci_space type,
|
|
const char *propname, struct fdt_pci_addr *addr)
|
|
{
|
|
const fdt32_t *cell;
|
|
int len;
|
|
int ret = -ENOENT;
|
|
|
|
debug("%s: %s: ", __func__, propname);
|
|
|
|
/*
|
|
* If we follow the pci bus bindings strictly, we should check
|
|
* the value of the node's parent node's #address-cells and
|
|
* #size-cells. They need to be 3 and 2 accordingly. However,
|
|
* for simplicity we skip the check here.
|
|
*/
|
|
cell = ofnode_get_property(node, propname, &len);
|
|
if (!cell)
|
|
goto fail;
|
|
|
|
if ((len % FDT_PCI_REG_SIZE) == 0) {
|
|
int num = len / FDT_PCI_REG_SIZE;
|
|
int i;
|
|
|
|
for (i = 0; i < num; i++) {
|
|
debug("pci address #%d: %08lx %08lx %08lx\n", i,
|
|
(ulong)fdt32_to_cpu(cell[0]),
|
|
(ulong)fdt32_to_cpu(cell[1]),
|
|
(ulong)fdt32_to_cpu(cell[2]));
|
|
if ((fdt32_to_cpu(*cell) & type) == type) {
|
|
addr->phys_hi = fdt32_to_cpu(cell[0]);
|
|
addr->phys_mid = fdt32_to_cpu(cell[1]);
|
|
addr->phys_lo = fdt32_to_cpu(cell[2]);
|
|
break;
|
|
}
|
|
|
|
cell += (FDT_PCI_ADDR_CELLS +
|
|
FDT_PCI_SIZE_CELLS);
|
|
}
|
|
|
|
if (i == num) {
|
|
ret = -ENXIO;
|
|
goto fail;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
ret = -EINVAL;
|
|
|
|
fail:
|
|
debug("(not found)\n");
|
|
return ret;
|
|
}
|
|
|
|
int ofnode_read_pci_vendev(ofnode node, u16 *vendor, u16 *device)
|
|
{
|
|
const char *list, *end;
|
|
int len;
|
|
|
|
list = ofnode_get_property(node, "compatible", &len);
|
|
if (!list)
|
|
return -ENOENT;
|
|
|
|
end = list + len;
|
|
while (list < end) {
|
|
len = strlen(list);
|
|
if (len >= strlen("pciVVVV,DDDD")) {
|
|
char *s = strstr(list, "pci");
|
|
|
|
/*
|
|
* check if the string is something like pciVVVV,DDDD.RR
|
|
* or just pciVVVV,DDDD
|
|
*/
|
|
if (s && s[7] == ',' &&
|
|
(s[12] == '.' || s[12] == 0)) {
|
|
s += 3;
|
|
*vendor = simple_strtol(s, NULL, 16);
|
|
|
|
s += 5;
|
|
*device = simple_strtol(s, NULL, 16);
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
list += (len + 1);
|
|
}
|
|
|
|
return -ENOENT;
|
|
}
|
|
|
|
int ofnode_read_addr_cells(ofnode node)
|
|
{
|
|
if (ofnode_is_np(node)) {
|
|
return of_n_addr_cells(ofnode_to_np(node));
|
|
} else {
|
|
int parent = fdt_parent_offset(gd->fdt_blob,
|
|
ofnode_to_offset(node));
|
|
|
|
return fdt_address_cells(gd->fdt_blob, parent);
|
|
}
|
|
}
|
|
|
|
int ofnode_read_size_cells(ofnode node)
|
|
{
|
|
if (ofnode_is_np(node)) {
|
|
return of_n_size_cells(ofnode_to_np(node));
|
|
} else {
|
|
int parent = fdt_parent_offset(gd->fdt_blob,
|
|
ofnode_to_offset(node));
|
|
|
|
return fdt_size_cells(gd->fdt_blob, parent);
|
|
}
|
|
}
|
|
|
|
int ofnode_read_simple_addr_cells(ofnode node)
|
|
{
|
|
if (ofnode_is_np(node))
|
|
return of_simple_addr_cells(ofnode_to_np(node));
|
|
else
|
|
return fdt_address_cells(gd->fdt_blob, ofnode_to_offset(node));
|
|
}
|
|
|
|
int ofnode_read_simple_size_cells(ofnode node)
|
|
{
|
|
if (ofnode_is_np(node))
|
|
return of_simple_size_cells(ofnode_to_np(node));
|
|
else
|
|
return fdt_size_cells(gd->fdt_blob, ofnode_to_offset(node));
|
|
}
|
|
|
|
bool ofnode_pre_reloc(ofnode node)
|
|
{
|
|
#if defined(CONFIG_SPL_BUILD) || defined(CONFIG_TPL_BUILD)
|
|
/* for SPL and TPL the remaining nodes after the fdtgrep 1st pass
|
|
* had property dm-pre-reloc or u-boot,dm-spl/tpl.
|
|
* They are removed in final dtb (fdtgrep 2nd pass)
|
|
*/
|
|
return true;
|
|
#else
|
|
if (ofnode_read_bool(node, "u-boot,dm-pre-reloc"))
|
|
return true;
|
|
if (ofnode_read_bool(node, "u-boot,dm-pre-proper"))
|
|
return true;
|
|
|
|
/*
|
|
* In regular builds individual spl and tpl handling both
|
|
* count as handled pre-relocation for later second init.
|
|
*/
|
|
if (ofnode_read_bool(node, "u-boot,dm-spl") ||
|
|
ofnode_read_bool(node, "u-boot,dm-tpl"))
|
|
return true;
|
|
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
int ofnode_read_resource(ofnode node, uint index, struct resource *res)
|
|
{
|
|
if (ofnode_is_np(node)) {
|
|
return of_address_to_resource(ofnode_to_np(node), index, res);
|
|
} else {
|
|
struct fdt_resource fres;
|
|
int ret;
|
|
|
|
ret = fdt_get_resource(gd->fdt_blob, ofnode_to_offset(node),
|
|
"reg", index, &fres);
|
|
if (ret < 0)
|
|
return -EINVAL;
|
|
memset(res, '\0', sizeof(*res));
|
|
res->start = fres.start;
|
|
res->end = fres.end;
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int ofnode_read_resource_byname(ofnode node, const char *name,
|
|
struct resource *res)
|
|
{
|
|
int index;
|
|
|
|
index = ofnode_stringlist_search(node, "reg-names", name);
|
|
if (index < 0)
|
|
return index;
|
|
|
|
return ofnode_read_resource(node, index, res);
|
|
}
|
|
|
|
u64 ofnode_translate_address(ofnode node, const fdt32_t *in_addr)
|
|
{
|
|
if (ofnode_is_np(node))
|
|
return of_translate_address(ofnode_to_np(node), in_addr);
|
|
else
|
|
return fdt_translate_address(gd->fdt_blob, ofnode_to_offset(node), in_addr);
|
|
}
|
|
|
|
u64 ofnode_translate_dma_address(ofnode node, const fdt32_t *in_addr)
|
|
{
|
|
if (ofnode_is_np(node))
|
|
return of_translate_dma_address(ofnode_to_np(node), in_addr);
|
|
else
|
|
return fdt_translate_dma_address(gd->fdt_blob, ofnode_to_offset(node), in_addr);
|
|
}
|
|
|
|
int ofnode_device_is_compatible(ofnode node, const char *compat)
|
|
{
|
|
if (ofnode_is_np(node))
|
|
return of_device_is_compatible(ofnode_to_np(node), compat,
|
|
NULL, NULL);
|
|
else
|
|
return !fdt_node_check_compatible(gd->fdt_blob,
|
|
ofnode_to_offset(node),
|
|
compat);
|
|
}
|
|
|
|
ofnode ofnode_by_compatible(ofnode from, const char *compat)
|
|
{
|
|
if (of_live_active()) {
|
|
return np_to_ofnode(of_find_compatible_node(
|
|
(struct device_node *)ofnode_to_np(from), NULL,
|
|
compat));
|
|
} else {
|
|
return offset_to_ofnode(fdt_node_offset_by_compatible(
|
|
gd->fdt_blob, ofnode_to_offset(from), compat));
|
|
}
|
|
}
|
|
|
|
ofnode ofnode_by_prop_value(ofnode from, const char *propname,
|
|
const void *propval, int proplen)
|
|
{
|
|
if (of_live_active()) {
|
|
return np_to_ofnode(of_find_node_by_prop_value(
|
|
(struct device_node *)ofnode_to_np(from), propname,
|
|
propval, proplen));
|
|
} else {
|
|
return offset_to_ofnode(fdt_node_offset_by_prop_value(
|
|
gd->fdt_blob, ofnode_to_offset(from),
|
|
propname, propval, proplen));
|
|
}
|
|
}
|
|
|
|
int ofnode_write_prop(ofnode node, const char *propname, int len,
|
|
const void *value)
|
|
{
|
|
const struct device_node *np = ofnode_to_np(node);
|
|
struct property *pp;
|
|
struct property *pp_last = NULL;
|
|
struct property *new;
|
|
|
|
if (!of_live_active())
|
|
return -ENOSYS;
|
|
|
|
if (!np)
|
|
return -EINVAL;
|
|
|
|
for (pp = np->properties; pp; pp = pp->next) {
|
|
if (strcmp(pp->name, propname) == 0) {
|
|
/* Property exists -> change value */
|
|
pp->value = (void *)value;
|
|
pp->length = len;
|
|
return 0;
|
|
}
|
|
pp_last = pp;
|
|
}
|
|
|
|
if (!pp_last)
|
|
return -ENOENT;
|
|
|
|
/* Property does not exist -> append new property */
|
|
new = malloc(sizeof(struct property));
|
|
if (!new)
|
|
return -ENOMEM;
|
|
|
|
new->name = strdup(propname);
|
|
if (!new->name) {
|
|
free(new);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
new->value = (void *)value;
|
|
new->length = len;
|
|
new->next = NULL;
|
|
|
|
pp_last->next = new;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ofnode_write_string(ofnode node, const char *propname, const char *value)
|
|
{
|
|
if (!of_live_active())
|
|
return -ENOSYS;
|
|
|
|
assert(ofnode_valid(node));
|
|
|
|
debug("%s: %s = %s", __func__, propname, value);
|
|
|
|
return ofnode_write_prop(node, propname, strlen(value) + 1, value);
|
|
}
|
|
|
|
int ofnode_set_enabled(ofnode node, bool value)
|
|
{
|
|
if (!of_live_active())
|
|
return -ENOSYS;
|
|
|
|
assert(ofnode_valid(node));
|
|
|
|
if (value)
|
|
return ofnode_write_string(node, "status", "okay");
|
|
else
|
|
return ofnode_write_string(node, "status", "disabled");
|
|
}
|