aboutsummaryrefslogtreecommitdiffstats
path: root/packages/linux/ixp4xx-kernel/2.6.15/85-timer.patch
blob: fee28e6f695ee9414b8693f45e93b748aa689693 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
 arch/arm/mach-ixp4xx/common.c       |  165 +++++++++++++++++++++++++++++++++---
 arch/arm/mach-ixp4xx/nslu2-setup.c  |    5 +
 include/asm-arm/arch-ixp4xx/nslu2.h |    5 -
 include/asm-arm/arch-ixp4xx/timex.h |   23 +++--
 4 files changed, 176 insertions(+), 22 deletions(-)

--- linux-nslu2.orig/arch/arm/mach-ixp4xx/common.c	2006-01-20 20:23:46.000000000 +0100
+++ linux-nslu2/arch/arm/mach-ixp4xx/common.c	2006-01-23 01:09:25.000000000 +0100
@@ -239,36 +239,165 @@ void __init ixp4xx_init_irq(void)
  * IXP4xx timer tick
  * We use OS timer1 on the CPU for the timer tick and the timestamp 
  * counter as a source of real clock ticks to account for missed jiffies.
+ *
+ * 'CLOCK_TICK_RATE' is the nominal number of internal ticks per second,
+ * this is significantly greater than the actual number on any ixp4xx
+ * board.  Neither this nor 'LATCH' are required by this code because
+ * the only requirement is to generate HZ timer_tick calls per second.
  *************************************************************************/
+#if TICK_NSEC * HZ != 1000000000
+	/* This will cause the jiffies to drift unnecessarily. */
+#	error CLOCK_TICK_RATE should be a multiple of HZ for this code
+#endif
+
+/* These are the control registers for the interrupt handler, they must
+ * only be read and written by the interrupt handler and by the init
+ * method (which sets them to 0).
+ */
+static volatile u32 last_timer_time;
+static volatile int accumulated_error;
+
+/* Most ixp4xx boards have 66.6666MHz crystals, so default to this, reset
+ * this from the board level code if required.  The following variables
+ * must be *written* only by set_board_tick_rate
+ */
+static u32 board_tick_rate;
+static u32 board_tick_per_1000; /* board_tick_rate/1000 */
+static u32 timer_count;
+
+/* The following symbol may be written to change the current tick rate,
+ * it is read by the interrupt handler and used to reload the timer.
+ * The 'real' value (the one in use) is 'board_tick_rate' above.
+ * NOTE: this can be tweaked to match the actual crystal on a particular
+ * machine.
+ */
+volatile u32 ixp4xx_board_tick_rate = 66666600;
+EXPORT_SYMBOL(ixp4xx_board_tick_rate);
+
+/* The set API may run asynchronously in the presence of interrupts,
+ * everything it does it is both atomic and complete (notice that it
+ * doesn't change any of the 'volatile' values).  The mathematics in
+ * here require the following values.  Changing the board tick rate
+ * implies an unknown error in the current timestamp tick count.
+ */
+#if IXP4XX_OST_RELOAD_MASK != 3 || IXP4XX_OST_ENABLE != 1
+#	error unexpected value for timer reload mask
+#endif
+static void set_board_tick_rate(u32 rate) {
+	u32 reload;
+
+	/* Store the two effectively informational rate values, the
+	 * error calculation is (rate - count*HZ) (above), and rate
+	 * is changed first, this can cause a temporary error which
+	 * will be corrected on the next interrupt.
+	 */
+	board_tick_rate = rate;
+	board_tick_per_1000 = (rate+500)/1000;
+
+	/* Calculate the correct value to load into the timer countdown
+	 * register, the low two bits must be b01 (to enable the timer).
+	 * Select the top bits to be as close to the desired value as
+	 * possible.
+	 *
+	 * First find the best value, regardless of the low two bits -
+	 * this is the value used in the interrupt calculation even though
+	 * it cannot necessarily be set into the register.
+	 */
+	timer_count = (rate + (HZ/2))/HZ;
+
+	/* Now the timer_ticks are being generated at this rate, calculate
+	 * an appropriate value for the register.  This stores a 30 bit
+	 * value which gives a period of 4*x+1, we want:
+	 *
+	 *   4*x+1 = board_tick_rate/HZ
+	 *
+	 * This needs to be rounded to the closest 4*HZ value:
+	 *
+	 *   x = ((board_tick_rate-HZ) + (4*HZ)/2) / 4*HZ
+	 *   x = (board_tick_rate+HZ) / (4*HZ);
+	 */
+	reload = (board_tick_rate + HZ) / HZ;
+	reload = (reload & ~IXP4XX_OST_RELOAD_MASK) | IXP4XX_OST_ENABLE;
+	*IXP4XX_OSRT1 = reload;
 
-static unsigned volatile last_jiffy_time;
+	/* If the clock is drifing, look in syslog: */
+	printk(KERN_INFO "IXP4xx: FREQ=%d COUNT=%d\n", rate, reload);
+}
 
