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; }
/* * Set up timer hardware into expected mode and state. */ static void __init imx_timer_hardware_init(void) { /* * Initialise to a known state (all timers off, and timing reset) */ IMX_TCTL(TIMER_BASE) = 0; IMX_TPRER(TIMER_BASE) = 0; IMX_TCMP(TIMER_BASE) = LATCH - 1; IMX_TCTL(TIMER_BASE) = TCTL_FRR | TCTL_CLK_PCLK1 | TCTL_IRQEN | TCTL_TEN; evt_diff = LATCH; }
/* * 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; }
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; } }
/* * Set up timer interrupt, and return the current time in seconds. */ static void __init imx_timer_init(void) { /* * Initialise to a known state (all timers off, and timing reset) */ IMX_TCTL(TIMER_BASE) = 0; IMX_TPRER(TIMER_BASE) = 0; IMX_TCMP(TIMER_BASE) = LATCH - 1; IMX_TCTL(TIMER_BASE) = TCTL_CLK_32 | TCTL_IRQEN | TCTL_TEN; /* * Make irqs happen for the system timer */ setup_irq(TIM1_INT, &imx_timer_irq); }