/* * 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_TIMERSstatic 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) ; }