1 /* $NetBSD: clock.c,v 1.34 2026/06/13 20:16:23 rkujawa Exp $ */ 2 /* $OpenBSD: clock.c,v 1.3 1997/10/13 13:42:53 pefo Exp $ */ 3 4 /* 5 * Copyright (C) 1995, 1996 Wolfgang Solfrank. 6 * Copyright (C) 1995, 1996 TooLs GmbH. 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed by TooLs GmbH. 20 * 4. The name of TooLs GmbH may not be used to endorse or promote products 21 * derived from this software without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR 24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 25 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 27 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 28 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 29 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 30 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 31 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 32 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 */ 34 35 #include <sys/cdefs.h> 36 __KERNEL_RCSID(0, "$NetBSD: clock.c,v 1.34 2026/06/13 20:16:23 rkujawa Exp $"); 37 38 #ifdef _KERNEL_OPT 39 #include "opt_ppcarch.h" 40 #include "opt_ppcparam.h" 41 #endif 42 43 #include <sys/param.h> 44 #include <sys/kernel.h> 45 #include <sys/systm.h> 46 #include <sys/timetc.h> 47 #include <sys/cpu.h> 48 49 #include <uvm/uvm_extern.h> 50 51 #include <prop/proplib.h> 52 53 #include <powerpc/spr.h> 54 #include <powerpc/ibm4xx/spr.h> 55 #include <powerpc/ibm4xx/cpu.h> 56 57 /* 58 * Initially we assume a processor with a bus frequency of 12.5 MHz. 59 */ 60 static u_long ticks_per_sec; 61 static long ticks_per_intr; 62 static volatile u_long lasttb, lasttb2; 63 static u_long ticksmissed; 64 static volatile int tickspending; 65 66 static void init_ppc4xx_tc(void); 67 static u_int get_ppc4xx_timecount(struct timecounter *); 68 69 static struct timecounter ppc4xx_timecounter = { 70 .tc_get_timecount = get_ppc4xx_timecount, 71 .tc_counter_mask = ~0u, 72 .tc_name = "ppc_timebase", 73 .tc_quality = 100, 74 }; 75 76 void decr_intr(struct clockframe *); /* called from trap_subr.S */ 77 void stat_intr(struct clockframe *); /* called from trap_subr.S */ 78 79 #ifdef FAST_STAT_CLOCK 80 /* Stat clock runs at ~ 1.5 kHz */ 81 #define PERIOD_POWER 17 82 #define TCR_PERIOD TCR_FP_2_17 83 #else 84 /* Stat clock runs at ~ 550Hz (440) / ~95Hz (405) */ 85 #define PERIOD_POWER 21 86 #define TCR_PERIOD TCR_FP_2_21 87 #endif 88 89 #ifdef PPC_IBM440 90 /* 91 * tprof sampling shares the FIT 92 */ 93 void (*ibm4xx_tprof_samplefn)(struct clockframe *); 94 u_int ibm4xx_tprof_div; 95 #endif 96 97 98 void 99 stat_intr(struct clockframe *frame) 100 { 101 struct cpu_info * const ci = curcpu(); 102 103 mtspr(SPR_TSR, TSR_FIS); /* Clear TSR[FIS] */ 104 ci->ci_data.cpu_nintr++; 105 ci->ci_ev_statclock.ev_count++; 106 107 #ifdef PPC_IBM440 108 if (ibm4xx_tprof_div != 0) { 109 static u_int statdiv; 110 111 /* take a PC sample every (fast) FIT tick. */ 112 (*ibm4xx_tprof_samplefn)(frame); 113 114 /* statclock only every ibm4xx_tprof_div ticks. */ 115 if (++statdiv < ibm4xx_tprof_div) 116 return; 117 statdiv = 0; 118 } 119 #endif 120 121 /* Nobody can interrupt us, but see if we're allowed to run. */ 122 int s = splclock(); 123 124 /* 125 * Reenable interrupts 126 */ 127 __asm volatile ("wrteei 1"); 128 129 if (IPL_CLOCK > s) { 130 ci->ci_idepth++; 131 statclock(frame); 132 ci->ci_idepth--; 133 } 134 splx(s); 135 } 136 137 void 138 decr_intr(struct clockframe *frame) 139 { 140 struct cpu_info * const ci = curcpu(); 141 int pcpl; 142 long tbtick, xticks; 143 int nticks; 144 145 /* 146 * Check whether we are initialized. 147 */ 148 if (!ticks_per_intr) 149 return; 150 151 tbtick = mftbl(); 152 mtspr(SPR_TSR, TSR_PIS); /* Clear TSR[PIS] */ 153 154 xticks = tbtick - lasttb2; /* Number of TLB cycles since last exception */ 155 for (nticks = 0; xticks > ticks_per_intr; nticks++) 156 xticks -= ticks_per_intr; 157 lasttb2 = tbtick - xticks; 158 159 ci->ci_data.cpu_nintr++; 160 ci->ci_ev_clock.ev_count++; 161 pcpl = splclock(); 162 163 /* 164 * Reenable interrupts 165 */ 166 __asm volatile ("wrteei 1"); 167 168 if (pcpl >= IPL_CLOCK) { 169 tickspending += nticks; 170 ticksmissed += nticks; 171 } else { 172 nticks += tickspending; 173 tickspending = 0; 174 175 /* 176 * lasttb is used during microtime. Set it to the virtual 177 * start of this tick interval. 178 */ 179 lasttb = lasttb2; 180 181 /* 182 * Do standard timer interrupt stuff. 183 */ 184 ci->ci_idepth++; 185 while (nticks-- > 0) 186 hardclock(frame); 187 ci->ci_idepth--; 188 } 189 splx(pcpl); 190 } 191 192 void 193 cpu_initclocks(void) 194 { 195 struct cpu_info * const ci = curcpu(); 196 197 /* Initialized in powerpc/ibm4xx/cpu.c */ 198 evcnt_attach_static(&ci->ci_ev_clock); 199 evcnt_attach_static(&ci->ci_ev_statclock); 200 201 ticks_per_intr = ticks_per_sec / hz; 202 stathz = profhz = ticks_per_sec / (1 << PERIOD_POWER); 203 204 printf("Setting PIT to %ld/%d = %ld\n", ticks_per_sec, hz, 205 ticks_per_intr); 206 207 lasttb2 = lasttb = mftbl(); 208 #ifdef PPC_IBM440 209 /* 210 * PIT is replaced by the decrementer! 211 * TCR/TSR bit positions match. 212 */ 213 mtspr(SPR_DECAR, ticks_per_intr); 214 mtspr(SPR_DEC, ticks_per_intr); 215 216 /* Enable DEC & FIT interrupts and auto-reload */ 217 mtspr(SPR_TCR, TCR_DIE | TCR_ARE | TCR_FIE | TCR_PERIOD); 218 #else 219 mtspr(SPR_PIT, ticks_per_intr); 220 221 /* Enable PIT & FIT(2^17c = 0.655ms) interrupts and auto-reload */ 222 mtspr(SPR_TCR, TCR_PIE | TCR_ARE | TCR_FIE | TCR_PERIOD); 223 #endif 224 225 init_ppc4xx_tc(); 226 } 227 228 void 229 calc_delayconst(void) 230 { 231 prop_number_t freq; 232 233 freq = prop_dictionary_get(board_properties, "processor-frequency"); 234 235 #ifndef PPC_CPU_FREQ 236 KASSERT(freq != NULL); 237 #else 238 /* XXX hack for pckbc_cnattach() for Explora */ 239 if (freq == NULL) 240 ticks_per_sec = (u_long) PPC_CPU_FREQ; 241 else 242 #endif 243 ticks_per_sec = (u_long) prop_number_integer_value(freq); 244 } 245 246 static u_int 247 get_ppc4xx_timecount(struct timecounter *tc) 248 { 249 u_long tb; 250 int msr; 251 252 __asm volatile ("mfmsr %0; wrteei 0" : "=r" (msr)); 253 tb = mftbl(); 254 __asm volatile ("mtmsr %0" :: "r" (msr)); 255 256 return tb; 257 } 258 259 /* 260 * Wait for about n microseconds (at least!). 261 */ 262 void 263 delay(unsigned int n) 264 { 265 u_quad_t tb; 266 u_long tbh, tbl, scratch; 267 268 tb = mftb(); 269 /* Math must handle clock speed above 1GHz. */ 270 tb += ((u_quad_t)n * ticks_per_sec + 999999) / 1000000; 271 tbh = tb >> 32; 272 tbl = tb; 273 __asm volatile ( 274 "1:" 275 #ifdef PPC_IBM403 276 "mftbhi %0;" 277 #else 278 "mftbu %0;" 279 #endif 280 "cmplw %0,%1;" 281 "blt 1b;" 282 "bgt 2f;" 283 #ifdef PPC_IBM403 284 "mftblo %0;" 285 #else 286 "mftb %0;" 287 #endif 288 "cmplw %0,%2;" 289 "blt 1b;" 290 "2:" 291 : "=&r" (scratch) 292 : "r" (tbh), "r" (tbl) 293 : "cr0"); 294 } 295 296 /* 297 * Nothing to do. 298 */ 299 void 300 setstatclockrate(int arg) 301 { 302 303 /* Do nothing */ 304 } 305 306 static void 307 init_ppc4xx_tc(void) 308 { 309 /* from machdep initialization */ 310 ppc4xx_timecounter.tc_frequency = ticks_per_sec; 311 tc_init(&ppc4xx_timecounter); 312 } 313