Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1951322
  • 博文数量: 185
  • 博客积分: 10707
  • 博客等级: 上将
  • 技术积分: 1777
  • 用 户 组: 普通用户
  • 注册时间: 2008-09-19 17:31
文章分类

全部博文(185)

文章存档

2014年(1)

2012年(6)

2011年(27)

2010年(13)

2009年(75)

2008年(63)

分类: LINUX

2008-12-20 22:39:01

--------------------------------------------
本文系本站原创,欢迎转载!
转载请注明出处:http://zhiqiang0071.cublog.cn
--------------------------------------------

针对该time.c的硬件手册是SPRUE26.pdf,可到TI的网站()上下载,或直接在谷歌里搜索。
以下是文件
time.c的浅析。

1.time.c

/*
 * linux/arch/arm/mach-davinci/time.c
 *
 * DaVinci timer subsystem
 *
 * Author: MontaVista Software, Inc.
 *
 * Copyright 2005 (c) MontaVista Software, Inc. This file is licensed
 * under the terms of the GNU General Public License version 2. This
 * program is licensed "as is" without any warranty of any kind,
 * whether express or implied.
 *
 */

#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/time.h>
#include <linux/timex.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/hrtime.h>

#include <asm/io.h>
#include <asm/hardware.h>
#include <asm/system.h>
#include <asm/leds.h>
#include <asm/irq.h>
#include <asm/mach/irq.h>
#include <asm/mach/time.h>

#include <asm/arch/timex.h>
#include <asm/arch/irqs.h>
#include <asm/errno.h>
#include <linux/hrtime.h>        /* for cycles-to-nsec macros */
#include <asm/arch/cpu.h>

/*
 DM644x平台共有三个64位定时器,T1和T2是通用定时器,可以分别拆分为2个32位的定时器,
 能被ARM和DSP使用。T3定时器是看门狗,只能被ARM使用。
 T0可以使用外部时钟来计数,T1定时器只能使用内部时钟源,也就是DM644x平台的27MHZ输入时钟。
 T1可以有最大8分频(27MHZ/8),而T0没有。
 所有定时器都有三种运行模式,一次定时模式(One-time operation)、
 连续定时模式(Continuous operation)和禁止模式。
 
*/

/* T0 and T1 can be divided into 2 - 32 bit timer; however, T2 (watchdog
timer) can only be used as a single 64-bit timer */

enum {
    T0_BOT = 0, T0_TOP, T1_BOT, T1_TOP, T2_WDT, MAX_TIMERS,
};
static int NUM_TIMERS;

#define IS_TIMER_TOP(id) ((id & 0x1))
#define IS_TIMER_BOT(id) (!IS_TIMER_TOP(id))

// 这些魔数在include/asm/arch/timex.h中定义

const unsigned int davinci_ck_rate[] = {
    DM644X_CLOCK_TICK_RATE,        // 27000000

    DM646X_CLOCK_TICK_RATE,        // 148500000

    DM355_CLOCK_TICK_RATE        // 24000000

};

// 针对DM646x平台

static int dm646x_timer_irqs[] = {
    IRQ_TINT0_TINT12,        
    IRQ_TINT0_TINT34,        
    IRQ_TINT1_TINT12,        
    IRQ_TINT1_TINT34,        
    IRQ_DM646X_WDINT,        
};

// 这些魔数在include/asm/arch/irqs.h中定义

static int davinci_timer_irqs[] = {
    IRQ_TINT0_TINT12,        // 32

    IRQ_TINT0_TINT34,        // 33

    IRQ_TINT1_TINT12,        // 34

    IRQ_TINT1_TINT34,        // 35

};
static int *timer_irqs;

/*
 * This driver configures the 2 64-bit DaVinci timers as 4 independent
 * 32-bit timers used as follows:
 *
 * T0_BOT: Timer 0, bottom: free-running counter, used for cycle counter
 * T0_TOP: Timer 0, top : high-res timer programmable timer
 * T1_BOT: Timer 1, bottom: reserved for DSP
 * T1_TOP: Timer 1, top : Linux system tick
 */

static int tid_system = T1_TOP;
static int tid_freerun = T0_BOT;
static int tid_hrt = T0_TOP;

/*
 timer regs
 定时器控制寄存器组映射到一个结构体中,方便操作
*/


