[PATCH v3 2/2] drivers: dma-contiguous: add initialization from device tree

Grant Likely grant.likely at secretlab.ca
Thu Jul 11 10:56:41 EDT 2013


Hi Marek,

Thanks for working on this. Comments below...

On Wed, Jun 26, 2013 at 2:40 PM, Marek Szyprowski
<m.szyprowski at samsung.com> wrote:
> Add device tree support for contiguous memory regions defined in device
> tree. Initialization is done in 2 steps. First, the contiguous memory is
> reserved, what happens very early when only flattened device tree is
> available. Then on device initialization the corresponding cma regions are
> assigned to each device structure.
>
> Signed-off-by: Marek Szyprowski <m.szyprowski at samsung.com>
> Acked-by: Kyungmin Park <kyungmin.park at samsung.com>
> ---
>  .../devicetree/bindings/contiguous-memory.txt      |   94 ++++++++++++++
>  arch/arm/boot/dts/skeleton.dtsi                    |    7 +-
>  drivers/base/dma-contiguous.c                      |  132 ++++++++++++++++++++
>  3 files changed, 232 insertions(+), 1 deletion(-)
>  create mode 100644 Documentation/devicetree/bindings/contiguous-memory.txt
>
> diff --git a/Documentation/devicetree/bindings/contiguous-memory.txt b/Documentation/devicetree/bindings/contiguous-memory.txt
> new file mode 100644
> index 0000000..a733df2
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/contiguous-memory.txt
> @@ -0,0 +1,94 @@
> +*** Contiguous Memory binding ***
> +
> +The /chosen/contiguous-memory node provides runtime configuration of
> +contiguous memory regions for Linux kernel. Such regions can be created
> +for special usage by various device drivers. A good example are
> +contiguous memory allocations or memory sharing with other operating
> +system(s) on the same hardware board. Those special memory regions might
> +depend on the selected board configuration and devices used on the target
> +system.
> +
> +Parameters for each memory region can be encoded into the device tree
> +with the following convention:
> +
> +contiguous-memory {
> +
> +       (name): region@(base-address) {
> +               reg = <(baseaddr) (size)>;
> +               (linux,default-contiguous-region);
> +               device = <&device_0 &device_1 ...>
> +       };
> +};

Okay, I've gone and read all of the backlog on the 3 versions of the
patch series, and I think I understand the issues. I actually think it
was better off to have the regions specified as children of the memory
node. I understand the argument about how would firmware know what
size the kernel wants and that it would be better to have a kernel
parameter to override the default. However, it is also reasonable for
the kernel to be provided with a default amount of CMA based on the
usage profile of the device. In that regard it is absolutely
appropriate to put the CMA region data into the memory node. I don't
think /chosen is the right place for that.

> +
> +name:          an name given to the defined region;

In the above node example, "name:" is a label used for creating
phandles. That information doesn't appear in the resulting .dtb
output. The label is actually optional It should instead be:
        [(label):] (name)@(address) { }

> +base-address:  the base address of the defined region;
> +size:          the size of the memory region (bytes);

The reg property should follow the normal reg rules which are well
defined. That also means that a memory region could have multiple reg
entries if appropriate.

> +linux,default-contiguous-region: property indicating that the region
> +               is the default region for all contiguous memory
> +               allocations, Linux specific (optional);
> +device:                array of phandles to the client device nodes, which
> +               will use the defined contiguous region.

This is backwards compared to the way device references usually work.
It would be more consistent for each device node to have a
"dma-memory-region" property with phandles to one or more memory
regions that it cares about.

> +Each defined region must use unique name. It is optional to specify the
> +base address, so if one wants to use autoconfiguration of the base
> +address, he must specify the '0' as base address in the 'reg' property
> +and assign ann uniqe name to such regions, following the convention:
> +'region at 0', 'region at 1', 'region at 2', ...

Drop the use of 'region'. "name at 0" is more typical. It would be
appropriate to have compatible = "reserved-memory-region" in each of
the reserved regions. That would avoid the problem of two regions
based at the same address having a conflict in name.

