目录
title: 字符设备驱动(三)中断框架
tags: linux
date: 2018-11-22 18:58:22
toc: true
---
字符设备驱动(三)中断框架
引入
裸机中断流程
- 外部触发
- CPU 发生中断, 强制的跳到异常向量处
- 跳转到具体函数
- 保存被中断处的现场(各种寄存器的值)。
- 处理具体任务
- 恢复被中断的现场
LINUX流程
ARM 架构的 CPU 的异常向量基址可以是 0x0000 0000
,也可以是 0xffff0000
,这个地址并不代表实际的内存,是虚拟地址.当建立了虚拟地址与物理地址间的映射后,得将那些异常向量,即相当于把那些跳转指令复制拷贝到这个 0xffff0000
这个地址处去。
汇编处理
在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);
vectors=CONFIG_VECTORS_BASE
,是配置项,,在最开始的内核配置.config
中定义为0xffff0000
__vectors_start
在arch/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;
__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_handler
被set_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_desc
的IRQ_EINT0
项中的相关信息,这里就是填充数组16~19.也就是中断最终会调用注册的handle_irq
.在中断0~3
中,注册了函数handle_edge_irq
为handle_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; };
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)
小结
流程
start_kernel
中调用trap_init
拷贝向量表到0xffff0000
- 触发中断,进入中断向量表
vector_irq
跳转进入保存现场,跳转到__irq_usr
__irq_usr
调用usr_entry
进行用户模式下发生中断时初始化中断处理堆栈,通过irq_handler
调用asm_do_IRQ
进入C函数处理asm_do_IRQ
寻找irq_desc 数组
,调用desc->handle_irq
来执行irq_desc
数组中的入口函数handle_irq
,这里外中断的话执行的是handle_edge_irq
- 中断入口函数清中断,执行
action
中的链表函数 action
中会判断是否为共享中断,设置中断类型,引脚寄存器等,同时开启中断- 使用
free_irq
卸载掉链表中的函数,如果链表函数空了则关中断
设置irq_desc
数组
- 在
s3c24xx_init_irq
中进行数组的初始化 set_irq_chip
初始化数组中的chip
结构set_irq_handler
设置中断入口函数handle_irq
request_irq
来创建action
链表,分配空间,并加入链表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; //标志,用于设置中断边沿等 };
完整框架图
流程一览
设置向量表
设置全局中断数组
设置用户action动作
来源:https://www.cnblogs.com/zongzi10010/p/10003477.html