字符设备驱动(三)中断框架

字符设备驱动(三)中断框架

引入

mark

裸机中断流程

  1. 外部触发
  2. CPU 发生中断, 强制的跳到异常向量处
  3. 跳转到具体函数
    1. 保存被中断处的现场(各种寄存器的值)。
    2. 处理具体任务
    3. 恢复被中断的现场

LINUX流程

ARM 架构的 CPU 的异常向量基址可以是 0x0000 0000,也可以是 0xffff0000,这个地址并不代表实际的内存,是虚拟地址.当建立了虚拟地址与物理地址间的映射后,得将那些异常向量,即相当于把那些跳转指令复制拷贝到这个 0xffff0000 这个地址处去。

汇编处理

mark

trap_init中实现(start_kernel中调用)

// arch/arm/kernel/traps.c
void __init trap_init(void)
{
	unsigned long vectors = CONFIG_VECTORS_BASE;
	extern char __stubs_start[], __stubs_end[];
	extern char __vectors_start[], __vectors_end[];
	extern char __kuser_helper_start[], __kuser_helper_end[];
	int kuser_sz = __kuser_helper_end - __kuser_helper_start;

	/*
	 * Copy the vectors, stubs and kuser helpers (in entry-armv.S)
	 * into the vector page, mapped at 0xffff0000, and ensure these
	 * are visible to the instruction stream.
	 */
	memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);
	memcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start);
	memcpy((void *)vectors + 0x1000 - kuser_sz, __kuser_helper_start, kuser_sz);

	/*
	 * Copy signal return handlers into the vector page, and
	 * set sigreturn to be a pointer to these.
	 */
	memcpy((void *)KERN_SIGRETURN_CODE, sigreturn_codes,
	       sizeof(sigreturn_codes));

	flush_icache_range(vectors, vectors + PAGE_SIZE);
	modify_domain(DOMAIN_USER, DOMAIN_CLIENT);
}

拷贝向量表

memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);
  1. vectors=CONFIG_VECTORS_BASE,是配置项,,在最开始的内核配置.config中定义为0xffff0000

  2. __vectors_startarch/arm/kernel/entry-armv.S中定义,是一段跳转指令,很明显是中断跳转指令

	.globl	__vectors_start
__vectors_start:
	swi	SYS_ERROR0
	b	vector_und + stubs_offset
	ldr	pc, .LCvswi + stubs_offset
	b	vector_pabt + stubs_offset
	b	vector_dabt + stubs_offset
	b	vector_addrexcptn + stubs_offset
	b	vector_irq + stubs_offset
	b	vector_fiq + stubs_offset

	.globl	__vectors_end
__vectors_end:

向量表宏解析

其中的vector_und等都是一个宏,搜索代码是搜索不到的.本质就是保护现场,设置管理模式,然后跳转

	.macro	vector_stub, name, mode, correction=0
	.align	5
	
