久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

Device Tree(三):代碼分析

 老匹夫 2014-09-09

Device Tree總共有三篇,,分別是:

1、為何要引入Device Tree,這個機(jī)制是用來解決什么問題的?(請參考引入Device Tree的原因

2、Device Tree的基礎(chǔ)概念(請參考DT基礎(chǔ)概念

3、ARM linux中和Device Tree相關(guān)的代碼分析(這是本文的主題)

本文主要內(nèi)容是:以Device Tree相關(guān)的數(shù)據(jù)流分析為索引,對ARM linux kernel的代碼進(jìn)行解析,。主要的數(shù)據(jù)流包括:

1、初始化流程,。也就是掃描dtb并將其轉(zhuǎn)換成Device Tree Structure,。

2、傳遞運(yùn)行時參數(shù)傳遞以及platform的識別流程分析

3,、如何將Device Tree Structure并入linux kernel的設(shè)備驅(qū)動模型,。

注:本文中的linux kernel使用的是3.14版本。

 

二,、如何通過Device Tree完成運(yùn)行時參數(shù)傳遞以及platform的識別功能,?

1、匯編部分的代碼分析

linux/arch/arm/kernel/head.S文件定義了bootloader和kernel的參數(shù)傳遞要求:

MMU = off, D-cache = off, I-cache = dont care, r0 = 0, r1 = machine nr, r2 = atags or dtb pointer.

目前的kernel支持舊的tag list的方式,,同時也支持device tree的方式,。r2可能是device tree binary file的指針(bootloader要傳遞給內(nèi)核之前要copy到memory中),也可以能是tag list的指針,。在ARM的匯編部分的啟動代碼中(主要是head.S和head-common.S),,machine type ID和指向DTB或者atags的指針被保存在變量__machine_arch_type和__atags_pointer中,這么做是為了后續(xù)c代碼進(jìn)行處理,。

2,、和device tree相關(guān)的setup_arch代碼分析

具體的c代碼都是在setup_arch中處理,這個函數(shù)是一個總的入口點(diǎn),。具體代碼如下(刪除了部分無關(guān)代碼):

void __init setup_arch(char **cmdline_p)
{
    const struct machine_desc *mdesc;

……

    mdesc = setup_machine_fdt(__atags_pointer);
    if (!mdesc)
        mdesc = setup_machine_tags(__atags_pointer, __machine_arch_type);
    machine_desc = mdesc;
    machine_name = mdesc->name;

……
}

對于如何確定HW platform這個問題,,舊的方法是靜態(tài)定義若干的machine描述符(struct machine_desc ),在啟動過程中,,通過machine type ID作為索引,,在這些靜態(tài)定義的machine描述符中掃描,找到那個ID匹配的描述符,。在新的內(nèi)核中,,首先使用setup_machine_fdt來setup machine描述符,如果返回NULL,,才使用傳統(tǒng)的方法setup_machine_tags來setup machine描述符,。傳統(tǒng)的方法需要給出__machine_arch_type(bootloader通過r1寄存器傳遞給kernel的)和tag list的地址(用來進(jìn)行tag parse)。__machine_arch_type用來尋找machine描述符,;tag list用于運(yùn)行時參數(shù)的傳遞,。隨著內(nèi)核的不斷發(fā)展,相信有一天linux kernel會完全拋棄tag list的機(jī)制,。

3,、匹配platform(machine描述符)

setup_machine_fdt函數(shù)的功能就是根據(jù)Device Tree的信息,找到最適合的machine描述符,。具體代碼如下:

const struct machine_desc * __init setup_machine_fdt(unsigned int dt_phys)
{
    const struct machine_desc *mdesc, *mdesc_best = NULL;

    if (!dt_phys || !early_init_dt_scan(phys_to_virt(dt_phys)))
        return NULL;

    mdesc = of_flat_dt_match_machine(mdesc_best, arch_get_next_mach);

    if (!mdesc) { 
        出錯處理
    }

    /* Change machine number to match the mdesc we're using */
    __machine_arch_type = mdesc->nr;

    return mdesc;
}

early_init_dt_scan函數(shù)有兩個功能,,一個是為后續(xù)的DTB scan進(jìn)行準(zhǔn)備工作,另外一個是運(yùn)行時參數(shù)傳遞,。具體請參考下面一個section的描述,。

of_flat_dt_match_machine是在machine描述符的列表中scan,找到最合適的那個machine描述符,。我們首先看如何組成machine描述符的列表,。和傳統(tǒng)的方法類似,也是靜態(tài)定義的,。DT_MACHINE_START和MACHINE_END用來定義一個machine描述符,。編譯的時候,compiler會把這些machine descriptor放到一個特殊的段中(.arch.info.init),,形成machine描述符的列表,。machine描述符用下面的數(shù)據(jù)結(jié)構(gòu)來標(biāo)識(刪除了不相關(guān)的member):

struct machine_desc {
    unsigned int        nr;        /* architecture number    */
    const char *const     *dt_compat;    /* array of device tree 'compatible' strings    */

……

   };

nr成員就是過去使用的machine type ID。內(nèi)核machine描述符的table有若干個entry,,每個都有自己的ID,。bootloader傳遞了machine type ID,指明使用哪一個machine描述符,。目前匹配machine描述符使用compatible strings,,也就是dt_compat成員,這是一個string list,,定義了這個machine所支持的列表,。在掃描machine描述符列表的時候需要不斷的獲取下一個machine描述符的compatible字符串的信息,具體的代碼如下:

static const void * __init arch_get_next_mach(const char *const **match)
{
    static const struct machine_desc *mdesc = __arch_info_begin;
    const struct machine_desc *m = mdesc;

    if (m >= __arch_info_end)
        return NULL;

    mdesc++;
    *match = m->dt_compat;
    return m;
}

__arch_info_begin指向machine描述符列表第一個entry,。通過mdesc++不斷的移動machine描述符指針(Note:mdesc是static的),。match返回了該machine描述符的compatible string list。具體匹配的算法倒是很簡單,,就是比較字符串而已,,一個是root node的compatible字符串列表,一個是machine描述符的compatible字符串列表,,得分最低的(最匹配的)就是我們最終選定的machine type,。

4,、運(yùn)行時參數(shù)傳遞

運(yùn)行時參數(shù)是在掃描DTB的chosen node時候完成的,具體的動作就是獲取chosen node的bootargs,、initrd等屬性的value,,并將其保存在全局變量(boot_command_line,initrd_start,、initrd_end)中,。使用tag list方法是類似的,通過分析tag list,,獲取相關(guān)信息,,保存在同樣的全局變量中。具體代碼位于early_init_dt_scan函數(shù)中:

bool __init early_init_dt_scan(void *params)
{
    if (!params)
        return false;

    /* 全局變量initial_boot_params指向了DTB的header*/
    initial_boot_params = params;

    /* 檢查DTB的magic,,確認(rèn)是一個有效的DTB */
    if (be32_to_cpu(initial_boot_params->magic) != OF_DT_HEADER) {
        initial_boot_params = NULL;
        return false;
    }

    /* 掃描 /chosen node,,保存運(yùn)行時參數(shù)(bootargs)到boot_command_line,此外,,還處理initrd相關(guān)的property,,并保存在initrd_start和initrd_end這兩個全局變量中 */
    of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);

    /* 掃描根節(jié)點(diǎn),獲取 {size,address}-cells信息,,并保存在dt_root_size_cells和dt_root_addr_cells全局變量中 */
    of_scan_flat_dt(early_init_dt_scan_root, NULL);

    /* 掃描DTB中的memory node,,并把相關(guān)信息保存在meminfo中,全局變量meminfo保存了系統(tǒng)內(nèi)存相關(guān)的信息,。*/
    of_scan_flat_dt(early_init_dt_scan_memory, NULL);

    return true;
}

設(shè)定meminfo(該全局變量確定了物理內(nèi)存的布局)有若干種途徑:

1,、通過tag list(tag是ATAG_MEM)傳遞memory bank的信息。

2,、通過command line(可以用tag list,,也可以通過DTB)傳遞memory bank的信息。

3,、通過DTB的memory node傳遞memory bank的信息,。

目前當(dāng)然是推薦使用Device Tree的方式來傳遞物理內(nèi)存布局信息。

 

三,、初始化流程

在系統(tǒng)初始化的過程中,,我們需要將DTB轉(zhuǎn)換成節(jié)點(diǎn)是device_node的樹狀結(jié)構(gòu),以便后續(xù)方便操作,。具體的代碼位于setup_arch->unflatten_device_tree中,。

void __init unflatten_device_tree(void)
{
    __unflatten_device_tree(initial_boot_params, &of_allnodes,
                early_init_dt_alloc_memory_arch);

    /* Get pointer to "/chosen" and "/aliases" nodes for use everywhere */
    of_alias_scan(early_init_dt_alloc_memory_arch);
}

我們用struct device_node 來抽象設(shè)備樹中的一個節(jié)點(diǎn),具體解釋如下:

struct device_node {
    const char *name;----------------------device node name
    const char *type;-----------------------對應(yīng)device_type的屬性
    phandle phandle;-----------------------對應(yīng)該節(jié)點(diǎn)的phandle屬性
    const char *full_name; ----------------從“/”開始的,,表示該node的full path

    struct    property *properties;-------------該節(jié)點(diǎn)的屬性列表
    struct    property *deadprops; ----------如果需要刪除某些屬性,,kernel并非真的刪除,而是掛入到deadprops的列表
    struct    device_node *parent;------parent、child以及sibling將所有的device node連接起來
    struct    device_node *child;
    struct    device_node *sibling;
    struct    device_node *next;  --------通過該指針可以獲取相同類型的下一個node
    struct    device_node *allnext;-------通過該指針可以獲取node global list下一個node
    struct    proc_dir_entry *pde;--------開放到userspace的proc接口信息
    struct    kref kref;-------------該node的reference count
    unsigned long _flags;
    void    *data;
};

unflatten_device_tree函數(shù)的主要功能就是掃描DTB,,將device node被組織成:

1,、global list。全局變量struct device_node *of_allnodes就是指向設(shè)備樹的global list

2,、tree,。

這些功能主要是在__unflatten_device_tree函數(shù)中實(shí)現(xiàn),具體代碼如下(去掉一些無關(guān)緊要的代碼):

static void __unflatten_device_tree(struct boot_param_header *blob,---需要掃描的DTB
                 struct device_node **mynodes,---------global list指針
                 void * (*dt_alloc)(u64 size, u64 align))------內(nèi)存分配函數(shù)
{
    unsigned long size;
    void *start, *mem;
    struct device_node **allnextp = mynodes;

    此處刪除了health check代碼,,例如檢查DTB header的magic,確認(rèn)blob的確指向一個DTB,。

    /* scan過程分成兩輪,,第一輪主要是確定device-tree structure的長度,保存在size變量中 */
    start = ((void *)blob) + be32_to_cpu(blob->off_dt_struct);
    size = (unsigned long)unflatten_dt_node(blob, 0, &start, NULL, NULL, 0);
    size = ALIGN(size, 4);

    /* 初始化的時候,,并不是掃描到一個node或者property就分配相應(yīng)的內(nèi)存,,實(shí)際上內(nèi)核是一次性的分配了一大片內(nèi)存,這些內(nèi)存包括了所有的struct device_node,、node name,、struct property所需要的內(nèi)存。*/
    mem = dt_alloc(size + 4, __alignof__(struct device_node));
    memset(mem, 0, size);

    *(__be32 *)(mem + size) = cpu_to_be32(0xdeadbeef);   //用來檢驗(yàn)后面unflattening是否溢出

    /* 這是第二輪的scan,,第一次scan是為了得到保存所有node和property所需要的內(nèi)存size,,第二次就是實(shí)打?qū)嵉囊獦?gòu)建device node tree了 */
    start = ((void *)blob) + be32_to_cpu(blob->off_dt_struct);
    unflatten_dt_node(blob, mem, &start, NULL, &allnextp, 0); 
   

    此處略去校驗(yàn)溢出和校驗(yàn)OF_DT_END。
}

具體的scan是在unflatten_dt_node函數(shù)中,,如果已經(jīng)清楚地了解DTB的結(jié)構(gòu),,其實(shí)代碼很簡單,這里就不再細(xì)述了,。

四,、如何并入linux kernel的設(shè)備驅(qū)動模型

在linux kernel引入統(tǒng)一設(shè)備模型之后,bus,、driver和device形成了設(shè)備模型中的鐵三角,。在驅(qū)動初始化的時候會將代表該driver的一個數(shù)據(jù)結(jié)構(gòu)(一般是xxx_driver)掛入bus上的driver鏈表。device掛入鏈表分成兩種情況,,一種是即插即用類型的bus,,在插入一個設(shè)備后,總線可以檢測到這個行為并動態(tài)分配一個device數(shù)據(jù)結(jié)構(gòu)(一般是xxx_device,,例如usb_device),,之后,將該數(shù)據(jù)結(jié)構(gòu)掛入bus上的device鏈表,。bus上掛滿了driver和device,,那么如何讓device遇到“對”的那個driver呢?那么就要靠緣分了,,也就是bus的match函數(shù),。

上面是一段導(dǎo)論,,我們還是回到Device Tree。導(dǎo)致Device Tree的引入ARM體系結(jié)構(gòu)的代碼其中一個最重要的原因的太多的靜態(tài)定義的表格,。例如:一般代碼中會定義一個static struct platform_device *xxx_devices的靜態(tài)數(shù)組,,在初始化的時候調(diào)用platform_add_devices。這些靜態(tài)定義的platform_device往往又需要靜態(tài)定義各種resource,,這導(dǎo)致靜態(tài)表格進(jìn)一步增大,。如果ARM linux中不再定義這些表格,那么一定需要一個轉(zhuǎn)換的過程,,也就是說,,系統(tǒng)應(yīng)該會根據(jù)Device tree來動態(tài)的增加系統(tǒng)中的platform_device。當(dāng)然,,這個過程并非只是發(fā)生在platform bus上(具體可以參考“Platform Device”的設(shè)備),,也可能發(fā)生在其他的非即插即用的bus上,例如AMBA總線,、PCI總線,。一言以蔽之,如果要并入linux kernel的設(shè)備驅(qū)動模型,,那么就需要根據(jù)device_node的樹狀結(jié)構(gòu)(root是of_allnodes)將一個個的device node掛入到相應(yīng)的總線device鏈表中,。只要做到這一點(diǎn),總線機(jī)制就會安排device和driver的約會,。

當(dāng)然,,也不是所有的device node都會掛入bus上的設(shè)備鏈表,比如cpus node,,memory node,,choose node等。

1,、cpus node的處理

這部分的處理可以參考setup_arch->arm_dt_init_cpu_maps中的代碼,,具體的代碼如下:

void __init arm_dt_init_cpu_maps(void)
{
    scan device node global list,尋找full path是“/cpus”的那個device node,。cpus這個device node只是一個容器,,其中包括了各個cpu node的定義以及所有cpu node共享的property。
    cpus = of_find_node_by_path("/cpus");

 

    for_each_child_of_node(cpus, cpu) {           遍歷cpus的所有的child node
        u32 hwid;

        if (of_node_cmp(cpu->type, "cpu"))        我們只關(guān)心那些device_type是cpu的node
            continue;


        if (of_property_read_u32(cpu, "reg", &hwid)) {    讀取reg屬性的值并賦值給hwid
            return;
        }

        reg的屬性值的8 MSBs必須設(shè)置為0,,這是ARM CPU binding定義的,。
        if (hwid & ~MPIDR_HWID_BITMASK)  
            return;

        不允許重復(fù)的CPU id,那是一個災(zāi)難性的設(shè)定
        for (j = 0; j < cpuidx; j++)
            if (WARN(tmp_map[j] == hwid, "Duplicate /cpu reg "
                             "properties in the DT\n"))
                return;

數(shù)組tmp_map保存了系統(tǒng)中所有CPU的MPIDR值(CPU ID值),,具體的index的編碼規(guī)則是: tmp_map[0]保存了booting CPU的id值,,其余的CPU的ID值保存在1~NR_CPUS的位置。
        if (hwid == mpidr) {
            i = 0;
            bootcpu_valid = true;
        } else {
            i = cpuidx++;
        }

        tmp_map[i] = hwid;
    }

根據(jù)DTB中的信息設(shè)定cpu logical map數(shù)組。

    for (i = 0; i < cpuidx; i++) {
        set_cpu_possible(i, true);
        cpu_logical_map(i) = tmp_map[i];
    }
}

要理解這部分的內(nèi)容,,需要理解ARM CUPs binding的概念,,可以參考linux/Documentation/devicetree/bindings/arm目錄下的CPU.txt文件的描述。

2,、memory的處理

這部分的處理可以參考setup_arch->setup_machine_fdt->early_init_dt_scan->early_init_dt_scan_memory中的代碼,。具體如下:

int __init early_init_dt_scan_memory(unsigned long node, const char *uname,
                     int depth, void *data)
{
    char *type = of_get_flat_dt_prop(node, "device_type", NULL); 獲取device_type屬性值
    __be32 *reg, *endp;
    unsigned long l;

    在初始化的時候,我們會對每一個device node都要調(diào)用該call back函數(shù),,因此,,我們要過濾掉那些和memory block定義無關(guān)的node。和memory block定義有的節(jié)點(diǎn)有兩種,,一種是node name是memory@形態(tài)的,,另外一種是node中定義了device_type屬性并且其值是memory。
    if (type == NULL) {
        if (depth != 1 || strcmp(uname, "memory@0") != 0)
            return 0;
    } else if (strcmp(type, "memory") != 0)
        return 0;

    獲取memory的起始地址和length的信息,。有兩種屬性和該信息有關(guān),一個是linux,usable-memory,,不過最新的方式還是使用reg屬性,。

reg = of_get_flat_dt_prop(node, "linux,usable-memory", &l);
    if (reg == NULL)
        reg = of_get_flat_dt_prop(node, "reg", &l);
    if (reg == NULL)
        return 0;

    endp = reg + (l / sizeof(__be32));

reg屬性的值是address,size數(shù)組,,那么如何來取出一個個的address/size呢,?由于memory node一定是root node的child,因此dt_root_addr_cells(root node的#address-cells屬性值)和dt_root_size_cells(root node的#size-cells屬性值)之和就是address,,size數(shù)組的entry size,。

    while ((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells)) {
        u64 base, size;

        base = dt_mem_next_cell(dt_root_addr_cells, ?);
        size = dt_mem_next_cell(dt_root_size_cells, ?);

        early_init_dt_add_memory_arch(base, size);  將具體的memory block信息加入到內(nèi)核中。
    }

    return 0;
}

 

3,、interrupt controller的處理

初始化是通過start_kernel->init_IRQ->machine_desc->init_irq()實(shí)現(xiàn)的,。我們用S3C2416為例來描述interrupt controller的處理過程。下面是machine描述符的定義,。

DT_MACHINE_START(S3C2416_DT, "Samsung S3C2416 (Flattened Device Tree)")
……
    .init_irq    = irqchip_init,
……
MACHINE_END

在driver/irqchip/irq-s3c24xx.c文件中定義了兩個interrupt controller,,如下:

IRQCHIP_DECLARE(s3c2416_irq, "samsung,s3c2416-irq", s3c2416_init_intc_of);

IRQCHIP_DECLARE(s3c2410_irq, "samsung,s3c2410-irq", s3c2410_init_intc_of);

當(dāng)然,系統(tǒng)中可以定義更多的irqchip,,不過具體用哪一個是根據(jù)DTB中的interrupt controller node中的compatible屬性確定的,。在driver/irqchip/irqchip.c文件中定義了irqchip_init函數(shù),如下:

void __init irqchip_init(void)
{
    of_irq_init(__irqchip_begin);
}

__irqchip_begin就是所有的irqchip的一個列表,,of_irq_init函數(shù)是遍歷Device Tree,,找到匹配的irqchip。具體的代碼如下:

void __init of_irq_init(const struct of_device_id *matches)
{
    struct device_node *np, *parent = NULL;
    struct intc_desc *desc, *temp_desc;
    struct list_head intc_desc_list, intc_parent_list;

    INIT_LIST_HEAD(&intc_desc_list);
    INIT_LIST_HEAD(&intc_parent_list);

    遍歷所有的node,,尋找定義了interrupt-controller屬性的node,,如果定義了interrupt-controller屬性則說明該node就是一個中斷控制器。

    for_each_matching_node(np, matches) {
        if (!of_find_property(np, "interrupt-controller", NULL) ||
                !of_device_is_available(np))
            continue;
       

分配內(nèi)存并掛入鏈表,當(dāng)然還有根據(jù)interrupt-parent建立controller之間的父子關(guān)系,。對于interrupt controller,,它也可能是一個樹狀的結(jié)構(gòu)。
        desc = kzalloc(sizeof(*desc), GFP_KERNEL);
        if (WARN_ON(!desc))
            goto err;

        desc->dev = np;
        desc->interrupt_parent = of_irq_find_parent(np);
        if (desc->interrupt_parent == np)
            desc->interrupt_parent = NULL;
        list_add_tail(&desc->list, &intc_desc_list);
    }

    正因?yàn)閕nterrupt controller被組織成樹狀的結(jié)構(gòu),,因此初始化的順序就需要控制,,應(yīng)該從根節(jié)點(diǎn)開始,依次遞進(jìn)到下一個level的interrupt controller,。
    while (!list_empty(&intc_desc_list)) {  intc_desc_list鏈表中的節(jié)點(diǎn)會被一個個的處理,,每處理完一個節(jié)點(diǎn)就會將該節(jié)點(diǎn)刪除,當(dāng)所有的節(jié)點(diǎn)被刪除,,整個處理過程也就是結(jié)束了,。
        
        list_for_each_entry_safe(desc, temp_desc, &intc_desc_list, list) {
            const struct of_device_id *match;
            int ret;
            of_irq_init_cb_t irq_init_cb;

            最開始的時候parent變量是NULL,確保第一個被處理的是root interrupt controller,。在處理完root node之后,,parent變量被設(shè)定為root interrupt controller,因此,,第二個循環(huán)中處理的是所有parent是root interrupt controller的child interrupt controller,。也就是level 1(如果root是level 0的話)的節(jié)點(diǎn)。

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

            list_del(&desc->list);      -----從鏈表中刪除
            match = of_match_node(matches, desc->dev);-----匹配并初始化
            if (WARN(!match->data,----------match->data是初始化函數(shù)
                "of_irq_init: no init function for %s\n",
                match->compatible)) {
                kfree(desc);
                continue;
            }

            irq_init_cb = (of_irq_init_cb_t)match->data;
            ret = irq_init_cb(desc->dev, desc->interrupt_parent);-----執(zhí)行初始化函數(shù)
            if (ret) {
                kfree(desc);
                continue;
            }

           處理完的節(jié)點(diǎn)放入intc_parent_list鏈表,,后面會用到
            list_add_tail(&desc->list, &intc_parent_list);
        }

        對于level 0,,只有一個root interrupt controller,對于level 1,,可能有若干個interrupt controller,,因此要遍歷這些parent interrupt controller,以便處理下一個level的child node,。
        desc = list_first_entry_or_null(&intc_parent_list,
                        typeof(*desc), list);
        if (!desc) {
            pr_err("of_irq_init: children remain, but no parents\n");
            break;
        }
        list_del(&desc->list);
        parent = desc->dev;
        kfree(desc);
    }

    list_for_each_entry_safe(desc, temp_desc, &intc_parent_list, list) {
        list_del(&desc->list);
        kfree(desc);
    }
err:
    list_for_each_entry_safe(desc, temp_desc, &intc_desc_list, list) {
        list_del(&desc->list);
        kfree(desc);
    }
}

只有該node中有interrupt-controller這個屬性定義,,那么linux kernel就會分配一個interrupt controller的描述符(struct intc_desc)并掛入隊(duì)列。通過interrupt-parent屬性,,可以確定各個interrupt controller的層次關(guān)系,。在scan了所有的Device Tree中的interrupt controller的定義之后,系統(tǒng)開始匹配過程,。一旦匹配到了interrupt chip列表中的項(xiàng)次后,,就會調(diào)用相應(yīng)的初始化函數(shù)。如果CPU是S3C2416的話,,匹配到的是irqchip的初始化函數(shù)是s3c2416_init_intc_of,。

OK,我們已經(jīng)通過compatible屬性找到了適合的interrupt controller,,那么如何解析reg屬性呢,?我們知道,,對于s3c2416的interrupt controller而言,其#interrupt-cells的屬性值是4,,定義為,。每個域的解釋如下:

(1)ctrl_num表示使用哪一種類型的interrupt controller,其值的解釋如下:

      - 0 ... main controller
      - 1 ... sub controller
      - 2 ... second main controller

(2)parent_irq,。對于sub controller,,parent_irq標(biāo)識了其在main controller的bit position。

(3)ctrl_irq標(biāo)識了在controller中的bit位置,。

(4)type標(biāo)識了該中斷的trigger type,,例如:上升沿觸發(fā)還是電平觸發(fā)。

為了更順暢的描述后續(xù)的代碼,,我需要簡單的介紹2416的中斷控制器,,其block diagram如下:

2416intc

53個Samsung2416的中斷源被分成兩種類型,一種是需要sub寄存器進(jìn)行控制的,,例如DMA,,系統(tǒng)中的8個DMA中斷是通過兩級識別的,先在SRCPND寄存器中得到是DMA中斷的信息,,具體是哪一個channel的DMA中斷需要繼續(xù)查詢SUBSRC寄存器,。那些不需要sub寄存器進(jìn)行控制的,例如timer,,5個timer的中斷可以直接從SRCPND中得到。
中斷MASK寄存器可以控制產(chǎn)生的中斷是否要報(bào)告給CPU,,當(dāng)一個中斷被mask的時候,,雖然SRCPND寄存器中,硬件會set該bit,,但是不會影響到INTPND寄存器,,從而不會向CPU報(bào)告該中斷。對于SUBMASK寄存器,,如果該bit被set,,也就是該sub中斷被mask了,那么即便產(chǎn)生了對應(yīng)的sub中斷,,也不會修改SRCPND寄存器的內(nèi)容,,只是修改SUBSRCPND中寄存器的內(nèi)容。

不過隨著硬件的演化,,更多的HW block加入到SOC中,,這使得中斷源不夠用了,因此中斷寄存器又被分成兩個group,,一個是group 1(開始地址是0X4A000000,,也就是main controller了),,另外一個是group2(開始地址是0X4A000040,叫做second main controller),。group 1中的sub寄存器的起始地址是0X4A000018(也就是sub controller),。

了解了上面的內(nèi)容后,下面的定義就比較好理解了:

static struct s3c24xx_irq_of_ctrl s3c2416_ctrl[] = {
    {
        .name = "intc", -----------main controller
        .offset = 0,
    }, {
        .name = "subintc", ---------sub controller
        .offset = 0x18,
        .parent = &s3c_intc[0],
    }, {
        .name = "intc2", ----------second main controller
        .offset = 0x40,
    }
};

對于s3c2416而言,,irqchip的初始化函數(shù)是s3c2416_init_intc_of,,s3c2416_ctrl作為參數(shù)傳遞給了s3c_init_intc_of,大部分的處理都是在s3c_init_intc_of函數(shù)中完成的,,由于這個函數(shù)和中斷子系統(tǒng)非常相關(guān),,這里就不詳述了,后續(xù)會有一份專門的文檔描述之,。

4,、GPIO controller的處理

暫不描述,后續(xù)會有一份專門的文檔描述GPIO sub system,。

5,、machine初始化

machine初始化的代碼可以沿著start_kernel->rest_init->kernel_init->kernel_init_freeable->do_basic_setup->do_initcalls路徑尋找。在do_initcalls函數(shù)中,,kernel會依次執(zhí)行各個initcall函數(shù),,在這個過程中,會調(diào)用customize_machine,,具體如下:

static int __init customize_machine(void)
{

    if (machine_desc->init_machine)
        machine_desc->init_machine();
    else
        of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);

    return 0;
}
arch_initcall(customize_machine);

