浅析硬件中断(mips)


注:图片取自:http://blog.chinaunix.net/uid-23769728-id-3079164.html

先简单介绍一下硬件中断的软件流程:handle_int   ->plat_irq_dispatch->malta_hw0_irqdispatch->do_IRQ(irq)->generic_handle_irq -> __do_IRQ()  -> irq_exit


1、中断初始化:

1、main.c中的start_kernel函数中,trap_init()函数,用来初始化handle_init函数,使其指向中断响应。

void __init trap_init(void){
.......

set_except_vector(0, handle_int);  //中断异常
.........
}
handle_init函数到下面的函数响应中再进行分析。

2、init_IRQ();//同样是在start_kernel函数中int_IRQ调用函数arch_init_irq,用来初始化中断全局参数irq_desc[irq]。

for:

void __init mips_cpu_irq_init(void)
{
	int irq_base = MIPS_CPU_IRQ_BASE;
	int i;

	/* Mask interrupts. */
	clear_c0_status(ST0_IM);
	clear_c0_cause(CAUSEF_IP);

	/*
	 * Only MT is using the software interrupts currently, so we just
	 * leave them uninitialized for other processors.
	 */
	if (cpu_has_mipsmt)
		for (i = irq_base; i < irq_base + 2; i++)
			set_irq_chip(i, &mips_mt_cpu_irq_controller);

	for (i = irq_base + 2; i < irq_base + 8; i++)
		set_irq_chip_and_handler(i, &mips_cpu_irq_controller,
					 handle_level_irq);
}

这个是mips_cpu的中断,也可以根据自己的CPU注册中断。

for:

static void  init_my_irqs(void)
{
	int i;
	for(i = 0;i<32;i++){
		irq_desc[i].status = IRQ_DISABLED;
		irq_desc[i].action = 0;
		irq_desc[i].depth = 1;
		irq_desc[i].chip = &my_irq_type;
	}
	/* enable the  interrupt line */
	IntEnable();
}

至此硬件中断初始化结束。


2、中断注册:

1、request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev);这个用来注册中断

irq:中断号

handler:中断函数句柄

flag:中断处理属性flag定义在include/linux/interrupt.h

/*
* These flags used only by the kernel as part of the
* irq handling routines.
*
* IRQF_DISABLED - keep irqs disabled when calling the action handler
* IRQF_SAMPLE_RANDOM - irq is used to feed the random generator
* IRQF_SHARED - allow sharing the irq among several devices
* IRQF_PROBE_SHARED - set by callers when they expect sharing mismatches to occur
* IRQF_TIMER - Flag to mark this interrupt as timer interrupt
* IRQF_PERCPU - Interrupt is per cpu
* IRQF_NOBALANCING - Flag to exclude this interrupt from irq balancing
* IRQF_IRQPOLL - Interrupt is used for polling (only the interrupt that is
*                registered first in an shared interrupt is considered for
*                performance reasons)
*/
#define IRQF_DISABLED           0x00000020       //独享中断号相当于早期的SA_INTERRUPT
#define IRQF_SAMPLE_RANDOM      0x00000040
#define IRQF_SHARED             0x00000080       //共享中断号相当于早期的SA_SHIRE
#define IRQF_PROBE_SHARED       0x00000100
#define IRQF_TIMER              0x00000200
#define IRQF_PERCPU             0x00000400
#define IRQF_NOBALANCING        0x00000800
#define IRQF_IRQPOLL            0x00001000
name:中断名字

dev:设备,这个跟共享中断有关系。独享的话为NULLfor:

    if(request_irq(OPL_UART1_IRQ, &uartInterrupt,IRQF_DISABLED, "uart1", NULL)) {
        unregister_chrdev(UART1_MAJOR, UFILE_NAME);	
	    return -1;
    }

注册串口1中断,其中中断处理函数为uartInterrupt,独享中断。

2、int setup_irq(unsigned int irq, struct irqaction *new)//这是另外一个注册中断函数

其实在request_irq中也调用了这个函数,在request_irq中其实就多了一个action = kmalloc(sizeof(struct irqaction), GFP_ATOMIC);以及一些安全判断。

我们注意到timer中断使用setup_irq(mips_cpu_timer_irq, irq);那是因为在调用这个函数的时候,还没发调用kmalloc函数,否则会导致内核挂起,kmalloc函数需要mem_init();kmem_cache_init();这两个函数被调用后才行。在此函数的最后几行,会注册此中断相对应的/proc/irq/(num)/(name)的虚拟文件系统,方便在应用层观察中断信息

