1. 程式人生 > >Linux GIC 的初始化

Linux GIC 的初始化

if (desc->interrupt_parent != parent)
continue;

將根中斷控制器從intc_desc_list中刪除
list_del(&desc->list);

初始化root中斷控制器
ret = desc->irq_init_cb(desc->dev, desc->interrupt_parent);

將root中斷控制器加入intc_parent_list中
list_add_tail(&desc->list, &intc_parent_list);
}

取出root中斷控制器,設定parent並free掉desc
desc = list_first_entry_or_null(&intc_parent_list, typeof(*desc), list);
list_del(&desc->list);
                parent = desc->dev;
                kfree(desc);
}
}

上面的code中分析提到真正的init函式是desc->irq_init_cb(), 而desc->irq_init_cb()對應著
IRQCHIP_DECLARE(gic_v3, "arm,gic-v3", gic_of_init)中的gic_of_init(),這裡為了簡化分析,
只分析drivers/irqchip/irq-gic.c, 對應的arm core如下: 
IRQCHIP_DECLARE(cortex_a15_gic, "arm,cortex-a15-gic", gic_of_init);
IRQCHIP_DECLARE(cortex_a9_gic, "arm,cortex-a9-gic", gic_of_init);
IRQCHIP_DECLARE(cortex_a7_gic, "arm,cortex-a7-gic", gic_of_init);

我們先了解gic的一些基本概念
Distributor: performs interrupt prioritization and distribution to the CPU interface
CPU interfaces: performs priority masking and preemption handling for a connected processor in the system.
Interrupt states: Inactive, Pending, Active, Active and pending
Interrupt types: 
Peripheral interrupt: an interrupt asserted by a signal to the GIC. including SPI&PPI
Software-generated interrupt (SGI): This is an interrupt generated by software writing to a GICD_SGIR register in the GIC.
Interrupt IDs:  ID0-ID31 are used for interrupts that are private to a CPU interface. These interrupts are banked in the Distributor.
ID0-ID15 are used for SGIs
ID16-ID31 are used for PPIs
ID32-ID1019 are used for SPIs.