typedef volatile struct davinci_timer_regs_s {
    unsigned int pid12;        /* 0x0 */
    unsigned int emumgt_clksped;    /* 0x4 */
    unsigned int gpint_en;        /* 0x8 */
    unsigned int gpdir_dat;        /* 0xC */
    unsigned int tim12;        /* 0x10 */
    unsigned int tim34;        /* 0x14 */
    unsigned int prd12;        /* 0x18 */
    unsigned int prd34;        /* 0x1C */
    unsigned int tcr;        /* 0x20 */
    unsigned int tgcr;        /* 0x24 */
    unsigned int wdtcr;        /* 0x28 */
    unsigned int tlgc;        /* 0x2C */ // DM644x平台不存在

    unsigned int tlmr;        /* 0x30 */ // DM644x平台不存在

} davinci_timer_regs_t;

// 定时器控制结构体

typedef struct davinci_timer_s {
    char *name;
    unsigned int id;
    unsigned long period;
    unsigned long opts;
    davinci_timer_regs_t *regs;
    struct irqaction irqaction;
} davinci_timer_t;

static davinci_timer_t *davinci_timers[MAX_TIMERS];

/* values for 'opts' field of davinci_timer_t */
#define TIMER_DISABLED 0x00
#define TIMER_ONE_SHOT 0x01
#define TIMER_CONTINUOUS 0x02

// 各定时器寄存器组的映射基地址(指针)数组

davinci_timer_regs_t *davinci_timer_base_index[] = {
    (davinci_timer_regs_t *)IO_ADDRESS(DAVINCI_TIMER0_BASE),
    (davinci_timer_regs_t *)IO_ADDRESS(DAVINCI_TIMER0_BASE),
    (davinci_timer_regs_t *)IO_ADDRESS(DAVINCI_TIMER1_BASE),
    (davinci_timer_regs_t *)IO_ADDRESS(DAVINCI_TIMER1_BASE),
    (davinci_timer_regs_t *)IO_ADDRESS(DAVINCI_WDOG_BASE),
};

#define davinci_timer_base(id) \
  ((id >= 0) && (id < MAX_TIMERS) ? \
    davinci_timer_base_index[id] : \
    davinci_timer_base_index[0])

// 设置系统定时器控制结构体

static int davinci_timer32_config(davinci_timer_t *t)
{
    davinci_timer_regs_t *regs = t->regs;
    u32 enamode_shift, reset_shift;
    int ret = 0;

    // 检查是低32位还是高32位定时器

    if (IS_TIMER_BOT(t->id)) {
        regs->prd12 = t->period;
        enamode_shift = 6;
        reset_shift = 0;
    } else {
        regs->prd34 = t->period;
        enamode_shift = 22;
        reset_shift = 1;
    }

    /*
     reset timer
     复位定时器
    */

    regs->tgcr &= ~(0x1 << reset_shift);

    /*
     Register interrupt
     向内核注册中断
     */

    if (t->irqaction.handler != NULL) {
        ret = setup_irq(timer_irqs[t->id], &t->irqaction);
    }

    /*
     Set enable mode
     设置定时器的运行模式
    */

    if (t->opts & TIMER_ONE_SHOT) {
        regs->tcr |= 0x1 << enamode_shift;
    } else if (t->opts & TIMER_CONTINUOUS) {
        regs->tcr |= 0x2 << enamode_shift;
    } else {        /* TIMER_DISABLED */
        regs->tcr &= ~(0x3 << enamode_shift);
    }

    /*
     unreset
     激活定时器
    */

    regs->tgcr |= (0x1 << reset_shift);

    return ret;
}

// 从寄存器中读取定时器的计数值

static inline u32 davinci_timer32_read(davinci_timer_t *t)
{
    davinci_timer_regs_t *regs = t->regs;

    if IS_TIMER_TOP
        (t->id) {
        return regs->tim34;
    } else {
        return regs->tim12;
    }
}

/*
 Last processed system timer interrupt
 系统中断服务例程。
 */

static unsigned long davinci_timer32_last = 0;
static irqreturn_t system_timer_interrupt(int irq, void *dev_id,
                     struct pt_regs *regs)
{
    unsigned long now, latency;

    write_seqlock(&xtime_lock);// 顺序锁

    now = davinci_timer32_read(davinci_timers[tid_freerun]);
    latency = davinci_timer32_read(davinci_timers[tid_system]);
    // 记当前时钟滴答时两个定时器计数值的差,以便后续获取当前时钟滴答后所增加的计数值

    davinci_timer32_last = now - latency;

    /*
     Do the Linux timer operations
     timer_tick()例程中主要做跟系统时钟相关的工作:
     调用profile_tick()监管内核代码
     调用do_set_rtc()同步外部时钟源(如网络时钟源),每11分钟写到CMOS RTC中
     在do_timer()中增加jiffies_64值和调用update_times()更新系统日期和时间
    */