-#define CLOCK_TICKS_PER_USEC	((CLOCK_TICK_RATE + USEC_PER_SEC/2) / USEC_PER_SEC)
+/* This returns the time in timer ticks since the 'last_timer_time'
+ * recorded above.  Use this to avoid arithmetic errors because of
+ * the overflow when the timer wraps.
+ */
+static inline u32 ixp4xx_timer_delta(void)
+{
+	return *IXP4XX_OSTS - last_timer_time;
+}
 
 /* IRQs are disabled before entering here from do_gettimeofday() */
 static unsigned long ixp4xx_gettimeoffset(void)
 {
-	u32 elapsed;
-
-	elapsed = *IXP4XX_OSTS - last_jiffy_time;
+	/* Return the offset of the current time from the last time
+	 * timer tick in microseconds.  This is only used for the
+	 * gettimeofday call.
+	 *
+	 * The result of this API is at most about 20000 (for a 50Hz
+	 * HZ - 20000 uS/tick), the input delta is at most about
+	 * 1.3M - 21 bits.
+	 */
+	u32 delta = ixp4xx_timer_delta(); /* About 21 bits max */
+	/* return delta * 1000000 / board_tick_rate; */
+	return (delta * 1000 + board_tick_per_1000/2) / board_tick_per_1000;
+}
 
-	return elapsed / CLOCK_TICKS_PER_USEC;
+/* This is the correct adjustment to the counter to compensate for an
+ * error iff timer_count-1 <= exact_count <= timer_count+1
+ */
+static inline int adjustment(int error) {
+	if (error >= HZ)
+		return 1;
+	else if (error <= -HZ)
+		return -1;
+	return 0;
 }
 
 static irqreturn_t ixp4xx_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
+	u32 rate;
+	u32 count;
+	int error;
+
 	write_seqlock(&xtime_lock);
 
 	/* Clear Pending Interrupt by writing '1' to it */
 	*IXP4XX_OSST = IXP4XX_OSST_TIMER_1_PEND;
 
+	/* If the board tick rate has been changed update the cached
+	 * value.
+	 */
+	if (ixp4xx_board_tick_rate != board_tick_rate) {
+		set_board_tick_rate(ixp4xx_board_tick_rate);
+		accumulated_error = 0;
+	}
+
 	/*
 	 * Catch up with the real idea of time
+	 *
+	 * board_tick_rate: actual ixp4xx ticks/second, read-only
+	 * accumulated_error: aggregate error/tick * HZ, read/write
+	 * timer_count: best ixp4xx ticks per timer_tick, read-only
 	 */
-	while ((*IXP4XX_OSTS - last_jiffy_time) > LATCH) {
+	rate = board_tick_rate;
+	error = accumulated_error;
+	count = timer_count;
+	do {
+		u32 adjusted_count = count + adjustment(error);
+		if (ixp4xx_timer_delta() < adjusted_count)
+			break;
 		timer_tick(regs);
-		last_jiffy_time += LATCH;
-	}
+		last_timer_time += adjusted_count;
+		error += rate - adjusted_count*HZ;
+	} while (1);
+	accumulated_error = error;
 
 	write_sequnlock(&xtime_lock);
 
@@ -281,17 +410,30 @@ static struct irqaction ixp4xx_timer_irq
 	.handler	= ixp4xx_timer_interrupt,
 };
 
