1 /* $NetBSD: iomd_clock.c,v 1.22 2007/02/22 05:14:05 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 __KERNEL_RCSID(0, "$NetBSD: iomd_clock.c,v 1.22 2007/02/22 05:14:05 thorpej Exp $"); 51 52 #include <sys/systm.h> 53 #include <sys/kernel.h> 54 #include <sys/time.h> 55 #include <sys/timetc.h> 56 #include <sys/device.h> 57 #include <sys/lock.h> 58 59 #include <dev/clock_subr.h> 60 61 #include <machine/intr.h> 62 63 #include <arm/cpufunc.h> 64 65 #include <arm/iomd/iomdvar.h> 66 #include <arm/iomd/iomdreg.h> 67 68 struct clock_softc { 69 struct device sc_dev; 70 bus_space_tag_t sc_iot; 71 bus_space_handle_t sc_ioh; 72 }; 73 74 #define TIMER_FREQUENCY 2000000 /* 2MHz clock */ 75 #define TICKS_PER_MICROSECOND (TIMER_FREQUENCY / 1000000) 76 77 static void *clockirq; 78 static void *statclockirq; 79 static struct clock_softc *clock_sc; 80 static int timer0_count; 81 82 static int clockmatch(struct device *parent, struct cfdata *cf, void *aux); 83 static void clockattach(struct device *parent, struct device *self, void *aux); 84 #ifdef DIAGNOSTIC 85 static void checkdelay(void); 86 #endif 87 88 static u_int iomd_timecounter0_get(struct timecounter *tc); 89 90 91 static volatile uint32_t timer0_lastcount; 92 static volatile uint32_t timer0_offset; 93 static volatile int timer0_ticked; 94 /* TODO: Get IRQ status */ 95 96 static struct simplelock tmr_lock = SIMPLELOCK_INITIALIZER; /* protect TC timer variables */ 97 98 99 static struct timecounter iomd_timecounter = { 100 iomd_timecounter0_get, 101 0, /* No poll_pps */ 102 ~0, /* 32bit accuracy */ 103 TIMER_FREQUENCY, 104 "iomd_timer0", 105 100 106 }; 107 108 int clockhandler(void *); 109 int statclockhandler(void *); 110 111 CFATTACH_DECL(clock, sizeof(struct clock_softc), 112 clockmatch, clockattach, NULL, NULL); 113 114 /* 115 * int clockmatch(struct device *parent, void *match, void *aux) 116 * 117 * Just return ok for this if it is device 0 118 */ 119 120 static int 121 clockmatch(struct device *parent, struct cfdata *cf, void *aux) 122 { 123 struct clk_attach_args *ca = aux; 124 125 if (strcmp(ca->ca_name, "clk") == 0) 126 return(1); 127 return(0); 128 } 129 130 131 /* 132 * void clockattach(struct device *parent, struct device *dev, void *aux) 133 * 134 * Map the IOMD and identify it. 135 * Then configure the child devices based on the IOMD ID. 136 */ 137 138 static void 139 clockattach(struct device *parent, struct device *self, void *aux) 140 { 141 struct clock_softc *sc = (struct clock_softc *)self; 142 struct clk_attach_args *ca = aux; 143 144 sc->sc_iot = ca->ca_iot; 145 sc->sc_ioh = ca->ca_ioh; /* This is a handle for the whole IOMD */ 146 147 clock_sc = sc; 148 149 /* Cannot do anything until cpu_initclocks() has been called */ 150 151 printf("\n"); 152 } 153 154 155 static void 156 tickle_tc(void) 157 { 158 if (timer0_count && 159 timecounter->tc_get_timecount == iomd_timecounter0_get) { 160 simple_lock(&tmr_lock); 161 if (timer0_ticked) 162 timer0_ticked = 0; 163 else { 164 timer0_offset += timer0_count; 165 timer0_lastcount = 0; 166 } 167 simple_unlock(&tmr_lock); 168 } 169 170 } 171 172 173 /* 174 * int clockhandler(struct clockframe *frame) 175 * 176 * Function called by timer 0 interrupts. This just calls 177 * hardclock(). Eventually the irqhandler can call hardclock() directly 178 * but for now we use this function so that we can debug IRQ's 179 */ 180 181 int 182 clockhandler(void *cookie) 183 { 184 struct clockframe *frame = cookie; 185 tickle_tc(); 186 187 hardclock(frame); 188 return 0; /* Pass the interrupt on down the chain */ 189 } 190 191 192 /* 193 * int statclockhandler(struct clockframe *frame) 194 * 195 * Function called by timer 1 interrupts. This just calls 196 * statclock(). Eventually the irqhandler can call statclock() directly 197 * but for now we use this function so that we can debug IRQ's 198 */ 199 200 int 201 statclockhandler(void *cookie) 202 { 203 struct clockframe *frame = cookie; 204 205 statclock(frame); 206 return 0; /* Pass the interrupt on down the chain */ 207 } 208 209 210 /* 211 * void setstatclockrate(int newhz) 212 * 213 * Set the stat clock rate. The stat clock uses timer1 214 */ 215 216 void 217 setstatclockrate(int newhz) 218 { 219 int count; 220 221 count = TIMER_FREQUENCY / newhz; 222 223 printf("Setting statclock to %dHz (%d ticks)\n", newhz, count); 224 225 bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh, 226 IOMD_T1LOW, (count >> 0) & 0xff); 227 bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh, 228 IOMD_T1HIGH, (count >> 8) & 0xff); 229 230 /* reload the counter */ 231 232 bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh, 233 IOMD_T1GO, 0); 234 } 235 236 237 #ifdef DIAGNOSTIC 238 static void 239 checkdelay(void) 240 { 241 struct timeval start, end, diff; 242 243 microtime(&start); 244 delay(10000); 245 microtime(&end); 246 timersub(&end, &start, &diff); 247 if (diff.tv_sec > 0) 248 return; 249 if (diff.tv_usec > 10000) 250 return; 251 printf("WARNING: delay(10000) took %ld us\n", diff.tv_usec); 252 } 253 #endif 254 255 /* 256 * void cpu_initclocks(void) 257 * 258 * Initialise the clocks. 259 * This sets up the two timers in the IOMD and installs the IRQ handlers 260 * 261 * NOTE: Currently only timer 0 is setup and the IRQ handler is not installed 262 */ 263 264 void 265 cpu_initclocks(void) 266 { 267 /* 268 * Load timer 0 with count down value 269 * This timer generates 100Hz interrupts for the system clock 270 */ 271 272 printf("clock: hz=%d stathz = %d profhz = %d\n", hz, stathz, profhz); 273 274 timer0_count = TIMER_FREQUENCY / hz; 275 276 bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh, 277 IOMD_T0LOW, (timer0_count >> 0) & 0xff); 278 bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh, 279 IOMD_T0HIGH, (timer0_count >> 8) & 0xff); 280 281 /* reload the counter */ 282 283 bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh, 284 IOMD_T0GO, 0); 285 286 clockirq = intr_claim(IRQ_TIMER0, IPL_CLOCK, "tmr0 hard clk", 287 clockhandler, 0); 288 289 if (clockirq == NULL) 290 panic("%s: Cannot installer timer 0 IRQ handler", 291 clock_sc->sc_dev.dv_xname); 292 293 if (stathz) { 294 setstatclockrate(stathz); 295 statclockirq = intr_claim(IRQ_TIMER1, IPL_CLOCK, 296 "tmr1 stat clk", statclockhandler, 0); 297 if (statclockirq == NULL) 298 panic("%s: Cannot installer timer 1 IRQ handler", 299 clock_sc->sc_dev.dv_xname); 300 } 301 #ifdef DIAGNOSTIC 302 checkdelay(); 303 #endif 304 tc_init(&iomd_timecounter); 305 } 306 307 308 309 static u_int iomd_timecounter0_get(struct timecounter *tc) 310 { 311 int s; 312 u_int tm; 313 314 /* 315 * Latch the current value of the timer and then read it. 316 * This garentees an atmoic reading of the time. 317 */ 318 s = splhigh(); 319 bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh, 320 IOMD_T0LATCH, 0); 321 322 tm = bus_space_read_1(clock_sc->sc_iot, clock_sc->sc_ioh, 323 IOMD_T0LOW); 324 tm += (bus_space_read_1(clock_sc->sc_iot, clock_sc->sc_ioh, 325 IOMD_T0HIGH) << 8); 326 splx(s); 327 simple_lock(&tmr_lock); 328 329 tm = timer0_count - tm; 330 331 332 if (timer0_count && 333 (tm < timer0_lastcount || (!timer0_ticked && false/* XXX: clkintr_pending */))) { 334 timer0_ticked = 1; 335 timer0_offset += timer0_count; 336 } 337 338 timer0_lastcount = tm; 339 tm += timer0_offset; 340 341 simple_unlock(&tmr_lock); 342 return tm; 343 } 344 345 346 347 /* 348 * Estimated loop for n microseconds 349 */ 350 351 /* Need to re-write this to use the timers */ 352 353 /* One day soon I will actually do this */ 354 355 int delaycount = 100; 356 357 void 358 delay(u_int n) 359 { 360 volatile u_int n2; 361 volatile u_int i; 362 363 if (n == 0) return; 364 n2 = n; 365 while (n2-- > 0) { 366 if (cputype == CPU_ID_SA110) /* XXX - Seriously gross hack */ 367 for (i = delaycount; --i;); 368 else 369 for (i = 8; --i;); 370 } 371 } 372 373 /* End of iomd_clock.c */ 374