#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 (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例程中,
会调用用户注册的例程,该例程在中断描述符结构体irq_desc[irq]的action结构体的hander中。
*/