1/* $NetBSD: clock.c,v 1.52 2011/02/08 20:20:10 rmind Exp $ */ 2 3/* 4 * Copyright (c) 1988 University of Utah. 5 * Copyright (c) 1982, 1990 The Regents of the University of California. 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to Berkeley by 9 * the Systems Programming Group of the University of Utah Computer 10 * Science Department. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 3. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 * 36 * from: Utah $Hdr: clock.c 1.18 91/01/21$ 37 * 38 * @(#)clock.c 7.6 (Berkeley) 5/7/91 39 */ 40 41#include <sys/cdefs.h> 42__KERNEL_RCSID(0, "$NetBSD: clock.c,v 1.52 2011/02/08 20:20:10 rmind Exp $"); 43 44#include <sys/param.h> 45#include <sys/kernel.h> 46#include <sys/systm.h> 47#include <sys/device.h> 48#include <sys/uio.h> 49#include <sys/conf.h> 50#include <sys/proc.h> 51#include <sys/event.h> 52#include <sys/timetc.h> 53 54#include <dev/clock_subr.h> 55 56#include <machine/psl.h> 57#include <machine/cpu.h> 58#include <machine/iomap.h> 59#include <machine/mfp.h> 60#include <atari/dev/clockreg.h> 61#include <atari/dev/clockvar.h> 62#include <atari/atari/device.h> 63 64#if defined(GPROF) && defined(PROFTIMER) 65#include <machine/profile.h> 66#endif 67 68#include "ioconf.h" 69 70static int atari_rtc_get(todr_chip_handle_t, struct clock_ymdhms *); 71static int atari_rtc_set(todr_chip_handle_t, struct clock_ymdhms *); 72 73/* 74 * The MFP clock runs at 2457600Hz. We use a {system,stat,prof}clock divider 75 * of 200. Therefore the timer runs at an effective rate of: 76 * 2457600/200 = 12288Hz. 77 */ 78#define CLOCK_HZ 12288 79 80static u_int clk_getcounter(struct timecounter *); 81 82static struct timecounter clk_timecounter = { 83 clk_getcounter, /* get_timecount */ 84 0, /* no poll_pps */ 85 ~0u, /* counter_mask */ 86 CLOCK_HZ, /* frequency */ 87 "clock", /* name, overriden later */ 88 100, /* quality */ 89 NULL, /* prev */ 90 NULL, /* next */ 91}; 92 93/* 94 * Machine-dependent clock routines. 95 * 96 * Inittodr initializes the time of day hardware which provides 97 * date functions. 98 * 99 * Resettodr restores the time of day hardware after a time change. 100 */ 101 102struct clock_softc { 103 device_t sc_dev; 104 int sc_flags; 105 struct todr_chip_handle sc_handle; 106}; 107 108/* 109 * 'sc_flags' state info. Only used by the rtc-device functions. 110 */ 111#define RTC_OPEN 1 112 113dev_type_open(rtcopen); 114dev_type_close(rtcclose); 115dev_type_read(rtcread); 116dev_type_write(rtcwrite); 117 118static void clockattach(device_t, device_t, void *); 119static int clockmatch(device_t, cfdata_t, void *); 120 121CFATTACH_DECL_NEW(clock, sizeof(struct clock_softc), 122 clockmatch, clockattach, NULL, NULL); 123 124const struct cdevsw rtc_cdevsw = { 125 rtcopen, rtcclose, rtcread, rtcwrite, noioctl, 126 nostop, notty, nopoll, nommap, nokqfilter, 127}; 128 129void statintr(struct clockframe); 130 131static int twodigits(char *, int); 132 133static int divisor; /* Systemclock divisor */ 134 135/* 136 * Statistics and profile clock intervals and variances. Variance must 137 * be a power of 2. Since this gives us an even number, not an odd number, 138 * we discard one case and compensate. That is, a variance of 64 would 139 * give us offsets in [0..63]. Instead, we take offsets in [1..63]. 140 * This is symmetric around the point 32, or statvar/2, and thus averages 141 * to that value (assuming uniform random numbers). 142 */ 143#ifdef STATCLOCK 144static int statvar = 32; /* {stat,prof}clock variance */ 145static int statmin; /* statclock divisor - variance/2 */ 146static int profmin; /* profclock divisor - variance/2 */ 147static int clk2min; /* current, from above choices */ 148#endif 149 150int 151clockmatch(device_t parent, cfdata_t cf, void *aux) 152{ 153 154 if (!strcmp("clock", aux)) 155 return 1; 156 return 0; 157} 158 159/* 160 * Start the real-time clock. 161 */ 162void clockattach(device_t parent, device_t self, void *aux) 163{ 164 struct clock_softc *sc = device_private(self); 165 struct todr_chip_handle *tch; 166 167 sc->sc_dev = self; 168 tch = &sc->sc_handle; 169 tch->todr_gettime_ymdhms = atari_rtc_get; 170 tch->todr_settime_ymdhms = atari_rtc_set; 171 tch->todr_setwen = NULL; 172 173 todr_attach(tch); 174 175 sc->sc_flags = 0; 176 177 /* 178 * Initialize Timer-A in the ST-MFP. We use a divisor of 200. 179 * The MFP clock runs at 2457600Hz. Therefore the timer runs 180 * at an effective rate of: 2457600/200 = 12288Hz. The 181 * following expression works for 48, 64 or 96 hz. 182 */ 183 divisor = CLOCK_HZ/hz; 184 MFP->mf_tacr = 0; /* Stop timer */ 185 MFP->mf_iera &= ~IA_TIMA; /* Disable timer interrupts */ 186 MFP->mf_tadr = divisor; /* Set divisor */ 187 188 clk_timecounter.tc_frequency = CLOCK_HZ; 189 190 if (hz != 48 && hz != 64 && hz != 96) { /* XXX */ 191 printf (": illegal value %d for systemclock, reset to %d\n\t", 192 hz, 64); 193 hz = 64; 194 } 195 printf(": system hz %d timer-A divisor 200/%d\n", hz, divisor); 196 tc_init(&clk_timecounter); 197 198#ifdef STATCLOCK 199 if ((stathz == 0) || (stathz > hz) || (CLOCK_HZ % stathz)) 200 stathz = hz; 201 if ((profhz == 0) || (profhz > (hz << 1)) || (CLOCK_HZ % profhz)) 202 profhz = hz << 1; 203 204 MFP->mf_tcdcr &= 0x7; /* Stop timer */ 205 MFP->mf_ierb &= ~IB_TIMC; /* Disable timer inter. */ 206 MFP->mf_tcdr = CLOCK_HZ/stathz; /* Set divisor */ 207 208 statmin = (CLOCK_HZ/stathz) - (statvar >> 1); 209 profmin = (CLOCK_HZ/profhz) - (statvar >> 1); 210 clk2min = statmin; 211#endif /* STATCLOCK */ 212} 213 214void cpu_initclocks(void) 215{ 216 217 MFP->mf_tacr = T_Q200; /* Start timer */ 218 MFP->mf_ipra = (u_int8_t)~IA_TIMA;/* Clear pending interrupts */ 219 MFP->mf_iera |= IA_TIMA; /* Enable timer interrupts */ 220 MFP->mf_imra |= IA_TIMA; /* ..... */ 221 222#ifdef STATCLOCK 223 MFP->mf_tcdcr = (MFP->mf_tcdcr & 0x7) | (T_Q200<<4); /* Start */ 224 MFP->mf_iprb = (u_int8_t)~IB_TIMC;/* Clear pending interrupts */ 225 MFP->mf_ierb |= IB_TIMC; /* Enable timer interrupts */ 226 MFP->mf_imrb |= IB_TIMC; /* ..... */ 227#endif /* STATCLOCK */ 228} 229 230void 231setstatclockrate(int newhz) 232{ 233 234#ifdef STATCLOCK 235 if (newhz == stathz) 236 clk2min = statmin; 237 else clk2min = profmin; 238#endif /* STATCLOCK */ 239} 240 241#ifdef STATCLOCK 242void 243statintr(struct clockframe frame) 244{ 245 register int var, r; 246 247 var = statvar - 1; 248 do { 249 r = random() & var; 250 } while (r == 0); 251 252 /* 253 * Note that we are always lagging behind as the new divisor 254 * value will not be loaded until the next interrupt. This 255 * shouldn't disturb the median frequency (I think ;-) ) as 256 * only the value used when switching frequencies is used 257 * twice. This shouldn't happen very often. 258 */ 259 MFP->mf_tcdr = clk2min + r; 260 261 statclock(&frame); 262} 263#endif /* STATCLOCK */ 264 265static u_int 266clk_getcounter(struct timecounter *tc) 267{ 268 uint32_t delta, count, cur_hardclock; 269 uint8_t ipra, tadr; 270 int s; 271 static uint32_t lastcount; 272 273 s = splhigh(); 274 cur_hardclock = hardclock_ticks; 275 ipra = MFP->mf_ipra; 276 tadr = MFP->mf_tadr; 277 delta = divisor - tadr; 278 279 if (ipra & IA_TIMA) 280 delta += divisor; 281 splx(s); 282 283 count = (divisor * cur_hardclock) + delta; 284 if ((int32_t)(count - lastcount) < 0) { 285 /* XXX wrapped; maybe hardclock() is blocked more than 2/HZ */ 286 count = lastcount + 1; 287 } 288 lastcount = count; 289 290 return count; 291} 292 293#define TIMB_FREQ 614400 294#define TIMB_LIMIT 256 295 296void 297init_delay(void) 298{ 299 300 /* 301 * Initialize Timer-B in the ST-MFP. This timer is used by 302 * the 'delay' function below. This timer is setup to be 303 * continueously counting from 255 back to zero at a 304 * frequency of 614400Hz. We do this *early* in the 305 * initialisation process. 306 */ 307 MFP->mf_tbcr = 0; /* Stop timer */ 308 MFP->mf_iera &= ~IA_TIMB; /* Disable timer interrupts */ 309 MFP->mf_tbdr = 0; 310 MFP->mf_tbcr = T_Q004; /* Start timer */ 311} 312 313/* 314 * Wait "n" microseconds. 315 * Relies on MFP-Timer B counting down from TIMB_LIMIT at TIMB_FREQ Hz. 316 * Note: timer had better have been programmed before this is first used! 317 */ 318void 319delay(unsigned int n) 320{ 321 int ticks, otick, remaining; 322 323 /* 324 * Read the counter first, so that the rest of the setup overhead is 325 * counted. 326 */ 327 otick = MFP->mf_tbdr; 328 329 if (n <= UINT_MAX / TIMB_FREQ) { 330 /* 331 * For unsigned arithmetic, division can be replaced with 332 * multiplication with the inverse and a shift. 333 */ 334 remaining = n * TIMB_FREQ / 1000000; 335 } else { 336 /* This is a very long delay. 337 * Being slow here doesn't matter. 338 */ 339 remaining = (unsigned long long) n * TIMB_FREQ / 1000000; 340 } 341 342 while (remaining > 0) { 343 ticks = MFP->mf_tbdr; 344 if (ticks > otick) 345 remaining -= TIMB_LIMIT - (ticks - otick); 346 else 347 remaining -= otick - ticks; 348 otick = ticks; 349 } 350} 351 352#ifdef GPROF 353/* 354 * profclock() is expanded in line in lev6intr() unless profiling kernel. 355 * Assumes it is called with clock interrupts blocked. 356 */ 357profclock(void *pc, int ps) 358{ 359 360 /* 361 * Came from user mode. 362 * If this process is being profiled record the tick. 363 */ 364 if (USERMODE(ps)) { 365 if (p->p_stats.p_prof.pr_scale) 366 addupc(pc, &curproc->p_stats.p_prof, 1); 367 } 368 /* 369 * Came from kernel (supervisor) mode. 370 * If we are profiling the kernel, record the tick. 371 */ 372 else if (profiling < 2) { 373 register int s = pc - s_lowpc; 374 375 if (s < s_textsize) 376 kcount[s / (HISTFRACTION * sizeof(*kcount))]++; 377 } 378 /* 379 * Kernel profiling was on but has been disabled. 380 * Mark as no longer profiling kernel and if all profiling done, 381 * disable the clock. 382 */ 383 if (profiling && (profon & PRF_KERNEL)) { 384 profon &= ~PRF_KERNEL; 385 if (profon == PRF_NONE) 386 stopprofclock(); 387 } 388} 389#endif 390 391/*********************************************************************** 392 * Real Time Clock support * 393 ***********************************************************************/ 394 395u_int mc146818_read(void *cookie, u_int regno) 396{ 397 struct rtc *rtc = cookie; 398 399 rtc->rtc_regno = regno; 400 return rtc->rtc_data & 0xff; 401} 402 403void mc146818_write(void *cookie, u_int regno, u_int value) 404{ 405 struct rtc *rtc = cookie; 406 407 rtc->rtc_regno = regno; 408 rtc->rtc_data = value; 409} 410 411static int 412atari_rtc_get(todr_chip_handle_t todr, struct clock_ymdhms *dtp) 413{ 414 int sps; 415 mc_todregs clkregs; 416 u_int regb; 417 418 sps = splhigh(); 419 regb = mc146818_read(RTC, MC_REGB); 420 MC146818_GETTOD(RTC, &clkregs); 421 splx(sps); 422 423 regb &= MC_REGB_24HR|MC_REGB_BINARY; 424 if (regb != (MC_REGB_24HR|MC_REGB_BINARY)) { 425 printf("Error: Nonstandard RealTimeClock Configuration -" 426 " value ignored\n" 427 " A write to /dev/rtc will correct this.\n"); 428 return 0; 429 } 430 if (clkregs[MC_SEC] > 59) 431 return -1; 432 if (clkregs[MC_MIN] > 59) 433 return -1; 434 if (clkregs[MC_HOUR] > 23) 435 return -1; 436 if (range_test(clkregs[MC_DOM], 1, 31)) 437 return -1; 438 if (range_test(clkregs[MC_MONTH], 1, 12)) 439 return -1; 440 if (clkregs[MC_YEAR] > 99) 441 return -1; 442 443 dtp->dt_year = clkregs[MC_YEAR] + GEMSTARTOFTIME; 444 dtp->dt_mon = clkregs[MC_MONTH]; 445 dtp->dt_day = clkregs[MC_DOM]; 446 dtp->dt_hour = clkregs[MC_HOUR]; 447 dtp->dt_min = clkregs[MC_MIN]; 448 dtp->dt_sec = clkregs[MC_SEC]; 449 450 return 0; 451} 452 453static int 454atari_rtc_set(todr_chip_handle_t todr, struct clock_ymdhms *dtp) 455{ 456 int s; 457 mc_todregs clkregs; 458 459 clkregs[MC_YEAR] = dtp->dt_year - GEMSTARTOFTIME; 460 clkregs[MC_MONTH] = dtp->dt_mon; 461 clkregs[MC_DOM] = dtp->dt_day; 462 clkregs[MC_HOUR] = dtp->dt_hour; 463 clkregs[MC_MIN] = dtp->dt_min; 464 clkregs[MC_SEC] = dtp->dt_sec; 465 466 s = splclock(); 467 MC146818_PUTTOD(RTC, &clkregs); 468 splx(s); 469 470 return 0; 471} 472 473/*********************************************************************** 474 * RTC-device support * 475 ***********************************************************************/ 476int 477rtcopen(dev_t dev, int flag, int mode, struct lwp *l) 478{ 479 int unit = minor(dev); 480 struct clock_softc *sc; 481 482 sc = device_lookup_private(&clock_cd, unit); 483 if (sc == NULL) 484 return ENXIO; 485 if (sc->sc_flags & RTC_OPEN) 486 return EBUSY; 487 488 sc->sc_flags = RTC_OPEN; 489 return 0; 490} 491 492int 493rtcclose(dev_t dev, int flag, int mode, struct lwp *l) 494{ 495 int unit = minor(dev); 496 struct clock_softc *sc = device_lookup_private(&clock_cd, unit); 497 498 sc->sc_flags = 0; 499 return 0; 500} 501 502int 503rtcread(dev_t dev, struct uio *uio, int flags) 504{ 505 struct clock_softc *sc; 506 mc_todregs clkregs; 507 int s, length; 508 char buffer[16]; 509 510 sc = device_lookup_private(&clock_cd, minor(dev)); 511 512 s = splhigh(); 513 MC146818_GETTOD(RTC, &clkregs); 514 splx(s); 515 516 sprintf(buffer, "%4d%02d%02d%02d%02d.%02d\n", 517 clkregs[MC_YEAR] + GEMSTARTOFTIME, 518 clkregs[MC_MONTH], clkregs[MC_DOM], 519 clkregs[MC_HOUR], clkregs[MC_MIN], clkregs[MC_SEC]); 520 521 if (uio->uio_offset > strlen(buffer)) 522 return 0; 523 524 length = strlen(buffer) - uio->uio_offset; 525 if (length > uio->uio_resid) 526 length = uio->uio_resid; 527 528 return uiomove((void *)buffer, length, uio); 529} 530 531static int 532twodigits(char *buffer, int pos) 533{ 534 int result = 0; 535 536 if (buffer[pos] >= '0' && buffer[pos] <= '9') 537 result = (buffer[pos] - '0') * 10; 538 if (buffer[pos+1] >= '0' && buffer[pos+1] <= '9') 539 result += (buffer[pos+1] - '0'); 540 return result; 541} 542 543int 544rtcwrite(dev_t dev, struct uio *uio, int flags) 545{ 546 mc_todregs clkregs; 547 int s, length, error; 548 char buffer[16]; 549 550 /* 551 * We require atomic updates! 552 */ 553 length = uio->uio_resid; 554 if (uio->uio_offset || (length != sizeof(buffer) 555 && length != sizeof(buffer - 1))) 556 return EINVAL; 557 558 if ((error = uiomove((void *)buffer, sizeof(buffer), uio))) 559 return error; 560 561 if (length == sizeof(buffer) && buffer[sizeof(buffer) - 1] != '\n') 562 return EINVAL; 563 564 s = splclock(); 565 mc146818_write(RTC, MC_REGB, 566 mc146818_read(RTC, MC_REGB) | MC_REGB_24HR | MC_REGB_BINARY); 567 MC146818_GETTOD(RTC, &clkregs); 568 splx(s); 569 570 clkregs[MC_SEC] = twodigits(buffer, 13); 571 clkregs[MC_MIN] = twodigits(buffer, 10); 572 clkregs[MC_HOUR] = twodigits(buffer, 8); 573 clkregs[MC_DOM] = twodigits(buffer, 6); 574 clkregs[MC_MONTH] = twodigits(buffer, 4); 575 s = twodigits(buffer, 0) * 100 + twodigits(buffer, 2); 576 clkregs[MC_YEAR] = s - GEMSTARTOFTIME; 577 578 s = splclock(); 579 MC146818_PUTTOD(RTC, &clkregs); 580 splx(s); 581 582 return 0; 583} 584