从零开始之驱动发开、linux驱动(十一、linux的中断框架)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_16777851/article/details/82564302

上一节我们学习了在linux中,中断的初始化流程以及调用流程。

本节我们对中断的框架进行说明。

开始之前我们先把前面说过的一个数据结构拿出来分析一下。

1.中断描述符


/**
 * struct irq_desc - interrupt descriptor
 * @irq_data:		per irq and chip data passed down to chip functions
 * @kstat_irqs:		irq stats per cpu
 * @handle_irq:		highlevel irq-events handler
 * @preflow_handler:	handler called before the flow handler (currently used by sparc)
 * @action:		the irq action chain
 * @status:		status information
 * @core_internal_state__do_not_mess_with_it: core internal status information
 * @depth:		disable-depth, for nested irq_disable() calls
 * @wake_depth:		enable depth, for multiple irq_set_irq_wake() callers
 * @irq_count:		stats field to detect stalled irqs
 * @last_unhandled:	aging timer for unhandled count
 * @irqs_unhandled:	stats field for spurious unhandled interrupts
 * @threads_handled:	stats field for deferred spurious detection of threaded handlers
 * @threads_handled_last: comparator field for deferred spurious detection of theraded handlers
 * @lock:		locking for SMP
 * @affinity_hint:	hint to user space for preferred irq affinity
 * @affinity_notify:	context for notification of affinity changes
 * @pending_mask:	pending rebalanced interrupts
 * @threads_oneshot:	bitfield to handle shared oneshot threads
 * @threads_active:	number of irqaction threads currently running
 * @wait_for_threads:	wait queue for sync_irq to wait for threaded handlers
 * @dir:		/proc/irq/ procfs entry
 * @name:		flow handler name for /proc/interrupts output
 */
struct irq_desc {                                    /* 中断描述符结构体 */
	struct irq_data		irq_data;            /* 每个irq和芯片数据传递给芯片功能 */
	unsigned int __percpu	*kstat_irqs;         /* 每个cpu的irq stats */
	irq_flow_handler_t	handle_irq;          /* 高级irq-events处理程序 */
#ifdef CONFIG_IRQ_PREFLOW_FASTEOI
	irq_preflow_handler_t	preflow_handler;
#endif
	struct irqaction	*action;	        /* IRQ action list irq动作链表 */
	unsigned int		status_use_accessors;   /* 状态信息 */ 
	unsigned int		core_internal_state__do_not_mess_with_it;   /* 核心内部状态信息 */
	unsigned int		depth;		        /* nested irq disables 用于嵌套的irq_disable() 调用*/
	unsigned int		wake_depth;	        /* nested wake enables 为多个irq_set_irq_wake()呼叫者启用深度*/
	unsigned int		irq_count;	        /* For detecting broken IRQs stats字段用于检测停滞的irqs*/
	unsigned long		last_unhandled;	        /* Aging timer for unhandled count 未处理计数的老化计时器*/
	unsigned int		irqs_unhandled;         /* 虚假未处理中断的统计字段 */
	atomic_t		threads_handled;        /* 用于线程处理程序的延迟虚假检测的stats字段 */
	int			threads_handled_last;   /* 比较器字段,用于对已处理的处理程序进行延迟虚假检测 */
	raw_spinlock_t		lock;               /* 锁定SMP */
	struct cpumask		*percpu_enabled;    
#ifdef CONFIG_SMP
	const struct cpumask	*affinity_hint;         /* 提示用户空间以获得首选的irq亲和力 */ 
	struct irq_affinity_notify *affinity_notify;    /* 用于通知亲和力变化的上下文 */
#ifdef CONFIG_GENERIC_PENDING_IRQ
	cpumask_var_t		pending_mask;               /* 待定重新平衡的中断 */
#endif
#endif
	unsigned long		threads_oneshot;            /* 用于处理共享的onehot线程的bitfield */
	atomic_t		threads_active;                 /* 当前正在运行的irqaction线程数 */
	wait_queue_head_t       wait_for_threads;           /* sync_irq的等待队列等待线程处理程序 */
#ifdef CONFIG_PROC_FS
	struct proc_dir_entry	*dir;                       /* / proc / irq / procfs条目 */
#endif
	int			parent_irq;
	struct module		*owner;
	const char		*name;                      /* / proc / interrupts输出的流程处理程序名称 */
} ____cacheline_internodealigned_in_smp;

#ifndef CONFIG_SPARSE_IRQ
extern struct irq_desc irq_desc[NR_IRQS];        /* 声明了NR_IRQS个irq描述符表 */
#endif

2.irq_data 


/**
 * struct irq_data - per irq and irq chip data passed down to chip functions
 * @mask:		precomputed bitmask for accessing the chip registers
 * @irq:		interrupt number
 * @hwirq:		hardware interrupt number, local to the interrupt domain
 * @node:		node index useful for balancing
 * @state_use_accessors: status information for irq chip functions.
 *			Use accessor functions to deal with it
 * @chip:		low level interrupt hardware access
 * @domain:		Interrupt translation domain; responsible for mapping
 *			between hwirq number and linux irq number.
 * @handler_data:	per-IRQ data for the irq_chip methods
 * @chip_data:		platform-specific per-chip private data for the chip
 *			methods, to allow shared chip implementations
 * @msi_desc:		MSI descriptor
 * @affinity:		IRQ affinity on SMP
 *
 * The fields here need to overlay the ones in irq_desc until we
 * cleaned up the direct references and switched everything over to
 * irq_data.
 */
struct irq_data {
	u32			mask;                        /* 预先计算的位掩码,用于访问芯片寄存器 */
	unsigned int		irq;                         /* 中断号 */
	unsigned long		hwirq;                       /* 硬件中断号,中断域本地 */
	unsigned int		node;                        /* 用于平衡的节点索引 */
	unsigned int		state_use_accessors;         /* irq芯片功能的状态信息。 */
	struct irq_chip		*chip;                       /* 低级中断硬件访问 */
	struct irq_domain	*domain;                     /* 中断翻译域; 负责制图 */
	void			*handler_data;               /* irq_chip方法的per-IRQ数据 */
	void			*chip_data;                  /* 芯片的平台特定的每芯片私有数据方法,允许共享芯片实现 */
	struct msi_desc		*msi_desc;                   /* MSI描述符 */
	cpumask_var_t		affinity;                    /* SMP上的IRQ亲和力 */
};

3.handle_irq中断处理函数,定义的是一个函数指针形式

typedef	void (*irq_flow_handler_t)(unsigned int irq,
					    struct irq_desc *desc);

4.irqaction ,真正的中断的动作行为

/* 函数指针,通常是我们要实现的 */
typedef irqreturn_t (*irq_handler_t)(int, void *);

