52c543f90c
This patch adds the foundation pieces for the Freescale MXC platforms, including i.MX2 and i.MX3 based systems. The bare-bones MX31 support in this patch boots to the rootdev panic with 8250 serial console configured "console=ttyS0,115200". It assumes that Redboot is the boot loader. Signed-off-by: Quinn Jensen <quinn.jensen@freescale.com> Acked-by: Lennert Buytenhek <buytenh@wantstofly.org> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
153 lines
4.0 KiB
C
153 lines
4.0 KiB
C
/*
|
|
* 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,
|
|
};
|