fdtdec: Implement fdtdec_add_reserved_memory()
authorThierry Reding <treding@nvidia.com>
Thu, 21 Mar 2019 18:10:02 +0000 (19:10 +0100)
committerSimon Glass <sjg@chromium.org>
Fri, 12 Apr 2019 02:10:50 +0000 (20:10 -0600)
This function can be used to add subnodes in the /reserved-memory node.

Reviewed-by: Simon Glass <sjg@chromium.org>
Signed-off-by: Thierry Reding <treding@nvidia.com>
Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt [new file with mode: 0644]
include/fdtdec.h
lib/fdtdec.c

diff --git a/Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt b/Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt
new file mode 100644 (file)
index 0000000..bac4afa
--- /dev/null
@@ -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>;
+               /* ... */
+       };
+};
index ba7f873b89edf4315eaaca38c565895288d20bed..9bd6a70587a67ebb474e3003c919869a3115d604 100644 (file)
@@ -1031,6 +1031,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
  */
index 079a9b18aaac7cf4e82bb5812e85db9f89af9bef..d247141f0b39ff9274b27fcc6cc3484ed2f6927f 100644 (file)
@@ -1268,6 +1268,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)