Recently, I write a linux module to generate interrupt every 20us using watch dog. I use the global timer to test whether the interval between two interrupts is 20us. But I find the result is greater than 20us. So I change the value of watchdog counter in the interrupt function to regulate the error if the error is bigger enough. After I add the code of regulating error, the result is better than before in most interrupts, while there still exist some huge error between two interrpts, and the error is much lager than 20us.
Thank you for reading my question, I hope it can be solved as soon as possible.
This is interrupt handler code:
static irqreturn_t wd_interrupt(int irq, void *dev_id) {
long long regulate_value;
long load_value;
long long err;
tick_start = read_global_timer();
//the cmp_cycle is the value of global timer if the interrupt are not delayed
cmp_cycle += (long long)wd_load;
err = tick_start - cmp_cycle;
//if the err is biger than cmp_err, I will write a new value to watch dog to eliminate the error
if(err > cmp_err)
{
regulate_value = (long long)wd_load - err;
load_value = least_load;
// if the err is very big, the regulate_value may be too small
if(regulate_value > (long long)load_value)
load_value = (long)regulate_value;
__raw_writel(load_value, twd_base + TWD_WDOG_COUNTER);
}
if(err > max_err)
max_err = err;
return IRQ_HANDLED;
}
this is my code to start the watch dog and global timer
static int kthread_init(void *arg) {
unsigned long ctl;
int err;
if((err = request_irq(30, wd_interrupt, 0, NULL, NULL)) < 0)
{
printk("request_irq err:%d\n",err);
return 0;
}
gt_base = ioremap((OMAP44XX_LOCAL_TWD_BASE - 0X400), SZ_256);
cmp_cycle = 0;
// wd_load = twd_timer_rate / 50000; //20us
wd_load = twd_timer_rate / 500; //2000us
cmp_err = 1000;
least_load = wd_load - 2000;
tick_start = 0;
max_err = 0;
//init the global timer
__raw_writel(0x00, gt_base + GLOBAL_TIMER_CONTROL);
__raw_writel(0x00, gt_base + GLOBAL_TIMER_COUNTER_LOW);
__raw_writel(0x00, gt_base + GLOBAL_TIMER_COUNTER_UPPER);
//switch the watch dog to timer mode
__raw_writel(0x12345678, twd_base + TWD_WDOG_DISABLE);
__raw_writel(0x87654321, twd_base + TWD_WDOG_DISABLE);
//write the watch dog load register
__raw_writel(wd_load, twd_base + TWD_WDOG_LOAD);
//write the watch dog control register
ctl = TWD_TIMER_CONTROL_ENABLE | TWD_TIMER_CONTROL_IT_ENABLE | TWD_TIMER_CONTROL_PERIODIC;
//start the watch dog and global timer
__raw_writel(ctl, twd_base + TWD_WDOG_CONTROL);
__raw_writel(0x01, gt_base + GLOBAL_TIMER_CONTROL);
set_current_state(TASK_INTERRUPTIBLE);
schedule();
while(!kthread_should_stop())
{
set_current_state(TASK_INTERRUPTIBLE);
schedule();
}
__raw_writel(0x0, twd_base + TWD_WDOG_CONTROL);
printk("wd_load:%lu,max_err:%lld\n",wd_load,max_err);
return 0;
}