/**
 * struct irqaction - per interrupt action descriptor
 * @handler:	interrupt handler function
 * @name:	name of the device
 * @dev_id:	cookie to identify the device
 * @percpu_dev_id:	cookie to identify the device
 * @next:	pointer to the next irqaction for shared interrupts
 * @irq:	interrupt number
 * @flags:	flags (see IRQF_* above)
 * @thread_fn:	interrupt handler function for threaded interrupts
 * @thread:	thread pointer for threaded interrupts
 * @thread_flags:	flags related to @thread
 * @thread_mask:	bitmask for keeping track of @thread activity
 * @dir:	pointer to the proc/irq/NN/name entry
 */
struct irqaction {                        /* 每个中断动作描述符 */
	irq_handler_t		handler;          /* 中断处理函数 */
	void			*dev_id;              /* 用于识别设备的cookie */
	void __percpu		*percpu_dev_id;   /* 用于识别设备的cookie */
	struct irqaction	*next;            /* 指向共享中断的下一个irqaction的指针 */
	irq_handler_t		thread_fn;        /* 用于线程中断的中断处理函数 */
	struct task_struct	*thread;          /* 线程中断的线程指针 */
	unsigned int		irq;              /* 中断号码 */
	unsigned int		flags;            /* flags(参见下面的IRQF_ *) */
	unsigned long		thread_flags;     /* 与@thread相关的标志 */
	unsigned long		thread_mask;      /* 用于跟踪@thread活动的位掩码 */
	const char		*name;                /* 设备名称 */
	struct proc_dir_entry	*dir;         /* 指向proc / irq / NN / name条目的指针 */
} ____cacheline_internodealigned_in_smp;

5.中断标志位


/* 下面几个是io相关的中断
 * These correspond to the IORESOURCE_IRQ_* defines in
 * linux/ioport.h to select the interrupt line behaviour.  When
 * requesting an interrupt without specifying a IRQF_TRIGGER, the
 * setting should be assumed to be "as already configured", which
 * may be as per machine or firmware initialisation.
 */
#define IRQF_TRIGGER_NONE	0x00000000
#define IRQF_TRIGGER_RISING	0x00000001        /* 上升沿中断 */
#define IRQF_TRIGGER_FALLING	0x00000002    /* 下降沿中断 */
#define IRQF_TRIGGER_HIGH	0x00000004        /* 高电平中断 */
#define IRQF_TRIGGER_LOW	0x00000008        /* 低电平中断 */
#define IRQF_TRIGGER_MASK	(IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW | \
				 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)
#define IRQF_TRIGGER_PROBE	0x00000010        

/*
 * These flags used only by the kernel as part of the
 * irq handling routines.
 *
 * IRQF_DISABLED - keep irqs disabled when calling the action handler.
 *                 DEPRECATED. This flag is a NOOP and scheduled to be removed
 * 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)
 * IRQF_ONESHOT - Interrupt is not reenabled after the hardirq handler finished.
 *                Used by threaded interrupts which need to keep the
 *                irq line disabled until the threaded handler has been run.
 * IRQF_NO_SUSPEND - Do not disable this IRQ during suspend
 * IRQF_FORCE_RESUME - Force enable it on resume even if IRQF_NO_SUSPEND is set
 * IRQF_NO_THREAD - Interrupt cannot be threaded
 * IRQF_EARLY_RESUME - Resume IRQ early during syscore instead of at device
 *                resume time.
 */
#define IRQF_DISABLED		0x00000020        /* 调用动作处理程序时禁用irqs,已弃用。此标志是NOOP并计划删除 */
#define IRQF_SHARED		0x00000080            /* 允许在多个设备之间共享irq */
#define IRQF_PROBE_SHARED	0x00000100        /* 当呼叫者期望发生共享不匹配时由呼叫者设置 */
#define __IRQF_TIMER		0x00000200        /* 将此中断标记为定时器中断的标志 */
#define IRQF_PERCPU		0x00000400            /* 中断是每个CPU */
#define IRQF_NOBALANCING	0x00000800        /* 用于从irq平衡中排除此中断的标志 */
#define IRQF_IRQPOLL		0x00001000        /* 中断用于轮询(仅限中断)考虑在共享中断中首先注册 */
#define IRQF_ONESHOT		0x00002000        /* 执行一次,在hardirq处理程序完成后,不会重新启用中断 */
#define IRQF_NO_SUSPEND		0x00004000        /* 暂停期间不要禁用此IRQ */
#define IRQF_FORCE_RESUME	0x00008000        /* 即使设置了IRQF_NO_SUSPEND,强制在恢复时启用它 */
#define IRQF_NO_THREAD		0x00010000        /* 中断无法进行线程化 */
#define IRQF_EARLY_RESUME	0x00020000        /* 在syscore而不是在设备期间提前恢复IRQ */

#define IRQF_TIMER		(__IRQF_TIMER | IRQF_NO_SUSPEND | IRQF_NO_THREAD)

6.chip

/**
 * struct irq_chip - hardware interrupt chip descriptor
 *
 * @name:		name for /proc/interrupts
 * @irq_startup:	start up the interrupt (defaults to ->enable if NULL)
 * @irq_shutdown:	shut down the interrupt (defaults to ->disable if NULL)
 * @irq_enable:		enable the interrupt (defaults to chip->unmask if NULL)
 * @irq_disable:	disable the interrupt
 * @irq_ack:		start of a new interrupt
 * @irq_mask:		mask an interrupt source
 * @irq_mask_ack:	ack and mask an interrupt source
 * @irq_unmask:		unmask an interrupt source
 * @irq_eoi:		end of interrupt
 * @irq_set_affinity:	set the CPU affinity on SMP machines
 * @irq_retrigger:	resend an IRQ to the CPU
 * @irq_set_type:	set the flow type (IRQ_TYPE_LEVEL/etc.) of an IRQ
 * @irq_set_wake:	enable/disable power-management wake-on of an IRQ
 * @irq_bus_lock:	function to lock access to slow bus (i2c) chips
 * @irq_bus_sync_unlock:function to sync and unlock slow bus (i2c) chips
 * @irq_cpu_online:	configure an interrupt source for a secondary CPU
 * @irq_cpu_offline:	un-configure an interrupt source for a secondary CPU
 * @irq_suspend:	function called from core code on suspend once per chip
 * @irq_resume:		function called from core code on resume once per chip
 * @irq_pm_shutdown:	function called from core code on shutdown once per chip
 * @irq_calc_mask:	Optional function to set irq_data.mask for special cases
 * @irq_print_chip:	optional to print special chip info in show_interrupts
 * @irq_request_resources:	optional to request resources before calling
 *				any other callback related to this irq
 * @irq_release_resources:	optional to release resources acquired with
 *				irq_request_resources
 * @flags:		chip specific flags
 */