> +
> +
> +*** Example ***
> +
> +This example defines a memory configuration containing 2 contiguous
> +regions for Linux kernel, one default of all device drivers (named
> +contig_mem, autoconfigured at boot time, 64MiB) and one dedicated to the
> +framebuffer device (named display_mem, placed at 0x78000000, 64MiB). The
> +display_mem region is then assigned to fb at 12300000, scaller at 12500000 and
> +codec at 12600000 devices for contiguous memory allocation with Linux
> +kernel drivers.
> +
> +The reason for creating a separate region for framebuffer device is to
> +match the framebuffer base address to the one configured by bootloader,
> +so once Linux kernel drivers starts no glitches on the displayed boot
> +logo appears. Scaller and codec drivers should share the memory
> +allocations with framebuffer driver.
> +
> +/ {
> +       /* ... */
> +
> +       chosen {
> +               bootargs = /* ... */
> +
> +               contiguous-memory {
> +
> +                       /*
> +                        * global autoconfigured region
> +                        * for contiguous allocations
> +                        */
> +                       contig_mem: region at 0 {
> +                               reg = <0x0 0x4000000>;
> +                               linux,default-contiguous-region;
> +                       };
> +
> +                       /*
> +                        * special region for framebuffer and
> +                        * multimedia processing devices
> +                        */
> +                       display_mem: region at 78000000 {
> +                               reg = <0x78000000 0x4000000>;
> +                               device = <&fb0 &scaller &codec>;
> +                       };
> +               };
> +       };
> +
> +       /* ... */
> +
> +       fb0: fb at 12300000 {
> +               status = "okay";
> +       };
> +       scaller: scaller at 12500000 {
> +               status = "okay";
> +       };
> +       codec: codec at 12600000 {
> +               status = "okay";
> +       };
> +};
> diff --git a/arch/arm/boot/dts/skeleton.dtsi b/arch/arm/boot/dts/skeleton.dtsi
> index b41d241..cadc3b9 100644
> --- a/arch/arm/boot/dts/skeleton.dtsi
> +++ b/arch/arm/boot/dts/skeleton.dtsi
> @@ -7,7 +7,12 @@
>  / {
>         #address-cells = <1>;
>         #size-cells = <1>;
> -       chosen { };
> +       chosen {
> +               contiguous-memory {
> +                       #address-cells = <1>;
> +                       #size-cells = <1>;
> +               };
> +       };
>         aliases { };
>         memory { device_type = "memory"; reg = <0 0>; };
>  };
> diff --git a/drivers/base/dma-contiguous.c b/drivers/base/dma-contiguous.c
> index 01fe743..ce5f5d1 100644
> --- a/drivers/base/dma-contiguous.c
> +++ b/drivers/base/dma-contiguous.c
> @@ -24,6 +24,9 @@
>
>  #include <linux/memblock.h>
>  #include <linux/err.h>
> +#include <linux/of.h>
> +#include <linux/of_fdt.h>
> +#include <linux/of_platform.h>
>  #include <linux/mm.h>
>  #include <linux/mutex.h>
>  #include <linux/page-isolation.h>
> @@ -37,6 +40,7 @@ struct cma {
>         unsigned long   base_pfn;
>         unsigned long   count;
>         unsigned long   *bitmap;
> +       char            full_name[32];
>  };
>
>  static DEFINE_MUTEX(cma_mutex);
> @@ -133,6 +137,52 @@ static __init int cma_activate_area(struct cma *cma)
>         return 0;
>  }
>
> +/*****************************************************************************/
> +
> +#ifdef CONFIG_OF
> +int __init cma_fdt_scan(unsigned long node, const char *uname,
> +                               int depth, void *data)
> +{
> +       static int level;
> +       phys_addr_t base, size;
> +       unsigned long len;
> +       struct cma *cma;
> +       __be32 *prop;
> +       int ret;
> +
> +       if (depth == 1 && strcmp(uname, "chosen") == 0) {
> +               level = depth;
> +               return 0;
> +       }
> +
> +       if (depth == 2 && strcmp(uname, "contiguous-memory") == 0) {
> +               level = depth;
> +               return 0;
> +       }
> +
> +       if (level != 2 || depth != 3 || strncmp(uname, "region@", 7) != 0)
> +               return 0;
> +
> +       prop = of_get_flat_dt_prop(node, "reg", &len);
> +       if (!prop || (len != 2 * sizeof(unsigned long)))
> +               return 0;
> +
> +       base = be32_to_cpu(prop[0]);
> +       size = be32_to_cpu(prop[1]);
> +
> +       pr_info("Found %s, memory base %lx, size %ld MiB\n", uname,
> +               (unsigned long)base, (unsigned long)size / SZ_1M);
> +
> +       ret = dma_contiguous_reserve_area(size, base, 0, &cma);
> +       if (ret == 0) {
> +               strcpy(cma->full_name, uname);
> +               if (of_get_flat_dt_prop(node, "linux,default-contiguous-region", NULL))
> +                       dma_contiguous_default_area = cma;
> +       }
> +       return 0;
> +}
> +#endif
> +
>  /**
>   * dma_contiguous_reserve() - reserve area(s) for contiguous memory handling
>   * @limit: End address of the reserved memory (optional, 0 for any).
> @@ -149,6 +199,10 @@ void __init dma_contiguous_reserve(phys_addr_t limit)
>
>         pr_debug("%s(limit %08lx)\n", __func__, (unsigned long)limit);
>
> +#ifdef CONFIG_OF
> +       of_scan_flat_dt(cma_fdt_scan, NULL);
> +#endif
> +
>         if (size_cmdline != -1) {
>                 sel_size = size_cmdline;
>         } else {
> @@ -265,6 +319,80 @@ int __init dma_contiguous_add_device(struct device *dev, struct cma *cma)
>         return 0;
>  }
>
> +#ifdef CONFIG_OF
> +
> +#define MAX_CMA_MAPS   64
> +
> +static struct cma_map {
> +       struct cma *cma;
> +       struct device_node *node;
> +} cma_maps[MAX_CMA_MAPS];
> +static unsigned cma_map_count;
> +
> +static void cma_assign_device_from_dt(struct device *dev)
> +{
> +       int i;
> +       for (i = 0; i < cma_map_count; i++) {
> +               if (cma_maps[i].node == dev->of_node) {
> +                       dev_set_cma_area(dev, cma_maps[i].cma);
> +                       pr_info("Assigned CMA %s to %s device\n",
> +                               cma_maps[i].cma->full_name,
> +                               dev_name(dev));
> +               }
> +       }
> +}
> +
> +static int cma_device_init_notifier_call(struct notifier_block *nb,
> +                                        unsigned long event, void *data)
> +{
> +       struct device *dev = data;
> +       if (event == BUS_NOTIFY_ADD_DEVICE && dev->of_node)
> +               cma_assign_device_from_dt(dev);
> +       return NOTIFY_DONE;
> +}
> +
> +static struct notifier_block cma_dev_init_nb = {
> +       .notifier_call = cma_device_init_notifier_call,
> +};
> +
> +void scan_cma_nodes(void)
> +{
> +       struct device_node *parent = of_find_node_by_path("/chosen/contiguous-memory");
> +       struct device_node *child;
> +
> +       if (!parent)
> +               return;
> +
> +       for_each_child_of_node(parent, child) {
> +               struct cma *cma = NULL;
> +               int i;
> +
> +               for (i = 0; i < cma_area_count; i++) {
> +                       char *p = strrchr(child->full_name, '/') + 1;
> +                       if (strcmp(p, cma_areas[i].full_name) == 0)
> +                               cma = &cma_areas[i];
> +               }
> +               if (!cma)
> +                       continue;
> +
> +               for (i = 0;; i++) {
> +                       struct device_node *node;
> +                       node = of_parse_phandle(child, "device", i);
> +                       if (!node)
> +                               break;
> +
> +                       if (cma_map_count < MAX_CMA_MAPS) {
> +                               cma_maps[cma_map_count].cma = cma;
> +                               cma_maps[cma_map_count].node = node;
> +                               cma_map_count++;
> +                       } else {
> +                               pr_err("CMA error: too many devices defined\n");
> +                       }
> +               }
> +       }
> +}
> +#endif
> +
>  static int __init cma_init_reserved_areas(void)
>  {
>         int i;
> @@ -275,6 +403,10 @@ static int __init cma_init_reserved_areas(void)
>                         return ret;
>         }
>
> +#ifdef CONFIG_OF
> +       scan_cma_nodes();
> +       bus_register_notifier(&platform_bus_type, &cma_dev_init_nb);
> +#endif
>         return 0;
>  }
>  core_initcall(cma_init_reserved_areas);
> --
> 1.7.9.5
>



More information about the linux-arm-kernel mailing list