245 lines
6.5 KiB
C
Executable File
245 lines
6.5 KiB
C
Executable File
#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");
|