struct irq_chip {                        /* 硬件中断芯片描述符 */
	const char	*name;                    /* / proc / interrupts的名称 */
	unsigned int	(*irq_startup)(struct irq_data *data);    /* 启动中断(默认为如果为NULL则启用) */
	void		(*irq_shutdown)(struct irq_data *data);       /* 关闭中断(默认为 - >禁用,如果为NULL) */
	void		(*irq_enable)(struct irq_data *data);        /* 启用中断(如果为NULL,则默认为chip-> unmask) */
	void		(*irq_disable)(struct irq_data *data);        /* 禁用中断 */
	void		(*irq_ack)(struct irq_data *data);            /* 开始新的中断 */
	void		(*irq_mask)(struct irq_data *data);           /* 屏蔽中断源 */
	void		(*irq_mask_ack)(struct irq_data *data);       /* 确认并屏蔽中断源 */
	void		(*irq_unmask)(struct irq_data *data);        /* 取消屏蔽中断源 */
	void		(*irq_eoi)(struct irq_data *data);            /* 中断结束 */

	int		(*irq_set_affinity)(struct irq_data *data, const struct cpumask *dest, bool force);        /* 在SMP机器上设置CPU亲和力 */
	int		(*irq_retrigger)(struct irq_data *data);        /* 向CPU重新发送IRQ */
	int		(*irq_set_type)(struct irq_data *data, unsigned int flow_type);    /* 设置IRQ的流类型(IRQ_TYPE_LEVEL / etc.) */
	int		(*irq_set_wake)(struct irq_data *data, unsigned int on);        /* 启用/禁用IRQ的电源管理唤醒 */

	void		(*irq_bus_lock)(struct irq_data *data);            /* 用于锁定对慢速总线(i2c)芯片的访问的功能 */
	void		(*irq_bus_sync_unlock)(struct irq_data *data);        /* 用于同步和解锁慢速总线(i2c)芯片的功能 */
	void		(*irq_cpu_online)(struct irq_data *data);           /* 为辅助CPU配置中断源 */ 
	void		(*irq_cpu_offline)(struct irq_data *data);        /* 取消配置辅助CPU的中断源 */
	void		(*irq_suspend)(struct irq_data *data);        /* 每个芯片暂停一次从核心代码调用的函数 */
	void		(*irq_resume)(struct irq_data *data);        /* 每个芯片在恢复时从核心代码调用的函数 */
	void		(*irq_pm_shutdown)(struct irq_data *data);    /* 每个芯片在关闭一次时从核心代码调用的函数 */
	void		(*irq_calc_mask)(struct irq_data *data);     /* 为特殊情况设置irq_data.mask的可选功能 */
	void		(*irq_print_chip)(struct irq_data *data, struct seq_file *p);    /* 可选择在show_interrupts中打印特殊芯片信息 */
	int		(*irq_request_resources)(struct irq_data *data);    /* 在调用之前请求资源的可选项与此irq相关的任何其他回调 */
	void		(*irq_release_resources)(struct irq_data *data);    /* 可选用于释放使用的资源 */

	unsigned long	flags;            /* 芯片特定标志 */
};

通常我们所说的中断分为两种,一种是内部中断(定时器,串口,I2C等),另一种是外部中断(GPIO+触发类型)

一般来说,内部中断是有固定的独立的中断号的。

而外部中断,因为数量比较多,每一个单独占用一个的话是特别浪费中断资源的,所以,外部中断通常会设计一些是有单独中断号的(对响应速度要求比较高),一些是多个io端口共享一个中断号。

首先看一下我们的S5PV210有多少个物理中断。

包括空闲的等也就一百零6个物理中断

接下来看一下内核中给我们分配了多少个。

struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = {
	[0 ... NR_IRQS-1] = {
		.handle_irq	= handle_bad_irq,
		.depth		= 1,
		.lock		= __RAW_SPIN_LOCK_UNLOCKED(irq_desc->lock),
	}
};


/* Set the default NR_IRQS中断数量 */
#define NR_IRQS			(IRQ_EINT(31) + S5P_GPIOINT_COUNT + 1)      /* IRQ_EINT(31) + 33 = 175 + 33 = 208 */

/**********************把31带入IRQ_EINT(x)*************************************/

#define S5P_GPIOINT_GROUP_COUNT 4
#define S5P_GPIOINT_GROUP_SIZE	8
#define S5P_GPIOINT_COUNT	(S5P_GPIOINT_GROUP_COUNT * S5P_GPIOINT_GROUP_SIZE)   /* 4*8 */


#define IRQ_EINT(x)		((x) < 16 ? ((x) + S5P_EINT_BASE1) \
					: ((x) - 16 + S5P_EINT_BASE2))    /* 15 + S5P_EINT_BASE2 = 160 + 15 = 175 */


#define S5P_EINT_BASE1		(S5P_IRQ_VIC0(0))
#define S5P_EINT_BASE2		(IRQ_VIC_END + 1)        /* 159 + 1 = 160*/

#define IRQ_VIC_END		S5P_IRQ_VIC3(31)              /* 128 + 31 */

#define S5P_IRQ_VIC3(x)		(S5P_VIC3_BASE + (x))    /* 128 + x */


#define S5P_VIC3_BASE		S5P_IRQ(96)        

#define S5P_IRQ_OFFSET		(32)

#define S5P_IRQ(x)		((x) + S5P_IRQ_OFFSET)     /* 32 + 96 */



/* Set the default NR_IRQS */
#define NR_IRQS			(IRQ_EINT(31) + S5P_GPIOINT_COUNT + 1)  /* 208 */



经过计算,总共208个,查看启动信息,也是208个,说明我们的计算没错。

为什么这么多呢?

多在那里了呢?仔细看一下上面的定义,可以发现。

#define S5P_IRQ_OFFSET		(32)

最开始预留了32个中断。

/* Typically only a few gpio chips require gpio interrupt support.
   To avoid memory waste irq descriptors are allocated only for
   S5P_GPIOINT_GROUP_COUNT chips, each with total number of
   S5P_GPIOINT_GROUP_SIZE pins/irqs. Each GPIOINT group can be assiged
   to any gpio chip with the s5p_register_gpio_interrupt() function */
#define S5P_GPIOINT_GROUP_COUNT 4
#define S5P_GPIOINT_GROUP_SIZE	8
#define S5P_GPIOINT_COUNT	(S5P_GPIOINT_GROUP_COUNT * S5P_GPIOINT_GROUP_SIZE)

/* Set the default NR_IRQS */
#define NR_IRQS			(IRQ_EINT(31) + S5P_GPIOINT_COUNT + 1)


这里又为gpiolib预留了32个中断。

上面注释写的很明白,使用这32个中断号的,用下面这个函数注册就可以,

int __init s5p_register_gpio_interrupt(int pin)

