xref: /netbsd-src/sys/arch/arm/s3c2xx0/s3c2800_clk.c (revision 27527e67bbdf8d9ec84fd58803048ed6d181ece2)
1 /* $NetBSD: s3c2800_clk.c,v 1.9 2005/12/24 20:06:52 perry 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.9 2005/12/24 20:06:52 perry 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(int newhz)
266 {
267 }
268 
269 
270 #define hardintr	(int (*)(void *))hardclock
271 #define statintr	(int (*)(void *))statclock
272 
273 void
274 cpu_initclocks()
275 {
276 	struct s3c2800_softc *sc = (struct s3c2800_softc *)s3c2xx0_softc;
277 	long tc;
278 	int prescaler;
279 	int pclk = s3c2xx0_softc->sc_pclk;
280 
281 	stathz = STATHZ;
282 	profhz = stathz;
283 
284 #define calc_time_constant(hz)					\
285 	do {							\
286 		prescaler = 1;					\
287 		do {						\
288 			++prescaler;				\
289 			tc = TIMER_FREQUENCY(pclk) /(hz)/ prescaler;	\
290 		} while( tc > 65536 );				\
291 	} while(0)
292 
293 
294 
295 	/* Use the channels 0 and 1 for hardclock and statclock, respectively */
296 	bus_space_write_4(sc->sc_sx.sc_iot, sc->sc_tmr0_ioh, TIMER_TMCON, 0);
297 	bus_space_write_4(sc->sc_sx.sc_iot, sc->sc_tmr1_ioh, TIMER_TMCON, 0);
298 
299 	calc_time_constant(hz);
300 	bus_space_write_4(sc->sc_sx.sc_iot, sc->sc_tmr0_ioh, TIMER_TMDAT,
301 	    ((prescaler - 1) << 16) | (tc - 1));
302 	timer0_prescaler = prescaler;
303 	timer0_reload_value = tc;
304 	timer0_mseccount = TIMER_FREQUENCY(pclk)/timer0_prescaler/1000 ;
305 
306 	printf("clock: hz=%d stathz = %d PCLK=%d prescaler=%d tc=%ld\n",
307 	    hz, stathz, pclk, prescaler, tc);
308 
309 	calc_time_constant(stathz);
310 	bus_space_write_4(sc->sc_sx.sc_iot, sc->sc_tmr1_ioh, TIMER_TMDAT,
311 	    ((prescaler - 1) << 16) | (tc - 1));
312 
313 
314 	s3c2800_intr_establish(S3C2800_INT_TIMER0, IPL_CLOCK,
315 			       IST_NONE, hardintr, 0);
316 	s3c2800_intr_establish(S3C2800_INT_TIMER1, IPL_STATCLOCK,
317 			       IST_NONE, statintr, 0);
318 
319 	/* start timers */
320 	bus_space_write_4(sc->sc_sx.sc_iot, sc->sc_tmr0_ioh, TIMER_TMCON,
321 	    TMCON_MUX_DIV32|TMCON_INTENA|TMCON_ENABLE);
322 	bus_space_write_4(sc->sc_sx.sc_iot, sc->sc_tmr1_ioh, TIMER_TMCON,
323 	    TMCON_MUX_DIV4|TMCON_INTENA|TMCON_ENABLE);
324 
325 	/* stop timer2 */
326 	{
327 		bus_space_handle_t tmp_ioh;
328 
329 		bus_space_map(sc->sc_sx.sc_iot, S3C2800_TIMER2_BASE,
330 		    S3C2800_TIMER_SIZE, 0, &tmp_ioh);
331 
332 		bus_space_write_4(sc->sc_sx.sc_iot, tmp_ioh,
333 		    TIMER_TMCON, 0);
334 
335 		bus_space_unmap(sc->sc_sx.sc_iot, tmp_ioh,
336 		    S3C2800_TIMER_SIZE);
337 
338 	}
339 }
340