Linux芯片级移植与底层驱动(基于3.7.4内核) --中断控制器

简介:

宋宝华 Barry Song <21cnbao@gmail.com>

新浪微博: @宋宝华Barry

3.    中断控制器驱动

Linux内核中,各个设备驱动可以简单地调用request_irq()enable_irq()disable_irq()local_irq_disable()local_irq_enable()等通用API完成中断申请、使能、禁止等功能。在将Linux移植到新的SoC时,芯片供应商需要提供该部分API的底层支持。

local_irq_disable()local_irq_enable()的实现与具体中断控制器无关,对于ARMv6以上的体系架构而言,是直接调用CPSID/CPSIE指令进行,而对于ARMv6以前的体系结构,则是透过MRSMSR指令来读取和设置ARMCPSR寄存器。由此可见,local_irq_disable()local_irq_enable()针对的并不是外部的中断控制器,而是直接让CPU本身不响应中断请求。相关的实现位于arch/arm/include/asm/irqflags.h

  11#if __LINUX_ARM_ARCH__ >= 6

  12

  13static inline unsigned long arch_local_irq_save(void)

  14{

  15        unsigned long flags;

  16

  17        asm volatile(

  18                "       mrs     %0, cpsr        @ arch_local_irq_save\n"

  19                "       cpsid   i"

  20                : "=r" (flags) : : "memory", "cc");

  21        return flags;

  22}

  23

  24static inline void arch_local_irq_enable(void)

  25{

  26        asm volatile(

  27                "       cpsie i                 @ arch_local_irq_enable"

  28                :

  29                :

  30                : "memory", "cc");

  31}

  32

  33static inline void arch_local_irq_disable(void)

  34{

  35        asm volatile(

  36                "       cpsid i                 @ arch_local_irq_disable"

  37                :

  38                :

  39                : "memory", "cc");

  40}

  44#else

  45

  46/*

  47 * Save the current interrupt enable state & disable IRQs

  48 */

  49static inline unsigned long arch_local_irq_save(void)

  50{

  51        unsigned long flags, temp;

  52

  53        asm volatile(

  54                "       mrs     %0, cpsr        @ arch_local_irq_save\n"

  55                "       orr     %1, %0, #128\n"

  56                "       msr     cpsr_c, %1"

  57                : "=r" (flags), "=r" (temp)

  58                :

  59                : "memory", "cc");

  60        return flags;

  61}

  62

  63/*

  64 * Enable IRQs

  65 */

  66static inline void arch_local_irq_enable(void)

  67{

  68        unsigned long temp;

  69        asm volatile(

  70                "       mrs     %0, cpsr        @ arch_local_irq_enable\n"

  71                "       bic     %0, %0, #128\n"

  72                "       msr     cpsr_c, %0"

  73                : "=r" (temp)

  74                :

  75                : "memory", "cc");

  76}

  77

  78/*

  79 * Disable IRQs

  80 */

  81static inline void arch_local_irq_disable(void)

  82{

  83        unsigned long temp;

  84        asm volatile(

  85                "       mrs     %0, cpsr        @ arch_local_irq_disable\n"

  86                "       orr     %0, %0, #128\n"

  87                "       msr     cpsr_c, %0"

  88                : "=r" (temp)

  89                :

  90                : "memory", "cc");

  91}

  92 #endif

local_irq_disable()local_irq_enable()不同,disable_irq()enable_irq()针对的则是外部的中断控制器。在内核中,透过irq_chip结构体来描述中断控制器。该结构体内部封装了中断maskunmaskack等成员函数,其定义于include/linux/irq.h

303struct irq_chip {

 304        const char      *name;

 305        unsigned int    (*irq_startup)(struct irq_data *data);

 306        void            (*irq_shutdown)(struct irq_data *data);

 307        void            (*irq_enable)(struct irq_data *data);

 308        void            (*irq_disable)(struct irq_data *data);

 309

 310        void            (*irq_ack)(struct irq_data *data);

 311        void            (*irq_mask)(struct irq_data *data);

 312        void            (*irq_mask_ack)(struct irq_data *data);

 313        void            (*irq_unmask)(struct irq_data *data);

 314        void            (*irq_eoi)(struct irq_data *data);

 315

 316        int             (*irq_set_affinity)(struct irq_data *data, const struct cpumask *dest, bool force);

 317        int             (*irq_retrigger)(struct irq_data *data);

 318        int             (*irq_set_type)(struct irq_data *data, unsigned int flow_type);

 319        int             (*irq_set_wake)(struct irq_data *data, unsigned int on);

 334};

