sdk-hwV1.3/lichee/linux-4.9/drivers/char/mem_operation/mem_operation_drv.c

245 lines
6.5 KiB
C
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <asm/pgtable.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/mman.h>
#include <linux/memblock.h>
#include <linux/memblock.h>
#include <linux/pagemap.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
// #define WEIGHT_ADDR 0x4307F000 // 权重加载的内存地址处
// #define M_PAGE_SIZE (4 * 1024) // 内存对齐应该是4K
#define MEM_ACCESS_RELEASE_PHYS_ADDR _IOR('M', 0, unsigned int)
#define MEM_ACCESS_GET_WKSRC_SRC _IOW('M', 1, unsigned int)
#define MEM_ACCESS_SET_WKSRC_SRC _IOR('M', 2, unsigned int)
typedef struct {
unsigned char readData[sizeof(int)];
} wakeup_src_t;
static int major = 0;
static struct class *mem_operation_class;
struct mem_area_info {
atomic_t init_flag;
void __iomem *g_vaddr;
int len;
};
static struct mem_area_info g_mem = {.init_flag = ATOMIC_INIT(0), .g_vaddr = NULL};
int mem_set_wakeup_source(unsigned char wakeup_src) {
if (atomic_read(&g_mem.init_flag) == 1)
{
writeb(wakeup_src, g_mem.g_vaddr);
return 0;
}
return -1;
}
EXPORT_SYMBOL(mem_set_wakeup_source);
unsigned char mem_get_wakeup_source(void) {
if (atomic_read(&g_mem.init_flag) == 1)
{
return readb(g_mem.g_vaddr);
}
return 0xFF;
}
EXPORT_SYMBOL(mem_get_wakeup_source);
static int check_page_reserved(unsigned long addr)
{
struct page *page;
page = pfn_to_page(addr >> PAGE_SHIFT); // 将虚拟地址转换为页面索引,然后得到页面结构
if (PageReserved(page)) {
printk(KERN_INFO "Page at address 0x%lx is reserved.\n", addr);
return 0;
} else {
printk(KERN_INFO "Page at address 0x%lx is not reserved.\n", addr);
return -1;
}
}
static int mem_operation_drv_open (struct inode *node, struct file *file)
{
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
static int mem_operation_drv_close (struct inode *node, struct file *file)
{
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
static int mem_operation_drv_mmap(struct file *file, struct vm_area_struct *vma)
{
unsigned long phy = (unsigned long)g_mem.g_vaddr;
if(check_page_reserved((unsigned long)g_mem.g_vaddr) < 0)
return -EINVAL;
/* 设置属性: cache, buffer */
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
/* map */
if (remap_pfn_range(vma, vma->vm_start, phy >> PAGE_SHIFT,
vma->vm_end - vma->vm_start, vma->vm_page_prot)) {
printk("mmap remap_pfn_range failed\n");
return -ENOBUFS;
}
return 0;
}
static long mem_operation_drv_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
wakeup_src_t wakeupsrc = {.readData[0] = 0};
switch (cmd) {
case MEM_ACCESS_RELEASE_PHYS_ADDR: {
unsigned int mem_size;
if (get_user(mem_size, (unsigned long __user *)arg))
return -EFAULT;
if(check_page_reserved((unsigned long)g_mem.g_vaddr) < 0)
return -EINVAL;
memblock_free((unsigned int)g_mem.g_vaddr, mem_size);
free_reserved_area(__va(g_mem.g_vaddr), __va(g_mem.g_vaddr + mem_size), -1, "wakeup_source");
// printk("release phy addr: 0x%x, size: %d\n", (int)g_mem.g_vaddr, mem_size);
break;
}
case MEM_ACCESS_GET_WKSRC_SRC:
{
// void __iomem *vaddr = NULL;
// vaddr = ioremap(WEIGHT_ADDR, sizeof(wakeupsrc));
// vaddr = ioremap_nocache(PHYS_ADDR, SIZE);
wakeupsrc.readData[0] = readb(g_mem.g_vaddr);
// memcpy_fromio(buffer, vaddr, SIZE);
// copy_to_user(arg, vaddr, SIZE) ?
// printk(KERN_EMERG "give wake up source:%d\n", wakeupsrc.readData[0]);
if (copy_to_user((void *)arg, &wakeupsrc, sizeof(wakeupsrc)))
{
// iounmap(vaddr);
return -EINVAL;
}
// iounmap(vaddr);
break;
}
case MEM_ACCESS_SET_WKSRC_SRC:
{
// void __iomem *vaddr = NULL;
if (copy_from_user(&wakeupsrc, (void __user *)arg, sizeof(wakeupsrc)))
{
return -EFAULT;
}
writeb(wakeupsrc.readData[0], g_mem.g_vaddr);
// printk(KERN_EMERG "set wake up source:%d\n", wakeupsrc.readData[0]);
break;
}
default:
return -EINVAL;
break;
}
return 0;
}
static struct file_operations mem_operation_drv = {
.owner = THIS_MODULE,
.open = mem_operation_drv_open,
.release = mem_operation_drv_close,
.mmap = mem_operation_drv_mmap,
.unlocked_ioctl = mem_operation_drv_ioctl,
};
static int __init mem_operation_init(void)
{
int err;
struct device_node *np;
struct resource res;
np = of_find_compatible_node(NULL, NULL, "wakeup-src");
if (!np) {
pr_err("mem Reserved memory node not found\n");
return -ENODEV;
}
if (of_address_to_resource(np, 0, &res)) {
pr_err("mem Failed to parse reg\n");
of_node_put(np);
return -EFAULT;
}
g_mem.len = resource_size(&res);
g_mem.g_vaddr = memremap(res.start, g_mem.len, MEMREMAP_WB);
if (!g_mem.g_vaddr)
{
return -EINVAL;
}
#ifdef CONFIG_XR806_WLAN
writeb(0, g_mem.g_vaddr); // 如果xr806而非boot0获取唤醒源需要先设默认值
#endif
atomic_set(&g_mem.init_flag, 1);
// printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
major = register_chrdev(0, "mem_operation", &mem_operation_drv); /* /dev/mem_operation */
mem_operation_class = class_create(THIS_MODULE, "mem_operation_class");
err = PTR_ERR(mem_operation_class);
if (IS_ERR(mem_operation_class)) {
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
unregister_chrdev(major, "mem_operation");
memunmap(g_mem.g_vaddr);
return -1;
}
device_create(mem_operation_class, NULL, MKDEV(major, 0), NULL, "mem_operation"); /* 设备节点: /dev/mem_operation */
return 0;
}
static void __exit mem_operation_exit(void)
{
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
device_destroy(mem_operation_class, MKDEV(major, 0));
class_destroy(mem_operation_class);
unregister_chrdev(major, "mem_operation");
if (atomic_read(&g_mem.init_flag) == 1)
{
memunmap(g_mem.g_vaddr);
}
}
// 注意放在disp之前
fs_initcall_sync(mem_operation_init);
module_exit(mem_operation_exit);
MODULE_LICENSE("GPL");