这个属于gpiolib库中的一部分内容。

那么208 - 128 - 64 = 16,剩下的用给那些了呢

再仔细看一下上面的

#define IRQ_VIC_END		S5P_IRQ_VIC3(31)            /* 最后一个物理中断127 */

#define S5P_EINT_BASE1		(S5P_IRQ_VIC0(0))        /* 物理中断0 */
#define S5P_EINT_BASE2		(IRQ_VIC_END + 1)        /* 物理中断128(硬件实现了寄存器,但没有这个中断) */


#define IRQ_EINT(x)		((x) < 16 ? ((x) + S5P_EINT_BASE1) \
					: ((x) - 16 + S5P_EINT_BASE2))

可以发现如果外部中断大于等于16,则会把这个中断安置在所有物理中断紧接着的后面位置

先分析一下外部中断,后面分析内部中断。

1.s5p_init_irq_eint(外部中断三星把其实现为架构相关的初始化时调用)

/* 外部中断0-15属于vic,他们每个是独立的chip */
static struct irq_chip s5p_irq_vic_eint = {
	.name		= "s5p_vic_eint",
	.irq_mask	= s5p_irq_vic_eint_mask,
	.irq_unmask	= s5p_irq_vic_eint_unmask,
	.irq_mask_ack	= s5p_irq_vic_eint_maskack,
	.irq_ack	= s5p_irq_vic_eint_ack,
	.irq_set_type	= s5p_irq_eint_set_type,
#ifdef CONFIG_PM
	.irq_set_wake	= s3c_irqext_wake,
#endif
};

static struct irq_chip s5p_irq_eint = {
	.name		= "s5p-eint",
	.irq_mask	= s5p_irq_eint_mask,
	.irq_unmask	= s5p_irq_eint_unmask,
	.irq_mask_ack	= s5p_irq_eint_maskack,
	.irq_ack	= s5p_irq_eint_ack,
	.irq_set_type	= s5p_irq_eint_set_type,
#ifdef CONFIG_PM
	.irq_set_wake	= s3c_irqext_wake,
#endif
};


static int __init s5p_init_irq_eint(void)
{
	int irq;

	if (of_have_populated_dt())    /* 我们没用device tree默认直接返回false */
		return -ENODEV;

    /* 设置前15个独立的外部中断的chip */
	for (irq = IRQ_EINT(0); irq <= IRQ_EINT(15); irq++)
		irq_set_chip(irq, &s5p_irq_vic_eint);   

    /* 设置16-31联合中断的chip和handle,并设置好高级处理程序(这里是设置在128号实际存在在中断后面的,
系统并不会响应这个,必须要真正的16-31联合的中断发生后,根据中断状态,计算出在linux中断里的中断号,即在irq_desc数组中的下标) */
	for (irq = IRQ_EINT(16); irq <= IRQ_EINT(31); irq++) {
		irq_set_chip_and_handler(irq, &s5p_irq_eint, handle_level_irq);
		set_irq_flags(irq, IRQF_VALID);
	}

    /* 外部中断16_31只有一个中断号,所以要设置成多路中断函数(即在这个函数中再分辨是那个中断源) */
	irq_set_chained_handler(IRQ_EINT16_31, s5p_irq_demux_eint16_31);
	return 0;
}

arch_initcall(s5p_init_irq_eint);        /* 在架构相关的初始化时调用 */

上面的注释写的很明白,在irq_desc所有中断的数组中填充我们的外部中断的32个中断。

其中0-16共17个属于硬件中就存在的中断,中断发生后,就会执行。(其中16号属于16~31的联合)

而16~31号中断虽然在irq_desc数组里,但需要通过先调用注册到IRQ_EINT16_31号里面的s5p_irq_demux_eint16_31函数找到再执行。

irq_set_chained_handler(IRQ_EINT16_31, s5p_irq_demux_eint16_31);

仔细分析过上面的可以知道,小于16号的外部中断是单独的,大于等于16号的是放在irq_desc数组后面位置。

	for (irq = IRQ_EINT(16); irq <= IRQ_EINT(31); irq++) {
		irq_set_chip_and_handler(irq, &s5p_irq_eint, handle_level_irq);
		set_irq_flags(irq, IRQF_VALID);
	}

从s5p_init_irq_eint函数分析,真正的16_31的联合中断,没有设置chip。而系统单独分开做的的16~31的16个外部中断每个都设置了chip.

可以看到IRQ_EINT16_31是真正的16号中断

下面我们看一下代码

1.设置irq_handler

/* 先看这句是怎么把s5p_irq_demux_eint16_31注册到 */
irq_set_chained_handler(IRQ_EINT16_31, s5p_irq_demux_eint16_31);

static inline void
irq_set_chained_handler(unsigned int irq, irq_flow_handler_t handle)
{
	__irq_set_handler(irq, handle, 1, NULL);    /* 设置handle */
}

void
__irq_set_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
		  const char *name)
{
	unsigned long flags;
	struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, 0);   /* 根据irq的num返回irq_desc[irq] */

	if (!desc)       /* 错误判断 */
		return;

	if (!handle) {    /* 错误判断 */
		handle = handle_bad_irq;    
	} else {
		if (WARN_ON(desc->irq_data.chip == &no_irq_chip))
			goto out;
	}

	/* Uninstall? */
	if (handle == handle_bad_irq) {
		if (desc->irq_data.chip != &no_irq_chip)
			mask_ack_irq(desc);
		irq_state_set_disabled(desc);
		desc->depth = 1;
	}
	desc->handle_irq = handle;    /* 添加s5p_irq_demux_eint16_31到16-31外部中断里 */
	desc->name = name;

	if (handle != handle_bad_irq && is_chained) {
		irq_settings_set_noprobe(desc);
		irq_settings_set_norequest(desc);
		irq_settings_set_nothread(desc);
		irq_startup(desc, true);
	}
out:
	irq_put_desc_busunlock(desc, flags);
}

2.外部中断15~31的处理函数

/* 注意大于等于16,就已经是在irq_desc数组后面位置了 */
#define IRQ_EINT(x)		((x) < 16 ? ((x) + S5P_EINT_BASE1) \
					: ((x) - 16 + S5P_EINT_BASE2))


static void s5p_irq_demux_eint16_31(unsigned int irq, struct irq_desc *desc)
{
	s5p_irq_demux_eint(IRQ_EINT(16));        /* 从16~23共8个 */
	s5p_irq_demux_eint(IRQ_EINT(24));        /* 从24~31共8个 */
}

/* s5p_irq_demux_eint
 *
 * This function demuxes the IRQ from the group0 external interrupts,
 * from EINTs 16 to 31. It is designed to be inlined into the specific
 * handler s5p_irq_demux_eintX_Y.
 *
 * Each EINT pend/mask registers handle eight of them.
 */
