static int imx_set_next_event(unsigned long evt, struct clock_event_device *unused) { unsigned long tcmp; tcmp = IMX_TCN(TIMER_BASE) + evt; IMX_TCMP(TIMER_BASE) = tcmp; return (int32_t)(tcmp - IMX_TCN(TIMER_BASE)) < 0 ? -ETIME : 0; }
static void imx_set_mode(enum clock_event_mode mode, struct clock_event_device *evt) { unsigned long flags; /* * The timer interrupt generation is disabled at least * for enough time to call imx_set_next_event() */ local_irq_save(flags); /* Disable interrupt in GPT module */ IMX_TCTL(TIMER_BASE) &= ~TCTL_IRQEN; if (mode != clockevent_mode) { /* Set event time into far-far future */ IMX_TCMP(TIMER_BASE) = IMX_TCN(TIMER_BASE) - 3; /* Clear pending interrupt */ IMX_TSTAT(TIMER_BASE) &= ~TSTAT_COMP; } #ifdef DEBUG printk(KERN_INFO "imx_set_mode: changing mode from %s to %s\n", clock_event_mode_label[clockevent_mode], clock_event_mode_label[mode]); #endif /*DEBUG*/ /* Remember timer mode */ clockevent_mode = mode; local_irq_restore(flags); switch (mode) { case CLOCK_EVT_MODE_PERIODIC: printk(KERN_ERR "imx_set_mode: Periodic mode is not supported for i.MX\n"); break; case CLOCK_EVT_MODE_ONESHOT: /* * Do not put overhead of interrupt enable/disable into * imx_set_next_event(), the core has about 4 minutes * to call imx_set_next_event() or shutdown clock after * mode switching */ local_irq_save(flags); IMX_TCTL(TIMER_BASE) |= TCTL_IRQEN; local_irq_restore(flags); break; case CLOCK_EVT_MODE_SHUTDOWN: case CLOCK_EVT_MODE_UNUSED: case CLOCK_EVT_MODE_RESUME: /* Left event sources disabled, no more interrupts appears */ break; } }
/* * IRQ handler for the timer */ static irqreturn_t imx_timer_interrupt(int irq, void *dev_id) { uint32_t tstat; /* clear the interrupt */ tstat = IMX_TSTAT(TIMER_BASE); IMX_TSTAT(TIMER_BASE) = 0; if (tstat & TSTAT_COMP) { do { write_seqlock(&xtime_lock); timer_tick(); write_sequnlock(&xtime_lock); IMX_TCMP(TIMER_BASE) += evt_diff; } while (unlikely((int32_t)(IMX_TCMP(TIMER_BASE) - IMX_TCN(TIMER_BASE)) < 0)); } return IRQ_HANDLED; }
/* * Returns number of us since last clock interrupt. Note that interrupts * will have been disabled by do_gettimeoffset() */ static unsigned long imx_gettimeoffset(void) { unsigned long ticks; /* * Get the current number of ticks. Note that there is a race * condition between us reading the timer and checking for * an interrupt. We get around this by ensuring that the * counter has not reloaded between our two reads. */ ticks = IMX_TCN(TIMER_BASE); /* * Interrupt pending? If so, we've reloaded once already. */ if (IMX_TSTAT(TIMER_BASE) & TSTAT_COMP) ticks += LATCH; /* * Convert the ticks to usecs */ return (1000000 / CLK32) * ticks; }
cycle_t imx_get_cycles(void) { return IMX_TCN(TIMER_BASE); }