1 /* $NetBSD: clock.c,v 1.32 2021/03/05 06:06:34 rin 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.32 2021/03/05 06:06:34 rin 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 u_long ns_per_tick; 62 static long ticks_per_intr; 63 static volatile u_long lasttb, lasttb2; 64 static u_long ticksmissed; 65 static volatile int tickspending; 66 67 static void init_ppc4xx_tc(void); 68 static u_int get_ppc4xx_timecount(struct timecounter *); 69 70 static struct timecounter ppc4xx_timecounter = { 71 .tc_get_timecount = get_ppc4xx_timecount, 72 .tc_counter_mask = ~0u, 73 .tc_name = "ppc_timebase", 74 .tc_quality = 100, 75 }; 76 77 void decr_intr(struct clockframe *); /* called from trap_subr.S */ 78 void stat_intr(struct clockframe *); /* called from trap_subr.S */ 79 80 #ifdef FAST_STAT_CLOCK 81 /* Stat clock runs at ~ 1.5 kHz */ 82 #define PERIOD_POWER 17 83 #define TCR_PERIOD TCR_FP_2_17 84 #else 85 /* Stat clock runs at ~ 95Hz */ 86 #define PERIOD_POWER 21 87 #define TCR_PERIOD TCR_FP_2_21 88 #endif 89 90 91 void 92 stat_intr(struct clockframe *frame) 93 { 94 struct cpu_info * const ci = curcpu(); 95 96 mtspr(SPR_TSR, TSR_FIS); /* Clear TSR[FIS] */ 97 ci->ci_data.cpu_nintr++; 98 ci->ci_ev_statclock.ev_count++; 99 100 /* Nobody can interrupt us, but see if we're allowed to run. */ 101 int s = splclock(); 102 103 /* 104 * Reenable interrupts 105 */ 106 __asm volatile ("wrteei 1"); 107 108 if (IPL_CLOCK > s) { 109 ci->ci_idepth++; 110 statclock(frame); 111 ci->ci_idepth--; 112 } 113 splx(s); 114 } 115 116 void 117 decr_intr(struct clockframe *frame) 118 { 119 struct cpu_info * const ci = curcpu(); 120 int pcpl; 121 long tbtick, xticks; 122 int nticks; 123 124 /* 125 * Check whether we are initialized. 126 */ 127 if (!ticks_per_intr) 128 return; 129 130 tbtick = mftbl(); 131 mtspr(SPR_TSR, TSR_PIS); /* Clear TSR[PIS] */ 132 133 xticks = tbtick - lasttb2; /* Number of TLB cycles since last exception */ 134 for (nticks = 0; xticks > ticks_per_intr; nticks++) 135 xticks -= ticks_per_intr; 136 lasttb2 = tbtick - xticks; 137 138 ci->ci_data.cpu_nintr++; 139 ci->ci_ev_clock.ev_count++; 140 pcpl = splclock(); 141 142 /* 143 * Reenable interrupts 144 */ 145 __asm volatile ("wrteei 1"); 146 147 if (pcpl >= IPL_CLOCK) { 148 tickspending += nticks; 149 ticksmissed += nticks; 150 } else { 151 nticks += tickspending; 152 tickspending = 0; 153 154 /* 155 * lasttb is used during microtime. Set it to the virtual 156 * start of this tick interval. 157 */ 158 lasttb = lasttb2; 159 160 /* 161 * Do standard timer interrupt stuff. 162 */ 163 ci->ci_idepth++; 164 while (nticks-- > 0) 165 hardclock(frame); 166 ci->ci_idepth--; 167 } 168 splx(pcpl); 169 } 170 171 void 172 cpu_initclocks(void) 173 { 174 struct cpu_info * const ci = curcpu(); 175 176 /* Initialized in powerpc/ibm4xx/cpu.c */ 177 evcnt_attach_static(&ci->ci_ev_clock); 178 evcnt_attach_static(&ci->ci_ev_statclock); 179 180 ticks_per_intr = ticks_per_sec / hz; 181 stathz = profhz = ticks_per_sec / (1 << PERIOD_POWER); 182 183 printf("Setting PIT to %ld/%d = %ld\n", ticks_per_sec, hz, 184 ticks_per_intr); 185 186 lasttb2 = lasttb = mftbl(); 187 mtspr(SPR_PIT, ticks_per_intr); 188 189 /* Enable PIT & FIT(2^17c = 0.655ms) interrupts and auto-reload */ 190 mtspr(SPR_TCR, TCR_PIE | TCR_ARE | TCR_FIE | TCR_PERIOD); 191 192 init_ppc4xx_tc(); 193 } 194 195 void 196 calc_delayconst(void) 197 { 198 prop_number_t freq; 199 200 freq = prop_dictionary_get(board_properties, "processor-frequency"); 201 202 #ifndef PPC_CPU_FREQ 203 KASSERT(freq != NULL); 204 #else 205 /* XXX hack for pckbc_cnattach() for Explora */ 206 if (freq == NULL) 207 ticks_per_sec = (u_long) PPC_CPU_FREQ; 208 else 209 #endif 210 ticks_per_sec = (u_long) prop_number_integer_value(freq); 211 212 ns_per_tick = 1000000000 / ticks_per_sec; 213 } 214 215 static u_int 216 get_ppc4xx_timecount(struct timecounter *tc) 217 { 218 u_long tb; 219 int msr; 220 221 __asm volatile ("mfmsr %0; wrteei 0" : "=r"(msr) :); 222 tb = mftbl(); 223 __asm volatile ("mtmsr %0" :: "r"(msr)); 224 225 return tb; 226 } 227 228 /* 229 * Wait for about n microseconds (at least!). 230 */ 231 void 232 delay(unsigned int n) 233 { 234 u_quad_t tb; 235 u_long tbh, tbl, scratch; 236 237 tb = mftb(); 238 /* use 1000ULL to force 64 bit math to avoid 32 bit overflows */ 239 tb += (n * 1000ULL + ns_per_tick - 1) / ns_per_tick; 240 tbh = tb >> 32; 241 tbl = tb; 242 __asm volatile ( 243 #ifdef PPC_IBM403 244 "1: mftbhi %0 \n" 245 #else 246 "1: mftbu %0 \n" 247 #endif 248 " cmplw %0,%1 \n" 249 " blt 1b \n" 250 " bgt 2f \n" 251 #ifdef PPC_IBM403 252 " mftblo %0 \n" 253 #else 254 " mftb %0 \n" 255 #endif 256 " cmplw %0,%2 \n" 257 " blt 1b \n" 258 "2: \n" 259 : "=&r"(scratch) : "r"(tbh), "r"(tbl) : "cr0"); 260 } 261 262 /* 263 * Nothing to do. 264 */ 265 void 266 setstatclockrate(int arg) 267 { 268 269 /* Do nothing */ 270 } 271 272 static void 273 init_ppc4xx_tc(void) 274 { 275 /* from machdep initialization */ 276 ppc4xx_timecounter.tc_frequency = ticks_per_sec; 277 tc_init(&ppc4xx_timecounter); 278 } 279