static inline void s5p_irq_demux_eint(unsigned int start)
{
	u32 status = __raw_readl(S5P_EINT_PEND(EINT_REG_NR(start)));    /* 读中断挂起寄存器  */
	u32 mask = __raw_readl(S5P_EINT_MASK(EINT_REG_NR(start)));      /* 读屏蔽寄存器 */
	unsigned int irq;

	status &= ~mask;
	status &= 0xff;

    /* 如果没有屏蔽中断,则执行当前所有中断位挂起的中断函数 */
	while (status) {
		irq = fls(status) - 1;        /* 找到从bit0到bit7,返回输入参数的最高有效bit位(从低位往左数最后的有效bit位)的序号 */
		generic_handle_irq(irq + start);    /* IRQ_EINT(x) + irq = 真正的中断号,传入,执行中断处理函数 */
		status &= ~(1 << irq);        /* 清除已经执行完的中断标记(可能有多个外部中断,所以每次都是清除已经执行了中断处理程序的那一个) */
	}
}


/**
 * fls - find last (most-significant) bit set
 * @x: the word to search
 *
 * This is defined the same way as ffs.
 * Note fls(0) = 0, fls(1) = 1, fls(0x80000000) = 32.
 */
/* 可以看到上面函数的注释0x80000000,的第31bit是1,所以是32
 * 我们如果是0x24,则   fls(0x42) = 3,因为2是低端第一个1所在的bit
static __always_inline int fls(int x);

中断处理程序我们上一篇文章已经分析过了

struct irq_desc *irq_to_desc(unsigned int irq)
{
	return (irq < NR_IRQS) ? irq_desc + irq : NULL;        /* 根据中断号,找到数组项 */
}


int generic_handle_irq(unsigned int irq)
{
	struct irq_desc *desc = irq_to_desc(irq);

	if (!desc)
		return -EINVAL;
	generic_handle_irq_desc(irq, desc);    /* 根据数组项,执行函数 */
	return 0;
}

static inline void generic_handle_irq_desc(unsigned int irq, struct irq_desc *desc)
{
	desc->handle_irq(irq, desc);        /* 执行数组项里面注册填充的函数 */
}

关于我的下面这个函数为什么要调用两次,传入不同的中断号

static void s5p_irq_demux_eint16_31(unsigned int irq, struct irq_desc *desc)
{
	s5p_irq_demux_eint(IRQ_EINT(16));
	s5p_irq_demux_eint(IRQ_EINT(24));
}

是因为我们的中断挂起寄存器和中断屏蔽寄存器,每个寄存器都是存放8个中断的,而16~31总共有16个,在两个寄存器中存放挂起和屏蔽标志位,所以要分两次执行。

上面把外部中断全部分析了,接下来分析一下内部的中断。

2.内部中断。

上一篇文章我我们分析了中断的初始化过程。其中最重要的是初始化了irq_desc数组,以及调用了我们三星的具体处理器实现的中断的初始化。

其中初始化中最终要的就是把我们的中断处理程序,放在了handle_arch_irq标号的位置。

其实register_vic里面还做了很多其他的事情。

2.1

/* CONFIG_ARM_VIC_NR = 4 */
static struct vic_device vic_devices[CONFIG_ARM_VIC_NR];
 
 
 
/**
 * vic_register() - Register a VIC.
 * @base: The base address of the VIC.
 * @parent_irq: The parent IRQ if cascaded, else 0.
 * @irq: The base IRQ for the VIC.
 * @valid_sources: bitmask of valid interrupts
 * @resume_sources: bitmask of interrupts allowed for resume sources.
 * @node: The device tree node associated with the VIC.
 *
 * Register the VIC with the system device tree so that it can be notified
 * of suspend and resume requests and ensure that the correct actions are
 * taken to re-instate the settings on resume.
 *
 * This also configures the IRQ domain for the VIC.
 */
static void __init vic_register(void __iomem *base, unsigned int parent_irq,
				unsigned int irq,
				u32 valid_sources, u32 resume_sources,
				struct device_node *node)
{
	struct vic_device *v;
	int i;
 
	if (vic_id >= ARRAY_SIZE(vic_devices)) {    /* 最多有4组 */
		printk(KERN_ERR "%s: too few VICs, increase CONFIG_ARM_VIC_NR\n", __func__);
		return;
	}
 
    /* 注册vic各种信息到vic_devices结构体数组 */
	v = &vic_devices[vic_id];    
	v->base = base;
	v->valid_sources = valid_sources;
	v->resume_sources = resume_sources;    
	set_handle_irq(vic_handle_irq);        /* 这句就是我们关注的,设置中断函数执行的代码 */    
	vic_id++;
 
	if (parent_irq) {    /* parent_irq = 0  */
		irq_set_handler_data(parent_irq, v);
		irq_set_chained_handler(parent_irq, vic_handle_irq_cascaded);
	}
 
    /* 这里我们注册了每个irq的中断domain */
	v->domain = irq_domain_add_simple(node, fls(valid_sources), irq,
					  &vic_irqdomain_ops, v);
	/* create an IRQ mapping for each valid IRQ */
	for (i = 0; i < fls(valid_sources); i++)
		if (valid_sources & (1 << i))
			irq_create_mapping(v->domain, i);
	/* If no base IRQ was passed, figure out our allocated base */
	if (irq)  /* 在我们没设置irq的情况下,系统会初始化成默认的,否则要是中断了,可能会跑飞掉 */
		v->irq = irq;        
	else
		v->irq = irq_find_mapping(v->domain, 0);
}

这个是irq_domain_add_simple的最后一个参数

static struct irq_domain_ops vic_irqdomain_ops = {
	.map = vic_irqdomain_map,
	.xlate = irq_domain_xlate_onetwocell,
};

2.2、增加domain信息

/**
 * irq_domain_add_simple() - Register an irq_domain and optionally map a range of irqs
 * @of_node: pointer to interrupt controller's device tree node.
 * @size: total number of irqs in mapping
 * @first_irq: first number of irq block assigned to the domain,
 *	pass zero to assign irqs on-the-fly. If first_irq is non-zero, then
 *	pre-map all of the irqs in the domain to virqs starting at first_irq.
 * @ops: map/unmap domain callbacks
 * @host_data: Controller private data pointer
 *
 * Allocates an irq_domain, and optionally if first_irq is positive then also
 * allocate irq_descs and map all of the hwirqs to virqs starting at first_irq.
 *
 * This is intended to implement the expected behaviour for most
 * interrupt controllers. If device tree is used, then first_irq will be 0 and
 * irqs get mapped dynamically on the fly. However, if the controller requires
 * static virq assignments (non-DT boot) then it will set that up correctly.
 */
