zz-am335x-LinuxKernel启动流程初始化141221c

来源:互联网 发布:电子海洛因 知乎 编辑:程序博客网 时间:2024/06/03 18:00
//zz//#######################################################################

zz-am335x-LinuxKernel启动流程初始化141221c

zz-Write:
    @2014-12-21 22:40:25
    @2014-12-21 23:57:29
    @2014-12-22 00:01:41
        Tab 转4空格,方便 csdn_blog 上传
    @

REF:
    linux-3.2.0-psp04.06ti-zz141219b.zip
        ti-sdk-am335x-evm-06.00.00.00-Linux-x86-Install.bin
    
    linux内核启动流程(上)
        http://blog.csdn.net/williamwang2013/article/details/8691883
    linux内核启动流程(下)
        http://blog.csdn.net/williamwang2013/article/details/8695618


KeyWord:
    ECC 校验 => BCH 8/12/16 bits => 2K: 13*4/??/14*?? Bytes


    arch/arm/mach-omap2/board-am335xevm.c
        MACHINE_START(AM335XIAEVM, "am335xiaevm")
        am335x_evm_init();
        struct evm_dev_cfg gen_purp_evm_dev_cfg[] = {...};
        struct mtd_partition am335x_nand_partitions[] = {...};
    
    arch/arm/boot/compress/head.S
        start:
        bl  decompress_kernel
        ARM(    mov pc, r4  )   @ call kernel
    arch/arm/kernel/head-common.S
        b   start_kernel
    
    init/main.c
        start_kernel()
        setup_arch(&command_line);
            mdesc = setup_machine_tags(machine_arch_type);
            machine_name = mdesc->name;
            mdesc->init_early();
    
    setup_machine_tags()
    arch/arm/include/asm/mach/arch.h
        #define for_each_machine_desc(p)    \
            for (p = __arch_info_begin; p < __arch_info_end; p++)

//zz//#######################################################################
1.
从汇编代码跳入C入口函数
start:  =>  start_kernel()
    
arch/arm/boot/compress/head.S
    start:
    bl  decompress_kernel
    ARM(    mov pc, r4  )   @ call kernel


arch/arm/kernel/head-common.S
    b   start_kernel



//zz//#######################################################################
2.
从C入口函数调用板子相关的初始化函数指针列表

1)
根据 machine_arch_type 即板子的 id 在表中查找,匹配 machine_desc
init/main.c
    start_kernel()
    
    //zz// MACHINE_START() define struct machine_desc
    //  init cpu-board __mach_desc_AM335XIAEVM  
    setup_arch(&command_line);
    
        //zz// machine_desc am335x cpu-board init 
        //  sequence func array[]
        struct machine_desc *mdesc;
        mdesc = setup_machine_tags(machine_arch_type);
        machine_name = mdesc->name;
        mdesc->init_early();

setup_machine_tags()
从链表中根据 arch id 查找本板子的 machine_desc 初始化结构体

arch/arm/include/asm/mach/arch.h
    #define for_each_machine_desc(p)    \
        for (p = __arch_info_begin; p < __arch_info_end; p++)

2)
MACHINE_START 宏定义一个板子的 machine_desc 结构体

arch/arm/mach-omap2/board-am335xevm.c
    MACHINE_START(AM335XIAEVM, "am335xiaevm")
    am335x_evm_init();
    struct evm_dev_cfg gen_purp_evm_dev_cfg[] = {...};
    struct mtd_partition am335x_nand_partitions[] = {...};

3)
板子相关 machine_desc 结构体的定义 __mach_desc_AM335XIAEVM
arch/arm/mach-omap2/board-am335xevm.c

//zz// MACH_TYPE_##_type => MACH_TYPE_AM335XIAEVM
//  __mach_desc_##_type => __mach_desc_AM335XIAEVM
//
//  struct machine_desc __mach_desc_AM335XIAEVM
//      .nr = MACH_TYPE_AM335XIAEVM;
//      .name = am335xiaevm;

MACHINE_START(AM335XIAEVM, "am335xiaevm")
    /* Maintainer: Texas Instruments */
    .atag_offset    = 0x100,
    .map_io     = am335x_evm_map_io,
    .init_irq   = ti81xx_init_irq,
    .init_early = am33xx_init_early,
    .timer      = &omap3_am33xx_timer,
    .init_machine   = am335x_evm_init,
MACHINE_END


//zz//#######################################################################
3.
machine_desc 结构体中函数做了什么

1)
其中重要的初始化函数
am335x_evm_init()
    //zz// 1. evm_dev_cfg[] ;init sequence func array are called
    am335x_evm_i2c_init();
    
    //zz// 2. am335x_i2c0_boardinfo => .. => evm_dev_cfg[]
    omap_register_i2c_bus(1, 100, am335x_i2c0_boardinfo,
                ARRAY_SIZE(am335x_i2c0_boardinfo));
    
    //zz// 3. am335x_baseboard_eeprom_info => .. => evm_dev_cfg[]
    /* Baseboard board EEPROM */
    I2C_BOARD_INFO("24c256", BASEBOARD_I2C_ADDR),
    .platform_data  = &am335x_baseboard_eeprom_info,
    
    //zz// 4. am335x_evm_setup => .. => evm_dev_cfg[]
    .setup          = am335x_evm_setup,
    .context        = (void *)NULL,

    //zz// 5. setup_general_purpose_evm() => .. => evm_dev_cfg[]
    if (!strncmp("SKU#01", config.opt, 6))
        setup_general_purpose_evm();


    //zz// 6. gen_purp_evm_dev_cfg[] is  evm_dev_cfg array.
    _configure_device(boardid, gen_purp_evm_dev_cfg, (1L << prof_sel));


