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