xref: /netbsd-src/sys/arch/arm/s3c2xx0/s3c2800_clk.c (revision 8e6ab8837d8d6b9198e67c1c445300b483e2f304)
1 /* $NetBSD: s3c2800_clk.c,v 1.6 2003/08/01 00:30:21 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 #include <sys/cdefs.h>
37 __KERNEL_RCSID(0, "$NetBSD: s3c2800_clk.c,v 1.6 2003/08/01 00:30:21 bsh 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 /*
242  * inittodr:
243  *
244  *	Initialize time from the time-of-day register.
245  */
246 void
247 inittodr(time_t base)
248 {
249 
250 	time.tv_sec = base;
251 	time.tv_usec = 0;
252 }
253 
254 /*
255  * resettodr:
256  *
257  *	Reset the time-of-day register with the current time.
258  */
259 void
260 resettodr(void)
261 {
262 }
263 
264 void
265 setstatclockrate(hz)
266 	int hz;
267 {
268 }
269 
270 
271 #define hardintr	(int (*)(void *))hardclock
272 #define statintr	(int (*)(void *))statclock
273 
274 void
275 cpu_initclocks()
276 {
277 	struct s3c2800_softc *sc = (struct s3c2800_softc *)s3c2xx0_softc;
278 	long tc;
279 	int prescaler;
280 	int pclk = s3c2xx0_softc->sc_pclk;
281 
282 	stathz = STATHZ;
283 	profhz = stathz;
284 
285 #define calc_time_constant(hz)					\
286 	do {							\
287 		prescaler = 1;					\
288 		do {						\
289 			++prescaler;				\
290 			tc = TIMER_FREQUENCY(pclk) /(hz)/ prescaler;	\
291 		} while( tc > 65536 );				\
292 	} while(0)
293 
294 
295 
296 	/* Use the channels 0 and 1 for hardclock and statclock, respectively */
297 	bus_space_write_4(sc->sc_sx.sc_iot, sc->sc_tmr0_ioh, TIMER_TMCON, 0);
298 	bus_space_write_4(sc->sc_sx.sc_iot, sc->sc_tmr1_ioh, TIMER_TMCON, 0);
299 
300 	calc_time_constant(hz);
301 	bus_space_write_4(sc->sc_sx.sc_iot, sc->sc_tmr0_ioh, TIMER_TMDAT,
302 	    ((prescaler - 1) << 16) | (tc - 1));
303 	timer0_prescaler = prescaler;
304 	timer0_reload_value = tc;
305 	timer0_mseccount = TIMER_FREQUENCY(pclk)/timer0_prescaler/1000 ;
306 
307 	printf("clock: hz=%d stathz = %d PCLK=%d prescaler=%d tc=%ld\n",
308 	    hz, stathz, pclk, prescaler, tc);
309 
310 	calc_time_constant(stathz);
311 	bus_space_write_4(sc->sc_sx.sc_iot, sc->sc_tmr1_ioh, TIMER_TMDAT,
312 	    ((prescaler - 1) << 16) | (tc - 1));
313 
314 
315 	s3c2800_intr_establish(S3C2800_INT_TIMER0, IPL_CLOCK,
316 			       IST_NONE, hardintr, 0);
317 	s3c2800_intr_establish(S3C2800_INT_TIMER1, IPL_STATCLOCK,
318 			       IST_NONE, statintr, 0);
319 
320 	/* start timers */
321 	bus_space_write_4(sc->sc_sx.sc_iot, sc->sc_tmr0_ioh, TIMER_TMCON,
322 	    TMCON_MUX_DIV32|TMCON_INTENA|TMCON_ENABLE);
323 	bus_space_write_4(sc->sc_sx.sc_iot, sc->sc_tmr1_ioh, TIMER_TMCON,
324 	    TMCON_MUX_DIV4|TMCON_INTENA|TMCON_ENABLE);
325 
326 	/* stop timer2 */
327 	{
328 		bus_space_handle_t tmp_ioh;
329 
330 		bus_space_map(sc->sc_sx.sc_iot, S3C2800_TIMER2_BASE,
331 		    S3C2800_TIMER_SIZE, 0, &tmp_ioh);
332 
333 		bus_space_write_4(sc->sc_sx.sc_iot, tmp_ioh,
334 		    TIMER_TMCON, 0);
335 
336 		bus_space_unmap(sc->sc_sx.sc_iot, tmp_ioh,
337 		    S3C2800_TIMER_SIZE);
338 
339 	}
340 }
341