【教程】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 headstatic 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

本文源码参考:

GitHub - hbwangjinwu/PIO_Interrupt: A example implement a custom driver for a button pio interrupt driver and test application on DE1_SoC Board · GitHub

往期阅读:

【教程】Agilex 5 SOC FPGA的HPS端如何响应FPGA端中断(一)------GIC控制器介绍

【教程】Agilex 5 SOC FPGA的HPS端如何响应FPGA端中断(二)------修改Quartus工程(GHRD工程)

【教程】Agilex 5 SOC FPGA的HPS端如何响应FPGA端中断(三)------设备树Overlay

相关推荐
璞致电子7 分钟前
基于 AD9361 (PZSDR‑P201Pro) 软件无线电实战综述:从板卡选型、硬件平台升级到 GNU Radio QPSK 完整收发链路
经验分享·嵌入式硬件·fpga开发·软件无线电·sdr·璞致电子
炎龙铠甲会合体2 小时前
从 SPEC 到上板:在 Zynq-7020 上用 RTL 手写一个 YOLO 卷积加速器(完整教程)
yolo·fpga开发·axi·芯片验证
FPGA小徐3 小时前
Vivado 按键消抖与复位模块代码修复详解:KEY_TOP、key、rst_gen 完整实战
fpga开发
炎龙铠甲会合体4 小时前
AXI2ICACHE— AXI4-Full 四路组相联指令 Cache 设计与 UVM 验证
fpga开发·面试·芯片验证
bingcheby5 小时前
FMQL15/20/45 (复旦微青龙系列 PSOC)新手使用说明
fpga开发
漫漫长远5 小时前
低功耗UPF介绍(一)
笔记·嵌入式硬件·学习·fpga开发
自小吃多17 小时前
【Verilog/FPGA】存储器(数组)
嵌入式硬件·fpga开发
szxinmai主板定制专家17 小时前
RK3588+FPGA异构架构|高速数据采集+边缘AI落地应用全解析
人工智能·嵌入式硬件·fpga开发·架构·zynq
明德扬1 天前
FPGA MIPI 多路视频聚合方案
fpga开发·mipi视频拼接·mipi多路拼接·mipi协议·mipi视频拼接方案·fpga mipi方案·mipi csi
XMAIPC_Robot1 天前
为什么大型储能需要边缘 AI 协调控制器?RK3588+FPGA 高速采集方案
人工智能·嵌入式硬件·fpga开发·arm+fpga·rk3588+fpga·协调控制器