龙空技术网

移植u-boot-2016.03到Jz2440之修改时钟、内存控制器、UART设置

疯狂写Bug 149

前言:

此刻同学们对“汇编语言jz指令应用实例”可能比较珍视,你们都想要知道一些“汇编语言jz指令应用实例”的相关资讯。那么小编同时在网上收集了一些关于“汇编语言jz指令应用实例””的相关内容,希望我们能喜欢,兄弟们快快来学习一下吧!

3.u-boot-2016.03 修改时钟、设置内存控制器、支持UART

    从第一节新建单板最后的测试结果可知,把编译好的u-boot.bin烧写到Jz2440开发板的Nor Flash并重启开发板,串口没有任何输出,在这一节我们将解决这个问题。这一节的内容主要分为三个部分:修改时钟、修改内存控制器设置、修改配置支持UART。

3.1 修改时钟

通过对u-boot-2016.03启动过程的分析,在start.S文件中发现不足,如下图所示:

从图中可知:uboot里先以60MHZ的时钟计算参数来设置内存控制器,但是MPLL 还未设置; 处理措施:把MPLL的设置放到start.S里,取消boardearlyinit_f里对MPLL的设置,同时根据新设置的时钟参数设置内存控制器。

关于Jz2440开发板SOC的时钟体系,可以阅读韦东山老师的博客掌握Jz2440_ARM芯片时钟体系。 下面修改代码,把时钟修改为FCLK=400MHz,并设置时钟比例FCLK:HCLK:PCLK=1:4:8,把上图设置时钟比例参数的代码删掉,添加如下代码:

/* 2. 设置时钟 */        ldr r0, =0x4c000014        mov r1, #0x05;            // FCLK:HCLK:PCLK=1:4:8        str r1, [r0]        /* 如果HDIVN非0,CPU的总线模式应该从“fast bus mode”变为“asynchronous bus mode” */        mrc p15, 0, r1, c1, c0, 0       /* 读出控制寄存器 */         orr r1, r1, #0xc0000000         /* 设置为“asynchronous bus mode” */        mcr p15, 0, r1, c1, c0, 0       /* 写入控制寄存器 */#define S3C2440_MPLL_400MHZ     ((0x5c<<12)|(0x01<<4)|(0x01))        /* MPLLCON = S3C2440_MPLL_200MHZ */        ldr r0, =0x4c000004        ldr r1, =S3C2440_MPLL_400MHZ        str r1, [r0]        /* 启动ICACHE */        mrc p15, 0, r0, c1, c0, 0   @ read control reg        orr r0, r0, #(1<<12)        mcr p15, 0, r0, c1, c0, 0   @ write it back
3.2 修改内存控制器设置

(1) uboot原理是依据HCLK=60MHz的时钟计算参数来设置内存控制器的,修改时钟后HCLK=100MHz,所以需要重新设置内存控制器。在board/samsung/jz2440/lowlevel_init.S中最后有如下代码:

SMRDATA:    .word (0+(B1_BWSCON<<4)+(B2_BWSCON<<8)+(B3_BWSCON<<12)+(B4_BWSCON<<16)+(B5_BWSCON<<20)+(B6_BWSCON<<24)+(B7_BWSCON<<28))    .word ((B0_Tacs<<13)+(B0_Tcos<<11)+(B0_Tacc<<8)+(B0_Tcoh<<6)+(B0_Tah<<4)+(B0_Tacp<<2)+(B0_PMC))    .word ((B1_Tacs<<13)+(B1_Tcos<<11)+(B1_Tacc<<8)+(B1_Tcoh<<6)+(B1_Tah<<4)+(B1_Tacp<<2)+(B1_PMC))    .word ((B2_Tacs<<13)+(B2_Tcos<<11)+(B2_Tacc<<8)+(B2_Tcoh<<6)+(B2_Tah<<4)+(B2_Tacp<<2)+(B2_PMC))    .word ((B3_Tacs<<13)+(B3_Tcos<<11)+(B3_Tacc<<8)+(B3_Tcoh<<6)+(B3_Tah<<4)+(B3_Tacp<<2)+(B3_PMC))    .word ((B4_Tacs<<13)+(B4_Tcos<<11)+(B4_Tacc<<8)+(B4_Tcoh<<6)+(B4_Tah<<4)+(B4_Tacp<<2)+(B4_PMC))    .word ((B5_Tacs<<13)+(B5_Tcos<<11)+(B5_Tacc<<8)+(B5_Tcoh<<6)+(B5_Tah<<4)+(B5_Tacp<<2)+(B5_PMC))    .word ((B6_MT<<15)+(B6_Trcd<<2)+(B6_SCAN))    .word ((B7_MT<<15)+(B7_Trcd<<2)+(B7_SCAN))    .word ((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Trc<<18)+(Tchr<<16)+REFCNT)    .word 0x32    .word 0x30    .word 0x30                         

