跳转到主要内容

Agilex 5 SOC FPGA的HPS端如何响应FPGA端中断(四)——设计中断驱动程序

驱动程序采用Linux平台总线(Platform Bus)模型,通过 platform_driver 结构体注册驱动,利用设备树实现设备和驱动的自动匹配。驱动同时注册了一个混杂设备(miscdevice),主设备号为10,为用户空间提供 /dev/pio_button 设备节点。

这个驱动我们不采用编译进内核的方式,而是当作一个可以动态加载的模块。这个驱动模块由源文件和makefile组成。

驱动源文件pio_interrupt.c:

//platform driver 
#include <linux/module.h> 
#include <linux/types.h> 
#include <linux/miscdevice.h> 
#include <linux/fs.h> 
#include <linux/init.h> 
#include <linux/platform_device.h> 
#include <linux/interrupt.h> 
#include <linux/clk.h> 
#include <linux/uaccess.h> 
#include <linux/io.h> 
#include <linux/poll.h> 
#include <linux/irq.h> 
#include <asm/unistd.h> 
#include <linux/device.h> 
#include <linux/of.h> 
#include <linux/slab.h> 
#include <linux/sched.h> 


// define the interrupt registers,  refer to embedded ip user guide for infomation 
#define  PIO_REGISTER_INTERRUPTMSK 2 
#define  PIO_REGISTER_EDGECAPTUE   3 

struct irq_des 
{ 
    int  button_irq; 
    void __iomem *pio_base; 
}; 


static volatile int key_values; 
static volatile int ev_press = 0; 

//define the wait quene head 
static DECLARE_WAIT_QUEUE_HEAD(button_waitq); 


struct irq_des pio_irq; 



static irqreturn_t buttons_interrupt(int irq, void *dev_id) 
{ 
    if(irq ==pio_irq.button_irq){ 
        key_values = readl(pio_irq.pio_base+PIO_REGISTER_EDGECAPTUE*4); 
        ev_press = 1; 
        wake_up_interruptible(&button_waitq);     
    } 
    //clear all  edge caputre registers 
    writel(0x0f,pio_irq.pio_base+PIO_REGISTER_EDGECAPTUE*4); 
    return IRQ_RETVAL(IRQ_HANDLED); 
} 


static int pio_buttons_open(struct inode *inode, struct file *file) 
{ 
    int err = 0; 
    err = request_irq(pio_irq.button_irq, buttons_interrupt, 0, 
                          "PIO_BUTTON", NULL); 
    if (err) { 
        disable_irq(pio_irq.button_irq); 
        free_irq(pio_irq.button_irq, NULL); 
        return -EBUSY; 
    }   
    ev_press = 0; 
    //enable pio interrupt 
    writel(0x0f,pio_irq.pio_base+PIO_REGISTER_INTERRUPTMSK*4); 
    return 0; 
} 


static int pio_buttons_close(struct inode *inode, struct file *file) 
{ 

    free_irq(pio_irq.button_irq, NULL); 
        return 0; 
} 


static ssize_t pio_buttons_read(struct file *filp, char __user *buff, size_t count, loff_t *offp) 
{ 
    unsigned long err; 
    wait_event_interruptible(button_waitq, ev_press); 
    ev_press = 0; 

    err = copy_to_user(buff, (const void *)&key_values, min(sizeof(key_values), count)); 

    return err ? -EFAULT : (ssize_t)min(sizeof(key_values), count); 
} 

static unsigned int pio_buttons_poll( struct file *file, struct poll_table_struct *wait) 
{ 
    unsigned int mask = 0; 
    poll_wait(file, &button_waitq, wait); 
    if (ev_press) 
        mask |= POLLIN | POLLRDNORM; 
    return mask; 
} 


static struct file_operations dev_fops = { 
    .owner   =   THIS_MODULE, 
    .open    =   pio_buttons_open, 
    .release =   pio_buttons_close, 
    .read    =   pio_buttons_read, 
    .poll    =   pio_buttons_poll, 
}; 

static struct miscdevice misc = { 
    .minor = MISC_DYNAMIC_MINOR, 
    .name = "pio_button", 
    .fops = &dev_fops, 
}; 

static int my_plat_probe(struct platform_device *pdev) 
{ 
    int ret; 
    struct resource *res; 
    int  irq; 
    int  error; 
    res = platform_get_resource(pdev,IORESOURCE_MEM,0); 
    if(res == NULL) 
        return -ENOENT;     
    if(!request_mem_region(res->start,resource_size(res),pdev->name)){ 
            error=-EBUSY; 
            return error; 
    } 
    pio_irq.pio_base=ioremap(res->start,resource_size(res)); 
    irq=platform_get_irq(pdev,0); 
    if(irq<0) 
    { 
        return irq; 
    } 
    pio_irq.button_irq=irq; 
    ret = misc_register(&misc); 
    if(ret) 
        return ret; 

    return 0; 
} 

static void my_plat_remove(struct platform_device *pdev) 
{ 
    struct resource *res; 
    printk("my platfrom device has removed.\n"); 
    res=platform_get_resource(pdev,IORESOURCE_MEM,0); 
    release_mem_region(res->start,resource_size(res)); 
    misc_deregister(&misc); 
    //return 0; 
} 

static struct of_device_id pio_button_match[]={ 
    {.compatible="terasic,pio-button",.data=NULL}, 
    {} 
}; 
MODULE_DEVICE_TABLE(of,pio_button_match); 

struct platform_driver my_buttons_drv = { 
    .probe = my_plat_probe, 
    .remove = my_plat_remove, 
    .driver = { 
        .owner = THIS_MODULE, 
        .name = "my_buttons", 
        .of_match_table=pio_button_match, 
    }, 
}; 

static int __init platform_drv_init(void) 
{ 
    int ret; 

    ret = platform_driver_register(&my_buttons_drv); 

    return ret; 
} 

static void __exit platform_drv_exit(void) 
{ 
    platform_driver_unregister(&my_buttons_drv); 
} 

module_init(platform_drv_init); 
module_exit(platform_drv_exit); 

MODULE_AUTHOR("Terasic"); 
MODULE_LICENSE("GPL");

注意上面Makefile文件当中 KDIR := /home/doreen/de25_nan0_sdmmc/linux-socfpga 是指定Linux Kernel 源码路径,读者要改成自己当前开发路径。

设置好编译链环境变量,执行make编译出pio_interrupt.ko文件:

export CROSS_COMPILE=aarch64-linux-gnu- 

export ARCH=arm64 

export PATH=/usr/bin:$PATH   

make

本文源码参考:https://github.com/hbwangjinwu/PIO_Interrupt/tree/master   

文章来源:友晶科技