vector_\name:
	.if \correction
	sub	lr, lr, #\correction
	.endif

	@
	@ Save r0, lr_<exception> (parent PC) and spsr_<exception>
	@ (parent CPSR)
	@
	stmia	sp, {r0, lr}		@ save r0, lr
	mrs	lr, spsr
	str	lr, [sp, #8]		@ save spsr

	@
	@ Prepare for SVC32 mode.  IRQs remain disabled.
	@
	mrs	r0, cpsr
	eor	r0, r0, #(\mode ^ SVC_MODE)
	msr	spsr_cxsf, r0

	@
	@ the branch table must immediately follow this code
	@
	and	lr, lr, #0x0f
	mov	r0, sp
	ldr	lr, [pc, lr, lsl #2]
	movs	pc, lr			@ branch to handler in SVC mode
	.endm

尝试展开这个宏

	vector_stub	und, UND_MODE

	.long	__und_usr			@  0 (USR_26 / USR_32)
	.long	__und_invalid			@  1 (FIQ_26 / FIQ_32)
	.long	__und_invalid			@  2 (IRQ_26 / IRQ_32)
	.long	__und_svc			@  3 (SVC_26 / SVC_32)
	.long	__und_invalid			@  4
	.long	__und_invalid			@  5
	.long	__und_invalid			@  6
	.long	__und_invalid			@  7
	.long	__und_invalid			@  8
	.long	__und_invalid			@  9
	.long	__und_invalid			@  a
	.long	__und_invalid			@  b
	.long	__und_invalid			@  c
	.long	__und_invalid			@  d
	.long	__und_invalid			@  e
	.long	__und_invalid			@  f

	.align	5

展开如下:

vector_und:

	@
	@ Save r0, lr_<exception> (parent PC) and spsr_<exception>
	@ (parent CPSR)
	@
	stmia	sp, {r0, lr}		@ save r0, lr
	mrs	lr, spsr
	str	lr, [sp, #8]		@ save spsr

	@
	@ Prepare for SVC32 mode.  IRQs remain disabled.
	@
	mrs	r0, cpsr
	eor	r0, r0, #(\mode ^ SVC_MODE)
	msr	spsr_cxsf, r0

	@
	@ the branch table must immediately follow this code
	@
	and	lr, lr, #0x0f
	mov	r0, sp
	ldr	lr, [pc, lr, lsl #2]
	movs	pc, lr			@ branch to handler in SVC mode
	
	
	.long	__und_usr			@  0 (USR_26 / USR_32)
	.long	__und_invalid			@  1 (FIQ_26 / FIQ_32)
	.long	__und_invalid			@  2 (IRQ_26 / IRQ_32)
	.long	__und_svc			@  3 (SVC_26 / SVC_32)
	.long	__und_invalid			@  4
	.long	__und_invalid			@  5
	.long	__und_invalid			@  6
	.long	__und_invalid			@  7
	.long	__und_invalid			@  8
	.long	__und_invalid			@  9
	.long	__und_invalid			@  a
	.long	__und_invalid			@  b
	.long	__und_invalid			@  c
	.long	__und_invalid			@  d
	.long	__und_invalid			@  e
	.long	__und_invalid			@  f

	.align	5

展开下中断跳转的宏vector_irq,这里比未定义指令异常增加了先计算lr返回地址lr=lr-4

# 	vector_stub	irq, IRQ_MODE, 4
# 	correction=4

vector_irq:
	@.if \correction
	@sub	lr, lr, #\correction
	@.endif
	if #4
	sub lr,lr,#4
	endif

	@
	@ Save r0, lr_<exception> (parent PC) and spsr_<exception>
	@ (parent CPSR)
	@
	stmia	sp, {r0, lr}		@ save r0, lr
	mrs	lr, spsr
	str	lr, [sp, #8]		@ save spsr

	@
	@ Prepare for SVC32 mode.  IRQs remain disabled.
	@
	mrs	r0, cpsr
	eor	r0, r0, #(\mode ^ SVC_MODE)
	msr	spsr_cxsf, r0

	@
	@ the branch table must immediately follow this code
	@
	and	lr, lr, #0x0f
	mov	r0, sp
	ldr	lr, [pc, lr, lsl #2]
	movs	pc, lr			@ branch to handler in SVC mode

# 跳转地址
	.long	__irq_usr			@  0  (USR_26 / USR_32)
	.long	__irq_invalid			@  1  (FIQ_26 / FIQ_32)
	.long	__irq_invalid			@  2  (IRQ_26 / IRQ_32)
	.long	__irq_svc			@  3  (SVC_26 / SVC_32)
	.long	__irq_invalid			@  4
	.long	__irq_invalid			@  5
	.long	__irq_invalid			@  6
	.long	__irq_invalid			@  7
	.long	__irq_invalid			@  8
	.long	__irq_invalid			@  9
	.long	__irq_invalid			@  a
	.long	__irq_invalid			@  b
	.long	__irq_invalid			@  c
	.long	__irq_invalid			@  d
	.long	__irq_invalid			@  e
	.long	__irq_invalid			@  f

跳转函数

宏的最后都有一个跳转,比如中段函数跳转到__irq_usr

__irq_usr:
	usr_entry

#ifdef CONFIG_TRACE_IRQFLAGS
	bl	trace_hardirqs_off
#endif
	get_thread_info tsk
#ifdef CONFIG_PREEMPT
	ldr	r8, [tsk, #TI_PREEMPT]		@ get preempt count
	add	r7, r8, #1			@ increment it
	str	r7, [tsk, #TI_PREEMPT]
#endif

	irq_handler
#ifdef CONFIG_PREEMPT
	ldr	r0, [tsk, #TI_PREEMPT]
	str	r8, [tsk, #TI_PREEMPT]
	teq	r0, r7
	strne	r0, [r0, -r0]
#endif
#ifdef CONFIG_TRACE_IRQFLAGS
	bl	trace_hardirqs_on
#endif

	mov	why, #0
	b	ret_to_user

	.ltorg

	.align	5

其中usr_entry用于用户模式下发生中断时初始化中断处理堆栈,同时保存所有SVC态寄存器到堆栈。

	.macro	usr_entry
	sub	sp, sp, #S_FRAME_SIZE
	stmib	sp, {r1 - r12}

	ldmia	r0, {r1 - r3}
	add	r0, sp, #S_PC		@ here for interlock avoidance
	mov	r4, #-1			@  ""  ""     ""        ""

	str	r1, [sp]		@ save the "real" r0 copied
					@ from the exception stack

#if __LINUX_ARM_ARCH__ < 6 && !defined(CONFIG_NEEDS_SYSCALL_FOR_CMPXCHG)
#ifndef CONFIG_MMU
#warning "NPTL on non MMU needs fixing"
#else
	@ make sure our user space atomic helper is aborted
	cmp	r2, #TASK_SIZE
	bichs	r3, r3, #PSR_Z_BIT
#endif
#endif

	@
	@ We are now ready to fill in the remaining blanks on the stack:
	@
	@  r2 - lr_<exception>, already fixed up for correct return/restart
	@  r3 - spsr_<exception>
	@  r4 - orig_r0 (see pt_regs definition in ptrace.h)
	@
	@ Also, separately save sp_usr and lr_usr
	@
	stmia	r0, {r2 - r4}
	stmdb	r0, {sp, lr}^

	@
	@ Enable the alignment trap while in kernel mode
	@
	alignment_trap r0

	@
	@ Clear FP to mark the first stack frame
	@
	zero_fp
	.endm

irq_handler也是一个宏,最终会调用asm_do_IRQ,也就是我们的处理函数

 .macro	irq_handler
 get_irqnr_preamble r5, lr
1:	get_irqnr_and_base r0, r6, r5, lr
 movne	r1, sp
 @
 @ routine called with r0 = irq number, r1 = struct pt_regs *
 @
 adrne	lr, 1b
 bne	asm_do_IRQ

C函数处理

asm_do_IRQ

汇编处理流程最终会进入asm_do_IRQ处理

asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
{
	struct pt_regs *old_regs = set_irq_regs(regs);
	struct irq_desc *desc = irq_desc + irq;

	/*
	 * Some hardware gives randomly wrong interrupts.  Rather
	 * than crashing, do something sensible.
	 */
	if (irq >= NR_IRQS)
		desc = &bad_irq_desc;

	irq_enter();

	desc_handle_irq(irq, desc);

	/* AT91 specific workaround */
	irq_finish(irq);

	irq_exit();
	set_irq_regs(old_regs);
}

irq_desc是中断函数处理的数组,最终处理在desc_handle_irq(irq, desc); desc是中断全局数组,irq中断号.

static inline void desc_handle_irq(unsigned int irq, struct irq_desc *desc)
{
	desc->handle_irq(irq, desc);
}

也就是说,irq_desc是一个按照中断号为索引的结构体数组,里面存储各种信息包含中断入口函数handle_irq

struct irq_desc {
	irq_flow_handler_t	handle_irq;
	struct irq_chip		*chip;
	struct msi_desc		*msi_desc;
	void			*handler_data;
	void			*chip_data;
	struct irqaction	*action;	/* IRQ action list */
	unsigned int		status;		/* IRQ status */

	unsigned int		depth;		/* nested irq disables */
	unsigned int		wake_depth;	/* nested wake enables */
	unsigned int		irq_count;	/* For detecting broken IRQs */
	unsigned int		irqs_unhandled;
	spinlock_t		lock;
#ifdef CONFIG_SMP
	cpumask_t		affinity;
	unsigned int		cpu;
#endif
#if defined(CONFIG_GENERIC_PENDING_IRQ) || defined(CONFIG_IRQBALANCE)
	cpumask_t		pending_mask;
#endif
#ifdef CONFIG_PROC_FS
	struct proc_dir_entry	*dir;
#endif
	const char		*name;
} ____cacheline_internodealigned_in_smp;

mark

__set_irq_handler

那么执行函数handle_irq具体是指向了什么?kernel/irq/chip.c中设置的desc->handle_irq = handle;

void
__set_irq_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
		  const char *name)
{
	struct irq_desc *desc;
	unsigned long flags;

	if (irq >= NR_IRQS) {
		printk(KERN_ERR
		       "Trying to install type control for IRQ%d\n", irq);
		return;
	}

	desc = irq_desc + irq;

	if (!handle)
		handle = handle_bad_irq;
	else if (desc->chip == &no_irq_chip) {
		printk(KERN_WARNING "Trying to install %sinterrupt handler "
		       "for IRQ%d\n", is_chained ? "chained " : "", irq);
		/*
		 * Some ARM implementations install a handler for really dumb
		 * interrupt hardware without setting an irq_chip. This worked
		 * with the ARM no_irq_chip but the check in setup_irq would
		 * prevent us to setup the interrupt at all. Switch it to
		 * dummy_irq_chip for easy transition.
		 */
		desc->chip = &dummy_irq_chip;
	}

	spin_lock_irqsave(&desc->lock, flags);

	/* Uninstall? */
	if (handle == handle_bad_irq) {
		if (desc->chip != &no_irq_chip)
			mask_ack_irq(desc, irq);
		desc->status |= IRQ_DISABLED;
		desc->depth = 1;
	}
	desc->handle_irq = handle;
	desc->name = name;

	if (handle != handle_bad_irq && is_chained) {
		desc->status &= ~IRQ_DISABLED;
		desc->status |= IRQ_NOREQUEST | IRQ_NOPROBE;
		desc->depth = 0;
		desc->chip->unmask(irq);
	}
	spin_unlock_irqrestore(&desc->lock, flags);
}

函数__set_irq_handlerset_irq_handler调用

static inline void
set_irq_handler(unsigned int irq, irq_flow_handler_t handle)
{
	__set_irq_handler(irq, handle, 0, NULL);
}

s3c24xx_init_irq

set_irq_handler被多处调用,比如在arch/arm/plat-s3c24xx/irq.c中的s3c24xx_init_irq

void __init s3c24xx_init_irq(void)


....
		case IRQ_EINT4t7:
		case IRQ_EINT8t23:
		case IRQ_UART0:
		case IRQ_UART1:
		case IRQ_UART2:
		case IRQ_ADCPARENT:
			set_irq_chip(irqno, &s3c_irq_level_chip);
			set_irq_handler(irqno, handle_level_irq);
			break;

		case IRQ_RESERVED6:
		case IRQ_RESERVED24:
			/* no IRQ here */
			break;

....
    set_irq_chained_handler(IRQ_EINT4t7, s3c_irq_demux_extint4t7);
	set_irq_chained_handler(IRQ_EINT8t23, s3c_irq_demux_extint8);

	set_irq_chained_handler(IRQ_UART0, s3c_irq_demux_uart0);
	set_irq_chained_handler(IRQ_UART1, s3c_irq_demux_uart1);
	set_irq_chained_handler(IRQ_UART2, s3c_irq_demux_uart2);
	set_irq_chained_handler(IRQ_ADCPARENT, s3c_irq_demux_adc);

...
	for (irqno = IRQ_EINT4; irqno <= IRQ_EINT23; irqno++) {
		irqdbf("registering irq %d (extended s3c irq)\n", irqno);
		set_irq_chip(irqno, &s3c_irqext_chip);
		set_irq_handler(irqno, handle_edge_irq); ..............设置中断handler
		set_irq_flags(irqno, IRQF_VALID);
	}

比如我们的外部中断0处理

#define IRQ_EINT0      S3C2410_IRQ(0)	    /* 16 */
for (irqno = IRQ_EINT0; irqno <= IRQ_EINT3; irqno++) {
	irqdbf("registering irq %d (ext int)\n", irqno);
	set_irq_chip(irqno, &s3c_irq_eint0t4);
	set_irq_handler(irqno, handle_edge_irq);
	set_irq_flags(irqno, IRQF_VALID);
}

handle_edge_irq

也就是填充这个全局结构数组irq_descIRQ_EINT0项中的相关信息,这里就是填充数组16~19.也就是中断最终会调用注册的handle_irq.在中断0~3中,注册了函数handle_edge_irqhandle_irq,边沿中断处理

///kernel/irq/chip.c
void fastcall
handle_edge_irq(unsigned int irq, struct irq_desc *desc)
{
	const unsigned int cpu = smp_processor_id();

	spin_lock(&desc->lock);

	desc->status &= ~(IRQ_REPLAY | IRQ_WAITING);

	/*
	 * If we're currently running this IRQ, or its disabled,
	 * we shouldn't process the IRQ. Mark it pending, handle
	 * the necessary masking and go out
	 */
	if (unlikely((desc->status & (IRQ_INPROGRESS | IRQ_DISABLED)) ||
		    !desc->action)) {
		desc->status |= (IRQ_PENDING | IRQ_MASKED);
		mask_ack_irq(desc, irq);
		goto out_unlock;
	}

	kstat_cpu(cpu).irqs[irq]++;

	/* Start handling the irq */
	desc->chip->ack(irq);

	/* Mark the IRQ currently in progress.*/
	desc->status |= IRQ_INPROGRESS;

	do {
		struct irqaction *action = desc->action;
		irqreturn_t action_ret;

		if (unlikely(!action)) {
			desc->chip->mask(irq);
			goto out_unlock;
		}

		/*
		 * When another irq arrived while we were handling
		 * one, we could have masked the irq.
		 * Renable it, if it was not disabled in meantime.
		 */
		if (unlikely((desc->status &
			       (IRQ_PENDING | IRQ_MASKED | IRQ_DISABLED)) ==
			      (IRQ_PENDING | IRQ_MASKED))) {
			desc->chip->unmask(irq);
			desc->status &= ~IRQ_MASKED;
		}

		desc->status &= ~IRQ_PENDING;
		spin_unlock(&desc->lock);
		action_ret = handle_IRQ_event(irq, action);
		if (!noirqdebug)
			note_interrupt(irq, desc, action_ret);
		spin_lock(&desc->lock);

	} while ((desc->status & (IRQ_PENDING | IRQ_DISABLED)) == IRQ_PENDING);

	desc->status &= ~IRQ_INPROGRESS;
out_unlock:
	spin_unlock(&desc->lock);
}

desc->chip->ack(irq);是之前设置的set_irq_chip(irqno, &s3c_irq_eint0t4);查找结构s3c_irq_eint0t4,具体也就是清中断了

static struct irq_chip s3c_irq_eint0t4 = {
	.name		= "s3c-ext0",
	.ack		= s3c_irq_ack,
	.mask		= s3c_irq_mask,
	.unmask		= s3c_irq_unmask,
	.set_wake	= s3c_irq_wake,
	.set_type	= s3c_irqext_type,
};
static inline void
s3c_irq_ack(unsigned int irqno)
{
	unsigned long bitval = 1UL << (irqno - IRQ_EINT0);

	__raw_writel(bitval, S3C2410_SRCPND);
	__raw_writel(bitval, S3C2410_INTPND);
}
static inline void
s3c_irq_ack(unsigned int irqno)
{
	unsigned long bitval = 1UL << (irqno - IRQ_EINT0);

	__raw_writel(bitval, S3C2410_SRCPND);
	__raw_writel(bitval, S3C2410_INTPND);
}
extern void		__raw_writel(u32 b, volatile void __iomem *addr);

handle_IRQ_event

接下来重点函数handle_IRQ_event,链表操作,取出action的链表成员,执行action->handle

struct irqaction *action = desc->action;
action_ret = handle_IRQ_event(irq, action);

desc->action

接着去分析action这个链表,它是desc的一个成员

struct irq_desc {
    irq_flow_handler_t	handle_irq;
	struct irq_chip		*chip;   //底层芯片相关的函数,清中断,开关使能等
    struct irqaction	*action;	/* IRQ action list */
    ...
}    
struct irqaction {
	irq_handler_t handler;
	unsigned long flags;
	cpumask_t mask;
	const char *name;
	void *dev_id;
	struct irqaction *next;
	int irq;
	struct proc_dir_entry *dir;
};

mark

request_irq

action函数链表由request_irq设置kernel/irq/manage.c,完成了中断引脚的设置使能等

irq:中断号
handler:处理函数
irqflags:上升沿触发,下降沿触发,边沿触发等。指定了快速中断或中断共享等中断处理属性.
*devname:中断名字。通常是设备驱动程序的名称。改值用在 /proc/interrupt 系统 (虚拟)
文件上,或内核发生中断错误时使用。
dev_id 可作为共享中断时的中断区别参数,也可以用来指定中断服务函数需要参考的数据地址。也用于卸载action
int request_irq(unsigned int irq, irq_handler_t handler,
		unsigned long irqflags, const char *devname, void *dev_id)
{
	struct irqaction *action;
	int retval;

#ifdef CONFIG_LOCKDEP
	/*
	 * Lockdep wants atomic interrupt handlers:
	 */
	irqflags |= IRQF_DISABLED;
#endif
	/*
	 * Sanity-check: shared interrupts must pass in a real dev-ID,
	 * otherwise we'll have trouble later trying to figure out
	 * which interrupt is which (messes up the interrupt freeing
	 * logic etc).
	 */
	if ((irqflags & IRQF_SHARED) && !dev_id)
		return -EINVAL;
	if (irq >= NR_IRQS)
		return -EINVAL;
	if (irq_desc[irq].status & IRQ_NOREQUEST)
		return -EINVAL;
	if (!handler)
		return -EINVAL;

	action = kmalloc(sizeof(struct irqaction), GFP_ATOMIC);
	if (!action)
		return -ENOMEM;

	action->handler = handler;
	action->flags = irqflags;
	cpus_clear(action->mask);
	action->name = devname;
	action->next = NULL;
	action->dev_id = dev_id;

	select_smp_affinity(irq);

#ifdef CONFIG_DEBUG_SHIRQ
	if (irqflags & IRQF_SHARED) {
		/*
		 * It's a shared IRQ -- the driver ought to be prepared for it
		 * to happen immediately, so let's make sure....
		 * We do this before actually registering it, to make sure that
		 * a 'real' IRQ doesn't run in parallel with our fake
		 */
		if (irqflags & IRQF_DISABLED) {
			unsigned long flags;

			local_irq_save(flags);
			handler(irq, dev_id);
			local_irq_restore(flags);
		} else
			handler(irq, dev_id);
	}
#endif

	retval = setup_irq(irq, action);
	if (retval)
		kfree(action);

	return retval;
}

这里分配一个action成员,将传递的参数赋值到action的成员然后使用setup_irq(irq, action);设置

setup_irq

判断是否为共享中断,所谓共享中断也就是中断源一样的中断,共享中断只能加入共享中断的链表中.接着设置引脚等

int setup_irq(unsigned int irq, struct irqaction *new)
{
	struct irq_desc *desc = irq_desc + irq;
	struct irqaction *old, **p;
	const char *old_name = NULL;
	unsigned long flags;
	int shared = 0;

	if (irq >= NR_IRQS)
		return -EINVAL;

	if (desc->chip == &no_irq_chip)
		return -ENOSYS;
	/*
	 * Some drivers like serial.c use request_irq() heavily,
	 * so we have to be careful not to interfere with a
	 * running system.
	 */
	if (new->flags & IRQF_SAMPLE_RANDOM) {
		/*
		 * This function might sleep, we want to call it first,
		 * outside of the atomic block.
		 * Yes, this might clear the entropy pool if the wrong
		 * driver is attempted to be loaded, without actually
		 * installing a new handler, but is this really a problem,
		 * only the sysadmin is able to do this.
		 */
		rand_initialize_irq(irq);
	}

	/*
	 * The following block of code has to be executed atomically
	 */
	spin_lock_irqsave(&desc->lock, flags);
	p = &desc->action;
	old = *p;
	if (old) {
		/*
		 * Can't share interrupts unless both agree to and are
		 * the same type (level, edge, polarity). So both flag
		 * fields must have IRQF_SHARED set and the bits which
		 * set the trigger type must match.
		 */
		if (!((old->flags & new->flags) & IRQF_SHARED) ||
		    ((old->flags ^ new->flags) & IRQF_TRIGGER_MASK)) {
			old_name = old->name;
			goto mismatch;
		}

#if defined(CONFIG_IRQ_PER_CPU)
		/* All handlers must agree on per-cpuness */
		if ((old->flags & IRQF_PERCPU) !=
		    (new->flags & IRQF_PERCPU))
			goto mismatch;
#endif

		/* add new interrupt at end of irq queue */
		do {
			p = &old->next;
			old = *p;
		} while (old);
		shared = 1;
	}

	*p = new;

	/* Exclude IRQ from balancing */
	if (new->flags & IRQF_NOBALANCING)
		desc->status |= IRQ_NO_BALANCING;

	if (!shared) {
		irq_chip_set_defaults(desc->chip);

#if defined(CONFIG_IRQ_PER_CPU)
		if (new->flags & IRQF_PERCPU)
			desc->status |= IRQ_PER_CPU;
#endif

		/* Setup the type (level, edge polarity) if configured: */
		if (new->flags & IRQF_TRIGGER_MASK) {
			if (desc->chip && desc->chip->set_type)
				desc->chip->set_type(irq,
						new->flags & IRQF_TRIGGER_MASK);
			else
				/*
				 * IRQF_TRIGGER_* but the PIC does not support
				 * multiple flow-types?
				 */
				printk(KERN_WARNING "No IRQF_TRIGGER set_type "
				       "function for IRQ %d (%s)\n", irq,
				       desc->chip ? desc->chip->name :
				       "unknown");
		} else
			compat_irq_chip_set_default_handler(desc);

		desc->status &= ~(IRQ_AUTODETECT | IRQ_WAITING |
				  IRQ_INPROGRESS);

		if (!(desc->status & IRQ_NOAUTOEN)) {
			desc->depth = 0;
			desc->status &= ~IRQ_DISABLED;
			if (desc->chip->startup)
				desc->chip->startup(irq);
			else
				desc->chip->enable(irq);
		} else
			/* Undo nested disables: */
			desc->depth = 1;
	}
	/* Reset broken irq detection when installing new handler */
	desc->irq_count = 0;
	desc->irqs_unhandled = 0;
	spin_unlock_irqrestore(&desc->lock, flags);

	new->irq = irq;
	register_irq_proc(irq);
	new->dir = NULL;
	register_handler_proc(irq, new);

	return 0;

mismatch:
#ifdef CONFIG_DEBUG_SHIRQ
	if (!(new->flags & IRQF_PROBE_SHARED)) {
		printk(KERN_ERR "IRQ handler type mismatch for IRQ %d\n", irq);
		if (old_name)
			printk(KERN_ERR "current handler: %s\n", old_name);
		dump_stack();
	}
#endif
	spin_unlock_irqrestore(&desc->lock, flags);
	return -EBUSY;
}

这里desc->chip->set_type调用设置中断的引脚寄存器等,是在初始化中s3c24xx_init_irq赋值过的chip,通过request_irq传递的flag指定触发模式等

static struct irq_chip s3c_irqext_chip = {
	.name		= "s3c-ext",
	.mask		= s3c_irqext_mask,
	.unmask		= s3c_irqext_unmask,
	.ack		= s3c_irqext_ack,
	.set_type	= s3c_irqext_type,
	.set_wake	= s3c_irqext_wake
};

static struct irq_chip s3c_irq_eint0t4 = {
	.name		= "s3c-ext0",
	.ack		= s3c_irq_ack,
	.mask		= s3c_irq_mask,
	.unmask		= s3c_irq_unmask,
	.set_wake	= s3c_irq_wake,
	.set_type	= s3c_irqext_type,
};

int
s3c_irqext_type(unsigned int irq, unsigned int type)
{
	void __iomem *extint_reg;
	void __iomem *gpcon_reg;
	unsigned long gpcon_offset, extint_offset;
	unsigned long newvalue = 0, value;

	if ((irq >= IRQ_EINT0) && (irq <= IRQ_EINT3))
	{
		gpcon_reg = S3C2410_GPFCON;
		extint_reg = S3C24XX_EXTINT0;
		gpcon_offset = (irq - IRQ_EINT0) * 2;
		extint_offset = (irq - IRQ_EINT0) * 4;
	}
	else if ((irq >= IRQ_EINT4) && (irq <= IRQ_EINT7))
	{
		gpcon_reg = S3C2410_GPFCON;
		extint_reg = S3C24XX_EXTINT0;
		gpcon_offset = (irq - (EXTINT_OFF)) * 2;
		extint_offset = (irq - (EXTINT_OFF)) * 4;
	}
	else if ((irq >= IRQ_EINT8) && (irq <= IRQ_EINT15))
	{
		gpcon_reg = S3C2410_GPGCON;
		extint_reg = S3C24XX_EXTINT1;
		gpcon_offset = (irq - IRQ_EINT8) * 2;
		extint_offset = (irq - IRQ_EINT8) * 4;
	}
	else if ((irq >= IRQ_EINT16) && (irq <= IRQ_EINT23))
	{
		gpcon_reg = S3C2410_GPGCON;
		extint_reg = S3C24XX_EXTINT2;
		gpcon_offset = (irq - IRQ_EINT8) * 2;
		extint_offset = (irq - IRQ_EINT16) * 4;
	} else
		return -1;

	/* Set the GPIO to external interrupt mode */
	value = __raw_readl(gpcon_reg);
	value = (value & ~(3 << gpcon_offset)) | (0x02 << gpcon_offset);
	__raw_writel(value, gpcon_reg);

	/* Set the external interrupt to pointed trigger type */
	switch (type)
	{
		case IRQT_NOEDGE:
			printk(KERN_WARNING "No edge setting!\n");
			break;

		case IRQT_RISING:
			newvalue = S3C2410_EXTINT_RISEEDGE;
			break;

		case IRQT_FALLING:
			newvalue = S3C2410_EXTINT_FALLEDGE;
			break;

		case IRQT_BOTHEDGE:
			newvalue = S3C2410_EXTINT_BOTHEDGE;
			break;

		case IRQT_LOW:
			newvalue = S3C2410_EXTINT_LOWLEV;
			break;

		case IRQT_HIGH:
			newvalue = S3C2410_EXTINT_HILEV;
			break;

		default:
			printk(KERN_ERR "No such irq type %d", type);
			return -1;
	}

	value = __raw_readl(extint_reg);
	value = (value & ~(7 << extint_offset)) | (newvalue << extint_offset);
	__raw_writel(value, extint_reg);

	return 0;
}

free_irq

关闭中断,也就是取消中断处理函数,在全局数组irq_desc寻找到对应的action链表,通过id将其从链表中删除.如果链表空了,则禁止中断

void free_irq(unsigned int irq, void *dev_id)

小结

流程

  1. start_kernel中调用trap_init 拷贝向量表到 0xffff0000
  2. 触发中断,进入中断向量表
  3. vector_irq跳转进入保存现场,跳转到__irq_usr
  4. __irq_usr调用usr_entry进行用户模式下发生中断时初始化中断处理堆栈,通过irq_handler调用asm_do_IRQ进入C函数处理
  5. asm_do_IRQ寻找irq_desc 数组,调用desc->handle_irq来执行irq_desc 数组中的入口函数handle_irq,这里外中断的话执行的是handle_edge_irq
  6. 中断入口函数清中断,执行action中的链表函数
  7. action中会判断是否为共享中断,设置中断类型,引脚寄存器等,同时开启中断
  8. 使用free_irq 卸载掉链表中的函数,如果链表函数空了则关中断

设置irq_desc 数组

  1. s3c24xx_init_irq中进行数组的初始化
  2. set_irq_chip初始化数组中的chip结构
  3. set_irq_handler设置中断入口函数handle_irq
  4. request_irq来创建action链表,分配空间,并加入链表
  5. struct irq_chip *chip; 这是底层芯片相关的函数,清中断,开关使能等

关键结构成员

struct irq_desc {
	irq_flow_handler_t	handle_irq;	//中断入口函数,在外中断指向了 handle_edge_irq
	struct irq_chip		*chip;		//芯片底层处理函数,指向s3c_irqex_chip
	struct irqaction	*action;	//具体执行函数的链表,会被handle_irq 入口函数使用的
}


struct irq_chip {
	const char	*name;
	unsigned int	(*startup)(unsigned int irq);	//启动中断
	void		(*shutdown)(unsigned int irq);		//关闭中断
	void		(*enable)(unsigned int irq);		//使能中断
	void		(*disable)(unsigned int irq);		//禁止中断

	void		(*ack)(unsigned int irq);			//响应中断,清中断
}

struct irqaction {
	irq_handler_t handler;							//action的执行函数
	unsigned long flags;							//标志,用于设置中断边沿等
};

mark

完整框架图

mark

流程一览

mark

设置向量表

mark

设置全局中断数组

mark

设置用户action动作

mark

posted @ 2018-11-22 19:47  zongzi10010  阅读(381)  评论(0编辑  收藏  举报