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