    timer_tick(regs);
    write_sequnlock(&xtime_lock);

    return IRQ_HANDLED;
}

// 获取自最近一次时钟滴答后(由system_timer_interrupt()中断服务例程更新)所经历的微妙数

unsigned long davinci_gettimeoffset(void)
{
    unsigned long now, elapsed, nsec;

    now = davinci_timer32_read(davinci_timers[tid_freerun]);
    // 获取最近一次时钟滴答后所增加的计数值,一个计数值的时间是1/27000000

    elapsed = now - davinci_timer32_last;
    // 把计数值转换为纳秒数,没有使用除法,代之以乘法和移位,速度快很多

    nsec = arch_cycle_to_nsec(elapsed);
    return nsec / 1000;
}

// freerun定时器中断服务例程,什么也没做

static irqreturn_t freerun_interrupt(int irq, void *dev_id,
                 struct pt_regs *regs)
{
    /* TODO: keep track of roll-overs for 64-bit cycle-count */
    return IRQ_HANDLED;
}

// 读取freerun定时器计数值

cycles_t davinci_get_cycles(void)
{
    return davinci_timer32_read(davinci_timers[tid_freerun]);
}

#ifdef CONFIG_HIGH_RES_TIMERS
// 用于设定high-res定时器的定时时间并注册中断

int schedule_hr_timer_int(unsigned long ref_jiffies, int ref_cycles)
{
    unsigned long temp_cycles, jiffies_f = jiffies;
    davinci_timer_t *t = davinci_timers[tid_hrt];

    BUG_ON(ref_cycles < 0);

    /*
     * Get offset from last jiffy
     */

    temp_cycles = (ref_jiffies - jiffies_f) * arch_cycles_per_jiffy +
     ref_cycles - get_arch_cycles(jiffies_f);

    if ((long)(ref_jiffies - jiffies_f) <= 0 && (long)temp_cycles < 0)
        return -ETIME;

    t->period = temp_cycles;    // 计数值

    t->opts = TIMER_ONE_SHOT;    // 一次定时模式

    davinci_timer32_config(t);    // 配置和注册


    return 0;
}

// 获取自ref_jiffies后所增加的计数值

int get_arch_cycles(unsigned long ref_jiffies)
{
    extern unsigned long do_getmachinecycles(void);
    int ret;
    unsigned now;
    unsigned temp_jiffies;
    unsigned diff_jiffies;

    do {
        /* snapshot jiffies */
        temp_jiffies = jiffies;
        barrier();

        /* calculate cycles since the current jiffy */
        now = davinci_timer32_read(davinci_timers[tid_freerun]);
        ret = now - davinci_timer32_last;

        /* compensate for ref_jiffies in the past */
        if (unlikely(diff_jiffies = jiffies - ref_jiffies))
            ret += diff_jiffies * arch_cycles_per_jiffy;

        barrier();
        /* repeat if we didn't have a consistent view of the world */
    } while (unlikely(temp_jiffies != jiffies));

    return ret;
}

// high-res定时器的中断服务例程,用于调度高精度软定时器

static irqreturn_t
hr_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
    /*
     在include/linux/hrtime.h中又如下定义:
         #ifdef HRTIME_PER_CPU
        #define do_hr_timer_int() raise_softirq(HRTIMER_SOFTIRQ)
        #else
        extern struct tasklet_struct hrt_tasklet;
        #define do_hr_timer_int() tasklet_schedule(&hrt_tasklet)
        #endif
    在DM644x平台,HRTIME_PER_CPU没有被定义,所以该中断例程只是用于调度高精度hrt_tasklet
    软中断
    */

    do_hr_timer_int();    

    return IRQ_HANDLED;
}

static int hr_timer_init(void)
{
    int ret = 0;

    /* Initialized by init of davinci_timers[] array */

    return ret;
}

__initcall(hr_timer_init);
#endif                /* CONFIG_HIGH_RES_TIMERS */

static davinci_timer_t davinci_system_timer = {
    .name = "system tick",    // 系统定时器,也就是系统的“心脏”,负责推动系统运行

    .period = ((DM644X_CLOCK_TICK_RATE / HZ) - 1),    // 设置系统定时器滴答时间(10ms)

    .opts = TIMER_CONTINUOUS,    // 连续运行模式

    .irqaction = {
            // 快速无延迟中断,SA_NODELAY在打开内核抢占的情况下表明不使用内核线程处理中断

         .flags = SA_INTERRUPT | SA_NODELAY,    
         .handler = system_timer_interrupt,// 系统定时器中断处理例程

         }
};
static davinci_timer_t davinci_freerun_timer = {
    .name = "free-run counter",    
    .period = 0xffffffff,    // 设置成这么大就是为了使他计时尽量久,作为标准值计数来参考

    .opts = TIMER_CONTINUOUS,    // 连续运行模式

    .irqaction = {
         .flags = SA_INTERRUPT,    // 快速中断

         .handler = freerun_interrupt, // 中断处理例程

         }
};