在這個函數(shù)中,,一般會調(diào)用machine描述符中的init_machine callback函數(shù)來把各種Device Tree中定義各個設(shè)備節(jié)點(diǎn)加入到系統(tǒng)。如果machine描述符中沒有定義init_machine函數(shù),,那么直接調(diào)用of_platform_populate把所有的platform device加入到kernel中,。對于s3c2416,其machine描述符中的init_machine callback函數(shù)就是s3c2416_dt_machine_init,,代碼如下:

static void __init s3c2416_dt_machine_init(void)
{
    of_platform_populate(NULL, --------傳入NULL參數(shù)表示從root node開始scan

of_default_bus_match_table, s3c2416_auxdata_lookup, NULL);

    s3c_pm_init(); --------power management相關(guān)的初始化
}

由此可見,,最終生成platform device的代碼來自of_platform_populate函數(shù)。該函數(shù)的邏輯比較簡單,,遍歷device node global list中所有的node,,并調(diào)用of_platform_bus_create處理,of_platform_bus_create函數(shù)代碼如下:

static int of_platform_bus_create(struct device_node *bus,-------------要創(chuàng)建的那個device node
                  const struct of_device_id *matches,-------要匹配的list
                  const struct of_dev_auxdata *lookup,------附屬數(shù)據(jù)
                  struct device *parent, bool strict)---------------parent指向父節(jié)點(diǎn),。strict是否要求完全匹配
{
    const struct of_dev_auxdata *auxdata;
    struct device_node *child;
    struct platform_device *dev;
    const char *bus_id = NULL;
    void *platform_data = NULL;
    int rc = 0;

刪除確保device node有compatible屬性的代碼,。

