1/*
2 * arch/arm/mach-ns9xxx/time.c
3 *
4 * Copyright (C) 2006 by Digi International Inc.
5 * All rights reserved.
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License version 2 as published by
9 * the Free Software Foundation.
10 */
11#include <linux/jiffies.h>
12#include <linux/interrupt.h>
13#include <linux/irq.h>
14#include <asm/arch-ns9xxx/regs-sys.h>
15#include <asm/arch-ns9xxx/clock.h>
16#include <asm/arch-ns9xxx/irqs.h>
17#include <asm/arch/system.h>
18#include "generic.h"
19
20#define TIMERCLOCKSELECT 64
21
22static u32 usecs_per_tick;
23
24static irqreturn_t
25ns9xxx_timer_interrupt(int irq, void *dev_id)
26{
27	write_seqlock(&xtime_lock);
28	timer_tick();
29	write_sequnlock(&xtime_lock);
30
31	return IRQ_HANDLED;
32}
33
34static unsigned long ns9xxx_timer_gettimeoffset(void)
35{
36	/* return the microseconds which have passed since the last interrupt
37	 * was _serviced_.  That is, if an interrupt is pending or the counter
38	 * reloads, return one period more. */
39
40	u32 counter1 = SYS_TR(0);
41	int pending = SYS_ISR & (1 << IRQ_TIMER0);
42	u32 counter2 = SYS_TR(0);
43	u32 elapsed;
44
45	if (pending || counter2 > counter1)
46		elapsed = 2 * SYS_TRC(0) - counter2;
47	else
48		elapsed = SYS_TRC(0) - counter1;
49
50	return (elapsed * usecs_per_tick) >> 16;
51
52}
53
54static struct irqaction ns9xxx_timer_irq = {
55	.name = "NS9xxx Timer Tick",
56	.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
57	.handler = ns9xxx_timer_interrupt,
58};
59
60static void __init ns9xxx_timer_init(void)
61{
62	int tc;
63
64	usecs_per_tick =
65		SH_DIV(1000000 * TIMERCLOCKSELECT, ns9xxx_cpuclock(), 16);
66
67	/* disable timer */
68	if ((tc = SYS_TC(0)) & SYS_TCx_TEN)
69		SYS_TC(0) = tc & ~SYS_TCx_TEN;
70
71	SYS_TRC(0) = SH_DIV(ns9xxx_cpuclock(), (TIMERCLOCKSELECT * HZ), 0);
72
73	REGSET(tc, SYS_TCx, TEN, EN);
74	REGSET(tc, SYS_TCx, TLCS, DIV64); /* This must match TIMERCLOCKSELECT */
75	REGSET(tc, SYS_TCx, INTS, EN);
76	REGSET(tc, SYS_TCx, UDS, DOWN);
77	REGSET(tc, SYS_TCx, TDBG, STOP);
78	REGSET(tc, SYS_TCx, TSZ, 32);
79	REGSET(tc, SYS_TCx, REN, EN);
80	SYS_TC(0) = tc;
81
82	setup_irq(IRQ_TIMER0, &ns9xxx_timer_irq);
83}
84
85struct sys_timer ns9xxx_timer = {
86	.init = ns9xxx_timer_init,
87	.offset = ns9xxx_timer_gettimeoffset,
88};
89