[PATCH 05/14] bus: mvebu-mbus: Update the mbus-compatible node's ranges property
From: Ezequiel Garcia <hidden>
Date: 2013-06-07 16:47:42
Also in:
linux-devicetree
Subsystem:
arm/marvell dove/mv78xx0/orion soc support, the rest · Maintainers:
Andrew Lunn, Sebastian Hesselbarth, Gregory Clement, Linus Torvalds
Possibly related (same subject, not in this thread)
- 2013-06-12 · Re: [PATCH 05/14] bus: mvebu-mbus: Update the mbus-compatible node's ranges property · Grant Likely <hidden>
Ideally 'ranges' entry should be added in device tree source files. However, since those properties do not inherit the entries from included files it would be a duplication hell, which means a nightmare to maintain. So, instead of hardcoding the required translation entries, we update them programatically, whenever a decoding window is allocated. Signed-off-by: Ezequiel Garcia <redacted> --- drivers/bus/mvebu-mbus.c | 177 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 160 insertions(+), 17 deletions(-)
diff --git a/drivers/bus/mvebu-mbus.c b/drivers/bus/mvebu-mbus.c
index ac4115a..1779f27 100644
--- a/drivers/bus/mvebu-mbus.c
+++ b/drivers/bus/mvebu-mbus.c@@ -53,6 +53,7 @@ #include <linux/kernel.h> #include <linux/module.h> +#include <linux/slab.h> #include <linux/init.h> #include <linux/mbus.h> #include <linux/io.h>
@@ -144,6 +145,7 @@ struct mvebu_mbus_state { struct dentry *debugfs_devs; const struct mvebu_mbus_soc_data *soc; int hw_io_coherency; + struct device_node *node; }; static struct mvebu_mbus_state mbus_state;
@@ -315,29 +317,155 @@ static int mvebu_mbus_setup_window(struct mvebu_mbus_state *mbus, return 0; } +#define WINDOWID(target, attr) ((target << 24) | (attr << 16)) + +static int mvebu_mbus_update_ranges(u32 base, u32 size, u32 target, u32 attr) +{ + int ranges_cells, addr_cells, c_addr_cells, c_size_cells; + int ranges_len, ret; + const __be32 *prop; + void *value; + struct property *old, *new; + struct device_node *node = mbus_state.node; + + addr_cells = of_n_addr_cells(node); + prop = of_get_property(node, "#address-cells", NULL); + c_addr_cells = be32_to_cpup(prop); + prop = of_get_property(node, "#size-cells", NULL); + c_size_cells = be32_to_cpup(prop); + + ranges_cells = c_addr_cells + addr_cells + c_size_cells; + + pr_debug("add translation: base %x, size %x - target %x, attr %x\n", + base, size, target, attr); + + /* DT nodes always have ranges property */ + old = of_find_property(node, "ranges", &ranges_len); + if (!old) + return -EINVAL; + + of_node_get(node); + + /* Allocate new property */ + new = kzalloc(sizeof(struct property), GFP_KERNEL); + if (!new) { + ret = -ENOMEM; + goto out_put_node; + } + + new->name = "ranges"; + value = kzalloc(old->length + (ranges_cells * sizeof(__be32)), + GFP_KERNEL); + if (!value) { + ret = -ENOMEM; + goto out_free_prop; + } + + new->value = value; + new->length = old->length + ranges_cells * sizeof(__be32); + if (old->length) { + memcpy(value, old->value, old->length); + value = value + old->length; + } + + /* Sanity check */ + if (c_addr_cells != 2 && addr_cells > 2) { + ret = -EINVAL; + goto out_free_value; + } + + /* + * The new ranges entry will look like this: + * <windowid child_base {0} base size> + * + * The extra zeroed-cell is needed if the addressing + * is 64-bit wide. + */ + + if (addr_cells > 1) { + struct { + __be32 windowid; + __be32 child_base; + __be32 base_high; + __be32 base_low; + __be32 size; + } *r = value; + + r->windowid = cpu_to_be32(WINDOWID(target, attr)); + r->child_base = cpu_to_be32(base); + r->base_high = cpu_to_be32(0); + r->base_low = cpu_to_be32(base); + r->size = cpu_to_be32(size); + } else { + struct { + __be32 windowid; + __be32 child_base; + __be32 base; + __be32 size; + } *r = value; + + r->windowid = cpu_to_be32(WINDOWID(target, attr)); + r->child_base = cpu_to_be32(base); + r->base = cpu_to_be32(base); + r->size = cpu_to_be32(size); + } + + ret = of_update_property(node, new); + if (ret < 0) + goto out_free_value; + + of_node_put(node); + return 0; + +out_free_value: + kfree(new->value); +out_free_prop: + kfree(new); +out_put_node: + of_node_put(node); + return ret; +} + static int mvebu_mbus_alloc_window(struct mvebu_mbus_state *mbus, phys_addr_t base, size_t size, phys_addr_t remap, u8 target, u8 attr) { - int win; + int win, ret = -ENOMEM; + bool found = false; if (remap == MVEBU_MBUS_NO_REMAP) { for (win = mbus->soc->num_remappable_wins; win < mbus->soc->num_wins; win++) - if (mvebu_mbus_window_is_free(mbus, win)) - return mvebu_mbus_setup_window(mbus, win, base, - size, remap, - target, attr); + if (mvebu_mbus_window_is_free(mbus, win)) { + found = true; + break; + } } + /* + * Either what we need is a remappable window, or a + * non-remappable window but no such window is available so we + * fallback to using a remappable window. + */ + if (!found) { + for (win = 0; win < mbus->soc->num_wins; win++) { + if (mvebu_mbus_window_is_free(mbus, win)) { + found = true; + break; + } + } + } - for (win = 0; win < mbus->soc->num_wins; win++) - if (mvebu_mbus_window_is_free(mbus, win)) - return mvebu_mbus_setup_window(mbus, win, base, size, - remap, target, attr); + /* No window found */ + if (!found) + return -ENOMEM; - return -ENOMEM; + ret = mvebu_mbus_setup_window(mbus, win, base, size, remap, + target, attr); + if (ret < 0) + return ret; + return mvebu_mbus_update_ranges(base, size, target, attr); } /*
@@ -902,11 +1030,26 @@ static int __init mbus_dt_setup_win(struct mvebu_mbus_state *mbus, const char *name; int i; - /* Special case for the identity mapping */ - if (target == 0xff && attr == 0xff) + /* + * The identity mapping needs a translation entry@the + * mbus node level. The entry makes this translation: + * {target/attr base} -> {base} + * + * In other words, it maps the fictitious address space + * that contains a field to encode the target/attribute tuple, + * into the 'true' MBus address space. + */ + if (target == 0xff && attr == 0xff) { + mvebu_mbus_update_ranges(base, size, target, attr); return 0; + } - /* Special case for the internal registers */ + /* + * Special case for the internal registers: + * There's nothing to do in this case, since + * the internal-regs base address is specified in the + * DT by means of an address translation entry. + */ if (target == 0x00 && attr == 0x00) return 0;
@@ -938,8 +1081,7 @@ static int __init mbus_dt_setup_win(struct mvebu_mbus_state *mbus, return 0; } -static int __init mbus_dt_setup(struct mvebu_mbus_state *mbus, - struct device_node *np) +static int __init mbus_dt_setup(struct mvebu_mbus_state *mbus) { struct device_node *child; int ranges_len, tuple_len;
@@ -947,7 +1089,7 @@ static int __init mbus_dt_setup(struct mvebu_mbus_state *mbus, int cell_count; const __be32 *r, *ranges_start, *ranges_end, *prop; - for_each_available_child_of_node(np, child) { + for_each_available_child_of_node(mbus_state.node, child) { addr_cells = of_n_addr_cells(child);
@@ -1004,6 +1146,7 @@ int __init mvebu_mbus_dt_init(void) of_id = of_match_node(of_mvebu_mbus_ids, np); mbus_state.soc = of_id->data; + mbus_state.node = np; if (of_address_to_resource(np, 0, &mbuswins_res)) { pr_err("cannot get MBUS register address\n");
@@ -1024,6 +1167,6 @@ int __init mvebu_mbus_dt_init(void) return ret; /* Setup statically declared windows in the DT */ - return mbus_dt_setup(&mbus_state, np); + return mbus_dt_setup(&mbus_state); } #endif
--
1.8.1.5