+u32 ixp4xx_get_board_tick_rate(void) {
+	return board_tick_rate;
+}
+
+EXPORT_SYMBOL(ixp4xx_get_board_tick_rate);
+
+void ixp4xx_set_board_tick_rate(u32 rate) {
+	ixp4xx_board_tick_rate = rate;
+}
+
+EXPORT_SYMBOL(ixp4xx_set_board_tick_rate);
+
 static void __init ixp4xx_timer_init(void)
 {
 	/* Clear Pending Interrupt by writing '1' to it */
 	*IXP4XX_OSST = IXP4XX_OSST_TIMER_1_PEND;
 
 	/* Setup the Timer counter value */
-	*IXP4XX_OSRT1 = (LATCH & ~IXP4XX_OST_RELOAD_MASK) | IXP4XX_OST_ENABLE;
+	set_board_tick_rate(ixp4xx_board_tick_rate);
 
 	/* Reset time-stamp counter */
 	*IXP4XX_OSTS = 0;
-	last_jiffy_time = 0;
+	last_timer_time = 0;
+	accumulated_error = 0;
 
 	/* Connect the interrupt handler and enable the interrupt */
 	setup_irq(IRQ_IXP4XX_TIMER1, &ixp4xx_timer_irq);
@@ -337,4 +479,3 @@ void __init ixp4xx_sys_init(void)
 				ARRAY_SIZE(ixp46x_devices));
 	}
 }
-
--- linux-nslu2.orig/arch/arm/mach-ixp4xx/nslu2-setup.c	2006-01-23 01:09:04.000000000 +0100
+++ linux-nslu2/arch/arm/mach-ixp4xx/nslu2-setup.c	2006-01-23 01:09:25.000000000 +0100
@@ -120,6 +120,11 @@ static void nslu2_power_off(void)
 
 static void __init nslu2_init(void)
 {
+	/* The NSLU2 has a 33MHz crystal on board - 1.01% different
+	 * from the typical value.
+	 */
+	ixp4xx_set_board_tick_rate(66000000);
+
 	ixp4xx_sys_init();
 
 	pm_power_off = nslu2_power_off;
--- linux-nslu2.orig/include/asm-arm/arch-ixp4xx/nslu2.h	2006-01-20 20:23:46.000000000 +0100
+++ linux-nslu2/include/asm-arm/arch-ixp4xx/nslu2.h	2006-01-23 01:09:25.000000000 +0100
@@ -38,11 +38,6 @@
 #define NSLU2_PCI_INTD_PIN	8
 
 
-/* NSLU2 Timer */
-#define NSLU2_FREQ 66000000
-#define NSLU2_CLOCK_TICK_RATE (((NSLU2_FREQ / HZ & ~IXP4XX_OST_RELOAD_MASK) + 1) * HZ)
-#define NSLU2_CLOCK_TICKS_PER_USEC ((NSLU2_CLOCK_TICK_RATE + USEC_PER_SEC/2) / USEC_PER_SEC)
-
 /* GPIO */
 
 #define NSLU2_GPIO0		0
--- linux-nslu2.orig/include/asm-arm/arch-ixp4xx/timex.h	2006-01-20 20:23:46.000000000 +0100
+++ linux-nslu2/include/asm-arm/arch-ixp4xx/timex.h	2006-01-23 01:09:25.000000000 +0100
@@ -6,10 +6,23 @@
 #include <asm/hardware.h>
 
 /*
- * We use IXP425 General purpose timer for our timer needs, it runs at 
- * 66.66... MHz. We do a convulted calculation of CLOCK_TICK_RATE b/c the
- * timer register ignores the bottom 2 bits of the LATCH value.
+ * In linux/timex.h 'LATCH' is defined as CLOCK_TICK_RATE/HZ and
+ * is the number of internal counts per timer interrupt.  Thus
+ * CLOCK_TICK_RATE is LATCH*HZ.
+ *
+ * The actual values of these numbers do not matter, because they
+ * are only used to calculate ACTHZ (rate/latch as a 24.8 fixed
+ * point number), so the value here gives a LATCH of 1 and pretty
+ * much guarantees to flush out any off-by-one errors.
+ *
+ * ACTHZ is equal to HZ, because CLOCK_TICK_RATE is a multiple of
+ * HZ, this is checked in the ixp4xx/common.c code.
  */
-#define FREQ 66666666
-#define CLOCK_TICK_RATE (((FREQ / HZ & ~IXP4XX_OST_RELOAD_MASK) + 1) * HZ)
+#define CLOCK_TICK_RATE HZ
 
+/* The following allow the exact board tick rate to be set and
+ * discovered.  The value should be exactly twice the frequency
+ * (in Hz) of the onboard crystal.
+ */
+extern u32 ixp4xx_get_board_tick_rate(void);
+extern void ixp4xx_set_board_tick_rate(u32 new_rate);