システム内のデバイスツリーを破壊する

本文章参考学习阅读:
基于设备树的TQ2440的中断(1)
https://www.cnblogs.com/pengdonglin137/p/6847685.html

基于设备树的TQ2440的中断(2)
https://www.cnblogs.com/pengdonglin137/p/6848851.html

基於tiny4412的Linux內核移植 --- 实例学习中断背后的知识(1)
http://www.cnblogs.com/pengdonglin137/p/6349209.html

linux kernel的中断子系统之:GIC代码分析
http://www.wowotech.net/irq_subsystem/interrupt_subsystem_architecture.html

本篇文章所用内核源码:
链接:https://pan.baidu.com/s/14WfehP9gF2GLrrCdxWG7yg 
提取码:gymq 
--来自百度网盘超级会员V6的分享

1. 割り込みの概念の導入と処理の流れ

1.中断とは何ですか?

  割り込みは、外部または内部のランダム イベントをリアルタイムで処理するシングルチップ マイクロコンピュータに設定されています。皆さんに割り込みの概念を理解しやすくするために、例を挙げて説明します。
ここに画像の説明を挿入
  この記事を読める人は、シングルチップ マイコンや組み込み機器のベテランであり、割り込みについて理解している必要があります。 、皆さんに簡単な思い出を教えてください。

(1) マクロレベルでの並列化のニーズを解決するために、割り込みの発明が使用されます。マクロというのは全体的な視点で、並列というのは複数のものが完了していることを意味します。

(2) ミクロコズミック並列処理とは、真の並列処理を指します。これは、毎秒、あるいはすべての瞬間にさえ正確であり、複数のことが同時に実行されます。マクロ レベルでの並列性とミクロ レベルでの並列性は同じではなく、マクロ レベルでは並列であり、ミクロ レベルでは直列である場合もあります。

(3) この例では、ある人が映画を見ています。配達員が到着したら、映画を一時停止して、走って配達員を迎えに行きます。配達員を受け取った後も、映画を見に戻ってきます。この例はマクロです。パラレルとマイクロシリアルです。この例では、SoC では 1 人が 1 CPU (つまりシングルコア CPU) に相当しますが、この CPU は映画鑑賞時に速達サービスを受けることができず、また、速達サービスを受けるときに映画を視聴することもできません (つまり、映画を視聴することはできません)。本当に平行です)。シングルコアCPUはミクロに見ると直列ですが、CPUが速いため巨視的に見ると並列化が可能です。

(4) 上記の例では、ほとんどの時間を映画の視聴に費やし、少しの時間を速達の受信に費やしている場合、CPU と同様に、映画の視聴は CPU の日常的なタスクとなり、受信中は、速達配信は割り込みルーチンである必要があります。つまり、CPU は常に映画を見続けており、配達員が到着すると (割り込みが発生すると)、配達員 (割り込みソースと同様) は宅配便を受け取るために誰かに電話をします (割り込みソースが割り込みをトリガーし、割り込みを処理するように CPU に通知します) 電話を受信した後 (CPU が割り込み信号を受信します)、彼は暫定的にムービー (CPU がルーチンタスクを保存するシーン) をスケジュールし、宅配便 (CPU が受け取る) を受け取るために走ります。割り込みハンドラISRを実行して割り込みを処理する)、クーリエを受信した後(ISRが実行される)、戻って動画の視聴を続ける(CPUが定型タスクを再開し、定型タスクを実行し続けるシーン)

(5) なぜ中断する必要があるのですか? シングルコア CPU は実際には並列化できませんが、割り込みメカニズムを通じて疑似並列化を実現できます (マクロ レベルでは並列ですが、ミクロ レベルでは実際には直列です)。

2. 処理の流れ

(1)保存现场
(2)处理异常
(3)恢复现场

3. SoC の割り込み実装メカニズム: 例外ベクタ テーブル

(1)例外ベクタ テーブルは、 CPU 内のいくつかの特定のアドレスの特定の定義です割り込みが発生すると、割り込みは CPU に割り込みを処理するように通知する方法を見つけなければなりません。どのようにすればよいでしょうか? これは例外ベクタ テーブルに依存します

(2) CPU の設計時に、CPU 内のいくつかの特定のアドレスが特定の例外のエントリ アドレスとして事前に定義されます (たとえば、アドレス 0x00000000 がリセット例外ベクタ アドレスとして定義されている場合、CPU は自動的にそのアドレスにジャンプします)リセット例外発生時に、アドレス 0x00000000 に命令を実行します(たとえば、外部割り込みに対応する例外ベクタアドレスが 0x30000008 の場合、外部割り込み発生後、CPU は自動的にアドレス 0x30000008 にジャンプして命令を実行します)

(3) 上記は CPU ハードウェア設計時の例外ベクタテーブルのサポートであり、ソフトウェアによるサポートが必要です。例外が発生したときに、CPU が自動的に PC にジャンプして実行するアドレスはハードウェアであらかじめ決められており、ソフトウェアは例外を処理するコードの先頭アドレスを例外ベクタ アドレスに代入するだけで済みます。

(4) 例外ベクタ テーブル内の各ベクタの相対位置は固定されていますが、開始アドレスは固定されておらず、さまざまな SoC によって異なる場合があり、複雑な ARM では、ソフトウェア ベース アドレスによって例外ベクタ テーブルを設定することもできます。

(5) 全アーキテクチャの CPU への拡張:全アーキテクチャ (51 シングルチップ マイコン、PIC シングルチップ マイコンなど) の CPU 実装割り込みは、例外ベクタ テーブルによって実現されており、この仕組みは変わりませんが構造と異なるCPU例外ベクタテーブルの構造と位置が異なります。
ここに画像の説明を挿入

4. 例外と割り込みの違いと関係

(1) SoC の場合、リセット、ソフト割り込み、割り込み、高速割り込み、命令フェッチ例外、データ例外などを総称して例外と呼びます。したがって、割り込みは実際には一種の例外です。

(2) 例外の定義は、CPU の通常の日常業務を中断する緊急事態であり、CPU は例外処理プログラムを実行するために例外ベクタ テーブルにジャンプする必要があり、割り込みは例外の一種であり、一般に周辺機器によって生成される SoC ルーチン ビジネスの内部割り込み、または外部割り込み (SoC の GPIO ピンから送り返される割り込み)。

  割り込みはマスクできますが、例外はマスクできません

2. Linux の割り込み処理のフレームワークとコード フローの簡単な説明

a. 異常なベクターエントリ: Arch\arm\kernel\entry-armv.S

    .section .vectors, "ax", %progbits
.L__vectors_start:
    W(b)    vector_rst
    W(b)    vector_und
    W(ldr)  pc, .L__vectors_start + 0x1000
    W(b)    vector_pabt
    W(b)    vector_dabt
    W(b)    vector_addrexcptn
    W(b)    vector_irq
    W(b)    vector_fiq

b. 割り込みベクトル:vector_irq

