2f2c267989
Reduce the function pointer mess of the m68knommu timer code by calling directly to the local hardware's timer setup, and expose the local common timer interrupt handler to the lower level hardware timer. Ultimately this will save definitions of all these functions across all the platform code to setup the function pointers (which for any given m68knommu CPU family member can be only one set of hardware timer functions). Signed-off-by: Greg Ungerer <gerg@uclinux.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
156 lines
4.4 KiB
C
156 lines
4.4 KiB
C
/***************************************************************************/
|
|
|
|
/*
|
|
* timers.c -- generic ColdFire hardware timer support.
|
|
*
|
|
* Copyright (C) 1999-2007, Greg Ungerer (gerg@snapgear.com)
|
|
*/
|
|
|
|
/***************************************************************************/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/init.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/irq.h>
|
|
#include <asm/io.h>
|
|
#include <asm/traps.h>
|
|
#include <asm/machdep.h>
|
|
#include <asm/coldfire.h>
|
|
#include <asm/mcftimer.h>
|
|
#include <asm/mcfsim.h>
|
|
|
|
/***************************************************************************/
|
|
|
|
/*
|
|
* By default use timer1 as the system clock timer.
|
|
*/
|
|
#define TA(a) (MCF_MBAR + MCFTIMER_BASE1 + (a))
|
|
|
|
/*
|
|
* Default the timer and vector to use for ColdFire. Some ColdFire
|
|
* CPU's and some boards may want different. Their sub-architecture
|
|
* startup code (in config.c) can change these if they want.
|
|
*/
|
|
unsigned int mcf_timervector = 29;
|
|
unsigned int mcf_profilevector = 31;
|
|
unsigned int mcf_timerlevel = 5;
|
|
|
|
/*
|
|
* These provide the underlying interrupt vector support.
|
|
* Unfortunately it is a little different on each ColdFire.
|
|
*/
|
|
extern void mcf_settimericr(int timer, int level);
|
|
extern int mcf_timerirqpending(int timer);
|
|
|
|
#if defined(CONFIG_M532x)
|
|
#define __raw_readtrr __raw_readl
|
|
#define __raw_writetrr __raw_writel
|
|
#else
|
|
#define __raw_readtrr __raw_readw
|
|
#define __raw_writetrr __raw_writew
|
|
#endif
|
|
|
|
/***************************************************************************/
|
|
|
|
static irqreturn_t hw_tick(int irq, void *dummy)
|
|
{
|
|
/* Reset the ColdFire timer */
|
|
__raw_writeb(MCFTIMER_TER_CAP | MCFTIMER_TER_REF, TA(MCFTIMER_TER));
|
|
|
|
return arch_timer_interrupt(irq, dummy);
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
static struct irqaction coldfire_timer_irq = {
|
|
.name = "timer",
|
|
.flags = IRQF_DISABLED | IRQF_TIMER,
|
|
.handler = hw_tick,
|
|
};
|
|
|
|
/***************************************************************************/
|
|
|
|
static int ticks_per_intr;
|
|
|
|
void hw_timer_init(void)
|
|
{
|
|
setup_irq(mcf_timervector, &coldfire_timer_irq);
|
|
|
|
__raw_writew(MCFTIMER_TMR_DISABLE, TA(MCFTIMER_TMR));
|
|
ticks_per_intr = (MCF_BUSCLK / 16) / HZ;
|
|
__raw_writetrr(ticks_per_intr - 1, TA(MCFTIMER_TRR));
|
|
__raw_writew(MCFTIMER_TMR_ENORI | MCFTIMER_TMR_CLK16 |
|
|
MCFTIMER_TMR_RESTART | MCFTIMER_TMR_ENABLE, TA(MCFTIMER_TMR));
|
|
|
|
mcf_settimericr(1, mcf_timerlevel);
|
|
|
|
#ifdef CONFIG_HIGHPROFILE
|
|
coldfire_profile_init();
|
|
#endif
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
unsigned long hw_timer_offset(void)
|
|
{
|
|
unsigned long tcn, offset;
|
|
|
|
tcn = __raw_readw(TA(MCFTIMER_TCN));
|
|
offset = ((tcn + 1) * (1000000 / HZ)) / ticks_per_intr;
|
|
|
|
/* Check if we just wrapped the counters and maybe missed a tick */
|
|
if ((offset < (1000000 / HZ / 2)) && mcf_timerirqpending(1))
|
|
offset += 1000000 / HZ;
|
|
return offset;
|
|
}
|
|
|
|
/***************************************************************************/
|
|
#ifdef CONFIG_HIGHPROFILE
|
|
/***************************************************************************/
|
|
|
|
/*
|
|
* By default use timer2 as the profiler clock timer.
|
|
*/
|
|
#define PA(a) (MCF_MBAR + MCFTIMER_BASE2 + (a))
|
|
|
|
/*
|
|
* Choose a reasonably fast profile timer. Make it an odd value to
|
|
* try and get good coverage of kernel operations.
|
|
*/
|
|
#define PROFILEHZ 1013
|
|
|
|
/*
|
|
* Use the other timer to provide high accuracy profiling info.
|
|
*/
|
|
irqreturn_t coldfire_profile_tick(int irq, void *dummy)
|
|
{
|
|
/* Reset ColdFire timer2 */
|
|
__raw_writeb(MCFTIMER_TER_CAP | MCFTIMER_TER_REF, PA(MCFTIMER_TER));
|
|
if (current->pid)
|
|
profile_tick(CPU_PROFILING, regs);
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
void coldfire_profile_init(void)
|
|
{
|
|
printk(KERN_INFO "PROFILE: lodging TIMER2 @ %dHz as profile timer\n", PROFILEHZ);
|
|
|
|
/* Set up TIMER 2 as high speed profile clock */
|
|
__raw_writew(MCFTIMER_TMR_DISABLE, PA(MCFTIMER_TMR));
|
|
|
|
__raw_writetrr(((MCF_CLK / 16) / PROFILEHZ), PA(MCFTIMER_TRR));
|
|
__raw_writew(MCFTIMER_TMR_ENORI | MCFTIMER_TMR_CLK16 |
|
|
MCFTIMER_TMR_RESTART | MCFTIMER_TMR_ENABLE, PA(MCFTIMER_TMR));
|
|
|
|
request_irq(mcf_profilevector, coldfire_profile_tick,
|
|
(IRQF_DISABLED | IRQ_FLG_FAST), "profile timer", NULL);
|
|
mcf_settimericr(2, 7);
|
|
}
|
|
|
|
/***************************************************************************/
|
|
#endif /* CONFIG_HIGHPROFILE */
|
|
/***************************************************************************/
|