1 /* $NetBSD: clock.c,v 1.61 2025/09/07 14:31:58 thorpej Exp $ */ 2 /* 3 * Copyright (c) 1995 Ludd, University of Lule}, Sweden. 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 __KERNEL_RCSID(0, "$NetBSD: clock.c,v 1.61 2025/09/07 14:31:58 thorpej Exp $"); 29 30 #include <sys/param.h> 31 #include <sys/systm.h> 32 #include <sys/cpu.h> 33 #include <sys/device.h> 34 #include <sys/timetc.h> 35 #include <sys/kernel.h> 36 37 #include <machine/sid.h> 38 #include <machine/clock.h> 39 40 #include "opt_cputype.h" 41 42 struct evcnt clock_misscnt = 43 EVCNT_INITIALIZER(EVCNT_TYPE_MISC, NULL, "clock", "intr miss"); 44 45 EVCNT_ATTACH_STATIC(clock_misscnt); 46 47 struct evcnt clock_intrcnt = 48 EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, "clock", "intr"); 49 50 EVCNT_ATTACH_STATIC(clock_intrcnt); 51 52 static int vax_gettime(todr_chip_handle_t, struct timeval *); 53 static int vax_settime(todr_chip_handle_t, struct timeval *); 54 55 static struct todr_chip_handle todr_handle = { 56 .todr_gettime = vax_gettime, 57 .todr_settime = vax_settime, 58 }; 59 60 #if VAX46 || VAXANY 61 static u_int 62 vax_diag_get_counter(struct timecounter *tc) 63 { 64 extern struct vs_cpu *ka46_cpu; 65 int cur_hardclock; 66 u_int counter; 67 68 do { 69 cur_hardclock = getticks(); 70 counter = *(volatile u_int *)&ka46_cpu->vc_diagtimu; 71 } while (cur_hardclock != getticks()); 72 73 counter = (counter & 0x3ff) + (counter >> 16) * 1024; 74 75 return counter + getticks() * tick; 76 } 77 #endif 78 79 static u_int 80 vax_mfpr_get_counter(struct timecounter *tc) 81 { 82 int cur_hardclock; 83 u_int counter; 84 static int prev_count, prev_hardclock; 85 86 do { 87 cur_hardclock = getticks(); 88 counter = mfpr(PR_ICR) + tick; 89 } while (cur_hardclock != getticks()); 90 91 /* 92 * Handle interval counter wrapping with interrupts blocked. 93 * If the current getticks() is less than what we saw 94 * previously, use the previous value. 95 * If the interval counter is smaller, assume it has wrapped, 96 * and if the [adjusted] current hardclock ticks is the same 97 * as what we saw previously, increment the local copy of 98 * the hardclock ticks. 99 */ 100 if (cur_hardclock < prev_hardclock) 101 cur_hardclock = prev_hardclock; 102 if (counter < prev_count && cur_hardclock == prev_hardclock) 103 cur_hardclock++; 104 105 prev_count = counter; 106 prev_hardclock=cur_hardclock; 107 108 return counter + cur_hardclock * tick; 109 } 110 111 #if VAX46 || VAXANY 112 static struct timecounter vax_diag_tc = { 113 .tc_get_timecount = vax_diag_get_counter, 114 .tc_counter_mask = ~0u, 115 .tc_frequency = 1000000, 116 .tc_name = "diagtimer", 117 .tc_quality = 100, 118 }; 119 #endif 120 121 static struct timecounter vax_mfpr_tc = { 122 .tc_get_timecount = vax_mfpr_get_counter, 123 .tc_counter_mask = ~0u, 124 .tc_frequency = 1000000, 125 .tc_name = "mfpr", 126 .tc_quality = 100, 127 }; 128 129 /* 130 * A delayloop that delays about the number of milliseconds that is 131 * given as argument. 132 */ 133 void 134 delay(int i) 135 { 136 __asm ("1: sobgtr %0, 1b" : : "r" (dep_call->cpu_vups * i)); 137 } 138 139 /* 140 * On all VAXen there are a microsecond clock that should 141 * be used for interval interrupts. Some CPUs don't use the ICR interval 142 * register but it doesn't hurt to load it anyway. 143 */ 144 void 145 cpu_initclocks(void) 146 { 147 mtpr(-10000, PR_NICR); /* Load in count register */ 148 mtpr(0x800000d1, PR_ICCS); /* Start clock and enable interrupt */ 149 150 todr_attach(&todr_handle); 151 152 #if VAX46 || VAXANY 153 if (vax_boardtype == VAX_BTYP_46) 154 tc_init(&vax_diag_tc); 155 #endif 156 if (vax_boardtype != VAX_BTYP_46 && vax_boardtype != VAX_BTYP_48) 157 tc_init(&vax_mfpr_tc); 158 } 159 160 int 161 vax_gettime(todr_chip_handle_t handle, struct timeval *tvp) 162 { 163 tvp->tv_sec = handle->todr_base_time; 164 return (*dep_call->cpu_gettime)(tvp); 165 } 166 167 int 168 vax_settime(todr_chip_handle_t handle, struct timeval *tvp) 169 { 170 (*dep_call->cpu_settime)(tvp); 171 return 0; 172 } 173 174 /* 175 * There are two types of real-time battery-backed up clocks on 176 * VAX computers, one with a register that counts up every 1/100 second, 177 * one with a clock chip that delivers time. For the register clock 178 * we have a generic version, and for the chip clock there are 179 * support routines for time conversion. 180 */ 181 /* 182 * Converts a year to corresponding number of ticks. 183 */ 184 int 185 yeartonum(int y) 186 { 187 int n; 188 189 for (n = 0, y -= 1; y > 1969; y--) 190 n += days_per_year(y) * SECS_PER_DAY; 191 return n; 192 } 193 194 /* 195 * Converts tick number to a year 1970 -> 196 */ 197 int 198 numtoyear(int num) 199 { 200 int y = 1970, j; 201 while(num >= (j = days_per_year(y) * SECS_PER_DAY)) { 202 y++; 203 num -= j; 204 } 205 return y; 206 } 207 208 #if VAX750 || VAX780 || VAX8600 || VAX650 || \ 209 VAX660 || VAX670 || VAX680 || VAX53 || VAXANY 210 /* 211 * Reads the TODR register; returns a (probably) true tick value, and 0 is 212 * success or EINVAL if failed. The year is based on the argument 213 * year; the TODR doesn't hold years. 214 */ 215 int 216 generic_gettime(struct timeval *tvp) 217 { 218 unsigned klocka = mfpr(PR_TODR); 219 220 /* 221 * Sanity check. 222 */ 223 if (klocka < TODRBASE) { 224 if (klocka == 0) 225 printf("TODR stopped"); 226 else 227 printf("TODR too small"); 228 return EINVAL; 229 } 230 231 tvp->tv_sec = yeartonum(numtoyear(tvp->tv_sec)) + (klocka - TODRBASE) / 100; 232 return 0; 233 } 234 235 /* 236 * Takes the current system time and writes it to the TODR. 237 */ 238 void 239 generic_settime(struct timeval *tvp) 240 { 241 unsigned tid = tvp->tv_sec, bastid; 242 243 bastid = tid - yeartonum(numtoyear(tid)); 244 mtpr((bastid * 100) + TODRBASE, PR_TODR); 245 } 246 #endif 247 248 #if VAX630 || VAX410 || VAX43 || VAX8200 || VAX46 || VAX48 || VAX49 || VAXANY 249 250 volatile short *clk_page; /* where the chip is mapped in virtual memory */ 251 int clk_adrshift; /* how much to multiply the in-page address with */ 252 int clk_tweak; /* Offset of time into word. */ 253 254 #define REGPEEK(off) (clk_page[off << clk_adrshift] >> clk_tweak) 255 #define REGPOKE(off, v) (clk_page[off << clk_adrshift] = ((v) << clk_tweak)) 256 257 int 258 chip_gettime(struct timeval *tvp) 259 { 260 struct clock_ymdhms c; 261 int timeout = 1<<15, s; 262 263 #ifdef DIAGNOSTIC 264 if (clk_page == 0) 265 panic("trying to use unset chip clock page"); 266 #endif 267 268 if ((REGPEEK(CSRD_OFF) & CSRD_VRT) == 0) { 269 printf("WARNING: TOY clock not marked valid\n"); 270 return EINVAL; 271 } 272 while (REGPEEK(CSRA_OFF) & CSRA_UIP) { 273 if (--timeout == 0) { 274 printf ("TOY clock timed out"); 275 return ETIMEDOUT; 276 } 277 } 278 279 s = splhigh(); 280 c.dt_year = ((u_char)REGPEEK(YR_OFF)) + 1970; 281 c.dt_mon = REGPEEK(MON_OFF); 282 c.dt_day = REGPEEK(DAY_OFF); 283 c.dt_wday = REGPEEK(WDAY_OFF); 284 c.dt_hour = REGPEEK(HR_OFF); 285 c.dt_min = REGPEEK(MIN_OFF); 286 c.dt_sec = REGPEEK(SEC_OFF); 287 splx(s); 288 289 tvp->tv_sec = clock_ymdhms_to_secs(&c); 290 tvp->tv_usec = 0; 291 return 0; 292 } 293 294 void 295 chip_settime(struct timeval *tvp) 296 { 297 struct clock_ymdhms c; 298 299 #ifdef DIAGNOSTIC 300 if (clk_page == 0) 301 panic("trying to use unset chip clock page"); 302 #endif 303 304 REGPOKE(CSRB_OFF, CSRB_SET); 305 306 clock_secs_to_ymdhms(tvp->tv_sec, &c); 307 308 REGPOKE(YR_OFF, ((u_char)(c.dt_year - 1970))); 309 REGPOKE(MON_OFF, c.dt_mon); 310 REGPOKE(DAY_OFF, c.dt_day); 311 REGPOKE(WDAY_OFF, c.dt_wday); 312 REGPOKE(HR_OFF, c.dt_hour); 313 REGPOKE(MIN_OFF, c.dt_min); 314 REGPOKE(SEC_OFF, c.dt_sec); 315 316 REGPOKE(CSRB_OFF, CSRB_DM|CSRB_24); 317 }; 318 #endif 319