| /* |
| * 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 <linux/module.h> |
| #include <linux/init.h> |
| #include <linux/interrupt.h> |
| #include <linux/irq.h> |
| #include <asm/hardware.h> |
| #include <asm/mach/time.h> |
| #include <asm/io.h> |
| #include <asm/arch/common.h> |
| |
| /*! |
| * 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; |
| |
| write_seqlock(&xtime_lock); |
| |
| 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); |
| } |
| |
| write_sequnlock(&xtime_lock); |
| |
| 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, |
| }; |