1. 程式人生 > >arm GIC介紹之三

arm GIC介紹之三

GIC_V3 初始化設定
在GIC 介紹之一和二中,可以看到GIC的邏輯結構和硬體框架。
那麼在初始化時候,需要將各個元件配置起來,Interrupt Controller中的Distributor,Redistributor,CPU Interface需要按照不同順序配置。既然Distributor是所有CPU都可以對應的,那麼這個只要在第一個CPU啟動時配置就可以了,對於Redistributor和CPU Interface是PER-CPU對應的,所以,在SECONDARY CPU啟動時候需要配置各自的元件。這也是下圖中所表示的:

這裡寫圖片描述
我們從Distributor初始化開始。
GIC的初始化主要是通過配置其對應的暫存器來實現。我們介紹過
Distributor上的主要暫存器,實際上,在實現中,也就是通過配置這些Registers來實現的。
我們參考的程式碼基於X20,並且首先在Ex3的ATF安全世界裡面先啟動,這個並不妨礙對流程的認識:
這裡寫圖片描述


一個ARM裝置在啟動時候,首先上電,之後會從片內的BOOT ROM獲取到啟動初始程式碼,在這裡做基本的晶片初始化,而後去
載入PRE-LOADER部分,這部分在X20上面也是做如MEMORY,UART等的設定。如果說沒有載入ATF(ARM TRUSTZONE FIRMWARE)的話,就會繼續LK和KERNEL的流程,在X20上面因為要做TRUSTZONE的需要,所以在這裡載入了ATF,同時,GIC的基本設定和最初的INIT也是在這裡。
這裡的程式碼結構主要:
這裡寫圖片描述
從Gic_setup入口,在這裡,首先獲取到 gicd的基地址,之後才可以在這個基礎上向不同暫存器寫入配置資料。
如上圖,之後要最的是通過控制CTLR暫存器先把Distributor功能關閉,並配置所有的SPI傳送目標域為NS-G1,在這裡就是KERNEL。並重新開啟Distributor。
之後會去進入gic_cpuif_init,對相應的Redistributor和CPU Interface做設定。
gic_cpuif_init

同樣道理,先獲取到Redistributor的基地址,之後用於配置。首先要設定WAKEUP,這樣保證CPU Interface和對應的CPU在之後能夠被喚醒。
在Distributor配置了SPI的目標域,但是沒有配置SGI和PPI,對於這32個IRQ,這裡在Redistributor進行配置,也是送到NS-G1,也就是KERNEL裡面處理。
ICC_SRE的開啟,是要啟用CPU Interface上對應的暫存器,啟用後,向對應的暫存器裡面寫入資訊,才會有相應的動作,否則是不起作用的。
enable_sgi_fiq,這裡補充說明下,本身是可選的,X20的平臺上面把
SGI,將13(FIQ_SMP_CALL_SGI)號SGI 作為優先順序最高的一個IRQ配置,在KERNEL 有HANDLE_FIQ_AS_NMI , 目的也是想DUMP 出關鍵資訊,目前程式碼只有部分框架。如果可以,後面看看其它廠家是不是也是這樣考慮的。
這裡在Ex3裡面就做了基本的配置了,那麼之後其它元件的設定好,進入KERNEL之後,還有其它部分要補充,這樣,中斷才能通暢到達KERNEL裡面並處理。
gic_init in kernel

上圖裡面體現的是第一個CPU起來之後,進入KERNEL裡面,開始逐步進行INIT,呼叫init_IRQ,這是現在KERNEL的框架結構,一直到irqchip_init,of_irq_init,在of_irq_init裡面不同的架構廠商或者說晶片的定製者,就可以增加特定的GIC程式碼了,比如說X86__IRQ_INIT,當然這只是設定個例子。在X20上,使用的是GIC_V3對應的GIC裝置,這裡增加的是gic_of_init來完成特定的初始化。
那麼在這裡面做了什麼呢?
其實也比較簡單:
1. irq_domain and initialize it. 我們從GIC側獲得IRQ是硬體型別的中斷號,而我們通常意義上在KERNEL裡面處理的是軟體層面的IRQ,比如用desc_irq表示的中,這樣兩者之間就需要對映,座椅就用到了irq_domain,原理如此,我們在後面會展開來說如何實現的。
2. 給系統的中斷入口設定特定體系的函式,一般是handle_irq,這裡是gic_handle_irq。
3. 設定IPI訊息的控制代碼,當傳送IPI訊息時候就可以呼叫到gic_raise_softirq,這個在前面介紹GIC暫存器時候,在GICC_SGI部分說明到。
另外要給出gic_cpu_notifier作為CPU ON時候的通知鏈條上的一環,在這裡面會增加SECONDARY CPU的設定。
4. gic_dist_init,這隻開啟G1的ARE功能,並將所有的SPI傳送目標定位全體CPU,這由使用者自己來定義,可以將特定的IRQ直接繫結到特定的CPU上處理。
5. gic_cpu_init對CPU對應的INTERFACE進行設定。我們可以看下里面的詳細內容:
gic_cpu_init

  1. gic_enable_redist , 設定WAKE PROCESSORSLEEP bit.
    Read WAKEUP_ChildrenAsleep ,確保標識已經變化。這裡實際上CPU 已經起來了,讀取這個標識為了明確該位置,不會對後續操作產生困擾(理解如此)。
  2. gic_enable_sre 這裡只是對ICC 也就是CPU interface 的Enable system registers , 並沒有對EL2, EL3操作, 而不像CORE_0那樣。在V3版的ATF程式碼裡面,有提供操作的介面,並且
    是PER_CPU的,是不是後面也提供了其它CPU進入EL3操作對應的EL2, EL3對應的GIC暫存器,應該是這樣。
  3. DEFAULT_PMR_VALUE 0xf0, GICD 裡面設定為0XA0, 所以X20裡面中斷是高於這個門限優先順序。
  4. ICC_IGRPEN1
    Controls whether Group 1 interrupts are enabled for the current Security state 。這個開啟後,就可以接收到G1-NS的中斷了。
    至此,當第一個CPU起來之後,對應的GIC設定完成,可以接受驅動的終端配置,並且接受終端處理了。
    那麼,當系統中有多個CPU,比如X20上有10個CPU(CORE),其它9個如何設定呢。
    gic init for Secondary cpu

上圖中,左側部分藍色框,表示CPU_0在KERNEL對應的流程,這裡簡化。在上面介紹過:gic_cpu_notifier.notifier_call
= gic_secondary_init,也就是主要註冊了gic_secondary_init。
在右側綠色部分表示CPU_0~9在KERNEL裡面的流程,這裡只列出和GIC關聯部分。入口是Secondary_start_kernel,那麼之後會Notify_cpu_starting,在這裡呼叫到Cpu_notify。其中Notify_cpu_chain就用到了之前註冊的gic_secondary_init,圖中下冊也給出了程式碼的入口,這樣CPU_0~9也不需要再初始化DISTRIBUTOR,只需要執行gic_cpu_init,設定自己對應的部分就可以了。
OK,到這裡,硬體上的配置流程介紹完,下面整理並介紹如何把
HARDWARE IRQ和軟體處理上的IRQ desc_irq聯絡起來……