struct irq_domain *irq_domain_add_simple(struct device_node *of_node,
					 unsigned int size,
					 unsigned int first_irq,
					 const struct irq_domain_ops *ops,
					 void *host_data)
{
	struct irq_domain *domain;

    /* 分配和初始化和irq_domain结构 */
	domain = __irq_domain_add(of_node, size, size, 0, ops, host_data);
	if (!domain)
		return NULL;

	if (first_irq > 0) {
		if (IS_ENABLED(CONFIG_SPARSE_IRQ)) {        /* 我们没定义使用稀疏IRQ(利用基数树实现) */
			/* attempt to allocated irq_descs */
			int rc = irq_alloc_descs(first_irq, first_irq, size,
						 of_node_to_nid(of_node));
			if (rc < 0)
				pr_info("Cannot allocate irq_descs @ IRQ%d, assuming pre-allocated\n",
					first_irq);
		}
		irq_domain_associate_many(domain, first_irq, 0, size);    /* irq和domain关联(比较重要) */
	}

	return domain;
}

2.2.1、__irq_domain_add

/**
 * __irq_domain_add() - Allocate a new irq_domain data structure
 * @of_node: optional device-tree node of the interrupt controller
 * @size: Size of linear map; 0 for radix mapping only
 * @hwirq_max: Maximum number of interrupts supported by controller
 * @direct_max: Maximum value of direct maps; Use ~0 for no limit; 0 for no
 *              direct mapping
 * @ops: map/unmap domain callbacks
 * @host_data: Controller private data pointer
 *
 * Allocates and initialize and irq_domain structure.
 * Returns pointer to IRQ domain, or NULL on failure.
 */
struct irq_domain *__irq_domain_add(struct device_node *of_node, int size,
				    irq_hw_number_t hwirq_max, int direct_max,
				    const struct irq_domain_ops *ops,
				    void *host_data)
{
	struct irq_domain *domain;

    /* 申请domian和基数树相关的空间 */
	domain = kzalloc_node(sizeof(*domain) + (sizeof(unsigned int) * size),
			      GFP_KERNEL, of_node_to_nid(of_node));
	if (WARN_ON(!domain))
		return NULL;

	/* Fill structure 初始化domain中的基数树*/
	INIT_RADIX_TREE(&domain->revmap_tree, GFP_KERNEL);
    /* 初始化domian */
	domain->ops = ops;                /* 把我们定义的ops绑定到domain上 */
	domain->host_data = host_data;
	domain->of_node = of_node_get(of_node);
	domain->hwirq_max = hwirq_max;
	domain->revmap_size = size;
	domain->revmap_direct_max_irq = direct_max;

	mutex_lock(&irq_domain_mutex);
	list_add(&domain->link, &irq_domain_list);   /* 把这个新的domain加入irq_domain_list链表 */
	mutex_unlock(&irq_domain_mutex);

	pr_debug("Added domain %s\n", domain->name);
	return domain;
}

2.2.2、irq_domain_associate_many,一次关联一组VIC,32个irq和domain

void irq_domain_associate_many(struct irq_domain *domain, unsigned int irq_base,
			       irq_hw_number_t hwirq_base, int count)
{
	int i;

	pr_debug("%s(%s, irqbase=%i, hwbase=%i, count=%i)\n", __func__,
		of_node_full_name(domain->of_node), irq_base, (int)hwirq_base, count);

    /* 我们的4个vic最初都是对0取反,所以count是32 ,即对每个vic的每个bit都要执行这个irq和domian的关联 */
	for (i = 0; i < count; i++) {
		irq_domain_associate(domain, irq_base + i, hwirq_base + i);
	}
}

2.2.3。关联每一个irq和domian


int irq_domain_associate(struct irq_domain *domain, unsigned int virq,
			 irq_hw_number_t hwirq)
{
	struct irq_data *irq_data = irq_get_irq_data(virq);    /* 通过vic中的irq号,找到irq_desc数组,返回里面的irq_data */
	int ret;

    /* 错误检查 */
	if (WARN(hwirq >= domain->hwirq_max,
		 "error: hwirq 0x%x is too large for %s\n", (int)hwirq, domain->name))
		return -EINVAL;
	if (WARN(!irq_data, "error: virq%i is not allocated", virq))
		return -EINVAL;
	if (WARN(irq_data->domain, "error: virq%i is already associated", virq))
		return -EINVAL;
    
    /* 初始化irq_data */
	mutex_lock(&irq_domain_mutex);
	irq_data->hwirq = hwirq;
	irq_data->domain = domain;
	if (domain->ops->map) {
		ret = domain->ops->map(domain, virq, hwirq);    /* 这一句很重要,后面分析 */
		if (ret != 0) {
			/*
			 * If map() returns -EPERM, this interrupt is protected
			 * by the firmware or some other service and shall not
			 * be mapped. Don't bother telling the user about it.
			 */
			if (ret != -EPERM) {
				pr_info("%s didn't like hwirq-0x%lx to VIRQ%i mapping (rc=%d)\n",
				       domain->name, hwirq, virq, ret);
			}
			irq_data->domain = NULL;
			irq_data->hwirq = 0;
			mutex_unlock(&irq_domain_mutex);
			return ret;
		}

		/* If not already assigned, give the domain the chip's name */
		if (!domain->name && irq_data->chip)
			domain->name = irq_data->chip->name;
	}

    /* 我们这里因为是静态一次分配好所有的中断,所以没有用基数树 */
	if (hwirq < domain->revmap_size) {
		domain->linear_revmap[hwirq] = virq;
	} else {
		mutex_lock(&revmap_trees_mutex);
		radix_tree_insert(&domain->revmap_tree, hwirq, irq_data);
		mutex_unlock(&revmap_trees_mutex);
	}
	mutex_unlock(&irq_domain_mutex);

	irq_clear_status_flags(virq, IRQ_NOREQUEST);    /* 清中断 */

	return 0;
}

2.2.4、初始化每个irq

static struct irq_domain_ops vic_irqdomain_ops = {
	.map = vic_irqdomain_map,
	.xlate = irq_domain_xlate_onetwocell,
};

这个实在前面就定义的,后来绑定到domian的ops上,在上面函数中执行

static int vic_irqdomain_map(struct irq_domain *d, unsigned int irq,
			     irq_hw_number_t hwirq)
{
	struct vic_device *v = d->host_data;

	/* Skip invalid IRQs, only register handlers for the real ones */
	if (!(v->valid_sources & (1 << hwirq)))
		return -EPERM;
	irq_set_chip_and_handler(irq, &vic_chip, handle_level_irq);   /* 设置chip */
	irq_set_chip_data(irq, v->base);        /* 设置chip_data */
	set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);    /* 设备flags */
	return 0;
}

可以看到这里为每一个irq都绑定了相同的vic_chip,和handler_level_irqy以及chip_data

我分析一下这几个的作用。