各个芯片公司会将芯片内部的中断控制器实现为irq_chip驱动的形式。受限于中断控制器硬件的能力,这些成员函数并不一定需要全部实现,有时候只需要实现其中的部分函数即可。譬如drivers/pinctrl/pinctrl-sirf.c驱动中的

1438static struct irq_chip sirfsoc_irq_chip = {

1439        .name = "sirf-gpio-irq",

1440        .irq_ack = sirfsoc_gpio_irq_ack,

1441        .irq_mask = sirfsoc_gpio_irq_mask,

1442        .irq_unmask = sirfsoc_gpio_irq_unmask,

1443        .irq_set_type = sirfsoc_gpio_irq_type,

1444};

我们只实现了其中的ackmaskunmaskset_type成员函数,ack函数用于清中断,maskunmask用于中断屏蔽和取消中断屏蔽、set_type则用于配置中断的触发方式,如高电平、低电平、上升沿、下降沿等。至于enable_irq()的时候,虽然没有实现irq_enable成员函数,但是内核会间接调用到irq_unmask成员函数,这点从kernel/irq/chip.c可以看出:

192void irq_enable(struct irq_desc *desc)

 193{

 194        irq_state_clr_disabled(desc);

 195        if (desc->irq_data.chip->irq_enable)

 196                desc->irq_data.chip->irq_enable(&desc->irq_data);

 197        else

 198                desc->irq_data.chip->irq_unmask(&desc->irq_data);

 199        irq_state_clr_masked(desc);

 200}

    在芯片内部,中断控制器可能不止1个,多个中断控制器之间还很可能是级联的。举个例子,假设芯片内部有一个中断控制器,支持32个中断源,其中有4个来源于GPIO控制器外围的4GPIO,每组GPIO上又有32个中断(许多芯片的GPIO控制器也同时是一个中断控制器),其关系如下图:

那么,一般来讲,在实际操作中,gpio0_0——gpio0_31这些引脚本身在第1级会使用中断号28,而这些引脚本身的中断号在实现GPIO控制器对应的irq_chip驱动时,我们又会把它映射到Linux系统的32——63号中断。同理,gpio1_0——gpio1_31这些引脚本身在第1级会使用中断号29,而这些引脚本身的中断号在实现GPIO控制器对应的irq_chip驱动时,我们又会把它映射到Linux系统的64——95号中断,以此类推。对于中断号的使用者而言,无需看到这种2级映射关系。如果某设备想申请gpio1_0这个引脚对应的中断,它只需要申请64号中断即可。这个关系图看起来如下:

还是以drivers/pinctrl/pinctrl-sirf.cirq_chip部分为例,我们对于每组GPIO都透过irq_domain_add_legacy()添加了相应的irq_domain,每组GPIO的中断号开始于SIRFSOC_GPIO_IRQ_START + i * SIRFSOC_GPIO_BANK_SIZE,而每组GPIO本身占用的第1级中断控制器的中断号则为bank->parent_irq,我们透过irq_set_chained_handler()设置了第1级中断发生的时候,会调用链式IRQ处理函数sirfsoc_gpio_handle_irq()

1689                bank->domain = irq_domain_add_legacy(np, SIRFSOC_GPIO_BANK_SIZE,

1690                        SIRFSOC_GPIO_IRQ_START + i * SIRFSOC_GPIO_BANK_SIZE, 0,

1691                        &sirfsoc_gpio_irq_simple_ops, bank);

1692

1693                if (!bank->domain) {

1694                        pr_err("%s: Failed to create irqdomain\n", np->full_name);

1695                        err = -ENOSYS;

1696                        goto out;

1697                }

1698

1699                irq_set_chained_handler(bank->parent_irq, sirfsoc_gpio_handle_irq);

1700                irq_set_handler_data(bank->parent_irq, bank);

而在sirfsoc_gpio_handle_irq()函数的入口出调用chained_irq_enter()暗示自身进入链式IRQ处理,在函数体内判决具体的GPIO中断,并透过generic_handle_irq()调用到最终的外设驱动中的中断服务程序,最后调用chained_irq_exit()暗示自身退出链式IRQ处理:

