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 */
63struct {
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
90uint32_t maxwdog;
91
92/* TMU */
93/* interrupt handler is timing critical. prepared for each. */
94int sh3_clock_intr(void *);
95int sh4_clock_intr(void *);
96u_int sh_timecounter_get(struct timecounter *);
97
98/*
99 * Estimate CPU and Peripheral clock.
100 */
101#define	TMU_START(x)							\
102do {									\
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
111void
112sh_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
191int
192sh_clock_get_cpuclock(void)
193{
194	return (sh_clock.cpuclock);
195}
196
197int
198sh_clock_get_pclock(void)
199{
200	return (sh_clock.pclock);
201}
202
203void
204setstatclockrate(int newhz)
205{
206}
207
208u_int
209sh_timecounter_get(struct timecounter *tc)
210{
211	return 0xffffffff - _reg_read_4(SH_(TCNT2));
212}
213
214/*
215 *  Wait at least `n' usec.
216 */
217void
218delay(int n)
219{
220	_cpu_spin(sh_clock.cpucycle_1us * n);
221}
222
223extern todr_chip_handle_t todr_handle;
224struct todr_chip_handle rtc_todr;
225
226int
227rtc_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
237int
238rtc_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 */
250void
251cpu_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
264void
265cpu_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
326int
327sh3_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
347int
348sh4_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
371void
372sh_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
378void
379sh_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
419void
420sh_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