326 lines
8.3 KiB
C
326 lines
8.3 KiB
C
#include <linux/interrupt.h>
|
|
#include <linux/module.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/miscdevice.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/device.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/mod_devicetable.h>
|
|
#include <linux/of_address.h>
|
|
#include <asm/io.h>
|
|
|
|
#include <linux/async_tx.h>
|
|
#include <linux/dmaengine.h>
|
|
#include <linux/gfp.h>
|
|
#include <linux/dma-mapping.h>
|
|
|
|
#include "ucp_2_revmem.h"
|
|
|
|
/*暂时将ucp_mem的主设备号定义为10 */
|
|
|
|
typedef struct tag_OSP_CACHE_REV
|
|
{
|
|
unsigned int phy_addr;
|
|
unsigned int len;
|
|
}OSP_CACHE_REV;
|
|
|
|
struct ucp_2_rev_device{
|
|
struct device *dev;
|
|
struct miscdevice *miscdev;
|
|
void __iomem *vir_head;
|
|
unsigned int phy_head;
|
|
};
|
|
|
|
struct ucp_2_rev_device *ucp_2_rev_dev;
|
|
|
|
#define DEVICE_NAME "ucp_2_revmem"
|
|
//#define DEVICE_NAME "ucp_2_mmap"
|
|
|
|
|
|
#define IOCTL_CACHE_INVALID _IO('k',0x10)
|
|
#define IOCTL_CACHE_FLUSH _IO('k',0x20)
|
|
#define IOCTL_SET_CACHE _IO('k',0x30)
|
|
#define IOCTL_SET_NOCACHE _IO('k',0x40)
|
|
|
|
#define UP_ALIGNED(num, n) (((num) + (n) - 1) & (~((n) - 1)))
|
|
|
|
#define REV_WITH_CACHE 2
|
|
#define REV_WITH_NOCACHE 3
|
|
|
|
int is_cached=0;
|
|
|
|
extern void arch_sync_dma_for_device_p(struct device *dev, void *addr,
|
|
size_t size, enum dma_data_direction dir);
|
|
extern void arch_sync_dma_for_cpu_p(struct device *dev, void *addr,
|
|
size_t size, enum dma_data_direction dir);
|
|
|
|
/*file open function*/
|
|
int ucp_2_revmem_open(struct inode *inode, struct file *filp)
|
|
{
|
|
/* device structure pointer assgined to file private data pointer */
|
|
printk("enter %s !!!\n",__func__);
|
|
return 0;
|
|
}
|
|
|
|
ssize_t ucp_2_revmem_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
|
|
{
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*file release function*/
|
|
int ucp_2_revmem_release(struct inode *inode, struct file *filp)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/* ioctl device control function */
|
|
static long ucp_2_revmem_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
|
{
|
|
OSP_CACHE_REV operation;
|
|
void __iomem *vir;
|
|
struct ucp_2_rev_device *rev_dev = ucp_2_rev_dev;
|
|
struct miscdevice *miscdev = rev_dev->miscdev;
|
|
|
|
// printk("%s\n",miscdev->name);
|
|
|
|
switch(cmd)
|
|
{
|
|
case IOCTL_SET_CACHE:
|
|
is_cached = REV_WITH_CACHE;
|
|
// dev_info(rev_dev->dev,"ioctl set with cache");
|
|
break;
|
|
case IOCTL_SET_NOCACHE:
|
|
is_cached = REV_WITH_NOCACHE;
|
|
// dev_info(rev_dev->dev,"ioctl set no cache");
|
|
break;
|
|
case IOCTL_CACHE_FLUSH:
|
|
if(copy_from_user((void *)(&operation), (char *)arg, sizeof(OSP_CACHE_REV)))
|
|
{
|
|
dev_info(rev_dev->dev,"ioctl copy_from_user error");
|
|
return - EINVAL;
|
|
}
|
|
|
|
if(operation.phy_addr<=0 || operation.len <=0)
|
|
{
|
|
dev_info(rev_dev->dev,"ioctl user arg error");
|
|
return - EINVAL;
|
|
}
|
|
|
|
operation.len = UP_ALIGNED(operation.len,4);
|
|
|
|
// vir = ioremap_cache(operation.phy_addr,operation.len);
|
|
vir = rev_dev->vir_head + (operation.phy_addr - rev_dev->phy_head);
|
|
if(vir == NULL)
|
|
{
|
|
dev_info(rev_dev->dev,"ioctl vir error");
|
|
return - EINVAL;
|
|
}
|
|
arch_sync_dma_for_device_p(rev_dev->dev, vir, operation.len, DMA_TO_DEVICE);
|
|
break;
|
|
|
|
case IOCTL_CACHE_INVALID:
|
|
if(copy_from_user((void *)(&operation), (char *)arg, sizeof(OSP_CACHE_REV)))
|
|
{
|
|
dev_info(rev_dev->dev,"ioctl copy_from_user error");
|
|
return - EINVAL;
|
|
}
|
|
|
|
if(operation.phy_addr<=0 || operation.len <=0)
|
|
{
|
|
dev_info(rev_dev->dev,"ioctl user arg error");
|
|
return - EINVAL;
|
|
}
|
|
|
|
operation.len = UP_ALIGNED(operation.len,4);
|
|
|
|
// vir = ioremap_cache(operation.phy_addr,operation.len);
|
|
vir = rev_dev->vir_head + (operation.phy_addr - rev_dev->phy_head);
|
|
if(vir == NULL)
|
|
{
|
|
dev_info(rev_dev->dev,"ioctl vir error");
|
|
return - EINVAL;
|
|
}
|
|
arch_sync_dma_for_cpu_p(rev_dev->dev, vir, operation.len, DMA_FROM_DEVICE);
|
|
break;
|
|
|
|
|
|
default:
|
|
return - EINVAL;
|
|
}
|
|
// iounmap(vir);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int ucp_2_revmem_mmap(struct file *filp, struct vm_area_struct *vma)
|
|
{
|
|
struct ucp_2_rev_device *rev_dev = ucp_2_rev_dev;
|
|
struct miscdevice *miscdev = rev_dev->miscdev;
|
|
uint64_t offset = vma->vm_pgoff << PAGE_SHIFT;
|
|
|
|
printk("enter %s !!!\n",__func__);
|
|
|
|
printk("ucp_revmem_mmap offset = %#llx,start = %#llx\n",offset,vma->vm_start);
|
|
|
|
|
|
vma->vm_flags |= VM_IO | VM_LOCKED;
|
|
|
|
if(offset < ARM_STACK_BASE_PHY_ADDR)
|
|
{
|
|
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
|
|
}
|
|
#if 0
|
|
if(is_cached == REV_WITH_NOCACHE)
|
|
{
|
|
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
|
|
}
|
|
else if(is_cached == REV_WITH_CACHE)
|
|
{
|
|
|
|
}
|
|
else
|
|
{
|
|
dev_info(rev_dev->dev,"flag is_cached is error %d\n",is_cached);
|
|
return -EAGAIN;
|
|
}
|
|
|
|
|
|
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
|
|
#endif
|
|
if (remap_pfn_range(vma,
|
|
vma->vm_start,
|
|
vma->vm_pgoff,
|
|
vma->vm_end-vma->vm_start,
|
|
vma->vm_page_prot) < 0)
|
|
{
|
|
return -EAGAIN;
|
|
}
|
|
// dev_info(rev_dev->dev,"ucp_revmem_mmap:%d ok\n",is_cached);
|
|
printk("leave %s !!!\n",__func__);
|
|
|
|
is_cached = 0;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*file operation structure*/
|
|
static const struct file_operations ucp_2_revmem_fops =
|
|
{
|
|
.owner = THIS_MODULE,
|
|
.open = ucp_2_revmem_open,
|
|
.unlocked_ioctl = ucp_2_revmem_ioctl,
|
|
.mmap = ucp_2_revmem_mmap,
|
|
.release = ucp_2_revmem_release,
|
|
};
|
|
|
|
static ssize_t ucp_2_revmem_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
int len=0;
|
|
|
|
len += sprintf(buf+len, "%s\r\n", "ucp_2_mem attribute OK!");
|
|
|
|
return len;
|
|
}
|
|
|
|
static DEVICE_ATTR(ucp_2_revmem, S_IRUGO, ucp_2_revmem_show, NULL);
|
|
|
|
static struct miscdevice ucp_2_revmem_miscdev = {
|
|
.minor = MISC_DYNAMIC_MINOR,
|
|
.name = DEVICE_NAME,
|
|
.fops = &ucp_2_revmem_fops,
|
|
};
|
|
|
|
static int __init ucp_2_revmem_module_init(void)
|
|
{
|
|
int ret;
|
|
struct miscdevice *misc_device;
|
|
void __iomem * vir_addr;
|
|
dma_addr_t phy_addr;
|
|
int i;
|
|
|
|
ucp_2_rev_dev = (struct ucp_2_rev_device *)kzalloc(sizeof(*ucp_2_rev_dev), GFP_KERNEL);
|
|
if (!ucp_2_rev_dev)
|
|
return -ENOMEM;
|
|
|
|
misc_device = &ucp_2_revmem_miscdev;
|
|
ret = misc_register(&ucp_2_revmem_miscdev);
|
|
if(ret)
|
|
{
|
|
goto misc_err;
|
|
}
|
|
|
|
ret = device_create_file(ucp_2_revmem_miscdev.this_device, &dev_attr_ucp_2_revmem);
|
|
if (ret)
|
|
{
|
|
goto attr_err;
|
|
}
|
|
ucp_2_rev_dev->dev = ucp_2_revmem_miscdev.this_device;
|
|
ucp_2_rev_dev->miscdev = &ucp_2_revmem_miscdev;
|
|
dev_set_drvdata(ucp_2_revmem_miscdev.this_device, ucp_2_rev_dev);
|
|
|
|
ucp_2_rev_dev->phy_head = UCP_2_APE_REV_MEM_START;
|
|
ucp_2_rev_dev->vir_head = ioremap_cache(UCP_2_APE_REV_MEM_START,UCP_2_APE_REM_MEM_LEN);
|
|
|
|
dma_coerce_mask_and_coherent(ucp_2_rev_dev->dev, DMA_BIT_MASK(32));
|
|
|
|
/*
|
|
for(i = 0; i< 100; i++)
|
|
{ //GFP_ATOMIC,GFP_KERNEL
|
|
vir_addr = dma_alloc_coherent(ucp_2_rev_dev->dev, 0x64000, (dma_addr_t *)&phy_addr, GFP_ATOMIC);
|
|
if(vir_addr == NULL)
|
|
{
|
|
printk("revmem dma_alloc_coherent error:%d\n",i);
|
|
}
|
|
}
|
|
*/
|
|
|
|
dev_info(ucp_2_rev_dev->dev,"ucp_2_revem_module_init ok\n");
|
|
return 0;
|
|
misc_err:
|
|
device_remove_file(ucp_2_revmem_miscdev.this_device, &dev_attr_ucp_2_revmem);
|
|
attr_err:
|
|
misc_deregister(&ucp_2_revmem_miscdev);
|
|
return -1;
|
|
|
|
}
|
|
|
|
/* module unload function*/
|
|
static void __exit ucp_2_revmem_module_exit(void)
|
|
{
|
|
device_remove_file(ucp_2_revmem_miscdev.this_device, &dev_attr_ucp_2_revmem);
|
|
misc_deregister(&ucp_2_revmem_miscdev);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
* a simple char device driver: ExtInt without mutex
|
|
*
|
|
* Copyright (C) 2014 Barry Song (baohua@kernel.org)
|
|
*
|
|
* Licensed under GPLv2 or later.
|
|
*/
|
|
|
|
|
|
MODULE_AUTHOR ("lte team");
|
|
MODULE_DESCRIPTION ("ucp_mem driver module" );
|
|
MODULE_LICENSE ("GPL");
|
|
module_init(ucp_2_revmem_module_init);
|
|
module_exit(ucp_2_revmem_module_exit);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|