1446static void sirfsoc_gpio_handle_irq(unsigned int irq, struct irq_desc *desc)

1447{

1448        

1454        chained_irq_enter(chip, desc);

1456        

1477        generic_handle_irq(first_irq + idx);

1478        

1484        chained_irq_exit(chip, desc);

1485}

很多中断控制器的寄存器定义呈现出简单的规律,如有一个mask寄存器,其中每1位可屏蔽1个中断等,这种情况下,我们无需实现1个完整的irq_chip驱动,可以使用内核提供的通用irq_chip驱动架构irq_chip_generic,这样只需要实现极少量的代码,如arch/arm/mach-prima2/irq.c中,注册CSR SiRFprimaII内部中断控制器的代码仅为:

  26static __init void

  27sirfsoc_alloc_gc(void __iomem *base, unsigned int irq_start, unsigned int num)

  28{

  29        struct irq_chip_generic *gc;

  30        struct irq_chip_type *ct;

  31

  32        gc = irq_alloc_generic_chip("SIRFINTC", 1, irq_start, base, handle_level_irq);

  33        ct = gc->chip_types;

  34

  35        ct->chip.irq_mask = irq_gc_mask_clr_bit;

  36        ct->chip.irq_unmask = irq_gc_mask_set_bit;

  37        ct->regs.mask = SIRFSOC_INT_RISC_MASK0;

  38

  39        irq_setup_generic_chip(gc, IRQ_MSK(num), IRQ_GC_INIT_MASK_CACHE, IRQ_NOREQUEST, 0);

  40}

特别值得一提的是,目前多数主流ARM芯片,内部的一级中断控制器都使用了ARM公司的GIC,我们几乎不需要实现任何代码,只需要在Device Tree中添加相关的结点并将gic_handle_irq()填入MACHINEhandle_irq成员。

如在arch/arm/boot/dts/exynos5250.dtsi即含有:

  36        gic:interrupt-controller@10481000 {

  37                compatible = "arm,cortex-a9-gic";

  38                #interrupt-cells = <3>;

  39                interrupt-controller;

  40                reg = <0x10481000 0x1000>, <0x10482000 0x2000>;

  41        };

而在arch/arm/mach-exynos/mach-exynos5-dt.c中即含有:

  95DT_MACHINE_START(EXYNOS5_DT, "SAMSUNG EXYNOS5 (Flattened Device Tree)")

  96        /* Maintainer: Kukjin Kim <kgene.kim@samsung.com> */

  97        .init_irq       = exynos5_init_irq,

  98        .smp            = smp_ops(exynos_smp_ops),

  99        .map_io         = exynos5250_dt_map_io,

 100        .handle_irq     = gic_handle_irq,

 101        .init_machine   = exynos5250_dt_machine_init,

 102        .init_late      = exynos_init_late,

 103        .timer          = &exynos4_timer,

 104        .dt_compat      = exynos5250_dt_compat,

 105        .restart        = exynos5_restart,

 106MACHINE_END



 本文转自 21cnbao 51CTO博客,原文链接:http://blog.51cto.com/21cnbao/1127017,如需转载请自行联系原作者



