linux/arch/arm/mach-pnx4008/time.c
Linus Torvalds 0cd61b68c3 Initial blind fixup for arm for irq changes
Untested, but this should fix up the bulk of the totally mechanical
issues, and should make the actual detail fixing easier.

Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-10-06 10:59:54 -07:00

137 lines
3.5 KiB
C

/*
* arch/arm/mach-pnx4008/time.c
*
* PNX4008 Timers
*
* Authors: Vitaly Wool, Dmitry Chigirev, Grigory Tolstolytkin <source@mvista.com>
*
* 2005 (c) MontaVista Software, Inc. This file is licensed under
* the terms of the GNU General Public License version 2. This program
* is licensed "as is" without any warranty of any kind, whether express
* or implied.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/module.h>
#include <linux/kallsyms.h>
#include <linux/time.h>
#include <linux/timex.h>
#include <linux/irq.h>
#include <asm/system.h>
#include <asm/hardware.h>
#include <asm/io.h>
#include <asm/leds.h>
#include <asm/mach/time.h>
#include <asm/errno.h>
/*! Note: all timers are UPCOUNTING */
/*!
* Returns number of us since last clock interrupt. Note that interrupts
* will have been disabled by do_gettimeoffset()
*/
static unsigned long pnx4008_gettimeoffset(void)
{
u32 ticks_to_match =
__raw_readl(HSTIM_MATCH0) - __raw_readl(HSTIM_COUNTER);
u32 elapsed = LATCH - ticks_to_match;
return (elapsed * (tick_nsec / 1000)) / LATCH;
}
/*!
* IRQ handler for the timer
*/
static irqreturn_t pnx4008_timer_interrupt(int irq, void *dev_id)
{
if (__raw_readl(HSTIM_INT) & MATCH0_INT) {
write_seqlock(&xtime_lock);
do {
timer_tick();
/*
* this algorithm takes care of possible delay
* for this interrupt handling longer than a normal
* timer period
*/
__raw_writel(__raw_readl(HSTIM_MATCH0) + LATCH,
HSTIM_MATCH0);
__raw_writel(MATCH0_INT, HSTIM_INT); /* clear interrupt */
/*
* The goal is to keep incrementing HSTIM_MATCH0
* register until HSTIM_MATCH0 indicates time after
* what HSTIM_COUNTER indicates.
*/
} while ((signed)
(__raw_readl(HSTIM_MATCH0) -
__raw_readl(HSTIM_COUNTER)) < 0);
write_sequnlock(&xtime_lock);
}
return IRQ_HANDLED;
}
static struct irqaction pnx4008_timer_irq = {
.name = "PNX4008 Tick Timer",
.flags = IRQF_DISABLED | IRQF_TIMER,
.handler = pnx4008_timer_interrupt
};
/*!
* Set up timer and timer interrupt.
*/
static __init void pnx4008_setup_timer(void)
{
__raw_writel(RESET_COUNT, MSTIM_CTRL);
while (__raw_readl(MSTIM_COUNTER)) ; /* wait for reset to complete. 100% guarantee event */
__raw_writel(0, MSTIM_CTRL); /* stop the timer */
__raw_writel(0, MSTIM_MCTRL);
__raw_writel(RESET_COUNT, HSTIM_CTRL);
while (__raw_readl(HSTIM_COUNTER)) ; /* wait for reset to complete. 100% guarantee event */
__raw_writel(0, HSTIM_CTRL);
__raw_writel(0, HSTIM_MCTRL);
__raw_writel(0, HSTIM_CCR);
__raw_writel(12, HSTIM_PMATCH); /* scale down to 1 MHZ */
__raw_writel(LATCH, HSTIM_MATCH0);
__raw_writel(MR0_INT, HSTIM_MCTRL);
setup_irq(HSTIMER_INT, &pnx4008_timer_irq);
__raw_writel(COUNT_ENAB | DEBUG_EN, HSTIM_CTRL); /*start timer, stop when JTAG active */
}
/* Timer Clock Control in PM register */
#define TIMCLK_CTRL_REG IO_ADDRESS((PNX4008_PWRMAN_BASE + 0xBC))
#define WATCHDOG_CLK_EN 1
#define TIMER_CLK_EN 2 /* HS and MS timers? */
static u32 timclk_ctrl_reg_save;
void pnx4008_timer_suspend(void)
{
timclk_ctrl_reg_save = __raw_readl(TIMCLK_CTRL_REG);
__raw_writel(0, TIMCLK_CTRL_REG); /* disable timers */
}
void pnx4008_timer_resume(void)
{
__raw_writel(timclk_ctrl_reg_save, TIMCLK_CTRL_REG); /* enable timers */
}
struct sys_timer pnx4008_timer = {
.init = pnx4008_setup_timer,
.offset = pnx4008_gettimeoffset,
.suspend = pnx4008_timer_suspend,
.resume = pnx4008_timer_resume,
};