From: Thierry Reding <[email protected]>

This function can be used to add subnodes in the /reserved-memory node.

Reviewed-by: Simon Glass <[email protected]>
Signed-off-by: Thierry Reding <[email protected]>
---
Changes in v3:
- use fdt_generate_phandle() instead of fdtdec_generate_phandle()
- add device tree bindings for /reserved-memory
- add examples to code comments

Changes in v2:
- split fdt_{addr,size}_unpack() helpers into separate patch
- use name@x,y notation only if the upper cell is > 0
- use debug() instead of printf() to save code size
- properly compute number of cells in reg property
- fix carveout size computations, was off by one
- use #size-cells where appropriate

 .../reserved-memory/reserved-memory.txt       | 136 ++++++++++++++++++
 include/fdtdec.h                              |  48 +++++++
 lib/fdtdec.c                                  | 131 +++++++++++++++++
 3 files changed, 315 insertions(+)
 create mode 100644 
Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt

diff --git 
a/Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt 
b/Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt
new file mode 100644
index 000000000000..bac4afa3b197
--- /dev/null
+++ b/Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt
@@ -0,0 +1,136 @@
+*** Reserved memory regions ***
+
+Reserved memory is specified as a node under the /reserved-memory node.
+The operating system shall exclude reserved memory from normal usage
+one can create child nodes describing particular reserved (excluded from
+normal use) memory regions. Such memory regions are usually designed for
+the special usage by various device drivers.
+
+Parameters for each memory region can be encoded into the device tree
+with the following nodes:
+
+/reserved-memory node
+---------------------
+#address-cells, #size-cells (required) - standard definition
+    - Should use the same values as the root node
+ranges (required) - standard definition
+    - Should be empty
+
+/reserved-memory/ child nodes
+-----------------------------
+Each child of the reserved-memory node specifies one or more regions of
+reserved memory. Each child node may either use a 'reg' property to
+specify a specific range of reserved memory, or a 'size' property with
+optional constraints to request a dynamically allocated block of memory.
+
+Following the generic-names recommended practice, node names should
+reflect the purpose of the node (ie. "framebuffer" or "dma-pool"). Unit
+address (@<address>) should be appended to the name if the node is a
+static allocation.
+
+Properties:
+Requires either a) or b) below.
+a) static allocation
+   reg (required) - standard definition
+b) dynamic allocation
+   size (required) - length based on parent's #size-cells
+                   - Size in bytes of memory to reserve.
+   alignment (optional) - length based on parent's #size-cells
+                        - Address boundary for alignment of allocation.
+   alloc-ranges (optional) - prop-encoded-array (address, length pairs).
+                           - Specifies regions of memory that are
+                             acceptable to allocate from.
+
+If both reg and size are present, then the reg property takes precedence
+and size is ignored.
+
+Additional properties:
+compatible (optional) - standard definition
+    - may contain the following strings:
+        - shared-dma-pool: This indicates a region of memory meant to be
+          used as a shared pool of DMA buffers for a set of devices. It can
+          be used by an operating system to instantiate the necessary pool
+          management subsystem if necessary.
+        - vendor specific string in the form <vendor>,[<device>-]<usage>
+no-map (optional) - empty property
+    - Indicates the operating system must not create a virtual mapping
+      of the region as part of its standard mapping of system memory,
+      nor permit speculative access to it under any circumstances other
+      than under the control of the device driver using the region.
+reusable (optional) - empty property
+    - The operating system can use the memory in this region with the
+      limitation that the device driver(s) owning the region need to be
+      able to reclaim it back. Typically that means that the operating
+      system can use that region to store volatile or cached data that
+      can be otherwise regenerated or migrated elsewhere.
+
+Linux implementation note:
+- If a "linux,cma-default" property is present, then Linux will use the
+  region for the default pool of the contiguous memory allocator.
+
+- If a "linux,dma-default" property is present, then Linux will use the
+  region for the default pool of the consistent DMA allocator.
+
+Device node references to reserved memory
+-----------------------------------------
+Regions in the /reserved-memory node may be referenced by other device
+nodes by adding a memory-region property to the device node.
+
+memory-region (optional) - phandle, specifier pairs to children of 
/reserved-memory
+
+Example
+-------
+This example defines 3 contiguous regions are defined for Linux kernel:
+one default of all device drivers (named linux,cma@72000000 and 64MiB in size),
+one dedicated to the framebuffer device (named framebuffer@78000000, 8MiB), and
+one for multimedia processing (named multimedia-memory@77000000, 64MiB).
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       memory {
+               reg = <0x40000000 0x40000000>;
+       };
+
+       reserved-memory {
+               #address-cells = <1>;
+               #size-cells = <1>;
+               ranges;
+
+               /* global autoconfigured region for contiguous allocations */
+               linux,cma {
+                       compatible = "shared-dma-pool";
+                       reusable;
+                       size = <0x4000000>;
+                       alignment = <0x2000>;
+                       linux,cma-default;
+               };
+
+               display_reserved: framebuffer@78000000 {
+                       reg = <0x78000000 0x800000>;
+               };
+
+               multimedia_reserved: multimedia@77000000 {
+                       compatible = "acme,multimedia-memory";
+                       reg = <0x77000000 0x4000000>;
+               };
+       };
+
+       /* ... */
+
+       fb0: video@12300000 {
+               memory-region = <&display_reserved>;
+               /* ... */
+       };
+
+       scaler: scaler@12500000 {
+               memory-region = <&multimedia_reserved>;
+               /* ... */
+       };
+
+       codec: codec@12600000 {
+               memory-region = <&multimedia_reserved>;
+               /* ... */
+       };
+};
diff --git a/include/fdtdec.h b/include/fdtdec.h
index 55600026c488..b54ed38fb362 100644
--- a/include/fdtdec.h
+++ b/include/fdtdec.h
@@ -992,6 +992,54 @@ int fdtdec_setup_memory_banksize(void);
  */
 int fdtdec_set_phandle(void *blob, int node, uint32_t phandle);
 
