1 /* $NetBSD: footbridge_clock.c,v 1.6 2002/05/04 10:04:42 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 header files */ 38 39 #include <sys/types.h> 40 #include <sys/param.h> 41 #include <sys/systm.h> 42 #include <sys/kernel.h> 43 #include <sys/time.h> 44 #include <sys/device.h> 45 46 #include <machine/intr.h> 47 48 #include <arm/cpufunc.h> 49 50 #include <arm/footbridge/dc21285reg.h> 51 #include <arm/footbridge/footbridgevar.h> 52 #include <arm/footbridge/footbridge.h> 53 54 extern struct footbridge_softc *clock_sc; 55 extern u_int dc21285_fclk; 56 57 int clockhandler __P((void *)); 58 int statclockhandler __P((void *)); 59 static int load_timer __P((int, int)); 60 61 62 #if 0 63 static int clockmatch __P((struct device *parent, struct cfdata *cf, void *aux)); 64 static void clockattach __P((struct device *parent, struct device *self, void *aux)); 65 66 struct cfattach footbridge_clock_ca = { 67 sizeof(struct clock_softc), clockmatch, clockattach 68 }; 69 70 /* 71 * int clockmatch(struct device *parent, void *match, void *aux) 72 * 73 * Just return ok for this if it is device 0 74 */ 75 76 static int 77 clockmatch(parent, cf, aux) 78 struct device *parent; 79 struct cfdata *cf; 80 void *aux; 81 { 82 union footbridge_attach_args *fba = aux; 83 84 if (strcmp(fba->fba_ca.ca_name, "clk") == 0) 85 return(1); 86 return(0); 87 } 88 89 90 /* 91 * void clockattach(struct device *parent, struct device *dev, void *aux) 92 * 93 */ 94 95 static void 96 clockattach(parent, self, aux) 97 struct device *parent; 98 struct device *self; 99 void *aux; 100 { 101 struct clock_softc *sc = (struct clock_softc *)self; 102 union footbridge_attach_args *fba = aux; 103 104 sc->sc_iot = fba->fba_ca.ca_iot; 105 sc->sc_ioh = fba->fba_ca.ca_ioh; 106 107 clock_sc = sc; 108 109 /* Cannot do anything until cpu_initclocks() has been called */ 110 111 printf("\n"); 112 } 113 #endif 114 115 /* 116 * int clockhandler(struct clockframe *frame) 117 * 118 * Function called by timer 1 interrupts. 119 * This just clears the interrupt condition and calls hardclock(). 120 */ 121 122 int 123 clockhandler(aframe) 124 void *aframe; 125 { 126 struct clockframe *frame = aframe; 127 bus_space_write_4(clock_sc->sc_iot, clock_sc->sc_ioh, 128 TIMER_1_CLEAR, 0); 129 hardclock(frame); 130 return(0); /* Pass the interrupt on down the chain */ 131 } 132 133 134 /* 135 * int statclockhandler(struct clockframe *frame) 136 * 137 * Function called by timer 2 interrupts. 138 * This just clears the interrupt condition and calls statclock(). 139 */ 140 141 int 142 statclockhandler(aframe) 143 void *aframe; 144 { 145 struct clockframe *frame = aframe; 146 bus_space_write_4(clock_sc->sc_iot, clock_sc->sc_ioh, 147 TIMER_2_CLEAR, 0); 148 statclock(frame); 149 return(0); /* Pass the interrupt on down the chain */ 150 } 151 152 static int 153 load_timer(base, hz) 154 int base; 155 int hz; 156 { 157 unsigned int timer_count; 158 int control; 159 160 timer_count = dc21285_fclk / hz; 161 if (timer_count > TIMER_MAX * 16) { 162 control = TIMER_FCLK_256; 163 timer_count >>= 8; 164 } else if (timer_count > TIMER_MAX) { 165 control = TIMER_FCLK_16; 166 timer_count >>= 4; 167 } else 168 control = TIMER_FCLK; 169 170 control |= (TIMER_ENABLE | TIMER_MODE_PERIODIC); 171 bus_space_write_4(clock_sc->sc_iot, clock_sc->sc_ioh, 172 base + TIMER_LOAD, timer_count); 173 bus_space_write_4(clock_sc->sc_iot, clock_sc->sc_ioh, 174 base + TIMER_CONTROL, control); 175 bus_space_write_4(clock_sc->sc_iot, clock_sc->sc_ioh, 176 base + TIMER_CLEAR, 0); 177 return(timer_count); 178 } 179 180 /* 181 * void setstatclockrate(int hz) 182 * 183 * Set the stat clock rate. The stat clock uses timer2 184 */ 185 186 void 187 setstatclockrate(hz) 188 int hz; 189 { 190 191 clock_sc->sc_statclock_count = load_timer(TIMER_2_BASE, hz); 192 } 193 194 /* 195 * void cpu_initclocks(void) 196 * 197 * Initialise the clocks. 198 * 199 * Timer 1 is used for the main system clock (hardclock) 200 * Timer 2 is used for the statistics clock (statclock) 201 */ 202 203 void 204 cpu_initclocks() 205 { 206 207 /* Report the clock frequencies */ 208 printf("clock: hz=%d stathz = %d profhz = %d\n", hz, stathz, profhz); 209 210 /* Setup timer 1 and claim interrupt */ 211 clock_sc->sc_clock_count = load_timer(TIMER_1_BASE, hz); 212 213 /* 214 * Use ticks per 256us for accuracy since ticks per us is often 215 * fractional e.g. @ 66MHz 216 */ 217 clock_sc->sc_clock_ticks_per_256us = 218 ((((clock_sc->sc_clock_count * hz) / 1000) * 256) / 1000); 219 clock_sc->sc_clockintr = intr_claim(IRQ_TIMER_1, IPL_CLOCK, 220 "tmr1 hard clk", clockhandler, 0); 221 222 if (clock_sc->sc_clockintr == NULL) 223 panic("%s: Cannot install timer 1 interrupt handler\n", 224 clock_sc->sc_dev.dv_xname); 225 226 /* If stathz is non-zero then setup the stat clock */ 227 if (stathz) { 228 /* Setup timer 2 and claim interrupt */ 229 setstatclockrate(stathz); 230 clock_sc->sc_statclockintr = intr_claim(IRQ_TIMER_2, IPL_CLOCK, 231 "tmr2 stat clk", statclockhandler, 0); 232 if (clock_sc->sc_statclockintr == NULL) 233 panic("%s: Cannot install timer 2 interrupt handler\n", 234 clock_sc->sc_dev.dv_xname); 235 } 236 } 237 238 239 /* 240 * void microtime(struct timeval *tvp) 241 * 242 * Fill in the specified timeval struct with the current time 243 * accurate to the microsecond. 244 */ 245 246 void 247 microtime(tvp) 248 struct timeval *tvp; 249 { 250 int s; 251 int tm; 252 int deltatm; 253 static struct timeval oldtv; 254 255 if (clock_sc == NULL || clock_sc->sc_clock_count == 0) 256 return; 257 258 s = splhigh(); 259 260 tm = bus_space_read_4(clock_sc->sc_iot, clock_sc->sc_ioh, 261 TIMER_1_VALUE); 262 263 deltatm = clock_sc->sc_clock_count - tm; 264 265 #ifdef DIAGNOSTIC 266 if (deltatm < 0) 267 panic("opps deltatm < 0 tm=%d deltatm=%d\n", tm, deltatm); 268 #endif 269 270 /* Fill in the timeval struct */ 271 *tvp = time; 272 tvp->tv_usec += ((deltatm << 8) / clock_sc->sc_clock_ticks_per_256us); 273 274 /* Make sure the micro seconds don't overflow. */ 275 while (tvp->tv_usec >= 1000000) { 276 tvp->tv_usec -= 1000000; 277 ++tvp->tv_sec; 278 } 279 280 /* Make sure the time has advanced. */ 281 if (tvp->tv_sec == oldtv.tv_sec && 282 tvp->tv_usec <= oldtv.tv_usec) { 283 tvp->tv_usec = oldtv.tv_usec + 1; 284 if (tvp->tv_usec >= 1000000) { 285 tvp->tv_usec -= 1000000; 286 ++tvp->tv_sec; 287 } 288 } 289 290 oldtv = *tvp; 291 (void)splx(s); 292 } 293 294 /* 295 * Use a timer to track microseconds, if the footbridge hasn't been setup we 296 * rely on an estimated loop, however footbridge is attached very early on. 297 */ 298 299 static int delay_clock_count = 0; 300 static int delay_count_per_usec = 0; 301 302 void 303 calibrate_delay(void) 304 { 305 delay_clock_count = load_timer(TIMER_3_BASE, 100); 306 delay_count_per_usec = delay_clock_count/10000; 307 } 308 309 int delaycount = 500; 310 311 void 312 delay(n) 313 u_int n; 314 { 315 volatile u_int i; 316 uint32_t cur, last, delta, usecs; 317 318 if (n == 0) return; 319 320 321 // not calibrated the timer yet, so try to live with this horrible 322 // loop! 323 if (delay_clock_count == 0) 324 { 325 while (n-- > 0) { 326 for (i = delaycount; --i;); 327 } 328 return; 329 } 330 last = bus_space_read_4(clock_sc->sc_iot, clock_sc->sc_ioh, 331 TIMER_3_VALUE); 332 333 delta = usecs = 0; 334 335 while (n > usecs) 336 { 337 cur = bus_space_read_4(clock_sc->sc_iot, clock_sc->sc_ioh, 338 TIMER_3_VALUE); 339 if (last < cur) 340 /* timer has wrapped */ 341 delta += ((delay_clock_count - cur) + last); 342 else 343 delta += (last - cur); 344 345 if (last == 0 && cur == 0) 346 { 347 /* reset the timer, not sure this is really needed */ 348 bus_space_write_4(clock_sc->sc_iot, clock_sc->sc_ioh, 349 TIMER_3_CLEAR, 0); 350 } 351 last = cur; 352 353 if (delta >= delay_count_per_usec) 354 { 355 usecs += delta / delay_count_per_usec; 356 delta %= delay_count_per_usec; 357 } 358 } 359 } 360 361 /* End of footbridge_clock.c */ 362