#ifdef CONFIG_HIGH_RES_TIMERS
static davinci_timer_t davinci_hrt_timer = {
    .name = "high-res timer",    // 高精度定时器,用于调度高精度软定时器

    .opts = TIMER_DISABLED,        // 禁止运行模式,也就是不计数,但保留当前的值

    .period = 0,                // 暂时设置成0

    .irqaction = {
         .flags = SA_INTERRUPT | SA_NODELAY,//快速无延迟中断

         .handler = hr_timer_interrupt,// 中断处理例程

         }
};
#endif
static davinci_timer_t davinci_default_timer = {
    .name = NULL,
};

// DM6446平台定时器初始化例程

void __init davinci_timer_init(void)
{
    int i;
    davinci_timer_regs_t *t0 = davinci_timer_base(T0_BOT);
    davinci_timer_regs_t *t1 = davinci_timer_base(T1_BOT);
    davinci_timer_regs_t *t2 = davinci_timer_base(T2_WDT);

    // 针对DM6467平台

    if (cpu_is_davinci_dm6467()) {
        davinci_system_timer.period = (DM646X_CLOCK_TICK_RATE / HZ) - 1;
        timer_irqs = dm646x_timer_irqs;
        NUM_TIMERS = ARRAY_SIZE(dm646x_timer_irqs);
        /*
         * T0_BOT: Timer 0, bottom: AV Sync
         * T0_TOP: Timer 0, top: free-running counter,
         used for cycle counter
         * T1_BOT: Timer 1, bottom: reserved for DSP
         * T1_TOP: Timer 1, top : Linux system tick
         * T2_WDT: Timer 2, : high-res timer programmable timer
         */

        tid_system = T1_TOP;
        tid_freerun = T0_TOP;
        tid_hrt = T2_WDT;
    } else {
        if (cpu_is_davinci_dm355())
            davinci_system_timer.period =
             (DM355_CLOCK_TICK_RATE / HZ) - 1;

        timer_irqs = davinci_timer_irqs;
        NUM_TIMERS = ARRAY_SIZE(davinci_timer_irqs);
        /*
         * T0_BOT: Timer 0, bottom: free-running counter,
         used for cycle counter
         * T0_TOP: Timer 0, top : high-res timer programmable timer
         * T1_BOT: Timer 1, bottom: reserved for DSP
         * T1_TOP: Timer 1, top : Linux system tick
         */

        tid_system = T1_TOP;
        tid_freerun = T0_BOT;
        tid_hrt = T0_TOP;
    }
    for (i = 0; i < NUM_TIMERS; i++)
        davinci_timers[i] = &davinci_default_timer;

    // 设置系统定时器控制结构体

    davinci_timers[tid_system] = &davinci_system_timer;
    davinci_timers[tid_freerun] = &davinci_freerun_timer;

#ifdef CONFIG_HIGH_RES_TIMERS
    davinci_timers[tid_hrt] = &davinci_hrt_timer;
#endif

    /*
     Disabled, Internal clock source
     T0和T1采用内部时钟源,且被禁止
    */

    t0->tcr = 0x0;
    t1->tcr = 0x0;

    /*
     reset both timers, no pre-scaler for timer34
     T1禁止分频
    */

    t0->tgcr = 0;
    t1->tgcr = 0;

    /*
     Set both timers to unchained 32-bit
     T0和T1设置成非级联的32位定时器
    */

    t0->tgcr |= 0x4;    // TIMMODE = 1

    t1->tgcr |= 0x4;

    /*
     Unreset timers
     激活定时器T0和T1
    */

    t0->tgcr |= 0x3;
    t1->tgcr |= 0x3;

    /*
     Init both counters to zero
     计数寄存器置为0
    */

    t0->tim12 = 0;
    t0->tim34 = 0;
    t1->tim12 = 0;
    t1->tim34 = 0;

    /* do the same thing for timer 2 if cpu is dm6467 */
    if (cpu_is_davinci_dm6467()) {
        t2->tcr = 0x0;
        t2->tgcr = 0;

        /* T2 can only operate as a single 64-bit timer
         * t2->tgcr |= 0x4; */

        t2->tgcr |= 0x3;
        t2->tim12 = 0;
        t2->tim34 = 0;
    }

    for (i = 0; i < NUM_TIMERS; i++) {
        davinci_timer_t *t = davinci_timers[i];

        if (t->name) {
            t->id = i;
            t->regs =    // 映射各个定时器的寄存器组

             (davinci_timer_regs_t *) davinci_timer_base(t->id);
            t->irqaction.name = t->name;    // 用户中断名称

            t->irqaction.dev_id = (void *)t;    // 用户中断私有数据    


            // 配置和注册时钟中断

            davinci_timer32_config(t);
        }
    }
}

