1 /* $OpenBSD: clock.c,v 1.17 2023/09/17 14:50:51 cheloha Exp $ */
2 /* $NetBSD: clock.c,v 1.32 2006/09/05 11:09:36 uwe Exp $ */
3
4 /*-
5 * Copyright (c) 2002 The NetBSD Foundation, Inc.
6 * All rights reserved.
7 *
8 * This code is derived from software contributed to The NetBSD Foundation
9 * by UCHIYAMA Yasushi.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/kernel.h>
36 #include <sys/clockintr.h>
37 #include <sys/device.h>
38 #include <sys/timetc.h>
39
40 #include <dev/clock_subr.h>
41
42 #include <sh/clock.h>
43 #include <sh/trap.h>
44 #include <sh/rtcreg.h>
45 #include <sh/tmureg.h>
46
47 #include <machine/intr.h>
48
49 #define NWDOG 0
50
51 #define MINYEAR 2002 /* "today" */
52 #define SH_RTC_CLOCK 16384 /* Hz */
53
54 /*
55 * OpenBSD/sh clock module
56 * + default 64Hz
57 * + use TMU channel 0 as clock interrupt source.
58 * + use TMU channel 1 as emulated software interrupt source.
59 * + use TMU channel 2 as freerunning counter for timecounter.
60 * + If RTC module is active, TMU channel 0 input source is RTC output.
61 * (1.6384kHz)
62 */
63 struct {
64 /* Hard clock */
65 uint32_t hz_cnt; /* clock interrupt interval count */
66 uint32_t cpucycle_1us; /* calibrated loop variable (1 us) */
67 uint32_t tmuclk; /* source clock of TMU0 (Hz) */
68
69 /* RTC ops holder. default SH RTC module */
70 struct rtc_ops rtc;
71 int rtc_initialized;
72
73 uint32_t pclock; /* PCLOCK */
74 uint32_t cpuclock; /* CPU clock */
75 int flags;
76
77 struct timecounter tc;
78 } sh_clock = {
79 #ifdef PCLOCK
80 .pclock = PCLOCK,
81 #endif
82 .rtc = {
83 /* SH RTC module to default RTC */
84 .init = sh_rtc_init,
85 .get = sh_rtc_get,
86 .set = sh_rtc_set
87 }
88 };
89
90 uint32_t maxwdog;
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
sh_clock_init(int flags,struct rtc_ops * rtc)112 sh_clock_init(int flags, struct rtc_ops *rtc)
113 {
114 uint32_t s, t0, cnt_1s;
115
116 sh_clock.flags = flags;
117 if (rtc != NULL)
118 sh_clock.rtc = *rtc; /* structure copy */
119
120 /* Initialize TMU */
121 _reg_write_2(SH_(TCR0), 0);
122 _reg_write_2(SH_(TCR1), 0);
123 _reg_write_2(SH_(TCR2), 0);
124
125 /* Reset RTC alarm and interrupt */
126 _reg_write_1(SH_(RCR1), 0);
127
128 /* Stop all counter */
129 _reg_write_1(SH_(TSTR), 0);
130
131 /*
132 * Estimate CPU clock.
133 */
134 if (sh_clock.flags & SH_CLOCK_NORTC) {
135 /* Set TMU channel 0 source to PCLOCK / 16 */
136 _reg_write_2(SH_(TCR0), TCR_TPSC_P16);
137 sh_clock.tmuclk = sh_clock.pclock / 16;
138 } else {
139 /* Set TMU channel 0 source to RTC counter clock (16.384kHz) */
140 _reg_write_2(SH_(TCR0),
141 CPU_IS_SH3 ? SH3_TCR_TPSC_RTC : SH4_TCR_TPSC_RTC);
142 sh_clock.tmuclk = SH_RTC_CLOCK;
143
144 /* Make sure RTC oscillator is enabled */
145 _reg_bset_1(SH_(RCR2), SH_RCR2_ENABLE);
146 }
147
148 s = _cpu_exception_suspend();
149 _cpu_spin(1); /* load function on cache. */
150 TMU_START(0);
151 _cpu_spin(10000000);
152 t0 = TMU_ELAPSED(0);
153 _cpu_exception_resume(s);
154
155 sh_clock.cpucycle_1us = (sh_clock.tmuclk * 10) / t0;
156
157 cnt_1s = ((uint64_t)sh_clock.tmuclk * 10000000 * 10 + t0 / 2) / t0;
158 if (CPU_IS_SH4)
159 sh_clock.cpuclock = cnt_1s / 2; /* two-issue */
160 else
161 sh_clock.cpuclock = cnt_1s;
162
163 /*
164 * Estimate PCLOCK
165 */
166 if (sh_clock.pclock == 0) {
167 uint32_t t1;
168
169 /* set TMU channel 1 source to PCLOCK / 4 */
170 _reg_write_2(SH_(TCR1), TCR_TPSC_P4);
171 s = _cpu_exception_suspend();
172 _cpu_spin(1); /* load function on cache. */
173 TMU_START(0);
174 TMU_START(1);
175 _cpu_spin(cnt_1s); /* 1 sec. */
176 t0 = TMU_ELAPSED(0);
177 t1 = TMU_ELAPSED(1);
178 _cpu_exception_resume(s);
179
180 sh_clock.pclock =
181 ((uint64_t)t1 * 4 * SH_RTC_CLOCK + t0 / 2) / t0;
182 }
183
184 /* Stop all counters */
185 _reg_write_1(SH_(TSTR), 0);
186
187 #undef TMU_START
188 #undef TMU_ELAPSED
189 }
190
191 int
sh_clock_get_cpuclock(void)192 sh_clock_get_cpuclock(void)
193 {
194 return (sh_clock.cpuclock);
195 }
196
197 int
sh_clock_get_pclock(void)198 sh_clock_get_pclock(void)
199 {
200 return (sh_clock.pclock);
201 }
202
203 void
setstatclockrate(int newhz)204 setstatclockrate(int newhz)
205 {
206 }
207
208 u_int
sh_timecounter_get(struct timecounter * tc)209 sh_timecounter_get(struct timecounter *tc)
210 {
211 return 0xffffffff - _reg_read_4(SH_(TCNT2));
212 }
213
214 /*
215 * Wait at least `n' usec.
216 */
217 void
delay(int n)218 delay(int n)
219 {
220 _cpu_spin(sh_clock.cpucycle_1us * n);
221 }
222
223 extern todr_chip_handle_t todr_handle;
224 struct todr_chip_handle rtc_todr;
225
226 int
rtc_gettime(struct todr_chip_handle * handle,struct timeval * tv)227 rtc_gettime(struct todr_chip_handle *handle, struct timeval *tv)
228 {
229 struct clock_ymdhms dt;
230
231 sh_clock.rtc.get(sh_clock.rtc._cookie, tv->tv_sec, &dt);
232 tv->tv_sec = clock_ymdhms_to_secs(&dt);
233 tv->tv_usec = 0;
234 return 0;
235 }
236
237 int
rtc_settime(struct todr_chip_handle * handle,struct timeval * tv)238 rtc_settime(struct todr_chip_handle *handle, struct timeval *tv)
239 {
240 struct clock_ymdhms dt;
241
242 clock_secs_to_ymdhms(tv->tv_sec, &dt);
243 sh_clock.rtc.set(sh_clock.rtc._cookie, &dt);
244 return 0;
245 }
246
247 /*
248 * Start the clock interrupt.
249 */
250 void
cpu_initclocks(void)251 cpu_initclocks(void)
252 {
253 if (sh_clock.pclock == 0)
254 panic("No PCLOCK information.");
255
256 /* Set global variables. */
257 tick = 1000000 / hz;
258 tick_nsec = 1000000000 / hz;
259
260 stathz = hz;
261 profhz = stathz;
262 }
263
264 void
cpu_startclock(void)265 cpu_startclock(void)
266 {
267 clockintr_cpu_init(NULL);
268
269 /*
270 * Use TMU channel 0 as hard clock
271 */
272 _reg_bclr_1(SH_(TSTR), TSTR_STR0);
273
274 if (sh_clock.flags & SH_CLOCK_NORTC) {
275 /* use PCLOCK/16 as TMU0 source */
276 _reg_write_2(SH_(TCR0), TCR_UNIE | TCR_TPSC_P16);
277 } else {
278 /* use RTC clock as TMU0 source */
279 _reg_write_2(SH_(TCR0), TCR_UNIE |
280 (CPU_IS_SH3 ? SH3_TCR_TPSC_RTC : SH4_TCR_TPSC_RTC));
281 }
282 sh_clock.hz_cnt = sh_clock.tmuclk / hz - 1;
283
284 _reg_write_4(SH_(TCOR0), sh_clock.hz_cnt);
285 _reg_write_4(SH_(TCNT0), sh_clock.hz_cnt);
286
287 intc_intr_establish(SH_INTEVT_TMU0_TUNI0, IST_LEVEL, IPL_CLOCK,
288 CPU_IS_SH3 ? sh3_clock_intr : sh4_clock_intr, NULL, "clock");
289 /* start hardclock */
290 _reg_bset_1(SH_(TSTR), TSTR_STR0);
291
292 /*
293 * TMU channel 1 is one shot timer for soft interrupts.
294 */
295 _reg_write_2(SH_(TCR1), TCR_UNIE | TCR_TPSC_P4);
296 _reg_write_4(SH_(TCOR1), 0xffffffff);
297
298 /*
299 * TMU channel 2 is freerunning counter for timecounter.
300 */
301 _reg_write_2(SH_(TCR2), TCR_TPSC_P4);
302 _reg_write_4(SH_(TCOR2), 0xffffffff);
303
304 /*
305 * Start and initialize timecounter.
306 */
307 _reg_bset_1(SH_(TSTR), TSTR_STR2);
308
309 sh_clock.tc.tc_get_timecount = sh_timecounter_get;
310 sh_clock.tc.tc_frequency = sh_clock.pclock / 4;
311 sh_clock.tc.tc_name = "tmu_pclock_4";
312 sh_clock.tc.tc_quality = 100;
313 sh_clock.tc.tc_counter_mask = 0xffffffff;
314 tc_init(&sh_clock.tc);
315
316 /* Make sure to start RTC */
317 if (sh_clock.rtc.init != NULL)
318 sh_clock.rtc.init(sh_clock.rtc._cookie);
319
320 rtc_todr.todr_gettime = rtc_gettime;
321 rtc_todr.todr_settime = rtc_settime;
322 todr_handle = &rtc_todr;
323 }
324
325 #ifdef SH3
326 int
sh3_clock_intr(void * arg)327 sh3_clock_intr(void *arg) /* trap frame */
328 {
329 #if (NWDOG > 0)
330 uint32_t i;
331
332 i = (uint32_t)SHREG_WTCNT_R;
333 if (i > maxwdog)
334 maxwdog = i;
335 wdog_wr_cnt(0); /* reset to zero */
336 #endif
337 /* clear underflow status */
338 _reg_bclr_2(SH3_TCR0, TCR_UNF);
339
340 clockintr_dispatch(arg);
341
342 return (1);
343 }
344 #endif /* SH3 */
345
346 #ifdef SH4
347 int
sh4_clock_intr(void * arg)348 sh4_clock_intr(void *arg) /* trap frame */
349 {
350 #if (NWDOG > 0)
351 uint32_t i;
352
353 i = (uint32_t)SHREG_WTCNT_R;
354 if (i > maxwdog)
355 maxwdog = i;
356 wdog_wr_cnt(0); /* reset to zero */
357 #endif
358 /* clear underflow status */
359 _reg_bclr_2(SH4_TCR0, TCR_UNF);
360
361 clockintr_dispatch(arg);
362
363 return (1);
364 }
365 #endif /* SH4 */
366
367 /*
368 * SH3 RTC module ops.
369 */
370
371 void
sh_rtc_init(void * cookie)372 sh_rtc_init(void *cookie)
373 {
374 /* Make sure to start RTC */
375 _reg_write_1(SH_(RCR2), SH_RCR2_ENABLE | SH_RCR2_START);
376 }
377
378 void
sh_rtc_get(void * cookie,time_t base,struct clock_ymdhms * dt)379 sh_rtc_get(void *cookie, time_t base, struct clock_ymdhms *dt)
380 {
381 int retry = 8;
382
383 /* disable carry interrupt */
384 _reg_bclr_1(SH_(RCR1), SH_RCR1_CIE);
385
386 do {
387 uint8_t r = _reg_read_1(SH_(RCR1));
388 r &= ~SH_RCR1_CF;
389 r |= SH_RCR1_AF; /* don't clear alarm flag */
390 _reg_write_1(SH_(RCR1), r);
391
392 if (CPU_IS_SH3)
393 dt->dt_year = FROMBCD(_reg_read_1(SH3_RYRCNT));
394 else
395 dt->dt_year = FROMBCD(_reg_read_2(SH4_RYRCNT) & 0x00ff);
396
397 /* read counter */
398 #define RTCGET(x, y) dt->dt_ ## x = FROMBCD(_reg_read_1(SH_(R ## y ## CNT)))
399 RTCGET(mon, MON);
400 RTCGET(wday, WK);
401 RTCGET(day, DAY);
402 RTCGET(hour, HR);
403 RTCGET(min, MIN);
404 RTCGET(sec, SEC);
405 #undef RTCGET
406 } while ((_reg_read_1(SH_(RCR1)) & SH_RCR1_CF) && --retry > 0);
407
408 if (retry == 0) {
409 printf("rtc_gettime: couldn't read RTC register.\n");
410 memset(dt, 0, sizeof(*dt));
411 return;
412 }
413
414 dt->dt_year = (dt->dt_year % 100) + 1900;
415 if (dt->dt_year < 1970)
416 dt->dt_year += 100;
417 }
418
419 void
sh_rtc_set(void * cookie,struct clock_ymdhms * dt)420 sh_rtc_set(void *cookie, struct clock_ymdhms *dt)
421 {
422 uint8_t r;
423
424 /* stop clock */
425 r = _reg_read_1(SH_(RCR2));
426 r |= SH_RCR2_RESET;
427 r &= ~SH_RCR2_START;
428 _reg_write_1(SH_(RCR2), r);
429
430 /* set time */
431 if (CPU_IS_SH3)
432 _reg_write_1(SH3_RYRCNT, TOBCD(dt->dt_year % 100));
433 else
434 _reg_write_2(SH4_RYRCNT, TOBCD(dt->dt_year % 100));
435 #define RTCSET(x, y) _reg_write_1(SH_(R ## x ## CNT), TOBCD(dt->dt_ ## y))
436 RTCSET(MON, mon);
437 RTCSET(WK, wday);
438 RTCSET(DAY, day);
439 RTCSET(HR, hour);
440 RTCSET(MIN, min);
441 RTCSET(SEC, sec);
442 #undef RTCSET
443 /* start clock */
444 _reg_write_1(SH_(RCR2), r | SH_RCR2_START);
445 }
446