xref: /netbsd/sys/arch/sh3/sh3/clock.c (revision 6550d01e)
1 /*	$NetBSD: clock.c,v 1.40 2009/03/18 10:22:36 cegger Exp $	*/
2 
3 /*-
4  * Copyright (c) 2002 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by UCHIYAMA Yasushi.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: clock.c,v 1.40 2009/03/18 10:22:36 cegger Exp $");
34 
35 #include "opt_pclock.h"
36 #include "opt_hz.h"
37 #include "wdog.h"
38 
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/kernel.h>
42 #include <sys/device.h>
43 #include <sys/timetc.h>
44 
45 #include <dev/clock_subr.h>
46 
47 #include <sh3/clock.h>
48 #include <sh3/exception.h>
49 #include <sh3/rtcreg.h>
50 #include <sh3/tmureg.h>
51 #include <sh3/wdogvar.h>
52 #include <sh3/wdtreg.h>
53 
54 #include <machine/intr.h>
55 
56 #ifndef HZ
57 #define	HZ		64
58 #endif
59 #define	SH_RTC_CLOCK	16384	/* Hz */
60 
61 /*
62  * NetBSD/sh3 clock module
63  *  + default 64Hz
64  *  + use TMU channel 0 as clock interrupt source.
65  *  + use TMU channel 1 as emulated software interrupt soruce.
66  *  + use TMU channel 2 as freerunning counter for timecounter.
67  *  + If RTC module is active, TMU channel 0 input source is RTC output.
68  *    (16.384kHz)
69  */
70 struct {
71 	/* Hard clock */
72 	uint32_t hz_cnt;	/* clock interrupt interval count */
73 	uint32_t cpucycle_1us;	/* calibrated loop variable (1 us) */
74 	uint32_t tmuclk;	/* source clock of TMU0 (Hz) */
75 
76 	uint32_t pclock;	/* PCLOCK */
77 	uint32_t cpuclock;	/* CPU clock */
78 	int flags;
79 
80 	struct timecounter tc;
81 } sh_clock = {
82 #ifdef PCLOCK
83 	.pclock = PCLOCK,
84 #endif
85 };
86 
87 uint32_t maxwdog;
88 
89 struct evcnt sh_hardclock_evcnt =
90     EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, "tmu0", "hardclock");
91 
92 /* TMU */
93 /* interrupt handler is timing critical. prepared for each. */
94 int sh3_clock_intr(void *);
95 int sh4_clock_intr(void *);
96 u_int sh_timecounter_get(struct timecounter *);
97 
98 /*
99  * Estimate CPU and Peripheral clock.
100  */
101 #define	TMU_START(x)							\
102 do {									\
103 	_reg_bclr_1(SH_(TSTR), TSTR_STR##x);				\
104 	_reg_write_4(SH_(TCNT ## x), 0xffffffff);			\
105 	_reg_bset_1(SH_(TSTR), TSTR_STR##x);				\
106 } while (/*CONSTCOND*/0)
107 
108 #define	TMU_ELAPSED(x)							\
109 	(0xffffffff - _reg_read_4(SH_(TCNT ## x)))
110 
111 void
112 sh_clock_init(int flags)
113 {
114 	uint32_t s, t0, cnt_1s;
115 
116 	sh_clock.flags = flags;
117 
118 	/* Initialize TMU */
119 	_reg_write_2(SH_(TCR0), 0);
120 	_reg_write_2(SH_(TCR1), 0);
121 	_reg_write_2(SH_(TCR2), 0);
122 
123 	/* Reset RTC alarm and interrupt */
124 	_reg_write_1(SH_(RCR1), 0);
125 
126 	/* Stop all counter */
127 	_reg_write_1(SH_(TSTR), 0);
128 
129 	/*
130 	 * Estimate CPU clock.
131 	 */
132 	if (sh_clock.flags & SH_CLOCK_NORTC) {
133 		/* Set TMU channel 0 source to PCLOCK / 16 */
134 		_reg_write_2(SH_(TCR0), TCR_TPSC_P16);
135 		sh_clock.tmuclk = sh_clock.pclock / 16;
136 	} else {
137 		/* Set TMU channel 0 source to RTC counter clock (16.384kHz) */
138 		_reg_write_2(SH_(TCR0),
139 		    CPU_IS_SH3 ? SH3_TCR_TPSC_RTC : SH4_TCR_TPSC_RTC);
140 		sh_clock.tmuclk = SH_RTC_CLOCK;
141 
142 		/* Make sure RTC oscillator is enabled */
143 		_reg_bset_1(SH_(RCR2), SH_RCR2_ENABLE);
144 	}
145 
146 	s = _cpu_exception_suspend();
147 	_cpu_spin(1);	/* load function on cache. */
148 	TMU_START(0);
149 	_cpu_spin(10000000);
150 	t0 = TMU_ELAPSED(0);
151 	_cpu_exception_resume(s);
152 
153 	sh_clock.cpucycle_1us = (sh_clock.tmuclk * 10) / t0;
154 
155 	cnt_1s = ((uint64_t)sh_clock.tmuclk * 10000000 * 10 + t0/2) / t0;
156 	if (CPU_IS_SH4)
157 		sh_clock.cpuclock = cnt_1s / 2; /* two-issue */
158 	else
159 		sh_clock.cpuclock = cnt_1s;
160 
161 	/*
162 	 * Estimate PCLOCK
163 	 */
164 	if (sh_clock.pclock == 0) {
165 		uint32_t t1;
166 
167 		/* set TMU channel 1 source to PCLOCK / 4 */
168 		_reg_write_2(SH_(TCR1), TCR_TPSC_P4);
169 		s = _cpu_exception_suspend();
170 		_cpu_spin(1);	/* load function on cache. */
171 		TMU_START(0);
172 		TMU_START(1);
173 		_cpu_spin(cnt_1s); /* 1 sec. */
174 		t0 = TMU_ELAPSED(0);
175 		t1 = TMU_ELAPSED(1);
176 		_cpu_exception_resume(s);
177 
178 		sh_clock.pclock
179 		    = ((uint64_t)t1 * 4 * SH_RTC_CLOCK + t0/2) / t0;
180 	}
181 
182 	/* Stop all counter */
183 	_reg_write_1(SH_(TSTR), 0);
184 
185 #undef TMU_START
186 #undef TMU_ELAPSED
187 }
188 
189 int
190 sh_clock_get_cpuclock(void)
191 {
192 
193 	return (sh_clock.cpuclock);
194 }
195 
196 int
197 sh_clock_get_pclock(void)
198 {
199 
200 	return (sh_clock.pclock);
201 }
202 
203 void
204 setstatclockrate(int newhz)
205 {
206 	/* XXX not yet */
207 }
208 
209 u_int
210 sh_timecounter_get(struct timecounter *tc)
211 {
212 
213 	return 0xffffffff - _reg_read_4(SH_(TCNT2));
214 }
215 
216 /*
217  *  Wait at least `n' usec.
218  */
219 void
220 delay(int n)
221 {
222 
223 	_cpu_spin(sh_clock.cpucycle_1us * n);
224 }
225 
226 /*
227  * Start the clock interrupt.
228  */
229 void
230 cpu_initclocks(void)
231 {
232 
233 	if (sh_clock.pclock == 0)
234 		panic("No PCLOCK information.");
235 
236 	/* Set global variables. */
237 	hz = HZ;
238 	tick = 1000000 / hz;
239 
240 	/*
241 	 * Use TMU channel 0 as hard clock
242 	 */
243 	_reg_bclr_1(SH_(TSTR), TSTR_STR0);
244 
245 	if (sh_clock.flags & SH_CLOCK_NORTC) {
246 		/* use PCLOCK/16 as TMU0 source */
247 		_reg_write_2(SH_(TCR0), TCR_UNIE | TCR_TPSC_P16);
248 	} else {
249 		/* use RTC clock as TMU0 source */
250 		_reg_write_2(SH_(TCR0), TCR_UNIE |
251 		    (CPU_IS_SH3 ? SH3_TCR_TPSC_RTC : SH4_TCR_TPSC_RTC));
252 	}
253 	sh_clock.hz_cnt = sh_clock.tmuclk / hz - 1;
254 
255 	_reg_write_4(SH_(TCOR0), sh_clock.hz_cnt);
256 	_reg_write_4(SH_(TCNT0), sh_clock.hz_cnt);
257 
258 	evcnt_attach_static(&sh_hardclock_evcnt);
259 	intc_intr_establish(SH_INTEVT_TMU0_TUNI0, IST_LEVEL, IPL_CLOCK,
260 	    CPU_IS_SH3 ? sh3_clock_intr : sh4_clock_intr, 0);
261 	/* start hardclock */
262 	_reg_bset_1(SH_(TSTR), TSTR_STR0);
263 
264 	/*
265 	 * TMU channel 1 is one shot timer for softintr(9).
266 	 */
267 	_reg_write_2(SH_(TCR1), TCR_UNIE | TCR_TPSC_P4);
268 	_reg_write_4(SH_(TCOR1), 0xffffffff);
269 
270 	/*
271 	 * TMU channel 2 is freerunning counter for timecounter(9).
272 	 */
273 	_reg_write_2(SH_(TCR2), TCR_TPSC_P4);
274 	_reg_write_4(SH_(TCOR2), 0xffffffff);
275 
276 	/*
277 	 * Start and initialize timecounter.
278 	 */
279 	_reg_bset_1(SH_(TSTR), TSTR_STR2);
280 
281 	sh_clock.tc.tc_get_timecount = sh_timecounter_get;
282 	sh_clock.tc.tc_frequency = sh_clock.pclock / 4;
283 	sh_clock.tc.tc_name = "tmu_pclock_4";
284 	sh_clock.tc.tc_quality = 0;
285 	sh_clock.tc.tc_counter_mask = 0xffffffff;
286 	tc_init(&sh_clock.tc);
287 }
288 
289 
290 #ifdef SH3
291 int
292 sh3_clock_intr(void *arg) /* trap frame */
293 {
294 #if (NWDOG > 0)
295 	uint32_t i;
296 
297 	i = (uint32_t)SHREG_WTCNT_R;
298 	if (i > maxwdog)
299 		maxwdog = i;
300 	wdog_wr_cnt(0);			/* reset to zero */
301 #endif
302 
303 	sh_hardclock_evcnt.ev_count++;
304 
305 	/* clear underflow status */
306 	_reg_bclr_2(SH3_TCR0, TCR_UNF);
307 
308 	hardclock(arg);
309 
310 	return (1);
311 }
312 #endif /* SH3 */
313 #ifdef SH4
314 int
315 sh4_clock_intr(void *arg) /* trap frame */
316 {
317 #if (NWDOG > 0)
318 	uint32_t i;
319 
320 	i = (uint32_t)SHREG_WTCNT_R;
321 	if (i > maxwdog)
322 		maxwdog = i;
323 	wdog_wr_cnt(0);			/* reset to zero */
324 #endif
325 
326 	sh_hardclock_evcnt.ev_count++;
327 
328 	/* clear underflow status */
329 	_reg_bclr_2(SH4_TCR0, TCR_UNF);
330 
331 	hardclock(arg);
332 
333 	return (1);
334 }
335 #endif /* SH4 */
336