1 /*
2  *  linux/arch/arm/mach-ebsa110/time.c
3  *
4  *  Copyright (C) 2001 Russell King
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  */
10 #include <linux/sched.h>
11 #include <linux/init.h>
12 
13 #include <asm/io.h>
14 
15 #define PIT_CTRL		(PIT_BASE + 0x0d)
16 #define PIT_T2			(PIT_BASE + 0x09)
17 #define PIT_T1			(PIT_BASE + 0x05)
18 #define PIT_T0			(PIT_BASE + 0x01)
19 
20 /*
21  * This is the rate at which your MCLK signal toggles (in Hz)
22  * This was measured on a 10 digit frequency counter sampling
23  * over 1 second.
24  */
25 #define MCLK	47894000
26 
27 /*
28  * This is the rate at which the PIT timers get clocked
29  */
30 #define CLKBY7	(MCLK / 7)
31 
32 /*
33  * If CLKBY7 is larger than this, then we must do software
34  * division of the timer interrupt.
35  */
36 #if CLKBY7 > 6553500
37 #define DIVISOR	2
38 #else
39 #define DIVISOR 1
40 #endif
41 
42 /*
43  * This is the counter value
44  */
45 #define COUNT	((CLKBY7 + (DIVISOR * HZ / 2)) / (DIVISOR * HZ))
46 
47 extern unsigned long (*gettimeoffset)(void);
48 
49 static unsigned long divisor;
50 
51 /*
52  * Get the time offset from the system PIT.  Note that if we have missed an
53  * interrupt, then the PIT counter will roll over (ie, be negative).
54  * This actually works out to be convenient.
55  */
ebsa110_gettimeoffset(void)56 static unsigned long ebsa110_gettimeoffset(void)
57 {
58 	unsigned long offset, count;
59 
60 	__raw_writeb(0x40, PIT_CTRL);
61 	count = __raw_readb(PIT_T1);
62 	count |= __raw_readb(PIT_T1) << 8;
63 
64 	/*
65 	 * If count > COUNT, make the number negative.
66 	 */
67 	if (count > COUNT)
68 		count |= 0xffff0000;
69 
70 	offset = COUNT * (DIVISOR - divisor);
71 	offset -= count;
72 
73 	/*
74 	 * `offset' is in units of timer counts.  Convert
75 	 * offset to units of microseconds.
76 	 */
77 	offset = offset * (1000000 / HZ) / (COUNT * DIVISOR);
78 
79 	return offset;
80 }
81 
ebsa110_reset_timer(void)82 int ebsa110_reset_timer(void)
83 {
84 	u32 count;
85 
86 	/* latch and read timer 1 */
87 	__raw_writeb(0x40, PIT_CTRL);
88 	count = __raw_readb(PIT_T1);
89 	count |= __raw_readb(PIT_T1) << 8;
90 
91 	count += COUNT;
92 
93 	__raw_writeb(count & 0xff, PIT_T1);
94 	__raw_writeb(count >> 8, PIT_T1);
95 
96 	if (divisor == 0)
97 		divisor = DIVISOR;
98 	divisor -= 1;
99 	return divisor;
100 }
101 
ebsa110_setup_timer(void)102 void __init ebsa110_setup_timer(void)
103 {
104 	/*
105 	 * Timer 1, mode 2, LSB/MSB
106 	 */
107 	__raw_writeb(0x70, PIT_CTRL);
108 	__raw_writeb(COUNT & 0xff, PIT_T1);
109 	__raw_writeb(COUNT >> 8, PIT_T1);
110 	divisor = DIVISOR - 1;
111 
112 	gettimeoffset = ebsa110_gettimeoffset;
113 }
114