Davinci DM6446 Linux 内核分析——irq.c

时间:2022-10-01 04:12:47
#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中。
 */