Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2287309
  • 博文数量: 668
  • 博客积分: 10016
  • 博客等级: 上将
  • 技术积分: 8588
  • 用 户 组: 普通用户
  • 注册时间: 2008-05-29 19:22
文章分类

全部博文(668)

文章存档

2011年(1)

2010年(2)

2009年(273)

2008年(392)

分类:

2009-05-04 17:58:29

针对该irq.c的硬件手册是SPRUE14A.pdf,可到TI的网站()上下载,或直接在谷歌里搜索。
以下是文件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 <linux/config.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/ptrace.h>

#include <asm/hardware.h>
#include <asm/irq.h>
#include <asm/io.h>

#include <asm/mach/irq.h>
#include <asm/arch/irq.h>
#include <asm/arch/irqs.h>
#include <asm/arch/memory.h>
#include <asm/arch/hardware.h>


#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 (= IRQ_INTPRI0_REG_OFFSET; i <= IRQ_INTPRI7_REG_OFFSET; i += 4) {
        unsigned    j;
        u32        pri;

        for (= 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 (= 0; i < DAVINCI_N_AINTC_IRQ; i++) {
        set_irq_chip(i, &davinci_irq_chip_0);
        set_irq_flags(i, IRQF_VALID | IRQF_PROBE);
        if (!= 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例程中,
 会调用用户注册的例程,该例程在中断描述符结构体irq_desc[irq]的action结构体的hander中。

阅读(738) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~