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