revert last commit - flash map driver not updated yet
[openwrt.git] / target / linux / x86-2.6 / patches / 100-scx200_hr_timer.patch
1 SCx200 High Resolution Timer Patch for Linux 2.6
2 http://www.gnusto.com/scx200-hr-timer.html
3
4 diff -Naurp linux-2.6.12-rc6.orig/arch/i386/Kconfig linux-2.6.12-rc6/arch/i386/Kconfig
5 --- linux-2.6.12-rc6.orig/arch/i386/Kconfig     2005-06-07 14:56:02.000000000 +0100
6 +++ linux-2.6.12-rc6/arch/i386/Kconfig  2005-06-07 16:43:19.000000000 +0100
7 @@ -458,6 +458,17 @@ config HPET_EMULATE_RTC
8         bool "Provide RTC interrupt"
9         depends on HPET_TIMER && RTC=y
10  
11 +config SCx200HR_TIMER
12 +       bool "NatSemi SCx200 27MHz High-Resolution Timer Support"
13 +       help
14 +         Some of the AMD (formerly National Semiconductor) Geode
15 +         processors, notably the SC1100, suffer from a buggy time
16 +         stamp counter which causes them to lose time when the
17 +         processor is sleeping.  Enable this option to use the
18 +         on-board 27Mz high-resolution timer to keep time instead.
19 +       depends on (SCx200)
20 +       default n
21 +
22  config SMP
23         bool "Symmetric multi-processing support"
24         ---help---
25 diff -Naurp linux-2.6.12-rc6.orig/arch/i386/kernel/scx200.c linux-2.6.12-rc6/arch/i386/kernel/scx200.c
26 --- linux-2.6.12-rc6.orig/arch/i386/kernel/scx200.c     2005-06-07 14:56:02.000000000 +0100
27 +++ linux-2.6.12-rc6/arch/i386/kernel/scx200.c  2005-06-07 16:43:19.000000000 +0100
28 @@ -27,6 +27,10 @@ long scx200_gpio_shadow[2];
29  
30  unsigned scx200_cb_base = 0;
31  
32 +#ifdef CONFIG_SCx200HR_TIMER
33 +extern void __devinit scx200hr_timer_enable(void);
34 +#endif
35 +
36  static struct pci_device_id scx200_tbl[] = {
37         { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SCx200_BRIDGE) },
38         { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SC1100_BRIDGE) },
39 @@ -83,6 +87,9 @@ static int __devinit scx200_probe(struct
40                 printk(KERN_INFO NAME ": Configuration Block base 0x%x\n", scx200_cb_base);
41         }
42  
43 +#ifdef CONFIG_SCx200HR_TIMER
44 +       scx200hr_timer_enable();
45 +#endif
46         return 0;
47  }
48  
49 diff -Naurp linux-2.6.12-rc6.orig/arch/i386/kernel/timers/Makefile linux-2.6.12-rc6/arch/i386/kernel/timers/Makefile
50 --- linux-2.6.12-rc6.orig/arch/i386/kernel/timers/Makefile      2004-03-11 18:21:13.000000000 +0000
51 +++ linux-2.6.12-rc6/arch/i386/kernel/timers/Makefile   2005-06-07 16:43:19.000000000 +0100
52 @@ -5,5 +5,6 @@
53  obj-y := timer.o timer_none.o timer_tsc.o timer_pit.o common.o
54  
55  obj-$(CONFIG_X86_CYCLONE_TIMER)        += timer_cyclone.o
56 +obj-$(CONFIG_SCx200HR_TIMER)   += timer_scx200hr.o
57  obj-$(CONFIG_HPET_TIMER)       += timer_hpet.o
58  obj-$(CONFIG_X86_PM_TIMER)     += timer_pm.o
59 diff -Naurp linux-2.6.12-rc6.orig/arch/i386/kernel/timers/timer.c linux-2.6.12-rc6/arch/i386/kernel/timers/timer.c
60 --- linux-2.6.12-rc6.orig/arch/i386/kernel/timers/timer.c       2004-12-26 14:07:37.000000000 +0000
61 +++ linux-2.6.12-rc6/arch/i386/kernel/timers/timer.c    2005-06-07 16:43:19.000000000 +0100
62 @@ -13,6 +13,9 @@
63  #endif
64  /* list of timers, ordered by preference, NULL terminated */
65  static struct init_timer_opts* __initdata timers[] = {
66 +#ifdef CONFIG_SCx200HR_TIMER
67 +       &timer_scx200hr_init,
68 +#endif
69  #ifdef CONFIG_X86_CYCLONE_TIMER
70         &timer_cyclone_init,
71  #endif
72 diff -Naurp linux-2.6.12-rc6.orig/arch/i386/kernel/timers/timer_scx200hr.c linux-2.6.12-rc6/arch/i386/kernel/timers/timer_scx200hr.c
73 --- linux-2.6.12-rc6.orig/arch/i386/kernel/timers/timer_scx200hr.c      1970-01-01 01:00:00.000000000 +0100
74 +++ linux-2.6.12-rc6/arch/i386/kernel/timers/timer_scx200hr.c   2005-06-07 16:43:19.000000000 +0100
75 @@ -0,0 +1,220 @@
76 +/*
77 + * Copyright (C) 2005 Ted Phelps
78 + *
79 + * This is a clock driver for the Geode SCx200's 27MHz high-resolution
80 + * timer as the system clock replacing its buggy time stamp counter.
81 + *
82 + * Based on parts of timer_hpet.c, timer_tsc.c and timer_pit.c.
83 + *
84 + * This program is free software; you can redistribute it and/or
85 + * modify it under the terms of the GNU General Public License as
86 + * published by the Free Software Foundation; either version 2 of the
87 + * License, or (at your option) any later version.
88 + */
89 +
90 +#include <asm/timer.h>
91 +#include <linux/init.h>
92 +#include <linux/pci.h>
93 +#include <linux/seq_file.h>
94 +#include <linux/scx200.h>
95 +
96 +#define NAME "scx200hr"
97 +
98 +/* Read the clock */
99 +#define SCx200HR_CLOCK() inl(scx200_cb_base + SCx200_TIMER_OFFSET)
100 +
101 +/* High-resolution timer configuration address */
102 +#define SCx200_TMCNFG_OFFSET (SCx200_TIMER_OFFSET + 5)
103 +
104 +/* Set this bit to disable the 27 MHz input clock */
105 +#define HR_TM27MPD (1 << 2)
106 +
107 +/* Set this bit to update the count-up timer once per cycle of the
108 + * 27MHz timer, clear it to update the timer once every 27 cycles
109 + * (effectively producing a 1MHz counter) */
110 +#define HR_TMCLKSEL (1 << 1)
111 +
112 +/* Set this bit to enable the high-resolution timer interrupt */
113 +#define HR_TMEN (1 << 0)
114 +
115 +/* The frequency of the timer. Change this to 27000000 and set
116 + * HR_TMCLKSEL in scx200hr_enable to run at the faster clock rate.  At
117 + * this point in time there is no point in doing so since times are
118 + * recorded in usec except for the monotonic clock, which is only used
119 + * by the hangcheck-timer. */
120 +#define HR_FREQ 1000000
121 +
122 +/* The number of cycles of the high-resolution timer we expect to see
123 + * in a single tick.  Note that the result is <<8 for greater precision*/
124 +#define HR_CYCLES_PER_TICK \
125 +    (SH_DIV(HR_FREQ / 1000000 * TICK_NSEC, 1000, 8))
126 +
127 +/* The number of cycles of the high-resolution timer we expect to see
128 + * in one microsecond, <<8 */
129 +#define HR_CYCLES_PER_US ((HR_FREQ / 1000000) << 8)
130 +
131 +
132 +/* The value of the timer at the last interrupt */
133 +static u32 clock_at_last_interrupt;
134 +
135 +/* The number of high-resolution clock cycles beyond what we would
136 + have expected that the last tick occurred, <<8 for greater precision */
137 +static long clock_delay;
138 +
139 +/* The total number of timer nanoseconds between the time the timer
140 + * went live and the most recent tick. */
141 +static unsigned long long total_ns;
142 +
143 +/* A lock to guard access to the monotonic clock-related variables
144 + * (total_ns and clocal_at_last_interrupt).  Note that these are also
145 + * protected by the xtime lock. */
146 +static seqlock_t hr_lock = SEQLOCK_UNLOCKED;
147 +
148 +/* Nonzero if the timer has been selected */
149 +static int enable_scx200hr;
150 +
151 +static int __init scx200hr_init(char *override)
152 +{
153 +       /* Watch for a command-line clock= override */
154 +       if (override[0] && strncmp(override, NAME, sizeof(NAME) - 1) != 0) {
155 +               return -ENODEV;
156 +       }
157 +
158 +        /* Note that we should try to enable this timer once the
159 +         * configuration block address is known */
160 +        printk(KERN_WARNING NAME ": timer not yet accessible; will probe later.\n");
161 +       enable_scx200hr = 1;
162 +       return -EAGAIN;
163 +}
164 +
165 +/* Called by the timer interrupt.  The xtime_lock will be held. */
166 +static void mark_offset_scx200hr(void)
167 +{
168 +       u32 now, delta;
169 +
170 +       /* Avoid races between the interrupt handler and monotonic_clock */
171 +       write_seqlock(&hr_lock);
172 +
173 +       /* Determine how many cycles have elapsed since the last interrupt */
174 +       now = SCx200HR_CLOCK();
175 +       delta = (now - clock_at_last_interrupt) << 8;
176 +       clock_at_last_interrupt = now;
177 +
178 +       /* Update the total us count and remainder */
179 +       total_ns += (delta * 1000) / HR_CYCLES_PER_US;
180 +
181 +       /* The monotonic clock is safe now */
182 +       write_sequnlock(&hr_lock);
183 +
184 +       /* Adjust for interrupt handling delay */
185 +       delta += clock_delay;
186 +
187 +       /* The high-resolution timer is driven by a different crystal
188 +        * to the main CPU, so there's no guarantee that the 1KHz
189 +        * interrupt rate will coincide with the timer.  This keeps
190 +        * the jiffies count in line with the high-resolution timer,
191 +        * which makes it possible for NTP to do its magic */
192 +       if (delta < HR_CYCLES_PER_TICK) {
193 +#if 1
194 +               /* Didn't go over 1000us: decrement jiffies to balance
195 +                * out increment in do_timer.  This will cause some
196 +                * jitter if the frequency offset is large, as that
197 +                * adjustment will be applied about 1ms late. */
198 +               jiffies_64--;
199 +               clock_delay = delta;
200 +#else /* !1 */
201 +                clock_delay = 0;
202 +#endif /* 1 */
203 +       } else if (delta < (HR_CYCLES_PER_TICK << 1) + (HR_CYCLES_PER_TICK >> 1)) {
204 +               clock_delay = delta - HR_CYCLES_PER_TICK;
205 +       } else {
206 +               jiffies_64 += delta / HR_CYCLES_PER_TICK - 2;
207 +               clock_delay = HR_CYCLES_PER_TICK + delta % HR_CYCLES_PER_TICK;
208 +       }
209 +}
210 +
211 +/* Called by gettimeofday().  Returns the number of microseconds since
212 + * the last interrupt. This is called with the xtime_lock held.*/
213 +static unsigned long get_offset_scx200hr(void)
214 +{
215 +       u32 delta;
216 +
217 +       /* Get the time now and determine how many cycles have
218 +        * transpired since the interrupt, adjusting for timer
219 +        * interrupt jitter. */
220 +       delta = ((SCx200HR_CLOCK() - clock_at_last_interrupt) << 8) + clock_delay;
221 +
222 +       /* Convert from cycles<<8 to microseconds */
223 +       return delta / HR_CYCLES_PER_US;
224 +}
225 +
226 +/* Returns the number of nanoseconds since the init of the timer. */
227 +static unsigned long long monotonic_clock_scx200hr(void)
228 +{
229 +       u32 delta, seq;
230 +       unsigned long long ns;
231 +
232 +       /* This function is *not* called with xtime_lock held, so we
233 +        * need to get the hr_lock to ensure we're not competing with
234 +        * mark_offset_scx200hr. */
235 +       do {
236 +               seq = read_seqbegin(&hr_lock);
237 +               ns = total_ns;
238 +               delta = SCx200HR_CLOCK() - clock_at_last_interrupt;
239 +       } while (read_seqretry(&hr_lock, seq));
240 +
241 +       /* Convert cycles to microseconds and add. */
242 +       return ns + delta * 1000 / HR_CYCLES_PER_US;
243 +}
244 +
245 +/* scx200hr timer_opts struct */
246 +struct timer_opts timer_scx200hr = {
247 +       .name = NAME,
248 +       .mark_offset = mark_offset_scx200hr, 
249 +       .get_offset = get_offset_scx200hr,
250 +       .monotonic_clock = monotonic_clock_scx200hr,
251 +       .delay = NULL
252 +};
253 +
254 +/* And the init_timer struct */
255 +struct init_timer_opts __devinitdata timer_scx200hr_init = {
256 +       .init = scx200hr_init,
257 +       .opts = &timer_scx200hr
258 +};
259 +
260 +
261 +/* Switch from the original timer to the high-resolution timer */
262 +void __devinit scx200hr_timer_enable(void)
263 +{
264 +        /* Make sure the timer was requested and that the
265 +         * configuration block is present */
266 +       if (!enable_scx200hr || !scx200_cb_present()) {
267 +               return;
268 +       }
269 +
270 +       /* Reserve the timer region for ourselves */
271 +       if (!request_region(scx200_cb_base + SCx200_TIMER_OFFSET,
272 +                           SCx200_TIMER_SIZE,
273 +                           "NatSemi SCx200 High-Resolution Timer")) {
274 +               printk(KERN_WARNING NAME ": unable to lock timer region\n");
275 +               return;
276 +       }
277 +
278 +       /* Configure the timer */
279 +       outb(0, scx200_cb_base + SCx200_TMCNFG_OFFSET);
280 +
281 +       /* Record the current value of the timer. */
282 +       clock_at_last_interrupt = SCx200HR_CLOCK();
283 +
284 +        /* Get the current value of the monotonic clock */
285 +       total_ns = cur_timer->monotonic_clock();
286 +
287 +        /* Switch from the original timer functions to ours, but keep
288 +         * the current delay function since loops_per_jiffy will have
289 +         * been computed using that */
290 +        timer_scx200hr.delay = cur_timer->delay;
291 +       cur_timer = &timer_scx200hr;
292 +
293 +       printk(KERN_INFO "switching to scx200 high-resolution timer (%lu cpt)\n",
294 +                HR_CYCLES_PER_TICK);
295 +}
296 diff -Naurp linux-2.6.12-rc6.orig/include/asm-i386/timer.h linux-2.6.12-rc6/include/asm-i386/timer.h
297 --- linux-2.6.12-rc6.orig/include/asm-i386/timer.h      2005-06-07 14:56:11.000000000 +0100
298 +++ linux-2.6.12-rc6/include/asm-i386/timer.h   2005-06-07 16:43:19.000000000 +0100
299 @@ -50,6 +50,9 @@ extern struct init_timer_opts timer_tsc_
300  #ifdef CONFIG_X86_CYCLONE_TIMER
301  extern struct init_timer_opts timer_cyclone_init;
302  #endif
303 +#ifdef CONFIG_SCx200HR_TIMER
304 +extern struct init_timer_opts timer_scx200hr_init;
305 +#endif
306  
307  extern unsigned long calibrate_tsc(void);
308  extern void init_cpu_khz(void);
309 diff -Naurp linux-2.6.12-rc6.orig/include/linux/scx200.h linux-2.6.12-rc6/include/linux/scx200.h
310 --- linux-2.6.12-rc6.orig/include/linux/scx200.h        2005-06-07 14:56:11.000000000 +0100
311 +++ linux-2.6.12-rc6/include/linux/scx200.h     2005-06-07 16:43:19.000000000 +0100
312 @@ -32,7 +32,7 @@ extern unsigned scx200_cb_base;
313  
314  /* High Resolution Timer */
315  #define SCx200_TIMER_OFFSET 0x08
316 -#define SCx200_TIMER_SIZE 0x05
317 +#define SCx200_TIMER_SIZE 0x06
318  
319  /* Clock Generators */
320  #define SCx200_CLOCKGEN_OFFSET 0x10