    auxdata = of_dev_lookup(lookup, bus);  在傳入的lookup table尋找和該device node匹配的附加數(shù)據(jù)
    if (auxdata) {
        bus_id = auxdata->name;-----------------如果找到,那么就用附加數(shù)據(jù)中的靜態(tài)定義的內(nèi)容
        platform_data = auxdata->platform_data;
    }

ARM公司提供了CPU core,,除此之外,,它設(shè)計(jì)了AMBA的總線來連接SOC內(nèi)的各個block。符合這個總線標(biāo)準(zhǔn)的SOC上的外設(shè)叫做ARM Primecell Peripherals,。如果一個device node的compatible屬性值是arm,primecell的話,,可以調(diào)用of_amba_device_create來向amba總線上增加一個amba device,。

    if (of_device_is_compatible(bus, "arm,primecell")) {
        of_amba_device_create(bus, bus_id, platform_data, parent);
        return 0;
    }

    如果不是ARM Primecell Peripherals,那么我們就需要向platform bus上增加一個platform device了

    dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);
    if (!dev || !of_match_node(matches, bus))
        return 0;

    一個device node可能是一個橋設(shè)備,,因此要重復(fù)調(diào)用of_platform_bus_create來把所有的device node處理掉,。

    for_each_child_of_node(bus, child) {
        pr_debug("   create child: %s\n", child->full_name);
        rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict);
        if (rc) {
            of_node_put(child);
            break;
        }
    }
    return rc;
}

