精华内容
下载资源
问答
  • 2021-08-10 09:11:45

    先简单说明一下GIC(具体详尽的介绍请查阅ARM GIC相关文档)

    GIC即general interrupt controller。

    它是一个架构,版本历经了GICv1(已弃用),GICv2,GICv3,GICv4。对于不同的GIC版本,arm公司设计了对应的GIC IP

    GIC的核心功能:对soc中外设的中断源的管理,并且提供给软件,配置以及控制这些中断源。

    下面一张ARM GICv2 的图

    中断源类型说明:

    SGI(Software-generated interrupt):范围0 - 15,软件触发的中断,一般用于核间通讯

    PPI(Private peripheral interrupt ): 范围16 - 31,私有外设中断,只对指定的core有效

    SPI(Shared peripheral interrupt):范围32 - 1019,共享中断,不限定特定的core

    控制分两部分,Distributor和CPU interface

    Distributor对中断的控制包括:
    (1)中断enable或者disable的控制
    (2)将当前优先级最高的中断事件分发到一个或者一组CPU interface
    (3)优先级控制
    (4)interrupt属性设定。例如是level-sensitive还是edge-triggered
    (5)interrupt group的设定


    CPU interface:将GICD发送的中断信息,通过IRQ,FIQ管脚,传输给core

    GIC对中断的处理包括以下4种状态:

    • inactive:中断处于无效状态

    • pending:中断处于有效状态,但是cpu没有响应该中断

    • active:cpu在响应该中断

    • active and pending:cpu在响应该中断,但是该中断源又发送中断过来

    其转换过程如下:

    发生中断信号走向:

    至此,关于GIC相关的介绍基本可以满足阅读Linux内核关于中断处理的相关code

    以下源代码基于ssd20x官方kernel为例,cortexA7,ARMv7架构,32位

    中断向量入口位于:arch/arm/kernel/entry-armv.S

    /*
     * Interrupt handling.
     */
    	.macro	irq_handler
    #ifdef CONFIG_MULTI_IRQ_HANDLER
    	ldr	r1, =handle_arch_irq
    	mov	r0, sp
    	badr	lr, 9997f
    	ldr	pc, [r1]
    #else
    	arch_irq_handler_default
    #endif

    关于宏CONFIG_MULTI_IRQ_HANDLER,表示"允许每台机器在运行时指定它自己的IRQ处理程序",因为kernel一般支持多种平台,多种处理器,所以此选项一般开启

    进入irq_handler有两种情况

    一个是用户模式下发生了中断

    	.align	5
    __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)

     另一个是内核态时发生了中断

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

    在irq_handler中handle_arch_irq即为动态指定的中断处理函数

    位于arch/arm/kernel/irq.c

    #ifdef CONFIG_MULTI_IRQ_HANDLER
    void __init set_handle_irq(void (*handle_irq)(struct pt_regs *))
    {
    	if (handle_arch_irq)
    		return;
    
    	handle_arch_irq = handle_irq;
    }
    #endif

    在gic初始化drivers/irqchip/irq-gic.c过程进行设置set_handle_irq(gic_handle_irq);

    static int __init __gic_init_bases(struct gic_chip_data *gic,
    				   int irq_start,
    				   struct fwnode_handle *handle)
    {
    	char *name;
    	int i, ret;
    
    	if (WARN_ON(!gic || gic->domain))
    		return -EINVAL;
    
    	if (gic == &gic_data[0]) {
    		/*
    		 * Initialize the CPU interface map to all CPUs.
    		 * It will be refined as each CPU probes its ID.
    		 * This is only necessary for the primary GIC.
    		 */
    		for (i = 0; i < NR_GIC_CPU_IF; i++)
    			gic_cpu_map[i] = 0xff;
    #ifdef CONFIG_SMP
    		set_smp_cross_call(gic_raise_softirq);
    #endif
    		cpuhp_setup_state_nocalls(CPUHP_AP_IRQ_GIC_STARTING,
    					  "AP_IRQ_GIC_STARTING",
    					  gic_starting_cpu, NULL);
                -----初始化过程中注册具体的处理函数
    		set_handle_irq(gic_handle_irq);
    
    		if (static_key_true(&supports_deactivate))
    			pr_info("GIC: Using split EOI/Deactivate mode\n");
    	}
    
    	if (static_key_true(&supports_deactivate) && gic == &gic_data[0]) {
    		name = kasprintf(GFP_KERNEL, "GICv2");
    		gic_init_chip(gic, NULL, name, true);
    	} else {
    		name = kasprintf(GFP_KERNEL, "GIC-%d", (int)(gic-&gic_data[0]));
    		gic_init_chip(gic, NULL, name, false);
    	}
    
    	ret = gic_init_bases(gic, irq_start, handle);
    	if (ret)
    		kfree(name);
    
    	return ret;
    }

     函数gic_handle_irq即为具体的中断处理函数,此时中断号是被GIC屏蔽的

    static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
    {
        u32 irqstat, irqnr;
        struct gic_chip_data *gic = &gic_data[0];
        void __iomem *cpu_base = gic_data_cpu_base(gic);
            

        do {

    -----从寄存器读取硬件中断号
            irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);
            irqnr = irqstat & GICC_IAR_INT_ID_MASK;
            
    #if defined(CONFIG_MP_IRQ_TRACE)
                 ms_records_irq_count(irqnr);
    #endif

    -----处理PPI 及 SPI中断
            if (likely(irqnr > 15 && irqnr < 1020)) {
                if (static_key_true(&supports_deactivate))
                    writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI);
                handle_domain_irq(gic->domain, irqnr, regs);
                continue;
            }

    ------处理SGI中断
            if (irqnr < 16) {
                writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI);
                if (static_key_true(&supports_deactivate))
                    writel_relaxed(irqstat, cpu_base + GIC_CPU_DEACTIVATE);
    #ifdef CONFIG_SMP
                /*
                 * Ensure any shared data written by the CPU sending
                 * the IPI is read after we've read the ACK register
                 * on the GIC.
                 *
                 * Pairs with the write barrier in gic_raise_softirq
                 */

    -----内存屏障
                smp_rmb();

    -----进入处理
                handle_IPI(irqnr, regs);
    。。。。。
                continue;
            }
            break;
        } while (1);
    }

     handle_domain_irq的实现如下:

    #ifdef CONFIG_HANDLE_DOMAIN_IRQ
    /**
     * __handle_domain_irq - Invoke the handler for a HW irq belonging to a domain
     * @domain:    The domain where to perform the lookup
     * @hwirq:    The HW irq number to convert to a logical one
     * @lookup:    Whether to perform the domain lookup or not
     * @regs:    Register file coming from the low-level handling code
     *
     * Returns:    0 on success, or -EINVAL if conversion has failed
     */
    int __handle_domain_irq(struct irq_domain *domain, unsigned int hwirq,
                bool lookup, struct pt_regs *regs)
    {
        struct pt_regs *old_regs = set_irq_regs(regs);
        unsigned int irq = hwirq;
        int ret = 0;

    --------关闭抢占

        irq_enter();

    #ifdef CONFIG_IRQ_DOMAIN
        if (lookup)
            irq = irq_find_mapping(domain, hwirq);
    #endif

        /*
         * Some hardware gives randomly wrong interrupts.  Rather
         * than crashing, do something sensible.
         */
        if (unlikely(!irq || irq >= nr_irqs)) {
            ack_bad_irq(irq);
            ret = -EINVAL;
        } else {

    ------执行处理
            generic_handle_irq(irq);  ----实际为注册IRQ号对应的函数:desc->handle_irq(desc);       
        }

    ----关闭抢占以及检测是否有软中断需要处理

        irq_exit();
        set_irq_regs(old_regs);
        return ret;
    }
    #endif

    关于抢占,即在适当的时机及时调用更高优先级的任务;

    几个抢占点:

    从中断返回到内核空间时;解锁或使能软中断时;调用 preempt_enable 使能抢占;

    显式调度;任务阻塞时;

    不可抢占点:

    正处于中断中;持有锁时;正在调度时;对Per-CPU操作时

    抢占的具体实现为对一个preempt_count变量进行互斥操作,位于thread_info结构中

    static __always_inline volatile int *preempt_count_ptr(void)

    {

    return &current_thread_info()->preempt_count;

    }

    static __always_inline void __preempt_count_add(int val)

    {

    *preempt_count_ptr() += val;

    }

    static __always_inline void __preempt_count_sub(int val)

    {

    *preempt_count_ptr() -= val;

    }

    #define preempt_count_add(val) __preempt_count_add(val)

    #define preempt_count_sub(val) __preempt_count_sub(val)

    #define preempt_count_dec_and_test() __preempt_count_dec_and_test()

    #define preempt_count_inc() preempt_count_add(1)

    #define preempt_count_dec() preempt_count_sub(1)

    借用一张图详细描述

    关于软中断,中断底半部的一种实现方式,静态分配,一共10种,可以充分利用SMP性能;

    其执行点一般在irq_exit时或者独立的内核线程中

    在irq_exit中会进行有没有待处理软中断的检测(其实就是判断preempt_count的BIT8-15)

    /*

    * Exit an interrupt context. Process softirqs if needed and possible:

    */

    void irq_exit(void)

    {

    #ifndef __ARCH_IRQ_EXIT_IRQS_DISABLED

    local_irq_disable();

    #else

    lockdep_assert_irqs_disabled();

    #endif

    account_irq_exit_time(current);

    -----计数-1

    preempt_count_sub(HARDIRQ_OFFSET);

    ----不在中断中以及有待处理软中断

    if (!in_interrupt() && local_softirq_pending())

            invoke_softirq();  ----实际为__do_softirq或者wakeup_softirqd

    tick_irq_exit();

    rcu_irq_exit();

    trace_hardirq_exit(); /* must be last! */

    }

    asmlinkage __visible void __softirq_entry __do_softirq(void)

    {

    unsigned long end = jiffies + MAX_SOFTIRQ_TIME;

    unsigned long old_flags = current->flags;

    int max_restart = MAX_SOFTIRQ_RESTART;

    struct softirq_action *h;

    bool in_hardirq;

    __u32 pending;

    int softirq_bit;

    #if defined(CONFIG_MP_IRQ_TRACE)

    MSYS_IRQ_INFO irq_info;

    #endif

    /*

    * Mask out PF_MEMALLOC s current task context is borrowed for the

    * softirq. A softirq handled such as network RX might set PF_MEMALLOC

    * again if the socket is related to swap

    */

    current->flags &= ~PF_MEMALLOC;

    pending = local_softirq_pending();

    account_irq_enter_time(current);

    -----软中断不可嵌套

    __local_bh_disable_ip(_RET_IP_, SOFTIRQ_OFFSET);

    in_hardirq = lockdep_softirq_start();

    restart:

    /* Reset the pending bitmask before enabling irqs */

    set_softirq_pending(0);

    ----开启中断,此后的部分随时会被中断打断;因此要保证软中短处理好函数的可重入性

    local_irq_enable();

    h = softirq_vec;

    while ((softirq_bit = ffs(pending))) {

    unsigned int vec_nr;

    int prev_count;

    h += softirq_bit - 1;

    vec_nr = h - softirq_vec;

    prev_count = preempt_count();

    kstat_incr_softirqs_this_cpu(vec_nr);

    #if defined(CONFIG_MP_IRQ_TRACE)

    irq_info.IRQNumber = vec_nr;

    irq_info.action = h->action;

    irq_info.timeStart = sched_clock();

    #endif

    trace_softirq_entry(vec_nr);

    ---执行处理函数

    h->action(h);

    trace_softirq_exit(vec_nr);

    #if defined(CONFIG_MP_IRQ_TRACE)

    irq_info.timeEnd = sched_clock();

    if (irq_info.timeEnd - irq_info.timeStart > 2500000)

    {

    if(sirq_head_initialized)

    {

    ms_records_sirq(&irq_info);

    }

    }

    #endif

    if (unlikely(prev_count != preempt_count())) {

    pr_err("huh, entered softirq %u %s %p with preempt_count %08x, exited with %08x?\n",

    vec_nr, softirq_to_name[vec_nr], h->action,

    prev_count, preempt_count());

    preempt_count_set(prev_count);

    }

    h++;

    pending >>= softirq_bit;

    }

    rcu_bh_qs();

    ----处理完后关闭中断

    local_irq_disable();

    ----再次检测是否有待处理软中断(因为软中断处理过程中开启了中断)

    pending = local_softirq_pending();

    if (pending) {

    ----软中断有最大执行时间限制

    if (time_before(jiffies, end) && !need_resched() &&

    --max_restart)

    ---有的话继续执行软中断

    goto restart;

    ----软中断积累太多,调用独立的内核线程ksoftirqd执行,每个core都有一个ksoftirqd,其实际上是一个Per-CPU变量

    wakeup_softirqd();

    }

    lockdep_softirq_end(in_hardirq);

    account_irq_exit_time(current);

    -----软中断底半部使能

    __local_bh_enable(SOFTIRQ_OFFSET);

    WARN_ON_ONCE(in_interrupt());

    tsk_restore_flags(current, old_flags, PF_MEMALLOC);

    }

    总结:

    1. GIC分为两部分,Distributor(处理优先级,中断保持,信号分发等)和CPU interface(响应及完成中断等)

    2. Linux中断不可嵌套

    3. Linux中断处理分为上半部(快速处理和响应)和底半部(延迟执行)

    4. 底半部常用手段有软中断(可以在不同CPU并发),tasklet(同一个tasklet在同一个CPU排队执行)以及工作队列(可睡眠)

    5. 关于Per-CPU变量,编译时位于特定的section中,加载内存后,每个cpu都有一份拷贝,各自用各自的独立拷贝,不存在多cpu访问互斥问题,只有本cpu线程和本cpu中断互斥访问问题

    6. 关于内存屏障,因为cpu大多都是流水线工作方式,并且可以乱序执行,内存屏障保证语句前后的指令具备严格的先后执行顺序

    更多相关内容
  • ARM GIC控制器_gic_V2_arm_

    2021-09-29 01:11:24
    ARM Generic Interrupt Controller(ARM GIC控制器)V2.0,可以参考
  • ARMGIC中断控制器介绍。
  • ARM自己设计的一款中断控制器GIC,用于多核的中断处理
  • ARM GIC介绍之二

    2021-03-31 11:21:37
    连接"arm GIC介绍之一",我们补充对应的GIC寄存器描述,和主要的用法说明,仅供参考。 3 . GIC_V3寄存器介绍 我们把完整的图放在这里: 从这里看到,GIC分为3个主要的部分,要完成其作用,而且给用户进行预先...

    连接"arm GIC介绍之一",我们补充对应的GIC 寄存器描述,和主要的用法说明,仅供参考。 

    3 . GIC_V3寄存器介绍

    我们把完整的图放在这里:


     从这里看到,GIC分为3个主要的部分,要完成其作用,而且给用户进行预先配置的方法,GIC就提供了一组寄存器,这些寄存器的设计就是围绕着这些主要的功能来实现的。

    3.1 Distributor Registers

    在前面介绍过,Distributor主要完成对应的IRQ状态的记录,对应的IRQ分发,那么围绕这样的功能,对应的寄存器主要有:

    这里面有Distributor控制寄存器,控制Distributor使能和关闭,以及对应的具体IRQ的状态寄存器,比如GICD_ISACTIVER,这里记录某一个IRQ是否ACTIVE,还有逻辑功能寄存器,比如GICD_IPRIORITY,这里记录对应

    IRQ的优先级别。这些都以GICD_*为前缀打头。我们来仔细说明。

    GICD_CTLR

    这个是GICD_CTLR寄存器,名字和对应的位功能来看是对Distributor控制使能,比如BIT_1,就是使能GROUP_1,

    并且是NON-SECURE,如果是LINUX系统,就是KERNEL空间。一般来说对这个寄存器每个位都是打开的。BIT_31是等待回应,当配置对应寄存器了,写入到硬件,需要生效,那么可以读取这个位来看是否已经写入配置信息并生效。ARE是对应的AFFINITY,其实就是分配到哪个具体的CPU,我们在ROUTER寄存器说明时候来补充。

    GICD_TYPER

    这个是GICD_TYPER寄存器,其主要作用是获取SOC平台厂商提供的GIC的具体信息,现在一般提供信息是该GIC能够支持最多多少个IRQ硬件中断源。

    ITLinesNumber:

    SPI numbers in specification, butthis should include (SGI, PPI actually)

    比如我们现在X20平台上都出来并且换算出来ITLinesNumber: 0xb,那么每个数字表示有32个IRQ      

    12* 32-1= 383 IRQ numbers.

    支持384个IRQ,从0到383,为最大数字。

    最大支持

    (0x1f+1)*32-1 =1023 

    1024个中断,这个GIC现在通用的说明是一致的,如果不考虑LPI的话。

    GICD_IGROUP&GICD_IGRPMODR

    这两个在一起使用,一个IRQ对应一个BIT的GICD_IGROUP,以及一个BIT的GICD_IGRPMODR。

    所以如果支持384个IRQ,那么真个GIC就需要384个BIT来表示384个GICD_IGRPMODR,同理其它对应的

    以BIT方式表示的也是如此。

    这两个结合起来表示某个具体的IRQ应该送到哪个Ex,是安全域还是非安全域,现在一般只支持3个,如上图表示。

    GICD_IROUTER

    这个寄存器的名字不好理解,其实,如果我们修改下GICD_TARGET,这样,就容易理解了。简单来说,该寄存器控制一个IRQ发到哪个CPU进行处理。在现在的ARM中,cpu的拓扑结构用AFFINITY来表示。

    CPU以CLUSTER为单位进行管理,比如X20上有10个CPU,其中4个小核为一个CLUSTER_2,另外4个CPU

    为CLUSTER_1,两个大核为CLUSTER_0,这样有利于对CPU的上电,频率等管理控制,尤其是CPU在OFF时候,当一个CLUSTER里面的CPU都OFF了,那么就以CLUSTER为单位把对用的电压给关闭或做其它处理,时钟进行对应的GATE,当然这只是考虑的部分因素,这部分需要参考Big.Little进行参考。我们在这里需要知道对应的管理结构,或者说

    拓扑结构就可以了,在ARM中有对应MIPDR寄存器表示对应的CPU处于哪个SYSTEM,哪个CLUSTER,以及在CLUSTER里面的对应的编号,如图中的右上角说明。

    所以,对于一个IRQ,需要知道发送给哪个CPU处理,就靠GICD_IROUTER里面的信息来决策。

    现在一般只使用AFFINITY 0~2,如果系统过于复杂,可能使用到AFFINITY 3,BIT 31其实是个特殊开关,如果设置1,那么来了一个IRQ,这个IRQ就给所有的CPU发送在1*N模型中,一个IRQ可以发送给多个CPU但是只能由其中一个处理,至于是具体哪个CPU先发现并处理,并通知其它CPU,这个在ARM的文档中说明是自定义实现,也就是SOC厂商可以在硬件上自己决策使用具体的策略和方式,这里不说明,另外一个原因目前无法获取到具体SOC厂商在这块的具体策略和实现方法的细节。另外一个是软件上可以进行处理互斥,保证当前CPU处理一个IRQ,另外的CPU不处理。另外一个模型是N*N,也就是一个IRQ信号可以发送给多个CPU,这多个CPU都可以处理,现在大多厂商没有采取这种方式。而是用前者。

    如果BIT 31没有设置为1,那么具体发送到哪个CPU就由AFFINITY 0/1/2来决定了,具体的对应关系见图中不同颜色的连接线。

    GICD_IPRIORITYR

    优先级寄存器

    对于IRQ,如果只有一个到来,送给CPU处理,这个比较简单。但是实际情况远比这种场景复杂,比如某一个中断在PENDING状态,等CPU处理,这个时候来了另外一个IRQ,而这个IRQ的紧迫性更高,实际上我们想打断之前的等待流程,让这个新的IRQ插队,那么就需要给不同IRQ设置不同优先级,来表示对用的处理优先级。

    在现在的GIC_V3中,一个IRQ优先级可以从0到255,用8个BIT来表示,见上图中红色圈出来的部分,所以一个32位的寄存器,其实可以表示4个IRQ的优先级。那么数字越小,级别越高,0表示最高,255最低,我们图中给出例子。

    IRQ_34的优先级别值为0b:10100000,IRQ_33的优先级别值为0b:10100001,IRQ_34的值比IRQ_33小1,所以级别就高。

    但是在实际中,我们不需要这么多的级别范围255,我们需要32个级别,或者64个级别就可以了,这就需要对级别LEVEL进行优化,可以忽略其低位的BIT,现在使用的X20上每个LEVEL的STEP是8,忽略低3位,这是ARM寄存器设计的写入忽略原则,以及读取为0原则。途中右下部分,我们做的简单实验,对优先级寄存器里面写入0b:11111111,但是都出来的是0b:11111000,也就是最低3位被忽略了。那么LEVEL_0是从0~7,LEVEL_1是从8~15,

    所以:原先IRQ_34的优先级别值为0b:10100000,IRQ_33的优先级别值为0b:10100001,IRQ_34的值比IRQ_33小1,

    但是实际能够配置到寄存器里面的值都是0b:10100000。优先级别是一样的。总的原则不变,还是值低的优先级高。所以设置时候需要使用者注意。

    GICD_SGIR

    这个寄存器作用是产生软件IRQ,现在GIC_V3以及之后的版本,放到CPU INTERFACE侧,这里先提示下。

    3.2Redistributor Registers

    对于Redistributor对应的寄存器,以GICR*为前缀打头。这里面涉及到的一部分是功能和开关寄存器,比如GICR_TYPER,GICR_CTLR。另外有针对SGI和PPI的状态优先级寄存器,由于GIC_V3后这32个IRQ的状态和对应的设置挪到Redistributor,所以增加了对应的GICR_ISACTIVER, GICR_IGROUPER0等,作用和Distributor里面对应的SPI(IRQ)寄存器一样,这里不重复,我们只说明几个特殊的寄存器,来解释Redistributor里特殊的功能。

     GICR_CTLR

    因为是每个CPU都对应一个Redistributor,所以这些寄存器上面的大多数设置都对应单个CUP,比如DPG0,表示对应的CPU是否要接受送到G0的中断,同理DPG1NS,DPG1S,这些并不影响其它CPU接受并在安全域和非安全域处理对应的中断。

    UWP这个其实是个Distributor通信状态的确认,这在不同芯片厂商是自己定义的,比如使用一组硬件上的信号来表示不同的状态,从一般的使用者层面来说感知不到具体的两者之间的通信交互,所以大多数开发者可以不关心这个位的设置。

     

    GICR_TYPER

    GICR_TYPER提供了对应的Redistributor信息,比如是否支持PLPIS,VLPIS,这些可能大家都用不到,可以先不用去管它。其中processor number,并且之前介绍的复杂的AFFINITY管理方式关闭后,从看到的资料理解上来说,可以直接用这里面编号来作为目标CPU,比如这里的编号15,就表示第16个CPU。目前这个寄存器我们没有看到对应的使用,都是默认使能的。

    GICR_WAKER

    在介绍这个寄存器之前,我们介绍下WAKE UP方式。

    举个例子,当设置IRQ_100这个中断固定发送到CPU_N上处理之后,实际中来个一个对应的中断信号,而CPU_N在之前因为无任务处理,进入了省电模式,其对应的TIMER被GATED,对应的电压也被POWER DOMAIN给处理降低了或者关闭了。而新的架构(GIC_V3)后CPU INTERFACE以及对应的寄存器部分都和CPU在同一个电压DOMAIN中,那么,这个时后明显是不能把IRQ直接发送到CPU INTERFACE侧,然后再给CPU的IRQ信号线发送信号的。那么Redistributor就需要发送一个Signal wakerequest,请求对应的POWERDOMAIN CONTROLLER先把电压给加上,TIMER给开启,CPU和对应的INTERFACE能够工作,这个时候再发送IRQ信号给其处理。这就是WAKER的开关作用。

    ChildrenAsleep是读取出来的,其中的值表示1的话,说明不能发送IRQ,对应的INTERFACE休眠,发了对方也不会理会的,0就是相反作用,这个一般是读信息,不写,使用者可以读出来为0的就表示可以和这个CPU正常交互。

    ProcessorSleep,就是我们介绍的WAKER方式,如果为0,表示设置CPU没有处于低电模式,1表示处于进入模式或处于低电。可以发送WakeRequest信号。

    其实,这两个定义看起来比较模糊,并不明确,在ARM的资料上介绍的话那么是提到设计的理念,也就是唤醒后再处理,具体实现时候,还是IMPDEF,也就似乎IMPLEMENTED DEFINE,芯片厂家自己定义实现。

    我们看下具体使用:

    其实这里代码实现很简洁,),那么设置对应PROCESSO不管CPU是否处于低电模式(这里第一次开启,一般是出于低电RSLEEP为0,以后就可以发送WAKE REQUEST去唤起,这应该会有硬件信号去唤醒INTERFACE,等待一段时间之后查看CHILDRENASLEEP来判断INTERFACE是否已经唤醒起来了。

    以上介绍Redistributor对应的寄存器。

    3.3 CPU Interface


    说到INTERFACE,我们看下一个IRQ的流程:

    当IRQ100到来,其中断优先级别为0xA0,如果优先级别不冲突,那么久经过Redistributor,送到对应的CPU Interface侧,和GIC_PMR寄存器里面设定的门槛优先级比较。高于对应的优先级,那么和正在处理的IRQ的优先级GIC_RPR寄存器值比较,也高于正在处理的IRQ,那么就抢断,如果没有正在处理的IRQ,那就不存在抢断。IRQ放到GIC_IAR寄存器里面,产生一个电信号给CPU的IRQ_input引脚, CPU读取GIC_IAR里面的中断号,并处理,之后向GIC_EOI寄存器里面写入IRQ,表示IRQ处理结束。

    至此,一个中断处理完成。

    那么这里面就涉及到了CPU Interface侧的对应的寄存器,我们主要列举如下:

    分别介绍。

    CPU Interface  对应的寄存器以GICC*为前缀。

     

    GICC_CTLR


    这个寄存器功能简单,控制FIQ/IRQ的BYPASS,并且EOI的配置。这个有点特殊,按照一般的解释,当IRQ由CPU处理完后,向GICC_EOIR里面写入IRQ号时候,那么表示中断流程完成,同时,状态要变成DE-ACTIVE,注意,是自动变成。但是EOIMODE这个配置有意思,可以决定这个“自动”的变化,如果取消“自动变为DE-ACTIVE”,那么需要使用者向GICC_DIR里面写入,这样才能把一个IRQ的最终状态变为DE-ATCIVE。目前看,这个没有使用到,也不需要手动写入GICC_DIR,从流程的逻辑性来看,这个手动写入具体用在什么样的特殊场景,还没有看到,也是个疑惑的地方。

     

    GICC_PMR


    这个寄存器从字面看,简单理解,中断只有优先级比这里面设置的高,才能继续向CPU侧传递,其中级别也是优化后的,只用32个LEVEL表示。之前在描述Distributor不再重复说明。

     

    GICC_RPR


    这个存器记录正在处理的IRQ的优先级别,主要是用来中断抢占使用的.

    正常情况下,我们看上图,低电平表示中断在处理,当IRQ_A和IRQ_B两个中断到来,先后处理即可。并不会产生冲突,即使IRQ_B的优先级别更高。


    即使说,当IRQ_A先到来在PENDING状态并且已经送到CPUINTERFACE侧等待CPU确认并处理,我们如果关闭抢占的话,这个时候IRQ_B到来,也不会造成一项,IRQ_B优先级别再高也得等待之前的IRQ_A处理后再进行处理。

    那么,如果抢占打开了,怎样呢?GICC_RPR就是用来做这样的信息记录和比较的。


    IRQ_A先到来,并且优先级高于GICC_PMR,那么得到处理,此时GICC_RPR里面的优先级从默认的0XFF变为对应的优先级,假如是0XB0;那么处理中,IRQ_B到来,优先级别也满足GICC_PMR的要求,并且值0XA0,高于GICC_RPR里面记录的IRQ_A的优先级,那么就抢占处理,执行IRQ_B对应的服务,结束之后,再继续完成对应的IRQ_A的服务。如果IRQ_A也服务完成了,那么GICC_RPR里面恢复成默认的OXFF值。

    GICC_IAR

     

    GICC_IAR寄存器,里面记录着等待处理的中断号,CPU从这里面读取获知到来的硬件中断号,并处理,随机对应的GIC中记录的IRQ状态从PENDING变为ACTIVE或者PENDING& ACTIVE。

    GICC_SGI

    SGI产生软件IRQ。

    INITD表示对应IRQ号,这里一般从IRQ0到IRQ15。

    IRM 为0时候,需要依靠TARGETLIST来确定目标CPU,如果为1的时候,那么就是广播发送,但是不给自身发送软中断。

    TARGETLIST这里不在重复说明,只是注意,TARGETLIST里面只是表示在一个具体的CLUSTER里面的CPU编号,CLUSTER号由AFF1表示,这个TARGETLIST相当于AFF0。

    在使用时候也比较简单:


    在系统INIT时候,图左侧红色框内,会使用set_smp_cross_call来注册系统的IPI消息发送函数,这里是gic_raise_softirq,那么右侧红色框内,发送一个特定IPI消息时候,当调用到__smp_cross_all时候就会链到绿色线所指的gic_raise_softirq,来产生一个IPI消息(SGI),见下面代码。

    GICC_EOIR

    最后介绍EOI,这个之前提到过,中断处理完成,写入对应的终端号,表示IRQ处理结束,IRQ状态变为DE-ACTIVE。


    展开全文
  • GIC电源管理,ARM官方手册,只有一页描述: 值得注意的是: 1、在符合GICv3体系结构的实现中,CPU接口和PE必须位于同一个位置power domain,但它不必与关联的Redistributor所在的power domain相同。这

    背景介绍

    GIC电源管理,ARM官方手册,只有一页描述:
    在这里插入图片描述值得注意的是:

    1、在符合GICv3体系结构的实现中,CPU接口和PE必须位于同一个位置power domain,但它不必与关联的Redistributor所在的power domain相同。这意味着可能出现PE及其CPU接口断电的情况,Redistributor、Distributor及其子系统都已通电。在这种情况下,GIC架构需要支持,向PE和CPU接口发送通电事件信号机制。

    2、ARM强烈建议,如果该PE上的唤醒软件无法处理中断,GIC的配置方式不应使中断唤醒特定的PE。GICv3提供电源管理来控制这种情况,因为该架构的设计允许由一个组织设计的再分配器,用于由一个组织设计的PEs和CPU接口不同的组织。

    3、Redistributor上电时,在关闭CPU接口和PE之前,软件必须将CPU接口和Redistributor之间的接口进入静态状态,否则系统将变为不可预期状态。

    4、GIC支持通过设置GICR_WAKER,可以启动到静态状态的转换。进程睡眠到1。当CPU处于静止状态时,GICR_WAKER。ChildrenAsleep也设置为1。

    那么Redistributor、GICR_WAKER在系统位置是怎么样的呢?

    Redistributor系统位置

    在带有gicv3的soc架构中,其框图如下所示:
    在这里插入图片描述
    gicv3中的redistributor与core中的cpu interface通过AXI-Stream进行通信。

    系统上电,CPU如何与GIC redistributor connect

    当core上电之后,需要将core中cpu interface与gic中的redistributor进行connect,这样将来gic才可以将中断发送给core。

    connection的流程如下所示:
    在这里插入图片描述

    GICR_WAKER寄存器

    在这里插入图片描述在这里插入图片描述

    上电流程,行为描述

    1. 执行在core的程序,将GICR_WAKER.ProcessorSleep位给置低,表示要connect redistributor。
    2. redistributor在完成connect之后,将GICR_WAKER.ChildrenAsleep位给置低,表示connect完成。
    3. 执行在core的程序,查询GICR_WAKER.ChildAsleep位是否为0,如果不是,表示redistributor还没有完成connect操作,就继续查询。
    4. 如果查询到0,表示connect完成,接着做之后的初始化工作。

    其汇编代码如下:
    在这里插入图片描述

    /******************************************************************************
     * This function marks the core as awake in the re-distributor and
     * ensures that the interface is active.
     *****************************************************************************/
    void gicv3_rdistif_mark_core_awake(uintptr_t gicr_base)
    {
    	/*
    	 * The WAKER_PS_BIT should be changed to 0
    	 * only when WAKER_CA_BIT is 1.
    	 */
    	assert((gicr_read_waker(gicr_base) & WAKER_CA_BIT) != 0U);
    
    	/* Mark the connected core as awake */
    	/* 执行在core的程序,将GICR_WAKER.ProcessorSleep位给置低,
    	 * 表示要connect redistributor。
    	 */
    	gicr_write_waker(gicr_base, gicr_read_waker(gicr_base) & ~WAKER_PS_BIT);
    
    	/* Wait till the WAKER_CA_BIT changes to 0 */
    	/* 执行在core的程序,查询GICR_WAKER.ChildAsleep位是否为0,
    	 * 如果不是,表示redistributor还没有完成connect操作,就继续查询。
    	 */
    	while ((gicr_read_waker(gicr_base) & WAKER_CA_BIT) != 0U) {
    	}
    }
    
    /*******************************************************************************
     * This function enables the GIC CPU interface of the calling CPU using only
     * system register accesses.
     ******************************************************************************/
    void gicv3_cpuif_enable(unsigned int proc_num)
    {
    	uintptr_t gicr_base;
    	u_register_t scr_el3;
    	unsigned int icc_sre_el3;
    
    	assert(gicv3_driver_data != NULL);
    	assert(proc_num < gicv3_driver_data->rdistif_num);
    	assert(gicv3_driver_data->rdistif_base_addrs != NULL);
    	assert(IS_IN_EL3());
    
    	/* Mark the connected core as awake */
    	/* 表示要connect redistributor */
    	gicr_base = gicv3_driver_data->rdistif_base_addrs[proc_num];
    	gicv3_rdistif_mark_core_awake(gicr_base);
    
    	/* Disable the legacy interrupt bypass */
    	icc_sre_el3 = ICC_SRE_DIB_BIT | ICC_SRE_DFB_BIT;
    
    	/*
    	 * Enable system register access for EL3 and allow lower exception
    	 * levels to configure the same for themselves. If the legacy mode is
    	 * not supported, the SRE bit is RAO/WI
    	 */
    	icc_sre_el3 |= (ICC_SRE_EN_BIT | ICC_SRE_SRE_BIT);
    	write_icc_sre_el3(read_icc_sre_el3() | icc_sre_el3);
    
    	scr_el3 = read_scr_el3();
    
    	/*
    	 * Switch to NS state to write Non secure ICC_SRE_EL1 and
    	 * ICC_SRE_EL2 registers.
    	 */
    	write_scr_el3(scr_el3 | SCR_NS_BIT);
    	isb();
    
    	write_icc_sre_el2(read_icc_sre_el2() | icc_sre_el3);
    	write_icc_sre_el1(ICC_SRE_SRE_BIT);
    	isb();
    
    	/* Switch to secure state. */
    	write_scr_el3(scr_el3 & (~SCR_NS_BIT));
    	isb();
    
    	/* Write the secure ICC_SRE_EL1 register */
    	write_icc_sre_el1(ICC_SRE_SRE_BIT);
    	isb();
    
    	/* Program the idle priority in the PMR */
    	write_icc_pmr_el1(GIC_PRI_MASK);
    
    	/* Enable Group0 interrupts */
    	write_icc_igrpen0_el1(IGRPEN1_EL1_ENABLE_G0_BIT);
    
    	/* Enable Group1 Secure interrupts */
    	write_icc_igrpen1_el3(read_icc_igrpen1_el3() |
    				IGRPEN1_EL3_ENABLE_G1S_BIT);
    	isb();
    }
    
    /*******************************************************************************
     * This function initialises the GIC Redistributor interface of the calling CPU
     * (identified by the 'proc_num' parameter) based upon the data provided by the
     * platform while initialising the driver.
     ******************************************************************************/
    void gicv3_rdistif_init(unsigned int proc_num)
    {
    	uintptr_t gicr_base;
    	unsigned int bitmap;
    	uint32_t ctlr;
    
    	assert(gicv3_driver_data != NULL);
    	assert(proc_num < gicv3_driver_data->rdistif_num);
    	assert(gicv3_driver_data->rdistif_base_addrs != NULL);
    	assert(gicv3_driver_data->gicd_base != 0U);
    
    	ctlr = gicd_read_ctlr(gicv3_driver_data->gicd_base);
    	assert((ctlr & CTLR_ARE_S_BIT) != 0U);
    
    	assert(IS_IN_EL3());
    
    	/* Power on redistributor */
    	gicv3_rdistif_on(proc_num);
    
    	gicr_base = gicv3_driver_data->rdistif_base_addrs[proc_num];
    	assert(gicr_base != 0U);
    
    	/* Set the default attribute of all SGIs and (E)PPIs */
    	gicv3_ppi_sgi_config_defaults(gicr_base);
    
    	bitmap = gicv3_secure_ppi_sgi_config_props(gicr_base,
    			gicv3_driver_data->interrupt_props,
    			gicv3_driver_data->interrupt_props_num);
    
    	/* Enable interrupt groups as required, if not already */
    	if ((ctlr & bitmap) != bitmap) {
    		gicd_set_ctlr(gicv3_driver_data->gicd_base, bitmap, RWP_TRUE);
    	}
    }
    
    /******************************************************************************
     * ARM common helper to initialize the GIC. Only invoked by BL31
     *****************************************************************************/
    void __init plat_arm_gic_init(void)
    {
    	gicv3_distif_init();
    	gicv3_rdistif_init(plat_my_core_pos());
    	gicv3_cpuif_enable(plat_my_core_pos());
    }
    

    系统下电,CPU如何与GIC redistributor disconnect

    当core下电之后,需要将core中cpu interface与gic中的redistributor进行disconnect,这样将来gic才不会将中断发送给core。

    disconnection的流程如下所示:
    在这里插入图片描述

    下电流程,行为描述

    1. 执行在core的程序,先将cpu interface的中断组使能给disable。
    2. 执行在core的程序,将GICR_WAKER.ProcessorSleep位给置高,表示要disconnect redistributor。
    3. redistributor给cpu interface发送 Quiesce包。
    4. cpu interface清掉内部所有pending的中断。
    5. 清除完毕后,cpu interface回发Quiesce Acknowledge包给redistibutor。
    6. redistributor收到cpu interface回发的响应之后,将GICR_WAKER.ChildrenAsleep位给置高,表示disconnect完成。
    7. 执行在core的程序,查询GICR_WAKER.ChildAsleep位是否为1,如果不是,表示redistributor还没有完成connect操作,就继续查询。
    8. 如果查询到1,表示disconnect完成。

    其汇编代码如下:
    在这里插入图片描述

    /******************************************************************************
     * This function marks the core as asleep in the re-distributor and ensures
     * that the interface is quiescent.
     *****************************************************************************/
    void gicv3_rdistif_mark_core_asleep(uintptr_t gicr_base)
    {
    	/* Mark the connected core as asleep */
    	/* 执行在core的程序,将GICR_WAKER.ProcessorSleep位给置高,
    	 * 表示要disconnect redistributor。
    	 */
    	gicr_write_waker(gicr_base, gicr_read_waker(gicr_base) | WAKER_PS_BIT);
    
    	/* Wait till the WAKER_CA_BIT changes to 1 */
    	/* 执行在core的程序,查询GICR_WAKER.ChildAsleep位是否为1,
    	 * 如果不是,表示redistributor还没有完成connect操作,就继续查询。
    	 */
    	while ((gicr_read_waker(gicr_base) & WAKER_CA_BIT) == 0U) {
    	}
    }
    
    /*******************************************************************************
     * This function disables the GIC CPU interface of the calling CPU using
     * only system register accesses.
     ******************************************************************************/
    void gicv3_cpuif_disable(unsigned int proc_num)
    {
    	uintptr_t gicr_base;
    
    	assert(gicv3_driver_data != NULL);
    	assert(proc_num < gicv3_driver_data->rdistif_num);
    	assert(gicv3_driver_data->rdistif_base_addrs != NULL);
    
    	assert(IS_IN_EL3());
    
    	/* Disable legacy interrupt bypass */
    	write_icc_sre_el3(read_icc_sre_el3() |
    			  (ICC_SRE_DIB_BIT | ICC_SRE_DFB_BIT));
    	
    	/* 1. 执行在core的程序,先将cpu interface的中断组使能给disable。 */
    	/* Disable Group0 interrupts */
    	write_icc_igrpen0_el1(read_icc_igrpen0_el1() &
    			      ~IGRPEN1_EL1_ENABLE_G0_BIT);
    
    	/* Disable Group1 Secure and Non-Secure interrupts */
    	write_icc_igrpen1_el3(read_icc_igrpen1_el3() &
    			      ~(IGRPEN1_EL3_ENABLE_G1NS_BIT |
    			      IGRPEN1_EL3_ENABLE_G1S_BIT));
    
    	/* Synchronise accesses to group enable registers */
    	isb();
    
    	/* Mark the connected core as asleep */
    	gicr_base = gicv3_driver_data->rdistif_base_addrs[proc_num];
    	assert(gicr_base != 0U);
    	gicv3_rdistif_mark_core_asleep(gicr_base);
    }
    
    

    以下是GIC-600 支持redistributor单独下电。

    static void gic600_pwr_off(uintptr_t base)
    {
    	/* Wait until group not transitioning */
    	gicr_wait_group_not_in_transit(base);
    
    	/* Power off redistributor */
    	gicr_write_pwrr(base, PWRR_OFF);
    
    	/*
    	 * If this is the last man, turning this redistributor frame off will
    	 * result in the group itself being powered off and RDGPD = 1.
    	 * In that case, wait as long as it's in transition, or has aborted
    	 * the transition altogether for any reason.
    	 */
    	if ((gicr_read_pwrr(base) & PWRR_RDGPD) != 0U) {
    		/* Wait until group not transitioning */
    		gicr_wait_group_not_in_transit(base);
    	}
    }
    
    /*
     * The Arm GIC-600 and GIC-Clayton models have their redistributors
     * powered down at reset.
     */
    /*
     * Power off GIC-600 redistributor (if configured and detected)
     */
    void gicv3_rdistif_off(unsigned int proc_num)
    {
    #if GICV3_SUPPORT_GIC600
    	uintptr_t gicr_base = get_gicr_base(proc_num);
    
    	/* Attempt to power redistributor off */
    	if (gicv3_redists_need_power_mgmt(gicr_base)) {
    		gic600_pwr_off(gicr_base);
    	}
    #endif
    }
    
    展开全文
  • ARM GIC介绍之四

    2021-03-31 12:01:22
    GICARM体系中重要的组件,在认识到GIC的组成和功能之后,了解到IRQ的大致流程,从硬件IRQ到来,到IRQ结束。我们实际在KERNEL里面,或者在设备驱动里面处理的IRQ其实是软件意义上的,那么硬件的中断和软件的中断...

    GIC是ARM体系中重要的组件,在认识到GIC的组成和功能之后,了解到IRQ的大致流程,从硬件IRQ到来,到IRQ结束。我们实际在KERNEL里面,或者在设备驱动里面处理的IRQ其实是软件意义上的,那么硬件的中断和软件的中断如何联系起来的呢,大概的处理流程是如何呢?
    这章我们介绍这部分内容。

    GIC 中断处理流程

    我们希望理解概念和流程,总结认识和思路,所以代码细节上的解释需要忽略掉。可以看代码细节,但是总结时候要去掉。毕竟,即使是自己看过了,过了一段时间再重新读代码,也是有些陌生的。我们还是以图开始。


    我们在之前介绍“arm GIC介绍之一/二/三”:
    http://blog.csdn.net/sunsissy/article/details/73791470
    http://blog.csdn.net/sunsissy/article/details/73842533
    http://blog.csdn.net/sunsissy/article/details/73842533
    一直在强调,GIC上对物理的IRQ的处理,比如上图,一个DEVICE_1上触发一个IRQ,到GIC,HWIRQ为111,如果软件侧管理中断不冲突的话,可以直接映射desc_irq 111,以此为结构并处理。但是实际中并不这么完美。
    比如图中另外一个DEVICE,同时来了3个信号,或者说,而这个设备和GIC只有一个IRQ的物理连接通路,只能传递一个IRQ信号,那么这又如何表示和区分3个信号呢,如何和CPU的软件意义上的IRQ联系起来呢?

    这就新增加了IRQ_DOMAIN的概念。

    struct irq_domain {  
       struct list_head link;  
       const char *name;  
       const struct irq_domain_ops *ops; //callback函数 
       void *host_data;//this will point to irq_data, and contains gicd_base, info and so on.
        /* Optional data */   
       struct device_node *of_node; //该interrupt domain对应的interrupt controller的device node  
       struct irq_domain_chip_generic *gc;  //generic irq chip concept , we ignore this.
        /* reverse map data. The linear map gets appended to the irq_domain */
        irq_hw_number_t hwirq_max; //该domain中最大的那个HW interrupt ID     
        unsigned int revmap_direct_max_irq; //
        unsigned int revmap_size; //线性映射的size,for Radix Tree map和no map,该值等于0     
        struct radix_tree_root revmap_tree; //Radix Tree map will use radix tree root node     
        unsigned int linear_revmap[]; //linear mapping lookup table, we will pay attention to it. 
        };

    简化后如图:

    总结来说,IRQ_DOMAIN是以GIC 为单位,一个GIC设备对应一个IRQ_DOMAIN, 从图中可以看出,如果系统中有多个IRQ_DOMAIN,那么会形成一个list,统一管理
    IRQ_DOMAIN里面包含了GIC的基本信息,比如host_data,可以保存对应的Distributor的基地址;最大的硬件中断数目hwirq_max;如果是线性映射,那么linear_revmap保存了线性映射的关系;
    当然,重要的irq_domain_ops里面有对应的操作,eg:gic_irq_domain_map这就是如何把硬件IRQ和软件处理侧的desc_irq对应起来的。
    只有这样映射后,软件侧才方便以desc_irq单位对IRQ进行管理和处理,desc_irq如图:

     

    对每个desc_irq,不仅包含了一个IRQ的基本信息,也包含了对应的控制等信息。
    Irq_data中,有一个IRQ对应的硬件中断号hwirq,对应的GIC DOMAIN。当然Irq_chip以及对应的API,提供了如何向GIC写入信息比如表示IRQ处理结束写入Irq_eoi等等。
    Irq_action 里面有中断的处理入口,以及对应的具体函数HANDLER。
    这是在软件处理层面的,有了这样的接口和信息,一个驱动才可以获得一个desc_irq,把对应的IRQ注册到里面,并对触发方式,是否MASK进行控制,并找到对应的HANDLER进行注册和后续处理。所以从整个的流程来说我们给出这样的逻辑结构,见下图:

    整个结构分为3部分,上面试DRIVER,可以认为是使用者,中间是软件的层面,最下面是Hardware ,这里重要的是中间的结构,为上层DRIVER提供了IRQ的注册接口request_threaded_irq使能接口enable_irq,亲和性或者说送到具体CPU处理的配置接口irq_set_affinity等,这里不全部列举。
    那么这些接
    口要具体配置到GIC硬件的Hardware上,在IRQ general logic中就有对应的 irq_set_chip,这里面对应的操作会去执行和具体平台硬件相关的设置。不仅如此,当设置完成,允许中断,如果来了一个硬件中断到Hardware ,那么也在IRQ general logic进行先处理,所以这提供双向的SERVICE。会先irq_to_desc,找到对应的desc_irq,然后分类处理如果IPI走对应处理,如果是其它的走generic_handle_irq,这就转到了左侧的IRQ flow
    control-layer 。
    IRQ flow control-layer顾名思义,就是把众多的接受到的中断分流,如果是LEVEL触发类型的,走handle_level_irq,上下沿触发的走handle_edge_irq,不同的入口可能对EOI的写入时机和方式有区别等等。再如handle_percpu_irq在处理时候,因为不涉及到其它CPU,所以对于多个CPU之间共享的操作就不需要LOCK做保护。然后再去找特定的每个驱动定义的HANDLER处理。

    GIC 中断处理流程实例

    看下:

    shell@amt6797_64_open:/ shell@amt679764open:/shell@amt679764open:/ cat /proc/interrupts
    CPU0
    29: 0 GICv3 29 arch_timer_sec_zhonghua
    30: 50721 GICv3 30 arch_timer
    96: 0 GICv3 96 mtk_cpuxgpt0
    97: 0 GICv3 97 mtk_cpuxgpt1
    ……
    184: 45 GICv3 184 mtk_cmdq
    188: 0 GICv3 188 m4u
    201: 0 GICv3 201 mt-gpt
    210: 0 GICv3 210 pmic_wrap
    211: 0 GICv3 211 mtk-kpd
    212: 0 GICv3 212 SPM
    231: 0 GICv3 231 SCP IPC_MD2HOST
    234: 720 GICv3 234 mutex

    ……
    255: 0 GICv3 255 aal
    361: 0 GICv3 361 ocp_cluster2
    362: 0 GICv3 362 ocp_cluster2
    389: 1 mt-eint 5 TOUCH_PANEL-eint
    390: 0 mt-eint 6 11240000.msdc1 cd
    392: 0 mt-eint 8 iddig_eint
    400: 0 mt-eint 16 accdet-eint
    560: 1 mt-eint 176 pmic-eint
    IPI0: 7106 Rescheduling interrupts
    IPI1: 7 Function call interrupts
    IPI2: 196 Single function call interrupts
    IPI3: 0 CPU stop interrupts
    IPI4: 0 Timer broadcast interrupts
    IPI5: 112 IRQ work interrupts
    Err: 0
    shell@amt6797_64_open:/
    上面是从X20上看到interrupts。左侧部分加粗体是硬件中断号,右侧对应的斜体数字是软件看到的desc_irq对应的编号。
    这里面可以看到,以左侧硬件中断号为索引来说。上面是29和30号中断,这个是PPI中断类型,在这里是CPU对应的TIMER,从这往后到362是SPI,在X20上支持384个硬件中断号,所以多出来的389~560就奇怪了。我们后面介绍。
    另外就是IPI消息,这个比较少。
    我们先分析下,硬件中断号如何和软件对应的。
    首先:

    在进入KERNEL之后,进行GIC初始化后,我们之前提到IRQ_DOMAIN的配置,提到以GIC为单位。那么在这里通过读取参数获取到支持的HARDWARE IRQ数目是384个,去掉其中16个SGI(不需要映射),新申请384-16个desc_irq,并且把IRQ_DOMAIN其它数据如基地址等都填好,挂到LIST上统一管理。在这里,采用的是简单的线性MAPPING,所以17到384这中间的硬件中断号和软件中断号是一一对应的,打印出来:

    第一个编号为16,最后一个编号为383的IRQ。
    之后进行基本的irq_domain_associate_many。我们看下这里做了什么:


    这里名字上说是associate,其实是对这些IRQ做些分类的基本信息填充,主要的是区分16~31号中断的描述信息,由于是PPI中断,所以将其分流入口设定为handle_percpu_devid_irq那么其它一般的SPI 分流入口这里都设定为handle_fasteoi_irq,而不是handle_level_irq或者handle_edge_irq。

    展开全文
  • arm GIC介绍之一

    千次阅读 2018-05-13 22:30:11
    本文摘自 sunsissy 的《arm GIC介绍之一》:https://blog.csdn.net/sunsissy/article/details/73791470 GICARM架构中及其重要的部分,本文只在公开ARM对应资料基础上,以MTK开发板为基础整理。个人理解之后记录,...
  • ARM公司提供的一个通用的中断控制器。主要作用为:接受硬件中断信号,并经过一定处理后,分发给对应的CPU进行处理。 GIC v3中断类别: GICv3定义了以下中断类型: SGI (Software Generated Interrupt):软件触发...
  • 下面以一个常见的低电平触发的中断为例,下面是一个中断的完整上报流程: 外设产生中断,拉低连接到 gic 的中断线,gic 中对应中断线的 pending bit 被置位,表示中断产生,这是 gic 的硬件自动完成的。 如果该中断...
  • arm GIC介绍之三

    2018-05-13 22:36:21
    本文摘自 sunsissy 的《arm GIC介绍之一》:https://blog.csdn.net/sunsissy/article/details/73791470 请大家多多尊重原创 GICARM架构中及其重要的部分,本文只在公开ARM对应资料基础上,以MTK开发板为基础整理...
  • ARM GIC中断架构描述

    2020-10-08 08:58:33
    ARM Generic Interrupt Controller描述文档,对于想了解ARM 中断控制器原理的同学比较有帮助
  • ARM GIC介绍之一

    2021-03-31 10:52:42
    GICARM架构中及其重要的部分,本文只在公开ARM对应资料基础上,以MTK开发板为基础整理。个人理解之后记录,巩固和加深认识,仅此而已,如果有错误,欢迎指出。 1. GIC的概述 看过SOC架构的同学知道,CPU接受外部...
  • 目录中断生命周期GIC寄存器 中断生命周期 generate:外设发起一个中断 distribute:distributor对收到的中断源进行仲裁,然后发送给对应的cpu interface deliver:cpu interface将中断发送给core activate:core...
  • 文章目录1、Terms and Abbreviations2、Introduction2.2 Brief history of the GIC architecture3、GICv3 fundamentals3.1.1 ARM 中断类型3.1.2 Interrupt Identifiers3.2 Interrupt state machine3.2.2 Edge-...
  • ARM Generic Interrupt Controller(ARM GIC控制器)V3.0与V4.0,是V2的升级版本
  • ARMGIC开发手册,可以作为详细的参考。除了GIC CPU Interface,整个GIC的实现还包括GIC Distributor、和外设中断线接口等等。
  • ARM GIC介绍之三

    2021-03-31 11:44:15
    GIC_V3 初始化设置 在GIC 介绍之一和二中,可以看到GIC的逻辑结构和硬件框架。 那么在初始化时候,需要将各个组件配置起来,Interrupt Controller中的Distributor,Redistributor,CPU Interface需要按照不同顺序配置...
  • 1、HW中断系统的逻辑block图 中断接口:ARM以及GIC组成的中断系统中,具体中断接口的形态就是一个硬件的信号线,通过电平信号传递中断事件 控制接口:CPU和Interrupt Controller之间还需要有控制信息的交流。...
  • 前几篇博文主要梳理了GIC V3架构基础,中断全生命周期,以及中断IRQ、FIQ基础概念。那在ARM TrustZone是如何支持安全中断的呢? ARM TrustZone一个非常大的优势就是很容易管理外设,管理外设包含了:外设访问权限,...
  • arm gic中断控制器

    2018-08-03 08:58:06
    arm gic中断控制器,arm学习必须,理解arm中断不可缺少,欢迎下载,
  • Linux 中断 —— ARM GIC 中断控制器

    千次阅读 2019-05-23 00:14:54
    GIC(Generic Interrupt Controller)是ARM公司提供的一个通用的中断控制器,其architecture specification目前有四个版本,V1~V4(V2最多支持8个ARM core,V3/V4支持更多的ARM core,主要用于ARM64服务器系统结构)...
  • ARM Generic Interrupt Controller(ARM GIC控制器)V2.0.pdf
  • ARM GIC(二)中断术语

    2020-09-27 18:09:20
    ARMGIC中,对于中断,定义了如下的一些术语。 一、中断状态 对于每一个中断而言,有以下4个状态: ◾inactive:中断处于无效状态 ◾pending:中断处于有效状态,但是cpu没有响应该中断 ◾active:cpu在响应该中断 ...
  • ARM Generic Interrupt Controller(ARM GIC控制器)V3.0与V4.0.pdf
  • 除了 GIC 会进行中断屏蔽外,处理器本身 PSTATE 的 DAIF 也可以进行屏蔽。 Vector table。VBAR_ELn路由选择决定了中断被哪个EL 所处理,即采用哪个异常级的中断向量表。 以上三者除了 PSTATE 复位会有值外其他寄存器...
  • ARM gic v2

    2022-05-13 16:26:26
    1.1.对于分发器, 0-31号中断是'banked'.(相关描述最好感受原文ARM®Generic Interrupt Controller Architecture version 2.0) Interrupt numbers ID0-ID31 are used for interrupts that are private to a CPU ...
  • arm GIC介绍之二

    千次阅读 2018-05-13 22:33:58
    本文摘自 sunsissy 的《arm GIC介绍之一》:https://blog.csdn.net/sunsissy/article/details/73842533 GICARM架构中及其重要的部分,本文只在公开ARM对应资料基础上,以MTK开发板为基础整理。个人理解之后记录,...
  • Linux 中断管理之ARM GIC V3 初始化

    千次阅读 2020-03-14 11:10:18
    1.ARM GIC V3中断控制器介绍 GIC(Generic Interrupt Controller)是一个通用的中断控制器,用来接收硬件中断信号,并经过一定处理后,分发给对应的CPU进行处理。GIC V3是其中一个版本,支持的中断类型如下表: ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,736
精华内容 1,494
关键字:

arm gic

友情链接: zfpdu.rar