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