gic_of_init()drivers/irqchip/irq-gic.c
{
struct gic_chip_data *gic;

解析dts中gic的引數
gic_of_setup(gic, node);
{
下文中的device node定義在arch/arm/boot/dts/imx7s.dtsi中
intc:
[email protected]
 
{
compatible = "arm,cortex-a7-gic";
interrupts = <GIC_PPI 9 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>;
#interrupt-cells = <3>;
interrupt-controller;
reg = <0x31001000 0x1000>,
  <0x31002000 0x2000>,
  <0x31004000 0x2000>,
  <0x31006000 0x2000>;
};

gic->raw_dist_base = of_iomap(node, 0);
gic->raw_cpu_base = of_iomap(node, 1);
}

__gic_init_bases(gic, -1, &node->fwnode);
{
一個gic中斷可以map到所有的cpu
for (i = 0; i < NR_GIC_CPU_IF; i++)
gic_cpu_map[i] = 0xff;

如果使能CONFIG_SMP, 設定 __smp_cross_call為gic_raise_softirq
set_smp_cross_call(gic_raise_softirq);

設定gic的中斷處理函式
set_handle_irq(gic_handle_irq)

初始化gic_chip
gic_init_chip(gic, NULL, name, true);
{
gic->chip = gic_chip;
{
gic_chip的結構體定義在drivers/irqchip/irq-gic.c中
static struct irq_chip gic_chip = 
{
.irq_mask               = gic_mask_irq,
.irq_unmask             = gic_unmask_irq,
.irq_eoi                = gic_eoi_irq,
.irq_set_type           = gic_set_type,
.irq_get_irqchip_state  = gic_irq_get_irqchip_state,
.irq_set_irqchip_state  = gic_irq_set_irqchip_state,
.flags                  = IRQCHIP_SET_TYPE_MASKED |
  IRQCHIP_SKIP_SET_WAKE |
  IRQCHIP_MASK_ON_SUSPEND,
};
}
gic->chip.name = name;
gic->chip.parent_device = dev;

if (use_eoimode1)
{
gic->chip.irq_mask = gic_eoimode1_mask_irq;
gic->chip.irq_eoi = gic_eoimode1_eoi_irq;
gic->chip.irq_set_vcpu_affinity = gic_irq_set_vcpu_affinity;
}
}

gic_init_bases(gic, irq_start, handle);
{
獲取當前gic所支援的硬體中斷號, 最大1020箇中斷
gic_irqs = readl_relaxed(gic_data_dist_base(gic) + GIC_DIST_CTR) & 0x1f;
gic_irqs = (gic_irqs + 1) * 32;
if (gic_irqs > 1020)
gic_irqs = 1020;
gic->gic_irqs = gic_irqs;

建立線性domaindrivers/irqchip/irq-gic.c
gic->domain = irq_domain_create_linear(handle, gic_irqs,
                                                   &gic_irq_domain_hierarchy_ops,
                                                   gic);
{
struct irq_domain *domain;

domain = kzalloc_node(sizeof(*domain) + (sizeof(unsigned int) * size), GFP_KERNEL, of_node_to_nid(of_node));

INIT_RADIX_TREE(&domain->revmap_tree, GFP_KERNEL);
domain->ops = ops;
domain->host_data = host_data;
domain->of_node = of_node_get(of_node);
domain->hwirq_max = hwirq_max;
domain->revmap_size = size;
domain->revmap_direct_max_irq = direct_max;
irq_domain_check_hierarchy(domain);

list_add(&domain->link, &irq_domain_list);

return domain;
}
初始化gic distributor
gic_dist_init(gic);
{
1.把gic distributor 關掉,writel_relaxed(GICD_DISABLE, base + GIC_DIST_CTRL);

2.為gic中斷分配cpu, GIC_DIST_TARGET暫存器為gic中斷提供8-bits的目標CPU,一個GIC_DIST_TARGET暫存器
可服務於4個gic中斷。 為什麼一個CPU target只需要8bit呢?因為cortex_a9_gic
只支援8個CPU。 若CPU targets=0xff, 表示任何一個cpu都可處理該中斷, 
如果CPUtargets=0x01, 則表示只有CPU0可處理該中斷。GIC_DIST_TARGET結構如下:
b31-b24b23-b16  b15-b8   b7-b0
  CPU targets CPU targets  CPU targets CPU targets
offset3offset2 offset1   offset0

分析一下code是怎麼工作的:
cpumask = gic_get_cpumask(gic);
{

i=0-31表示中斷號前32箇中斷(PPI和SGI), 則mask為前這些中斷的CPU mask情況
for (i = mask = 0; i < 32; i += 4) 
{
mask = readl_relaxed(base + GIC_DIST_TARGET + i);
mask |= mask >> 16;
mask |= mask >> 8;
if (mask)
break;
}
}

下面的code把所有的SPI中斷CPU設定為能處理PPI和SGI的cpu
cpumask |= cpumask << 8;
cpumask |= cpumask << 16;
for (i = 32; i < gic_irqs; i += 4)
                writel_relaxed(cpumask, base + GIC_DIST_TARGET + i * 4 / 4);

 3. gic_dist_config(base, gic_irqs, NULL);
{
1. 設定中斷的觸發型別, active low.
{
for (i = 32; i < gic_irqs; i += 16)
writel_relaxed(GICD_INT_ACTLOW_LVLTRIG,base + GIC_DIST_CONFIG + i / 4);
}

2. 設定default中斷的優先順序
{
for (i = 32; i < gic_irqs; i += 4)
writel_relaxed(GICD_INT_DEF_PRI_X4, base + GIC_DIST_PRI + i);
}

3. 除去PPI和SGI, disable所有的中斷
{
for (i = 32; i < gic_irqs; i += 32)
writel_relaxed(GICD_INT_EN_CLR_X32,base + GIC_DIST_ENABLE_CLEAR + i / 8);
}
}

4. enable中斷distributor, writel_relaxed(GICD_ENABLE, base + GIC_DIST_CTRL);
}
 
初始化gic的cpu interface
ret = gic_cpu_init(gic);
{
1. 獲取當前cpu的id, cpu = smp_processor_id();

2. 更新cpu interface的gic_cpu_map的值, 清理非當前CPU的gic_cpu_map
{
cpu_mask = gic_get_cpumask(gic);
gic_cpu_map[cpu] = cpu_mask;

for (i = 0; i < NR_GIC_CPU_IF; i++)
{
if (i != cpu)
gic_cpu_map[i] &= ~cpu_mask;
}
}


3.  配置cpu interface, gic_cpu_config(dist_base, NULL);
{
1. enable PPI和SGI中斷
{
writel_relaxed(GICD_INT_EN_CLR_PPI, base + GIC_DIST_ENABLE_CLEAR);
writel_relaxed(GICD_INT_EN_SET_SGI, base + GIC_DIST_ENABLE_SET);
}

2. 設定PPI and SGI的中斷優先順序
{
for (i = 0; i < 32; i += 4)
writel_relaxed(GICD_INT_DEF_PRI_X4,base + GIC_DIST_PRI + i * 4 / 4);
}

3. 設定優先順序的門限
{
writel_relaxed(GICC_INT_PRI_THRESHOLD, base + GIC_CPU_PRIMASK);
}
}
4. enable cpu intrface, gic_cpu_if_up
{
void __iomem *cpu_base = gic_data_cpu_base(&gic_data[0]);

void __iomem *cpu_base = gic_data_cpu_base(&gic_data[0]);
u32 bypass = 0;

/*清除bypass bit, 設定GICC_ENABLE */
bypass = readl(cpu_base + GIC_CPU_CTRL);
bypass &= GICC_DIS_BYPASS_MASK;


writel_relaxed(bypass | GICC_ENABLE, cpu_base + GIC_CPU_CTRL);
}
}
}
}

相關推薦

Linux啟動初始配置文件淺析

文本模式 設定 input 其它 ubunt 文檔 登錄 終端 bash 轉自:http://blog.51cto.com/19055/1144600 1)/etc/profile 登錄時,會執行。 全局(公有)配置,不管是哪個用戶,登錄時都會讀取該文件。 (2)/

systemd---Linux初始系統

控制 -abort ola ted 主機 end strong timeout idl 一、概述 systemd是一套Linux系統的基本構建塊。它提供了一個系統和服務管理器,它作為PID 1運行,並啟動系統的其余部分。systemd提供了積極的並行化能力,使用Socket

青蛙學Linux—系統初始init及執行級

Linux系統的啟動首先從BIOS開始,接下來Linux的載入程式將核心映像載入到記憶體,進行核心初始化。在核心初始化的最後一步,就是啟動PID為1的init程序,這個程序是系統的第一個程序,負責啟動那些開機時需要啟動的服務。 大多數的Linux發行版的init系統都是和System V相容的,所以也被稱為

Linux記憶體初始之夥伴系統(三)

這裡主要分析zone/zonelist的初始化,以及把系統空閒記憶體釋放到夥伴系統 1.zone的初始化 呼叫關係:start_kernel->setup_arch->paging_init->bootmem_init->zone_sizes_init->fre

Linux記憶體初始之sparse記憶體模型(二)

1.Linux記憶體模型 前面已經分析把實體記憶體新增到memblock以及給實體記憶體建立頁表對映,這裡我們分析sparse記憶體模型 在linux核心中支援3中記憶體模型,分別是flat memory model,Discontiguous memory model和sparse mem

Linux 系統初始指令碼;

1.背景日常伺服器申請完畢後需要各種系統指標初始化,實施思路(編寫系統初始化指令碼,ansible 批量拷貝到目標機器執行); 初始化指令碼如下; #!/bin/bash function install_pkg(){ yum -y install l

Linux核心初始流程筆記

好文章,轉載一下! 作者:[email protected] 部落格:blog.focus-linux.net   linuxfocus.blog.chinaunix.ne

Linux例項初始系統盤後重新掛載資料盤

在Linux例項中,重新初始化系統盤不會改變資料盤裡的內容,但是資料盤的掛載資訊會丟失,所以,在Linux重啟後,按以下步驟建立新的掛載點資訊並掛載資料盤分割槽。 1,檢視資料盤掛載資訊:執行命令 mount。返回結果中沒有/dev/vdb1的資訊。 sysfs on /sys ty

linux核心初始步驟(十)-----時間管理子系統初始

參考博文:https://blog.csdn.net/DroidPhone/article/details/8051405 時間管理子系統: /* 核心用jiffies變數記錄系統啟動以來經過的時鐘滴答數*/ Jiffies.c (kernel\time):core_initcall(ini

linux啟動初始步驟(九)----電源管理相關函式

參考http://www.wowotech.net/sort/linux_kenrel博文的內容 Linux電源管理是一個比較龐大的子系統,涉及到供電(power supply)、充電(Charger)、時鐘(Clock)、頻率(Frequency)、電壓(Voltage)、睡眠/喚醒(Su

Linux核心初始步驟(八)---GPIO相關的初始工作

參考博文:http://blog.chinaunix.net/uid-27717694-id-3624294.html 分析GPIO的初始化過程,GPIO是與硬體體系密切相關的,Linux提供一個模型來讓驅動統一處理GPIO, 即各個板卡都有實現自己的gpio_chip控制模組:reques

linux核心初始步驟(六)

do_initcalls()將按順序從__initcall_start開始,到__initcall_end結束的section中以函式指標的形式取出這些編譯到核心驅動模組中的初始化函式起始地址,來完成相應的初始化。 /* * Early initcalls run before ini

Linux核心初始步驟(五)

/* * Ok, the machine is now initialized. None of the devices * have been touched yet, but the CPU subsystem is up and * running, and memory an

Linux核心初始步驟(七)

到init_post函式為止,核心的初始化已經基本結束,接著會產生使用者程序 /* This is a non __init function. Force it to be noinline otherwise gcc * makes it inline to init() and

Linux核心初始步驟(四)

kernel_init函式將完成裝置驅動的初始化,並呼叫init_post函式啟動使用者空間的init程序 static int __init kernel_init(void * unused) { /* * Wait until kthreadd is all set-up.

Linux核心初始步驟(三)

在start_kernl函式的最後呼叫了reset_init函式進行後續的初始化 static noinline void __init_refok rest_init(void) { int pid; rcu_scheduler_starting();//核心RCU鎖機制排程啟

Linux核心初始步驟(二)

參考了http://www.360doc.com/content/16/0626/14/19351147_570866376.shtml和 https://blog.csdn.net/qing_ping/article/details/17351541的內容 setup_arch()是sta

Linux核心初始步驟(一)

本文參考了https://blog.csdn.net/qing_ping/article/details/17351017博主的內容。 核心的初始化過程由start_kernel函式(\init\main.c)開始,至第一個使用者程序init結束,呼叫了一系列的初始化函式對所有的核心元件進行

一鍵linux系統初始指令碼

一、前言一般我們在安裝新的系統時,系統的一些預設配置對我們來說是不行的,所以我們要自定義初始化系統。 二、需求1)設定時區並把同步時間加入計劃任務2)禁用selinux3)清空原防火牆預設策略只保留ssh4)歷史命令顯示操作時間級使用者5)建立ALL許可權使用者並禁止root遠端登入6)禁止定時任務傳送郵件

一鍵linux系統初始腳本

auto 新的 isa 必須 buck etc shang gre 謝謝 一、前言一般我們在安裝新的系統時,系統的一些默認配置對我們來說是不行的,所以我們要自定義初始化系統。 二、需求1)設置時區並把同步時間加入計劃任務2)禁用selinux3)清空原防火墻默認策略只保留s