1 /* $NetBSD: iomd_clock.c,v 1.10 2002/10/02 15:45:12 thorpej 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 CFATTACH_DECL(clock, sizeof(struct clock_softc), 88 clockmatch, clockattach, NULL, NULL); 89 90 /* 91 * int clockmatch(struct device *parent, void *match, void *aux) 92 * 93 * Just return ok for this if it is device 0 94 */ 95 96 static int 97 clockmatch(parent, cf, aux) 98 struct device *parent; 99 struct cfdata *cf; 100 void *aux; 101 { 102 struct clk_attach_args *ca = aux; 103 104 if (strcmp(ca->ca_name, "clk") == 0) 105 return(1); 106 return(0); 107 } 108 109 110 /* 111 * void clockattach(struct device *parent, struct device *dev, void *aux) 112 * 113 * Map the IOMD and identify it. 114 * Then configure the child devices based on the IOMD ID. 115 */ 116 117 static void 118 clockattach(parent, self, aux) 119 struct device *parent; 120 struct device *self; 121 void *aux; 122 { 123 struct clock_softc *sc = (struct clock_softc *)self; 124 struct clk_attach_args *ca = aux; 125 126 sc->sc_iot = ca->ca_iot; 127 sc->sc_ioh = ca->ca_ioh; /* This is a handle for the whole IOMD */ 128 129 clock_sc = sc; 130 131 /* Cannot do anything until cpu_initclocks() has been called */ 132 133 printf("\n"); 134 } 135 136 137 /* 138 * int clockhandler(struct clockframe *frame) 139 * 140 * Function called by timer 0 interrupts. This just calls 141 * hardclock(). Eventually the irqhandler can call hardclock() directly 142 * but for now we use this function so that we can debug IRQ's 143 */ 144 145 int 146 clockhandler(cookie) 147 void *cookie; 148 { 149 struct clockframe *frame = cookie; 150 151 hardclock(frame); 152 return(0); /* Pass the interrupt on down the chain */ 153 } 154 155 156 /* 157 * int statclockhandler(struct clockframe *frame) 158 * 159 * Function called by timer 1 interrupts. This just calls 160 * statclock(). Eventually the irqhandler can call statclock() directly 161 * but for now we use this function so that we can debug IRQ's 162 */ 163 164 int 165 statclockhandler(cookie) 166 void *cookie; 167 { 168 struct clockframe *frame = cookie; 169 170 statclock(frame); 171 return(0); /* Pass the interrupt on down the chain */ 172 } 173 174 175 /* 176 * void setstatclockrate(int hz) 177 * 178 * Set the stat clock rate. The stat clock uses timer1 179 */ 180 181 void 182 setstatclockrate(hz) 183 int hz; 184 { 185 int count; 186 187 count = TIMER_FREQUENCY / hz; 188 189 printf("Setting statclock to %dHz (%d ticks)\n", hz, count); 190 191 bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh, 192 IOMD_T1LOW, (count >> 0) & 0xff); 193 bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh, 194 IOMD_T1HIGH, (count >> 8) & 0xff); 195 196 /* reload the counter */ 197 198 bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh, 199 IOMD_T1GO, 0); 200 } 201 202 203 #ifdef DIAGNOSTIC 204 static void 205 checkdelay() 206 { 207 struct timeval start, end, diff; 208 209 microtime(&start); 210 delay(10000); 211 microtime(&end); 212 timersub(&end, &start, &diff); 213 if (diff.tv_sec > 0) 214 return; 215 if (diff.tv_usec > 10000) 216 return; 217 printf("WARNING: delay(10000) took %ld us\n", diff.tv_usec); 218 } 219 #endif 220 221 /* 222 * void cpu_initclocks(void) 223 * 224 * Initialise the clocks. 225 * This sets up the two timers in the IOMD and installs the IRQ handlers 226 * 227 * NOTE: Currently only timer 0 is setup and the IRQ handler is not installed 228 */ 229 230 void 231 cpu_initclocks() 232 { 233 /* 234 * Load timer 0 with count down value 235 * This timer generates 100Hz interrupts for the system clock 236 */ 237 238 printf("clock: hz=%d stathz = %d profhz = %d\n", hz, stathz, profhz); 239 240 timer0_count = TIMER_FREQUENCY / hz; 241 242 bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh, 243 IOMD_T0LOW, (timer0_count >> 0) & 0xff); 244 bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh, 245 IOMD_T0HIGH, (timer0_count >> 8) & 0xff); 246 247 /* reload the counter */ 248 249 bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh, 250 IOMD_T0GO, 0); 251 252 clockirq = intr_claim(IRQ_TIMER0, IPL_CLOCK, "tmr0 hard clk", 253 clockhandler, 0); 254 255 if (clockirq == NULL) 256 panic("%s: Cannot installer timer 0 IRQ handler", 257 clock_sc->sc_dev.dv_xname); 258 259 if (stathz) { 260 setstatclockrate(stathz); 261 statclockirq = intr_claim(IRQ_TIMER1, IPL_CLOCK, 262 "tmr1 stat clk", statclockhandler, 0); 263 if (statclockirq == NULL) 264 panic("%s: Cannot installer timer 1 IRQ handler", 265 clock_sc->sc_dev.dv_xname); 266 } 267 #ifdef DIAGNOSTIC 268 checkdelay(); 269 #endif 270 } 271 272 273 /* 274 * void microtime(struct timeval *tvp) 275 * 276 * Fill in the specified timeval struct with the current time 277 * accurate to the microsecond. 278 */ 279 280 void 281 microtime(tvp) 282 struct timeval *tvp; 283 { 284 int s; 285 int tm; 286 int deltatm; 287 static struct timeval oldtv; 288 289 if (timer0_count == 0) 290 return; 291 292 s = splhigh(); 293 294 /* 295 * Latch the current value of the timer and then read it. 296 * This garentees an atmoic reading of the time. 297 */ 298 299 bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh, 300 IOMD_T0LATCH, 0); 301 302 tm = bus_space_read_1(clock_sc->sc_iot, clock_sc->sc_ioh, 303 IOMD_T0LOW); 304 tm += (bus_space_read_1(clock_sc->sc_iot, clock_sc->sc_ioh, 305 IOMD_T0HIGH) << 8); 306 307 deltatm = timer0_count - tm; 308 if (deltatm < 0) 309 printf("opps deltatm < 0 tm=%d deltatm=%d\n", 310 tm, deltatm); 311 312 /* Fill in the timeval struct */ 313 *tvp = time; 314 315 tvp->tv_usec += (deltatm / TICKS_PER_MICROSECOND); 316 317 /* Make sure the micro seconds don't overflow. */ 318 while (tvp->tv_usec >= 1000000) { 319 tvp->tv_usec -= 1000000; 320 ++tvp->tv_sec; 321 } 322 323 /* Make sure the time has advanced. */ 324 if (tvp->tv_sec == oldtv.tv_sec && 325 tvp->tv_usec <= oldtv.tv_usec) { 326 tvp->tv_usec = oldtv.tv_usec + 1; 327 if (tvp->tv_usec >= 1000000) { 328 tvp->tv_usec -= 1000000; 329 ++tvp->tv_sec; 330 } 331 } 332 333 oldtv = *tvp; 334 (void)splx(s); 335 } 336 337 /* 338 * Estimated loop for n microseconds 339 */ 340 341 /* Need to re-write this to use the timers */ 342 343 /* One day soon I will actually do this */ 344 345 int delaycount = 100; 346 347 void 348 delay(n) 349 u_int n; 350 { 351 u_int i; 352 353 if (n == 0) return; 354 while (n-- > 0) { 355 if (cputype == CPU_ID_SA110) /* XXX - Seriously gross hack */ 356 for (i = delaycount; --i;); 357 else 358 for (i = 8; --i;); 359 } 360 } 361 362 /* End of iomd_clock.c */ 363