具體增加platform device的代碼在of_platform_device_create_pdata中,代碼如下:

static struct platform_device *of_platform_device_create_pdata(
                    struct device_node *np,
                    const char *bus_id,
                    void *platform_data,
                    struct device *parent)
{
    struct platform_device *dev;

    if (!of_device_is_available(np))---------check status屬性,,確保是enable或者OK的,。
        return NULL;

    of_device_alloc除了分配struct platform_device的內(nèi)存,還分配了該platform device需要的resource的內(nèi)存(參考struct platform_device 中的resource成員),。當(dāng)然,,這就需要解析該device node的interrupt資源以及memory address資源。

    dev = of_device_alloc(np, bus_id, parent);
    if (!dev)
        return NULL;

設(shè)定platform_device 中的其他成員
    dev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
    if (!dev->dev.dma_mask)
        dev->dev.dma_mask = &dev->dev.coherent_dma_mask;
    dev->dev.bus = &platform_bus_type;
    dev->dev.platform_data = platform_data;

    if (of_device_add(dev) != 0) {------------------把這個platform device加入統(tǒng)一設(shè)備模型系統(tǒng)中
        platform_device_put(dev);
        return NULL;
    }

    return dev;
}

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購買等信息,,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請點(diǎn)擊一鍵舉報(bào),。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多