How to work with reserved CMA memory?

cosinus0 picture cosinus0 · Feb 22, 2016 · Viewed 11.6k times · Source

I would like to allocate piece of physically contiguous reserved memory (in predefined physical addresses) for my device with DMA support. As I see CMA has three options: 1. To reserve memory via kernel config file. 2. To reserve memory via kernel cmdline. 3. To reserve memory via device-tree memory node. In the first case: size and number of areas could be reserved.

CONFIG_DMA_CMA=y
CONFIG_CMA_AREAS=7
CONFIG_CMA_SIZE_MBYTES=8

So I could use:

start_cma_virt = dma_alloc_coherent(dev->cmadev, (size_t)size_cma, &start_cma_dma, GFP_KERNEL);

in my driver to allocate contiguous memory. I could use it max 7 times and it will be possible allocate up to 8M. But unfortunately

dma_contiguous_reserve(min(arm_dma_limit, arm_lowmem_limit));

from arch/arm/mm/init.c:

void __init arm_memblock_init(struct meminfo *mi,const struct machine_desc *mdesc)

it is impossible to set predefined physical addresses for contiguous allocation. Of Course I could use kernel cmdline:

mem=cma=cmadevlabel=8M@32M cma_map=mydevname=cmadevlabel
//struct device *dev = cmadev->dev; /*dev->name is mydevname*/

After that dma_alloc_coherent() should alloc memory in physical memory area from 32M + 8M (0x2000000 + 0x800000) up to 0x27FFFFF. But unfortunately I have problem with this solution. Maybe my cmdline has error? Next one try was device tree implementation.

cmadev_region: mycma {
    /*no-map;*/ /*DMA coherent memory*/
    /*reusable;*/
    reg = <0x02000000 0x00100000>;      
};

And phandle in some node:

memory-region = <&cmadev_region>;

As I saw in kernel usual it should be used like:

of_find_node_by_name(); //find needed node
of_parse_phandle(); //resolve a phandle property to a device_node pointer
of_get_address(); //get DT __be32 physical addresses
of_translate_address(); //DT represent local (bus, device) addresses so translate it to CPU physical addresses 
request_mem_region(); //reserve IOMAP memory (cat /proc/iomem)
ioremap(); //alloc entry in page table for reserved memory and return kernel logical addresses.

But I want use DMA via (as I know only one external API function dma_alloc_coherent) dma_alloc_coherent() instead IO-MAP ioremap(). But how call

start_cma_virt = dma_alloc_coherent(dev->cmadev, (size_t)size_cma, &start_cma_dma, GFP_KERNEL);

associate memory from device-tree (reg = <0x02000000 0x00100000>;) to dev->cmadev ? In case with cmdline it is clear it has device name and addresses region. Does reserved memory after call of_parse_phandle() automatically should be booked for your special driver (which parse DT). And next call dma_alloc_coherent will allocate dma area inside memory from cmadev_region: mycma?

Answer

Ashish Mhetre picture Ashish Mhetre · Mar 12, 2019

To use dma_alloc_coherent() on reserved memory node, you need to declare that area as dma_coherent. You can do some thing like:

In dt:

cmadev_region: mycma {
    compatible = "compatible-name"
    no-map;
    reg = <0x02000000 0x00100000>;      
};

In your driver:

struct device *cma_dev;

static int rmem_dma_device_init(struct reserved_mem *rmem, struct device *dev)
{
    int ret;

    if (!mem) {
        ret = dma_declare_coherent_memory(cma_dev, rmem->base, rmem->base,
                           rmem->size, DMA_MEMORY_EXCLUSIVE);
        if (ret) {
            pr_err("Error");
            return ret;
        }
    }
    return 0;
}

static void rmem_dma_device_release(struct reserved_mem *rmem,
                struct device *dev)
{
    if (dev)
        dev->dma_mem = NULL;
}

static const struct reserved_mem_ops rmem_dma_ops = {
    .device_init    = rmem_dma_device_init,
    .device_release = rmem_dma_device_release,
};

int __init cma_setup(struct reserved_mem *rmem)
{
    rmem->ops = &rmem_dma_ops;
    return 0;
}
RESERVEDMEM_OF_DECLARE(some-name, "compatible-name", cma_setup);

Now on this cma_dev you can perform dma_alloc_coherent and get memory.