/*
 这个结构体在arch/arm/mach-davinci文件中被调用,保存在机器描述符里(struct machine_desc),
 在系统启动时会调用davinci_timer_init()例程初始化定时器。调用的过程是:
 start_kernel()-->setup_arch()-->system_timer = mdesc->timer(system_timer是个全局
 指针变量,mdesc->timer指针指向的就是本文中的已经注册到机器描述符里的davinci_timer结构体),
 然后便是start_kernel()-->time_init()-->system_timer->init()(也就是
 davinci_timer_init())。
 从上可以看出经历了两个过程,才调用davinci_timer_init()例程来初始化定时器。
*/

struct sys_timer davinci_timer = {
    .init = davinci_timer_init,
    .offset = davinci_gettimeoffset,
};

/*
 使用看门狗复位系统.
 用户使用重启命令reboot后,会调用到该例程,具体过程是:sys_reboot()-->machine_restart()
 -->arch_reset()-->davinci_watchdog_reset()。
*/

void davinci_watchdog_reset(void)
{
    davinci_timer_regs_t *davinci_wdt =
        (davinci_timer_regs_t *)IO_ADDRESS(DAVINCI_WDOG_BASE);

    davinci_wdt->tcr = 0x0;        /* disable timer */
    davinci_wdt->tgcr = 0x0;    /* reset timer */
    davinci_wdt->tgcr = 0x8;    /* configure timer2 as 64-bit */
    davinci_wdt->tgcr |= 0x3;    /* release timer from reset */
    davinci_wdt->tim12 = 0;        /* clear counter and period regs */
    davinci_wdt->tim34 = 0;
    davinci_wdt->prd12 = 0;
    davinci_wdt->prd34 = 0;
    davinci_wdt->wdtcr |= 0x4000;    /* enable watchdog timer */

    /* put watchdog in pre-active state */
    davinci_wdt->wdtcr = 0xA5C64000;

    /* put watchdog in active state */
    davinci_wdt->wdtcr = 0xDA7E4000;

    /*
     write an invalid value to the WDKEY field to trigger
     a watchdog reset
     如果要喂看门狗,则使用:
     davinci_wdt->wdtcr = 0xA5C64000;
     davinci_wdt->wdtcr = 0xDA7E4000;
     必须成对顺序使用。
    */

    davinci_wdt->wdtcr = 0x00004000;
}

// 读取定时器clock_id的计数值

u32 davinci_timer_read(int clock_id)
{
    davinci_timer_t *t = davinci_timers[clock_id];    // 获取该定时器的控制结构体


    return davinci_timer32_read(t);
}


2.timex.h

/*
 * linux/include/asm-arm/arch-davinci/timex.h
 *
 * BRIEF MODULE DESCRIPTION
 * DAVINCI Virtual memofy definitions
 *
 * Copyright (C) 2006 Texas Instruments.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or (at your
 * option) any later version.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
 * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */


#ifndef __ASM_ARCH_TIMEX_H
#define __ASM_ARCH_TIMEX_H


#include <asm/arch/cpu.h>

/* The source frequency for the timers is the 27MHz MXI clock */
#define CLOCK_TICK_RATE                24000000
#define DM644X_CLOCK_TICK_RATE            27000000
#define DM646X_CLOCK_TICK_RATE            148500000
#define DM355_CLOCK_TICK_RATE            24000000

#define DAVINCI_CLOCK_TICK_RATE ((cpu_is_davinci_dm6467()) ? \
        DM646X_CLOCK_TICK_RATE : ((cpu_is_davinci_dm644x()) ? \
        DM644X_CLOCK_TICK_RATE : DM355_CLOCK_TICK_RATE))

extern void davinci_watchdog_reset(void);
extern cycles_t davinci_get_cycles(void);
static inline cycles_t get_cycles(void)
{
    return davinci_get_cycles();
}

#endif                /* __ASM_ARCH_TIMEX_H__ */

阅读(2875) | 评论(1) | 转发(1) |
给主人留下些什么吧!~~

chinaunix网友2009-09-17 05:19:10

It is cool, very clear! thanks