/*
* 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 (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中。
|