相关实践学习
深入解析Docker容器化技术
Docker是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会有任何接口。Docker是世界领先的软件容器平台。开发人员利用Docker可以消除协作编码时“在我的机器上可正常工作”的问题。运维人员利用Docker可以在隔离容器中并行运行和管理应用,获得更好的计算密度。企业利用Docker可以构建敏捷的软件交付管道,以更快的速度、更高的安全性和可靠的信誉为Linux和Windows Server应用发布新功能。 在本套课程中,我们将全面的讲解Docker技术栈,从环境安装到容器、镜像操作以及生产环境如何部署开发的微服务应用。本课程由黑马程序员提供。 &nbsp; &nbsp; 相关的阿里云产品:容器服务 ACK 容器服务 Kubernetes 版(简称 ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。 了解产品详情: https://www.aliyun.com/product/kubernetes
相关文章
|
2月前
|
监控 Linux 开发者
理解Linux操作系统内核中物理设备驱动(phy driver)的功能。
综合来看,物理设备驱动在Linux系统中的作用是至关重要的,它通过与硬件设备的紧密配合,为上层应用提供稳定可靠的通信基础设施。开发一款优秀的物理设备驱动需要开发者具备深厚的硬件知识、熟练的编程技能以及对Linux内核架构的深入理解,以确保驱动程序能在不同的硬件平台和网络条件下都能提供最优的性能。
120 0
|
5月前
|
并行计算 Linux
Linux内核中的线程和进程实现详解
了解进程和线程如何工作,可以帮助我们更好地编写程序,充分利用多核CPU,实现并行计算,提高系统的响应速度和计算效能。记住,适当平衡进程和线程的使用,既要拥有独立空间的'兄弟',也需要在'家庭'中分享和并行的成员。对于这个世界,现在,你应该有一个全新的认识。
236 67
|
3月前
|
存储 负载均衡 算法
Linux2.6内核进程调度队列
本篇文章是Linux进程系列中的最后一篇文章,本来是想放在上一篇文章的结尾的,但是想了想还是单独写一篇文章吧,虽然说这部分内容是比较难的,所有一般来说是简单的提及带过的,但是为了让大家对进程有更深的理解与认识,还是看了一些别人的文章,然后学习了学习,然后对此做了总结,尽可能详细的介绍明白。最后推荐一篇文章Linux的进程优先级 NI 和 PR - 简书。
101 0
|
5月前
|
存储 Linux
Linux内核中的current机制解析
总的来说,current机制是Linux内核中进程管理的基础,它通过获取当前进程的task_struct结构的地址,可以方便地获取和修改进程的信息。这个机制在内核中的使用非常广泛,对于理解Linux内核的工作原理有着重要的意义。
211 11
|
6月前
|
自然语言处理 监控 Linux
Linux 内核源码分析---proc 文件系统
`proc`文件系统是Linux内核中一个灵活而强大的工具,提供了一个与内核数据结构交互的接口。通过本文的分析,我们深入探讨了 `proc`文件系统的实现原理,包括其初始化、文件的创建与操作、动态内容生成等方面。通过对这些内容的理解,开发者可以更好地利用 `proc`文件系统来监控和调试内核,同时也为系统管理提供了便利的工具。
250 16
|
8月前
|
Ubuntu Linux 开发者
Ubuntu20.04搭建嵌入式linux网络加载内核、设备树和根文件系统
使用上述U-Boot命令配置并启动嵌入式设备。如果配置正确,设备将通过TFTP加载内核和设备树,并通过NFS挂载根文件系统。
443 15
|
8月前
|
安全 Linux 测试技术
Intel Linux 内核测试套件-LKVS介绍 | 龙蜥大讲堂104期
《Intel Linux内核测试套件-LKVS介绍》(龙蜥大讲堂104期)主要介绍了LKVS的定义、使用方法、测试范围、典型案例及其优势。LKVS是轻量级、低耦合且高代码覆盖率的测试工具,涵盖20多个硬件和内核属性,已开源并集成到多个社区CICD系统中。课程详细讲解了如何使用LKVS进行CPU、电源管理和安全特性(如TDX、CET)的测试,并展示了其在实际应用中的价值。
195 4
|
10月前
|
算法 Linux 调度
深入理解Linux内核调度器:从基础到优化####
本文旨在通过剖析Linux操作系统的心脏——内核调度器,为读者揭开其高效管理CPU资源的神秘面纱。不同于传统的摘要概述,本文将直接以一段精简代码片段作为引子,展示一个简化版的任务调度逻辑,随后逐步深入,详细探讨Linux内核调度器的工作原理、关键数据结构、调度算法演变以及性能调优策略,旨在为开发者与系统管理员提供一份实用的技术指南。 ####
348 4
|
10月前
|
缓存 并行计算 Linux
深入解析Linux操作系统的内核优化策略
本文旨在探讨Linux操作系统内核的优化策略,包括内核参数调整、内存管理、CPU调度以及文件系统性能提升等方面。通过对这些关键领域的分析,我们可以理解如何有效地提高Linux系统的性能和稳定性,从而为用户提供更加流畅和高效的计算体验。
388 24
|
9月前
|
缓存 监控 网络协议
Linux操作系统的内核优化与实践####
本文旨在探讨Linux操作系统内核的优化策略与实际应用案例,深入分析内核参数调优、编译选项配置及实时性能监控的方法。通过具体实例讲解如何根据不同应用场景调整内核设置,以提升系统性能和稳定性,为系统管理员和技术爱好者提供实用的优化指南。 ####