Annotation of sys/arch/arm/s3c2xx0/s3c2800_clk.c, Revision 1.1.1.1
1.1 nbrk 1: /* $NetBSD: s3c2800_clk.c,v 1.10 2007/01/06 16:18:18 christos 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: #include <sys/cdefs.h>
37: __KERNEL_RCSID(0, "$NetBSD: s3c2800_clk.c,v 1.10 2007/01/06 16:18:18 christos Exp $");
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: static unsigned int timer0_reload_value;
59: static unsigned int timer0_prescaler;
60: static unsigned int timer0_mseccount;
61:
62: #define usec_to_counter(t) \
63: ((timer0_mseccount*(t))/1000)
64:
65: #define counter_to_usec(c,pclk) \
66: (((c)*timer0_prescaler*1000)/(TIMER_FREQUENCY(pclk)/1000))
67:
68: /*
69: * microtime:
70: *
71: * Fill in the specified timeval struct with the current time
72: * accurate to the microsecond.
73: */
74: void
75: microtime(struct timeval *tvp)
76: {
77: struct s3c2800_softc *sc = (struct s3c2800_softc *) s3c2xx0_softc;
78: int save, int_pend0, int_pend1, count, delta;
79: static struct timeval last;
80: int pclk = s3c2xx0_softc->sc_pclk;
81:
82: if( timer0_reload_value == 0 ){
83: /* not initialized yet */
84: tvp->tv_sec = 0;
85: tvp->tv_usec = 0;
86: return;
87: }
88:
89: save = disable_interrupts(I32_bit);
90:
91: again:
92: int_pend0 = S3C2800_INT_TIMER0 &
93: bus_space_read_4(sc->sc_sx.sc_iot, sc->sc_sx.sc_intctl_ioh,
94: INTCTL_SRCPND);
95: count = bus_space_read_2(sc->sc_sx.sc_iot, sc->sc_tmr0_ioh,
96: TIMER_TMCNT);
97:
98: for (;;){
99:
100: int_pend1 = S3C2800_INT_TIMER0 &
101: bus_space_read_4(sc->sc_sx.sc_iot, sc->sc_sx.sc_intctl_ioh,
102: INTCTL_SRCPND);
103: if( int_pend0 == int_pend1 )
104: break;
105:
106: /*
107: * Down counter reached to zero while we were reading
108: * timer values. do it again to get consistent values.
109: */
110: int_pend0 = int_pend1;
111: count = bus_space_read_2(sc->sc_sx.sc_iot, sc->sc_tmr0_ioh,
112: TIMER_TMCNT);
113: }
114:
115: if( __predict_false(count > timer0_reload_value) ){
116: /*
117: * Buggy Hardware Warning --- sometimes timer counter
118: * reads bogus value like 0xffff. I guess it happens when
119: * the timer is reloaded.
120: */
121: #if 0
122: printf( "Bogus value from timer counter: %d\n", count );
123: #endif
124: goto again;
125: }
126:
127: /* copy system time */
128: *tvp = time;
129:
130: restore_interrupts(save);
131:
132: delta = timer0_reload_value - count;
133:
134: if( int_pend1 ){
135: /*
136: * down counter underflow, but
137: * clock interrupt have not serviced yet
138: */
139: #if 1
140: tvp->tv_usec += tick;
141: #else
142: delta = 0;
143: #endif
144: }
145:
146: tvp->tv_usec += counter_to_usec(delta, pclk);
147:
148: /* Make sure microseconds doesn't overflow. */
149: tvp->tv_sec += tvp->tv_usec / 1000000;
150: tvp->tv_usec = tvp->tv_usec % 1000000;
151:
152: if (last.tv_sec &&
153: (tvp->tv_sec < last.tv_sec ||
154: (tvp->tv_sec == last.tv_sec &&
155: tvp->tv_usec < last.tv_usec) ) ){
156:
157: /* XXX: This happens very often when the kernel runs
158: under Multi-ICE */
159: #if 0
160: printf("time reversal: %ld.%06ld(%d,%d) -> %ld.%06ld(%d,%d)\n",
161: last.tv_sec, last.tv_usec,
162: last_count, last_pend,
163: tvp->tv_sec, tvp->tv_usec,
164: count, int_pend1 );
165: #endif
166:
167: /* make sure the time has advanced. */
168: *tvp = last;
169: tvp->tv_usec++;
170: if( tvp->tv_usec >= 1000000 ){
171: tvp->tv_usec -= 1000000;
172: tvp->tv_sec++;
173: }
174: }
175:
176: last = *tvp;
177: }
178:
179: static inline int
180: read_timer(struct s3c2800_softc *sc)
181: {
182: int count;
183:
184: do {
185: count = bus_space_read_2(sc->sc_sx.sc_iot, sc->sc_tmr0_ioh,
186: TIMER_TMCNT);
187: } while ( __predict_false(count > timer0_reload_value) );
188:
189: return count;
190: }
191:
192: /*
193: * delay:
194: *
195: * Delay for at least N microseconds.
196: */
197: void
198: delay(u_int n)
199: {
200: struct s3c2800_softc *sc = (struct s3c2800_softc *) s3c2xx0_softc;
201: int v0, v1, delta;
202: u_int ucnt;
203:
204: if ( timer0_reload_value == 0 ){
205: /* not initialized yet */
206: while ( n-- > 0 ){
207: int m;
208:
209: for (m=0; m<100; ++m )
210: ;
211: }
212: return;
213: }
214:
215: /* read down counter */
216: v0 = read_timer(sc);
217:
218: ucnt = usec_to_counter(n);
219:
220: while( ucnt > 0 ) {
221: v1 = read_timer(sc);
222: delta = v0 - v1;
223: if ( delta < 0 )
224: delta += timer0_reload_value;
225: #ifdef DEBUG
226: if (delta < 0 || delta > timer0_reload_value)
227: panic("wrong value from timer counter");
228: #endif
229:
230: if((u_int)delta < ucnt){
231: ucnt -= (u_int)delta;
232: v0 = v1;
233: }
234: else {
235: ucnt = 0;
236: }
237: }
238: /*NOTREACHED*/
239: }
240:
241: void
242: setstatclockrate(int newhz)
243: {
244: }
245:
246:
247: #define hardintr (int (*)(void *))hardclock
248: #define statintr (int (*)(void *))statclock
249:
250: void
251: cpu_initclocks()
252: {
253: struct s3c2800_softc *sc = (struct s3c2800_softc *)s3c2xx0_softc;
254: long tc;
255: int prescaler;
256: int pclk = s3c2xx0_softc->sc_pclk;
257:
258: stathz = STATHZ;
259: profhz = stathz;
260:
261: #define calc_time_constant(hz) \
262: do { \
263: prescaler = 1; \
264: do { \
265: ++prescaler; \
266: tc = TIMER_FREQUENCY(pclk) /(hz)/ prescaler; \
267: } while( tc > 65536 ); \
268: } while(0)
269:
270:
271:
272: /* Use the channels 0 and 1 for hardclock and statclock, respectively */
273: bus_space_write_4(sc->sc_sx.sc_iot, sc->sc_tmr0_ioh, TIMER_TMCON, 0);
274: bus_space_write_4(sc->sc_sx.sc_iot, sc->sc_tmr1_ioh, TIMER_TMCON, 0);
275:
276: calc_time_constant(hz);
277: bus_space_write_4(sc->sc_sx.sc_iot, sc->sc_tmr0_ioh, TIMER_TMDAT,
278: ((prescaler - 1) << 16) | (tc - 1));
279: timer0_prescaler = prescaler;
280: timer0_reload_value = tc;
281: timer0_mseccount = TIMER_FREQUENCY(pclk)/timer0_prescaler/1000 ;
282:
283: printf("clock: hz=%d stathz = %d PCLK=%d prescaler=%d tc=%ld\n",
284: hz, stathz, pclk, prescaler, tc);
285:
286: calc_time_constant(stathz);
287: bus_space_write_4(sc->sc_sx.sc_iot, sc->sc_tmr1_ioh, TIMER_TMDAT,
288: ((prescaler - 1) << 16) | (tc - 1));
289:
290:
291: s3c2800_intr_establish(S3C2800_INT_TIMER0, IPL_CLOCK,
292: IST_NONE, hardintr, 0);
293: s3c2800_intr_establish(S3C2800_INT_TIMER1, IPL_STATCLOCK,
294: IST_NONE, statintr, 0);
295:
296: /* start timers */
297: bus_space_write_4(sc->sc_sx.sc_iot, sc->sc_tmr0_ioh, TIMER_TMCON,
298: TMCON_MUX_DIV32|TMCON_INTENA|TMCON_ENABLE);
299: bus_space_write_4(sc->sc_sx.sc_iot, sc->sc_tmr1_ioh, TIMER_TMCON,
300: TMCON_MUX_DIV4|TMCON_INTENA|TMCON_ENABLE);
301:
302: /* stop timer2 */
303: {
304: bus_space_handle_t tmp_ioh;
305:
306: bus_space_map(sc->sc_sx.sc_iot, S3C2800_TIMER2_BASE,
307: S3C2800_TIMER_SIZE, 0, &tmp_ioh);
308:
309: bus_space_write_4(sc->sc_sx.sc_iot, tmp_ioh,
310: TIMER_TMCON, 0);
311:
312: bus_space_unmap(sc->sc_sx.sc_iot, tmp_ioh,
313: S3C2800_TIMER_SIZE);
314:
315: }
316: }
CVSweb