1/* $NetBSD: footbridge_clock.c,v 1.25 2008/09/20 14:53:37 chris Exp $ */ 2 3/* 4 * Copyright (c) 1997 Mark Brinicombe. 5 * Copyright (c) 1997 Causality Limited. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by Mark Brinicombe 19 * for the NetBSD Project. 20 * 4. The name of the company nor the name of the author may be used to 21 * endorse or promote products derived from this software without specific 22 * prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 25 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 26 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 27 * IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 28 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 29 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 */ 36 37#include <sys/cdefs.h> 38__KERNEL_RCSID(0, "$NetBSD: footbridge_clock.c,v 1.25 2008/09/20 14:53:37 chris Exp $"); 39 40/* Include header files */ 41 42#include <sys/types.h> 43#include <sys/param.h> 44#include <sys/systm.h> 45#include <sys/kernel.h> 46#include <sys/time.h> 47#include <sys/timetc.h> 48#include <sys/device.h> 49 50#include <machine/intr.h> 51 52#include <arm/cpufunc.h> 53 54#include <arm/footbridge/dc21285reg.h> 55#include <arm/footbridge/footbridgevar.h> 56#include <arm/footbridge/footbridge.h> 57 58extern struct footbridge_softc *clock_sc; 59extern u_int dc21285_fclk; 60 61int clockhandler(void *); 62int statclockhandler(void *); 63static int load_timer(int, int); 64 65/* 66 * Statistics clock variance, in usec. Variance must be a 67 * power of two. Since this gives us an even number, not an odd number, 68 * we discard one case and compensate. That is, a variance of 1024 would 69 * give us offsets in [0..1023]. Instead, we take offsets in [1..1023]. 70 * This is symmetric about the point 512, or statvar/2, and thus averages 71 * to that value (assuming uniform random numbers). 72 */ 73const int statvar = 1024; 74int statmin; /* minimum stat clock count in ticks */ 75int statcountperusec; /* number of ticks per usec at current stathz */ 76int statprev; /* last value of we set statclock to */ 77 78void footbridge_tc_init(void); 79 80#if 0 81static int clockmatch(device_t parent, cfdata_t cf, void *aux); 82static void clockattach(device_t parent, device_t self, void *aux); 83 84CFATTACH_DECL_NEW(footbridge_clock, sizeof(struct clock_softc), 85 clockmatch, clockattach, NULL, NULL); 86 87/* 88 * int clockmatch(device_t parent, cfdata_t cf, void *aux); 89 * 90 * Just return ok for this if it is device 0 91 */ 92 93static int 94clockmatch(device_t parent, cfdata_t cf, void *aux) 95{ 96 union footbridge_attach_args *fba = aux; 97 98 if (strcmp(fba->fba_ca.ca_name, "clk") == 0) 99 return 1; 100 return 0; 101} 102 103 104/* 105 * void clockattach(device_t parent, device_t self, void *aux) 106 * 107 */ 108 109static void 110clockattach(device_t parent, device_t self, void *aux) 111{ 112 struct clock_softc *sc = device_private(self); 113 union footbridge_attach_args *fba = aux; 114 115 sc->sc_dev = self; 116 sc->sc_iot = fba->fba_ca.ca_iot; 117 sc->sc_ioh = fba->fba_ca.ca_ioh; 118 119 clock_sc = sc; 120 121 /* Cannot do anything until cpu_initclocks() has been called */ 122 123 aprint_normal("\n"); 124} 125#endif 126 127/* 128 * int clockhandler(struct clockframe *frame) 129 * 130 * Function called by timer 1 interrupts. 131 * This just clears the interrupt condition and calls hardclock(). 132 */ 133 134int 135clockhandler(void *aframe) 136{ 137 struct clockframe *frame = aframe; 138 bus_space_write_4(clock_sc->sc_iot, clock_sc->sc_ioh, 139 TIMER_1_CLEAR, 0); 140 hardclock(frame); 141 return 0; /* Pass the interrupt on down the chain */ 142} 143 144/* 145 * int statclockhandler(struct clockframe *frame) 146 * 147 * Function called by timer 2 interrupts. 148 * This just clears the interrupt condition and calls statclock(). 149 */ 150 151int 152statclockhandler(void *aframe) 153{ 154 struct clockframe *frame = aframe; 155 int newint, r; 156 int currentclock ; 157 158 /* start the clock off again */ 159 bus_space_write_4(clock_sc->sc_iot, clock_sc->sc_ioh, 160 TIMER_2_CLEAR, 0); 161 162 do { 163 r = random() & (statvar-1); 164 } while (r == 0); 165 newint = statmin + (r * statcountperusec); 166 167 /* fetch the current count */ 168 currentclock = bus_space_read_4(clock_sc->sc_iot, clock_sc->sc_ioh, 169 TIMER_2_VALUE); 170 171 /* 172 * work out how much time has run, add another usec for time spent 173 * here 174 */ 175 r = ((statprev - currentclock) + statcountperusec); 176 177 if (r < newint) { 178 newint -= r; 179 r = 0; 180 } 181 else 182 printf("statclockhandler: Statclock overrun\n"); 183 184 185 /* 186 * update the clock to the new counter, this reloads the existing 187 * timer 188 */ 189 bus_space_write_4(clock_sc->sc_iot, clock_sc->sc_ioh, 190 TIMER_2_LOAD, newint); 191 statprev = newint; 192 statclock(frame); 193 if (r) 194 /* 195 * We've completely overrun the previous interval, 196 * make sure we report the correct number of ticks. 197 */ 198 statclock(frame); 199 200 return 0; /* Pass the interrupt on down the chain */ 201} 202 203static int 204load_timer(int base, int herz) 205{ 206 unsigned int timer_count; 207 int control; 208 209 timer_count = dc21285_fclk / herz; 210 if (timer_count > TIMER_MAX_VAL * 16) { 211 control = TIMER_FCLK_256; 212 timer_count >>= 8; 213 } else if (timer_count > TIMER_MAX_VAL) { 214 control = TIMER_FCLK_16; 215 timer_count >>= 4; 216 } else 217 control = TIMER_FCLK; 218 219 control |= (TIMER_ENABLE | TIMER_MODE_PERIODIC); 220 bus_space_write_4(clock_sc->sc_iot, clock_sc->sc_ioh, 221 base + TIMER_LOAD, timer_count); 222 bus_space_write_4(clock_sc->sc_iot, clock_sc->sc_ioh, 223 base + TIMER_CONTROL, control); 224 bus_space_write_4(clock_sc->sc_iot, clock_sc->sc_ioh, 225 base + TIMER_CLEAR, 0); 226 return timer_count; 227} 228 229/* 230 * void setstatclockrate(int herz) 231 * 232 * Set the stat clock rate. The stat clock uses timer2 233 */ 234 235void 236setstatclockrate(int herz) 237{ 238 int statint; 239 int countpersecond; 240 int statvarticks; 241 242 /* statint == num in counter to drop by desired herz */ 243 statint = statprev = clock_sc->sc_statclock_count = 244 load_timer(TIMER_2_BASE, herz); 245 246 /* Get the total ticks a second */ 247 countpersecond = statint * herz; 248 249 /* now work out how many ticks per usec */ 250 statcountperusec = countpersecond / 1000000; 251 252 /* calculate a variance range of statvar */ 253 statvarticks = statcountperusec * statvar; 254 255 /* minimum is statint - 50% of variant */ 256 statmin = statint - (statvarticks / 2); 257} 258 259/* 260 * void cpu_initclocks(void) 261 * 262 * Initialise the clocks. 263 * 264 * Timer 1 is used for the main system clock (hardclock) 265 * Timer 2 is used for the statistics clock (statclock) 266 */ 267 268void 269cpu_initclocks(void) 270{ 271 /* stathz and profhz should be set to something, we have the timer */ 272 if (stathz == 0) 273 stathz = hz; 274 275 if (profhz == 0) 276 profhz = stathz * 5; 277 278 /* Report the clock frequencies */ 279 aprint_debug("clock: hz=%d stathz = %d profhz = %d\n", hz, stathz, profhz); 280 281 /* Setup timer 1 and claim interrupt */ 282 clock_sc->sc_clock_count = load_timer(TIMER_1_BASE, hz); 283 284 /* 285 * Use ticks per 256us for accuracy since ticks per us is often 286 * fractional e.g. @ 66MHz 287 */ 288 clock_sc->sc_clock_ticks_per_256us = 289 ((((clock_sc->sc_clock_count * hz) / 1000) * 256) / 1000); 290 clock_sc->sc_clockintr = footbridge_intr_claim(IRQ_TIMER_1, IPL_CLOCK, 291 "tmr1 hard clk", clockhandler, 0); 292 293 if (clock_sc->sc_clockintr == NULL) 294 panic("%s: Cannot install timer 1 interrupt handler", 295 device_xname(clock_sc->sc_dev)); 296 297 /* If stathz is non-zero then setup the stat clock */ 298 if (stathz) { 299 /* Setup timer 2 and claim interrupt */ 300 setstatclockrate(stathz); 301 clock_sc->sc_statclockintr = footbridge_intr_claim(IRQ_TIMER_2, IPL_HIGH, 302 "tmr2 stat clk", statclockhandler, 0); 303 if (clock_sc->sc_statclockintr == NULL) 304 panic("%s: Cannot install timer 2 interrupt handler", 305 device_xname(clock_sc->sc_dev)); 306 } 307 308 footbridge_tc_init(); 309} 310 311static uint32_t 312fclk_get_count(struct timecounter *tc) 313{ 314 return (TIMER_MAX_VAL - 315 bus_space_read_4(clock_sc->sc_iot, clock_sc->sc_ioh, 316 TIMER_3_VALUE)); 317} 318 319void 320footbridge_tc_init(void) 321{ 322 static struct timecounter fb_tc = { 323 .tc_get_timecount = fclk_get_count, 324 .tc_counter_mask = TIMER_MAX_VAL, 325 .tc_name = "dc21285_fclk", 326 .tc_quality = 100 327 }; 328 fb_tc.tc_frequency = dc21285_fclk; 329 tc_init(&fb_tc); 330} 331 332/* 333 * Use a timer to track microseconds, if the footbridge hasn't been setup we 334 * rely on an estimated loop, however footbridge is attached very early on. 335 */ 336 337static int delay_count_per_usec = 0; 338 339void 340calibrate_delay(void) 341{ 342 /* 343 * For all current footbridge hardware, the fclk runs at a 344 * rate that is sufficiently slow enough that we don't need to 345 * use a prescaler. A prescaler would be needed if the fclk 346 * could wrap within 2 hardclock periods (2 * HZ). With 347 * normal values of HZ (100 and higher), this is unlikely to 348 * ever happen. 349 * 350 * We let TIMER 3 just run free, at the freqeuncy supplied by 351 * dc21285_fclk. 352 */ 353 bus_space_write_4(clock_sc->sc_iot, clock_sc->sc_ioh, 354 TIMER_3_BASE + TIMER_CONTROL, TIMER_ENABLE); 355 delay_count_per_usec = dc21285_fclk / 1000000; 356 if (dc21285_fclk % 1000000) 357 delay_count_per_usec += 1; 358} 359 360void 361delay(unsigned n) 362{ 363 uint32_t cur, last, delta, usecs; 364 365 if (n == 0) 366 return; 367 368 /* 369 * not calibrated the timer yet, so try to live with this horrible 370 * loop! 371 * 372 * Note: a much better solution might be to have the timers 373 * get get calibrated out of mach_init. Of course, the 374 * clock_sc needs to be set up, so we can read/write the clock 375 * registers. 376 */ 377 if (!delay_count_per_usec) 378 { 379 /* 380 * the loop below has a core of 6 instructions 381 * StrongArms top out at 233Mhz, so one instruction takes 382 * 0.004 us, and 6 take 0.025 us, so we need to loop 40 383 * times to make one usec 384 */ 385 int delaycount = 40; 386 volatile int i; 387 388 while (n-- > 0) { 389 for (i = delaycount; --i;); 390 } 391 return; 392 } 393 394 last = bus_space_read_4(clock_sc->sc_iot, clock_sc->sc_ioh, 395 TIMER_3_VALUE); 396 delta = usecs = 0; 397 398 while (n > usecs) { 399 cur = bus_space_read_4(clock_sc->sc_iot, clock_sc->sc_ioh, 400 TIMER_3_VALUE); 401 if (last < cur) 402 /* timer has wrapped */ 403 delta += ((TIMER_MAX_VAL - cur) + last); 404 else 405 delta += (last - cur); 406 407 last = cur; 408 409 while (delta >= delay_count_per_usec) { 410 delta -= delay_count_per_usec; 411 usecs++; 412 } 413 } 414} 415 416/* End of footbridge_clock.c */ 417