+/**
+ * fdtdec_add_reserved_memory() - add or find a reserved-memory node
+ *
+ * If a reserved-memory node already exists for the given carveout, a phandle
+ * for that node will be returned. Otherwise a new node will be created and a
+ * phandle corresponding to it will be returned.
+ *
+ * See Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt
+ * for details on how to use reserved memory regions.
+ *
+ * As an example, consider the following code snippet:
+ *
+ *     struct fdt_memory fb = {
+ *         .start = 0x92cb3000,
+ *         .end = 0x934b2fff,
+ *     };
+ *     uint32_t phandle;
+ *
+ *     fdtdec_add_reserved_memory(fdt, "framebuffer", &fb, &phandle);
+ *
+ * This results in the following subnode being added to the top-level
+ * /reserved-memory node:
+ *
+ *     reserved-memory {
+ *         #address-cells = <0x00000002>;
+ *         #size-cells = <0x00000002>;
+ *         ranges;
+ *
+ *         framebuffer@92cb3000 {
+ *             reg = <0x00000000 0x92cb3000 0x00000000 0x00800000>;
+ *             phandle = <0x0000004d>;
+ *         };
+ *     };
+ *
+ * If the top-level /reserved-memory node does not exist, it will be created.
+ * The phandle returned from the function call can be used to reference this
+ * reserved memory region from other nodes.
+ *
+ * @param blob         FDT blob
+ * @param basename     base name of the node to create
+ * @param carveout     information about the carveout region
+ * @param phandlep     return location for the phandle of the carveout region
+ * @return 0 on success or a negative error code on failure
+ */
+int fdtdec_add_reserved_memory(void *blob, const char *basename,
+                              const struct fdt_memory *carveout,
+                              uint32_t *phandlep);
+
 /**
  * Set up the device tree ready for use
  */
diff --git a/lib/fdtdec.c b/lib/fdtdec.c
index 00db90e3cdfd..be54ad5bd092 100644
--- a/lib/fdtdec.c
+++ b/lib/fdtdec.c
@@ -1250,6 +1250,137 @@ int fdtdec_set_phandle(void *blob, int node, uint32_t 
phandle)
        return fdt_setprop(blob, node, "phandle", &value, sizeof(value));
 }
 
