1327 lines
35 KiB
C
Executable File
1327 lines
35 KiB
C
Executable File
/**
|
|
NVT memory operation handling
|
|
To handle the flush allocate and other memory handling api
|
|
@file fmem.c
|
|
@ingroup
|
|
@note
|
|
Copyright Novatek Microelectronics Corp. 2019. All rights reserved.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License version 2 as
|
|
published by the Free Software Foundation.
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/init.h>
|
|
#include <linux/module.h>
|
|
#include <linux/memblock.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/sort.h>
|
|
#include <linux/mmzone.h>
|
|
#include <linux/memory.h>
|
|
#include <linux/kallsyms.h>
|
|
#include <linux/nodemask.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/dma-mapping.h>
|
|
#include <linux/dma-contiguous.h>
|
|
#include <linux/cma.h>
|
|
#include <linux/mutex.h>
|
|
#include <asm/setup.h>
|
|
#include <asm/pgtable.h>
|
|
#include <asm/pgalloc.h>
|
|
#include <asm/tlbflush.h>
|
|
#include <asm/glue-cache.h>
|
|
#include <asm/outercache.h>
|
|
#include <asm/mach/map.h>
|
|
#include <asm/mmu.h>
|
|
#include <linux/proc_fs.h>
|
|
#include <linux/seq_file.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/of_reserved_mem.h>
|
|
#include <linux/of.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/cpumask.h>
|
|
#include <linux/smp.h>
|
|
#include <mach/fmem.h>
|
|
#include "../../arch/arm/mm/dma.h"
|
|
#include "../../arch/arm/mm/mm.h"
|
|
|
|
#define DEBUG_WRONG_CACHE_API 0x0
|
|
#define DEBUG_WRONG_CACHE_VA 0x1
|
|
#define DEBUG_WRONG_CACHE_LEN 0x2
|
|
|
|
#define CACHE_ALIGN_MASK L1_CACHE_BYTES //cache line with 64 bytes: arch/${CPU}/include/asm/cache.h
|
|
#define MAX_CMD_LENGTH 30
|
|
#define MAX_CACHE_TRY_LOCK 10
|
|
|
|
/*
|
|
* Local variables declaration
|
|
*/
|
|
static DEFINE_SEMAPHORE(sema_cma);
|
|
static DEFINE_SEMAPHORE(sema_cma_mem);
|
|
static DEFINE_SEMAPHORE(sema_mem_lock);
|
|
#ifdef CONFIG_CMA
|
|
struct list_head nvt_cma_info_list_root;
|
|
#endif
|
|
|
|
u32 nonlinear_cpuaddr_flush = 0;
|
|
u32 cpuaddr_flush = 0;
|
|
u32 va_not_64align = 0, length_not_64align = 0;
|
|
u32 debug_counter = 0;
|
|
static u32 vb_cache_flush_en = 0;
|
|
unsigned int total_num_cpu = NR_CPUS;
|
|
|
|
struct cma {
|
|
unsigned long base_pfn;
|
|
unsigned long count;
|
|
unsigned long *bitmap;
|
|
unsigned int order_per_bit; /* Order of pages represented by one bit */
|
|
struct mutex lock;
|
|
#ifdef CONFIG_CMA_DEBUGFS
|
|
struct hlist_head mem_head;
|
|
spinlock_t mem_head_lock;
|
|
#endif
|
|
};
|
|
|
|
unsigned long fmem_dcache_sync_op(void *vaddr, u32 len, enum dma_data_direction dir, unsigned int num_cpu_involve, unsigned int use_native_api);
|
|
|
|
#if (defined(CONFIG_NVT_CACHE_FLUSH_ALL_FOR_HDAL) || \
|
|
defined(CONFIG_NVT_CACHE_INVALIDATE_ALL_FOR_HDAL))
|
|
static DEFINE_RAW_SPINLOCK(l1_flush_all_lock);
|
|
|
|
#define CACHE_THREAD_OP_THRESHOLD 0x400000
|
|
#ifdef CONFIG_CACHE_L2X0
|
|
extern unsigned int l2c310_get_l2_size(void);
|
|
#define CACHE_FLUSH_SIZE (l2c310_get_l2_size() << CONFIG_NVT_CACHE_FLUSH_ALL_THRESHOLD)
|
|
#define CACHE_INV_SIZE (l2c310_get_l2_size() << CONFIG_NVT_CACHE_INVALIDATE_ALL_THRESHOLD)
|
|
#else
|
|
#define CACHE_FLUSH_SIZE (0x20000)
|
|
#define CACHE_INV_SIZE (0x20000)
|
|
#endif
|
|
|
|
#ifdef CONFIG_OUTER_CACHE
|
|
extern unsigned long nvt_l2c210_clean_all(void);
|
|
extern unsigned long nvt_l2c210_flush_all(void);
|
|
#endif
|
|
|
|
#if defined(CONFIG_CPU_V7)
|
|
extern void v7_flush_d_cache_all_if(void);
|
|
#endif
|
|
|
|
unsigned long nvt_cache_op(void *vaddr, phys_addr_t paddr, u32 len, enum dma_data_direction dir, unsigned int max_retry, unsigned int num_cpu_involve)
|
|
{
|
|
unsigned long ret = 0;
|
|
unsigned long flags;
|
|
|
|
#ifdef CONFIG_OUTER_CACHE
|
|
int try_lock;
|
|
#endif
|
|
|
|
//L1 cache operation
|
|
if (vaddr != NULL) {
|
|
raw_spin_lock_irqsave(&l1_flush_all_lock, flags);
|
|
|
|
switch (dir) {
|
|
case DMA_BIDIRECTIONAL:
|
|
#ifdef CONFIG_NVT_CACHE_FLUSH_ALL_FOR_HDAL
|
|
if (num_cpu_involve == 1) {
|
|
flush_cache_all();
|
|
} else
|
|
#endif
|
|
dmac_flush_range(vaddr, vaddr + len);
|
|
break;
|
|
|
|
case DMA_TO_DEVICE:
|
|
#ifdef CONFIG_NVT_CACHE_FLUSH_ALL_FOR_HDAL
|
|
if (num_cpu_involve == 1) {
|
|
#if defined(CONFIG_CPU_V7)
|
|
v7_flush_d_cache_all_if();
|
|
#else
|
|
flush_cache_all();
|
|
#endif
|
|
} else
|
|
#endif
|
|
dmac_map_area(vaddr, len, DMA_TO_DEVICE);
|
|
break;
|
|
|
|
case DMA_FROM_DEVICE:
|
|
#ifdef CONFIG_NVT_CACHE_INVALIDATE_ALL_FOR_HDAL
|
|
if (num_cpu_involve == 1) {
|
|
flush_cache_all();
|
|
} else
|
|
#endif
|
|
dmac_map_area(vaddr, len, DMA_FROM_DEVICE);
|
|
break;
|
|
|
|
default:
|
|
pr_err("fmem:unknown data dir:%d\n", dir);
|
|
raw_spin_unlock_irqrestore(&l1_flush_all_lock, flags);
|
|
return (unsigned long)-1;
|
|
}
|
|
raw_spin_unlock_irqrestore(&l1_flush_all_lock, flags);
|
|
}
|
|
|
|
#ifdef CONFIG_CACHE_L2X0
|
|
//Do L2 cache operation if L2X0 present.
|
|
try_lock = 0;
|
|
ret = (unsigned long)-1;
|
|
while (try_lock < max_retry) {
|
|
switch (dir) {
|
|
|
|
case DMA_BIDIRECTIONAL:
|
|
#ifdef CONFIG_NVT_CACHE_FLUSH_ALL_FOR_HDAL
|
|
ret = nvt_l2c210_flush_all();
|
|
#else
|
|
ret = (unsigned long) - 1;
|
|
try_lock = max_retry;
|
|
#endif
|
|
break;
|
|
|
|
case DMA_TO_DEVICE:
|
|
#ifdef CONFIG_NVT_CACHE_FLUSH_ALL_FOR_HDAL
|
|
ret = nvt_l2c210_clean_all();
|
|
#else
|
|
ret = (unsigned long) - 1;
|
|
try_lock = max_retry;
|
|
#endif
|
|
break;
|
|
|
|
case DMA_FROM_DEVICE:
|
|
#ifdef CONFIG_NVT_CACHE_INVALIDATE_ALL_FOR_HDAL
|
|
ret = nvt_l2c210_flush_all();
|
|
#else
|
|
ret = (unsigned long) - 1;
|
|
try_lock = max_retry;
|
|
#endif
|
|
break;
|
|
|
|
default:
|
|
pr_err("fmem:unknown data dir:%d\n", dir);
|
|
return (unsigned long)-1;
|
|
}
|
|
|
|
if (ret != (unsigned long) -1) {
|
|
break;
|
|
}
|
|
|
|
try_lock++;
|
|
if (try_lock < max_retry) {
|
|
udelay(200);
|
|
}
|
|
}
|
|
#else
|
|
ret = (unsigned long)-1;
|
|
#endif
|
|
|
|
#if CONFIG_OUTER_CACHE
|
|
/* Execute native cache operation when last cache operation failed */
|
|
if (ret == (unsigned long) -1) {
|
|
if (paddr != 0) {
|
|
ret = 0;
|
|
|
|
switch (dir) {
|
|
case DMA_BIDIRECTIONAL:
|
|
outer_flush_range(paddr, paddr + len);
|
|
break;
|
|
|
|
case DMA_TO_DEVICE:
|
|
outer_clean_range(paddr, paddr + len);
|
|
break;
|
|
|
|
case DMA_FROM_DEVICE:
|
|
outer_inv_range(paddr, paddr + len);
|
|
break;
|
|
|
|
default:
|
|
pr_err("fmem:unknown data dir:%d\n", dir);
|
|
ret = (unsigned long)-1;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
unsigned long fmem_dcache_sync_by_cpu(void *vaddr, u32 len, enum dma_data_direction dir, unsigned int cache_op_cpu_id)
|
|
{
|
|
unsigned int cpu_id;
|
|
unsigned long result;
|
|
|
|
if (cache_op_cpu_id >= total_num_cpu) {
|
|
pr_err("%s: Invalidate cpu id:%d\n", __func__, cache_op_cpu_id);
|
|
return (unsigned long)-1;
|
|
}
|
|
|
|
if (in_irq() || irqs_disabled()) {
|
|
result = fmem_dcache_sync_op(vaddr, len, dir, NR_CPUS, 1);
|
|
} else {
|
|
|
|
cpu_id = get_cpu();
|
|
|
|
if (cpu_id == cache_op_cpu_id) {
|
|
result = fmem_dcache_sync_op(vaddr, len, dir, 1, 0);
|
|
} else {
|
|
pr_err("%s:cpu_id mismatch(cpu_id:%d cache_op_cpu_id:%d), use default API to do cache operation\n", __func__, cpu_id, cache_op_cpu_id);
|
|
result = fmem_dcache_sync_op(vaddr, len, dir, total_num_cpu, 0);
|
|
}
|
|
|
|
put_cpu();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
#else
|
|
unsigned long fmem_dcache_sync_by_cpu(void *vaddr, u32 len, enum dma_data_direction dir, unsigned int cache_op_cpu_id)
|
|
{
|
|
return fmem_dcache_sync_op(vaddr, len, dir, total_num_cpu, 0);
|
|
}
|
|
#endif
|
|
|
|
/* proc function
|
|
*/
|
|
static struct proc_dir_entry *fmem_proc_root = NULL;
|
|
|
|
/*************************************************************************************************************
|
|
* cma handling
|
|
*************************************************************************************************************/
|
|
|
|
#ifdef CONFIG_CMA
|
|
static struct nvt_cma_info_t* fmem_get_cma_info(unsigned int id)
|
|
{
|
|
struct nvt_cma_info_t *curr_info, *next_info;
|
|
|
|
down(&sema_cma);
|
|
list_for_each_entry_safe(curr_info, next_info, &nvt_cma_info_list_root, list) {
|
|
if (curr_info->id == id) {
|
|
up(&sema_cma);
|
|
return curr_info;
|
|
}
|
|
}
|
|
up(&sema_cma);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static struct nvt_cma_info_t* fmem_cma_info_alloc(struct platform_device *pdev, unsigned int id)
|
|
{
|
|
struct nvt_cma_info_t* cma_info = NULL;
|
|
|
|
if (id > MAX_CMA_AREAS) {
|
|
dev_err(&pdev->dev, "fmem: Your cma area %u is larger than %d\n", id, MAX_CMA_AREAS);
|
|
return NULL;
|
|
}
|
|
|
|
cma_info = fmem_get_cma_info(id);
|
|
if (cma_info != NULL) {
|
|
dev_err(&pdev->dev, "fmem: This cma area %u had already added\n", id);
|
|
return NULL;
|
|
}
|
|
|
|
cma_info = (struct nvt_cma_info_t*)kzalloc(sizeof(struct nvt_cma_info_t), GFP_KERNEL);
|
|
if (cma_info == NULL) {
|
|
dev_err(&pdev->dev, "fmem: Your cma area %u can't be allocated\n", id);
|
|
return NULL;
|
|
}
|
|
INIT_LIST_HEAD(&cma_info->list);
|
|
INIT_LIST_HEAD(&cma_info->fmem_list_root);
|
|
cma_info->fmem_mem_cache = kmem_cache_create("fmem_mem_cache", sizeof(struct nvt_fmem_mem_cma_info_t), 0, SLAB_PANIC, NULL);
|
|
if (!cma_info->fmem_mem_cache) {
|
|
pr_err("%s: Allocating memory failed\n", __func__);
|
|
return NULL;
|
|
}
|
|
cma_info->id = id;
|
|
|
|
return cma_info;
|
|
}
|
|
|
|
static struct nvt_fmem_mem_cma_info_t* fmem_get_cmamem_info(struct nvt_fmem_mem_cma_info_t* my_fmem_mem_info, unsigned int id)
|
|
{
|
|
struct nvt_fmem_mem_cma_info_t *curr_info, *next_info;
|
|
struct nvt_cma_info_t* cma_info = NULL;
|
|
|
|
if (id > MAX_CMA_AREAS) {
|
|
pr_err("fmem/%s: Your cma area %u is larger than %d\n", __func__, id, MAX_CMA_AREAS);
|
|
return NULL;
|
|
}
|
|
|
|
cma_info = fmem_get_cma_info(id);
|
|
if (cma_info == NULL) {
|
|
pr_err("fmem/%s: This cma area %u can't be used\n", __func__, id);
|
|
return NULL;
|
|
}
|
|
|
|
down(&sema_cma_mem);
|
|
list_for_each_entry_safe(curr_info, next_info, &cma_info->fmem_list_root, list) {
|
|
if ((unsigned long)my_fmem_mem_info == (unsigned long)curr_info) {
|
|
up(&sema_cma_mem);
|
|
if (curr_info->magic_num != NVT_FMEM_MEM_MAGIC) {
|
|
panic("fmem/%s: Error magic number 0x%08lx\n", __func__, curr_info->magic_num);
|
|
}
|
|
return curr_info;
|
|
}
|
|
}
|
|
up(&sema_cma_mem);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static void* fmem_mem_info_alloc(struct nvt_fmem_mem_info_t *nvt_fmem_info, unsigned int id)
|
|
{
|
|
struct nvt_cma_info_t* cma_info = NULL;
|
|
struct nvt_fmem_mem_cma_info_t* my_fmem_meminfo = NULL;
|
|
|
|
if (id > MAX_CMA_AREAS) {
|
|
pr_err("fmem/%s: Your cma area %u is larger than %d\n", __func__, id, MAX_CMA_AREAS);
|
|
return NULL;
|
|
}
|
|
|
|
cma_info = fmem_get_cma_info(id);
|
|
if (cma_info == NULL) {
|
|
pr_err("fmem/%s: This cma area %u can't be used\n", __func__, id);
|
|
return NULL;
|
|
}
|
|
|
|
/* Allocate a buffer to record memory info */
|
|
my_fmem_meminfo = (struct nvt_fmem_mem_cma_info_t *)kmem_cache_zalloc(cma_info->fmem_mem_cache, GFP_KERNEL);
|
|
if (!my_fmem_meminfo) {
|
|
return NULL;
|
|
}
|
|
|
|
/* Copy memory info to managed data struct */
|
|
memcpy(&my_fmem_meminfo->mem_info, nvt_fmem_info, sizeof(struct nvt_fmem_mem_info_t));
|
|
INIT_LIST_HEAD(&my_fmem_meminfo->list);
|
|
/* Add to list for the managed use */
|
|
down(&sema_cma_mem);
|
|
list_add_tail(&my_fmem_meminfo->list, &cma_info->fmem_list_root);
|
|
up(&sema_cma_mem);
|
|
my_fmem_meminfo->magic_num = NVT_FMEM_MEM_MAGIC;
|
|
|
|
return (void*)my_fmem_meminfo;
|
|
}
|
|
|
|
static int fmem_mem_info_release(struct nvt_fmem_mem_cma_info_t *my_fmem_meminfo, unsigned int id)
|
|
{
|
|
struct nvt_cma_info_t* cma_info = NULL;
|
|
|
|
if (id > MAX_CMA_AREAS) {
|
|
pr_err("fmem/%s: Your cma area %u is larger than %d\n", __func__, id, MAX_CMA_AREAS);
|
|
return -1;
|
|
}
|
|
|
|
cma_info = fmem_get_cma_info(id);
|
|
if (cma_info == NULL) {
|
|
pr_err("fmem/%s: This cma area %u can't be used\n", __func__, id);
|
|
return -1;
|
|
}
|
|
|
|
if (my_fmem_meminfo->magic_num != NVT_FMEM_MEM_MAGIC) {
|
|
pr_err("fmem/%s: Error magic number with %p id: %d\n", __func__, my_fmem_meminfo, id);
|
|
return -1;
|
|
}
|
|
|
|
/* Copy memory info to managed data struct */
|
|
memset(&my_fmem_meminfo->mem_info, 0, sizeof(struct nvt_fmem_mem_info_t));
|
|
/* Delete list from fmem_list_root */
|
|
down(&sema_cma_mem);
|
|
list_del(&my_fmem_meminfo->list);
|
|
up(&sema_cma_mem);
|
|
/* Release a buffer to record memory info */
|
|
kmem_cache_free(cma_info->fmem_mem_cache, my_fmem_meminfo);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int fmem_cma_info_free(struct platform_device *pdev, unsigned int id)
|
|
{
|
|
struct nvt_cma_info_t *curr_info, *next_info;
|
|
|
|
if (id > MAX_CMA_AREAS) {
|
|
dev_err(&pdev->dev, "fmem: Your cma area %u is larger than %d\n", id, MAX_CMA_AREAS);
|
|
return -1;
|
|
}
|
|
|
|
down(&sema_cma);
|
|
list_for_each_entry_safe(curr_info, next_info, &nvt_cma_info_list_root, list) {
|
|
if (curr_info->id == id) {
|
|
list_del(&curr_info->list);
|
|
memset(curr_info, 0, sizeof(struct nvt_cma_info_t));
|
|
kfree(curr_info);
|
|
up(&sema_cma);
|
|
return 0;
|
|
}
|
|
}
|
|
up(&sema_cma);
|
|
|
|
dev_err(&pdev->dev, "fmem: We can't free this id%u \n", id);
|
|
return -1;
|
|
}
|
|
|
|
static int fmem_add_cma_info(struct platform_device *pdev, struct nvt_cma_info_t* cma_info)
|
|
{
|
|
if (cma_info == NULL) {
|
|
dev_err(&pdev->dev, "fmem: We can't add this cma_info because this is NULL\n");
|
|
return -1;
|
|
}
|
|
|
|
down(&sema_cma);
|
|
list_add_tail(&cma_info->list, &nvt_cma_info_list_root);
|
|
cma_info->name = pdev->name;
|
|
cma_info->dev = &pdev->dev;
|
|
up(&sema_cma);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int nvt_cma_probe(struct platform_device *pdev)
|
|
{
|
|
struct nvt_cma_info_t *cma_info = NULL;
|
|
int ret = 0;
|
|
unsigned int id = 0;
|
|
|
|
/* pointer to shared-dma-pool in dts */
|
|
ret = of_reserved_mem_device_init(&pdev->dev);
|
|
if (ret < 0) {
|
|
dev_err(&pdev->dev, "memory reserved init failed with %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
of_property_read_u32(pdev->dev.of_node, "id",
|
|
&id);
|
|
|
|
cma_info = fmem_cma_info_alloc(pdev, id);
|
|
if (cma_info == NULL)
|
|
return -1;
|
|
|
|
cma_info->cma = dev_get_cma_area(&pdev->dev);
|
|
if (!cma_info->cma) {
|
|
dev_err(&pdev->dev, "fmem: cma id%u probe failed\n", id);
|
|
return -1;
|
|
} else {
|
|
cma_info->start = cma_get_base(cma_info->cma);
|
|
cma_info->size = cma_get_size(cma_info->cma);
|
|
}
|
|
|
|
ret = fmem_add_cma_info(pdev, cma_info);
|
|
if (ret < 0) {
|
|
fmem_cma_info_free(pdev, id);
|
|
return -1;
|
|
}
|
|
|
|
dev_info(&pdev->dev, "fmem: cma area id: %u, cma base:0x%x, size:0x%x added\n", id, cma_info->start, (u32)cma_info->size);
|
|
dev_info(&pdev->dev, "fmem/Version: %s\n", NVT_FMEM_VERSION);
|
|
dev_info(&pdev->dev, "Probe successfully\n");
|
|
return 0;
|
|
}
|
|
|
|
static const struct of_device_id nvt_cma_dt_match[] = {
|
|
{.compatible = "nvt,nvt_cma"},
|
|
{},
|
|
};
|
|
MODULE_DEVICE_TABLE(of, nvt_cma_dt_match);
|
|
|
|
static struct platform_driver nvt_cma_driver = {
|
|
.probe = nvt_cma_probe,
|
|
.driver = {
|
|
.name = "nvt_cma",
|
|
.owner = THIS_MODULE,
|
|
.of_match_table = of_match_ptr(nvt_cma_dt_match),
|
|
},
|
|
};
|
|
|
|
static int __init nvt_cma_early_init(void)
|
|
{
|
|
int ret = 0;
|
|
|
|
INIT_LIST_HEAD(&nvt_cma_info_list_root);
|
|
ret = platform_driver_register(&nvt_cma_driver);
|
|
if (ret < 0) {
|
|
pr_err("%s: init failed\n", __func__);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
core_initcall(nvt_cma_early_init);
|
|
#endif
|
|
|
|
void* fmem_alloc_from_cma(struct nvt_fmem_mem_info_t *nvt_fmem_info, unsigned int index)
|
|
{
|
|
#ifdef CONFIG_CMA
|
|
struct page *pages;
|
|
void *vaddr = NULL;
|
|
void *handle = NULL;
|
|
unsigned page_count = ALIGN(nvt_fmem_info->size, PAGE_SIZE)/PAGE_SIZE;
|
|
struct nvt_cma_info_t *cma_info = NULL;
|
|
struct device * dev = NULL;
|
|
|
|
|
|
down(&sema_mem_lock);
|
|
/* To get cma info by id */
|
|
cma_info = fmem_get_cma_info(index);
|
|
if (cma_info == NULL) {
|
|
pr_err("fmem/%s: This cma area %u can't be used\n", __func__, index);
|
|
goto alloc_cma_exit;
|
|
}
|
|
dev = cma_info->dev;
|
|
|
|
if (!dev)
|
|
goto alloc_cma_exit;
|
|
|
|
if (page_count == 0)
|
|
goto alloc_cma_exit;
|
|
|
|
if (nvt_fmem_info->type == NVT_FMEM_ALLOC_CACHE) {
|
|
pages = dma_alloc_from_contiguous(dev, page_count, 0, __GFP_NOWARN);
|
|
if (pages != NULL) {
|
|
nvt_fmem_info->paddr = __pfn_to_phys(page_to_pfn(pages));
|
|
vaddr = __va(nvt_fmem_info->paddr);
|
|
}
|
|
} else if (nvt_fmem_info->type == NVT_FMEM_ALLOC_NONCACHE) {
|
|
vaddr = dma_alloc_coherent(dev, nvt_fmem_info->size, &nvt_fmem_info->paddr, GFP_KERNEL);
|
|
page_count = 0;
|
|
pages = NULL;
|
|
} else {
|
|
vaddr = dma_alloc_writecombine(dev, nvt_fmem_info->size, &nvt_fmem_info->paddr, GFP_KERNEL);
|
|
page_count = 0;
|
|
pages = NULL;
|
|
}
|
|
|
|
if (vaddr == NULL) {
|
|
pr_err("%s:%d Can't to create this buffer with CMA area %d\n", __func__, __LINE__, index);
|
|
goto alloc_cma_exit;
|
|
}
|
|
|
|
nvt_fmem_info->vaddr = vaddr;
|
|
nvt_fmem_info->page_count = page_count;
|
|
nvt_fmem_info->pages = pages;
|
|
nvt_fmem_info->size = ALIGN(nvt_fmem_info->size, PAGE_SIZE);
|
|
|
|
handle = fmem_mem_info_alloc(nvt_fmem_info, index);
|
|
if (handle == NULL) {
|
|
if (nvt_fmem_info->type == NVT_FMEM_ALLOC_CACHE) {
|
|
dma_release_from_contiguous(dev, pages, page_count);
|
|
} else if (nvt_fmem_info->type == NVT_FMEM_ALLOC_NONCACHE) {
|
|
dma_free_coherent(dev, nvt_fmem_info->size, vaddr, nvt_fmem_info->paddr);
|
|
} else {
|
|
dma_free_writecombine(dev, nvt_fmem_info->size, vaddr, nvt_fmem_info->paddr);
|
|
}
|
|
goto alloc_cma_exit;
|
|
}
|
|
|
|
up(&sema_mem_lock);
|
|
return handle;
|
|
|
|
alloc_cma_exit:
|
|
up(&sema_mem_lock);
|
|
return NULL;
|
|
#else
|
|
return NULL;
|
|
#endif
|
|
}
|
|
|
|
int fmem_release_from_cma(void *handle, unsigned int index)
|
|
{
|
|
#ifdef CONFIG_CMA
|
|
struct nvt_fmem_mem_cma_info_t* my_fmem_meminfo = NULL;
|
|
int ret = 0;
|
|
struct nvt_cma_info_t *cma_info = NULL;
|
|
struct device * dev = NULL;
|
|
|
|
down(&sema_mem_lock);
|
|
/* To get cma info by id */
|
|
cma_info = fmem_get_cma_info(index);
|
|
if (cma_info == NULL) {
|
|
pr_err("fmem/%s: This cma area %u can't be used\n", __func__, index);
|
|
goto release_cma_exit;
|
|
}
|
|
dev = cma_info->dev;
|
|
|
|
if (!dev)
|
|
goto release_cma_exit;
|
|
|
|
my_fmem_meminfo = fmem_get_cmamem_info((struct nvt_fmem_mem_cma_info_t*)handle, index);
|
|
if (my_fmem_meminfo == NULL) {
|
|
pr_err("fmem/%s: Error to release buffer with this handle %p\n", __func__, handle);
|
|
goto release_cma_exit;
|
|
}
|
|
|
|
if (my_fmem_meminfo->mem_info.type == NVT_FMEM_ALLOC_CACHE) {
|
|
dma_release_from_contiguous(dev, my_fmem_meminfo->mem_info.pages, my_fmem_meminfo->mem_info.page_count);
|
|
} else if (my_fmem_meminfo->mem_info.type == NVT_FMEM_ALLOC_NONCACHE) {
|
|
dma_free_coherent(dev, my_fmem_meminfo->mem_info.size, my_fmem_meminfo->mem_info.vaddr, my_fmem_meminfo->mem_info.paddr);
|
|
} else {
|
|
dma_free_writecombine(dev, my_fmem_meminfo->mem_info.size, my_fmem_meminfo->mem_info.vaddr, my_fmem_meminfo->mem_info.paddr);
|
|
}
|
|
|
|
ret = fmem_mem_info_release(my_fmem_meminfo, index);
|
|
if (ret < 0) {
|
|
pr_err("fmem/%s: We can't release this cma memory info\n", __func__);
|
|
goto release_cma_exit;
|
|
}
|
|
|
|
up(&sema_mem_lock);
|
|
return 0;
|
|
|
|
release_cma_exit:
|
|
up(&sema_mem_lock);
|
|
return -1;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
/*************************************************************************************************************
|
|
* fmem proc handling
|
|
*************************************************************************************************************/
|
|
|
|
/* counter info
|
|
*/
|
|
static int proc_show_dbgcounter(struct seq_file *sfile, void *v)
|
|
{
|
|
seq_printf(sfile, "\n");
|
|
|
|
seq_printf(sfile, "L1 Cache line size: %d Bytes\n", CACHE_ALIGN_MASK);
|
|
seq_printf(sfile, "linear cpu_addr flush: %d \n", cpuaddr_flush);
|
|
seq_printf(sfile, "non-linear cpu_addr flush: %d \n", nonlinear_cpuaddr_flush);
|
|
seq_printf(sfile, "vaddr is not cache alignment: %d \n", va_not_64align);
|
|
seq_printf(sfile, "len is not cache alignment: %d \n", length_not_64align);
|
|
seq_printf(sfile, "debug_counter = 0x%x \n\n", debug_counter);
|
|
|
|
seq_printf(sfile, "DEBUG_WRONG_CACHE_API = 0x%x \n", DEBUG_WRONG_CACHE_API);
|
|
seq_printf(sfile, "DEBUG_WRONG_CACHE_VA = 0x%x \n", DEBUG_WRONG_CACHE_VA);
|
|
seq_printf(sfile, "DEBUG_WRONG_CACHE_LEN = 0x%x \n\n", DEBUG_WRONG_CACHE_LEN);
|
|
|
|
seq_printf(sfile, "\rUsage:\n");
|
|
seq_printf(sfile, "\r\tEnable: CACHE_API/CACHE_VA/CACHE_LEN \
|
|
\t=> echo 7 > /proc/fmem/dbg_info\n");
|
|
seq_printf(sfile, "\r\tEnable: CACHE_API/CACHE_VA \
|
|
\t\t=> echo 3 > /proc/fmem/dbg_info\n");
|
|
seq_printf(sfile, "\r\tEnable: CACHE_VA/CACHE_LEN \
|
|
\t\t=> echo 6 > /proc/fmem/dbg_info\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* debug counter */
|
|
static ssize_t proc_write_dbgcounter(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
|
|
{
|
|
unsigned char value[30];
|
|
|
|
if (copy_from_user(value, buffer, count))
|
|
return 0;
|
|
|
|
sscanf(value, "%x\n", &debug_counter);
|
|
|
|
printk("debug counter: 0x%x \n", debug_counter);
|
|
|
|
return count;
|
|
}
|
|
|
|
static int dbgcounter_proc_open(struct inode *inode, struct file *file)
|
|
{
|
|
return single_open(file, proc_show_dbgcounter, NULL);
|
|
}
|
|
|
|
static const struct file_operations dbgcounter_proc_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = dbgcounter_proc_open,
|
|
.read = seq_read,
|
|
.llseek = seq_lseek,
|
|
.release = single_release,
|
|
.write = proc_write_dbgcounter,
|
|
};
|
|
|
|
static ssize_t proc_resolve_pa(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
|
|
{
|
|
unsigned char value[30];
|
|
unsigned int vaddr;
|
|
phys_addr_t paddr;
|
|
|
|
if (copy_from_user(value, buffer, count))
|
|
return 0;
|
|
|
|
sscanf(value, "%x\n", &vaddr);
|
|
paddr = fmem_lookup_pa(vaddr);
|
|
|
|
printk("Resolve vaddr: 0x%x ---> paddr: 0x%x \n", vaddr, paddr);
|
|
|
|
return count;
|
|
}
|
|
|
|
static int resolve_proc_show(struct seq_file *m, void *v)
|
|
{
|
|
return 0;
|
|
}
|
|
static int resolve_proc_open(struct inode *inode, struct file *file)
|
|
{
|
|
return single_open(file, resolve_proc_show, NULL);
|
|
}
|
|
|
|
static const struct file_operations resolve_proc_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = resolve_proc_open,
|
|
.read = seq_read,
|
|
.llseek = seq_lseek,
|
|
.release = single_release,
|
|
.write = proc_resolve_pa,
|
|
};
|
|
|
|
#ifdef CONFIG_CMA
|
|
struct nvtcma_proc_list_cmainfo_t {
|
|
struct seq_file *sfile;
|
|
phys_addr_t base;
|
|
size_t size;
|
|
};
|
|
|
|
static int nvtcma_list_all_cma_info(struct cma *cma, void*data)
|
|
{
|
|
struct nvtcma_proc_list_cmainfo_t *cmainfo = (struct nvtcma_proc_list_cmainfo_t *)data;
|
|
unsigned long used = 0;
|
|
u64 val = 0;
|
|
|
|
if (cma_get_base(cma) == cmainfo->base) {
|
|
mutex_lock(&cma->lock);
|
|
/* pages counter is smaller than sizeof(int) */
|
|
used = bitmap_weight(cma->bitmap, (int)cma->count);
|
|
mutex_unlock(&cma->lock);
|
|
val = (u64)used << cma->order_per_bit;
|
|
if (cmainfo->size != cma_get_size(cma))
|
|
seq_printf(cmainfo->sfile, "You used the dma api directly, not from nvt fmem api!!!\n");
|
|
seq_printf(cmainfo->sfile, "CMA managed >> Used/Total size: %lu(bytes)/%lu(bytes), %llu(pages)\n", (used * PAGE_SIZE), cma_get_size(cma), val);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int nvtcma_proc_show(struct seq_file *sfile, void *v)
|
|
{
|
|
struct nvt_cma_info_t *curr_info, *next_info;
|
|
struct nvt_fmem_mem_cma_info_t *curr_mem_info, *next_mem_info;
|
|
struct nvtcma_proc_list_cmainfo_t proc_cmainfo;
|
|
size_t total_used_size = 0;
|
|
|
|
seq_printf(sfile, "================= NVT CMA INFO =================\n");
|
|
down(&sema_cma);
|
|
list_for_each_entry_safe(curr_info, next_info, &nvt_cma_info_list_root, list) {
|
|
seq_printf(sfile, "\r\tArea:%d Name: %s Total size: %u\n", curr_info->id, curr_info->name, curr_info->size);
|
|
seq_printf(sfile, "\r\tphysical address: 0x%08x@0x%08x \n", curr_info->size, curr_info->start);
|
|
down(&sema_cma_mem);
|
|
list_for_each_entry_safe(curr_mem_info, next_mem_info, &curr_info->fmem_list_root, list) {
|
|
seq_printf(sfile, "\r\t\t Device_Name: %s, Type: %s, Physical addr:0x%08x, Virtual addr: 0x%08lx, size: %u\n",
|
|
(curr_mem_info->mem_info.dev != NULL)?curr_mem_info->mem_info.dev->init_name:"",
|
|
(curr_mem_info->mem_info.type == NVT_FMEM_ALLOC_CACHE)?"NVT_FMEM_ALLOC_CACHE":
|
|
(curr_mem_info->mem_info.type == NVT_FMEM_ALLOC_NONCACHE)?"NVT_FMEM_ALLOC_NONCACHE":
|
|
(curr_mem_info->mem_info.type == NVT_FMEM_ALLOC_BUFFER)?"NVT_FMEM_ALLOC_BUFFER":"ERROR Type",
|
|
curr_mem_info->mem_info.paddr,
|
|
(unsigned long)curr_mem_info->mem_info.vaddr,
|
|
curr_mem_info->mem_info.size);
|
|
total_used_size += curr_mem_info->mem_info.size;
|
|
}
|
|
proc_cmainfo.sfile = sfile;
|
|
proc_cmainfo.base = curr_info->start;
|
|
proc_cmainfo.size = curr_info->size;
|
|
cma_for_each_area(nvtcma_list_all_cma_info, (void*)&proc_cmainfo);
|
|
seq_printf(sfile, "Fmem managed >> Used/Total size: %u(bytes)/%u(bytes)\n\n", total_used_size, curr_info->size);
|
|
up(&sema_cma_mem);
|
|
}
|
|
up(&sema_cma);
|
|
|
|
return 0;
|
|
}
|
|
static int nvtcma_proc_open(struct inode *inode, struct file *file)
|
|
{
|
|
return single_open(file, nvtcma_proc_show, NULL);
|
|
}
|
|
|
|
static const struct file_operations nvtcma_proc_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = nvtcma_proc_open,
|
|
.read = seq_read,
|
|
.llseek = seq_lseek,
|
|
.release = single_release,
|
|
};
|
|
#endif
|
|
|
|
static int version_proc_show(struct seq_file *sfile, void *v)
|
|
{
|
|
seq_printf(sfile, "Version: %s\n", NVT_FMEM_VERSION);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int version_proc_open(struct inode *inode, struct file *file)
|
|
{
|
|
return single_open(file, version_proc_show, NULL);
|
|
}
|
|
|
|
static const struct file_operations version_proc_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = version_proc_open,
|
|
.read = seq_read,
|
|
.llseek = seq_lseek,
|
|
.release = single_release,
|
|
};
|
|
|
|
static int __init fmem_proc_init(void)
|
|
{
|
|
struct proc_dir_entry *p;
|
|
static struct proc_dir_entry *myproc = NULL;
|
|
|
|
p = proc_mkdir("fmem", NULL);
|
|
if (p == NULL) {
|
|
printk("%s, fail! \n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
fmem_proc_root = p;
|
|
|
|
/* resolve, va->pa
|
|
*/
|
|
myproc = proc_create("resolve", S_IRUGO, fmem_proc_root, &resolve_proc_fops);
|
|
if (myproc == NULL)
|
|
panic("FMEM: Fail to create proc resolve_proc!\n");
|
|
|
|
/* CMA info */
|
|
#ifdef CONFIG_CMA
|
|
myproc = proc_create("cma_info", S_IRUGO, fmem_proc_root, &nvtcma_proc_fops);
|
|
if (myproc == NULL)
|
|
panic("FMEM: Fail to create proc cma info!\n");
|
|
#endif
|
|
/* Version info */
|
|
myproc = proc_create("version", S_IRUGO, fmem_proc_root, &version_proc_fops);
|
|
if (myproc == NULL)
|
|
panic("FMEM: Fail to create proc version info!\n");
|
|
|
|
/* counter */
|
|
myproc = proc_create("dbg_info", S_IRUGO, fmem_proc_root, &dbgcounter_proc_fops);
|
|
if (myproc == NULL)
|
|
panic("FMEM: Fail to create proc dbg_info!\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int __init fmem_init(void)
|
|
{
|
|
int ret;
|
|
|
|
#ifdef CONFIG_HOTPLUG_CPU
|
|
total_num_cpu = num_present_cpus();
|
|
#else
|
|
total_num_cpu = num_online_cpus();
|
|
#endif
|
|
|
|
ret =fmem_proc_init();
|
|
|
|
return ret;
|
|
}
|
|
|
|
core_initcall(fmem_init);
|
|
|
|
/**
|
|
* @brief to resolve the virtual address (including direct mapping, ioremap or user space address to
|
|
* its real physical address.
|
|
*
|
|
* @parm vaddr indicates any virtual address
|
|
*
|
|
* @return >= 0 for success, 0xFFFFFFFF for fail
|
|
*/
|
|
phys_addr_t fmem_lookup_pa(unsigned int vaddr)
|
|
{
|
|
pgd_t *pgdp;
|
|
pmd_t *pmdp;
|
|
pud_t *pudp;
|
|
pte_t *ptep;
|
|
phys_addr_t paddr = 0xFFFFFFFF;
|
|
|
|
/* check if va is module space global variable */
|
|
if (vaddr >= TASK_SIZE && vaddr < PAGE_OFFSET) {
|
|
printk("Invalid va 0x%x , TASK_SIZE = 0x%x, PAGE_OFFSET = 0x%x\r\n", vaddr, (int)TASK_SIZE, (int)PAGE_OFFSET);
|
|
dump_stack();
|
|
return paddr;
|
|
}
|
|
/* for speed up */
|
|
if (virt_addr_valid(vaddr)) {
|
|
return (phys_addr_t)__pa(vaddr);
|
|
}
|
|
|
|
/*
|
|
* The pgd is never bad, and a pmd always exists(as it's folded into the pgd entry)
|
|
*/
|
|
if (vaddr >= TASK_SIZE) { /* use init_mm as mmu to look up in kernel mode */
|
|
pgdp = pgd_offset_k(vaddr);
|
|
} else if (current && current->mm){
|
|
pgdp = pgd_offset(current->mm, vaddr);
|
|
} else {
|
|
printk("Invalid va 0x%x\r\n", vaddr);
|
|
dump_stack();
|
|
return paddr;
|
|
}
|
|
|
|
if (unlikely(pgd_none(*pgdp))) {
|
|
printk("fmem: 0x%x not mapped in pgd table! \n", vaddr);
|
|
goto err;
|
|
}
|
|
|
|
/* because we don't have 3-level MMU, so pud = pgd. Here we are in order to meet generic Linux
|
|
* version, pud is listed here.
|
|
*/
|
|
pudp = pud_offset(pgdp, vaddr);
|
|
if (unlikely(pud_none(*pudp))) {
|
|
printk(KERN_INFO"fmem: 0x%x not mapped in pud table! \n", vaddr);
|
|
goto err;
|
|
}
|
|
|
|
pmdp = pmd_offset(pudp, vaddr);
|
|
if (unlikely(pmd_none(*pmdp))) {
|
|
printk(KERN_INFO"fmem: 0x%x not mapped in pmd 0x%x! \n", vaddr, (u32)pmdp);
|
|
goto err;
|
|
}
|
|
|
|
if (!pmd_bad(*pmdp)) {
|
|
//u32 v_addr;
|
|
|
|
/* page mapping */
|
|
ptep = pte_offset_kernel(pmdp, vaddr);
|
|
if (pte_none(*ptep)) {
|
|
pr_err("fmem: 0x%x not mapped in pte! \n", vaddr);
|
|
goto err;
|
|
}
|
|
paddr = (pte_pfn(*ptep) << PAGE_SHIFT) + (vaddr & (PAGE_SIZE - 1));
|
|
} else {
|
|
/* section mapping. The cases are:
|
|
* 1. DDR direct mapping
|
|
* 2. ioremap's vaddr, size and paddr all are 2M alignment.
|
|
*/
|
|
if (vaddr & SECTION_SIZE)
|
|
pmdp ++; /* move to pmd[1] */
|
|
paddr = (pmd_val(*pmdp) & SECTION_MASK) | (vaddr & ~SECTION_MASK);
|
|
}
|
|
err:
|
|
return paddr;
|
|
}
|
|
|
|
/**
|
|
* @brief to resolve the virtual address (including direct mapping, ioremap or user space address to
|
|
* its real physical address.
|
|
*
|
|
* @parm va indicates any virtual address
|
|
* @parm mm is the process mm
|
|
* @parm ret_pa is returned pa if succeeded
|
|
*
|
|
* @return >= 0 for success, -1 for failure
|
|
*/
|
|
int fmem_ptdump_v2p(unsigned long va, struct mm_struct *mm, phys_addr_t *ret_pa)
|
|
{
|
|
pgd_t *pgdp;
|
|
pmd_t *pmdp;
|
|
pud_t *pudp;
|
|
pte_t *ptep;
|
|
struct static_vm *p_static_vm;
|
|
|
|
/* for speed up */
|
|
if (virt_addr_valid(va)) {
|
|
*ret_pa = __pa(va);
|
|
return 0;
|
|
}
|
|
|
|
/* find static mapping address */
|
|
p_static_vm = find_static_vm_vaddr((void *)va);
|
|
if (NULL != p_static_vm) {
|
|
*ret_pa = (phys_addr_t)va - (phys_addr_t)p_static_vm->vm.addr + p_static_vm->vm.phys_addr;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* The pgd is never bad, and a pmd always exists(as it's folded into the pgd entry)
|
|
*/
|
|
if (va >= TASK_SIZE) {/* use init_mm as mmu to look up in kernel mode */
|
|
pgdp = pgd_offset_k(va);
|
|
} else {
|
|
pgdp = pgd_offset(mm, va);
|
|
}
|
|
|
|
if (unlikely(pgd_none(*pgdp))) {
|
|
goto err;
|
|
}
|
|
|
|
/* because we don't have 3-level MMU, so pud = pgd. Here we are in order to meet generic Linux
|
|
* version, pud is listed here.
|
|
*/
|
|
pudp = pud_offset(pgdp, va);
|
|
if (unlikely(pud_none(*pudp))) {
|
|
goto err;
|
|
}
|
|
|
|
pmdp = pmd_offset(pudp, va);
|
|
if (unlikely(pmd_none(*pmdp))) {
|
|
goto err;
|
|
}
|
|
|
|
if (!pmd_bad(*pmdp)) {
|
|
/* page mapping */
|
|
ptep = pte_offset_kernel(pmdp, va);
|
|
if (pte_none(*ptep)) {
|
|
goto err;
|
|
}
|
|
*ret_pa = (pte_pfn(*ptep) << PAGE_SHIFT) + (va & (PAGE_SIZE - 1));
|
|
return 0;
|
|
} else {
|
|
/* section mapping. The cases are:
|
|
* 1. DDR direct mapping
|
|
* 2. ioremap's vaddr, size and paddr all are 2M alignment.
|
|
*/
|
|
if (va & SECTION_SIZE) {
|
|
pmdp ++; /* move to pmd[1] */
|
|
}
|
|
*ret_pa = (pmd_val(*pmdp) & SECTION_MASK) | (va & ~SECTION_MASK);
|
|
return 0;
|
|
}
|
|
|
|
err:
|
|
*ret_pa = (phys_addr_t)-1;
|
|
return -1;
|
|
}
|
|
|
|
static void dev_release(struct device *dev)
|
|
{
|
|
return;
|
|
}
|
|
|
|
#define DRIVER_NAME "fmem_sync"
|
|
static struct platform_device pseudo_dev = {
|
|
.name = DRIVER_NAME,
|
|
.id = 0,
|
|
.num_resources = 0,
|
|
.dev = {
|
|
.coherent_dma_mask = DMA_BIT_MASK(32),
|
|
.release = dev_release,
|
|
}
|
|
};
|
|
|
|
/* @this function is a data cache operation function,
|
|
* @parm: vaddr: any virtual address
|
|
* @parm: dir will be:
|
|
* DMA_BIDIRECTIONAL = 0, it means flush operation.
|
|
* DMA_TO_DEVICE = 1, it means clean operation.
|
|
* DMA_FROM_DEVICE = 2, it means invalidate operation.
|
|
* DMA_NONE = 3,
|
|
*/
|
|
void fmem_dcache_sync(void *vaddr, u32 len, enum dma_data_direction dir)
|
|
{
|
|
fmem_dcache_sync_op(vaddr, len, dir, total_num_cpu, 0);
|
|
}
|
|
|
|
unsigned long fmem_dcache_sync_op(void *vaddr, u32 len, enum dma_data_direction dir, unsigned int num_cpu_involve, unsigned int use_native_api)
|
|
{
|
|
struct device *dev = &pseudo_dev.dev;
|
|
unsigned long ret = 0;
|
|
#ifdef CONFIG_OUTER_CACHE
|
|
phys_addr_t paddr = fmem_lookup_pa((unsigned int)vaddr);
|
|
#endif
|
|
|
|
if (!valid_dma_direction(dir))
|
|
panic("%s, invalid direction: %d \n", __func__, dir);
|
|
|
|
/* kernel buffer may not cache line alignment, it only can use both clean/inv
|
|
* for safe. Others we regard them as warning cases in coding.
|
|
*/
|
|
if (dir != DMA_BIDIRECTIONAL) {
|
|
if (!IS_ALIGNED((uintptr_t)vaddr, CACHE_ALIGN_MASK)) {
|
|
va_not_64align ++;
|
|
if (debug_counter & (0x1 << DEBUG_WRONG_CACHE_VA)) {
|
|
printk("%s, warning, vaddr: 0x%x not cache alignment! \n", __func__, (u32)vaddr);
|
|
dump_stack();
|
|
}
|
|
}
|
|
|
|
if (!IS_ALIGNED(len, CACHE_ALIGN_MASK)) {
|
|
length_not_64align ++;
|
|
if (debug_counter & (0x1 << DEBUG_WRONG_CACHE_LEN)) {
|
|
printk("%s, warning, len: %d not cache alignment! \n", __func__, len);
|
|
dump_stack();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (virt_addr_valid(vaddr) && virt_addr_valid(vaddr + len - 1)) {
|
|
/* Notice:
|
|
* The following code only for the direct mapping VA
|
|
*/
|
|
switch (dir) {
|
|
case DMA_BIDIRECTIONAL:
|
|
dma_map_single(dev, vaddr, len, DMA_TO_DEVICE);
|
|
/* v7_dma_unmap_area is only valid for DMA_FROM_DEVICE */
|
|
dma_unmap_single(dev, __pa(vaddr), len, DMA_FROM_DEVICE);
|
|
break;
|
|
case DMA_TO_DEVICE:
|
|
dma_map_single(dev, vaddr, len, DMA_TO_DEVICE);
|
|
break;
|
|
case DMA_FROM_DEVICE:
|
|
/* v7_dma_unmap_area is only valid for DMA_FROM_DEVICE */
|
|
dma_unmap_single(dev, __pa(vaddr), len, DMA_FROM_DEVICE);
|
|
break;
|
|
case DMA_NONE:
|
|
break;
|
|
}
|
|
cpuaddr_flush ++;
|
|
} else {
|
|
/* Notice:
|
|
* We must process outer cache if have. The code is not implement yet!
|
|
*/
|
|
switch (dir) {
|
|
case DMA_BIDIRECTIONAL:
|
|
#ifdef CONFIG_NVT_CACHE_FLUSH_ALL_FOR_HDAL
|
|
if ((len > CACHE_FLUSH_SIZE) && (use_native_api == 0)) {
|
|
ret = nvt_cache_op(vaddr, paddr, len, DMA_BIDIRECTIONAL, MAX_CACHE_TRY_LOCK, num_cpu_involve);
|
|
} else {
|
|
#else
|
|
{
|
|
#endif
|
|
dmac_flush_range(vaddr, vaddr + len);
|
|
#ifdef CONFIG_OUTER_CACHE
|
|
outer_flush_range(paddr, paddr + len);
|
|
#endif
|
|
}
|
|
break;
|
|
|
|
case DMA_TO_DEVICE:
|
|
#ifdef CONFIG_NVT_CACHE_FLUSH_ALL_FOR_HDAL
|
|
if (len > CACHE_FLUSH_SIZE && (use_native_api == 0)) {
|
|
ret = nvt_cache_op(vaddr, paddr, len, DMA_TO_DEVICE, MAX_CACHE_TRY_LOCK, num_cpu_involve);
|
|
} else {
|
|
#else
|
|
{
|
|
#endif
|
|
dmac_map_area(vaddr, len, DMA_TO_DEVICE);
|
|
#ifdef CONFIG_OUTER_CACHE
|
|
outer_clean_range(paddr, paddr + len);
|
|
#endif
|
|
}
|
|
break;
|
|
case DMA_FROM_DEVICE:
|
|
#ifdef CONFIG_NVT_CACHE_INVALIDATE_ALL_FOR_HDAL
|
|
if (len > CACHE_INV_SIZE && (use_native_api == 0)) {
|
|
ret = nvt_cache_op(vaddr, paddr, len, DMA_FROM_DEVICE, MAX_CACHE_TRY_LOCK, num_cpu_involve);
|
|
} else {
|
|
#else
|
|
{
|
|
#endif
|
|
dmac_map_area(vaddr, len, DMA_FROM_DEVICE);
|
|
#ifdef CONFIG_OUTER_CACHE
|
|
outer_inv_range(paddr, paddr + len);
|
|
#endif
|
|
}
|
|
break;
|
|
case DMA_NONE:
|
|
break;
|
|
}
|
|
nonlinear_cpuaddr_flush ++;
|
|
if (debug_counter & (0x1 << DEBUG_WRONG_CACHE_API)) {
|
|
printk("%s, warning, memory addr 0x%x not direct mapped! \n", __func__, (u32)vaddr);
|
|
dump_stack();
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
void fmem_dcache_sync_vb(void *vaddr, u32 len, enum dma_data_direction dir)
|
|
{
|
|
struct device *dev = &pseudo_dev.dev;
|
|
#ifdef CONFIG_OUTER_CACHE
|
|
phys_addr_t paddr = fmem_lookup_pa((unsigned int)vaddr);
|
|
#endif
|
|
|
|
if (0 == vb_cache_flush_en){
|
|
return;
|
|
}
|
|
if (!valid_dma_direction(dir))
|
|
panic("%s, invalid direction: %d \n", __func__, dir);
|
|
|
|
/* kernel buffer may not cache line alignment, it only can use both clean/inv
|
|
* for safe. Others we regard them as warning cases in coding.
|
|
*/
|
|
if (dir != DMA_BIDIRECTIONAL) {
|
|
if (!IS_ALIGNED((uintptr_t)vaddr, CACHE_ALIGN_MASK)) {
|
|
va_not_64align ++;
|
|
if (debug_counter & (0x1 << DEBUG_WRONG_CACHE_VA)) {
|
|
printk("%s, warning, vaddr: 0x%x not cache alignment! \n", __func__, (u32)vaddr);
|
|
dump_stack();
|
|
}
|
|
}
|
|
|
|
if (!IS_ALIGNED(len, CACHE_ALIGN_MASK)) {
|
|
length_not_64align ++;
|
|
if (debug_counter & (0x1 << DEBUG_WRONG_CACHE_LEN)) {
|
|
printk("%s, warning, len: %d not cache alignment! \n", __func__, len);
|
|
dump_stack();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (virt_addr_valid(vaddr) && virt_addr_valid(vaddr + len - 1)) {
|
|
/* Notice:
|
|
* The following code only for the direct mapping VA
|
|
*/
|
|
switch (dir) {
|
|
case DMA_BIDIRECTIONAL:
|
|
dma_map_single(dev, vaddr, len, DMA_TO_DEVICE);
|
|
/* v7_dma_unmap_area is only valid for DMA_FROM_DEVICE */
|
|
dma_unmap_single(dev, __pa(vaddr), len, DMA_FROM_DEVICE);
|
|
break;
|
|
case DMA_TO_DEVICE:
|
|
dma_map_single(dev, vaddr, len, DMA_TO_DEVICE);
|
|
break;
|
|
case DMA_FROM_DEVICE:
|
|
/* v7_dma_unmap_area is only valid for DMA_FROM_DEVICE */
|
|
dma_unmap_single(dev, __pa(vaddr), len, DMA_FROM_DEVICE);
|
|
break;
|
|
case DMA_NONE:
|
|
break;
|
|
}
|
|
cpuaddr_flush ++;
|
|
} else {
|
|
/* Notice:
|
|
* We must process outer cache if have. The code is not implement yet!
|
|
*/
|
|
switch (dir) {
|
|
case DMA_BIDIRECTIONAL:
|
|
dmac_flush_range(vaddr, vaddr + len);
|
|
#ifdef CONFIG_OUTER_CACHE
|
|
outer_flush_range(paddr, paddr + len);
|
|
#endif
|
|
break;
|
|
case DMA_TO_DEVICE:
|
|
dmac_map_area(vaddr, len, DMA_TO_DEVICE);
|
|
#ifdef CONFIG_OUTER_CACHE
|
|
outer_clean_range(paddr, paddr + len);
|
|
#endif
|
|
break;
|
|
case DMA_FROM_DEVICE:
|
|
dmac_map_area(vaddr, len, DMA_FROM_DEVICE);
|
|
#ifdef CONFIG_OUTER_CACHE
|
|
outer_inv_range(paddr, paddr + len);
|
|
#endif
|
|
break;
|
|
case DMA_NONE:
|
|
break;
|
|
}
|
|
nonlinear_cpuaddr_flush ++;
|
|
if (debug_counter & (0x1 << DEBUG_WRONG_CACHE_API)) {
|
|
printk("%s, warning, memory addr 0x%x not direct mapped! \n", __func__, (u32)vaddr);
|
|
dump_stack();
|
|
}
|
|
}
|
|
}
|
|
|
|
EXPORT_SYMBOL(fmem_alloc_from_cma);
|
|
EXPORT_SYMBOL(fmem_release_from_cma);
|
|
EXPORT_SYMBOL(fmem_lookup_pa);
|
|
EXPORT_SYMBOL(fmem_dcache_sync);
|
|
EXPORT_SYMBOL(fmem_dcache_sync_vb);
|
|
EXPORT_SYMBOL(fmem_dcache_sync_by_cpu);
|