clock.c revision 1.6
1/*	$OpenBSD: clock.c,v 1.6 2008/06/26 05:42:13 ray 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/device.h>
37
38#include <dev/clock_subr.h>
39
40#include <sh/clock.h>
41#include <sh/trap.h>
42#include <sh/rtcreg.h>
43#include <sh/tmureg.h>
44
45#include <machine/intr.h>
46
47#define	NWDOG 0
48
49#ifndef HZ
50#define	HZ		64
51#endif
52#define	MINYEAR		2002	/* "today" */
53#define	SH_RTC_CLOCK	16384	/* Hz */
54
55/*
56 * OpenBSD/sh clock module
57 *  + default 64Hz
58 *  + use TMU channel 0 as clock interrupt source.
59 *  + use TMU channel 1 and 2 as emulated software interrupt source.
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} sh_clock = {
77#ifdef PCLOCK
78	.pclock = PCLOCK,
79#endif
80	.rtc = {
81		/* SH RTC module to default RTC */
82		.init	= sh_rtc_init,
83		.get	= sh_rtc_get,
84		.set	= sh_rtc_set
85	}
86};
87
88uint32_t maxwdog;
89
90/* TMU */
91/* interrupt handler is timing critical. prepared for each. */
92int sh3_clock_intr(void *);
93int sh4_clock_intr(void *);
94
95/*
96 * Estimate CPU and Peripheral clock.
97 */
98#define	TMU_START(x)							\
99do {									\
100	_reg_bclr_1(SH_(TSTR), TSTR_STR##x);				\
101	_reg_write_4(SH_(TCNT ## x), 0xffffffff);			\
102	_reg_bset_1(SH_(TSTR), TSTR_STR##x);				\
103} while (/*CONSTCOND*/0)
104#define	TMU_ELAPSED(x)							\
105	(0xffffffff - _reg_read_4(SH_(TCNT ## x)))
106
107void
108sh_clock_init(int flags, struct rtc_ops *rtc)
109{
110	uint32_t s, t0, t1 __attribute__((__unused__));
111
112	sh_clock.flags = flags;
113	if (rtc != NULL)
114		sh_clock.rtc = *rtc;	/* structure copy */
115
116	/* Initialize TMU */
117	_reg_write_2(SH_(TCR0), 0);
118	_reg_write_2(SH_(TCR1), 0);
119	_reg_write_2(SH_(TCR2), 0);
120
121	/* Reset RTC alarm and interrupt */
122	_reg_write_1(SH_(RCR1), 0);
123
124	/* Stop all counter */
125	_reg_write_1(SH_(TSTR), 0);
126
127	/*
128	 * Estimate CPU clock.
129	 */
130	if (sh_clock.flags & SH_CLOCK_NORTC) {
131		/* Set TMU channel 0 source to PCLOCK / 16 */
132		_reg_write_2(SH_(TCR0), TCR_TPSC_P16);
133		sh_clock.tmuclk = sh_clock.pclock / 16;
134	} else {
135		/* Set TMU channel 0 source to RTC counter clock (16.384kHz) */
136		_reg_write_2(SH_(TCR0),
137		    CPU_IS_SH3 ? SH3_TCR_TPSC_RTC : SH4_TCR_TPSC_RTC);
138		sh_clock.tmuclk = SH_RTC_CLOCK;
139	}
140
141	s = _cpu_exception_suspend();
142	_cpu_spin(1);	/* load function on cache. */
143	TMU_START(0);
144	_cpu_spin(10000000);
145	t0 = TMU_ELAPSED(0);
146	_cpu_exception_resume(s);
147
148	sh_clock.cpuclock = ((10000000 * 10) / t0) * sh_clock.tmuclk;
149	sh_clock.cpucycle_1us = (sh_clock.tmuclk * 10) / t0;
150
151	if (CPU_IS_SH4)
152		sh_clock.cpuclock >>= 1;	/* two-issue */
153
154	/*
155	 * Estimate PCLOCK
156	 */
157	if (sh_clock.pclock == 0) {
158		/* set TMU channel 1 source to PCLOCK / 4 */
159		_reg_write_2(SH_(TCR1), TCR_TPSC_P4);
160		s = _cpu_exception_suspend();
161		_cpu_spin(1);	/* load function on cache. */
162		TMU_START(0);
163		TMU_START(1);
164		_cpu_spin(sh_clock.cpucycle_1us * 1000000);	/* 1 sec. */
165		t0 = TMU_ELAPSED(0);
166		t1 = TMU_ELAPSED(1);
167		_cpu_exception_resume(s);
168
169		sh_clock.pclock = ((t1 * 4)/ t0) * SH_RTC_CLOCK;
170	}
171
172	/* Stop all counter */
173	_reg_write_1(SH_(TSTR), 0);
174
175#undef TMU_START
176#undef TMU_ELAPSED
177}
178
179int
180sh_clock_get_cpuclock()
181{
182	return (sh_clock.cpuclock);
183}
184
185int
186sh_clock_get_pclock()
187{
188	return (sh_clock.pclock);
189}
190
191void
192setstatclockrate(int newhz)
193{
194	/* XXX not yet */
195}
196
197/*
198 * Return the best possible estimate of the time in the timeval to
199 * which tv points.
200 */
201void
202microtime(struct timeval *tv)
203{
204	static struct timeval lasttime;
205	u_int32_t tcnt0;
206	int s;
207
208	s = splclock();
209	*tv = time;
210	tcnt0 = _reg_read_4(SH_(TCNT0));
211	splx(s);
212
213	tv->tv_usec += ((sh_clock.hz_cnt - tcnt0) * 1000000) / sh_clock.tmuclk;
214	while (tv->tv_usec >= 1000000) {
215		tv->tv_usec -= 1000000;
216		tv->tv_sec++;
217	}
218
219	if (tv->tv_sec == lasttime.tv_sec &&
220	    tv->tv_usec <= lasttime.tv_usec &&
221	    (tv->tv_usec = lasttime.tv_usec + 1) >= 1000000) {
222		tv->tv_usec -= 1000000;
223		tv->tv_sec++;
224	}
225	lasttime = *tv;
226}
227
228/*
229 *  Wait at least `n' usec.
230 */
231void
232delay(int n)
233{
234	_cpu_spin(sh_clock.cpucycle_1us * n);
235}
236
237/*
238 * Start the clock interrupt.
239 */
240void
241cpu_initclocks()
242{
243	if (sh_clock.pclock == 0)
244		panic("No PCLOCK information.");
245
246	/* Set global variables. */
247	hz = HZ;
248	tick = 1000000 / hz;
249
250	/*
251	 * Use TMU channel 0 as hard clock
252	 */
253	_reg_bclr_1(SH_(TSTR), TSTR_STR0);
254
255	if (sh_clock.flags & SH_CLOCK_NORTC) {
256		/* use PCLOCK/16 as TMU0 source */
257		_reg_write_2(SH_(TCR0), TCR_UNIE | TCR_TPSC_P16);
258	} else {
259		/* use RTC clock as TMU0 source */
260		_reg_write_2(SH_(TCR0), TCR_UNIE |
261		    (CPU_IS_SH3 ? SH3_TCR_TPSC_RTC : SH4_TCR_TPSC_RTC));
262	}
263	sh_clock.hz_cnt = sh_clock.tmuclk / hz - 1;
264
265	_reg_write_4(SH_(TCOR0), sh_clock.hz_cnt);
266	_reg_write_4(SH_(TCNT0), sh_clock.hz_cnt);
267
268	intc_intr_establish(SH_INTEVT_TMU0_TUNI0, IST_LEVEL, IPL_CLOCK,
269	    CPU_IS_SH3 ? sh3_clock_intr : sh4_clock_intr, NULL, "clock");
270	/* start hardclock */
271	_reg_bset_1(SH_(TSTR), TSTR_STR0);
272
273	/*
274	 * TMU channel 1, 2 are one shot timer.
275	 */
276	_reg_write_2(SH_(TCR1), TCR_UNIE | TCR_TPSC_P4);
277	_reg_write_4(SH_(TCOR1), 0xffffffff);
278	_reg_write_2(SH_(TCR2), TCR_UNIE | TCR_TPSC_P4);
279	_reg_write_4(SH_(TCOR2), 0xffffffff);
280
281	/* Make sure to start RTC */
282	if (sh_clock.rtc.init != NULL)
283		sh_clock.rtc.init(sh_clock.rtc._cookie);
284}
285
286void
287inittodr(time_t base)
288{
289	struct clock_ymdhms dt;
290	time_t rtc;
291	int s;
292
293	if (!sh_clock.rtc_initialized)
294		sh_clock.rtc_initialized = 1;
295
296	sh_clock.rtc.get(sh_clock.rtc._cookie, base, &dt);
297	rtc = clock_ymdhms_to_secs(&dt);
298
299#ifdef DEBUG
300	printf("inittodr: %d/%d/%d/%d/%d/%d(%d)\n", dt.dt_year,
301	    dt.dt_mon, dt.dt_day, dt.dt_hour, dt.dt_min, dt.dt_sec,
302	    dt.dt_wday);
303#endif
304
305	if (!(sh_clock.flags & SH_CLOCK_NOINITTODR) &&
306	    (rtc < base ||
307		dt.dt_year < MINYEAR || dt.dt_year > 2037 ||
308		dt.dt_mon < 1 || dt.dt_mon > 12 ||
309		dt.dt_wday > 6 ||
310		dt.dt_day < 1 || dt.dt_day > 31 ||
311		dt.dt_hour > 23 || dt.dt_min > 59 || dt.dt_sec > 59)) {
312		/*
313		 * Believe the time in the file system for lack of
314		 * anything better, resetting the RTC.
315		 */
316		s = splclock();
317		time.tv_sec = base;
318		time.tv_usec = 0;
319		splx(s);
320		printf("WARNING: preposterous clock chip time\n");
321		resettodr();
322		printf(" -- CHECK AND RESET THE DATE!\n");
323		return;
324	}
325
326	s = splclock();
327	time.tv_sec = rtc;
328	time.tv_usec = 0;
329	splx(s);
330
331	return;
332}
333
334void
335resettodr()
336{
337	struct clock_ymdhms dt;
338	int s;
339
340	if (!sh_clock.rtc_initialized)
341		return;
342
343	s = splclock();
344	clock_secs_to_ymdhms(time.tv_sec, &dt);
345	splx(s);
346
347	sh_clock.rtc.set(sh_clock.rtc._cookie, &dt);
348#ifdef DEBUG
349        printf("%s: %d/%d/%d/%d/%d/%d(%d)\n", __FUNCTION__,
350	    dt.dt_year, dt.dt_mon, dt.dt_day, dt.dt_hour, dt.dt_min, dt.dt_sec,
351	    dt.dt_wday);
352#endif
353}
354
355#ifdef SH3
356int
357sh3_clock_intr(void *arg) /* trap frame */
358{
359#if (NWDOG > 0)
360	uint32_t i;
361
362	i = (uint32_t)SHREG_WTCNT_R;
363	if (i > maxwdog)
364		maxwdog = i;
365	wdog_wr_cnt(0);			/* reset to zero */
366#endif
367	/* clear underflow status */
368	_reg_bclr_2(SH3_TCR0, TCR_UNF);
369
370	hardclock(arg);
371
372	return (1);
373}
374#endif /* SH3 */
375
376#ifdef SH4
377int
378sh4_clock_intr(void *arg) /* trap frame */
379{
380#if (NWDOG > 0)
381	uint32_t i;
382
383	i = (uint32_t)SHREG_WTCNT_R;
384	if (i > maxwdog)
385		maxwdog = i;
386	wdog_wr_cnt(0);			/* reset to zero */
387#endif
388	/* clear underflow status */
389	_reg_bclr_2(SH4_TCR0, TCR_UNF);
390
391	hardclock(arg);
392
393	return (1);
394}
395#endif /* SH4 */
396
397/*
398 * SH3 RTC module ops.
399 */
400
401void
402sh_rtc_init(void *cookie)
403{
404	/* Make sure to start RTC */
405	_reg_write_1(SH_(RCR2), SH_RCR2_ENABLE | SH_RCR2_START);
406}
407
408void
409sh_rtc_get(void *cookie, time_t base, struct clock_ymdhms *dt)
410{
411	int retry = 8;
412
413	/* disable carry interrupt */
414	_reg_bclr_1(SH_(RCR1), SH_RCR1_CIE);
415
416	do {
417		uint8_t r = _reg_read_1(SH_(RCR1));
418		r &= ~SH_RCR1_CF;
419		r |= SH_RCR1_AF; /* don't clear alarm flag */
420		_reg_write_1(SH_(RCR1), r);
421
422		if (CPU_IS_SH3)
423			dt->dt_year = FROMBCD(_reg_read_1(SH3_RYRCNT));
424		else
425			dt->dt_year = FROMBCD(_reg_read_2(SH4_RYRCNT) & 0x00ff);
426
427		/* read counter */
428#define	RTCGET(x, y)	dt->dt_ ## x = FROMBCD(_reg_read_1(SH_(R ## y ## CNT)))
429		RTCGET(mon, MON);
430		RTCGET(wday, WK);
431		RTCGET(day, DAY);
432		RTCGET(hour, HR);
433		RTCGET(min, MIN);
434		RTCGET(sec, SEC);
435#undef RTCGET
436	} while ((_reg_read_1(SH_(RCR1)) & SH_RCR1_CF) && --retry > 0);
437
438	if (retry == 0) {
439		printf("rtc_gettime: couldn't read RTC register.\n");
440		memset(dt, 0, sizeof(*dt));
441		return;
442	}
443
444	dt->dt_year = (dt->dt_year % 100) + 1900;
445	if (dt->dt_year < 1970)
446		dt->dt_year += 100;
447}
448
449void
450sh_rtc_set(void *cookie, struct clock_ymdhms *dt)
451{
452	uint8_t r;
453
454	/* stop clock */
455	r = _reg_read_1(SH_(RCR2));
456	r |= SH_RCR2_RESET;
457	r &= ~SH_RCR2_START;
458	_reg_write_1(SH_(RCR2), r);
459
460	/* set time */
461	if (CPU_IS_SH3)
462		_reg_write_1(SH3_RYRCNT, TOBCD(dt->dt_year % 100));
463	else
464		_reg_write_2(SH4_RYRCNT, TOBCD(dt->dt_year % 100));
465#define	RTCSET(x, y)	_reg_write_1(SH_(R ## x ## CNT), TOBCD(dt->dt_ ## y))
466	RTCSET(MON, mon);
467	RTCSET(WK, wday);
468	RTCSET(DAY, day);
469	RTCSET(HR, hour);
470	RTCSET(MIN, min);
471	RTCSET(SEC, sec);
472#undef RTCSET
473	/* start clock */
474	_reg_write_1(SH_(RCR2), r | SH_RCR2_START);
475}
476