/*
 * Interrupt dispatcher
 */
    vector_stub irq, IRQ_MODE, 4   // 相当于 vector_irq: ..., 
                                   // 它会根据SPSR寄存器的值,
                                   // 判断被中断时CPU是处于USR状态还是SVC状态, 
                                   // 然后调用下面的__irq_usr或__irq_svc

    .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

c. __irq_usr/__irq_svc

__irq_usr:
	usr_entry
	kuser_cmpxchg_check
	irq_handler
	get_thread_info tsk
	mov	why, #0
	b	ret_to_user_from_irq
 UNWIND(.fnend		)
ENDPROC(__irq_usr)

	.ltorg

	.align	5
__irq_svc:
	svc_entry
	irq_handler

#ifdef CONFIG_PREEMPT
	ldr	r8, [tsk, #TI_PREEMPT]		@ get preempt count
	ldr	r0, [tsk, #TI_FLAGS]		@ get flags
	teq	r8, #0				@ if preempt count != 0
	movne	r0, #0				@ force flags to 0
	tst	r0, #_TIF_NEED_RESCHED
	blne	svc_preempt
#endif

	svc_exit r5, irq = 1			@ return from exception
 UNWIND(.fnend		)
ENDPROC(__irq_svc)

	.ltorg

これら 2 つの関数の処理は似ています。
  シーンを保存し
  、irq_handler を呼び出して
  シーンを復元します。

d. irq_handler: C 関数 handle_arch_irq を呼び出します。

    .macro  irq_handler
#ifdef CONFIG_GENERIC_IRQ_MULTI_HANDLER
    ldr r1, =handle_arch_irq
    mov r0, sp
    badr    lr, 9997f
    ldr pc, [r1]
#else
    arch_irq_handler_default
#endif
9997:
    .endm

e. handle_arch_irq の処理

#ifdef CONFIG_GENERIC_IRQ_MULTI_HANDLER
void (*handle_arch_irq)(struct pt_regs *) __ro_after_init;
#endif

(1)读取寄存器获得中断信息: hwirq(hardware irq)

(2)把hwirq(硬件中断号)转换为virq(虚拟中断号),一个硬件中断号可能会对应多个
虚拟中断号,因为存在多个硬件共享一个硬件中断号,但具有不同的虚拟中断号。通过调用
irq_desc[virq].handle_irq实现二者的转换
   
对于S3C2440, s3c24xx_handle_irq 是用于处理中断的C语言入口函数

ここに画像の説明を挿入
割り込み処理の流れ:

假设中断结构如下:
sub interrupt controller(子中断控制器) ---> interrupt controller(中断控制器) ---> cpu

发生中断时,
cpu跳到"vector_irq", 保存现场, 调用C函数handle_arch_irq

handle_arch_irq:
a. 读 interrupt controller, 得到hwirq
b. 根据hwirq得到virq
c. 调用 irq_desc[virq].handle_irq

如果该中断没有子中断, irq_desc[virq].handle_irq的操作:
a. 取出irq_desc[virq].action 链表 中的每一个handler, 执行它
b. 使用irq_desc[virq].irq_data.chip的函数清中断

如果该中断是由子中断产生, irq_desc[virq].handle_irq的操作:
a. 读 sub interrupt controller, 得到hwirq
b. 根据hwirq得到virq
c. 调用 irq_desc[virq].handle_irq

ここに画像の説明を挿入

/**
 * struct irq_desc - interrupt descriptor
 * @irq_common_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
 * @nr_actions:		number of installed actions on this descriptor
 * @no_suspend_depth:	number of irqactions on a irq descriptor with
 *			IRQF_NO_SUSPEND set
 * @force_resume_depth:	number of irqactions on a irq descriptor with
 *			IRQF_FORCE_RESUME set
 * @rcu:		rcu head for delayed free
 * @kobj:		kobject used to represent this struct in sysfs
 * @request_mutex:	mutex to protect request/free before locking desc->lock
 * @dir:		/proc/irq/ procfs entry
 * @debugfs_file:	dentry for the debugfs file
 * @name:		flow handler name for /proc/interrupts output
 */
struct irq_desc {
    
    
	struct irq_common_data	irq_common_data;
	struct irq_data		irq_data;
	unsigned int __percpu	*kstat_irqs;
	irq_flow_handler_t	handle_irq;
#ifdef CONFIG_IRQ_PREFLOW_FASTEOI
	irq_preflow_handler_t	preflow_handler;
#endif
	struct irqaction	*action;	/* IRQ action list */
	unsigned int		status_use_accessors;
	unsigned int		core_internal_state__do_not_mess_with_it;
	unsigned int		depth;		/* nested irq disables */
	unsigned int		wake_depth;	/* nested wake enables */
	unsigned int		irq_count;	/* For detecting broken IRQs */
	unsigned long		last_unhandled;	/* Aging timer for unhandled count */
	unsigned int		irqs_unhandled;
	atomic_t		threads_handled;
	int			threads_handled_last;
	raw_spinlock_t		lock;
	struct cpumask		*percpu_enabled;
	const struct cpumask	*percpu_affinity;
#ifdef CONFIG_SMP
	const struct cpumask	*affinity_hint;
	struct irq_affinity_notify *affinity_notify;
#ifdef CONFIG_GENERIC_PENDING_IRQ
	cpumask_var_t		pending_mask;
#endif
#endif
	unsigned long		threads_oneshot;
	atomic_t		threads_active;
	wait_queue_head_t       wait_for_threads;
#ifdef CONFIG_PM_SLEEP
	unsigned int		nr_actions;
	unsigned int		no_suspend_depth;
	unsigned int		cond_suspend_depth;
	unsigned int		force_resume_depth;
#endif
#ifdef CONFIG_PROC_FS
	struct proc_dir_entry	*dir;
#endif
#ifdef CONFIG_GENERIC_IRQ_DEBUGFS
	struct dentry		*debugfs_file;
	const char		*dev_name;
#endif
#ifdef CONFIG_SPARSE_IRQ
	struct rcu_head		rcu;
	struct kobject		kobj;
#endif
	struct mutex		request_mutex;
	int			parent_irq;
	struct module		*owner;
	const char		*name;
} ____cacheline_internodealigned_in_smp;
typedef	void (*irq_flow_handler_t)(struct irq_desc *desc);
/**
 * 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
 * @secondary:	pointer to secondary irqaction (force threading)
 * @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;
	void __percpu		*percpu_dev_id;
	struct irqaction	*next;
	irq_handler_t		thread_fn;
	struct task_struct	*thread;
	struct irqaction	*secondary;
	unsigned int		irq;
	unsigned int		flags;
	unsigned long		thread_flags;
	unsigned long		thread_mask;
	const char		*name;
	struct proc_dir_entry	*dir;
} ____cacheline_internodealigned_in_smp;

ここに画像の説明を挿入

3. 割り込み番号と irq_domain の進化

1. 前回の割り込み処理処理

  以前は、割り込み番号 (virq) はハードウェアと密接に関連していましたが、現在の傾向では、割り込み番号はハードウェアとは関係がなく、単なるラベルです以前は、ハードウェア割り込み (hwirq) ごとにその割り込み番号 (virq) があらかじめ決められており、これらの割り込み番号は通常、arch\arm\mach-s3c24xx\include\mach\irqs.h などのヘッダー ファイルに書き込まれていました。
ここに画像の説明を挿入

使用时,
a. 执行 request_irq(virq, my_handler) :
   内核根据virq可以知道对应的硬件中断, 然后去设置、使能中断等
b. 发生硬件中断时,
   内核读取硬件信息, 确定hwirq, 反算出virq,
   然后调用 irq_desc[virq].handle_irq, 最终会用到my_handler

  hwirq に基づいて virq を計算するにはどうすればよいですか?
  ハードウェア上には複数の intc (割り込みコントローラ) があり、同じ hwirq 値に対して、異なる virq に対応します。そのため、 hwirq について話すときは、 hwirq から virq への変換を説明するときに、「どの割り込みコントローラの hwirq であるか」を強調する必要があります。概念を導入します: irq_domain、ドメイン、このドメインでは hwirq は特定の virq に変換されます

/**
 * struct irq_domain - Hardware interrupt number translation object
 * @link: Element in global irq_domain list.
 * @name: Name of interrupt domain
 * @ops: pointer to irq_domain methods
 * @host_data: private data pointer for use by owner.  Not touched by irq_domain
 *             core code.
 * @flags: host per irq_domain flags
 * @mapcount: The number of mapped interrupts
 *
 * Optional elements
 * @fwnode: Pointer to firmware node associated with the irq_domain. Pretty easy
 *          to swap it for the of_node via the irq_domain_get_of_node accessor
 * @gc: Pointer to a list of generic chips. There is a helper function for
 *      setting up one or more generic chips for interrupt controllers
 *      drivers using the generic chip library which uses this pointer.
 * @parent: Pointer to parent irq_domain to support hierarchy irq_domains
 * @debugfs_file: dentry for the domain debugfs file
 *
 * Revmap data, used internally by irq_domain
 * @revmap_direct_max_irq: The largest hwirq that can be set for controllers that
 *                         support direct mapping
 * @revmap_size: Size of the linear map table @linear_revmap[]
 * @revmap_tree: Radix map tree for hwirqs that don't fit in the linear map
 * @linear_revmap: Linear table of hwirq->virq reverse mappings
 */
struct irq_domain {
    
    
	struct list_head link;
	const char *name;
	const struct irq_domain_ops *ops;
	void *host_data;
	unsigned int flags;
	unsigned int mapcount;

	/* Optional data */
	struct fwnode_handle *fwnode;
	enum irq_domain_bus_token bus_token;
	struct irq_domain_chip_generic *gc;
#ifdef	CONFIG_IRQ_DOMAIN_HIERARCHY
	struct irq_domain *parent;
#endif
#ifdef CONFIG_GENERIC_IRQ_DEBUGFS
	struct dentry		*debugfs_file;
#endif

	/* reverse map data. The linear map gets appended to the irq_domain */
	irq_hw_number_t hwirq_max;
	unsigned int revmap_direct_max_irq;
	unsigned int revmap_size;
	struct radix_tree_root revmap_tree;
	struct mutex revmap_tree_mutex;
	unsigned int linear_revmap[];
};

  割り込みコントローラーの数が増え、割り込みの数も増えると、上記の方法 (virq および hwirq の固定バインディング) には欠陥が生じます。

a. 增加工作量, 你需要给每一个中断确定它的中断号, 写出对应的宏, 可能有成百上千个
b. 你要确保每一个硬件中断对应的中断号互不重复

  改善する方法はありますか?

a. hwirq跟virq之间不再绑定

b. 要使用某个hwirq时, 
   先在irq_desc数组中找到一个空闲项, 它的位置就是virq
   再在irq_desc[virq]中放置处理函数

2. デバイスツリー使用後の割り込み処理

ここに画像の説明を挿入

intc: 割り込みコントローラ
ここに画像の説明を挿入
ここに画像の説明を挿入
  新しい割り込みシステムでの割り込みの使用方法:

a.以前是request_irq发起,
  现在是先在设备树文件中声明想使用哪一个中断(哪一个中断控制器下的哪一个中断)

b. 内核解析设备树时,
   会根据"中断控制器"确定irq_domain,
   根据"哪一个中断"确定hwirq, 
   然后在irq_desc数组中找出一个空闲项, 它的位置就是virq
   并且把virq和hwirq的关系保存在irq_domain中: 
   		irq_domain.linear_revmap[hwirq] = virq;

c. 驱动程序 request_irq(virq, my_handler)

d. 发生硬件中断时,
	内核读取硬件信息, 确定hwirq, 确定 virq =  irq_domain.linear_revmap[hwirq];
然后调用 irq_desc[virq].handle_irq, 最终会用到my_handler

假设要使用子中断控制器(subintc)的n号中断, 它发生时会导致父中断控制器(intc)的m号
中断:
	1. 设备树表明要使用<subintc n>
   		subintc表示要使用<intc m>
	2. 解析设备树时,
  		会为<subintc n>找到空闲项 irq_desc[virq'], sub irq_domain.linear_revmap[n] = virq';
   
   		会为<intc m>   找到空闲项 irq_desc[virq], irq_domain.linear_revmap[m] = virq;
   		并且设置它的handle_irq为某个分析函数demux_func

	3. 驱动程序 request_irq(virq', my_handler)

d. 发生硬件中断时,
	内核读取intc硬件信息, 确定hwirq = m, 确定 virq =  irq_domain.linear_revmap[m];
然后调用 irq_desc[m].handle_irq, 即demux_func

e. demux_func:
	读取sub intc硬件信息, 确定hwirq = n, 确定 virq' =  sub irq_domain.linear_revmap[n];
然后调用 irq_desc[n].handle_irq, 即my_handler

4. 例_デバイス ツリーを使用して S3C2440 での割り込みエクスペリエンスを説明する

a. 某个设备要使用中断, 需要在设备树中描述中断, 如何描述?
   它要用哪一个中断? 这个中断连接到哪一个中断控制器去?: 使用哪一个中断控制器的哪一个中断?
   
   至少有有2个属性:
   interrupts        // 表示要使用哪一个中断, 中断的触发类型等等
   interrupt-parent  // 这个中断要接到哪一个设备去? 即父中断控制器是谁
b. 上述的interrupts属性用多少个u32来表示?
   这应该由它的父中断控制器来描述,
   在父中断控制器中, 至少有2个属性:
   interrupt-controller;   // 表示自己是一个中断控制器
    #interrupt-cells       // 表示自己的子设备里应该有几个U32的数据来描述中断

  デバイスツリーのデバイスノードに「どの割り込みコントローラのどの割り込み、どの割り込みトリガモードで使用するか」を示す「割り込みハードウェア情報」を記述し、

  デバイスノードはplatform_deviceに変換され、「割り込みハードウェア情報」は「割り込み番号」に変換され、platform_deviceの「割り込みリソース」に格納されます。ドライバは「割り込み」から割り込み番号を取り出します。 platform_device の resource" を指定して request_irq を作成できます。

实验:
a. jz2440_irq.dts" 放入内核 arch/arm/boot/dts目录,
在内核根目录下执行:make dtbs   // 得到 arch/arm/boot/dts/jz2440_irq.dtb
使用这个jz2440_irq.dtb启动内核;

b. 编译、测试驱动:
b.1 把 buttons_drv 上传到ubuntu
b.2 编译驱动:
export  PATH=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/work/system/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabi/bin

cd buttons_drv
make   // 得到 buttons.ko

b.3 编译测试程序:
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/usr/local/arm/4.3.2/bin

cd buttons_drv

arm-linux-gcc -o buttons_test  buttons_test.c

b.4 测试:
insmod buttons.ko
./buttons_test &
然后按键

button.c ファイル

#include <linux/module.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
//#include <asm/arch/regs-gpio.h>
//#include <asm/hardware.h>
#include <linux/device.h>
//#include <mach/gpio.h>
#include <linux/interrupt.h>
#include <linux/poll.h>
#include <mach/regs-gpio.h>
#include <mach/gpio-samsung.h>
#include <plat/gpio-cfg.h>

#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>

static struct class *sixthdrv_class;
static struct device	*sixthdrv_class_dev;

volatile unsigned long *gpfcon;
volatile unsigned long *gpfdat;

volatile unsigned long *gpgcon;
volatile unsigned long *gpgdat;

static struct timer_list buttons_timer;


static DECLARE_WAIT_QUEUE_HEAD(button_waitq);

/* 中断事件标志, 中断服务程序将它置1,sixth_drv_read将它清0 */
static volatile int ev_press = 0;

static struct fasync_struct *button_async;


struct pin_desc{
    
    
	unsigned int pin;
	unsigned int key_val;
	int irq;
};


/* 键值: 按下时, 0x01, 0x02, 0x03, 0x04 */
/* 键值: 松开时, 0x81, 0x82, 0x83, 0x84 */
static unsigned char key_val;

struct pin_desc pins_desc[4] = {
    
    
	{
    
    S3C2410_GPF(0), 0x01},
	{
    
    S3C2410_GPF(2), 0x02},
	{
    
    S3C2410_GPG(3), 0x03},
	{
    
    S3C2410_GPG(11), 0x04},
};

static struct pin_desc *irq_pd;

//static atomic_t canopen = ATOMIC_INIT(1);     //定义原子变量并初始化为1

//static DECLARE_MUTEX(button_lock);     //定义互斥锁
struct semaphore button_lock;


/*
  * 确定按键值
  */
static irqreturn_t buttons_irq(int irq, void *dev_id)
{
    
    
	/* 10ms后启动定时器 */
	irq_pd = (struct pin_desc *)dev_id;
	mod_timer(&buttons_timer, jiffies+HZ/100);
	return IRQ_RETVAL(IRQ_HANDLED);
}

static int sixth_drv_open(struct inode *inode, struct file *file)
{
    
    
	int ret;
	
#if 0	
	if (!atomic_dec_and_test(&canopen))
	{
    
    
		atomic_inc(&canopen);
		return -EBUSY;
	}
#endif		

	if (file->f_flags & O_NONBLOCK)
	{
    
    
		if (down_trylock(&button_lock))
			return -EBUSY;
	}
	else
	{
    
    
		/* 获取信号量 */
		down(&button_lock);
	}

	/* 配置GPF0,2为输入引脚 */
	/* 配置GPG3,11为输入引脚 */
	ret = request_irq(pins_desc[0].irq,  buttons_irq, 0, "S2", &pins_desc[0]);
	if (ret) {
    
    
		printk("reqeust_irq %d for EINT0 err : %d!\n", pins_desc[0].irq, ret);
		//return ret;
	}
	
	ret = request_irq(pins_desc[1].irq,  buttons_irq, 0, "S3", &pins_desc[1]);
	if (ret) {
    
    
		printk("reqeust_irq for EINT2 err : %d!\n", ret);
		//return ret;
	}

	ret = request_irq(pins_desc[2].irq, buttons_irq, 0, "S4", &pins_desc[2]);
	if (ret) {
    
    
		printk("reqeust_irq for EINT11 err : %d!\n", ret);
		//return ret;
	}

	ret = request_irq(pins_desc[3].irq, buttons_irq, 0, "S5", &pins_desc[3]);	
	if (ret) {
    
    
		printk("reqeust_irq for EINT19 err : %d!\n", ret);
		//return ret;
	}
	

	return 0;
}

ssize_t sixth_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
    
    
	if (size != 1)
		return -EINVAL;

	if (file->f_flags & O_NONBLOCK)
	{
    
    
		if (!ev_press)
			return -EAGAIN;
	}
	else
	{
    
    
		/* 如果没有按键动作, 休眠 */
		wait_event_interruptible(button_waitq, ev_press);
	}

	/* 如果有按键动作, 返回键值 */
	copy_to_user(buf, &key_val, 1);
	ev_press = 0;
	
	return 1;
}


int sixth_drv_close(struct inode *inode, struct file *file)
{
    
    
	int i;
	int ret;
	
	//atomic_inc(&canopen);
	for (i = 0; i < sizeof(pins_desc)/sizeof(pins_desc[0]); i++) {
    
    
		free_irq(pins_desc[i].irq, &pins_desc[i]);
	}
	up(&button_lock);
	return 0;
}

static unsigned sixth_drv_poll(struct file *file, poll_table *wait)
{
    
    
	unsigned int mask = 0;
	poll_wait(file, &button_waitq, wait); // 不会立即休眠

	if (ev_press)
		mask |= POLLIN | POLLRDNORM;

	return mask;
}

static int sixth_drv_fasync (int fd, struct file *filp, int on)
{
    
    
	printk("driver: sixth_drv_fasync\n");
	return fasync_helper (fd, filp, on, &button_async);
}


static struct file_operations sencod_drv_fops = {
    
    
    .owner   =  THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
    .open    =  sixth_drv_open,     
	.read	 =	sixth_drv_read,	   
	.release =  sixth_drv_close,
	.poll    =  sixth_drv_poll,
	.fasync	 =  sixth_drv_fasync,
};


int major;

static void buttons_timer_function(struct timer_list *t)
{
    
    
	struct pin_desc * pindesc = irq_pd;
	unsigned int pinval;

	if (!pindesc)
		return;
	
	pinval = gpio_get_value(pindesc->pin);

	if (pinval)
	{
    
    
		/* 松开 */
		key_val = 0x80 | pindesc->key_val;
	}
	else
	{
    
    
		/* 按下 */
		key_val = pindesc->key_val;
	}

    ev_press = 1;                  /* 表示中断发生了 */
    wake_up_interruptible(&button_waitq);   /* 唤醒休眠的进程 */
	
	kill_fasync (&button_async, SIGIO, POLL_IN);
}


static int sixth_drv_init(void)
{
    
    
	//init_timer(&buttons_timer);
	//buttons_timer.function = buttons_timer_function;
	//buttons_timer.expires  = 0;
	timer_setup(&buttons_timer, buttons_timer_function, 0);
	add_timer(&buttons_timer); 

	major = register_chrdev(0, "sixth_drv", &sencod_drv_fops);

	sixthdrv_class = class_create(THIS_MODULE, "sixth_drv");

	/* 为了让mdev根据这些信息来创建设备节点 */
	sixthdrv_class_dev = device_create(sixthdrv_class, NULL, MKDEV(major, 0), NULL, "buttons"); /* /dev/buttons */

	gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);
	gpfdat = gpfcon + 1;

	gpgcon = (volatile unsigned long *)ioremap(0x56000060, 16);
	gpgdat = gpgcon + 1;

	sema_init(&button_lock, 1);

	return 0;
}

static void sixth_drv_exit(void)
{
    
    
	del_timer(&buttons_timer); 
	unregister_chrdev(major, "sixth_drv");
	device_destroy(sixthdrv_class, MKDEV(major, 0));
	class_destroy(sixthdrv_class);
	iounmap(gpfcon);
	iounmap(gpgcon);
}

static int buttons_probe(struct platform_device *pdev)
{
    
    
	struct device *dev = &pdev->dev;		
	struct device_node *dp_node = dev->of_node;
	struct resource		*res;
	int i;

	for (i = 0; i < sizeof(pins_desc)/sizeof(pins_desc[0]); i++)
	{
    
    
		/* 根据platform_device的资源进行获得中断号,触发类型 */
		res = platform_get_resource(pdev, IORESOURCE_IRQ, i);
		if (res) {
    
    
			pins_desc[i].irq  = res->start;
			printk("get irq %d\n", pins_desc[i].irq);
		}
		else {
    
    
			printk("can not get irq res for eint0\n");
			return -1;
		}

		pins_desc[i].pin = of_get_named_gpio(dp_node, "eint-pins", i);
		printk("pins_desc[%d].pin = %d\n", i, pins_desc[i].pin);
	}

	return sixth_drv_init();
}

static int buttons_remove(struct platform_device *pdev)
{
    
    
	sixth_drv_exit();
	return 0;
}



static const struct of_device_id of_match_buttons[] = {
    
    
	{
    
     .compatible = "jz2440_button", .data = NULL },
	{
    
     /* sentinel */ }
};


struct platform_driver buttons_drv = {
    
    
	.probe		= buttons_probe,
	.remove		= buttons_remove,
	.driver		= {
    
    
		.name	= "mybuttons",
		.of_match_table = of_match_buttons, /* 能支持哪些来自于dts的platform_device */
	}
};


static int buttons_init(void)
{
    
    
	platform_driver_register(&buttons_drv);
	return 0;
}

static void buttons_exit(void)
{
    
    
	platform_driver_unregister(&buttons_drv);
}

module_init(buttons_init);
module_exit(buttons_exit);
MODULE_LICENSE("GPL");

デバイスツリーファイル

/dts-v1/;

/ {
    
    
	compatible = "samsung,s3c2440", "samsung,smdk2440";
	interrupt-parent = <0x1>;
	#address-cells = <0x1>;
	#size-cells = <0x1>;
	model = "JZ2440";

	aliases {
    
    
		pinctrl0 = "/pinctrl@56000000";
		serial0 = "/serial@50000000";
		serial1 = "/serial@50004000";
		serial2 = "/serial@50008000";
		i2c1 = "/i2c-gpio-1";
	};

	interrupt-controller@4a000000 {
    
    
		compatible = "samsung,s3c2410-irq";
		reg = <0x4a000000 0x100>;
		interrupt-controller;
		#interrupt-cells = <0x4>;
		phandle = <0x1>;
	};

	pinctrl@56000000 {
    
    
		reg = <0x56000000 0x1000>;
		compatible = "samsung,s3c2440-pinctrl";

		wakeup-interrupt-controller {
    
    
			compatible = "samsung,s3c2410-wakeup-eint";
			interrupts = <0x0 0x0 0x0 0x3 0x0 0x0 0x1 0x3 0x0 0x0 0x2 0x3 0x0 0x0 0x3 0x3 0x0 0x0 0x4 0x4 0x0 0x0 0x5 0x4>;
		};

		gpa {
    
    
			gpio-controller;
			#gpio-cells = <0x2>;
		};

		gpb {
    
    
			gpio-controller;
			#gpio-cells = <0x2>;
			phandle = <0xd>;
		};

		gpc {
    
    
			gpio-controller;
			#gpio-cells = <0x2>;
		};

		gpd {
    
    
			gpio-controller;
			#gpio-cells = <0x2>;
		};

		gpe {
    
    
			gpio-controller;
			#gpio-cells = <0x2>;
			phandle = <0x7>;
		};

		gpf {
    
    
			gpio-controller;
			#gpio-cells = <0x2>;
			interrupt-controller;
			#interrupt-cells = <0x2>;
			phandle = <0x6>;
		};

		gpg {
    
    
			gpio-controller;
			#gpio-cells = <0x2>;
			interrupt-controller;
			#interrupt-cells = <0x2>;
			phandle = <0xe>;
		};

		gph {
    
    
			gpio-controller;
			#gpio-cells = <0x2>;
		};

		gpj {
    
    
			gpio-controller;
			#gpio-cells = <0x2>;
		};

		uart0-data {
    
    
			samsung,pins = "gph-0", "gph-1";
			samsung,pin-function = <0x2>;
			phandle = <0x3>;
		};

		i2c0-bus {
    
    
			samsung,pins = "gpe-14", "gpe-15";
			samsung,pin-function = <0x2>;
			phandle = <0x4>;
		};

		nand_pinctrl {
    
    
			samsung,pins = "gpa-17", "gpa-18", "gpa-19", "gpa-20", "gpa-22";
			samsung,pin-function = <0x1>;
			phandle = <0x5>;
		};

		lcd_pinctrl {
    
    
			samsung,pins = "gpc-8", "gpc-9", "gpc-10", "gpc-11", "gpc-12", "gpc-13", "gpc-14", "gpc-15", "gpd-0", "gpd-1", "gpd-2", "gpd-3", "gpd-4", "gpd-5", "gpd-6", "gpd-7", "gpd-8", "gpd-9", "gpd-10", "gpd-11", "gpd-12", "gpd-13", "gpd-14", "gpd-15", "gpc-1", "gpc-2", "gpc-3", "gpc-4";
			samsung,pin-function = <0x2>;
			phandle = <0x8>;
		};

		lcd_backlight {
    
    
			samsung,pins = "gpg-4";
			samsung,pin-function = <0x3>;
			phandle = <0x9>;
		};

		uda1340_codec_pinctrl {
    
    
			samsung,pins = "gpb-4", "gpb-3", "gpb-2";
			samsung,pin-function = <0x1>;
			phandle = <0xc>;
		};

		s3c2440_iis_pinctrl {
    
    
			samsung,pins = "gpe-0", "gpe-1", "gpe-2", "gpe-3", "gpe-4";
			samsung,pin-function = <0x2>;
			phandle = <0xa>;
		};
	};

	timer@51000000 {
    
    
		compatible = "samsung,s3c2410-pwm";
		reg = <0x51000000 0x1000>;
		interrupts = <0x0 0x0 0xa 0x3 0x0 0x0 0xb 0x3 0x0 0x0 0xc 0x3 0x0 0x0 0xd 0x3 0x0 0x0 0xe 0x3>;
		#pwm-cells = <0x4>;
		clock-names = "timers";
		clocks = <0x2 0x19>;
	};

	serial@50000000 {
    
    
		compatible = "samsung,s3c2440-uart";
		reg = <0x50000000 0x4000>;
		interrupts = <0x1 0x1c 0x0 0x4 0x1 0x1c 0x1 0x4>;
		status = "okay";
		clock-names = "uart";
		clocks = <0x2 0x10>;
		pinctrl-names = "default";
		pinctrl-0 = <0x3>;
	};

	serial@50004000 {
    
    
		compatible = "samsung,s3c2410-uart";
		reg = <0x50004000 0x4000>;
		interrupts = <0x1 0x17 0x3 0x4 0x1 0x17 0x4 0x4>;
		status = "disabled";
	};

	serial@50008000 {
    
    
		compatible = "samsung,s3c2410-uart";
		reg = <0x50008000 0x4000>;
		interrupts = <0x1 0xf 0x6 0x4 0x1 0xf 0x7 0x4>;
		status = "disabled";
	};

	watchdog@53000000 {
    
    
		compatible = "samsung,s3c2410-wdt";
		reg = <0x53000000 0x100>;
		interrupts = <0x1 0x9 0x1b 0x3>;
		status = "okay";
		clocks = <0x2 0x6>;
		clock-names = "watchdog";
	};

	rtc@57000000 {
    
    
		compatible = "samsung,s3c2410-rtc";
		reg = <0x57000000 0x100>;
		interrupts = <0x0 0x0 0x1e 0x3 0x0 0x0 0x8 0x3>;
		status = "okay";
		clocks = <0x2 0x1a>;
		clock-names = "rtc";
	};

	i2c@54000000 {
    
    
		compatible = "samsung,s3c2440-i2c";
		reg = <0x54000000 0x100>;
		interrupts = <0x0 0x0 0x1b 0x3>;
		#address-cells = <0x1>;
		#size-cells = <0x0>;
		status = "disabled";
		clocks = <0x2 0x13>;
		clock-names = "i2c";
		pinctrl-names = "default";
		pinctrl-0 = <0x4>;
	};

	cpus {
    
    
		#address-cells = <0x1>;
		#size-cells = <0x0>;

		cpu {
    
    
			compatible = "arm,arm920t";
		};
	};

	xti_clock {
    
    
		compatible = "fixed-clock";
		clock-frequency = <0xb71b00>;
		clock-output-names = "xti";
		#clock-cells = <0x0>;
	};

	clock-controller@4c000000 {
    
    
		compatible = "samsung,s3c2440-clock";
		reg = <0x4c000000 0x20>;
		#clock-cells = <0x1>;
		phandle = <0x2>;
	};

	nand@4e000000 {
    
    
		compatible = "samsung,s3c2440-nand";
		reg = <0x4e000000 0x40>;
		interrupts = <0x0 0x0 0x18 0x3>;
		clocks = <0x2 0x23>;
		clock-names = "nand";
		pinctrl-names = "default";
		pinctrl-0 = <0x5>;
		status = "okay";
		nand,tacls = <0xa>;
		nand,twrph0 = <0x19>;
		nand,twrph1 = <0xa>;
		#address-cells = <0x1>;
		#size-cells = <0x1>;

		partitions {
    
    
			#address-cells = <0x1>;
			#size-cells = <0x1>;
			nr-chips = <0x1>;
			set-name = "jz2440-0";

			partition@0 {
    
    
				label = "bootloader";
				reg = <0x0 0x40000>;
				read-only;
			};

			partition@40000 {
    
    
				label = "device_tree";
				reg = <0x40000 0x20000>;
				read-only;
			};

			partition@60000 {
    
    
				label = "params";
				reg = <0x60000 0x20000>;
				read-only;
			};

			partition@80000 {
    
    
				label = "kernel";
				reg = <0x80000 0x400000>;
				read-only;
			};

			partition@480000 {
    
    
				label = "rootfs";
				reg = <0x480000 0x0>;
			};
		};
	};

	usb_ohci@49000000 {
    
    
		compatible = "samsung,s3c2440-ohci";
		reg = <0x49000000 0x60>;
		interrupts = <0x0 0x0 0x1a 0x3>;
		clocks = <0x2 0x21 0x2 0x7>;
		clock-names = "usb-host", "usb-bus-host";
		status = "okay";
	};

	memory {
    
    
		device_type = "memory";
		reg = <0x30000000 0x4000000>;
	};

	chosen {
    
    
		bootargs = "noinitrd root=/dev/mtdblock4 rw init=/linuxrc console=ttySAC0,115200";
	};

	srom-cs4@20000000 {
    
    
		compatible = "simple-bus";
		#address-cells = <0x1>;
		#size-cells = <0x1>;
		reg = <0x20000000 0x8000000>;
		ranges;

		ethernet@20000000 {
    
    
			compatible = "davicom,dm9000";
			reg = <0x20000000 0x2 0x20000004 0x2>;
			interrupt-parent = <0x6>;
			interrupts = <0x7 0x1>;
			local-mac-address = [00 00 de ad be ef];
			davicom,no-eeprom;
		};
	};

	i2c-gpio-1 {
    
    
		compatible = "i2c-gpio";
		#address-cells = <0x1>;
		#size-cells = <0x0>;
		gpios = <0x7 0xf 0x0 0x7 0xe 0x0>;
		i2c-gpio,delay-us = <0x5>;
		status = "disabled";

		eeprom@50 {
    
    
			compatible = "24c02";
			reg = <0x50>;
			pagesize = <0x20>;
			status = "okay";
		};
	};

	fb@4d000000 {
    
    
		compatible = "jz2440,lcd";
		reg = <0x4d000000 0x60>;
		interrupts = <0x0 0x0 0x10 0x3>;
		clocks = <0x2 0x20>;
		clock-names = "lcd";
		pinctrl-names = "default";
		pinctrl-0 = <0x8 0x9>;
		status = "okay";
		lcdcon5 = <0xb09>;
		type = <0x60>;
		width = [01 e0];
		height = [01 10];
		pixclock = <0x186a0>;
		xres = [01 e0];
		yres = [01 10];
		bpp = [00 10];
		left_margin = [00 02];
		right_margin = [00 02];
		hsync_len = [00 29];
		upper_margin = [00 02];
		lower_margin = [00 02];
		vsync_len = [00 0a];
	};

	jz2440ts@5800000 {
    
    
		compatible = "jz2440,ts";
		reg = <0x58000000 0x100>;
		reg-names = "adc_ts_physical";
		interrupts = <0x1 0x1f 0x9 0x3 0x1 0x1f 0xa 0x3>;
		interrupt-names = "int_ts", "int_adc_s";
		clocks = <0x2 0x16>;
		clock-names = "adc";
	};

	s3c2410-dma@4B000000 {
    
    
		compatible = "s3c2440-dma";
		reg = <0x4b000000 0x1000>;
		interrupts = <0x0 0x0 0x11 0x3 0x0 0x0 0x12 0x3 0x0 0x0 0x13 0x3 0x0 0x0 0x14 0x3>;
		#dma-cells = <0x1>;
		phandle = <0xb>;
	};

	s3c2440_iis@55000000 {
    
    
		compatible = "s3c24xx-iis";
		reg = <0x55000000 0x100>;
		clocks = <0x2 0x18>;
		clock-names = "iis";
		pinctrl-names = "default";
		pinctrl-0 = <0xa>;
		dmas = <0xb 0x9 0xb 0xa>;
		dma-names = "rx", "tx";
	};

	s3c24xx_uda134x {
    
    
		compatible = "s3c24xx_uda134x";
		clocks = <0x2 0x2 0x2 0x18>;
		clock-names = "mpll", "iis";
	};

	uda134x-codec {
    
    
		compatible = "uda134x-codec";
		pinctrl-names = "default";
		pinctrl-0 = <0xc>;
		uda,clk_gpio = <0xd 0x4 0x1>;
		uda,data_gpio = <0xd 0x3 0x1>;
		uda,mode_gpio = <0xd 0x2 0x1>;
		uda,use_gpios;
		uda,data_hold;
		uda,data_setup;
		uda,clock_high;
		uda,mode_hold;
		uda,mode;
		uda,mode_setup;
		uda,model = <0x2>;
	};

	buttons {
    
    
		compatible = "jz2440_button";
		eint-pins = <0x6 0x0 0x0 0x6 0x2 0x0 0xe 0x3 0x0 0xe 0xb 0x0>;
		interrupts-extended = <0x1 0x0 0x0 0x0 0x3 0x1 0x0 0x0 0x2 0x3 0xe 0x3 0x3 0xe 0xb 0x3>;
	};
};

ボタン駆動のアプリケーション層テスト プログラム:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <poll.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>


/* sixthdrvtest 
  */
int fd;

void my_signal_fun(int signum)
{
    
    
	unsigned char key_val;
	read(fd, &key_val, 1);
	printf("key_val: 0x%x\n", key_val);
}

int main(int argc, char **argv)
{
    
    
	unsigned char key_val;
	int ret;
	int Oflags;

	//signal(SIGIO, my_signal_fun);
	
	fd = open("/dev/buttons", O_RDWR);
	if (fd < 0)
	{
    
    
		printf("can't open!\n");
		return -1;
	}

	//fcntl(fd, F_SETOWN, getpid());
	
	//Oflags = fcntl(fd, F_GETFL); 
	
	//fcntl(fd, F_SETFL, Oflags | FASYNC);


	while (1)
	{
    
    
		ret = read(fd, &key_val, 1);
		printf("key_val: 0x%x, ret = %d\n", key_val, ret);
		//sleep(5);
	}
	
	return 0;
}

五、カーネルによるデバイスツリー割り込み情報の処理

  ハードウェア構造の観点から見ると、処理プロセスは、割り込みコントローラと割り込みを使用するデバイスの2 つのレベルに分かれています。

  ソフトウェア構造の観点から見ると、処理プロセスはデバイス ツリーへの情報の記述と、ドライバーでのデバイス ツリーの処理の2 つの部分に分かれます。

(1) 中断控制器
这又分为root irq controller, gpf/gpg irq controller
a. root irq controller
a.1 在设备树中的描述
a.2 在内核中的驱动 

b. 对于S3C2440, 还有: gpf/gpg irq controller
b.1 在设备树中的描述(在pinctrl节点里)
b.2 在内核中的驱动 (在pinctrl驱动中)

(2) 设备的中断
a.1 在设备节点中描述(表明使用"哪一个中断控制器里的哪一个中断, 及中断触发方式")
a.2 在内核中的驱动 (在platform_driver.probe中获得IRQ资源, 即中断号)

irq_domain是核心:
a. 每一个中断控制器都有一个irq_domain
b. 对设备中断信息的解析, 
b.1 需要调用 irq_domain->ops->xlate (即从设备树中获得hwirq, type)
b.2 获取未使用的virq, 保存: irq_domain->linear_revmap[hwirq] = virq;
b.3 在hwirq和virq之间建立联系:
   要调用 irq_domain->ops->map, 比如根据hwirq的属性设置virq的中断处理函数(是一
个分发函数还是可以直接处理中断)
        irq_desc[virq].handle_irq = 常规函数;
   如果这个hwirq有上一级中断, 假设它的中断号为virq', 还要设置: 
        irq_desc[virq'].handle_irq = 中断分发函数;

(1) 割り込み処理を発生させるにはどうすればよいですか?

a. カーネル起動時の初期化割り込みのエントリ:

start_kernel // init/main.c
    init_IRQ();
        if (IS_ENABLED(CONFIG_OF) && !machine_desc->init_irq)
            irqchip_init();   // 一般使用它
        else
            machine_desc->init_irq();

b. デバイス ツリー内の割り込みコントローラの処理エントリ:

irqchip_init // drivers/irqchip/irqchip.c
    of_irq_init(__irqchip_of_table);  // 对设备树文件中每一个中断控制器节点, 调用对应的处理函数
        为每一个符合的"interrupt-controller"节点,
        分配一个of_intc_desc结构体, desc->irq_init_cb = match->data; // = IRQCHIP_DECLARE中传入的函数
        并调用处理函数
        
        (先调用root irq controller对应的函数, 再调用子控制器的函数, 再调用更下一级控制器的函数...)

(2) ルート IRQ コントローラーのドライバー呼び出しプロセス:

a. root IRQ コントローラーの処理関数を定義します。

IRQCHIP_DECLARE(s3c2410_irq, "samsung,s3c2410-irq", s3c2410_init_intc_of);  //drivers/irqchip/irq-s3c24xx.c

其中:
#define IRQCHIP_DECLARE(name, compat, fn) OF_DECLARE_2(irqchip, name, compat, fn)
#define OF_DECLARE_2(table, name, compat, fn) \
        _OF_DECLARE(table, name, compat, fn, of_init_fn_2)
#define _OF_DECLARE(table, name, compat, fn, fn_type)           \
    static const struct of_device_id __of_table_##name      \
        __used __section(__##table##_of_table)          \
         = {
      
       .compatible = compat,              \
             .data = (fn == (fn_type)NULL) ? fn : fn  }

展开为:
    static const struct of_device_id __of_table_s3c2410_irq     \
        __used __section("__irqchip_of_table")          \
         = {
    
     .compatible = "samsung,s3c2410-irq",               \
             .data = s3c2410_init_intc_of  }

它定义了一个of_device_id结构体, 段属性为"__irqchip_of_table", 在编译内核时
这些段被放在__irqchip_of_table地址处。即__irqchip_of_table起始地址处,放置
了一个或多个 of_device_id, 它含有compatible成员;

设备树中的设备节点含有compatible属性,如果双方的compatible相同, 并且设备节点
含有"interrupt-controller"属性,则调用of_device_id中的函数来处理该设备节点。

所以: IRQCHIP_DECLARE 是用来声明设备树中的中断控制器的处理函数。

b. root IRQ コントローラー処理関数の実行プロセス:

s3c2410_init_intc_of  // drivers/irqchip/irq-s3c24xx.c
    // 初始化中断控制器: intc, subintc
    s3c_init_intc_of(np, interrupt_parent, s3c2410_ctrl, ARRAY_SIZE(s3c2410_ctrl));
                
        // 为中断控制器创建irq_domain
        domain = irq_domain_add_linear(np, num_ctrl * 32,
                                 &s3c24xx_irq_ops_of, NULL);

        intc->domain = domain;

        // 设置handle_arch_irq, 即中断处理的C语言总入口函数
        set_handle_irq(s3c24xx_handle_irq);

(3) pinctrl システムの gpf/gpg irq コントローラーのドライバー呼び出しプロセス:

a. pinctrl システムのドライバー:

a.1 源代码: drivers/pinctrl/samsung/pinctrl-samsung.c
static struct platform_driver samsung_pinctrl_driver = {
    
    
    .probe      = samsung_pinctrl_probe,
    .driver = {
    
    
        .name   = "samsung-pinctrl",
        .of_match_table = samsung_pinctrl_dt_match, // 含有 { .compatible = "samsung,s3c2440-pinctrl", .data = &s3c2440_of_data },
        .suppress_bind_attrs = true,
        .pm = &samsung_pinctrl_pm_ops,
    },
};

a.2 设备树中:
pinctrl@56000000 {
    
    
    reg = <0x56000000 0x1000>;
    compatible = "samsung,s3c2440-pinctrl";  // 据此找到驱动

a.3 驱动中的操作:
samsung_pinctrl_probe  // drivers/pinctrl/samsung/pinctrl-samsung.c
    最终会调用到 s3c24xx_eint_init // drivers/pinctrl/samsung/pinctrl-s3c24xx.c
    
        // eint0,1,2,3的处理函数在处理root irq controller时已经设置; 
        // 设置eint4_7, eint8_23的处理函数(它们是分发函数)
        for (i = 0; i < NUM_EINT_IRQ; ++i) {
    
    
            unsigned int irq;

            if (handlers[i]) /* add by [email protected], 不再设置eint0,1,2,3的处理函数 */
            {
    
    
                irq = irq_of_parse_and_map(eint_np, i);
                if (!irq) {
    
    
                    dev_err(dev, "failed to get wakeup EINT IRQ %d\n", i);
                    return -ENXIO;
                }

                eint_data->parents[i] = irq;
                irq_set_chained_handler_and_data(irq, handlers[i], eint_data);
            }
        }

        // 为GPF、GPG设置irq_domain
        for (i = 0; i < d->nr_banks; ++i, ++bank) {
    
    
        
            ops = (bank->eint_offset == 0) ? &s3c24xx_gpf_irq_ops
                               : &s3c24xx_gpg_irq_ops;

            bank->irq_domain = irq_domain_add_linear(bank->of_node, bank->nr_pins, ops, ddata);
        }

(4) 割り込みを利用したドライバ呼び出し処理:

a. デバイスノードに記述(「どの割り込みコントローラのどの割り込み、割り込みトリガモード」を使用するかを示す)

比如:
    buttons {
    
    
        compatible = "jz2440_button";
        eint-pins  = <&gpf 0 0>, <&gpf 2 0>, <&gpg 3 0>, <&gpg 11 0>;
        interrupts-extended = <&intc 0 0 0 3>,
                              <&intc 0 0 2 3>,
                              <&gpg 3 3>,
                              <&gpg 11 3>;
    };

b. デバイス ノードは platform_device に変換されます

   "中断的硬件信息" 会转换为"中断号", 
   保存在platform_device的"中断资源"里

   在之前讲解了设备树中设备节点转换为 platform_device 的过程;
   我们只关心里面对中断信息的处理:

of_device_alloc (drivers/of/platform.c)
    dev = platform_device_alloc("", PLATFORM_DEVID_NONE);  // 分配 platform_device
    
    num_irq = of_irq_count(np);  // 计算中断数
    
    of_irq_to_resource_table(np, res, num_irq) // drivers/of/irq.c, 根据设备节点中的中断信息, 构造中断资源
        of_irq_to_resource
            int irq = of_irq_get(dev, index);  // 获得virq, 中断号
                            rc = of_irq_parse_one(dev, index, &oirq); // drivers/of/irq.c, 解析设备树中的中断信息, 保存在of_phandle_args结构体中
                            
                            domain = irq_find_host(oirq.np);   // 查找irq_domain, 每一个中断控制器都对应一个irq_domain
                            
                            irq_create_of_mapping(&oirq);             // kernel/irq/irqdomain.c, 创建virq和中断信息的映射
                                irq_create_fwspec_mapping(&fwspec);
                                    irq_create_fwspec_mapping(&fwspec);
                                        irq_domain_translate(domain, fwspec, &hwirq, &type) // 调用irq_domain->ops->xlate, 把设备节点里的中断信息解析为hwirq, type
                                        
                                        virq = irq_find_mapping(domain, hwirq); // 看看这个hwirq是否已经映射, 如果virq非0就直接返回
                                        
                                        virq = irq_create_mapping(domain, hwirq); // 否则创建映射
                                                    virq = irq_domain_alloc_descs(-1, 1, hwirq, of_node_to_nid(of_node), NULL);  // 返回未占用的virq
                                                    
                                                    irq_domain_associate(domain, virq, hwirq) // 调用irq_domain->ops->map(domain, virq, hwirq), 做必要的硬件设置

c. ドライバーは、platform_device の「割り込みリソース」から割り込み番号を取り出し、request_irq を実行できます。

注: この記事は、「魏東山氏の組み込みコース」、「朱友鵬氏の組み込みコース」、「郭天祥氏の 51 シングルチップ マイコンの本」のメモを、彼自身の実際の開発経験と技術的な内容と組み合わせて参照しています。インターネット上の他人の記事を総合的に整理して入手。侵害がある場合は、削除するために連絡してください。レベルは限られています、コメント欄でのコミュニケーションを歓迎します

おすすめ

転載: blog.csdn.net/weixin_45842280/article/details/125074506