1 /* $NetBSD: clock.c,v 1.2 1995/05/05 16:31:46 leo 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. All advertising materials mentioning features or use of this software 21 * must display the following acknowledgement: 22 * This product includes software developed by the University of 23 * California, Berkeley and its contributors. 24 * 4. Neither the name of the University nor the names of its contributors 25 * may be used to endorse or promote products derived from this software 26 * without specific prior written permission. 27 * 28 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 31 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 38 * SUCH DAMAGE. 39 * 40 * from: Utah $Hdr: clock.c 1.18 91/01/21$ 41 * 42 * @(#)clock.c 7.6 (Berkeley) 5/7/91 43 */ 44 45 #include <sys/param.h> 46 #include <sys/kernel.h> 47 #include <sys/device.h> 48 #include <machine/psl.h> 49 #include <machine/cpu.h> 50 #include <machine/iomap.h> 51 #include <machine/mfp.h> 52 #include <atari/dev/clockreg.h> 53 54 #if defined(PROF) && defined(PROFTIMER) 55 #include <sys/PROF.h> 56 #endif 57 58 59 /* 60 * Machine-dependent clock routines. 61 * 62 * Startrtclock restarts the real-time clock, which provides 63 * hardclock interrupts to kern_clock.c. 64 * 65 * Inittodr initializes the time of day hardware which provides 66 * date functions. 67 * 68 * Resettodr restores the time of day hardware after a time change. 69 * 70 * A note on the real-time clock: 71 * We actually load the clock with CLK_INTERVAL-1 instead of CLK_INTERVAL. 72 * This is because the counter decrements to zero after N+1 enabled clock 73 * periods where N is the value loaded into the counter. 74 */ 75 76 int clockmatch __P((struct device *, struct cfdata *, void *)); 77 void clockattach __P((struct device *, struct device *, void *)); 78 79 struct cfdriver clockcd = { 80 NULL, "clock", (cfmatch_t)clockmatch, clockattach, 81 DV_DULL, sizeof(struct device), NULL, 0 82 }; 83 84 static u_long gettod __P((void)); 85 static int settod __P((u_long)); 86 87 static int divisor; 88 89 int 90 clockmatch(pdp, cfp, auxp) 91 struct device *pdp; 92 struct cfdata *cfp; 93 void *auxp; 94 { 95 if(!strcmp("clock", auxp)) 96 return(1); 97 return(0); 98 } 99 100 /* 101 * Start the real-time clock. 102 */ 103 void clockattach(pdp, dp, auxp) 104 struct device *pdp, *dp; 105 void *auxp; 106 { 107 /* 108 * Initialize Timer-A in the ST-MFP. An exact reduce to HZ is not 109 * possible by hardware. We use a divisor of 64 and reduce by software 110 * with a factor of 4. The MFP clock runs at 2457600Hz. Therefore the 111 * timer runs at an effective rate of: 2457600/(64*4) = 9600Hz. The 112 * following expression works for all 'normal' values of hz. 113 */ 114 divisor = 9600/hz; 115 MFP->mf_tacr = 0; /* Stop timer */ 116 MFP->mf_iera &= ~IA_TIMA; /* Disable timer interrupts */ 117 MFP->mf_tadr = divisor; /* Set divisor */ 118 119 printf(": system hz %d timer-A divisor %d\n", hz, divisor); 120 121 /* 122 * Initialize Timer-B in the ST-MFP. This timer is used by the 'delay' 123 * function below. This time is setup to be continueously counting from 124 * 255 back to zero at a frequency of 614400Hz. 125 */ 126 MFP->mf_tbcr = 0; /* Stop timer */ 127 MFP->mf_iera &= ~IA_TIMB; /* Disable timer interrupts */ 128 MFP->mf_tbdr = 0; 129 MFP->mf_tbcr = T_Q004; /* Start timer */ 130 131 } 132 133 void cpu_initclocks() 134 { 135 MFP->mf_tacr = T_Q064; /* Start timer */ 136 MFP->mf_ipra &= ~IA_TIMA; /* Clear pending interrupts */ 137 MFP->mf_iera |= IA_TIMA; /* Enable timer interrupts */ 138 MFP->mf_imra |= IA_TIMA; /* ..... */ 139 } 140 141 setstatclockrate(hz) 142 int hz; 143 { 144 } 145 146 /* 147 * Returns number of usec since last recorded clock "tick" 148 * (i.e. clock interrupt). 149 */ 150 clkread() 151 { 152 extern short clk_div; 153 u_int delta, elapsed; 154 155 elapsed = (divisor - MFP->mf_tadr) + ((4 - clk_div) * divisor); 156 delta = (elapsed * tick) / (divisor << 2); 157 158 /* 159 * Account for pending clock interrupts 160 */ 161 if(MFP->mf_iera & IA_TIMA) 162 return(delta + tick); 163 return(delta); 164 } 165 166 #define TIMB_FREQ 614400 167 #define TIMB_LIMIT 256 168 169 /* 170 * Wait "n" microseconds. 171 * Relies on MFP-Timer B counting down from TIMB_LIMIT at TIMB_FREQ Hz. 172 * Note: timer had better have been programmed before this is first used! 173 */ 174 void delay(n) 175 int n; 176 { 177 int tick, otick; 178 179 /* 180 * Read the counter first, so that the rest of the setup overhead is 181 * counted. 182 */ 183 otick = MFP->mf_tbdr; 184 185 /* 186 * Calculate ((n * TIMER_FREQ) / 1e6) using explicit assembler code so 187 * we can take advantage of the intermediate 64-bit quantity to prevent 188 * loss of significance. 189 */ 190 n -= 5; 191 if(n < 0) 192 return; 193 { 194 u_int temp; 195 196 __asm __volatile ("mulul %2,%1:%0" : "=d" (n), "=d" (temp) 197 : "d" (TIMB_FREQ)); 198 __asm __volatile ("divul %1,%2:%0" : "=d" (n) 199 : "d"(1000000),"d"(temp),"0"(n)); 200 } 201 202 while(n > 0) { 203 tick = MFP->mf_tbdr; 204 if(tick > otick) 205 n -= TIMB_LIMIT - (tick - otick); 206 else n -= otick - tick; 207 otick = tick; 208 } 209 } 210 211 #ifdef PROFTIMER 212 /* 213 * This code allows the amiga kernel to use one of the extra timers on 214 * the clock chip for profiling, instead of the regular system timer. 215 * The advantage of this is that the profiling timer can be turned up to 216 * a higher interrupt rate, giving finer resolution timing. The profclock 217 * routine is called from the lev6intr in locore, and is a specialized 218 * routine that calls addupc. The overhead then is far less than if 219 * hardclock/softclock was called. Further, the context switch code in 220 * locore has been changed to turn the profile clock on/off when switching 221 * into/out of a process that is profiling (startprofclock/stopprofclock). 222 * This reduces the impact of the profiling clock on other users, and might 223 * possibly increase the accuracy of the profiling. 224 */ 225 int profint = PRF_INTERVAL; /* Clock ticks between interrupts */ 226 int profscale = 0; /* Scale factor from sys clock to prof clock */ 227 char profon = 0; /* Is profiling clock on? */ 228 229 /* profon values - do not change, locore.s assumes these values */ 230 #define PRF_NONE 0x00 231 #define PRF_USER 0x01 232 #define PRF_KERNEL 0x80 233 234 initprofclock() 235 { 236 #if NCLOCK > 0 237 struct proc *p = curproc; /* XXX */ 238 239 /* 240 * If the high-res timer is running, force profiling off. 241 * Unfortunately, this gets reflected back to the user not as 242 * an error but as a lack of results. 243 */ 244 if (clockon) { 245 p->p_stats->p_prof.pr_scale = 0; 246 return; 247 } 248 /* 249 * Keep track of the number of user processes that are profiling 250 * by checking the scale value. 251 * 252 * XXX: this all assumes that the profiling code is well behaved; 253 * i.e. profil() is called once per process with pcscale non-zero 254 * to turn it on, and once with pcscale zero to turn it off. 255 * Also assumes you don't do any forks or execs. Oh well, there 256 * is always adb... 257 */ 258 if (p->p_stats->p_prof.pr_scale) 259 profprocs++; 260 else 261 profprocs--; 262 #endif 263 /* 264 * The profile interrupt interval must be an even divisor 265 * of the CLK_INTERVAL so that scaling from a system clock 266 * tick to a profile clock tick is possible using integer math. 267 */ 268 if (profint > CLK_INTERVAL || (CLK_INTERVAL % profint) != 0) 269 profint = CLK_INTERVAL; 270 profscale = CLK_INTERVAL / profint; 271 } 272 273 startprofclock() 274 { 275 unsigned short interval; 276 277 /* stop timer B */ 278 ciab.crb = ciab.crb & 0xc0; 279 280 /* load interval into registers. 281 the clocks run at NTSC: 715.909kHz or PAL: 709.379kHz */ 282 283 interval = profint - 1; 284 285 /* order of setting is important ! */ 286 ciab.tblo = interval & 0xff; 287 ciab.tbhi = interval >> 8; 288 289 /* enable interrupts for timer B */ 290 ciab.icr = (1<<7) | (1<<1); 291 292 /* start timer B in continuous shot mode */ 293 ciab.crb = (ciab.crb & 0xc0) | 1; 294 } 295 296 stopprofclock() 297 { 298 /* stop timer B */ 299 ciab.crb = ciab.crb & 0xc0; 300 } 301 302 #ifdef PROF 303 /* 304 * profclock() is expanded in line in lev6intr() unless profiling kernel. 305 * Assumes it is called with clock interrupts blocked. 306 */ 307 profclock(pc, ps) 308 caddr_t pc; 309 int ps; 310 { 311 /* 312 * Came from user mode. 313 * If this process is being profiled record the tick. 314 */ 315 if (USERMODE(ps)) { 316 if (p->p_stats.p_prof.pr_scale) 317 addupc(pc, &curproc->p_stats.p_prof, 1); 318 } 319 /* 320 * Came from kernel (supervisor) mode. 321 * If we are profiling the kernel, record the tick. 322 */ 323 else if (profiling < 2) { 324 register int s = pc - s_lowpc; 325 326 if (s < s_textsize) 327 kcount[s / (HISTFRACTION * sizeof (*kcount))]++; 328 } 329 /* 330 * Kernel profiling was on but has been disabled. 331 * Mark as no longer profiling kernel and if all profiling done, 332 * disable the clock. 333 */ 334 if (profiling && (profon & PRF_KERNEL)) { 335 profon &= ~PRF_KERNEL; 336 if (profon == PRF_NONE) 337 stopprofclock(); 338 } 339 } 340 #endif 341 #endif 342 343 /* 344 * Initialize the time of day register, based on the time base which is, e.g. 345 * from a filesystem. 346 */ 347 inittodr(base) 348 time_t base; 349 { 350 u_long timbuf = base; /* assume no battery clock exists */ 351 352 timbuf = gettod(); 353 354 if(timbuf < base) { 355 printf("WARNING: bad date in battery clock\n"); 356 timbuf = base; 357 } 358 359 /* Battery clock does not store usec's, so forget about it. */ 360 time.tv_sec = timbuf; 361 } 362 363 resettodr() 364 { 365 if(settod(time.tv_sec) == 1) 366 return; 367 printf("Cannot set battery backed clock\n"); 368 } 369 370 static char dmsize[12] = 371 { 372 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 373 }; 374 375 static char ldmsize[12] = 376 { 377 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 378 }; 379 380 static __inline__ int rtc_getclkreg(regno) 381 int regno; 382 { 383 RTC->rtc_regno = RTC_REGA; 384 RTC->rtc_regno = regno; 385 return(RTC->rtc_data & 0377); 386 } 387 388 static __inline__ void rtc_setclkreg(regno, value) 389 int regno, value; 390 { 391 RTC->rtc_regno = regno; 392 RTC->rtc_data = value; 393 } 394 395 static u_long 396 gettod() 397 { 398 int i, year, mon, day, hour, min, sec; 399 u_long new_time = 0; 400 char *msize; 401 402 /* 403 * Hold clock 404 */ 405 rtc_setclkreg(RTC_REGB, rtc_getclkreg(RTC_REGB) | RTC_B_SET); 406 407 /* 408 * Read clock 409 */ 410 sec = rtc_getclkreg(RTC_SEC); 411 min = rtc_getclkreg(RTC_MIN); 412 hour = rtc_getclkreg(RTC_HOUR); 413 day = rtc_getclkreg(RTC_DAY) - 1; 414 mon = rtc_getclkreg(RTC_MONTH) - 1; 415 year = rtc_getclkreg(RTC_YEAR) + STARTOFTIME; 416 417 /* 418 * Let it run again.. 419 */ 420 rtc_setclkreg(RTC_REGB, rtc_getclkreg(RTC_REGB) & ~RTC_B_SET); 421 422 if(range_test(hour, 0, 23)) 423 return(0); 424 if(range_test(day, 0, 30)) 425 return(0); 426 if (range_test(mon, 0, 11)) 427 return(0); 428 if(range_test(year, STARTOFTIME, 2000)) 429 return(0); 430 431 for(i = STARTOFTIME; i < year; i++) { 432 if(is_leap(i)) 433 new_time += 366; 434 else new_time += 365; 435 } 436 437 msize = is_leap(year) ? ldmsize : dmsize; 438 for(i = 0; i < mon; i++) 439 new_time += msize[i]; 440 new_time += day; 441 return((new_time * SECS_DAY) + (hour * 3600) + (min * 60) + sec); 442 } 443 444 static int 445 settod(newtime) 446 u_long newtime; 447 { 448 register long days, rem, year; 449 register char *ml; 450 int sec, min, hour, month; 451 452 /* Number of days since Jan. 1 1970 */ 453 days = newtime / SECS_DAY; 454 rem = newtime % SECS_DAY; 455 456 /* 457 * Calculate sec, min, hour 458 */ 459 hour = rem / SECS_HOUR; 460 rem %= SECS_HOUR; 461 min = rem / 60; 462 sec = rem % 60; 463 464 /* 465 * Figure out the year. Day in year is left in 'days'. 466 */ 467 year = STARTOFTIME; 468 while(days >= (rem = is_leap(year) ? 366 : 365)) { 469 ++year; 470 days -= rem; 471 } 472 while(days < 0) { 473 --year; 474 days += is_leap(year) ? 366 : 365; 475 } 476 477 /* 478 * Determine the month 479 */ 480 ml = is_leap(year) ? ldmsize : dmsize; 481 for(month = 0; days >= ml[month]; ++month) 482 days -= ml[month]; 483 484 /* 485 * Now that everything is calculated, program the RTC 486 */ 487 rtc_setclkreg(RTC_REGB, RTC_B_SET); 488 rtc_setclkreg(RTC_REGA, RTC_A_DV1|RTC_A_RS2|RTC_A_RS3); 489 rtc_setclkreg(RTC_REGB, RTC_B_SET|RTC_B_SQWE|RTC_B_DM|RTC_B_24_12); 490 rtc_setclkreg(RTC_SEC, sec); 491 rtc_setclkreg(RTC_MIN, min); 492 rtc_setclkreg(RTC_HOUR, hour); 493 rtc_setclkreg(RTC_DAY, days+1); 494 rtc_setclkreg(RTC_MONTH, month+1); 495 rtc_setclkreg(RTC_YEAR, year-1970); 496 rtc_setclkreg(RTC_REGB, RTC_B_SQWE|RTC_B_DM|RTC_B_24_12); 497 498 return(1); 499 } 500