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