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         | 152 +++++++++++++++++++++
 drivers/bus/mvebu-mbus.c                           | 110 ++++++++++++++-
 2 files changed, 261 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..2b0303e
--- /dev/null
+++ b/Documentation/devicetree/bindings/bus/mvebu-mbus.txt
@@ -0,0 +1,152 @@
+
+* Marvell MBus controller
+
+Required properties:
+
+- compatible:  Should be set to one of the following:
+               marvell,armada370-mbus
+               marvell,armadaxp-mbus
+
+- 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 window.
+
+Example:
+
+       soc {
+               compatible = "marvell,armada370-mbus", "simple-bus";
+               reg = <0xd0020000 0x100>, <0xd0020180 0x20>;
+       };
+
+** How does it work?
+
+The MBus driver controls the allocation and release of the addresses
+decoding windows needed to access devices.
+
+Each window, is identified by its target ID and attribute ID. In order
+to represent this, each of mbus-node first-level child has to declare
+a suitable 'ranges' translation entry for the MBus to allocate a window
+for it. This entry will encode the target and attribute in a 'windowid'
+ad-hoc cell, like this:
+
+       soc {
+               bootrom {
+                       ranges = <0 0x01e00000 0xfff00000 0x100000>;
+               };
+       };
+
+In this case, the MBus driver will allocate a window with target ID = 0x01,
+attribute ID = 0xe0, base address 0xfff00000, and size 0x100000.
+The windowid cell encodes the target and attribute in the upper bytes.
+
+Note that, the above 'ranges' property is creating a fictitious 2-cell address
+space with the windowid and the base address. We need to get rid of this
+2-cell address space, translating it into a real address space consisting of
+just the base address.
+
+The obvious way is to add a translation at the mbus-node level, like this:
+
+       soc {
+               ranges = <0x01e00000 0xfff00000 0xfff00000 0x100000>;
+               bootrom {
+                       ranges = <0 0x01e00000 0xfff00000 0x100000>;
+               };
+       };
+
+This would lead to a lot of 'ranges' properties duplication because
+when you need to declare a new translation for a given device, you have
+to do it at the per-board DTS file. But since 'ranges' entries are not
+inherited from included dtsi files, you would have to go through each of the
+included dtsi files, making sure you duplicate each of the previous entries.
+
+So to avoid this duplication, the MBus driver is capable of adding the
+required translation entry to the device tree programatically. In other
+words, when writing the device tree you don't need to extend the mbus-node
+translation entry list; the driver will do it dynamically.
+
+So for instance, if you want to add support for a NOR device, that sits
+behind the Device Bus controller, you would have this device tree:
+(Note that the windowid cell is encoding the target ID = 0x01 and attribute
+ID = 0x2f, and the selected base address for the window is 0xe8000000).
+
+       soc {
+               devbus-bootcs {
+                       status = "okay";
+                       ranges = <0 0x012f0000 0xe8000000 0x8000000>;
+
+                       /* Device Bus parameters are required but not shown */
+                       /* ... */
+
+                       /* NOR 128 MiB */
+                       nor@0 {
+                               compatible = "cfi-flash";
+                               reg = <0 0x8000000>;
+                               bank-width = <2>;
+                       };
+               };
+       };
+
+** 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.
+
+** About the special target ID and attribute ID
+
+As stated above, for each 'ranges' translation entry in mbus-node first-level
+children, the MBus driver will allocate a decoding window.
+However, in some cases it's desirable to declare a translation entry but have
+the MBus driver skip it.
+
+This is currently the case for the internal-regs and the pcie-controller
+nodes.
+
+In the former, a dummy translation entry is needed to map the regular device
+nodes into the {windowid / base address} address space, using a zeroed windowid
+(target = 0x0, attribute = 0x0). The MBus driver will skip entries with such
+windowid.
+
+       soc {
+               /* ... */
+               internal-regs {
+                       compatible = "simple-bus";
+                       ranges = <0 0 0 0x100000>;  /* Dummy translation */
+                       serial@12000 {
+                               reg = <0x12000 0x100>;
+                               /* ... */
+                       };
+               };
+       };
+
+In the pcie-controller case, we have two kinds of entries we need to skip.
+The first one is identical to the internal-regs case; in fact it's declaring a
+translation into the internal-regs address space.
+
+The second one is identified by a target ID = 0xff and attribute ID = 0xff.
+We use this to create the mapping into the large region where the PCIe
+controller will request decoding window allocations from the MBus driver.
+
+       soc {
+               pcie-controller {
+                       ranges =
+                              <0x82000000 0 0x40000    0          0x40000    0 
0x00002000
+                               0x82000000 0 0x80000    0          0x80000    0 
0x00002000
+                               0x82000000 0 0xe0000000 0xffff0000 0xe0000000 0 
0x08000000
+                               0x81000000 0 0          0xffff0000 0xe8000000 0 
0x00100000>;
+
+                       pcie@1,0 {
+                               /* ... */
+                       };
+               };
+       };
+
+We don't want the MBus driver to statically allocate windows for this region,
+because the PCIe controller itself will make such requests depending on what
+kind of PCIe devices are detected.
diff --git a/drivers/bus/mvebu-mbus.c b/drivers/bus/mvebu-mbus.c
index 23f6ae6..ac4115a 100644
--- a/drivers/bus/mvebu-mbus.c
+++ b/drivers/bus/mvebu-mbus.c
@@ -885,6 +885,110 @@ 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 24 to 31: window target ID
+ *  - bits 16 to 23: window attribute ID
+ *  - bits  0 to 15: unused
+ */
+#define TARGET(id) (((id) & 0xFF000000) >> 24)
+#define ATTR(id)   (((id) & 0x00FF0000) >> 16)
+
+static int __init mbus_dt_setup_win(struct mvebu_mbus_state *mbus,
+                                   u32 windowid, u32 base, u32 size,
+                                   u8 target, u8 attr)
+{
+       const struct mvebu_mbus_mapping *map = mbus->soc->map;
+       const char *name;
+       int i;
+
+       /* Special case for the identity mapping */
+       if (target == 0xff && attr == 0xff)
+               return 0;
+
+       /* Special case for the internal registers */
+       if (target == 0x00 && attr == 0x00)
+               return 0;
+
+       /* 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_dt_setup(struct mvebu_mbus_state *mbus,
+                               struct device_node *np)
+{
+       struct device_node *child;
+       int ranges_len, tuple_len;
+       int addr_cells, c_addr_cells, c_size_cells;
+       int cell_count;
+       const __be32 *r, *ranges_start, *ranges_end, *prop;
+
+       for_each_available_child_of_node(np, child) {
+
+               addr_cells = of_n_addr_cells(child);
+
+               prop = of_get_property(child, "#address-cells", NULL);
+               c_addr_cells = be32_to_cpup(prop);
+
+               prop = of_get_property(child, "#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(child, "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", child->name);
+                       continue;
+               }
+
+               for (r = ranges_start; r < ranges_end; r += cell_count) {
+                       u32 windowid, base, size;
+                       u8 target, attr;
+                       int ret;
+
+                       windowid = of_read_number(r + c_addr_cells, 1);
+                       base = of_read_number(r + c_addr_cells + 1, 1);
+                       size = of_read_number(r + addr_cells + c_addr_cells,
+                                             c_size_cells);
+                       target = TARGET(windowid);
+                       attr = ATTR(windowid);
+
+                       ret = mbus_dt_setup_win(mbus, windowid, base, size,
+                                               target, attr);
+                       if (ret < 0)
+                               return ret;
+               }
+       }
+       return 0;
+}
+
 int __init mvebu_mbus_dt_init(void)
 {
        struct resource mbuswins_res, sdramwins_res;
@@ -916,6 +1020,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

Reply via email to