|  | /* | 
|  | * 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, | 
|  | }; |