首先vic_chip,比较代码中比较奇葩,定义了两遍,我以为我看错了。检查了好久,确实是定义了两遍。

static struct irq_chip vic_chip;

static struct irq_chip vic_chip = {
	.name		= "VIC",
	.irq_ack	= vic_ack_irq,            /* 清中断 */
	.irq_mask	= vic_mask_irq,            /* 屏蔽中断 */
	.irq_unmask	= vic_unmask_irq,        /* 使能中断 */
	.irq_set_wake	= vic_set_wake,        /* 使能恢复(之前可能挂起了) */
};

后来我自己按上面的形式也测试了一下。gcc居然可以自动优化掉,我以为会告重复定义。

这边举例看几个
static void vic_ack_irq(struct irq_data *d)
{
	void __iomem *base = irq_data_get_irq_chip_data(d);
	unsigned int irq = d->hwirq;
	writel(1 << irq, base + VIC_INT_ENABLE_CLEAR);    /* clear enable就是清中断 */
	/* moreover, clear the soft-triggered, in case it was the reason */
	writel(1 << irq, base + VIC_INT_SOFT_CLEAR);   /* 清软触发 */ 
}

static void vic_mask_irq(struct irq_data *d)
{
	void __iomem *base = irq_data_get_irq_chip_data(d);
	unsigned int irq = d->hwirq;
	writel(1 << irq, base + VIC_INT_ENABLE_CLEAR);    /* 清中断(为什么不是屏蔽??) */
}

static void vic_unmask_irq(struct irq_data *d)
{
	void __iomem *base = irq_data_get_irq_chip_data(d);
	unsigned int irq = d->hwirq;
	writel(1 << irq, base + VIC_INT_ENABLE);    /* 使能中断 */
}

handle_level_irq每个中断注册的执行函数


/**
 *	handle_level_irq - Level type irq handler
 *	@irq:	the interrupt number
 *	@desc:	the interrupt description structure for this irq
 *
 *	Level type interrupts are active as long as the hardware line has
 *	the active level. This may require to mask the interrupt and unmask
 *	it after the associated handler has acknowledged the device, so the
 *	interrupt line is back to inactive.
 */
void
handle_level_irq(unsigned int irq, struct irq_desc *desc)
{
	raw_spin_lock(&desc->lock);        /* desc上锁 */
	mask_ack_irq(desc);            /* 清中断 */

	if (unlikely(irqd_irq_inprogress(&desc->irq_data)))    
		if (!irq_check_poll(desc))
			goto out_unlock;

	desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);    /* 清重复和等待标志位 */
	kstat_incr_irqs_this_cpu(irq, desc);    /* 增加随机数状态 */

	/*
	 * If its disabled or no action available
	 * keep it masked and get out of here    屏蔽irq或action无效则挂起
	 */
	if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) {
		desc->istate |= IRQS_PENDING;
		goto out_unlock;
	}

	handle_irq_event(desc);        /* 处理中断程序 */

	cond_unmask_irq(desc);        

out_unlock:
	raw_spin_unlock(&desc->lock);    /* desc解锁 */
}



irqreturn_t handle_irq_event(struct irq_desc *desc)
{
	struct irqaction *action = desc->action;
	irqreturn_t ret;

	desc->istate &= ~IRQS_PENDING;
	irqd_set(&desc->irq_data, IRQD_IRQ_INPROGRESS);    /* 标记中断正在运行 */
	raw_spin_unlock(&desc->lock);       /* desc解锁 */   

	ret = handle_irq_event_percpu(desc, action);    /* 中断处理 */

	raw_spin_lock(&desc->lock);        /* desc上锁 */
	irqd_clear(&desc->irq_data, IRQD_IRQ_INPROGRESS);  /* 清中断正在运行标记 */ 
	return ret;
}



irqreturn_t
handle_irq_event_percpu(struct irq_desc *desc, struct irqaction *action)
{
	irqreturn_t retval = IRQ_NONE;
	unsigned int flags = 0, irq = desc->irq_data.irq;    /* 得到中断号 */

	do {
		irqreturn_t res;

		trace_irq_handler_entry(irq, action);
		res = action->handler(irq, action->dev_id);    /* 指行cation链表中的handler */
		trace_irq_handler_exit(irq, action, res);

		if (WARN_ONCE(!irqs_disabled(),"irq %u handler %pF enabled interrupts\n",
			      irq, action->handler))
			local_irq_disable();

		switch (res) {
		case IRQ_WAKE_THREAD:
			/*
			 * Catch drivers which return WAKE_THREAD but  驱动程序返回弱线程但是
			 * did not set up a thread function            没有设置线程函数
			 */
			if (unlikely(!action->thread_fn)) {
				warn_no_thread(irq, action);
				break;
			}

			__irq_wake_thread(desc, action);    /* 中断函数线程话 */

			/* Fall through to add to randomness 通过添加到随机性*/
		case IRQ_HANDLED:
			flags |= action->flags;
			break;

		default:
			break;
		}

		retval |= res;
		action = action->next;    /* 链表的下一个中断 */
	} while (action);

	add_interrupt_randomness(irq, flags);    /* 增加系统的随机数种子(有多个肯定就是共享中断了) */

	if (!noirqdebug)
		note_interrupt(irq, desc, retval);
	return retval;
}

现在中断框架部分基本就差不多了,现在总结一下一个中断从产生到执行的流程。

vector_irq                /* 中断向量表 */
    保存现场
    __irq_usr/__irq_svc
        usr_entry            /* 保存现场 */
        irq_handler
            handle_arch_irq     /* 直接调转到handle_arch_irq标号处,在初始化阶段设置的中断处理函数中 */         
                vic_handle_irq       /* 我们是在vic_register_里面注册的到handle_arch_irq标号处的 */
                    handle_one_vic    /* 我们有四个vic,要调用四次handle_one_vic     */
                        readl_relaxed    /* 读该vic的状态寄存器,如果不为0表示由中断,读出,检查并每次执行其中一个 */
                        handle_IRQ       /* 执行handle_IRQ */
                            generic_handle_irq(irq)        /* 执行通用的注册的中断 */
                                generic_handle_irq_desc(irq, desc)    /*  */
                                    desc->handle_irq(irq, desc)    /* 执行初始化阶段注册的通用的 */
                                       handle_level_irq(unsigned int irq, struct irq_desc *desc)    /* 这个就是初始化阶段为每个中断都绑定的 */
                                            handle_irq_event(desc);
                                                handle_irq_event_percpu(desc, action);
                                                    action->handler(irq, action->dev_id)    /* 由驱动工程师实现的驱动函数 */
                                                    action = action->next;    /* 共享中断要执行相同中断号的cation链表的所有中断 */

对比上一节分析的中断的初始化。可以发现基本系统已经都帮我们实现了。

