1 1.16 rin /* $NetBSD: ifpga_clock.c,v 1.16 2020/05/29 12:30:39 rin Exp $ */ 2 1.1 rearnsha 3 1.1 rearnsha /* 4 1.1 rearnsha * Copyright (c) 2001 ARM Ltd 5 1.1 rearnsha * All rights reserved. 6 1.1 rearnsha * 7 1.1 rearnsha * Redistribution and use in source and binary forms, with or without 8 1.1 rearnsha * modification, are permitted provided that the following conditions 9 1.1 rearnsha * are met: 10 1.1 rearnsha * 1. Redistributions of source code must retain the above copyright 11 1.1 rearnsha * notice, this list of conditions and the following disclaimer. 12 1.1 rearnsha * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 rearnsha * notice, this list of conditions and the following disclaimer in the 14 1.1 rearnsha * documentation and/or other materials provided with the distribution. 15 1.1 rearnsha * 3. The name of the company may not be used to endorse or promote 16 1.1 rearnsha * products derived from this software without specific prior written 17 1.1 rearnsha * permission. 18 1.1 rearnsha * 19 1.1 rearnsha * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20 1.1 rearnsha * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 1.1 rearnsha * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 1.1 rearnsha * IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 23 1.1 rearnsha * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 1.1 rearnsha * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 1.1 rearnsha * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 1.1 rearnsha * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 1.1 rearnsha * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 1.1 rearnsha * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 1.1 rearnsha * SUCH DAMAGE. 30 1.1 rearnsha */ 31 1.1 rearnsha 32 1.1 rearnsha /* 33 1.1 rearnsha * The IFPGA has three timers. Timer 0 is clocked by the system bus clock, 34 1.15 skrll * while timers 1 and 2 are clocked at 24MHz (1Mhz for Integrator CP). To 35 1.15 skrll * keep things simple here, we use timers 1 and 2 only. All three timers 36 1.15 skrll * are 16-bit counters that are programmable in either periodic mode or in 37 1.15 skrll * one-shot mode. 38 1.1 rearnsha */ 39 1.1 rearnsha 40 1.1 rearnsha /* Include header files */ 41 1.5 lukem 42 1.5 lukem #include <sys/cdefs.h> 43 1.16 rin __KERNEL_RCSID(0, "$NetBSD: ifpga_clock.c,v 1.16 2020/05/29 12:30:39 rin Exp $"); 44 1.1 rearnsha 45 1.1 rearnsha #include <sys/types.h> 46 1.1 rearnsha #include <sys/param.h> 47 1.1 rearnsha #include <sys/systm.h> 48 1.1 rearnsha #include <sys/kernel.h> 49 1.12 joerg #include <sys/atomic.h> 50 1.1 rearnsha #include <sys/time.h> 51 1.12 joerg #include <sys/timetc.h> 52 1.1 rearnsha #include <sys/device.h> 53 1.1 rearnsha 54 1.2 thorpej #include <arm/cpufunc.h> 55 1.1 rearnsha #include <machine/intr.h> 56 1.3 thorpej 57 1.1 rearnsha #include <evbarm/ifpga/ifpgavar.h> 58 1.1 rearnsha #include <evbarm/ifpga/ifpgamem.h> 59 1.1 rearnsha #include <evbarm/ifpga/ifpgareg.h> 60 1.1 rearnsha 61 1.1 rearnsha /* 62 1.1 rearnsha * Statistics clock interval and variance, in usec. Variance must be a 63 1.1 rearnsha * power of two. Since this gives us an even number, not an odd number, 64 1.1 rearnsha * we discard one case and compensate. That is, a variance of 1024 would 65 1.1 rearnsha * give us offsets in [0..1023]. Instead, we take offsets in [1..1023]. 66 1.1 rearnsha * This is symmetric about the point 512, or statvar/2, and thus averages 67 1.1 rearnsha * to that value (assuming uniform random numbers). 68 1.1 rearnsha */ 69 1.1 rearnsha static int statvar = 1024 / 4; /* {stat,prof}clock variance */ 70 1.1 rearnsha static int statmin; /* statclock interval - variance/2 */ 71 1.1 rearnsha static int profmin; /* profclock interval - variance/2 */ 72 1.1 rearnsha static int timer2min; /* current, from above choices */ 73 1.1 rearnsha static int statprev; /* previous value in stat timer */ 74 1.1 rearnsha 75 1.1 rearnsha #define TIMER_1_CLEAR (IFPGA_TIMER1_BASE + TIMERx_CLR) 76 1.1 rearnsha #define TIMER_1_LOAD (IFPGA_TIMER1_BASE + TIMERx_LOAD) 77 1.1 rearnsha #define TIMER_1_VALUE (IFPGA_TIMER1_BASE + TIMERx_VALUE) 78 1.1 rearnsha #define TIMER_1_CTRL (IFPGA_TIMER1_BASE + TIMERx_CTRL) 79 1.1 rearnsha 80 1.1 rearnsha #define TIMER_2_CLEAR (IFPGA_TIMER2_BASE + TIMERx_CLR) 81 1.1 rearnsha #define TIMER_2_LOAD (IFPGA_TIMER2_BASE + TIMERx_LOAD) 82 1.1 rearnsha #define TIMER_2_VALUE (IFPGA_TIMER2_BASE + TIMERx_VALUE) 83 1.1 rearnsha #define TIMER_2_CTRL (IFPGA_TIMER2_BASE + TIMERx_CTRL) 84 1.1 rearnsha 85 1.1 rearnsha #define COUNTS_PER_SEC (IFPGA_TIMER1_FREQ / 16) 86 1.1 rearnsha 87 1.12 joerg static u_int ifpga_get_timecount(struct timecounter *); 88 1.12 joerg 89 1.12 joerg static struct timecounter ifpga_timecounter = { 90 1.16 rin .tc_get_timecount = ifpga_get_timecount, 91 1.16 rin .tc_counter_mask = 0xffffffff, 92 1.16 rin .tc_frequency = COUNTS_PER_SEC, 93 1.16 rin .tc_name = "ifpga", 94 1.16 rin .tc_quality = 100, 95 1.12 joerg }; 96 1.12 joerg 97 1.12 joerg static volatile uint32_t ifpga_base; 98 1.12 joerg 99 1.7 rearnsha extern struct ifpga_softc *ifpga_sc; 100 1.14 dyoung extern device_t ifpga_dev; 101 1.1 rearnsha 102 1.1 rearnsha static int clock_started = 0; 103 1.1 rearnsha 104 1.1 rearnsha static int load_timer(int, int); 105 1.1 rearnsha 106 1.10 perry static inline u_int 107 1.1 rearnsha getclock(void) 108 1.1 rearnsha { 109 1.7 rearnsha return bus_space_read_4(ifpga_sc->sc_iot, ifpga_sc->sc_tmr_ioh, 110 1.1 rearnsha TIMER_1_VALUE); 111 1.1 rearnsha } 112 1.1 rearnsha 113 1.10 perry static inline u_int 114 1.1 rearnsha getstatclock(void) 115 1.1 rearnsha { 116 1.7 rearnsha return bus_space_read_4(ifpga_sc->sc_iot, ifpga_sc->sc_tmr_ioh, 117 1.1 rearnsha TIMER_2_VALUE); 118 1.1 rearnsha } 119 1.1 rearnsha 120 1.1 rearnsha /* 121 1.1 rearnsha * int clockhandler(struct clockframe *frame) 122 1.1 rearnsha * 123 1.1 rearnsha * Function called by timer 1 interrupts. 124 1.1 rearnsha * This just clears the interrupt condition and calls hardclock(). 125 1.1 rearnsha */ 126 1.1 rearnsha 127 1.1 rearnsha static int 128 1.1 rearnsha clockhandler(void *fr) 129 1.1 rearnsha { 130 1.1 rearnsha struct clockframe *frame = (struct clockframe *)fr; 131 1.1 rearnsha 132 1.7 rearnsha bus_space_write_4(ifpga_sc->sc_iot, ifpga_sc->sc_tmr_ioh, 133 1.1 rearnsha TIMER_1_CLEAR, 0); 134 1.12 joerg 135 1.12 joerg atomic_add_32(&ifpga_base, ifpga_sc->sc_clock_count); 136 1.12 joerg 137 1.1 rearnsha hardclock(frame); 138 1.1 rearnsha return 0; /* Pass the interrupt on down the chain */ 139 1.1 rearnsha } 140 1.1 rearnsha 141 1.1 rearnsha 142 1.1 rearnsha /* 143 1.1 rearnsha * int statclockhandler(struct clockframe *frame) 144 1.1 rearnsha * 145 1.1 rearnsha * Function called by timer 2 interrupts. 146 1.1 rearnsha * Add some random jitter to the clock, and then call statclock(). 147 1.1 rearnsha */ 148 1.1 rearnsha 149 1.1 rearnsha static int 150 1.1 rearnsha statclockhandler(void *fr) 151 1.1 rearnsha { 152 1.1 rearnsha struct clockframe *frame = (struct clockframe *) fr; 153 1.1 rearnsha int newint, r, var; 154 1.1 rearnsha 155 1.1 rearnsha var = statvar; 156 1.1 rearnsha do { 157 1.1 rearnsha r = random() & (var - 1); 158 1.1 rearnsha } while (r == 0); 159 1.1 rearnsha newint = timer2min + r; 160 1.1 rearnsha 161 1.1 rearnsha if (newint & ~0x0000ffff) 162 1.1 rearnsha panic("statclockhandler: statclock variance too large"); 163 1.1 rearnsha 164 1.1 rearnsha /* 165 1.1 rearnsha * The timer was automatically reloaded with the previous latch 166 1.1 rearnsha * value at the time of the interrupts. Compensate now for the 167 1.1 rearnsha * amount of time that has run off since then, plus one tick 168 1.1 rearnsha * roundoff. This should keep us closer to the mean. 169 1.1 rearnsha */ 170 1.1 rearnsha 171 1.1 rearnsha r = (statprev - getstatclock() + 1); 172 1.1 rearnsha if (r < newint) { 173 1.1 rearnsha newint -= r; 174 1.1 rearnsha r = 0; 175 1.1 rearnsha } 176 1.1 rearnsha else 177 1.1 rearnsha printf("statclockhandler: Statclock overrun\n"); 178 1.1 rearnsha 179 1.1 rearnsha statprev = load_timer(IFPGA_TIMER2_BASE, newint); 180 1.1 rearnsha statclock(frame); 181 1.1 rearnsha if (r) 182 1.1 rearnsha /* 183 1.1 rearnsha * We've completely overrun the previous interval, 184 1.1 rearnsha * make sure we report the correct number of ticks. 185 1.1 rearnsha */ 186 1.1 rearnsha statclock(frame); 187 1.1 rearnsha 188 1.1 rearnsha return 0; /* Pass the interrupt on down the chain */ 189 1.1 rearnsha } 190 1.1 rearnsha 191 1.1 rearnsha static int 192 1.1 rearnsha load_timer(int base, int intvl) 193 1.1 rearnsha { 194 1.1 rearnsha int control; 195 1.1 rearnsha 196 1.1 rearnsha if (intvl & ~0x0000ffff) 197 1.4 provos panic("clock: Invalid interval"); 198 1.1 rearnsha 199 1.15 skrll #if defined(INTEGRATOR_CP) 200 1.15 skrll control = (TIMERx_CTRL_ENABLE | TIMERx_CTRL_MODE_PERIODIC | 201 1.15 skrll TIMERx_CTRL_PRESCALE_DIV16 | TIMERx_CTRL_RAISE_IRQ); 202 1.15 skrll #else 203 1.1 rearnsha control = (TIMERx_CTRL_ENABLE | TIMERx_CTRL_MODE_PERIODIC | 204 1.1 rearnsha TIMERx_CTRL_PRESCALE_DIV16); 205 1.15 skrll #endif 206 1.1 rearnsha 207 1.7 rearnsha bus_space_write_4(ifpga_sc->sc_iot, ifpga_sc->sc_tmr_ioh, 208 1.1 rearnsha base + TIMERx_LOAD, intvl); 209 1.7 rearnsha bus_space_write_4(ifpga_sc->sc_iot, ifpga_sc->sc_tmr_ioh, 210 1.1 rearnsha base + TIMERx_CTRL, control); 211 1.7 rearnsha bus_space_write_4(ifpga_sc->sc_iot, ifpga_sc->sc_tmr_ioh, 212 1.1 rearnsha base + TIMERx_CLR, 0); 213 1.1 rearnsha return intvl; 214 1.1 rearnsha } 215 1.1 rearnsha 216 1.1 rearnsha /* 217 1.1 rearnsha * void setstatclockrate(int hz) 218 1.1 rearnsha * 219 1.1 rearnsha * We assume that hz is either stathz or profhz, and that neither will 220 1.1 rearnsha * change after being set by cpu_initclocks(). We could recalculate the 221 1.1 rearnsha * intervals here, but that would be a pain. 222 1.1 rearnsha */ 223 1.1 rearnsha 224 1.1 rearnsha void 225 1.8 rearnsha setstatclockrate(int new_hz) 226 1.1 rearnsha { 227 1.8 rearnsha if (new_hz == stathz) 228 1.1 rearnsha timer2min = statmin; 229 1.1 rearnsha else 230 1.1 rearnsha timer2min = profmin; 231 1.1 rearnsha } 232 1.1 rearnsha 233 1.1 rearnsha /* 234 1.1 rearnsha * void cpu_initclocks(void) 235 1.1 rearnsha * 236 1.1 rearnsha * Initialise the clocks. 237 1.1 rearnsha */ 238 1.1 rearnsha 239 1.1 rearnsha void 240 1.13 cegger cpu_initclocks(void) 241 1.1 rearnsha { 242 1.1 rearnsha int intvl; 243 1.1 rearnsha int statint; 244 1.1 rearnsha int profint; 245 1.1 rearnsha int minint; 246 1.1 rearnsha 247 1.1 rearnsha if (hz < 50 || COUNTS_PER_SEC % hz) { 248 1.1 rearnsha printf("cannot get %d Hz clock; using 100 Hz\n", hz); 249 1.1 rearnsha hz = 100; 250 1.1 rearnsha tick = 1000000 / hz; 251 1.1 rearnsha } 252 1.1 rearnsha 253 1.1 rearnsha if (stathz == 0) 254 1.1 rearnsha stathz = hz; 255 1.1 rearnsha else if (stathz < 50 || COUNTS_PER_SEC % stathz) { 256 1.1 rearnsha printf("cannot get %d Hz statclock; using 100 Hz\n", stathz); 257 1.1 rearnsha stathz = 100; 258 1.1 rearnsha } 259 1.1 rearnsha 260 1.1 rearnsha if (profhz == 0) 261 1.1 rearnsha profhz = stathz * 5; 262 1.1 rearnsha else if (profhz < stathz || COUNTS_PER_SEC % profhz) { 263 1.1 rearnsha printf("cannot get %d Hz profclock; using %d Hz\n", profhz, 264 1.1 rearnsha stathz); 265 1.1 rearnsha profhz = stathz; 266 1.1 rearnsha } 267 1.1 rearnsha 268 1.1 rearnsha intvl = COUNTS_PER_SEC / hz; 269 1.1 rearnsha statint = COUNTS_PER_SEC / stathz; 270 1.1 rearnsha profint = COUNTS_PER_SEC / profhz; 271 1.1 rearnsha minint = statint / 2 + 100; 272 1.1 rearnsha while (statvar > minint) 273 1.1 rearnsha statvar >>= 1; 274 1.1 rearnsha 275 1.1 rearnsha /* Adjust interval counts, per note above. */ 276 1.1 rearnsha intvl--; 277 1.1 rearnsha statint--; 278 1.1 rearnsha profint--; 279 1.1 rearnsha 280 1.1 rearnsha /* Calculate the base reload values. */ 281 1.1 rearnsha statmin = statint - (statvar >> 1); 282 1.1 rearnsha profmin = profint - (statvar >> 1); 283 1.1 rearnsha timer2min = statmin; 284 1.1 rearnsha statprev = statint; 285 1.1 rearnsha 286 1.1 rearnsha /* Report the clock frequencies */ 287 1.1 rearnsha printf("clock: hz=%d stathz = %d profhz = %d\n", hz, stathz, profhz); 288 1.1 rearnsha 289 1.1 rearnsha /* Setup timer 1 and claim interrupt */ 290 1.7 rearnsha ifpga_sc->sc_clockintr = ifpga_intr_establish(IFPGA_TIMER1_IRQ, 291 1.7 rearnsha IPL_CLOCK, clockhandler, 0); 292 1.7 rearnsha if (ifpga_sc->sc_clockintr == NULL) 293 1.4 provos panic("%s: Cannot install timer 1 interrupt handler", 294 1.14 dyoung device_xname(ifpga_dev)); 295 1.1 rearnsha 296 1.7 rearnsha ifpga_sc->sc_clock_count 297 1.1 rearnsha = load_timer(IFPGA_TIMER1_BASE, intvl); 298 1.1 rearnsha 299 1.1 rearnsha /* 300 1.1 rearnsha * Use ticks per 256us for accuracy since ticks per us is often 301 1.1 rearnsha * fractional e.g. @ 66MHz 302 1.1 rearnsha */ 303 1.7 rearnsha ifpga_sc->sc_clock_ticks_per_256us = 304 1.7 rearnsha ((((ifpga_sc->sc_clock_count * hz) / 1000) * 256) / 1000); 305 1.1 rearnsha 306 1.1 rearnsha clock_started = 1; 307 1.1 rearnsha 308 1.1 rearnsha /* Set up timer 2 as statclk/profclk. */ 309 1.7 rearnsha ifpga_sc->sc_statclockintr = ifpga_intr_establish(IFPGA_TIMER2_IRQ, 310 1.11 ad IPL_HIGH, statclockhandler, 0); 311 1.7 rearnsha if (ifpga_sc->sc_statclockintr == NULL) 312 1.4 provos panic("%s: Cannot install timer 2 interrupt handler", 313 1.14 dyoung device_xname(ifpga_dev)); 314 1.1 rearnsha load_timer(IFPGA_TIMER2_BASE, statint); 315 1.12 joerg 316 1.12 joerg tc_init(&ifpga_timecounter); 317 1.1 rearnsha } 318 1.1 rearnsha 319 1.12 joerg static u_int 320 1.12 joerg ifpga_get_timecount(struct timecounter *tc) 321 1.1 rearnsha { 322 1.12 joerg u_int base, counter; 323 1.1 rearnsha 324 1.12 joerg do { 325 1.12 joerg base = ifpga_base; 326 1.12 joerg counter = getclock(); 327 1.12 joerg } while (base != ifpga_base); 328 1.1 rearnsha 329 1.12 joerg return base - counter; 330 1.1 rearnsha } 331 1.1 rearnsha 332 1.1 rearnsha /* 333 1.1 rearnsha * Estimated loop for n microseconds 334 1.1 rearnsha */ 335 1.1 rearnsha 336 1.1 rearnsha /* Need to re-write this to use the timers */ 337 1.1 rearnsha 338 1.1 rearnsha /* One day soon I will actually do this */ 339 1.1 rearnsha 340 1.1 rearnsha int delaycount = 50; 341 1.1 rearnsha 342 1.1 rearnsha void 343 1.1 rearnsha delay(u_int n) 344 1.1 rearnsha { 345 1.1 rearnsha if (clock_started) { 346 1.1 rearnsha u_int starttime; 347 1.1 rearnsha u_int curtime; 348 1.6 rearnsha u_int delta = 0; 349 1.7 rearnsha u_int count_max = ifpga_sc->sc_clock_count; 350 1.1 rearnsha 351 1.1 rearnsha starttime = getclock(); 352 1.1 rearnsha 353 1.1 rearnsha n *= IFPGA_TIMER1_FREQ / 1000000; 354 1.1 rearnsha 355 1.1 rearnsha do { 356 1.6 rearnsha n -= delta; 357 1.1 rearnsha curtime = getclock(); 358 1.6 rearnsha delta = curtime - starttime; 359 1.6 rearnsha if (curtime < starttime) 360 1.6 rearnsha delta += count_max; 361 1.6 rearnsha starttime = curtime; 362 1.6 rearnsha } while (n > delta); 363 1.1 rearnsha } else { 364 1.1 rearnsha volatile u_int i; 365 1.1 rearnsha 366 1.1 rearnsha if (n == 0) return; 367 1.1 rearnsha while (n-- > 0) { 368 1.1 rearnsha /* XXX - Seriously gross hack */ 369 1.1 rearnsha if (cputype == CPU_ID_SA110) 370 1.1 rearnsha for (i = delaycount; --i;) 371 1.1 rearnsha ; 372 1.1 rearnsha else 373 1.1 rearnsha for (i = 8; --i;) 374 1.1 rearnsha ; 375 1.1 rearnsha } 376 1.1 rearnsha } 377 1.1 rearnsha } 378