1 /* $NetBSD: iomd_clock.c,v 1.6 2002/05/02 22:01:47 mycroft Exp $ */ 2 3 /* 4 * Copyright (c) 1994-1997 Mark Brinicombe. 5 * Copyright (c) 1994 Brini. 6 * All rights reserved. 7 * 8 * This code is derived from software written for Brini by Mark Brinicombe 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by Mark Brinicombe. 21 * 4. The name of the company nor the name of the author may be used to 22 * endorse or promote products derived from this software without specific 23 * prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 26 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 27 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 28 * IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 29 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 30 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 31 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 * SUCH DAMAGE. 36 * 37 * RiscBSD kernel project 38 * 39 * clock.c 40 * 41 * Timer related machine specific code 42 * 43 * Created : 29/09/94 44 */ 45 46 /* Include header files */ 47 48 #include <sys/param.h> 49 50 __RCSID("$NetBSD"); 51 52 #include <sys/systm.h> 53 #include <sys/kernel.h> 54 #include <sys/time.h> 55 #include <sys/device.h> 56 57 #include <machine/intr.h> 58 59 #include <arm/cpufunc.h> 60 61 #include <arm/iomd/iomdvar.h> 62 #include <arm/iomd/iomdreg.h> 63 64 struct clock_softc { 65 struct device sc_dev; 66 bus_space_tag_t sc_iot; 67 bus_space_handle_t sc_ioh; 68 }; 69 70 #define TIMER_FREQUENCY 2000000 /* 2MHz clock */ 71 #define TICKS_PER_MICROSECOND (TIMER_FREQUENCY / 1000000) 72 73 static void *clockirq; 74 static void *statclockirq; 75 static struct clock_softc *clock_sc; 76 static int timer0_count; 77 78 static int clockmatch __P((struct device *parent, struct cfdata *cf, void *aux)); 79 static void clockattach __P((struct device *parent, struct device *self, void *aux)); 80 #ifdef DIAGNOSTIC 81 static void checkdelay __P((void)); 82 #endif 83 84 int clockhandler __P((void *)); 85 int statclockhandler __P((void *)); 86 87 struct cfattach clock_ca = { 88 sizeof(struct clock_softc), clockmatch, clockattach 89 }; 90 91 /* 92 * int clockmatch(struct device *parent, void *match, void *aux) 93 * 94 * Just return ok for this if it is device 0 95 */ 96 97 static int 98 clockmatch(parent, cf, aux) 99 struct device *parent; 100 struct cfdata *cf; 101 void *aux; 102 { 103 struct clk_attach_args *ca = aux; 104 105 if (strcmp(ca->ca_name, "clk") == 0) 106 return(1); 107 return(0); 108 } 109 110 111 /* 112 * void clockattach(struct device *parent, struct device *dev, void *aux) 113 * 114 * Map the IOMD and identify it. 115 * Then configure the child devices based on the IOMD ID. 116 */ 117 118 static void 119 clockattach(parent, self, aux) 120 struct device *parent; 121 struct device *self; 122 void *aux; 123 { 124 struct clock_softc *sc = (struct clock_softc *)self; 125 struct clk_attach_args *ca = aux; 126 127 sc->sc_iot = ca->ca_iot; 128 sc->sc_ioh = ca->ca_ioh; /* This is a handle for the whole IOMD */ 129 130 clock_sc = sc; 131 132 /* Cannot do anything until cpu_initclocks() has been called */ 133 134 printf("\n"); 135 } 136 137 138 /* 139 * int clockhandler(struct clockframe *frame) 140 * 141 * Function called by timer 0 interrupts. This just calls 142 * hardclock(). Eventually the irqhandler can call hardclock() directly 143 * but for now we use this function so that we can debug IRQ's 144 */ 145 146 int 147 clockhandler(cookie) 148 void *cookie; 149 { 150 struct clockframe *frame = cookie; 151 152 hardclock(frame); 153 return(0); /* Pass the interrupt on down the chain */ 154 } 155 156 157 /* 158 * int statclockhandler(struct clockframe *frame) 159 * 160 * Function called by timer 1 interrupts. This just calls 161 * statclock(). Eventually the irqhandler can call statclock() directly 162 * but for now we use this function so that we can debug IRQ's 163 */ 164 165 int 166 statclockhandler(cookie) 167 void *cookie; 168 { 169 struct clockframe *frame = cookie; 170 171 statclock(frame); 172 return(0); /* Pass the interrupt on down the chain */ 173 } 174 175 176 /* 177 * void setstatclockrate(int hz) 178 * 179 * Set the stat clock rate. The stat clock uses timer1 180 */ 181 182 void 183 setstatclockrate(hz) 184 int hz; 185 { 186 int count; 187 188 count = TIMER_FREQUENCY / hz; 189 190 printf("Setting statclock to %dHz (%d ticks)\n", hz, count); 191 192 bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh, 193 IOMD_T1LOW, (count >> 0) & 0xff); 194 bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh, 195 IOMD_T1HIGH, (count >> 8) & 0xff); 196 197 /* reload the counter */ 198 199 bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh, 200 IOMD_T1GO, 0); 201 } 202 203 204 #ifdef DIAGNOSTIC 205 static void 206 checkdelay() 207 { 208 struct timeval start, end, diff; 209 210 microtime(&start); 211 delay(10000); 212 microtime(&end); 213 timersub(&end, &start, &diff); 214 if (diff.tv_sec > 0) 215 return; 216 if (diff.tv_usec > 10000) 217 return; 218 printf("WARNING: delay(10000) took %ld us\n", diff.tv_usec); 219 } 220 #endif 221 222 /* 223 * void cpu_initclocks(void) 224 * 225 * Initialise the clocks. 226 * This sets up the two timers in the IOMD and installs the IRQ handlers 227 * 228 * NOTE: Currently only timer 0 is setup and the IRQ handler is not installed 229 */ 230 231 void 232 cpu_initclocks() 233 { 234 /* 235 * Load timer 0 with count down value 236 * This timer generates 100Hz interrupts for the system clock 237 */ 238 239 printf("clock: hz=%d stathz = %d profhz = %d\n", hz, stathz, profhz); 240 241 timer0_count = TIMER_FREQUENCY / hz; 242 243 bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh, 244 IOMD_T0LOW, (timer0_count >> 0) & 0xff); 245 bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh, 246 IOMD_T0HIGH, (timer0_count >> 8) & 0xff); 247 248 /* reload the counter */ 249 250 bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh, 251 IOMD_T0GO, 0); 252 253 clockirq = intr_claim(IRQ_TIMER0, IPL_CLOCK, "tmr0 hard clk", 254 clockhandler, 0); 255 256 if (clockirq == NULL) 257 panic("%s: Cannot installer timer 0 IRQ handler\n", 258 clock_sc->sc_dev.dv_xname); 259 260 if (stathz) { 261 setstatclockrate(stathz); 262 statclockirq = intr_claim(IRQ_TIMER1, IPL_CLOCK, 263 "tmr1 stat clk", statclockhandler, 0); 264 if (statclockirq == NULL) 265 panic("%s: Cannot installer timer 1 IRQ handler\n", 266 clock_sc->sc_dev.dv_xname); 267 } 268 #ifdef DIAGNOSTIC 269 checkdelay(); 270 #endif 271 } 272 273 274 /* 275 * void microtime(struct timeval *tvp) 276 * 277 * Fill in the specified timeval struct with the current time 278 * accurate to the microsecond. 279 */ 280 281 void 282 microtime(tvp) 283 struct timeval *tvp; 284 { 285 int s; 286 int tm; 287 int deltatm; 288 static struct timeval oldtv; 289 290 if (timer0_count == 0) 291 return; 292 293 s = splhigh(); 294 295 /* 296 * Latch the current value of the timer and then read it. 297 * This garentees an atmoic reading of the time. 298 */ 299 300 bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh, 301 IOMD_T0LATCH, 0); 302 303 tm = bus_space_read_1(clock_sc->sc_iot, clock_sc->sc_ioh, 304 IOMD_T0LOW); 305 tm += (bus_space_read_1(clock_sc->sc_iot, clock_sc->sc_ioh, 306 IOMD_T0HIGH) << 8); 307 308 deltatm = timer0_count - tm; 309 if (deltatm < 0) 310 printf("opps deltatm < 0 tm=%d deltatm=%d\n", 311 tm, deltatm); 312 313 /* Fill in the timeval struct */ 314 *tvp = time; 315 316 tvp->tv_usec += (deltatm / TICKS_PER_MICROSECOND); 317 318 /* Make sure the micro seconds don't overflow. */ 319 while (tvp->tv_usec >= 1000000) { 320 tvp->tv_usec -= 1000000; 321 ++tvp->tv_sec; 322 } 323 324 /* Make sure the time has advanced. */ 325 if (tvp->tv_sec == oldtv.tv_sec && 326 tvp->tv_usec <= oldtv.tv_usec) { 327 tvp->tv_usec = oldtv.tv_usec + 1; 328 if (tvp->tv_usec >= 1000000) { 329 tvp->tv_usec -= 1000000; 330 ++tvp->tv_sec; 331 } 332 } 333 334 oldtv = *tvp; 335 (void)splx(s); 336 } 337 338 /* 339 * Estimated loop for n microseconds 340 */ 341 342 /* Need to re-write this to use the timers */ 343 344 /* One day soon I will actually do this */ 345 346 int delaycount = 100; 347 348 void 349 delay(n) 350 u_int n; 351 { 352 u_int i; 353 354 if (n == 0) return; 355 while (n-- > 0) { 356 if (cputype == CPU_ID_SA110) /* XXX - Seriously gross hack */ 357 for (i = delaycount; --i;); 358 else 359 for (i = 8; --i;); 360 } 361 } 362 363 /* End of iomd_clock.c */ 364