linux arm irq (1): irq init
linux arm irq (1)
1 irq init
Author: Yangkai Wang
wang_yangkai@163.com
Coding in 2021/05/10
转载请注明author,出处.
linux version 3.4.39
s5p6818 soc
Cortex-A53 Octa core CPU
Interrupt Controller,GIC400
GIC (Generic Interrupt Controllers), reference:Arm Generic Interrupt Controller Architecture version 2.0,Architecture Specification
- ARM SOC(GIC)发生中断主要处理流程(超精简描述):
- 进入中断处理流程;
- 读取Hw interrupt number,找到对应的interrupt descriptor(struct irq_desc),call irq_desc->action->handler();clean int flag;
- 退出中断;
Note:其中action->handler(),是其他外设驱动通过request_irq(),注册给irq子系统的interrupt handlle;
- ARM SOC(GIC)发生中断主要处理流程(精简描述):
- 进入irq模式,切换到svc模式,保存现场;
- 读取GIC irq chip 获取是哪个硬件中断number,这个Hw int number是SOC硬件设计确定的;
对于GIC Share Peripheral Interrupt(SPI),一般是一个soc controller对应一个Hw int number; - 找到这个Hw int 对应的interrupt descriptor,(struct irq_desc);call 其handle_irq(high level irq-events handler);
handle_irq中call action->handler();
PS:这个Hw int如果是一个irq chip,(这个中断chained若干个中断,可以是int controller或一个GPIO控制器连接若干个IO),这个中断的handle_irq(),(highlevel irq-events handler)中就需要去读取这个irq chip 的寄存器,确定Hw init number;再走lable 3流程; - 执行GIC 的end of irq operations;(action->handler()中有clear irq(operate controller))
- b ret_to_user_from_irq or svc_exit r5;
irq init,就是为了完成如上描述的过程;做的初始化工作;
- 1 early_irq_init
asmlinkage void __init start_kernel(void)
|
early_irq_init(); /* struct irq_desc irq_desc[NR_IRQS] initilaze */
|
arch_early_irq_init(); /* no implementa */
/* kernel/irq/irqdesc.c */
struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = {
[0 ... NR_IRQS-1] = {
.handle_irq = handle_bad_irq,
.depth = 1,
.lock = __RAW_SPIN_LOCK_UNLOCKED(irq_desc->lock),
}
};
int __init early_irq_init(void)
{
int count, i, node = first_online_node;
struct irq_desc *desc;
init_irq_default_affinity();
printk(KERN_INFO "NR_IRQS:%d\n", NR_IRQS);
desc = irq_desc;
count = ARRAY_SIZE(irq_desc);
printk("~~~ %s() irq_desc count:%d, call desc_set_defaults()\n", \
__func__, count);
for (i = 0; i < count; i++) {
desc[i].kstat_irqs = alloc_percpu(unsigned int);
alloc_masks(&desc[i], GFP_KERNEL, node);
raw_spin_lock_init(&desc[i].lock);
lockdep_set_class(&desc[i].lock, &irq_desc_lock_class);
desc_set_defaults(i, &desc[i], node, NULL);
}
return arch_early_irq_init();
}
early_irq_init(),中,初始化struct irq_desc irq_desc[NR_IRQS],数组;这个数组是全局变量,大小固定,由SOC设计的中断数量决定;
NR_IRQS,是SOC的中断的总数;
使用dts的平台一般都是在dts中配置好中断相关描述,内核解析,动态生成全局struct irq_desc irq_desc;
总之:
struct irq_desc irq_desc[NR_IRQS];是linux中断子系统的核心数据结构;
interrupt descriptor;
一个struct irq_desc irq_desc[] 成员,代表一个中断;
/* arch/arm/mach-s5p6818/include/mach/irqs.h */
#include "s5p6818_irq.h"
#define NR_IRQS IRQ_TOTAL_MAX_COUNT
/* arch/arm/mach-s5p6818/include/mach/s5p6818_irq.h */
/*
* (C) Copyright 2009
* jung hyun kim, Nexell Co, <jhkim@nexell.co.kr>
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#ifndef __S5P6818_INTR_H__
#define __S5P6818_INTR_H__
/*
* GIC Interrupt (0 ~ 32), must be align 32
*/
#define IRQ_GIC_START (0)
#define IRQ_GIC_PPI_START (IRQ_GIC_START + 16)
#define IRQ_GIC_PPI_PVT (IRQ_GIC_START + 29)
#define IRQ_GIC_PPI_WDT (IRQ_GIC_START + 30)
#define IRQ_GIC_PPI_VIC (IRQ_GIC_START + 31)
#define IRQ_GIC_END (IRQ_GIC_START + 32)
/*
* Physical Interrupt Number 64 (0~63)
*/
#define IRQ_PHY_MCUSTOP (0 + 32)
#define IRQ_PHY_DMA0 (1 + 32)
#define IRQ_PHY_DMA1 (2 + 32)
#define IRQ_PHY_CLKPWR_INTREQPWR (3 + 32)
#define IRQ_PHY_CLKPWR_ALIVEIRQ (4 + 32)
#define IRQ_PHY_CLKPWR_RTCIRQ (5 + 32)
#define IRQ_PHY_UART1 (6 + 32) // pl01115_Uart_modem
#define IRQ_PHY_UART0 (7 + 32) // UART0_MODULE
#define IRQ_PHY_UART2 (8 + 32) // UART1_MODULE
#define IRQ_PHY_UART3 (9 + 32) // pl01115_Uart_nodma0
#define IRQ_PHY_UART4 (10 + 32) // pl01115_Uart_nodma1
#define IRQ_PHY_UART5 (11 + 32) // pl01115_Uart_nodma2
#define IRQ_PHY_SSP0 (12 + 32)
#define IRQ_PHY_SSP1 (13 + 32)
#define IRQ_PHY_SSP2 (14 + 32)
#define IRQ_PHY_I2C0 (15 + 32)
#define IRQ_PHY_I2C1 (16 + 32)
#define IRQ_PHY_I2C2 (17 + 32)
#define IRQ_PHY_DEINTERLACE (18 + 32)
#define IRQ_PHY_SCALER (19 + 32)
#define IRQ_PHY_AC97 (20 + 32)
#define IRQ_PHY_SPDIFRX (21 + 32)
#define IRQ_PHY_SPDIFTX (22 + 32)
#define IRQ_PHY_TIMER_INT0 (23 + 32)
#define IRQ_PHY_TIMER_INT1 (24 + 32)
#define IRQ_PHY_TIMER_INT2 (25 + 32)
#define IRQ_PHY_TIMER_INT3 (26 + 32)
#define IRQ_PHY_PWM_INT0 (27 + 32)
#define IRQ_PHY_PWM_INT1 (28 + 32)
#define IRQ_PHY_PWM_INT2 (29 + 32)
#define IRQ_PHY_PWM_INT3 (30 + 32)
#define IRQ_PHY_WDT (31 + 32)
#define IRQ_PHY_MPEGTSI (32 + 32)
#define IRQ_PHY_DPC_P (33 + 32)
#define IRQ_PHY_DPC_S (34 + 32)
#define IRQ_PHY_RESCONV (35 + 32)
#define IRQ_PHY_HDMI (36 + 32)
#define IRQ_PHY_VIP0 (37 + 32)
#define IRQ_PHY_VIP1 (38 + 32)
#define IRQ_PHY_MIPI (39 + 32)
#define IRQ_PHY_VR (40 + 32)
#define IRQ_PHY_ADC (41 + 32)
#define IRQ_PHY_PPM (42 + 32)
#define IRQ_PHY_SDMMC0 (43 + 32)
#define IRQ_PHY_SDMMC1 (44 + 32)
#define IRQ_PHY_SDMMC2 (45 + 32)
#define IRQ_PHY_CODA960_HOST (46 + 32)
#define IRQ_PHY_CODA960_JPG (47 + 32)
#define IRQ_PHY_GMAC (48 + 32)
#define IRQ_PHY_USB20OTG (49 + 32)
#define IRQ_PHY_USB20HOST (50 + 32)
#define IRQ_PHY_CAN0 (51 + 32)
#define IRQ_PHY_CAN1 (52 + 32)
#define IRQ_PHY_GPIOA (53 + 32)
#define IRQ_PHY_GPIOB (54 + 32)
#define IRQ_PHY_GPIOC (55 + 32)
#define IRQ_PHY_GPIOD (56 + 32)
#define IRQ_PHY_GPIOE (57 + 32)
#define IRQ_PHY_CRYPTO (58 + 32)
#define IRQ_PHY_PDM (59 + 32)
#define IRQ_PHY_TMU0 (60 + 32)
#define IRQ_PHY_TMU1 (61 + 32)
#define IRQ_PHY_VIP2 (72 + 32)
#define IRQ_PHY_MAX_COUNT (74 + 32) // ADD GIC IRQ
/*
* GPIO Interrupt Number 160 (106~265)
*/
#define IRQ_GPIO_START IRQ_PHY_MAX_COUNT
#define IRQ_GPIO_END (IRQ_GPIO_START + 32 * 5) // Group: A,B,C,D,E
#define IRQ_GPIO_A_START (IRQ_GPIO_START + PAD_GPIO_A)
#define IRQ_GPIO_B_START (IRQ_GPIO_START + PAD_GPIO_B)
#define IRQ_GPIO_C_START (IRQ_GPIO_START + PAD_GPIO_C)
#define IRQ_GPIO_D_START (IRQ_GPIO_START + PAD_GPIO_D)
#define IRQ_GPIO_E_START (IRQ_GPIO_START + PAD_GPIO_E)
/*
* ALIVE Interrupt Number 6 (266~271)
*/
#define IRQ_ALIVE_START IRQ_GPIO_END
#define IRQ_ALIVE_END (IRQ_ALIVE_START + 6)
#define IRQ_ALIVE_0 (IRQ_ALIVE_START + 0)
#define IRQ_ALIVE_1 (IRQ_ALIVE_START + 1)
#define IRQ_ALIVE_2 (IRQ_ALIVE_START + 2)
#define IRQ_ALIVE_3 (IRQ_ALIVE_START + 3)
#define IRQ_ALIVE_4 (IRQ_ALIVE_START + 4)
#define IRQ_ALIVE_5 (IRQ_ALIVE_START + 5)
/*
* MAX(Physical+Virtual) Interrupt Number
*/
#define IRQ_SYSTEM_END IRQ_ALIVE_END
#if defined (CONFIG_REGULATOR_NXE2000)
#define IRQ_RESERVED_OFFSET 72 // refer NXE2000_NR_IRQS <linux/mfd/nxe2000.h>
#else
#define IRQ_RESERVED_OFFSET 0
#endif
#define IRQ_SYSTEM_RESERVED IRQ_RESERVED_OFFSET
#define IRQ_TOTAL_MAX_COUNT (IRQ_SYSTEM_END + IRQ_SYSTEM_RESERVED)
#endif //__S5P6818_INTR_H__
- 2 init_IRQ
asmlinkage void __init start_kernel(void)
|
init_IRQ();
|
machine_desc->init_irq(); /* call nxp_cpu_irq_init() */
/* arch/arm/mach-s5p6818/irq.c */
/*
* cpu irq handler
*/
void __init nxp_cpu_irq_init(void)
{
pr_debug("%s:%d\n", __func__, __LINE__);
printk("~~~ %s()\n", __func__);
__gic_init(GIC_DIST_BASE, (void __iomem *)GIC_CPUI_BASE);
gpio_init(GPIO_INT_BASE , IRQ_GPIO_START, GPIO_INT_MASK, 0); /* 64 ~ 223 (A,B,C,D,E) */
alive_init(ALIVE_INT_BASE, IRQ_ALIVE_START, ALIVE_INT_MASK, 0); /* 224 ~ 231 */
#ifdef CONFIG_FIQ
init_FIQ();
#endif
/* wake up source from idle */
irq_set_irq_wake(IRQ_PHY_CLKPWR_ALIVEIRQ + GIC_PHY_OFFSET, 1);
#if PM_RTC_WAKE
irq_set_irq_wake(IRQ_PHY_CLKPWR_RTCIRQ + GIC_PHY_OFFSET, 1);
#endif
}
nxp_cpu_irq_init(),中,
gic 初始化:
__gic_init(GIC_DIST_BASE, (void __iomem *)GIC_CPUI_BASE);
gpio 中断相关初始化:
gpio_init(GPIO_INT_BASE , IRQ_GPIO_START, GPIO_INT_MASK, 0);C,D,E);
alive_init(ALIVE_INT_BASE, IRQ_ALIVE_START, ALIVE_INT_MASK, 0);
主要是完成这些事:
- 初始化中断控制器(gic irq chip, gpio irq chip);
- 设置struct irq_desc irq_desc[NR_IRQS];的handle_irq,high level irq-events handler;
wyk@ubuntu:~/github/NanoPi3/linux-3.4.y$ grep "irq-events handler" . -nir
./include/linux/irqdesc.h:20: * @handle_irq: highlevel irq-events handler
wyk@ubuntu:~/github/NanoPi3/linux-3.4.y$
- 3 __gic_init()
static void __init __gic_init(void __iomem *dist_base, void __iomem *cpu_base)
{
int irq = IRQ_GIC_PPI_VIC;
printk(KERN_INFO "GIC @%p: start %3d (gic %d)\n",
dist_base, IRQ_GIC_START, (irq-IRQ_GIC_START));
printk("~~~ %s() call gic_init()\n", __func__);
gic_init(0, IRQ_GIC_PPI_START, dist_base, cpu_base);
}
__gic_init(),参数是void __iomem *dist_base, void __iomem *cpu_base,GIC控制器register base address虚拟地址;
有:
#define GIC_DIST_BASE (void __iomem *)(INTC_BASE + 0x00001000) // 0xC0009000
#define GIC_CPUI_BASE (void __iomem *)(INTC_BASE + 0x00002000) // 0xC000a000
#define INTC_BASE (void __iomem *)IO_ADDRESS(PHY_BASEADDR_INTC)
#define GIC_DIST_BASE (void __iomem *)(INTC_BASE + 0x00001000) // 0xC0009000
static inline void gic_init(unsigned int nr, int start,
void __iomem *dist , void __iomem *cpu)
{
printk("~~~ %s() call gic_init_bases()\n", __func__);
gic_init_bases(nr, start, dist, cpu, 0, NULL);
}
gic_init(), int nr是 GIC控制器的number,编号;只有一个GIC控制器,nr为0, int start是 GIC PPI INT 的起始Hw int number,为16
有:
#define IRQ_GIC_START (0)
#define IRQ_GIC_PPI_START (IRQ_GIC_START + 16)
Note:(reference GIC datasheet)
GIC inttrupt type:
- Peripheral Interrupt
Private Peripheral Interrupt(PPI)
Interrupt Number 16--31 are used for PPIs
Share Peripheral Interrupt(SPI)
Interrupt numbers 32--1019 are used for SPIs - Software Generated Interrupt(SGI)
Interrupt number 0--15 are used for SGIs - Virtual interrupt
- Maintenance interrupt
/* arch/arm/mach-s5p6818/gic.c */
static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
irq_hw_number_t hw)
{
/*printk("~~~ %s() irq:%d, hw:%d, call irq_set_chip_and_handler()\n", \
__func__, irq, hw); */
if (hw < 32) {
irq_set_percpu_devid(irq);
irq_set_chip_and_handler(irq, &gic_chip,
handle_percpu_devid_irq);
set_irq_flags(irq, IRQF_VALID | IRQF_NOAUTOEN);
} else {
irq_set_chip_and_handler(irq, &gic_chip,
handle_fasteoi_irq);
set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
}
irq_set_chip_data(irq, d->host_data);
return 0;
}
static int gic_irq_domain_xlate(struct irq_domain *d,
struct device_node *controller,
const u32 *intspec, unsigned int intsize,
unsigned long *out_hwirq, unsigned int *out_type)
{
if (d->of_node != controller)
return -EINVAL;
if (intsize < 3)
return -EINVAL;
/* Get the interrupt number and add 16 to skip over SGIs */
*out_hwirq = intspec[1] + 16;
/* For SPIs, we need to add 16 more to get the GIC irq ID number */
if (!intspec[0])
*out_hwirq += 16;
*out_type = intspec[2] & IRQ_TYPE_SENSE_MASK;
return 0;
}
const struct irq_domain_ops gic_irq_domain_ops = {
.map = gic_irq_domain_map,
.xlate = gic_irq_domain_xlate,
};
void __init gic_init_bases(unsigned int gic_nr, int irq_start,
void __iomem *dist_base, void __iomem *cpu_base,
u32 percpu_offset, struct device_node *node)
{
irq_hw_number_t hwirq_base;
struct gic_chip_data *gic;
int gic_irqs, irq_base;
printk("~~~ %s() gic_nr:%d, irq_start:%d\n", __func__, \
gic_nr, irq_start);
BUG_ON(gic_nr >= MAX_GIC_NR);
gic = &gic_data[gic_nr];
#ifdef CONFIG_GIC_NON_BANKED
if (percpu_offset) { /* Frankein-GIC without banked registers... */
unsigned int cpu;
gic->dist_base.percpu_base = alloc_percpu(void __iomem *);
gic->cpu_base.percpu_base = alloc_percpu(void __iomem *);
if (WARN_ON(!gic->dist_base.percpu_base ||
!gic->cpu_base.percpu_base)) {
free_percpu(gic->dist_base.percpu_base);
free_percpu(gic->cpu_base.percpu_base);
return;
}
for_each_possible_cpu(cpu) {
unsigned long offset = percpu_offset * cpu_logical_map(cpu);
*per_cpu_ptr(gic->dist_base.percpu_base, cpu) = dist_base + offset;
*per_cpu_ptr(gic->cpu_base.percpu_base, cpu) = cpu_base + offset;
}
gic_set_base_accessor(gic, gic_get_percpu_base);
} else
#endif
{ /* Normal, sane GIC... */
WARN(percpu_offset,
"GIC_NON_BANKED not enabled, ignoring %08x offset!",
percpu_offset);
gic->dist_base.common_base = dist_base;
gic->cpu_base.common_base = cpu_base;
gic_set_base_accessor(gic, gic_get_common_base);
}
/*
* For primary GICs, skip over SGIs.
* For secondary GICs, skip over PPIs, too.
*/
if (gic_nr == 0 && (irq_start & 31) > 0) {
hwirq_base = 16;
if (irq_start != -1)
irq_start = (irq_start & ~31) + 16;
} else {
hwirq_base = 32;
}
printk("~~~ %s() hwirq_base:%u\n", __func__, hwirq_base);
/*
* Find out how many interrupts are supported.
* The GIC only supports up to 1020 interrupt sources.
*/
gic_irqs = readl_relaxed(gic_data_dist_base(gic) + GIC_DIST_CTR) & 0x1f;
gic_irqs = (gic_irqs + 1) * 32;
printk("~~~ %s() gic_irqs:%d\n", __func__, gic_irqs);
if (gic_irqs > 1020)
gic_irqs = 1020;
gic_irqs = NR_IRQS;
gic->gic_irqs = gic_irqs;
printk("~~~ %s() NU_IRS:%d\n", __func__, NR_IRQS);
gic_irqs -= hwirq_base; /* calculate # of irqs to allocate */
printk("~~~ %s() gic_irqs -= hwirq_base:%d\n", __func__, gic_irqs);
irq_base = irq_alloc_descs(irq_start, 16, gic_irqs, numa_node_id());
if (IS_ERR_VALUE(irq_base)) {
WARN(1, "Cannot allocate irq_descs @ IRQ%d, assuming pre-allocated\n",
irq_start);
irq_base = irq_start;
}
gic->domain = irq_domain_add_legacy(node, gic_irqs, irq_base,
hwirq_base, &gic_irq_domain_ops, gic);
if (WARN_ON(!gic->domain))
return;
gic_chip.flags |= gic_arch_extn.flags;
gic_dist_init(gic);
gic_cpu_init(gic);
gic_pm_init(gic);
}
struct gic_chip_data {
union gic_base dist_base;
union gic_base cpu_base;
#ifdef CONFIG_CPU_PM
u32 saved_spi_enable[DIV_ROUND_UP(1020, 32)];
u32 saved_spi_conf[DIV_ROUND_UP(1020, 16)];
u32 saved_spi_target[DIV_ROUND_UP(1020, 4)];
u32 __percpu *saved_ppi_enable;
u32 __percpu *saved_ppi_conf;
#endif
struct irq_domain *domain;
unsigned int gic_irqs;
#ifdef CONFIG_GIC_NON_BANKED
void __iomem *(*get_base)(union gic_base *);
#endif
};
struct gic_chip_data,记录gic controller的起始地址等;
gic_init_bases()中,初始化struct gic_chip_data,GIC dist_base和cpu_base;
读 GICD_TYPER reg,bit[0:4],IT lines number, Indicates the maximum number of interrupts that the GIC supports. If ITLinesNumber=N, the
maximum number of interrupts is 32(N+1).
但是并没有用到这个获取到的中断数量gic_irqs,这个是GIC控制器支持的中断数量; NR_IRQS 才是系统中断数量的准确值,NR_IRQS赋值给gic_irqs;
gic_irqs -= hwirq_base; / * calculate # of irqs to allocate */
hwirq_base 就是第一个PPI中断nr,hw int id 为16; hw int id:0~15,这16个hw int id是SGI 中断;
gic_irqs - 16个Software Generated Interrupt(SGI) 中断,SGI中断是不需要对应的struct irq_desc;
SGI中断有特殊的hanle:handle_IPI(irqnr, regs); / * arch/arm/kernel/smp.c */;
SGI 中断用于core 间通信;
SGI Hw int number值代表不同的ipi msg type,
enum ipi_msg_type {
IPI_TIMER = 2,
IPI_RESCHEDULE,
IPI_CALL_FUNC,
IPI_CALL_FUNC_SINGLE,
IPI_CPU_STOP,
IPI_CPU_BACKTRACE,
};
Interrupt IDs
2.2.1 Interrupt IDs
Interrupts from sources are identified using ID numbers. Each CPU interface can see up to 1020 interrupts. The
banking of SPIs and PPIs increases the total number of interrupts supported by the Distributor.
The GIC assigns interrupt ID numbers ID0-ID1019 as follows:
• Interrupt numbers ID32-ID1019 are used for SPIs.
• Interrupt numbers ID0-ID31 are used for interrupts that are private to a CPU interface. These interrupts are
banked in the Distributor.
A banked interrupt is one where the Distributor can have multiple interrupts with the same ID. A banked
interrupt is identified uniquely by its ID number and its associated CPU interface number. Of the banked
interrupt IDs:
— ID0-ID15 are used for SGIs
— ID16-ID31 are used for PPIs
ID0-ID15 are used for SGIs
/* arch/arm/common/gic.c */
asmlinkage 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 & ~0x1c00;
if (likely(irqnr > 15 && irqnr < 1021)) {
irqnr = irq_find_mapping(gic->domain, irqnr);
handle_IRQ(irqnr, regs);
continue;
}
if (irqnr < 16) {
writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI);
#ifdef CONFIG_SMP
handle_IPI(irqnr, regs);
#endif
continue;
}
break;
} while (1);
}
if (irqnr < 16) {
writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI);
handle_IPI(irqnr, regs);
}
如果是SIGs中断,call handle_IPI(irqnr, regs);
#define irq_alloc_descs(irq, from, cnt, node) \
__irq_alloc_descs(irq, from, cnt, node, THIS_MODULE)
/**
* irq_alloc_descs - allocate and initialize a range of irq descriptors
* @irq: Allocate for specific irq number if irq >= 0
* @from: Start the search from this irq number
* @cnt: Number of consecutive irqs to allocate.
* @node: Preferred node on which the irq descriptor should be allocated
* @owner: Owning module (can be NULL)
*
* Returns the first irq number or error code
*/
int __ref
__irq_alloc_descs(int irq, unsigned int from, unsigned int cnt, int node,
struct module *owner)
{
return alloc_descs(start, cnt, node, owner);
}
__irq_alloc_descs,看代码参数,返回值是:Returns the first irq number or error code; 返回start的值16;
static inline int alloc_descs(unsigned int start, unsigned int cnt, int node,
struct module *owner)
{
u32 i;
printk("~~~ %s() start:%d, cnt:%d\n", __func__, start, cnt);
for (i = 0; i < cnt; i++) {
struct irq_desc *desc = irq_to_desc(start + i);
desc->owner = owner;
}
return start;
}
alloc_descs(); 只是irqnr作为,全局的struct irq_desc irq_desc[NR_IRQS]数组的下标,对desc元素desc->owner = owner;
gic->domain = irq_domain_add_legacy(node, gic_irqs, irq_base,
hwirq_base, &gic_irq_domain_ops, gic);
/**
* irq_domain_add_legacy() - Allocate and register a legacy revmap irq_domain.
* @of_node: pointer to interrupt controller's device tree node.
* @size: total number of irqs in legacy mapping
* @first_irq: first number of irq block assigned to the domain
* @first_hwirq: first hwirq number to use for the translation. Should normally
* be '0', but a positive integer can be used if the effective
* hwirqs numbering does not begin at zero.
* @ops: map/unmap domain callbacks
* @host_data: Controller private data pointer
*
* Note: the map() callback will be called before this function returns
* for all legacy interrupts except 0 (which is always the invalid irq for
* a legacy controller).
*/
struct irq_domain *irq_domain_add_legacy(struct device_node *of_node,
unsigned int size,
unsigned int first_irq,
irq_hw_number_t first_hwirq,
const struct irq_domain_ops *ops,
void *host_data)
{
struct irq_domain *domain;
unsigned int i;
domain = irq_domain_alloc(of_node, IRQ_DOMAIN_MAP_LEGACY, ops, host_data);
if (!domain)
return NULL;
printk("~~~ %s() domain->revmandata first_irq:%d, first_hwirq:%u, size:%d\n", \
__func__, first_irq, first_hwirq, size);
domain->revmap_data.legacy.first_irq = first_irq;
domain->revmap_data.legacy.first_hwirq = first_hwirq;
domain->revmap_data.legacy.size = size;
mutex_lock(&irq_domain_mutex);
/* Verify that all the irqs are available */
for (i = 0; i < size; i++) {
int irq = first_irq + i;
struct irq_data *irq_data = irq_get_irq_data(irq);
if (WARN_ON(!irq_data || irq_data->domain)) {
mutex_unlock(&irq_domain_mutex);
of_node_put(domain->of_node);
kfree(domain);
return NULL;
}
}
printk("~~~ %s() irq_desc.irq_data.hwirq/domain set\n", __func__);
/* Claim all of the irqs before registering a legacy domain */
for (i = 0; i < size; i++) {
struct irq_data *irq_data = irq_get_irq_data(first_irq + i);
irq_data->hwirq = first_hwirq + i;
irq_data->domain = domain;
}
mutex_unlock(&irq_domain_mutex);
for (i = 0; i < size; i++) {
int irq = first_irq + i;
int hwirq = first_hwirq + i;
/* IRQ0 gets ignored */
if (!irq)
continue;
/* Legacy flags are left to default at this point,
* one can then use irq_create_mapping() to
* explicitly change them
*/
ops->map(domain, irq, hwirq);
/* Clear norequest flags */
irq_clear_status_flags(irq, IRQ_NOREQUEST);
}
irq_domain_add(domain);
return domain;
}
irq_domain_add_legacy()参数:
struct irq_domain *irq_domain_add_legacy(struct device_node *of_node, /* */
unsigned int size, /* 中断的数量 */
unsigned int first_irq, /* 第一个irq number,这个可以理解为struct irq_desc irq_desc[NR_IRQS],的下标值 */
irq_hw_number_t first_hwirq, /* 第一个 Hw init number,这个是硬件中断控制器相关的 */
const struct irq_domain_ops *ops, /* irq_domain_ops */
void *host_data)
实现中,动态申请了一个struct irq_domain *domain;并初始化:
domain->revmap_data.legacy.first_irq = first_irq;
domain->revmap_data.legacy.first_hwirq = first_hwirq;
domain->revmap_data.legacy.size = size;
Verify that all the irqs are available.
/* Claim all of the irqs before registering a legacy domain */
for (i = 0; i < size; i++) {
struct irq_data *irq_data = irq_get_irq_data(first_irq + i);
irq_data->hwirq = first_hwirq + i;
irq_data->domain = domain;
}
给struct irq_desc irq_desc[NR_IRQS];的irq_desc->irq_data.hwirq;irq_desc->irq_data.domain,赋值;
legacy,irq 和hwirq是线性对应关系;
for (i = 0; i < size; i++) {
int irq = first_irq + i;
int hwirq = first_hwirq + i;
/* IRQ0 gets ignored */
if (!irq)
continue;
/* Legacy flags are left to default at this point,
* one can then use irq_create_mapping() to
* explicitly change them
*/
ops->map(domain, irq, hwirq);
/* Clear norequest flags */
irq_clear_status_flags(irq, IRQ_NOREQUEST);
}
call, ops->map(domain, irq, hwirq); 还有给struct irq_desc irq_desc[NR_IRQS],设置对应的
desc->irq_data.chip = chip;
desc->handle_irq = handle;
irq_domain_add(domain); domain添加到全局irq_domain_list,list_add(&domain->link, &irq_domain_list);
重点是:
/* arch/arm/mach-s5p6818/gic.c */
static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
irq_hw_number_t hw)
{
/*printk("~~~ %s() irq:%d, hw:%d, call irq_set_chip_and_handler()\n", \
__func__, irq, hw); */
if (hw < 32) {
irq_set_percpu_devid(irq);
irq_set_chip_and_handler(irq, &gic_chip,
handle_percpu_devid_irq);
set_irq_flags(irq, IRQF_VALID | IRQF_NOAUTOEN);
} else {
irq_set_chip_and_handler(irq, &gic_chip,
handle_fasteoi_irq);
set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
}
irq_set_chip_data(irq, d->host_data);
return 0;
}
gic_irq_domain_map();
判断hw irq nr小于32,也就是PPIs 中断,设置对应的全局struct irq_desc irq_desc[irq]:
irq_set_chip_and_handler(irq, &gic_chip,
handle_percpu_devid_irq);
|
desc->irq_data.chip = gic_chip;
desc->handle_irq = handle_percpu_devid_irq;
判断hw irq nr 大于等于32,也就是SPI中断,设置对应的struct irq_desc irq_desc[irq]:
irq_set_chip_and_handler(irq, &gic_chip,
handle_fasteoi_irq);
|
desc->irq_data.chip = gic_chip;
desc->handle_irq = handle_fasteoi_irq;
到目前,有:
- alloc and add了一个irq_domain(添加到全局irq_domain_list), 这个irq_domain的类型是 domain->revmap_type:IRQ_DOMAIN_MAP_LEGACY;
struct gic_chip_data gic 和 domain,相互连接;
host_data:struct gic_chip_data *gic;
domain = irq_domain_alloc(of_node, IRQ_DOMAIN_MAP_LEGACY, ops, host_data);
domain->host_data = host_data;
and:
gic->domain = irq_domain_add_legacy(node, gic_irqs, irq_base,
hwirq_base, &gic_irq_domain_ops, gic);
这种类型的irq_domain hw irq number 和 irq number是线性关系:irq number = hw irq number - first_hwirq + first_irq;
相关first_hwirq,first_irq 线性关系参数记录在:
domain->revmap_data.legacy.first_irq = first_irq;
domain->revmap_data.legacy.first_hwirq = first_hwirq;
domain->revmap_data.legacy.size = size;
- 设置了全局struct irq_desc irq_desc[irq];的
irq_data->hwirq = first_hwirq + i;
irq_data->domain = domain;
desc->irq_data.chip = gic_chip;
desc->handle_irq = handle_percpu_devid_irq; or desc->handle_irq = handle_fasteoi_irq;
- 设置struct irq_desc irq_desc[irq];的desc->irq_data.chip = gic_chip;
/* arch/arm/mach-s5p6818/gic.c */
static struct irq_chip gic_chip = {
.name = "GIC",
.irq_mask = gic_mask_irq,
.irq_unmask = gic_unmask_irq,
.irq_eoi = gic_eoi_irq,
.irq_set_type = gic_set_type,
.irq_retrigger = gic_retrigger,
#ifdef CONFIG_SMP
.irq_set_affinity = gic_set_affinity,
#endif
.irq_set_wake = gic_set_wake,
};
struct irq_chip,hardware interrupt chip descriptor;是中断控制器的相关操作实现,当我们需要设置一个irq硬件相关配置,struct irq_desc;就会call到相关的irq chip ops;
- 有了1,2,当中断触发之后,通过获取hw int number,就可以得到irq number,call irq = irq_domain_legacy_revmap(gic->domain, hwirq);
从而确认是哪个struct irq_desc irq_desc[irq number]; call desc->handle_irq();
后有:
gic_dist_init(gic);
gic_cpu_init(gic);
gic_pm_init(gic);
__init gic_dist_init(struct gic_chip_data *gic)
static void __init gic_dist_init(struct gic_chip_data *gic)
{
unsigned int i;
u32 cpumask;
unsigned int gic_irqs = gic->gic_irqs;
void __iomem *base = gic_data_dist_base(gic);
#if defined (CONFIG_SMP) && defined (CONFIG_ARCH_S5P6818_REV)
cpumask = 0xff; /* transfer interrupt to all cores */
#else
u32 cpu = cpu_logical_map(smp_processor_id());
cpumask = 1 << cpu;
#endif
cpumask |= cpumask << 8;
cpumask |= cpumask << 16;
writel_relaxed(0, base + GIC_DIST_CTRL);
/*
* Set all global interrupts to be level triggered, active low.
*/
for (i = 32; i < gic_irqs; i += 16)
writel_relaxed(0, base + GIC_DIST_CONFIG + i * 4 / 16);
/*
* Set all global interrupts to this CPU only.
*/
for (i = 32; i < gic_irqs; i += 4)
writel_relaxed(cpumask, base + GIC_DIST_TARGET + i * 4 / 4);
/*
* Set priority on all global interrupts.
*/
for (i = 32; i < gic_irqs; i += 4)
writel_relaxed(0xa0a0a0a0, base + GIC_DIST_PRI + i * 4 / 4);
/*
* Disable all interrupts. Leave the PPI and SGIs alone
* as these enables are banked registers.
*/
for (i = 32; i < gic_irqs; i += 32)
writel_relaxed(0xffffffff, base + GIC_DIST_ENABLE_CLEAR + i * 4 / 32);
writel_relaxed(1, base + GIC_DIST_CTRL);
}
writel_relaxed(0/1, base + GIC_DIST_CTRL);
/*
Global enable for forwarding pending Group 0 interrupts from the Distributor to the CPU interfaces:
0 = Group 0 interrupts not forwarded.
1 = Group 0 interrupts forwarded, subject to the priority rules.
*/
gic_dist_init()
注释写的比较清楚了,具体可看数据手册看对应register的功能:
Set Group 0 interrupts not forwarded.
Set all global interrupts to be level triggered, active low.
Set all global interrupts to All SMP CPU. // cpumask = 0xff; transfer interrupt to all cores
Set all SPI interrupt same priority 0xa0.
Disable all interrupts, leave the PPI and SGIs alone, // irq_request(), 过程中,会call static struct irq_chip gic_chip,.irq_unmask = gic_unmask_irq, set GIC_DIST_ENABLE_SET reg, enable interrupt
Set Group 0 interrupts forwarded.
void __cpuinit gic_cpu_init(struct gic_chip_data *gic)
static void __cpuinit gic_cpu_init(struct gic_chip_data *gic)
{
void __iomem *dist_base = gic_data_dist_base(gic);
void __iomem *base = gic_data_cpu_base(gic);
int i;
/*
* Deal with the banked PPI and SGI interrupts - disable all
* PPI interrupts, ensure all SGI interrupts are enabled.
*/
writel_relaxed(0xffff0000, dist_base + GIC_DIST_ENABLE_CLEAR);
writel_relaxed(0x0000ffff, dist_base + GIC_DIST_ENABLE_SET);
/*
* Set priority on PPI and SGI interrupts
*/
for (i = 0; i < 32; i += 4)
writel_relaxed(0xa0a0a0a0, dist_base + GIC_DIST_PRI + i * 4 / 4);
writel_relaxed(0xf0, base + GIC_CPU_PRIMASK);
writel_relaxed(1, base + GIC_CPU_CTRL);
}
gic_cpu_init()
Set GIC_DIST_ENABLE_CLEAR, GIC_DIST_ENABLE_SET; 注意是DIST block;
writel_relaxed(0xffff0000, dist_base + GIC_DIST_ENABLE_CLEAR); // disable all PPI interrupts
writel_relaxed(0x0000ffff, dist_base + GIC_DIST_ENABLE_SET); // ensure all SGI interrupts are enabled
Set priority on PPI and SGI interrupts
writel_relaxed(0xf0, base + GIC_CPU_PRIMASK);
GIC_CPU_PRIMASK
bit [31:8] Reserved
bit [7:0] w/r
The priority masks level for the CPU interface. If the
priority of an interrupt is higher than the value indicated
by this field, the interface signals the interrupt to the
processor. If the GIC supports fewer than 256 priority
levels then some bits are RAZ/WI, as follows:
128 supported levels Bit [0] = 0.
64 supported levels Bit [1:0] = 0b00.
32 supported levels Bit [2:0] = 0b000.
16 supported levels Bit [3:0] = 0b0000. (this one,16 priority levels)
writel_relaxed(1, base + GIC_CPU_CTRL);
GIC_CPU_CTRL
bit [0] w/r
Enable for the signaling of Group 0 interrupts by the CPU
interface to the connected processor.
0 = Disable signaling of Group 0 interrupts.
1 = Enable signaling of Group 0 interrupts.
void __init gic_pm_init(struct gic_chip_data *gic)
static void __init gic_pm_init(struct gic_chip_data *gic)
{
gic->saved_ppi_enable = __alloc_percpu(DIV_ROUND_UP(32, 32) * 4,
sizeof(u32));
BUG_ON(!gic->saved_ppi_enable);
gic->saved_ppi_conf = __alloc_percpu(DIV_ROUND_UP(32, 16) * 4,
sizeof(u32));
BUG_ON(!gic->saved_ppi_conf);
if (gic == &gic_data[0])
cpu_pm_register_notifier(&gic_notifier_block);
}
register_notifier 内核通知链 cpu_pm_notifier_chain,
static RAW_NOTIFIER_HEAD(cpu_pm_notifier_chain); /* kernel/cpu_pm.c */
休眠前save irq GIC_DIST GIC_CPU中相关interrupt 配置状态;
休眠唤醒restore;
PS:
About GIC partitioning:
1. Distributo
The Distributor block performs interrupt prioritization and distribution to the CPU interface blocks that connect to the processors in the system.The Distributor block registers are identified by the GICD_ prefix.
2. CPU interfaces
Each CPU interface block performs priority masking and preemption handling for a connected processor in the system.
CPU interface block registers are identified by the GICC_ prefix.
When describing a GIC that includes the GIC Virtualization Extensions, a CPU interface is sometimes called a physical CPU interface, to avoid possible confusion with a virtual CPU interface.
3. Virtual CPU interfaces
GIC register:
/* arch/arm/include/asm/hardware/gic.h */
#define GIC_CPU_CTRL 0x00
#define GIC_CPU_PRIMASK 0x04
#define GIC_CPU_BINPOINT 0x08
#define GIC_CPU_INTACK 0x0c
#define GIC_CPU_EOI 0x10
#define GIC_CPU_RUNNINGPRI 0x14
#define GIC_CPU_HIGHPRI 0x18
#define GIC_DIST_CTRL 0x000
#define GIC_DIST_CTR 0x004
#define GIC_DIST_ENABLE_SET 0x100
#define GIC_DIST_ENABLE_CLEAR 0x180
#define GIC_DIST_PENDING_SET 0x200
#define GIC_DIST_PENDING_CLEAR 0x280
#define GIC_DIST_ACTIVE_BIT 0x300
#define GIC_DIST_PRI 0x400
#define GIC_DIST_TARGET 0x800
#define GIC_DIST_CONFIG 0xc00
#define GIC_DIST_SOFTINT 0xf00
debug log:
[ 0.000000] NR_IRQS:272
[ 0.000000] ~~~ early_irq_init() irq_desc count:272, call desc_set_defaults()
[ 0.000000] ~~~ init_IRQ() call machine_desc->init_irq()
[ 0.000000] ~~~ nxp_cpu_irq_init()
[ 0.000000] GIC @f0009000: start 0 (gic 31)
[ 0.000000] ~~~ __gic_init() call gic_init()
[ 0.000000] ~~~ gic_init() call gic_init_bases()
[ 0.000000] ~~~ gic_init_bases() gic_nr:0, irq_start:16
[ 0.000000] ~~~ gic_init_bases() hwirq_base:16
[ 0.000000] ~~~ gic_init_bases() NU_IRS:272
[ 0.000000] ~~~ gic_init_bases() gic_irqs -= hwirq_base:256
[ 0.000000] ~~~ alloc_descs() start:16, cnt:256
[ 0.000000] ~~~ irq_domain_add_legacy() domain->revmandata first_irq:16, first_hwirq:16, size:256
[ 0.000000] ~~~ irq_domain_add_legacy() irq_desc.irq_data.hwirq/domain set
[ 0.000000] ~~~ irq_domain_add() irq: Allocated domain of type 0 @0xe5800a80