• Linux中断子系统:级联中断控制器驱动


    Linux中断子系统

    Linux中断子系统是个很大的话题,如下面的思维导图所示,包含硬件、驱动、中断上半部、中断下半部等等。本文着眼于中断控制器(PIC),特别是级联中断控制器驱动部分,对驱动的设计和调试要点进行分析总结。

    级联中断控制器驱动

    中断控制器的核心对象是irq_chip,其提供了很多接口,一般情况下不需要全部实现。下面是几个类似的接口的差别介绍:

    1. irq_enable:在中断注册或使能时调用,一般在使能中断前需要清除中断状态。
    2. irq_disable:在中断注销或关闭时调用,一般在关闭中断前不清除中断状态。
    3. irq_mask_ack:在进入中断处理函数前调用,一般在屏蔽中断前需要清除中断源信号。
    4. irq_unmask:在退出中断处理函数后调用,一般在去屏蔽中断前不清除清除中断源信号。

    级联中断控制器的初始化流程

    中断控制器的级联如上图所示。级联中断控制器的初始化流程分为2部分:根中断控制器的初始化和子中断控制器的初始化。

    根中断控制器相关部分

    1. 根据irq获取根PIC的irt;
    2. 将irt/irq的映射关系配置到根PIC的IRT表寄存器;
    3. 初始化根PIC的irq_chip接口对象;
    4. 调用irq_set_chip_and_handler设置根PIC irq的irq_chip及其handler;
    5. 调用irq_set_chip_data设置根PIC irq_chip接口调用时的环境对象;

    子中断控制器相关部分

    1. 初始化子PIC的irq_chip接口对象;
    2. 映射子PIC io mem空间,以便后续访问其寄存器;
    3. 获取PIC的irq domain的irq基值,并调用irq_domain_add_xxx获取子PIC的中断域空间;
    4. 调用irq_set_chip_and_handler设置子PIC irq的irq_chip及其handler;
    5. 调用irq_set_chip_data设置子PIC irq_chip接口调用时的环境对象;
    6. 获取根PIC的irq;
    7. 调用irq_set_chained_handler设置根PIC irq handler;
    8. 调用irq_set_handler_data设置根PIC irq handler调用时的环境对象;

    级联中断控制器的中断处理流程

    首先,CPU只能感知到root PIC的中断,当root PIC的中断触发后,进入root irq handler,在handler中找到触发中断的sub irq,并调用其handler进行中断处理。换句话说,root irq的handler是root PIC的中断路由功能的软件扩展。

    找到sub irq的方法依赖sub PIC具体硬件实现,以GPIO中断控制器的root irq handler为例,其方法如下:

    1. 调用chained_irq_enter,mask_ack root irq;
    2. 获取gpio 中断pin脚使能情况;
    3. 读取gpio interrupt status寄存器,判断此gpio 中断使能pin脚是否触发了中断;
    4. 通过irq_find_mapping获取此gpio pin对应的irq#;
    5. 通过generic_handle_irq调用 sub irq handler来进行gpio中断处理;
    6. 调用chained_irq_exit,unmask root irq。

    中断控制器驱动调试要点

    1. 根据中断控制器特点,仔细设计irq_chip的接口。那么哪些需要实现,哪些又不需要是需要仔细考虑;
    2. root irq的handler要用chained_irq_enter/chained_irq_exit进行保护;
    3. 理顺中断触发时序。比如设备、子PIC的中断状态、中断源信号等中断标记清除顺序,以此来决定在哪些irq_chip接口中清除中断标记;
    4. 一般来说,对于任何中断设备,在使能中断前需要清除中断状态,避免垃圾中断干扰。

    附关键数据结构

    /**
     * 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_print_chip:	optional to print special chip info in show_interrupts
     * @flags:		chip specific flags
     */
    struct irq_chip {
    	const char	*name;
    	unsigned int	(*irq_startup)(struct irq_data *data);
    	void		(*irq_shutdown)(struct irq_data *data);
    	void		(*irq_enable)(struct irq_data *data);
    	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);
    	int		(*irq_retrigger)(struct irq_data *data);
    	int		(*irq_set_type)(struct irq_data *data, unsigned int flow_type);
    	int		(*irq_set_wake)(struct irq_data *data, unsigned int on);
    
    	void		(*irq_bus_lock)(struct irq_data *data);
    	void		(*irq_bus_sync_unlock)(struct irq_data *data);
    
    	void		(*irq_cpu_online)(struct irq_data *data);
    	void		(*irq_cpu_offline)(struct irq_data *data);
    
    	void		(*irq_suspend)(struct irq_data *data);
    	void		(*irq_resume)(struct irq_data *data);
    	void		(*irq_pm_shutdown)(struct irq_data *data);
    
    	void		(*irq_print_chip)(struct irq_data *data, struct seq_file *p);
    
    	unsigned long	flags;
    };
    
    /**
     * struct irq_data - per irq and irq chip data passed down to chip functions
     * @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 {
    	unsigned int		irq;
    	unsigned long		hwirq;
    	unsigned int		node;
    	unsigned int		state_use_accessors;
    	struct irq_chip		*chip;
    	struct irq_domain	*domain;
    	void			*handler_data;
    	void			*chip_data;
    	struct msi_desc		*msi_desc;
    	cpumask_var_t		affinity;
    };
    
    /**
     * 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
     * @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;
    	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;
    	u64			random_ip;
    	raw_spinlock_t		lock;
    	struct cpumask		*percpu_enabled;
    #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_PROC_FS
    	struct proc_dir_entry	*dir;
    #endif
    	int			parent_irq;
    	struct module		*owner;
    	const char		*name;
    } ____cacheline_internodealigned_in_smp;
    

      

    --EOF--

  • 相关阅读:
    Hive架构(数据库和表)?
    BUG管理工具的跟踪过程(用BugZilla为例子)
    shiro有哪些组件?
    接口绑定有几种实现方式,分别是怎么实现的?
    什么是 MyBatis 的接口绑定,有什么好处?
    MyBatis 的好处是什么?
    MyBatis 与 Hibernate 有哪些不同?
    什么是 MyBatis 的接口绑定,有什么好处?
    MyBatis 的好处是什么?
    MyBatis 与 Hibernate 有哪些不同?
  • 原文地址:https://www.cnblogs.com/wahaha02/p/7019783.html
Copyright © 2020-2023  润新知