Davinci DM6446 Linux 内核分析——time.c

#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.
  *
http://www.61ic.com/Article/DaVinci/DM644X/201201/40300.html  

 
#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__ */

你可能感兴趣的:(linux,内核,Davinci,DM6446,time.c)