3、中断响应:

好了,现在重头戏来了,中断响应。

handle_int   ->plat_irq_dispatch->malta_hw0_irqdispatch->do_IRQ(irq)->generic_handle_irq -> __do_IRQ()  ->hanle_IRQ_event-> irq_exit

1、handle_init

	NESTED(handle_int, PT_SIZE, sp)

#ifdef CONFIG_TRACE_IRQFLAGS
	/*
	 * Check to see if the interrupted code has just disabled
	 * interrupts and ignore this interrupt for now if so.
	 *
	 * local_irq_disable() disables interrupts and then calls
	 * trace_hardirqs_off() to track the state. If an interrupt is taken
	 * after interrupts are disabled but before the state is updated
	 * it will appear to restore_all that it is incorrectly returning with
	 * interrupts disabled
	 */
	.set	push
	.set	noat
	mfc0	k0, CP0_STATUS
#if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX)
	and	k0, ST0_IEP
	bnez	k0, 1f

	mfc0	k0, EP0_EPC
	.set	noreorder
	j	k0
	rfe
#else
	and	k0, ST0_IE
	bnez	k0, 1f

	eret
#endif
1:
	.set pop
#endif
	SAVE_ALL
	CLI
	TRACE_IRQS_OFF

	LONG_L	s0, TI_REGS($28)
	LONG_S	sp, TI_REGS($28)
	PTR_LA	ra, ret_from_irq
	j	plat_irq_dispatch	//跳入plat_irq_patch
	END(handle_int)

	__INIT
plat_irq_patch
asmlinkage void plat_irq_dispatch()
{
	int i;
	int pending = read_c0_status() & read_c0_cause();

	//printk("%s, %s\n", __FILE__, __FUNCTION__);
	if(pending & CAUSEF_IP2)     /*IP2:处理芯片内部中断*/
		malta_hw0_irqdispatch();
	else {
		for(i = 7; i>0; i--)
			if(pending & (0x100<<i)) do_IRQ(i);
	}
	spurious_interrupt();
}
在malta-hw0_irqdispatch函数中根据中断状态寄存器来判断irq号,进入do_IRQ(irq);
#define do_IRQ(irq)							\
do {									\
	irq_enter();							\
	__DO_IRQ_SMTC_HOOK(irq);					\
	generic_handle_irq(irq);					\
	irq_exit();							\
} while (0)
generic_handle_irq(irq)为处理中断上半部分的顶部中断,进入__do_IRQ(irq)
fastcall unsigned int __do_IRQ(unsigned int irq)
{	
kstat_this_cpu.irqs[irq]++;    //这个变量在/proc/interrupt文件中显示成为进入中断次数统计
...........
for (;;) {
		irqreturn_t action_ret;

		spin_unlock(&desc->lock);

		action_ret = handle_IRQ_event(irq, action);     //中断处理函数
		if (!noirqdebug)
			note_interrupt(irq, desc, action_ret);

		spin_lock(&desc->lock);
		if (likely(!(desc->status & IRQ_PENDING)))	
			break;
		desc->status &= ~IRQ_PENDING;
	}
..........
}
接下来进入hanle_IRQ_event
irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action)
{
	irqreturn_t ret, retval = IRQ_NONE;
	unsigned int status = 0;

	handle_dynamic_tick(action);

	//printk("%s,%s\n",__FILE__,__FUNCTION__,__LINE__);

	if (!(action->flags & IRQF_DISABLED))
		local_irq_enable_in_hardirq();      //如果为共享中断,则开启全局中断

	do {
		//printk("%s,%s,%s,irq=%d,handler=0x%x\n",__FILE__,__FUNCTION__,__LINE__, action->name, 
		//	action->irq);
		ret = action->handler(irq, action->dev_id);
		if (ret == IRQ_HANDLED)
			status |= action->flags;
		retval |= ret;
		action = action->next;
	} while (action);       //共享中断的话,要一个个执行过来

	if (status & IRQF_SAMPLE_RANDOM)
		add_interrupt_randomness(irq);
	local_irq_disable(); //在返回__do_IRQ前必须关中断,前面共享中断有可能开启全局中断,
			     //且action->flag有可能在中途改变,故这里强行关会比较安全

	return retval;

}

至此上半部分中断已经处理完毕irq_exit为下半部分中断即为软中断,这里不多加说明了,以后有时间在分析。


注:由于水平有限,可能有些地方有错误,或不清楚,还望多多见谅、指出。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值