Linux下注册中断并指定中断名称

Linux下注册中断并指定中断名称

Linux下可以调用request_irq,devm_request_irq,request_threaded_irq以及devm_request_threaded_irq接口来为硬件设备注册中断,这些个内核接口函数的区别大家可以自行百度。以request_threaded_irq为例,它的原型为

request_threaded_irq(unsigned int irq, irq_handler_t handler,
					irq_handler_t thread_fn,unsigned long flags, 
					const char *name, void *dev);

其中第一个参数irq是中断号,这个中断号通常不是芯片datasheet里的硬件中断号,而是经过调用irq_of_parse_and_map,of_irq_get或者platform_get_irq映射到Linux系统中的中断号(Linux IRQ number),第二个参数handler是中断处理函数(ISR),该函数运行在中断上下文,第三个参数thread_fn也是一个函数,它运行于内核线程上下文,第四个参数flags可以指定中断触发的模式(边沿触发,电平触发,是否共享,ONE_SHOT等等),第五个参数就是本文讨论的中断设备的名称,这个名称是可以在内核启动后通过cat /proc/interrupts查看的,最后一个参数dev可以指向描述硬件设备的结构体,因为多个设备可能共享中断,释放中断的时候需要用到该参数。

cat /proc/interrupts

Linux的魅力在于一切皆文件,不管是硬件设备本身,还是设备的属性都可以当成文件来操作,例如cat /proc/interrupts就可以将当前注册到系统中的硬件设备的中断信息全部打印出来:
在这里插入图片描述
上图中一共有7列,第1列就是Linux IRQ number,也就是上面request_threaded_irq的第一个参数,第2列是在CPU0上中断发生的次数,第3列是在CPU1上中断发生的次数,第4列是中断控制器的名称,第5列是datasheet中描述的硬件中断号,第6列是中断的触发模式(边沿触发or电平触发),第7列就是中断设备的名称,也就是request_threaded_irq的第5个参数。由于在request_threaded_irq函数原型中限制第5个参数为const char *类型,因此它只能是字符串常量,无法以字符数组指针的形式传递进来并进行动态修改,所以对于图片中多个相同类型设备使用同一份驱动的情况,要指定中断名称就有一点小麻烦,要么全部设备都指定同样的名称,要么驱动程序中根据设备数量多次调用request_threaded_irq,但是如果设备数量是可配置的,则每次增加设备就要修改驱动程序。

使用设备树描述中断节点

这个时候设备树的中断节点就派上用场啦,可以利用中断节点的interrupt-names属性或者自定义一个属性来指定中断设备名称:

	mlvds_top_0: mlvds_top@43ca0000 {
		clock-names = "s_axi_aclk";
		clocks = <&clkc 15>;
		compatible = "xxxx,mlvds-1.0.0";
		reg = <0x43ca0000 0x10000>;
		interrupt-parent = <&intc>;
		interrupts = <0 54 4 0 55 4>;
		interrupt-names = "mlvds0_timer", "mlvds0_other";
		current-speed = <50>;
	};
	mlvds_top_1: mlvds_top@43cb0000 {
		clock-names = "s_axi_aclk";
		clocks = <&clkc 15>;
		compatible = "xxxx,mlvds-1.0.0";
		reg = <0x43cb0000 0x10000>;
		interrupt-parent = <&intc>;
		interrupts = <0 56 4 0 57 4>;
		interrupt-names = "mlvds1_timer", "mlvds1_other";
		current-speed = <50>;
	};

驱动程序解析设备树节点

在驱动程序中可以这样来解析以上设备树节点,获取中断相关的属性和资源:

        /* Look for the interrupt parent. */
        p = of_irq_find_parent(np);
        if (p == NULL){
            printk(KERN_ERR "mlvds %d NO interrupt parent defined!\n", dev_id);
            ret = -EINVAL;
            goto fail_init;
        }
        
        /* Get size of interrupt specifier */
        if (of_property_read_u32(p, "#interrupt-cells", &intsize)) {
            printk(KERN_ERR "mlvds %d interrupt parent property \
            #interrupt-cells NOT specified!\n", dev_id);
            ret = -EINVAL;
            goto fail_init;
        }

        mlvds->irq_timer = irq_of_parse_and_map(np, 0);
        if(0 == mlvds->irq_timer){
            printk(KERN_ERR "mlvds %d: irq_timer NOT specified!\n", dev_id);
            ret = -ENOMEM;
            goto fail_init;
        }
		ret = of_property_read_u32_index(np, "interrupts",
						 intsize-1,
						 &mlvds->irq_timer_trigmode);
        if(0 != ret){
            printk(KERN_ERR "mlvds %d int flag NOT specified!\n", dev_id);
            ret = -EINVAL;
            goto fail_init;
        }
    	if (of_property_read_string_index(np, "interrupt-names", 0, \
    	&mlvds->irq_timer_name)) {
    		printk(KERN_WARNING "mlvds %d: interrupt-names property \
    		NOT set\n", dev_id);
    		mlvds->irq_timer_name = "mlvds_timer";
    	}

其中调用of_property_read_string_index来获取设备树节点中指定的中断设备名称,由于设备树中每个节点有两个中断号,因此需要用index来指定是获取哪个中断的名称。下面也给出硬件设备的结构体描述供参考:

struct mlvds_dev {
    struct cdev cdev;
    int chan;           /*  channel index 0, 1, ... */
    u32 irq_timer; /* Linux irq number */
    const char * irq_timer_name; /* Linux irq name */
    u32 irq_other; /* Linux irq number */
    const char * irq_other_name; /* Linux irq name */
    u32 irq_timer_trigmode; /* Linux irq mode */
    u32 irq_other_trigmode; /* Linux irq mode */
    }

驱动程序注册中断

以上,通过设备树获取了中断相关的属性和资源,最后,驱动程序中调用request_threaded_irq来注册中断:

    if(0 != request_threaded_irq(mlvds_device[channel].irq_timer,\
    mlvds_timming_isr, mlvds_timming_isr_thread, \
    mlvds_device[channel].irq_timer_trigmode | IRQF_ONESHOT, \
    mlvds_device[channel].irq_timer_name, &mlvds_device[channel])){
        printk(KERN_ERR "mlvds_channel_init: mlvds%d request_threaded_irq \
        error!\n", \channel);
        return -1;
    }

    if(0 != request_threaded_irq(mlvds_device[channel].irq_other, \
    mlvds_msg_isr, mlvds_msg_isr_thread, \
    mlvds_device[channel].irq_other_trigmode | IRQF_ONESHOT, \
    mlvds_device[channel].irq_other_name, &mlvds_device[channel])){
        printk(KERN_ERR "mlvds_channel_init: mlvds%d request_threaded_irq \
        error!\n", channel);
        return -1;
    }

安装驱动模块后的效果

OK,大功告成,即使后面再增加多个设备,只需要修改设备树即可,无须修改驱动程序,运行效果:
在这里插入图片描述

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值