达芬奇DM644X平台(ARM9, Linux-2.6.10)BSP之irq.c浅析

来源:互联网 发布:resty golang 编辑:程序博客网 时间:2024/06/05 00:28

针对该irq.c的硬件手册是SPRUE14A.pdf,可到TI的网站(www.ti.com)上下载,或直接在谷歌里搜索。
以下是文件irq.c的浅析。

/*

 * linux/arch/arm/mach-davinci/irq.c


 * Interrupt handler for DaVinci boards.
 *
 * Copyright (C) 2006 Texas Instruments.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */
#include
#include
#include
#include
#include
#include

#include
#include
#include

#include
#include
#include
#include
#include


#define IRQ_BIT(irq)        ((irq) & 0x1f)

#define FIQ_REG0_OFFSET            0x0000        // 快速中断请求状态寄存器0

#define FIQ_REG1_OFFSET            0x0004        // 快速中断请求状态寄存器1

#define IRQ_REG0_OFFSET            0x0008        // 中断请求状态寄存器0

#define IRQ_REG1_OFFSET            0x000C        // 中断请求状态寄存器1

#define IRQ_ENT_REG0_OFFSET        0x0018        // 中断使能寄存器0

#define IRQ_ENT_REG1_OFFSET        0x001C        // 中断使能寄存器1

#define IRQ_INCTL_REG_OFFSET    0x0020        // 中断控制寄存器

#define IRQ_EABASE_REG_OFFSET    0x0024        // 中断跳转表首地址寄存器

#define IRQ_INTPRI0_REG_OFFSET    0x0030        // 中断优先级寄存器0

#define IRQ_INTPRI7_REG_OFFSET    0x004C        // 中断优先级寄存器7


const u8 *davinci_def_priorities;

static inline unsigned int davinci_irq_readl(int offset)
{
    return davinci_readl(DAVINCI_ARM_INTC_BASE + offset);
}

static inline void davinci_irq_writel(unsigned long value, int offset)
{
    davinci_writel(value, DAVINCI_ARM_INTC_BASE + offset);
}

/*
 禁止单个中断线。向中断使能寄存器写0禁止中断。
*/
/* Disable interrupt */
static void davinci_mask_irq(unsigned int irq)
{
    unsigned int mask;
    u32 l;

    mask = 1 << IRQ_BIT(irq);

    if (irq > 31) {
        l = davinci_irq_readl(IRQ_ENT_REG1_OFFSET);
        l &= ~mask;
        davinci_irq_writel(l, IRQ_ENT_REG1_OFFSET);
    } else {
        l = davinci_irq_readl(IRQ_ENT_REG0_OFFSET);
        l &= ~mask;
        davinci_irq_writel(l, IRQ_ENT_REG0_OFFSET);
    }
}

/*
 使能单个中断线。向中断使能寄存器写1使能中断。
*/
/* Enable interrupt */
static void davinci_unmask_irq(unsigned int irq)
{
    unsigned int mask;
    u32 l;

    mask = 1 << IRQ_BIT(irq);

    if (irq > 31) {
        l = davinci_irq_readl(IRQ_ENT_REG1_OFFSET);
        l |= mask;
        davinci_irq_writel(l, IRQ_ENT_REG1_OFFSET);
    } else {
        l = davinci_irq_readl(IRQ_ENT_REG0_OFFSET);
        l |= mask;
        davinci_irq_writel(l, IRQ_ENT_REG0_OFFSET);
    }
}

/*
 响应某个中断,向中断请求状态寄存器写入1响应中断并清除该位。
*/
/* EOI interrupt */
static void davinci_ack_irq(unsigned int irq)
{
    unsigned int mask;

    mask = 1 << IRQ_BIT(irq);

    if (irq > 31)
        davinci_irq_writel(mask, IRQ_REG1_OFFSET);
    else
        davinci_irq_writel(mask, IRQ_REG0_OFFSET);
}

/*
 在gpio.c中曾经说明过。该结构体注册到代表irq的irq_desc结构体中,
 当中断发生时调用这些回调函数,完成中断的使能禁止和中断响应。
*/
static struct irqchip davinci_irq_chip_0 = {
    .ack    = davinci_ack_irq,
    .mask    = davinci_mask_irq,
    .unmask = davinci_unmask_irq,
};

