/* * System Timer Interrupt reconfigured to run in free-run mode. * Author: Vitaly Wool * Copyright 2004 MontaVista Software Inc. * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. */ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ /*! * @file time.c * @brief This file contains OS tick and wdog timer implementations. * * This file contains OS tick and wdog timer implementations. * * @ingroup Timers */ #include #include #include #include #include #include #include #include /*! * This is the timer interrupt service routine to do required tasks. * It also services the WDOG timer at the frequency of twice per WDOG * timeout value. For example, if the WDOG's timeout value is 4 (2 * seconds since the WDOG runs at 0.5Hz), it will be serviced once * every 2/2=1 second. * * @param irq GPT interrupt source number (not used) * @param dev_id this parameter is not used * @return always returns \b IRQ_HANDLED as defined in * include/linux/interrupt.h. */ static irqreturn_t mxc_timer_interrupt(int irq, void *dev_id) { unsigned int next_match; if (__raw_readl(MXC_GPT_GPTSR) & GPTSR_OF1) { do { timer_tick(); next_match = __raw_readl(MXC_GPT_GPTOCR1) + LATCH; __raw_writel(GPTSR_OF1, MXC_GPT_GPTSR); __raw_writel(next_match, MXC_GPT_GPTOCR1); } while ((signed long)(next_match - __raw_readl(MXC_GPT_GPTCNT)) <= 0); } return IRQ_HANDLED; } /*! * This function is used to obtain the number of microseconds since the last * timer interrupt. Note that interrupts is disabled by do_gettimeofday(). * * @return the number of microseconds since the last timer interrupt. */ static unsigned long mxc_gettimeoffset(void) { unsigned long ticks_to_match, elapsed, usec, tick_usec, i; /* Get ticks before next timer match */ ticks_to_match = __raw_readl(MXC_GPT_GPTOCR1) - __raw_readl(MXC_GPT_GPTCNT); /* We need elapsed ticks since last match */ elapsed = LATCH - ticks_to_match; /* Now convert them to usec */ /* Insure no overflow when calculating the usec below */ for (i = 1, tick_usec = tick_nsec / 1000;; i *= 2) { tick_usec /= i; if ((0xFFFFFFFF / tick_usec) > elapsed) break; } usec = (unsigned long)(elapsed * tick_usec) / (LATCH / i); return usec; } /*! * The OS tick timer interrupt structure. */ static struct irqaction timer_irq = { .name = "MXC Timer Tick", .flags = IRQF_DISABLED | IRQF_TIMER, .handler = mxc_timer_interrupt }; /*! * This function is used to initialize the GPT to produce an interrupt * based on HZ. It is called by start_kernel() during system startup. */ void __init mxc_init_time(void) { u32 reg, v; reg = __raw_readl(MXC_GPT_GPTCR); reg &= ~GPTCR_ENABLE; __raw_writel(reg, MXC_GPT_GPTCR); reg |= GPTCR_SWR; __raw_writel(reg, MXC_GPT_GPTCR); while ((__raw_readl(MXC_GPT_GPTCR) & GPTCR_SWR) != 0) cpu_relax(); reg = GPTCR_FRR | GPTCR_CLKSRC_HIGHFREQ; __raw_writel(reg, MXC_GPT_GPTCR); /* TODO: get timer rate from clk driver */ v = 66500000; __raw_writel((v / CLOCK_TICK_RATE) - 1, MXC_GPT_GPTPR); if ((v % CLOCK_TICK_RATE) != 0) { pr_info("\nWARNING: Can't generate CLOCK_TICK_RATE at %d Hz\n", CLOCK_TICK_RATE); } pr_info("Actual CLOCK_TICK_RATE is %d Hz\n", v / ((__raw_readl(MXC_GPT_GPTPR) & 0xFFF) + 1)); reg = __raw_readl(MXC_GPT_GPTCNT); reg += LATCH; __raw_writel(reg, MXC_GPT_GPTOCR1); setup_irq(MXC_INT_GPT, &timer_irq); reg = __raw_readl(MXC_GPT_GPTCR); reg = GPTCR_FRR | GPTCR_CLKSRC_HIGHFREQ | GPTCR_STOPEN | GPTCR_DOZEN | GPTCR_WAITEN | GPTCR_ENMOD | GPTCR_ENABLE; __raw_writel(reg, MXC_GPT_GPTCR); __raw_writel(GPTIR_OF1IE, MXC_GPT_GPTIR); } struct sys_timer mxc_timer = { .init = mxc_init_time, .offset = mxc_gettimeoffset, };