需要在vivado中将PL的部分引入IRQ_F2P连号即可。
自定义IP的设备树可以很容易通过HDF文件和PETALINUX自动生成,具体命令为:
先配置下环境:
source *petalinux目录*/setting.sh
hsi
hsi后:
open_hw_design *hdf文件目录*
set_repo_path *device-tree-xlnx-xilinx-v2018.3库文件目录*
create_sw_design device-tree -os device_tree -proc ps7_cortexa9_0
generate_target -dir *存放目录*
自动生成之后可以看到一个名曰pl.dtsi的文件,里面放着自定义IP核的相关信息:
breath_led_ip_0: breath_led_ip@43c20000 {
clock-names = "s0_axi_aclk";
clocks = <&clkc 15>;
compatible = "xlnx,breath-led-ip-1.0";
interrupt-names = "breath_intr";
interrupt-parent = <&intc>;
interrupts = <0 29 4>;
reg = <0x43c20000 0x10000>;
xlnx,s0-axi-addr-width = <0x4>;
xlnx,s0-axi-data-width = <0x20>;
};
interrupt-parent = <&intc>;
interrupts = <0 29 4>;
0代表中断域为SPI中断,29代表硬件中断号为29+32=61。4代表触发方式,但具体触发方式貌似可以在自己写的LINUX驱动里修改。
驱动中每个中断都有一个中断号,通过中断号即可区分不同的中断。注意这里的中断号不是硬件的中断号,例如PL-PS的中断是从61开始的,但是这个61并不是LINUX的对应的中断号,我们需要通过irq_of_parse_and_map进行中断号进行申请。
unsigned int irq_of_parse_and_map(struct device_node *dev, int index)
dev :设备节点。
index:索引号,interrupts 属性可能包含多条中断信息,通过 index 指定要获取的信息。只要一个的话就写0。
int request_irq(unsigned int irq,
irq_handler_t handler,
unsigned long flags,
const char *name,
void *dev)
函数参数和返回值含义如下:
irq:要申请中断的中断号,是通过irq_of_parse_and_map返回的中断号,不是硬件终端号61。
handler:中断处理函数,当中断发生以后就会执行此中断处理函数。
flags:中断标志,可以在文件 include/linux/interrupt.h 里面查看所有的中断标志,里面有一堆标志,标志间可以通过或运算同时选择。这里可以设置触发方式。
name:中断名字,设置以后可以在/proc/interrupts 文件中看到对应的中断名字。
dev :如果将 flags 设置为 IRQF_SHARED 的话,dev 用来区分不同的中断,一般情况下将dev 设置为设备结构体,dev 会传递给中断处理函数 irq_handler_t 的第二个参数。笔者喜欢设置成NULL。
返回值:0 中断申请成功,其他负值 中断申请失败,如果返回-EBUSY 的话表示中断已经被申请了。
释放中断
void free_irq(unsigned int irq, void *dev)
函数参数和返回值含义如下:
irq :要释放的中断。
dev:如果中断设置为共享(IRQF_SHARED)的话,此参数用来区分具体的中断。共享中断
只有在释放最后中断处理函数的时候才会被禁止掉。
使用 request_irq 函数申请中断的时候需要设置中断处理函数,中断处理函数格式如下所示:
irqreturn_t (*irq_handler_t) (int, void *)
返回值要设置为:
return IRQ_RETVAL(IRQ_HANDLED)
一般通过下面两个函数对中断进行全局的关闭和开启:
local_irq_save(flags)
local_irq_restore(flags)
tasklet的套路:
/* 定义 taselet */
struct tasklet_struct testtasklet;
/* tasklet 处理函数 */
void testtasklet_func(unsigned long data)
{
}
/* 中断处理函数 */
irqreturn_t test_handler(int irq, void *dev_id)
{
......
tasklet_schedule(&testtasklet);
......
}
/* 驱动入口函数 */
static int __init xxxx_init(void)
{
......
/* 初始化 tasklet */
tasklet_init(&testtasklet, testtasklet_func, data);
......
}
工作队列的套路:
/* 定义工作(work) */
struct work_struct testwork;
/* work 处理函数 */
void testwork_func_t(struct work_struct *work);
{
/* work 具体处理内容 */
}
/* 中断处理函数 */
irqreturn_t test_handler(int irq, void *dev_id)
{
......
/* 调度 work */
schedule_work(&testwork);
......
}
/* 驱动入口函数 */
static int __init xxxx_init(void)
{
......
/* 初始化 work */
INIT_WORK(&testwork, testwork_func_t);
......
}
工作队列允许所需要的任务进入睡眠,不需要的话就就选择 tasklet。
自定义IP每隔一段时间生成一个中断信号,驱动收到中断后将变量val置位,如果被测试程序读取,则将val复位。测试程序一直轮询驱动中val的值,当值为1时输出。
驱动程序:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define NEWCHRLED_CNT 1 //the number of the devids
#define NEWCHRLED_NAME "intr_ip_test"
static void __iomem *LED_CTR_REG_MAP;
static void __iomem *LED_FRE_REG_MAP;
struct intr_ip_test_dev{
dev_t devid;
struct cdev cdev;
struct class *class;
struct device *device;
int major;
int minor;
spinlock_t lock;
struct device_node *nd;
unsigned int irq; //the interrupt number of breath_led_ip
unsigned int regdata[2]; //the two control registers made by ourselves
int val; //the interrupt test flag
struct tasklet_struct testtasklet;
};
static struct intr_ip_test_dev intr_ip_test;
static irqreturn_t ip_intr_handler(int irq, void *dev){
//printk("intr detected!\r\n");
/* awaken the tasklet function*/
tasklet_schedule(&intr_ip_test.testtasklet);
return IRQ_RETVAL(IRQ_HANDLED);
}
/* tasklet function */
void testtasklet_func(unsigned long data){
intr_ip_test.val = 1;
// this val will be cleared as the application APP reads the data from the driver.
}
static int chrdev_open(struct inode *inode, struct file *filp)
{
filp->private_data = &intr_ip_test; /* set our own structure as the private_data */
printk("intr_ip_test open\r\n");
return 0;
}
static ssize_t chrdev_read(struct file *filp, char __user *buf,
size_t cnt, loff_t *offt)
{
unsigned long flags;
int ret;
spin_lock_irqsave(&intr_ip_test.lock,flags);
ret = copy_to_user(buf,&intr_ip_test.val,sizeof(intr_ip_test.val));
intr_ip_test.val = 0; //clear val
spin_unlock_irqrestore(&intr_ip_test.lock,flags);
return 0;
}
static ssize_t chrdev_write(struct file *filp, const char __user *buf,
size_t cnt, loff_t *offt)
{
return 0;
}
static int chrdev_release(struct inode *inode, struct file *filp)
{
printk("intr_ip_test release!\r\n");
return 0;
}
static struct file_operations chrdev_fops = {
.owner = THIS_MODULE,
.open = chrdev_open,
.read = chrdev_read,
.write = chrdev_write,
.release = chrdev_release,
};
void inline open_led(void){
writel(1,LED_CTR_REG_MAP);
}
void inline close_led(void){
writel(0,LED_CTR_REG_MAP);
}
void inline set_led_g1(void){
writel(0x8000002f,LED_FRE_REG_MAP);
}
void inline set_led_g2(void){
writel(0x800000ef,LED_FRE_REG_MAP);
}
void inline reg_map (void){
LED_CTR_REG_MAP = ioremap(intr_ip_test.regdata[0], 4);
LED_FRE_REG_MAP = ioremap(intr_ip_test.regdata[0]+4, 4);
}
void inline reg_unmap (void){
iounmap(LED_CTR_REG_MAP);
iounmap(LED_FRE_REG_MAP);
}
static int __init intr_ip_test_init(void)
{
int ret;
spin_lock_init(&intr_ip_test.lock);
/* the initialization of the tasklet*/
tasklet_init(&intr_ip_test.testtasklet, testtasklet_func, NULL);
intr_ip_test.nd = of_find_node_by_path("/amba_pl/breath_led_ip@43c20000");
if(intr_ip_test.nd == NULL){
printk("no node from dts found!\r\n");
goto out0;
}
ret = of_property_read_u32_array(intr_ip_test.nd, "reg", intr_ip_test.regdata, 2);
if(ret < 0){
printk("reg read failed!\r\n");
} else {
u8 i = 0;
printk("reg data:\r\n");
for(i = 0; i < 2; i++)
printk("%#X ", intr_ip_test.regdata[i]);
printk("\r\n");
}
/*ip interrupt*/
intr_ip_test.irq = irq_of_parse_and_map(intr_ip_test.nd,0);
ret = request_irq(intr_ip_test.irq,
ip_intr_handler,
IRQF_TRIGGER_RISING,
"BREATH_LED",
NULL);
if(ret < 0 )
{
printk("irq %d request failed\r\n", intr_ip_test.irq);
ret = -EFAULT;
goto out0;
}
printk("irq %d request done\r\n", intr_ip_test.irq);
/* major minor and devid */
ret = alloc_chrdev_region(&intr_ip_test.devid,0,NEWCHRLED_CNT,NEWCHRLED_NAME);
if(ret)
goto out1;
intr_ip_test.major = MAJOR(intr_ip_test.devid);
intr_ip_test.major = MINOR(intr_ip_test.devid);
printk("intr_ip_test major=%d,minor=%d\r\n",intr_ip_test.major, intr_ip_test.minor);
/*initailization of cdev*/
intr_ip_test.cdev.owner = THIS_MODULE;
cdev_init(&intr_ip_test.cdev,&chrdev_fops);
ret = cdev_add(&intr_ip_test.cdev, intr_ip_test.devid,NEWCHRLED_CNT);
if(ret)
goto out2;
/* create a class*/
intr_ip_test.class = class_create(THIS_MODULE,NEWCHRLED_NAME);
if (IS_ERR(intr_ip_test.class)) {
ret = PTR_ERR(intr_ip_test.class);
goto out3;
}
intr_ip_test.device = device_create(intr_ip_test.class, NULL,
intr_ip_test.devid, NULL, NEWCHRLED_NAME);
if (IS_ERR(intr_ip_test.device)) {
ret = PTR_ERR(intr_ip_test.device);
goto out4;
}
/*map the addresses of the my_ip*/
reg_map();
open_led();
set_led_g1();
return 0;
out4:
class_destroy(intr_ip_test.class);
out3:
cdev_del(&intr_ip_test.cdev);
out2:
unregister_chrdev_region(intr_ip_test.devid,NEWCHRLED_CNT);
out1:
free_irq(intr_ip_test.irq, NULL);
out0:
return ret;
}
static void __exit intr_ip_test_exit(void)
{
// printk("Closing the driver!");
close_led();
reg_unmap();
disable_irq_nosync(intr_ip_test.irq);
device_destroy(intr_ip_test.class, intr_ip_test.devid);
class_destroy(intr_ip_test.class);
cdev_del(&intr_ip_test.cdev);
unregister_chrdev_region(intr_ip_test.devid,NEWCHRLED_CNT);
free_irq(intr_ip_test.irq, NULL);
}
module_init(intr_ip_test_init);
module_exit(intr_ip_test_exit);
MODULE_AUTHOR("zangyujiagood");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("intr_ip_test");
测试程序:
#include
#include
#include
#include
#include
#include
#include
/*
* @description : main主程序
* @param - argc : argv数组元素个数
* @param - argv : 具体参数
* @return : 0 成功;其他 失败
*/
int main(int argc, char *argv[])
{
int fd, ret;
int val;
if(2 != argc) {
printf("Usage:\n"
"\t./App /dev/intr_ip_test\n"
);
return -1;
}
fd = open(argv[1], O_RDWR);
if(0 > fd) {
printf("ERROR: %s file open failed!\n", argv[1]);
return -1;
}
for ( ; ; ) {
read(fd, &val, sizeof(int));
if (0x1 == val)
printf("PS received the intr signal, the 'val' = 0x%x\n", val);
}
close(fd);
return 0;
}