+static int fdtdec_init_reserved_memory(void *blob)
+{
+       int na, ns, node, err;
+       fdt32_t value;
+
+       /* inherit #address-cells and #size-cells from the root node */
+       na = fdt_address_cells(blob, 0);
+       ns = fdt_size_cells(blob, 0);
+
+       node = fdt_add_subnode(blob, 0, "reserved-memory");
+       if (node < 0)
+               return node;
+
+       err = fdt_setprop(blob, node, "ranges", NULL, 0);
+       if (err < 0)
+               return err;
+
+       value = cpu_to_fdt32(ns);
+
+       err = fdt_setprop(blob, node, "#size-cells", &value, sizeof(value));
+       if (err < 0)
+               return err;
+
+       value = cpu_to_fdt32(na);
+
+       err = fdt_setprop(blob, node, "#address-cells", &value, sizeof(value));
+       if (err < 0)
+               return err;
+
+       return node;
+}
+
+int fdtdec_add_reserved_memory(void *blob, const char *basename,
+                              const struct fdt_memory *carveout,
+                              uint32_t *phandlep)
+{
+       fdt32_t cells[4] = {}, *ptr = cells;
+       uint32_t upper, lower, phandle;
+       int parent, node, na, ns, err;
+       char name[64];
+
+       /* create an empty /reserved-memory node if one doesn't exist */
+       parent = fdt_path_offset(blob, "/reserved-memory");
+       if (parent < 0) {
+               parent = fdtdec_init_reserved_memory(blob);
+               if (parent < 0)
+                       return parent;
+       }
+
+       /* only 1 or 2 #address-cells and #size-cells are supported */
+       na = fdt_address_cells(blob, parent);
+       if (na < 1 || na > 2)
+               return -FDT_ERR_BADNCELLS;
+
+       ns = fdt_size_cells(blob, parent);
+       if (ns < 1 || ns > 2)
+               return -FDT_ERR_BADNCELLS;
+
+       /* find a matching node and return the phandle to that */
+       fdt_for_each_subnode(node, blob, parent) {
+               const char *name = fdt_get_name(blob, node, NULL);
+               phys_addr_t addr, size;
+
+               addr = fdtdec_get_addr_size(blob, node, "reg", &size);
+               if (addr == FDT_ADDR_T_NONE) {
+                       debug("failed to read address/size for %s\n", name);
+                       continue;
+               }
+
+               if (addr == carveout->start && (addr + size) == carveout->end) {
+                       *phandlep = fdt_get_phandle(blob, node);
+                       return 0;
+               }
+       }
+
+       /*
+        * Unpack the start address and generate the name of the new node
+        * base on the basename and the unit-address.
+        */
+       lower = fdt_addr_unpack(carveout->start, &upper);
+
+       if (na > 1 && upper > 0)
+               snprintf(name, sizeof(name), "%s@%x,%x", basename, upper,
+                        lower);
+       else {
+               if (upper > 0) {
+                       debug("address %08x:%08x exceeds addressable space\n",
+                             upper, lower);
+                       return -FDT_ERR_BADVALUE;
+               }
+
+               snprintf(name, sizeof(name), "%s@%x", basename, lower);
+       }
+
+       node = fdt_add_subnode(blob, parent, name);
+       if (node < 0)
+               return node;
+
+       err = fdt_generate_phandle(blob, &phandle);
+       if (err < 0)
+               return err;
+
+       err = fdtdec_set_phandle(blob, node, phandle);
+       if (err < 0)
+               return err;
+
+       /* store one or two address cells */
+       if (na > 1)
+               *ptr++ = cpu_to_fdt32(upper);
+
+       *ptr++ = cpu_to_fdt32(lower);
+
+       /* store one or two size cells */
+       lower = fdt_size_unpack(carveout->end - carveout->start + 1, &upper);
+
+       if (ns > 1)
+               *ptr++ = cpu_to_fdt32(upper);
+
+       *ptr++ = cpu_to_fdt32(lower);
+
+       err = fdt_setprop(blob, node, "reg", cells, (na + ns) * sizeof(*cells));
+       if (err < 0)
+               return err;
+
+       /* return the phandle for the new node for the caller to use */
+       if (phandlep)
+               *phandlep = phandle;
+
+       return 0;
+}
+
 int fdtdec_setup(void)
 {
 #if CONFIG_IS_ENABLED(OF_CONTROL)
-- 
2.21.0

_______________________________________________
U-Boot mailing list
[email protected]
https://lists.denx.de/listinfo/u-boot

Reply via email to