This patch adds static window allocation to the device tree binding. Each first-child of the mbus-compatible node, with a suitable 'ranges' property, declaring an address translation, will trigger an address decoding window allocation.
Signed-off-by: Ezequiel Garcia <ezequiel.gar...@free-electrons.com> --- .../devicetree/bindings/bus/mvebu-mbus.txt | 256 +++++++++++++++++++++ drivers/bus/mvebu-mbus.c | 121 +++++++++- 2 files changed, 376 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/bus/mvebu-mbus.txt diff --git a/Documentation/devicetree/bindings/bus/mvebu-mbus.txt b/Documentation/devicetree/bindings/bus/mvebu-mbus.txt new file mode 100644 index 0000000..66d17df --- /dev/null +++ b/Documentation/devicetree/bindings/bus/mvebu-mbus.txt @@ -0,0 +1,256 @@ + +* Marvell MBus + +Required properties: + +- compatible: Should be set to one of the following: + marvell,armada370-mbus + marvell,armadaxp-mbus + +- address-cells: Must be '2'. The first cell for the MBus ID encoding, + the second cell for the address offset within the window. + +- size-cells: Must be '1'. + +- ranges: Must be set up to provide a proper translation for each child. + See the examples below. + +- controller: Contains a single phandle referring to the MBus controller + node. This allows to specify the node that contains the + registers that control the MBus, which is typically contained + within the internal register window (see below). + +* Marvell MBus controller + +Required properties: + +- compatible: Should be set to "marvell,mbus-controller". + +- reg: Device's register space. + Two entries are expected (see the examples below): + the first one controls the devices decoding window and + the second one controls the SDRAM decoding windows. + +Example: + + soc { + compatible = "marvell,armada370-mbus", "simple-bus"; + #address-cells = <2>; + #size-cells = <1>; + controller = <&mbusc>; + + internal-regs { + compatible = "simple-bus"; + + mbusc: mbus-controller@20000 { + compatible = "marvell,mbus-controller"; + reg = <0x20000 0x100>, <0x20180 0x20>; + }; + + /* more children ...*/ + }; + }; + +** MBus address decoding window specification + +The MBus children address space is comprised of two cells: the first one for +the window ID and the second one for the offset within the window. +In order to allow to describe valid and non-valid window entries, the +following encoding is used: + + 0xSIAA0000 0x00oooooo + +Where: + + S = 0x0 for a MBus valid window + S = 0xf for a non-valid window (see below) + +If S = 0x0, then: + + I = 4-bit window target ID + AA = windpw attribute + +If S = 0xf, then: + + I = don't care + AA = 1 for internal register + AA = 2 for PCI-E + +Following the above encoding, for each ranges entry for a MBus valid window +(S = 0x0), an address decoding window is allocated. On the other side, +entries for translation that do not correspond to valid windows (S = 0xf) +are skipped. + + soc { + compatible = "marvell,armada370-mbus", "simple-bus"; + #address-cells = <2>; + #size-cells = <1>; + controller = <&mbusc>; + + ranges = <0xf0010000 0 0 0xd0000000 0x100000 + 0x01e00000 0 0 0xfff00000 0x100000>; + + bootrom { + compatible = "marvell,bootrom"; + reg = <0x01e00000 0 0x100000>; + }; + + /* other children */ + ... + + internal-regs { + compatible = "simple-bus"; + ranges = <0 0xf0010000 0 0x100000>; + + mbusc: mbus-controller@20000 { + compatible = "marvell,mbus-controller"; + reg = <0x20000 0x100>, <0x20180 0x20>; + }; + + /* more children ...*/ + }; + }; + +In the shown example, the translation entry in the 'ranges' property is what +makes the MBus driver create a static decoding window for the corresponding +given child device. Note that the binding does not require child nodes to be +present. Of course, child nodes are needed to probe the devices. + +Since each window is identified by its target ID and attribute ID there's +a special macro that can be use to simplify the translation entries: + +#define MBUS_ID(target,attributes) (((target) << 24) | ((attributes) << 16)) + +Using this macro, the above example would be: + + soc { + compatible = "marvell,armada370-mbus", "simple-bus"; + #address-cells = <2>; + #size-cells = <1>; + controller = <&mbusc>; + + ranges = < MBUS_ID(0xf0, 0x01) 0 0 0xd0000000 0x100000 + MBUS_ID(0x01, 0xe0) 0 0 0xfff00000 0x100000>; + + bootrom { + compatible = "marvell,bootrom"; + reg = <MBUS_ID(0x01, 0xe0) 0 0x100000>; + }; + + /* other children */ + ... + + internal-regs { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0 MBUS_ID(0xf0, 0x01) 0 0x100000>; + + mbusc: mbus-controller@20000 { + compatible = "marvell,mbus-controller"; + reg = <0x20000 0x100>, <0x20180 0x20>; + }; + + /* other children */ + ... + }; + }; + + +** About the window base address + +Remember the MBus controller allows a great deal of flexibility for choosing +the decoding window base address. When planning the device tree layout it's +possible to choose any address as the base address, provided of course there's +a region large enough available, and with the required alignment. + +Yet in other words: there's nothing preventing us from setting a base address +of 0xf0000000, or 0xd0000000 for the NOR device shown above, if such region is +unused. + +** Window allocation policy + +The mbus-node ranges property defines a set of mbus windows that are expected +to be set by the operating system and that are guaranteed to be free of overlaps +with one another or with the system memory ranges. + +Each entry in the property refers to exactly one window. If the operating system +choses to use a different set of mbus windows, it must ensure that any address +translations performed from downstream devices are adapted accordingly. + +The operating system may insert additional mbus windows that do not conflict +with the ones listed in the ranges, e.g. for mapping PCIe devices. +As a special case, the internal register window must be set up by the boot +loader at the address listed in the ranges property, since access to that region +is needed to set up the other windows. + +** Example + +See the example below, where a more complete device tree is shown: + + soc { + compatible = "marvell,armadaxp-mbus", "simple-bus"; + controller = <&mbusc>; + + ranges = <MBUS_ID(0xf0, 0x01) 0 0 0xd0000000 0x100000 /* internal-regs */ + MBUS_ID(0xf0, 0x02) 0xe0000000 0 0xe0000000 0x8100000 /* pcie */ + MBUS_ID(0x01, 0x1d) 0 0 0xfff00000 0x100000 + MBUS_ID(0x01, 0x2f) 0 0 0xf0000000 0x8000000>; + + bootrom { + compatible = "marvell,bootrom"; + reg = <MBUS_ID(0x01, 0x1d) 0 0x100000>; + }; + + devbus-bootcs { + status = "okay"; + ranges = <0 MBUS_ID(0x01, 0x2f) 0 0x8000000>; + + /* NOR */ + nor { + compatible = "cfi-flash"; + reg = <0 0x8000000>; + bank-width = <2>; + }; + }; + + pcie-controller { + compatible = "marvell,armada-xp-pcie"; + status = "okay"; + device_type = "pci"; + + #address-cells = <3>; + #size-cells = <2>; + + ranges = + <0x82000000 0 0x40000 MBUS_ID(0xf0, 0x01) 0x40000 0 0x00002000 /* Port 0.0 registers */ + 0x82000000 0 0x42000 MBUS_ID(0xf0, 0x01) 0x42000 0 0x00002000 /* Port 2.0 registers */ + 0x82000000 0 0x44000 MBUS_ID(0xf0, 0x01) 0x44000 0 0x00002000 /* Port 0.1 registers */ + 0x82000000 0 0x48000 MBUS_ID(0xf0, 0x01) 0x48000 0 0x00002000 /* Port 0.2 registers */ + 0x82000000 0 0x4c000 MBUS_ID(0xf0, 0x01) 0x4c000 0 0x00002000 /* Port 0.3 registers */ + 0x82000000 0 0x80000 MBUS_ID(0xf0, 0x01) 0x80000 0 0x00002000 /* Port 1.0 registers */ + 0x82000000 0 0x82000 MBUS_ID(0xf0, 0x01) 0x82000 0 0x00002000 /* Port 3.0 registers */ + 0x82000000 0 0xe0000000 MBUS_ID(0xf0, 0x02) 0xe0000000 0 0x08000000 /* non-prefetchable memory */ + 0x81000000 0 0 MBUS_ID(0xf0, 0x02) 0xe8000000 0 0x00100000>; /* downstream I/O */ + + pcie@1,0 { + /* Port 0, Lane 0 */ + status = "okay"; + }; + }; + + internal-regs { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0 MBUS_ID(0xf0, 0x01) 0 0x100000>; + + mbusc: mbus-controller@20000 { + reg = <0x20000 0x100>, <0x20180 0x20>; + }; + + interrupt-controller@20000 { + reg = <0x20a00 0x2d0>, <0x21070 0x58>; + }; + }; + }; diff --git a/drivers/bus/mvebu-mbus.c b/drivers/bus/mvebu-mbus.c index 1f8b5c8..1079e7a 100644 --- a/drivers/bus/mvebu-mbus.c +++ b/drivers/bus/mvebu-mbus.c @@ -885,6 +885,121 @@ int __init mvebu_mbus_init(const char *soc, phys_addr_t mbuswins_phys_base, } #ifdef CONFIG_OF +/* + * The window IDs in the ranges DT property have the following format: + * - bits 28 to 31: MBus custom field + * - bits 24 to 27: window target ID + * - bits 16 to 23: window attribute ID + * - bits 0 to 15: unused + */ +#define CUSTOM(id) (((id) & 0xF0000000) >> 24) +#define TARGET(id) (((id) & 0x0F000000) >> 24) +#define ATTR(id) (((id) & 0x00FF0000) >> 16) + +static int __init mbus_dt_setup_win(struct mvebu_mbus_state *mbus, + u32 base, u32 size, + u8 target, u8 attr) +{ + const struct mvebu_mbus_mapping *map = mbus->soc->map; + const char *name; + int i; + + /* Search for a suitable window in the existing mappings */ + for (i = 0; map[i].name; i++) + if (map[i].target == target && + map[i].attr == (attr & map[i].attrmask)) + break; + + name = map[i].name; + if (!name) { + pr_err("window 0x%x:0x%x is unknown, skipping\n", + target, attr); + return -EINVAL; + } + + if (!mvebu_mbus_window_conflicts(mbus, base, size, target, attr)) { + pr_err("cannot add window '%s', conflicts with another window\n", + name); + return -EBUSY; + } + + if (mvebu_mbus_alloc_window(mbus, base, size, MVEBU_MBUS_NO_REMAP, + target, attr)) { + pr_err("cannot add window '%s', too many windows\n", + name); + return -ENOMEM; + } + return 0; +} + +static int __init +mbus_parse_ranges(struct device_node *node, + int *addr_cells, int *c_addr_cells, int *c_size_cells, + int *cell_count, const __be32 **ranges_start, + const __be32 **ranges_end) +{ + const __be32 *prop; + int ranges_len, tuple_len; + + *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); + + *cell_count = *addr_cells + *c_addr_cells + *c_size_cells; + tuple_len = (*cell_count) * sizeof(__be32); + + *ranges_start = of_get_property(node, "ranges", &ranges_len); + *ranges_end = *ranges_start + ranges_len / sizeof(__be32); + + if (*ranges_start == NULL || ranges_len % tuple_len) { + pr_warn("malformed ranges entry '%s'\n", node->name); + return -EINVAL; + } + return 0; +} + +static int __init mbus_dt_setup(struct mvebu_mbus_state *mbus, + struct device_node *np) +{ + int addr_cells, c_addr_cells, c_size_cells; + int i, ret, cell_count; + const __be32 *r, *ranges_start, *ranges_end; + + ret = mbus_parse_ranges(np, &addr_cells, &c_addr_cells, + &c_size_cells, &cell_count, + &ranges_start, &ranges_end); + if (ret < 0) + return ret; + + for (i = 0, r = ranges_start; r < ranges_end; r += cell_count, i++) { + u32 windowid, base, size; + u8 target, attr; + + /* + * An entry with a non-zero custom field do not + * correspond to a static window, so skip it. + */ + windowid = of_read_number(r, 1); + if (CUSTOM(windowid)) + continue; + + target = TARGET(windowid); + attr = ATTR(windowid); + + base = of_read_number(r + c_addr_cells, addr_cells); + size = of_read_number(r + c_addr_cells + addr_cells, + c_size_cells); + ret = mbus_dt_setup_win(mbus, base, size, target, attr); + if (ret < 0) + return ret; + } + return 0; +} + int __init mvebu_mbus_dt_init(void) { struct resource mbuswins_res, sdramwins_res; @@ -929,6 +1044,10 @@ int __init mvebu_mbus_dt_init(void) resource_size(&mbuswins_res), sdramwins_res.start, resource_size(&sdramwins_res)); - return ret; + if (ret) + return ret; + + /* Setup statically declared windows in the DT */ + return mbus_dt_setup(&mbus_state, np); } #endif -- 1.8.1.5 _______________________________________________ devicetree-discuss mailing list devicetree-discuss@lists.ozlabs.org https://lists.ozlabs.org/listinfo/devicetree-discuss