fdtdec: Implement fdtdec_add_reserved_memory()
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>
This commit is contained in:
parent
8153d53b93
commit
c9222a08b3
@ -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>;
|
||||||
|
/* ... */
|
||||||
|
};
|
||||||
|
};
|
@ -1031,6 +1031,54 @@ int fdtdec_setup_memory_banksize(void);
|
|||||||
*/
|
*/
|
||||||
int fdtdec_set_phandle(void *blob, int node, uint32_t phandle);
|
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
|
* Set up the device tree ready for use
|
||||||
*/
|
*/
|
||||||
|
131
lib/fdtdec.c
131
lib/fdtdec.c
@ -1268,6 +1268,137 @@ int fdtdec_set_phandle(void *blob, int node, uint32_t phandle)
|
|||||||
return fdt_setprop(blob, node, "phandle", &value, sizeof(value));
|
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)
|
int fdtdec_setup(void)
|
||||||
{
|
{
|
||||||
#if CONFIG_IS_ENABLED(OF_CONTROL)
|
#if CONFIG_IS_ENABLED(OF_CONTROL)
|
||||||
|
Loading…
Reference in New Issue
Block a user