更改为如下代码:

SMRDATA:    .long 0x22011110     //BWSCON    .long 0x00000700     //BANKCON0    .long 0x00000700     //BANKCON1    .long 0x00000700     //BANKCON2    .long 0x00000700     //BANKCON3      .long 0x00000740     //BANKCON4    .long 0x00000700     //BANKCON5    .long 0x00018005     //BANKCON6    .long 0x00018005     //BANKCON7    .long 0x008C04F4     // REFRESH    .long 0x000000B1     //BANKSIZE    .long 0x00000030     //MRSRB6    .long 0x00000030     //MRSRB7

(2) 取消boardearlyinitf里对MPLL的设置,修改 board/samsung/jz2440/jz2440.c中boardearlyinitf函数代码如下:

int board_early_init_f(void){    struct s3c24x0_clock_power * const clk_power =                    s3c24x0_get_base_clock_power();    struct s3c24x0_gpio * const gpio = s3c24x0_get_base_gpio();#if 0    /* to reduce PLL lock time, adjust the LOCKTIME register */    writel(0xFFFFFF, &clk_power->locktime);    /* configure MPLL */    writel((M_MDIV << 12) + (M_PDIV << 4) + M_SDIV,           &clk_power->mpllcon);    /* some delay between MPLL and UPLL */    pll_delay(4000);#endif    /* configure UPLL */    writel((U_M_MDIV << 12) + (U_M_PDIV << 4) + U_M_SDIV,           &clk_power->upllcon);    /* some delay between MPLL and UPLL */    pll_delay(8000);    /* set up the I/O ports */    writel(0x007FFFFF, &gpio->gpacon);    writel(0x00044555, &gpio->gpbcon);    writel(0x000007FF, &gpio->gpbup);    writel(0xAAAAAAAA, &gpio->gpccon);    writel(0x0000FFFF, &gpio->gpcup);    writel(0xAAAAAAAA, &gpio->gpdcon);    writel(0x0000FFFF, &gpio->gpdup);    writel(0xAAAAAAAA, &gpio->gpecon);    writel(0x0000FFFF, &gpio->gpeup);    writel(0x000055AA, &gpio->gpfcon);    writel(0x000000FF, &gpio->gpfup);    writel(0xFF95FFBA, &gpio->gpgcon);    writel(0x0000FFFF, &gpio->gpgup);    writel(0x002AFAAA, &gpio->gphcon);    writel(0x000007FF, &gpio->gphup);    return 0;}

(3) 做了以上修改后,重新make编译成功,烧写u-boot.bin到Nor Flash,重启开发板,串口输出如下:

从上图可知,串口输出的是乱码,究其原因应该是波特率设置的问题。

3.3 查看波特率的设置,解决乱码的问题

(1) 查看串口波特率的设置,在common/boardf.c有函数boardinitf,它通过数组initsequencef里的函数初始化硬件,boardinit_f函数的代码如下:

void board_init_f(ulong boot_flags){#ifdef CONFIG_SYS_GENERIC_GLOBAL_DATA    /*     * For some archtectures, global data is initialized and used before     * calling this function. The data should be preserved. For others,     * CONFIG_SYS_GENERIC_GLOBAL_DATA should be defined and use the stack     * here to host global data until relocation.     */    gd_t data;    gd = &data;    /*     * Clear global data before it is accessed at debug print     * in initcall_run_list. Otherwise the debug print probably     * get the wrong vaule of gd->have_console.     */    zero_global_data();#endif    gd->flags = boot_flags;    gd->have_console = 0;    if (initcall_run_list(init_sequence_f))        hang();#if !defined(CONFIG_ARM) && !defined(CONFIG_SANDBOX) && \        !defined(CONFIG_EFI_APP)    /* NOTREACHED - jump_to_copy() does not return */    hang();#endif}

source insight 里跳转找到这个数据,从第二节启动过程分析,去掉无关代码后的initsequencef数组如下:

static init_fnc_t init_sequence_f[] = {    /* setup_mon_len函数是设置gd结构体成员gd->mon_len的函数;     *在setup_mon_len函数中:gd->mon_len = (ulong)&__bss_end - (ulong)_start;     *gd->mon_len等于uboot.bin大小加上bss段的大小,_start为0     *从反汇编的setup_mon_len函数可知:(ulong)&__bss_end = 0x000c636c;     *所以,gd->mon_len = 0x000c636c;     */    setup_mon_len,    /* 1.在initf_malloc函数里,由于CONFIG_SYS_MALLOC_F_LEN没定义,     * 直接返回0,相当于一个空函数     * 2.initf_console_record函数,同理     */    initf_malloc,    initf_console_record,/* 空函数 */    arch_cpu_init,      /* 空函数 */ /* basic arch cpu dependent setup */    initf_dm,           /* 空函数 */    arch_cpu_init_dm,  /* 空函数 */    mark_bootstage,     /* 标记名字 *//* need timer, go after init dm */    board_early_init_f,  /* 设置系统时钟,设置各个GPIO引脚 */    timer_init,     /* initialize timer */    env_init,       /* 设置gd的成员,初始化环境变量 *//* initialize environment */    init_baud_rate,     /* initialze baudrate settings */    serial_init,        /* serial communications setup */    console_init_f,     /* stage 1 init of console */    display_options,    /* 打印uboot版本等信息 *//* say that we are here */    display_text_info,  /* 打印uboot代码信息 *//* show debugging info if required */    print_cpuinfo,      /* 打印uboot时钟频率信息 *//* display cpu info (and speed) */    announce_dram_init,  /* 打印“ DRAM:  ” */    /* TODO: unify all these dram functions? */    dram_init,      /* 设置gd->ram_size= 0x04000000(64MB) *//* configure available RAM banks */    setup_dest_addr, /* 将gd->relocaddr、gd->ram_top指向SDRAM最顶端 */    reserve_round_4k, /* gd->relocaddr 4KB对齐 */    reserve_mmu,  /* 预留16KB的MMU页表并且64KB对齐 */    reserve_trace,  /* 空函数 */    /*reserve_uboot的作用是在SDRAM预留存放u-boot的空间(加上bss段)     *gd->relocaddr -= gd->mon_len;      *gd->relocaddr &= ~(4096 - 1);      * gd->start_addr_sp = gd->relocaddr;     */    reserve_uboot,    /* reserve_malloc函数:     * gd->start_addr_sp = gd->start_addr_sp - TOTAL_MALLOC_LEN;      * 因为jz2440.h默认定义了CONFIG_ENV_ADDR,所以此时在include/common.h中     * 执行#define TOTAL_MALLOC_LEN (CONFIG_SYS_MALLOC_LEN + CONFIG_ENV_SIZE)     * 也就是TOTAL_MALLOC_LEN=4*1024*1024+0x10000=4MB+64KB     * 预留4MB+64KB MALLOC内存池      */    reserve_malloc,   /* reserve_board函数:    * gd->start_addr_sp -= sizeof(bd_t);  预留bd_t结构体空间,查看反汇编可知为80字节    * gd->bd = (bd_t *)gd->start_addr_sp; 指定重定位bd地址    * memset(gd->bd, '\0', sizeof(bd_t)); 清零     */       reserve_board,    /*setup_machine函数:     *gd->bd->bi_arch_number = CONFIG_MACH_TYPE; /* board id for Linux      */    setup_machine,    reserve_global_data,/* 预留gd结构体空间,查看反汇编可知为168字节。并设置gd->new_gd */    reserve_fdt,  /* 如果设置了gd->new_fdt则预留fdt设备树空间,这里没有设置,不用管 */    reserve_arch,/* 空函数 */   /* reserve_stacks函数:    * gd->start_addr_sp -= 16;    * gd->start_addr_sp &= ~0xf;    * return arch_reserve_stacks();这里调用的不是board_f.c里的arch_reserve_stacks函数    * 因为该函数被__weak修饰符声明,调用的是arch/arm/lib/stack.c里的arch_reserve_stacks函数    * gd->irq_sp = gd->start_addr_sp;    * gd->start_addr_sp -= 16;    */    reserve_stacks,    setup_dram_config,/* 设置gd结构体的SDRAM地址与大小 */    show_dram_config,/* 打印SDRAM信息 */    display_new_sp, /* 打印新的栈地址 */    reloc_fdt, /*没有设置设备树,忽略*/    /*setup_reloc函数:     *gd->reloc_off = gd->relocaddr - CONFIG_SYS_TEXT_BASE;计算重定位地址与链接地址偏移值,CONFIG_SYS_TEXT_BASE在jz2440.h定义为0,gd->reloc_off = gd->relocaddr     *memcpy(gd->new_gd, (char *)gd, sizeof(gd_t));     *把旧的gd复制到新的gd地址里     */    setup_reloc,    NULL,};

(2) 从上面的initsequencef数组,可以找到串口初始化相关的函数serial_init,在drivers/serial/serial.c找到该函数的代码如下:

int serial_init(void){    gd->flags |= GD_FLG_SERIAL_READY;    return get_current()->start();}

同样在drivers/serial/serial.c可以找到get_current函数,如下所示:

static struct serial_device *get_current(void){    struct serial_device *dev;    if (!(gd->flags & GD_FLG_RELOC))        dev = default_serial_console();    else if (!serial_current)        dev = default_serial_console();    else        dev = serial_current;    /* We must have a console device */    if (!dev) {#ifdef CONFIG_SPL_BUILD        puts("Cannot find console\n");        hang();#else        panic("Cannot find console\n");#endif    }    return dev;}

getcurrent函数可知,它返回的是一个serialdevice 结构体,该结构如下:

struct serial_device {    /* enough bytes to match alignment of following func pointer */    char    name[16];    int (*start)(void);    int (*stop)(void);    void    (*setbrg)(void);    int (*getc)(void);    int (*tstc)(void);    void    (*putc)(const char c);    void    (*puts)(const char *s);#if CONFIG_POST & CONFIG_SYS_POST_UART    void    (*loop)(int);#endif    struct serial_device    *next;};

所以,serialinit函数是通过调用serialdevice结构体的start成员函数来初始化串口的。

(3) 从第二节的uboot启动过程分析可以知道,gd->flags是uboot是否重定位的标记,在执行 boardinitf函数时,代码还没开始重定位 ,此时gd->flags=0,所以执行的是if (!(gd->flags & GD_FLG_RELOC))分支里的dev = default_serial_console();。 在drivers/serial/serials3c24x0.c可以找到defaultserial_console函数,代码如下:

__weak struct serial_device *default_serial_console(void){#if defined(CONFIG_SERIAL1)    return &s3c24xx_serial0_device;#elif defined(CONFIG_SERIAL2)    return &s3c24xx_serial1_device;#elif defined(CONFIG_SERIAL3)    return &s3c24xx_serial2_device;#else#error "CONFIG_SERIAL? missing."#endif}

由于CONFIGSERIAL1在jz2440.h文件被定义,所以该函数返回的是s3c24xxserial0device结构体,搜索s3c24xxserial0device可以在drivers/serial/serials3c24x0.c发现s3c24xx_serial0_device =INIT_S3C_SERIAL_STRUCTURE(0, "s3ser0");,如下图所示:

那么我们继续往下看,看看INITS3CSERIAL_STRUCTURE是如何定义的,如下图所示:

原来它是一个宏, 以上的“##”连接符在编译时被去掉,连接符内的变量被替代,最终s3c24xxserial0device被定义成如下:

s3c24xx_serial0_device = {    \    .name   = "s3ser0",             \    .start  = s3serial0_init,       \    .stop   = NULL,                 \    .setbrg = s3serial0_setbrg,     \    .getc   = s3serial0_getc,       \    .tstc   = s3serial0_tstc,       \    .putc   = s3serial0_putc,       \    .puts   = s3serial0_puts,       \}

对于函数声明的宏DECLARE_S3C_SERIAL_FUNCTIONS(0);,展开后代码如下:

    int s3serial0_init(void)     {         return serial_init_dev(0);     }     void s3serial0_setbrg(void)     {         serial_setbrg_dev(0);     }     int s3serial0_getc(void)     {         return serial_getc_dev(0);     }     int s3serial0_tstc(void)     {         return serial_tstc_dev(0);     }     void s3serial0_putc(const char c)     {         serial_putc_dev(0, c);     }     void s3serial0_puts(const char *s)     {         serial_puts_dev(0, s);     }

由此可见,宏DECLARE_S3C_SERIAL_FUNCTIONS(0);一下子就声明了多个函数,现在最关心的是s3serial0_init函数,它调用serial_init_dev函数,该函数的代码如下:

/* Initialise the serial port. The settings are always 8 data bits, no parity, * 1 stop bit, no start bits. */static int serial_init_dev(const int dev_index){    /* 得到ULCON0控制寄存器基地址0x50000000 */    struct s3c24x0_uart *uart = s3c24x0_get_base_uart(dev_index);    /* FIFO enable, Tx/Rx FIFO clear */    writel(0x07, &uart->ufcon);    writel(0x0, &uart->umcon);    /* Normal,No parity,1 stop,8 bit */    writel(0x3, &uart->ulcon);    /*     * tx=level,rx=edge,disable timeout int.,enable rx error int.,     * normal,interrupt or polling     */    writel(0x245, &uart->ucon);    _serial_setbrg(dev_index); /*设置波特率*/    return (0);}

串口输出乱码,很大原因是波特率设置不正确,所以我们需要了解 serialsetbrg函数是如何设置波特率的,serialsetbrg函数的代码如下:

static void _serial_setbrg(const int dev_index){    struct s3c24x0_uart *uart = s3c24x0_get_base_uart(dev_index);    unsigned int reg = 0;    int i;    /* value is calculated so : (int)(PCLK/16./baudrate) -1 */    reg = get_PCLK() / (16 * gd->baudrate) - 1;    writel(reg, &uart->ubrdiv);    for (i = 0; i < 100; i++)        /* Delay */ ;}

在这里终于看到有时钟相关的函数:get_PCLK() ,然后跳转到 arch/arm/cpu/arm920t/s3c24x0/speed.c 文件的get_PCLK() ,代码如下:

/* return PCLK frequency */ulong get_PCLK(void){    const uint8_t pclk_divisors[] = { 1, 2, 4, 8 };    struct syscon_regs *syscon = (struct syscon_regs *)SYSCON_BASE;    const uint32_t clkset1 = readl(&syscon->clkset1);    const uint8_t pclk_div =        pclk_divisors[(clkset1 >> SYSCON_CLKSET1_PCLK_DIV_SHIFT) & 3];    const ulong pclk_rate = get_HCLK() / pclk_div;    return pclk_rate;}

从上面的代码看,PCLK通过HCLK频得到的,那么继续看get_HCLK函数,代码如下图所示:

从上图中可以发现#ifdef CONFIG_S3C2440 这一句是黑色的,说明没有定义这个CONFIG_S3C2440; 处理措施:include/configs/jz2440.h中去掉CONFIGS3C2410 ,换成CONFIGS3C2440,如下所示:

然后,执行如下命令重新编译:

make distcleanmake jz2440_defconfigmake

编译成功,重新烧写到开发的Nor Flash,重启开发板,串口输出信息如下图所示:

从上图可知,串口终于可以正常输出了,但是Flash和NAND都是显示0,接下来会继续修改代码使uboot支持Flash和NAND,这一节就到此结束。

标签: #汇编语言jz指令应用实例