1/*
2 *  linux/include/asm-arm/arch-integrator/time.h
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17 */
18#include <asm/system.h>
19#include <asm/leds.h>
20
21/*
22 * Where is the timer (VA)?
23 */
24#define TIMER0_VA_BASE (IO_ADDRESS(INTEGRATOR_CT_BASE)+0x00000000)
25#define TIMER1_VA_BASE (IO_ADDRESS(INTEGRATOR_CT_BASE)+0x00000100)
26#define TIMER2_VA_BASE (IO_ADDRESS(INTEGRATOR_CT_BASE)+0x00000200)
27#define VA_IC_BASE     IO_ADDRESS(INTEGRATOR_IC_BASE)
28
29/*
30 * How long is the timer interval?
31 */
32#define TIMER_INTERVAL	(TICKS_PER_uSEC * mSEC_10)
33#if TIMER_INTERVAL >= 0x100000
34#define TIMER_RELOAD	(TIMER_INTERVAL >> 8)		/* Divide by 256 */
35#define TIMER_CTRL	0x88				/* Enable, Clock / 256 */
36#define TICKS2USECS(x)	(256 * (x) / TICKS_PER_uSEC)
37#elif TIMER_INTERVAL >= 0x10000
38#define TIMER_RELOAD	(TIMER_INTERVAL >> 4)		/* Divide by 16 */
39#define TIMER_CTRL	0x84				/* Enable, Clock / 16 */
40#define TICKS2USECS(x)	(16 * (x) / TICKS_PER_uSEC)
41#else
42#define TIMER_RELOAD	(TIMER_INTERVAL)
43#define TIMER_CTRL	0x80				/* Enable */
44#define TICKS2USECS(x)	((x) / TICKS_PER_uSEC)
45#endif
46
47/*
48 * What does it look like?
49 */
50typedef struct TimerStruct {
51	unsigned long TimerLoad;
52	unsigned long TimerValue;
53	unsigned long TimerControl;
54	unsigned long TimerClear;
55} TimerStruct_t;
56
57extern unsigned long (*gettimeoffset)(void);
58
59/*
60 * Returns number of ms since last clock interrupt.  Note that interrupts
61 * will have been disabled by do_gettimeoffset()
62 */
63static unsigned long integrator_gettimeoffset(void)
64{
65	volatile TimerStruct_t *timer1 = (TimerStruct_t *)TIMER1_VA_BASE;
66	unsigned long ticks1, ticks2, status;
67
68	/*
69	 * Get the current number of ticks.  Note that there is a race
70	 * condition between us reading the timer and checking for
71	 * an interrupt.  We get around this by ensuring that the
72	 * counter has not reloaded between our two reads.
73	 */
74	ticks2 = timer1->TimerValue & 0xffff;
75	do {
76		ticks1 = ticks2;
77		status = __raw_readl(VA_IC_BASE + IRQ_RAW_STATUS);
78		ticks2 = timer1->TimerValue & 0xffff;
79	} while (ticks2 > ticks1);
80
81	/*
82	 * Number of ticks since last interrupt.
83	 */
84	ticks1 = TIMER_RELOAD - ticks2;
85
86	/*
87	 * Interrupt pending?  If so, we've reloaded once already.
88	 */
89	if (status & IRQMASK_TIMERINT1)
90		ticks1 += TIMER_RELOAD;
91
92	/*
93	 * Convert the ticks to usecs
94	 */
95	return TICKS2USECS(ticks1);
96}
97
98/*
99 * IRQ handler for the timer
100 */
101static void integrator_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
102{
103	volatile TimerStruct_t *timer1 = (volatile TimerStruct_t *)TIMER1_VA_BASE;
104
105	// ...clear the interrupt
106	timer1->TimerClear = 1;
107
108	do_leds();
109	do_timer(regs);
110	do_profile(regs);
111}
112
113/*
114 * Set up timer interrupt, and return the current time in seconds.
115 */
116static inline void setup_timer(void)
117{
118	volatile TimerStruct_t *timer0 = (volatile TimerStruct_t *)TIMER0_VA_BASE;
119	volatile TimerStruct_t *timer1 = (volatile TimerStruct_t *)TIMER1_VA_BASE;
120	volatile TimerStruct_t *timer2 = (volatile TimerStruct_t *)TIMER2_VA_BASE;
121
122	timer_irq.handler = integrator_timer_interrupt;
123
124	/*
125	 * Initialise to a known state (all timers off)
126	 */
127	timer0->TimerControl = 0;
128	timer1->TimerControl = 0;
129	timer2->TimerControl = 0;
130
131	timer1->TimerLoad    = TIMER_RELOAD;
132	timer1->TimerValue   = TIMER_RELOAD;
133	timer1->TimerControl = TIMER_CTRL | 0x40;	/* periodic */
134
135	/*
136	 * Make irqs happen for the system timer
137	 */
138	setup_arm_irq(IRQ_TIMERINT1, &timer_irq);
139	gettimeoffset = integrator_gettimeoffset;
140}
141