yb_arm/driver/ucp_2_revmem/ucp_2_revmem.c
2023-07-12 14:14:31 +08:00

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);