1 /* $NetBSD: s3c2800_clk.c,v 1.14 2008/07/04 11:59:45 bsh Exp $ */
4 * Copyright (c) 2002 Fujitsu Component Limited
5 * Copyright (c) 2002 Genetec Corporation
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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.
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
36 #include <sys/cdefs.h>
37 __KERNEL_RCSID(0, "$NetBSD: s3c2800_clk.c,v 1.14 2008/07/04 11:59:45 bsh Exp $");
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/kernel.h>
42 #include <sys/atomic.h>
44 #include <sys/timetc.h>
46 #include <machine/bus.h>
47 #include <machine/intr.h>
48 #include <arm/cpufunc.h>
50 #include <arm/s3c2xx0/s3c2800reg.h>
51 #include <arm/s3c2xx0/s3c2800var.h>
58 #define TIMER_FREQUENCY(pclk) ((pclk)/32) /* divider=1/32 */
60 static unsigned int timer0_reload_value
;
61 static unsigned int timer0_prescaler
;
62 static unsigned int timer0_mseccount
;
64 #define usec_to_counter(t) \
65 ((timer0_mseccount*(t))/1000)
67 #define counter_to_usec(c,pclk) \
68 (((c)*timer0_prescaler*1000)/(TIMER_FREQUENCY(pclk)/1000))
70 static u_int
s3c2800_get_timecount(struct timecounter
*);
72 static struct timecounter s3c2800_timecounter
= {
73 s3c2800_get_timecount
, /* get_timecount */
75 0xffffffff, /* counter_mask */
77 "s3c23800", /* name */
83 static volatile uint32_t s3c2800_base
;
86 s3c2800_get_timecount(struct timecounter
*tc
)
88 struct s3c2800_softc
*sc
= (struct s3c2800_softc
*) s3c2xx0_softc
;
89 int save
, int_pend0
, int_pend1
, count
;
91 save
= disable_interrupts(I32_bit
);
94 int_pend0
= S3C2800_INT_TIMER0
&
95 bus_space_read_4(sc
->sc_sx
.sc_iot
, sc
->sc_sx
.sc_intctl_ioh
,
97 count
= bus_space_read_2(sc
->sc_sx
.sc_iot
, sc
->sc_tmr0_ioh
,
102 int_pend1
= S3C2800_INT_TIMER0
&
103 bus_space_read_4(sc
->sc_sx
.sc_iot
, sc
->sc_sx
.sc_intctl_ioh
,
105 if( int_pend0
== int_pend1
)
109 * Down counter reached to zero while we were reading
110 * timer values. do it again to get consistent values.
112 int_pend0
= int_pend1
;
113 count
= bus_space_read_2(sc
->sc_sx
.sc_iot
, sc
->sc_tmr0_ioh
,
117 if( __predict_false(count
> timer0_reload_value
) ){
119 * Buggy Hardware Warning --- sometimes timer counter
120 * reads bogus value like 0xffff. I guess it happens when
121 * the timer is reloaded.
124 printf( "Bogus value from timer counter: %d\n", count
);
129 restore_interrupts(save
);
132 count
-= timer0_reload_value
;
134 return s3c2800_base
- count
;
138 read_timer(struct s3c2800_softc
*sc
)
143 count
= bus_space_read_2(sc
->sc_sx
.sc_iot
, sc
->sc_tmr0_ioh
,
145 } while ( __predict_false(count
> timer0_reload_value
) );
153 * Delay for at least N microseconds.
158 struct s3c2800_softc
*sc
= (struct s3c2800_softc
*) s3c2xx0_softc
;
162 if ( timer0_reload_value
== 0 ){
163 /* not initialized yet */
167 for (m
=0; m
<100; ++m
)
173 /* read down counter */
176 ucnt
= usec_to_counter(n
);
182 delta
+= timer0_reload_value
;
184 if (delta
< 0 || delta
> timer0_reload_value
)
185 panic("wrong value from timer counter");
188 if((u_int
)delta
< ucnt
){
189 ucnt
-= (u_int
)delta
;
200 setstatclockrate(int newhz
)
207 atomic_add_32(&s3c2800_base
, timer0_reload_value
);
209 hardclock((struct clockframe
*)arg
);
217 statclock((struct clockframe
*)arg
);
225 struct s3c2800_softc
*sc
= (struct s3c2800_softc
*)s3c2xx0_softc
;
228 int pclk
= s3c2xx0_softc
->sc_pclk
;
233 #define calc_time_constant(hz) \
238 tc = TIMER_FREQUENCY(pclk) /(hz)/ prescaler; \
239 } while( tc > 65536 ); \
244 /* Use the channels 0 and 1 for hardclock and statclock, respectively */
245 bus_space_write_4(sc
->sc_sx
.sc_iot
, sc
->sc_tmr0_ioh
, TIMER_TMCON
, 0);
246 bus_space_write_4(sc
->sc_sx
.sc_iot
, sc
->sc_tmr1_ioh
, TIMER_TMCON
, 0);
248 calc_time_constant(hz
);
249 bus_space_write_4(sc
->sc_sx
.sc_iot
, sc
->sc_tmr0_ioh
, TIMER_TMDAT
,
250 ((prescaler
- 1) << 16) | (tc
- 1));
251 timer0_prescaler
= prescaler
;
252 timer0_reload_value
= tc
;
253 timer0_mseccount
= TIMER_FREQUENCY(pclk
)/timer0_prescaler
/1000 ;
255 printf("clock: hz=%d stathz = %d PCLK=%d prescaler=%d tc=%ld\n",
256 hz
, stathz
, pclk
, prescaler
, tc
);
258 calc_time_constant(stathz
);
259 bus_space_write_4(sc
->sc_sx
.sc_iot
, sc
->sc_tmr1_ioh
, TIMER_TMDAT
,
260 ((prescaler
- 1) << 16) | (tc
- 1));
263 s3c2800_intr_establish(S3C2800_INT_TIMER0
, IPL_CLOCK
,
264 IST_NONE
, hardintr
, 0);
265 s3c2800_intr_establish(S3C2800_INT_TIMER1
, IPL_HIGH
,
266 IST_NONE
, statintr
, 0);
269 bus_space_write_4(sc
->sc_sx
.sc_iot
, sc
->sc_tmr0_ioh
, TIMER_TMCON
,
270 TMCON_MUX_DIV32
|TMCON_INTENA
|TMCON_ENABLE
);
271 bus_space_write_4(sc
->sc_sx
.sc_iot
, sc
->sc_tmr1_ioh
, TIMER_TMCON
,
272 TMCON_MUX_DIV4
|TMCON_INTENA
|TMCON_ENABLE
);
276 bus_space_handle_t tmp_ioh
;
278 bus_space_map(sc
->sc_sx
.sc_iot
, S3C2800_TIMER2_BASE
,
279 S3C2800_TIMER_SIZE
, 0, &tmp_ioh
);
281 bus_space_write_4(sc
->sc_sx
.sc_iot
, tmp_ioh
,
284 bus_space_unmap(sc
->sc_sx
.sc_iot
, tmp_ioh
,
289 s3c2800_timecounter
.tc_frequency
= TIMER_FREQUENCY(pclk
) / timer0_prescaler
;
290 tc_init(&s3c2800_timecounter
);