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