0

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;
 }

2 Answers2

0

My guess is that your handler together with all the Linux wrapping code can't finish running in 20us, so the following interrupts get delayed. Try increasing the delay and see if that helps.

Igor Skochinsky
  • 24,629
  • 2
  • 72
  • 109
  • I increased the interrupt interval up to 2000us, but the error still exist and the max error is about 100us. And The interrupt interval I want is 20us. the cost of interrupt handler is not more than 100 cycles. – Yu Shixiang Jun 11 '13 at 02:15
0

I think what you are seeing is other interrupts happening. When for example the network card sends an interrupt the kernel enters interrupt mode and process the network interrupt. When it returns from that the cpu sees that your interrupt is pending and triggers that. But you've lost the time the network interrupt took.

You would have to change any other interrupt handler that takes longer than say 10us to reenable interrupts and run the actual handler in SYS/SVC mode.

Goswin von Brederlow
  • 11,875
  • 2
  • 24
  • 42