我们要实现的就是最后一个的cation结构体就可以了。

当然这个的百分之九十的工作系统也已经帮我们做好了,提供了接口函数,我们只要调用相应函数就可以了。

申请并注册中断,内部实现基本差不多

extern int __must_check
request_threaded_irq(unsigned int irq, irq_handler_t handler,
		     irq_handler_t thread_fn,
		     unsigned long flags, const char *name, void *dev);

static inline int __must_check
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
	    const char *name, void *dev)
{
	return request_threaded_irq(irq, handler, NULL, flags, name, dev);
}

extern int __must_check
request_any_context_irq(unsigned int irq, irq_handler_t handler,
			unsigned long flags, const char *name, void *dev_id);

extern int __must_check
request_percpu_irq(unsigned int irq, irq_handler_t handler,
		   const char *devname, void __percpu *percpu_dev_id);

我们以最常用的request_irq来分析

/* 申请并注册一个中断 */
static inline int __must_check
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
	    const char *name, void *dev)
{
	return request_threaded_irq(irq, handler, NULL, flags, name, dev);
}

/* 内部函数,默认thread_fn为0,即没有使用线程化 */
int request_threaded_irq(unsigned int irq, irq_handler_t handler,
			 irq_handler_t thread_fn, unsigned long irqflags,
			 const char *devname, void *dev_id)
{
	struct irqaction *action;
	struct irq_desc *desc;
	int retval;

	/*
	 * 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).  共享中断必须设置dev_id
	 */
	if ((irqflags & IRQF_SHARED) && !dev_id)
		return -EINVAL;

	desc = irq_to_desc(irq);        /* 根据中断号,得到irq_desc数组下标 */
	if (!desc)
		return -EINVAL;

	if (!irq_settings_can_request(desc) ||        /* 错误检验 */
	    WARN_ON(irq_settings_is_per_cpu_devid(desc)))
		return -EINVAL;

	if (!handler) {        /* 非线程化和线程化必须要选一个,两个都有默认非线程化 */
		if (!thread_fn)
			return -EINVAL;
		handler = irq_default_primary_handler;
	}

    /* 分配cation */
	action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
	if (!action)
		return -ENOMEM;

    /* 填充cation结构 */
	action->handler = handler;
	action->thread_fn = thread_fn;
	action->flags = irqflags;
	action->name = devname;
	action->dev_id = dev_id;

	chip_bus_lock(desc);
	retval = __setup_irq(irq, desc, action);    /* 把cation加入到desc的irq相的链表中去 */
	chip_bus_sync_unlock(desc);

	if (retval)
		kfree(action);

#ifdef CONFIG_DEBUG_SHIRQ_FIXME
	if (!retval && (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 disable the irq to make sure that a 'real' IRQ doesn't
		 * run in parallel with our fake.
		 */
		unsigned long flags;

		disable_irq(irq);
		local_irq_save(flags);

		handler(irq, dev_id);

		local_irq_restore(flags);
		enable_irq(irq);
	}
#endif
	return retval;
}

释放申请的irq

extern void free_irq(unsigned int, void *);
extern void free_percpu_irq(unsigned int, void __percpu *);

我们也用最常见的free_irq来分析

void free_irq(unsigned int irq, void *dev_id)
{
	struct irq_desc *desc = irq_to_desc(irq);    /* 根据中断号得到对应的desc */

	if (!desc || WARN_ON(irq_settings_is_per_cpu_devid(desc)))
		return;

#ifdef CONFIG_SMP
	if (WARN_ON(desc->affinity_notify))
		desc->affinity_notify = NULL;
#endif

	kfree(__free_irq(irq, dev_id));    /* 释放掉action结构体 */
}


static struct irqaction *__free_irq(unsigned int irq, void *dev_id)
{
	struct irq_desc *desc = irq_to_desc(irq);
	struct irqaction *action, **action_ptr;
	unsigned long flags;

	WARN(in_interrupt(), "Trying to free IRQ %d from IRQ context!\n", irq);

	if (!desc)
		return NULL;

    /* 上锁 */
	chip_bus_lock(desc);
	raw_spin_lock_irqsave(&desc->lock, flags);

	/*
	 * There can be multiple actions per IRQ descriptor, find the right
	 * one based on the dev_id:
	 */
	action_ptr = &desc->action;    /* 得到action链表头 */
	for (;;) {
		action = *action_ptr;

		if (!action) {        /* 下面也说明了,释放已经释放了的中断或没找到dev_id对应的 */
			WARN(1, "Trying to free already-free IRQ %d\n", irq);
			raw_spin_unlock_irqrestore(&desc->lock, flags);
			chip_bus_sync_unlock(desc);
			return NULL;
		}

		if (action->dev_id == dev_id)    /* 链表中找到对应的dev_id标记的action */
			break;
		action_ptr = &action->next;    /* 遍历链表查找dev_id */
	}

	/* Found it - now remove it from the list of entries: */
	*action_ptr = action->next;    /* 链表中删除action */

	/* If this was the last handler, shut down the IRQ line: */
	if (!desc->action) {
		irq_shutdown(desc);    /* 如果不是共享中断或共享中断这个是最后一个,则要关闭中断 */
		irq_release_resources(desc);    
	}

#ifdef CONFIG_SMP
	/* make sure affinity_hint is cleaned up */
	if (WARN_ON_ONCE(desc->affinity_hint))
		desc->affinity_hint = NULL;
#endif

    /* 解锁 */
	raw_spin_unlock_irqrestore(&desc->lock, flags);
	chip_bus_sync_unlock(desc);

	unregister_handler_proc(irq, action);    /* 更新proc中的irq */

	/* Make sure it's not being used on another CPU: */
	synchronize_irq(irq);        /* 多核cpu需要同步一下 */

#ifdef CONFIG_DEBUG_SHIRQ
	/*
	 * It's a shared IRQ -- the driver ought to be prepared for an IRQ
	 * event to happen even now it's being freed, so let's make sure that
	 * is so by doing an extra call to the handler ....
	 *
	 * ( We do this after actually deregistering it, to make sure that a
	 *   'real' IRQ doesn't run in * parallel with our fake. )
	 */
	if (action->flags & IRQF_SHARED) {
		local_irq_save(flags);
		action->handler(irq, dev_id);
		local_irq_restore(flags);
	}
#endif

	if (action->thread) {
		kthread_stop(action->thread);        /* 释放的这个action如果是有线程化,也要关闭这个线程 */
		put_task_struct(action->thread);
	}

	module_put(desc->owner);
	return action;        /* 返回cation指针,下一个函数释放掉内存 */
}

中断就暂时分析到这里,下一节我们来写一个简单的测试程序来使用一下中断。

猜你喜欢

转载自blog.csdn.net/qq_16777851/article/details/82564302