s3c2800_clk.c revision 1.4 1 /* $NetBSD: s3c2800_clk.c,v 1.4 2003/05/13 08:07:39 bsh Exp $ */
2
3 /*
4 * Copyright (c) 2002 Fujitsu Component Limited
5 * Copyright (c) 2002 Genetec Corporation
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of The Fujitsu Component Limited nor the name of
17 * Genetec corporation may not be used to endorse or promote products
18 * derived from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY FUJITSU COMPONENT LIMITED AND GENETEC
21 * CORPORATION ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
22 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
23 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 * DISCLAIMED. IN NO EVENT SHALL FUJITSU COMPONENT LIMITED OR GENETEC
25 * CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
28 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
29 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35
36 /*
37 * Clock & Power Management
38 */
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/kernel.h>
42 #include <sys/time.h>
43
44 #include <machine/bus.h>
45 #include <machine/intr.h>
46 #include <arm/cpufunc.h>
47
48 #include <arm/s3c2xx0/s3c2800reg.h>
49 #include <arm/s3c2xx0/s3c2800var.h>
50
51
52 #ifndef STATHZ
53 #define STATHZ 64
54 #endif
55
56 #define TIMER_FREQUENCY(pclk) ((pclk)/32) /* divider=1/32 */
57
58 #define TIMER_RELOAD_VAL 1000
59 #define COUNTS_PER_USEC 100
60
61 static unsigned int timer0_reload_value;
62 static unsigned int timer0_prescaler;
63 static unsigned int timer0_mseccount;
64
65 #define usec_to_counter(t) \
66 ((timer0_mseccount*(t))/1000)
67
68 #define counter_to_usec(c,pclk) \
69 (((c)*timer0_prescaler*1000)/(TIMER_FREQUENCY(pclk)/1000))
70
71 /*
72 * microtime:
73 *
74 * Fill in the specified timeval struct with the current time
75 * accurate to the microsecond.
76 */
77 void
78 microtime(struct timeval *tvp)
79 {
80 struct s3c2800_softc *sc = (struct s3c2800_softc *) s3c2xx0_softc;
81 int save, int_pend0, int_pend1, count, delta;
82 static struct timeval last;
83 int pclk = s3c2xx0_softc->sc_pclk;
84
85 if( timer0_reload_value == 0 ){
86 /* not initialized yet */
87 tvp->tv_sec = 0;
88 tvp->tv_usec = 0;
89 return;
90 }
91
92 save = disable_interrupts(I32_bit);
93
94 again:
95 int_pend0 = S3C2800_INT_TIMER0 &
96 bus_space_read_4(sc->sc_sx.sc_iot, sc->sc_sx.sc_intctl_ioh,
97 INTCTL_SRCPND);
98 count = bus_space_read_2(sc->sc_sx.sc_iot, sc->sc_tmr0_ioh,
99 TIMER_TMCNT);
100
101 for (;;){
102
103 int_pend1 = S3C2800_INT_TIMER0 &
104 bus_space_read_4(sc->sc_sx.sc_iot, sc->sc_sx.sc_intctl_ioh,
105 INTCTL_SRCPND);
106 if( int_pend0 == int_pend1 )
107 break;
108
109 /*
110 * Down counter reached to zero while we were reading
111 * timer values. do it again to get consistent values.
112 */
113 int_pend0 = int_pend1;
114 count = bus_space_read_2(sc->sc_sx.sc_iot, sc->sc_tmr0_ioh,
115 TIMER_TMCNT);
116 }
117
118 if( __predict_false(count > timer0_reload_value) ){
119 /*
120 * Buggy Hardware Warning --- sometimes timer counter
121 * reads bogus value like 0xffff. I guess it happens when
122 * the timer is reloaded.
123 */
124 #if 0
125 printf( "Bogus value from timer counter: %d\n", count );
126 #endif
127 goto again;
128 }
129
130 /* copy system time */
131 *tvp = time;
132
133 restore_interrupts(save);
134
135 delta = timer0_reload_value - count;
136
137 if( int_pend1 ){
138 /*
139 * down counter underflow, but
140 * clock interrupt have not serviced yet
141 */
142 #if 1
143 tvp->tv_usec += tick;
144 #else
145 delta = 0;
146 #endif
147 }
148
149 tvp->tv_usec += counter_to_usec(delta, pclk);
150
151 /* Make sure microseconds doesn't overflow. */
152 tvp->tv_sec += tvp->tv_usec / 1000000;
153 tvp->tv_usec = tvp->tv_usec % 1000000;
154
155 if (last.tv_sec &&
156 (tvp->tv_sec < last.tv_sec ||
157 (tvp->tv_sec == last.tv_sec &&
158 tvp->tv_usec < last.tv_usec) ) ){
159
160 /* XXX: This happens very often when the kernel runs
161 under Multi-ICE */
162 #if 0
163 printf("time reversal: %ld.%06ld(%d,%d) -> %ld.%06ld(%d,%d)\n",
164 last.tv_sec, last.tv_usec,
165 last_count, last_pend,
166 tvp->tv_sec, tvp->tv_usec,
167 count, int_pend1 );
168 #endif
169
170 /* make sure the time has advanced. */
171 *tvp = last;
172 tvp->tv_usec++;
173 if( tvp->tv_usec >= 1000000 ){
174 tvp->tv_usec -= 1000000;
175 tvp->tv_sec++;
176 }
177 }
178
179 last = *tvp;
180 }
181
182 static __inline int
183 read_timer(struct s3c2800_softc *sc)
184 {
185 int count;
186
187 do {
188 count = bus_space_read_2(sc->sc_sx.sc_iot, sc->sc_tmr0_ioh,
189 TIMER_TMCNT);
190 } while ( __predict_false(count > timer0_reload_value) );
191
192 return count;
193 }
194
195 /*
196 * delay:
197 *
198 * Delay for at least N microseconds.
199 */
200 void
201 delay(u_int n)
202 {
203 struct s3c2800_softc *sc = (struct s3c2800_softc *) s3c2xx0_softc;
204 int v0, v1, delta;
205 u_int ucnt;
206
207 if ( timer0_reload_value == 0 ){
208 /* not initialized yet */
209 while ( n-- > 0 ){
210 int m;
211
212 for (m=0; m<100; ++m )
213 ;
214 }
215 return;
216 }
217
218 /* read down counter */
219 v0 = read_timer(sc);
220
221 ucnt = usec_to_counter(n);
222
223 while( ucnt > 0 ) {
224 v1 = read_timer(sc);
225 delta = v0 - v1;
226 if ( delta < 0 )
227 delta += timer0_reload_value;
228 #ifdef DEBUG
229 if (delta < 0 || delta > timer0_reload_value)
230 panic("wrong value from timer counter");
231 #endif
232
233 if((u_int)delta < ucnt){
234 ucnt -= (u_int)delta;
235 v0 = v1;
236 }
237 else {
238 ucnt = 0;
239 }
240 }
241 /*NOTREACHED*/
242 }
243
244 /*
245 * inittodr:
246 *
247 * Initialize time from the time-of-day register.
248 */
249 void
250 inittodr(time_t base)
251 {
252
253 time.tv_sec = base;
254 time.tv_usec = 0;
255 }
256
257 /*
258 * resettodr:
259 *
260 * Reset the time-of-day register with the current time.
261 */
262 void
263 resettodr(void)
264 {
265 }
266
267 void
268 setstatclockrate(hz)
269 int hz;
270 {
271 }
272
273
274 #define hardintr (int (*)(void *))hardclock
275 #define statintr (int (*)(void *))statclock
276
277 void
278 cpu_initclocks()
279 {
280 struct s3c2800_softc *sc = (struct s3c2800_softc *)s3c2xx0_softc;
281 long tc;
282 int prescaler;
283 int pclk = s3c2xx0_softc->sc_pclk;
284
285 stathz = STATHZ;
286 profhz = stathz;
287
288 #define calc_time_constant(hz) \
289 do { \
290 prescaler = 1; \
291 do { \
292 ++prescaler; \
293 tc = TIMER_FREQUENCY(pclk) /(hz)/ prescaler; \
294 } while( tc > 65536 ); \
295 } while(0)
296
297
298
299 /* Use the channels 0 and 1 for hardclock and statclock, respectively */
300 bus_space_write_4(sc->sc_sx.sc_iot, sc->sc_tmr0_ioh, TIMER_TMCON, 0);
301 bus_space_write_4(sc->sc_sx.sc_iot, sc->sc_tmr1_ioh, TIMER_TMCON, 0);
302
303 calc_time_constant(hz);
304 bus_space_write_4(sc->sc_sx.sc_iot, sc->sc_tmr0_ioh, TIMER_TMDAT,
305 ((prescaler - 1) << 16) | (tc - 1));
306 timer0_prescaler = prescaler;
307 timer0_reload_value = tc;
308 timer0_mseccount = TIMER_FREQUENCY(pclk)/timer0_prescaler/1000 ;
309
310 printf("clock: hz=%d stathz = %d PCLK=%d prescaler=%d tc=%ld\n",
311 hz, stathz, pclk, prescaler, tc);
312
313 calc_time_constant(stathz);
314 bus_space_write_4(sc->sc_sx.sc_iot, sc->sc_tmr1_ioh, TIMER_TMDAT,
315 ((prescaler - 1) << 16) | (tc - 1));
316
317
318 s3c2800_intr_establish(S3C2800_INT_TIMER0, IPL_CLOCK,
319 IST_NONE, hardintr, 0);
320 s3c2800_intr_establish(S3C2800_INT_TIMER1, IPL_STATCLOCK,
321 IST_NONE, statintr, 0);
322
323 /* start timers */
324 bus_space_write_4(sc->sc_sx.sc_iot, sc->sc_tmr0_ioh, TIMER_TMCON,
325 TMCON_MUX_DIV32|TMCON_INTENA|TMCON_ENABLE);
326 bus_space_write_4(sc->sc_sx.sc_iot, sc->sc_tmr1_ioh, TIMER_TMCON,
327 TMCON_MUX_DIV4|TMCON_INTENA|TMCON_ENABLE);
328
329 /* stop timer2 */
330 {
331 bus_space_handle_t tmp_ioh;
332
333 bus_space_map(sc->sc_sx.sc_iot, S3C2800_TIMER2_BASE,
334 S3C2800_TIMER_SIZE, 0, &tmp_ioh);
335
336 bus_space_write_4(sc->sc_sx.sc_iot, tmp_ioh,
337 TIMER_TMCON, 0);
338
339 bus_space_unmap(sc->sc_sx.sc_iot, tmp_ioh,
340 S3C2800_TIMER_SIZE);
341
342 }
343 }
344