/*
 中断初始化函数,其在borad_evm.c中注册到__mach_desc_DAVINCI_EVM_type结构体中,
 系统启动时会调用。调用的过程是:start_kernel()-->setup_arch()-->
 init_arch_irq = mdesc->init_irq(init_arch_irq是个全局函数指针变量,
 mdesc->init_irq指针指向的就是本文中的已经注册到机器描述符里的davinci_irq_init()例程),
 然后便是start_kernel()-->init_IRQ()-->init_arch_irq()(也就是davinci_irq_init())。
 从上可以看出经历了两个过程,才调用davinci_irq_init()例程来初始化中断。
*/
/* ARM Interrupt Controller Initialization */
void __init davinci_irq_init(void)
{
    unsigned i;

    // 向中断请求状态寄存器写0,清除所有中断请求

    /* Clear all interrupt requests */
    davinci_irq_writel(~0x0, FIQ_REG0_OFFSET);
    davinci_irq_writel(~0x0, FIQ_REG1_OFFSET);
    davinci_irq_writel(~0x0, IRQ_REG0_OFFSET);
    davinci_irq_writel(~0x0, IRQ_REG1_OFFSET);

    /* 向中断使能寄存器0,禁止所有中断线 */
    /* Disable all interrupts */
    davinci_irq_writel(0x0, IRQ_ENT_REG0_OFFSET);
    davinci_irq_writel(0x0, IRQ_ENT_REG1_OFFSET);
   
    /*
     禁止将被禁止的中断或快速中断的地址放置在IRQENTRY或FIQENTRY中
     设置中断禁止模式为立即禁止中断模式
    */
    /* Interrupts disabled immediately, IRQ entry reflects all */
    davinci_irq_writel(0x0, IRQ_INCTL_REG_OFFSET);

    /*
     不使用硬件跳转表,只使用其入口地址(0x0),地址宽度为4个字节。这样就可以
     从中断请求入口地址寄存器(IRQENTRY)读取的地址值,除于4来获得中断号。
    */
    /* we don't use the hardware vector table, just its entry addresses */
    davinci_irq_writel(0, IRQ_EABASE_REG_OFFSET);

     // 向中断请求状态寄存器写0,清除所有中断请求

    /* Clear all interrupt requests */
    davinci_irq_writel(~0x0, FIQ_REG0_OFFSET);
    davinci_irq_writel(~0x0, FIQ_REG1_OFFSET);
    davinci_irq_writel(~0x0, IRQ_REG0_OFFSET);
    davinci_irq_writel(~0x0, IRQ_REG1_OFFSET);

    // 设置各中断的优先级

    for (i = IRQ_INTPRI0_REG_OFFSET; i <= IRQ_INTPRI7_REG_OFFSET; i += 4) {
        unsigned    j;
        u32        pri;

        for (j = 0, pri = 0; j < 32; j += 4, davinci_def_priorities++)
            pri |= (*davinci_def_priorities & 0x07) << j;// 每个中断优先级占据四位,

                                                         // 其中最高位保留

        davinci_irq_writel(pri, i);
    }

    /* 注册回调例程 */
    /* set up genirq dispatch for ARM INTC */
    for (i = 0; i < DAVINCI_N_AINTC_IRQ; i++) {
        set_irq_chip(i, &davinci_irq_chip_0);
        set_irq_flags(i, IRQF_VALID | IRQF_PROBE);
        if (i != IRQ_TINT1_TINT34)
            set_irq_handler(i, do_edge_IRQ);
        else
            set_irq_handler(i, do_level_IRQ);
    }
}

/*
 dm644x平台中断的过程大致如下:硬件中断发生-->硬件保存cpsr和pc并跳转到irq的ISR
 入口地址-->此入口地址为跳转指令,跳转到vector_irq例程,然后再根据当前的运行模式
 (usr或svc)跳转到响应的例程-->进入_irq_usr或_irq_svc例程,进行更为详细的
 上下文保存,然后根据IRQENTRY/4-1得到中断号,通过寄存器r0作为形参传给
 asm_do_IRQ例程,并跳转到asm_do_IRQ例程-->asm_do_IRQ例程根据传进来的中断号获得
 相应的中断描述符结构体irq_desc[irq],然后调用handle例程(一个函数指针,由
 set_irq_handler()注册,如上面的程序,就注册了handle_edge_irq和handle_level_irq,
 前者处理边沿触发的中断,后者处理电平触发的中断),再跳转到__do_irq例程-->在__do_irq例程中,
 会调用用户注册的例程,该

原创粉丝点击