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