2)
am335x_evm 板子的初始化函数指针数组,这些函数会逐个被调用
这些就是内核启动时候初始化各硬件配置寄存器的函数,可以添加自己自定义的


/* General Purpose EVM */
//zz// 7. gen_purp_evm_dev_cfg[] is sequence func array[]
static struct evm_dev_cfg gen_purp_evm_dev_cfg[] = {
    {am335x_rtc_init, DEV_ON_BASEBOARD, PROFILE_ALL},
    {clkout2_enable, DEV_ON_BASEBOARD, PROFILE_ALL},
    {enable_ecap0,  DEV_ON_DGHTR_BRD, (PROFILE_0 | PROFILE_1 |
                        PROFILE_2 | PROFILE_7) },
    {lcdc_init, DEV_ON_DGHTR_BRD, (PROFILE_0 | PROFILE_1 |
                        PROFILE_2 | PROFILE_7) },
    {mfd_tscadc_init,   DEV_ON_DGHTR_BRD, (PROFILE_0 | PROFILE_1 |
                        PROFILE_2 | PROFILE_7) },
    {rgmii1_init,   DEV_ON_BASEBOARD, PROFILE_ALL},
    {rgmii2_init,   DEV_ON_DGHTR_BRD, (PROFILE_1 | PROFILE_2 |
    ... ...


//zz//#######################################################################
4.
start_kernel() 除了完成 arch 初始化,还有其他许多东西需要配置执行

例如
    time_init();
    console_init();


最后会执行 文件系统的 init 程序等等操作,以后再分析

#########################
1)

arch/arm/mach-omap2/board-am335xevm.c
此文件是移植到一个板子的基础,再增加一点其他的说明


static struct mtd_partition am335x_nand_partitions[] = {
/* All the partition sizes are listed in terms of NAND block size */
    {
        .name           = "SPL",
        .offset         = 0,            /* Offset = 0x0 */
        .size           = SZ_128K,
    },
    ...

文件系统若使用 nand flash , 则此文件 mtd_partition 类型结构指定了分区信息
必须和 u-boot 写入 nand flash 的位置一样
才可能正确启动 Linux-Kernel

#########################
2)
Nand Ecc 简单说明
am335x 使用的是 ubi 格式的镜像格式烧写文件系统 rootfs

ECC 算法主要有 汉明码 伯明翰(BCH) RS 三种
    汉明码:
        s3c24x0-ARM9-yaffs-512byte 小页的 nand 使用的多,只能纠正1位错误
    BCH伯明翰码:
        SLC(2K等较大页) MLC(页很大,4K,8K..) 现在用的多,可以纠正多位 8/12/16 位错误
        算法复杂,各家都有自己的算法..
    
我所知道的两种 BCH 方式所需要的 ECC 码:
    2K page 的 OOB 有64Bytes ,其中页开头 2 Bytes 保留用作 Block 坏块标记(虽然只有 该 Block 中Page0 用到)
        512 Byte(数据) - 8 bit(t 最大纠错位数) - 13 Bytes(OOB中ECC码) => 2K的页需要 13*4 = 52 字节 ECC 码
        512 Byte(数据) - 16 bit(t 最大纠错位数) - 14 Bytes(OOB中ECC码) => 2K的页需要 14*4 = 56 字节 ECC 码


ECC 有硬件和软件实现
    设置为 NAND_ECC_NONE 并不是不做 ECC 校验的意思;而是内核Kernel不做,交给 yaffs2 或 ubifs代码来做
    

include/linux/mtd/nand.h

typedef enum {
    NAND_ECC_NONE,
        //zz// 内核常用项; 如此 ECC 由文件系统 yaffs2 或者 ubifs 代码中实现 ECC
        // 至于是 HW 还是 SOFT 就要看文件系统镜像格式代码中怎么做了
    NAND_ECC_SOFT,
        //zz// ECC 校验由内核完成,内核采用 SOFT 软件C代码实现
        
    NAND_ECC_HW,
        //zz// ECC 校验由内核完成,内核采用 HW 模块实现(操作寄存器?)


    NAND_ECC_HW_SYNDROME,
    NAND_ECC_HW_OOB_FIRST,
    NAND_ECC_SOFT_BCH,
        //zz// 可以校验多位(8/12/16)错误的 ECC 校验算法,SLC大页/MLC 现在多用这种
        //  但一般也是 yaffs2 或 ubifs 代码来做,不会在内核做吧?


} nand_ecc_modes_t;


0 0
原创粉丝点击