Home | History | Annotate | Line # | Download | only in sh3
      1 /*	$NetBSD: clock.c,v 1.41 2020/08/03 16:43:44 uwe 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.41 2020/08/03 16:43:44 uwe 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 sr, 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 	sr = _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_set_sr(sr);
    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 		sr = _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_set_sr(sr);
    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