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--

Linux中断子系统:级联中断控制器驱动的更多相关文章

  1. linux中断子系统:中断号的映射与维护初始化mmap过程

    本文均属自己阅读源代码的点滴总结.转账请注明出处谢谢. 欢迎和大家交流.qq:1037701636 email:gzzaigcn2009@163.com 写在前沿: 好久好久没有静下心来整理一些东西了 ...

  2. 【原创】Linux中断子系统(一)-中断控制器及驱动分析

    背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本: ...

  3. Linux输入子系统详解

    input输入子系统框架  linux输入子系统(linux input subsystem)从上到下由三层实现,分别为:输入子系统事件处理层(EventHandler).输入子系统核心层(Input ...

  4. Linux kernel的中断子系统之(五):驱动申请中断API

    返回目录:<ARM-Linux中断系统>. 总结:二重点区分了抢占式内核和非抢占式内核的区别:抢占式内核可以在内核空间进行抢占,通过对中断处理进行线程化可以提高Linux内核实时性. 三介 ...

  5. Linux kernel中断子系统之(五):驱动申请中断API【转】

    转自:http://www.wowotech.net/linux_kenrel/request_threaded_irq.html 一.前言 本文主要的议题是作为一个普通的驱动工程师,在撰写自己负责的 ...

  6. Linux kernel的中断子系统之(一):综述

    返回目录:<ARM-Linux中断系统>. 总结: 一从作为一名驱动工程师角度看,用好中断需要正确认识request_threaded_irq/request_irq关系.中断临界区保护. ...

  7. Linux kernel的中断子系统之(三):IRQ number和中断描述符

    返回目录:<ARM-Linux中断系统>. 总结: 二描述了中断处理示意图,以及关中断.开中断,和IRQ number重要概念. 三介绍了三个重要的结构体,irq_desc.irq_dat ...

  8. Linux kernel的中断子系统之(二):IRQ Domain介绍

    返回目录:<ARM-Linux中断系统>. 总结:一.二概述了软硬件不同角度的IRQ Number和HW Interrupt ID,这就需要他们之间架个桥梁. 三介绍了架设这种桥梁的几种方 ...

  9. Linux kernel的中断子系统之(四):High level irq event handler

    返回目录:<ARM-Linux中断系统>. 总结:从架构相关的汇编处理跳转到Machine/控制器相关的handle_arch_irq,generic_handle_irq作为High l ...

随机推荐

  1. 剑指offer第五天

    28.数组中出现次数超过一半的数字 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字.例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}.由于数字2在数组中出现了5次,超过数 ...

  2. Golang http 服务器

    package main import ( "net/http" "fmt" ) func main() { app := http.NewServeMux() ...

  3. Qt 信号如何自动连接槽函数?

    on_objectName_signal [static] void QMetaObject::connectSlotsByName(QObject *object) void on_<obje ...

  4. UVA1600 状态BFS

    刚开是我用了一种很笨的bfs过掉的,后来看到原来还可以三维带状态BFS,觉得是一个不错的思路. d[x][y][k]表示坐标位于(x,y)经过K个障碍到达时的最短路径,当然如果(x,y)处的数字是0就 ...

  5. nyoj44 子串和 线性DP

    线性DP经典题. dp[i]表示以i为结尾最大连续和,状态转移方程dp[i] = max (a[i] , dp[i - 1] + a[i]) AC代码: #include<cstdio> ...

  6. JQ小随笔

    JQ  select的val 会自动选择option的val layer 弹窗插件 Validation-Engine验证插件

  7. nginx笔记6-总结

    1.轮询(默认)每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除.2.weight指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况.3.ip_ ...

  8. burpsuite + sqlmap 日志导出批量扫描

    http://lcx.cc/?i=4207   在burpsuite中options -->misc-->logging中选择要记录的日志来源: 一般是proxy request: 生成的 ...

  9. Flex弹出窗口请求Action函数

    Flex弹出窗口请求Action函数 private function askQuestion(event:MouseEvent):void { var askQuestions:AskWindow ...

  10. Java之Iterator

    Java之Iterator 1.实例源码 /** * @Title:IteratorJava.java * @Package:com.you.model * @Description:Iterator ...