树莓派官方自带gpio中断驱动bcm2708_gpio.c原理分析 linux 中断架构 中断子系统

[转载请注明出处 http://www.cnblogs.com/umbrellary/p/5167151.html  ]

上一篇记录了树莓派自带的gpio驱动(http://www.cnblogs.com/umbrellary/p/5164148.html),在bcm2708_gpio.c实现gpio驱动的同时其实也实现了中断控制器的驱动,本文记录bcm2708_gpio.c中驱动的实现。

一·bcm2708_gpio_irq_init中断初始化函数建立gpio中断描述表

static void bcm2708_gpio_irq_init(struct bcm2708_gpio *ucb)
{
    unsigned irq;

    printk(KERN_ERR DRIVER_NAME ": bcm2708_gpio_irq_init is not null!\n");

    ucb->gc.to_irq = bcm2708_gpio_to_irq;//获取该端口对应的中断号

    /* 针对每一个IRQ Line建立irq_desc  */
    for (irq = GPIO_IRQ_START; irq < (GPIO_IRQ_START + GPIO_IRQS); irq++) {
        irq_set_chip_data(irq, ucb);//设置私有数据
        irq_set_chip_and_handler(irq, &bcm2708_irqchip, handle_simple_irq);//同时设置irq_desc中的handle_irq回调和irq_chip指针
        set_irq_flags(irq, IRQF_VALID);
    }

    bcm2708_gpio_irq.dev_id = ucb;//用于标示唯一性和传递私有结构体
    setup_irq(IRQ_GPIO3, &bcm2708_gpio_irq);//IRQ_GPIO3响应bank1和bank2种所有的中断
}

这个函数是在本驱动的bcm2708_gpio_probe驱动初始化函数的最后面中调用的,作用是初始化中断。

1·首先赋值中断号转换函数

  ucb->gc.to_irq = bcm2708_gpio_to_irq;//获取该端口对应的中断号

2.使用循环建立系统所有的中断描述表

  irq_set_chip_data(irq, ucb);//设置私有数据

  irq_set_chip_and_handler(irq, &bcm2708_irqchip, handle_simple_irq);//同时设置irq_desc中的handle_irq回调和irq_chip指针

3.注册IRQ_GPIO3这个中断。

  这个中断是干嘛的?这个问题我找了好多帖子才大致有点了解。IRQ_GPIO3这个中断号的作用是,在bank1或者bank2中如果有任意一个引脚发生电平转换引发中断都会引起这个IRQ_GPIO3这个中断变量的中断。然后驱动便在这个IRQ_GPIO3中断的回调函数中(bcm2708_gpio_interrupt函数)进行扫描寄存器寻找发生中断的中断号,发生中断的即调用相应的回调函数。(下文分析)

二·IRQ_GPIO3中断的bcm2708_gpio_interrupt中断处理函数

static irqreturn_t bcm2708_gpio_interrupt(int irq, void *dev_id)
{
    unsigned long edsr;
    unsigned bank;
    int i;
    unsigned gpio;
    unsigned level_bits;
    struct bcm2708_gpio *gpio_data = dev_id;

    printk(KERN_ERR DRIVER_NAME ": bcm2708_gpio_interrupt %d\n", irq);

    /* 循环两个bank中的中断 */
    for (bank = 0; bank < GPIO_BANKS; bank++) {
        edsr = readl(__io_address(GPIO_BASE) + GPIOEDS(bank));//获取该bank的基地址
        level_bits = gpio_data->high[bank] | gpio_data->low[bank];

        for_each_set_bit(i, &edsr, 32) {//循环寻找edsr中为1的位

            gpio = i + bank * 32;//找出是哪个gpio

            /* ack edge triggered IRQs immediately */
            if (!(level_bits & (1<<i)))
                writel(1<<i, __io_address(GPIO_BASE) + GPIOEDS(bank));//消除中断标示

            generic_handle_irq(gpio_to_irq(gpio));

            /* ack level triggered IRQ after handling them */
            if (level_bits & (1<<i))
                writel(1<<i, __io_address(GPIO_BASE) + GPIOEDS(bank));//防止重入
        }
    }
    return IRQ_HANDLED;
}

本函数是IRQ_GPIO3中断向量的回调函数,作用是:扫描寄存器寻找发生中断的中断号,发生中断的即调用相应的回调函数。

1.for_each_set_bit(i, &edsr, 32) //循环寻找edsr中为1的位 即发生中断

2.generic_handle_irq(gpio_to_irq(gpio));//调用该中断向量的中断回调函数(驱动申请irq资源时会填写)  gpio_to_irq(gpio)为发生中断的中断向量号

三·中断控制器回调函数

/* 中断控制器 */
static struct irq_chip bcm2708_irqchip = {
    .name = "GPIO",
    .irq_enable = bcm2708_gpio_irq_unmask,//使能该irq,通常是直接调用irq_unmask()
    .irq_disable = bcm2708_gpio_irq_mask,//禁止该irq,通常是直接调用irq_mask
    .irq_unmask = bcm2708_gpio_irq_unmask,//取消屏蔽该irq
    .irq_mask = bcm2708_gpio_irq_mask,//屏蔽该irq
    .irq_set_type = bcm2708_gpio_irq_set_type,//设置irq的电气触发条件
};

内核为中断进行了抽象,必要的与平台相关的函数由irq_chip来表述。在这些回调函数中通通是通过写寄存器的方式来开启或者屏蔽中断。

四·打印实验

当我将gpio4同地线短接的时候就会发生IRQ_GPIO3中断(中断向量52)。在应用层可以使用poll函数来监听这个事件进行相应的处理。

树莓派官方自带gpio中断驱动bcm2708_gpio.c原理分析 linux 中断架构 中断子系统_第1张图片

 

 

你可能感兴趣的:(树莓派官方自带gpio中断驱动bcm2708_gpio.c原理分析 linux 中断架构 中断子系统)