2018-08-24 08:31:08 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
/*
|
|
|
|
* Copyright (c) 2014 The Linux Foundation
|
|
|
|
*/
|
2020-10-20 08:41:07 +00:00
|
|
|
#include <linux/dma-map-ops.h>
|
2018-08-24 08:31:08 +00:00
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/vmalloc.h>
|
|
|
|
|
2019-06-03 07:14:31 +00:00
|
|
|
struct page **dma_common_find_pages(void *cpu_addr)
|
|
|
|
{
|
|
|
|
struct vm_struct *area = find_vm_area(cpu_addr);
|
|
|
|
|
2024-08-10 00:59:11 +00:00
|
|
|
if (!area || !(area->flags & VM_DMA_COHERENT))
|
2019-06-03 07:14:31 +00:00
|
|
|
return NULL;
|
2024-08-10 00:59:11 +00:00
|
|
|
WARN(area->flags != VM_DMA_COHERENT,
|
|
|
|
"unexpected flags in area: %p\n", cpu_addr);
|
2019-06-03 07:14:31 +00:00
|
|
|
return area->pages;
|
|
|
|
}
|
|
|
|
|
2018-08-24 08:31:08 +00:00
|
|
|
/*
|
|
|
|
* Remaps an array of PAGE_SIZE pages into another vm_area.
|
|
|
|
* Cannot be used in non-sleeping contexts
|
|
|
|
*/
|
|
|
|
void *dma_common_pages_remap(struct page **pages, size_t size,
|
2019-08-30 06:51:01 +00:00
|
|
|
pgprot_t prot, const void *caller)
|
2018-08-24 08:31:08 +00:00
|
|
|
{
|
2020-06-02 04:50:32 +00:00
|
|
|
void *vaddr;
|
2018-08-24 08:31:08 +00:00
|
|
|
|
2020-06-23 12:07:55 +00:00
|
|
|
vaddr = vmap(pages, PAGE_ALIGN(size) >> PAGE_SHIFT,
|
|
|
|
VM_DMA_COHERENT, prot);
|
2020-06-02 04:50:32 +00:00
|
|
|
if (vaddr)
|
|
|
|
find_vm_area(vaddr)->pages = pages;
|
|
|
|
return vaddr;
|
2018-08-24 08:31:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Remaps an allocated contiguous region into another vm_area.
|
|
|
|
* Cannot be used in non-sleeping contexts
|
|
|
|
*/
|
|
|
|
void *dma_common_contiguous_remap(struct page *page, size_t size,
|
|
|
|
pgprot_t prot, const void *caller)
|
|
|
|
{
|
2020-06-23 12:07:55 +00:00
|
|
|
int count = PAGE_ALIGN(size) >> PAGE_SHIFT;
|
2018-08-24 08:31:08 +00:00
|
|
|
struct page **pages;
|
2020-06-02 04:50:32 +00:00
|
|
|
void *vaddr;
|
|
|
|
int i;
|
2018-08-24 08:31:08 +00:00
|
|
|
|
2023-06-06 12:47:37 +00:00
|
|
|
pages = kvmalloc_array(count, sizeof(struct page *), GFP_KERNEL);
|
2018-08-24 08:31:08 +00:00
|
|
|
if (!pages)
|
|
|
|
return NULL;
|
2020-06-02 04:50:32 +00:00
|
|
|
for (i = 0; i < count; i++)
|
2018-08-24 08:31:08 +00:00
|
|
|
pages[i] = nth_page(page, i);
|
2020-06-02 04:50:32 +00:00
|
|
|
vaddr = vmap(pages, count, VM_DMA_COHERENT, prot);
|
2023-06-06 12:47:37 +00:00
|
|
|
kvfree(pages);
|
2018-08-24 08:31:08 +00:00
|
|
|
|
2020-06-02 04:50:32 +00:00
|
|
|
return vaddr;
|
2018-08-24 08:31:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Unmaps a range previously mapped by dma_common_*_remap
|
|
|
|
*/
|
2019-08-30 06:51:01 +00:00
|
|
|
void dma_common_free_remap(void *cpu_addr, size_t size)
|
2018-08-24 08:31:08 +00:00
|
|
|
{
|
2019-10-05 08:23:30 +00:00
|
|
|
struct vm_struct *area = find_vm_area(cpu_addr);
|
2018-08-24 08:31:08 +00:00
|
|
|
|
2024-08-10 00:59:11 +00:00
|
|
|
if (!area || !(area->flags & VM_DMA_COHERENT)) {
|
2018-08-24 08:31:08 +00:00
|
|
|
WARN(1, "trying to free invalid coherent area: %p\n", cpu_addr);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
vunmap(cpu_addr);
|
|
|
|
}
|