uboot中timer定时器的设置

来源:互联网 发布:ubuntu ssh 编辑:程序博客网 时间:2024/06/05 08:57

uboot中timer定时器的设置

fulinux

转载声明:http://blog.csdn.net/fulinus/article/details/8688769

uboot中timer定时器的设置,uboot汇编部分没有对timer初始化,而是在C中。在C入口函数void start_armboot(void)中,首先是通过如下代码方式调用:

typedef int (init_fnc_t) (void);init_fnc_t *init_sequence[] = {                                                                                                                 #if defined(CONFIG_ARCH_CPU_INIT)    arch_cpu_init,      /* basic arch cpu dependent setup */#endif    board_init,     /* basic board dependent setup */#if defined(CONFIG_USE_IRQ)    interrupt_init,     /* set up exceptions */#endif    timer_init,     /* initialize timer */#ifdef CONFIG_FSL_ESDHC    get_clocks,#endif    env_init,       /* initialize environment */    init_baudrate,      /* initialze baudrate settings */    serial_init,        /* serial communications setup */    console_init_f,     /* stage 1 init of console */    display_banner,     /* say that we are here */#if defined(CONFIG_DISPLAY_CPUINFO)    print_cpuinfo,      /* display cpu info (and speed) */#endif#if defined(CONFIG_DISPLAY_BOARDINFO)    checkboard,     /* display board info */#endif#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)    init_func_i2c,#endif    dram_init,      /* configure available RAM banks */#if defined(CONFIG_CMD_PCI) || defined (CONFIG_PCI)    arm_pci_init,#endif    display_dram_config,    NULL,};


这里是u-boot的C代码部分的入口:

void start_armboot (void){    init_fnc_t **init_fnc_ptr;              。              。              。       for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {        if ((*init_fnc_ptr)() != 0) {            hang ();        }    }

上面的函数调用很复杂,不是讨论这个所以暂时不深究,直接看它调用arch/arm/cpu/arm920t/s3c24x0/timer.c文件中的函数int timer_init (void),代码如下:

 

int timer_init(void){    struct s3c24x0_timers *timers = s3c24x0_get_base_timers();    ulong tmr;    /* use PWM Timer 4 because it has no output */    /* prescaler for Timer 4 is 16 */    writel(0x0f00, &timers->TCFG0);    if (timer_load_val == 0) {        /*         * for 10 ms clock period @ PCLK with 4 bit divider = 1/2         * (default) and prescaler = 16. Should be 10390         * @33.25MHz and 15625 @ 50 MHz         */        timer_load_val = get_PCLK() / (2 * 16 * 100);        timer_clk = get_PCLK() / (2 * 16);    }    /* load value for 10 ms timeout */    lastdec = timer_load_val;    writel(timer_load_val, &timers->TCNTB4);    /* auto load, manual update of Timer 4 */    tmr = (readl(&timers->TCON) & ~0x0700000) | 0x0600000;    writel(tmr, &timers->TCON);    /* auto load, start Timer 4 */    tmr = (tmr & ~0x0700000) | 0x0500000;    writel(tmr, &timers->TCON);    timestamp = 0;    return (0);}

以下是对函数的详细分析:

 1、先看数据结构s3c24x0_timers

struct s3c24x0_timers {    u32 TCFG0;    u32 TCFG1;    u32 TCON;    struct s3c24x0_timer    ch[4];    u32 TCNTB4;    u32 TCNTO4; };  

有引出来了另一个结构s3c24x0_timer

struct s3c24x0_timer {    u32 TCNTB;    u32 TCMPB;    u32 TCNTO;};

看下这两个结构是不是涵盖了所有timer的寄存器,对照s3c2440的datasheet得出结论是:timer寄存器地址空间从(0x51000000~0x51000040)依次对应的寄存器名是却是是上面的结构体中的寄存器,总共是17个寄存器.

为什么会有4个结构s3c24x0_timer呢?因为2440有5个timer,其中前四个timer0~timer3工作方式类似,都有外部引脚引出,而第五个timer4不同与他们,第一个就是它没有TCMPB寄存器,具体的区别可以看看这个文档或是datasheet:

http://wenku.baidu.com/view/6d765ec42cc58bd63186bd24.html

2、s3c24x0_get_base_timers()函数返回的就是timer寄存器空间的起始地址0x51000000

static inline struct s3c24x0_timers *s3c24x0_get_base_timers(void){       return (struct s3c24x0_timers *)S3C24X0_TIMER_BASE;}

3

 /* use PWM Timer 4 because it has no output */    /* prescaler for Timer 4 is 16 */

这个注释是不是有问题呢,我怎么查不到timer4预分频器是16位的,都是8位。是不是要表达计数器是16位的呢?

4、writel(0x0ff,&timers->TCFG0)的作用是什么呢?在arch/arm/include/asm/io.h文件中既然有3处定义了这个函数,考察了一下只有下面被编译:

#define writel(v,a)         __arch_putl(v,a)#define __arch_putl(v,a)        (*(volatile unsigned int *)(a) = (v))

分析:就是往a寄存器写入v.

这里就是往寄存器TCFG0写入0x0ff,先看下TCFG0寄存器信息:


 

 将timer0、1的预分频器的值写ff就是255,

        其值N为: 0~255
       输出频率为:PCLK ÷(N+1)

这时候N = 255,结合这篇文档http://blog.csdn.net/sonbai/article/details/8687858知道PCLK的频率是101.25MHz,输出频率就出来了,这时候最低,同时没有用到他们,所以不考虑了。t只考虑imer4,这时候N = 0,输出频率最大就是PCLK。为什么要设最大,先放着

5、timer_load_val是一个全局变量,顾名思义它是装入TCNTB4寄存器的值。看下面的注释:

        /*         * for 10 ms clock period @ PCLK with 4 bit divider = 1/2         * (default) and prescaler = 16. Should be 10390         * @33.25MHz and 15625 @ 50 MHz         */


它的意思是如果要将timer4的时钟周期设置为10ms的话,要通过PCLK、分配器divider默认的值1/2和prescaler = 16来设置如何设置(?)来看一下下面这个函数

/* return PCLK frequency */ulong get_PCLK(void){       struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power();        return (readl(&clk_power->CLKDIVN) & 1) ? get_HCLK() / 2 : get_HCLK();}   

看看结构s3c24x0_clock_power

/* CLOCK & POWER MANAGEMENT (see S3C2400 manual chapter 6) *//*                          (see S3C2410 manual chapter 7) */struct s3c24x0_clock_power {    u32 LOCKTIME;    u32 MPLLCON;    u32 UPLLCON;    u32 CLKCON;    u32 CLKSLOW;    u32 CLKDIVN;#if defined(CONFIG_S3C2440)    U32 CAMDIVN;#endif};  

涵盖了所有时钟设置的寄存器,s3c24x0_get_base_clock_power();返回的是(struct s3c24x0_clock_power *)0x4C000000也就是时钟设置寄存器的起始地址

return (readl(&clk_power->CLKDIVN) & 1) ? get_HCLK() / 2 : get_HCLK();

判断一下CLKDIVN是不是被设置成了1,如果是说明PCLK = 1/2HCLK,不在去看get_HCLK了。前面我已经说过PCLK = 101.25MHz,至于timer_load_val = 101.25MHz / (2*16*100);这个公式,分析如下:

我们要设置timer4的时钟周期是T = 10ms;

又  T = [1 /(PCLK / (预分频器的值(prescaler)*分频器的值(div)))] *timer_load_val

已知T、PCLK、perscaler、div,就可以求的

timer_load_val = PCLK / (perscaler * div)

但是这里为什么会有100呢?(暂时不清楚,望大家给我一个解释)
timer_clk下面暂时没有遇到,先不考虑。

6

 /* load value for 10 ms timeout */    lastdec = timer_load_val;    writel(timer_load_val, &timers->TCNTB4);

然后将timer_load_val的值写入TCNTB4寄存器中

7、下面是要配置TCON寄存器,也就是timer控制寄存器

 /* auto load, manual update of Timer 4 */    tmr = (readl(&timers->TCON) & ~0x0700000) | 0x0600000;    writel(tmr, &timers->TCON);

先来看看TCON寄存器信息:


TCON现实与(~0x0700000)与运算,即TCON = TCON & F8FFFFF,也就是将TCON的20、21、21这几个位清零,然后TCON = TCON | 0X0600000将位21、20设置为1,对照datasheet知道将timer4设置为自动装载,和手动更新.这就有一个矛盾既然自动转载干嘛还要手动更新呢?看下8就知道了。writel()函数已经介绍过了。

8、你看下面这个代码就很有趣,他和上面的那个代码很类似

 /* auto load, start Timer 4 */    tmr = (tmr & ~0x0700000) | 0x0500000;    writel(tmr, &timers->TCON);

起始2440定时器第一次往TCONTB装入初始值是要手动更新的,如果没有更新初值自动更新那更新什么呢,他不知道,TCONTB默认是0,更新0有什么用呢。所以需要手动更新,然后关掉手动更新,设置好后,再启动timer4就完成了timer4的设置和运行。

9、timestamp = 0,这句我们暂时还没涉及到,他是后面要用的。

 

10、然后返回

return (0);


 

 

 

 

 

 

原创粉丝点击