1 /* $NetBSD: epclk.c,v 1.10 2007/01/06 16:18:18 christos Exp $ */ 2 3 /* 4 * Copyright (c) 2004 Jesse Off 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the NetBSD 18 * Foundation, Inc. and its contributors. 19 * 4. Neither the name of The NetBSD Foundation nor the names of its 20 * contributors may be used to endorse or promote products derived 21 * from this software without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 24 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 25 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 26 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 27 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 28 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 29 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 30 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 31 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33 * POSSIBILITY OF SUCH DAMAGE. 34 */ 35 36 /* 37 * Driver for the ep93xx clock tick. 38 * 39 * We use the 64Hz RTC interrupt as its the only thing that allows for timekeeping 40 * of a second (crystal error only). There are two general purpose timers 41 * on the ep93xx, but they run at a frequency that makes a perfect integer 42 * number of ticks per second impossible. Note that there was an errata with 43 * the ep93xx processor and many early boards (including the Cirrus eval board) have 44 * a broken crystal oscillator input that may make this 64Hz unreliable. However, 45 * not all boards are susceptible, the Technologic Systems TS-7200 is a notable 46 * exception that is immune to this errata. --joff 47 */ 48 49 #include <sys/cdefs.h> 50 __KERNEL_RCSID(0, "$NetBSD: epclk.c,v 1.10 2007/01/06 16:18:18 christos Exp $"); 51 52 #include <sys/types.h> 53 #include <sys/param.h> 54 #include <sys/systm.h> 55 #include <sys/kernel.h> 56 #include <sys/time.h> 57 #include <sys/device.h> 58 59 #include <machine/bus.h> 60 #include <machine/intr.h> 61 62 #include <arm/cpufunc.h> 63 64 #include <arm/ep93xx/epsocvar.h> 65 #include <arm/ep93xx/epclkreg.h> 66 #include <arm/ep93xx/ep93xxreg.h> 67 #include <arm/ep93xx/ep93xxvar.h> 68 #include <dev/clock_subr.h> 69 70 #include "opt_hz.h" 71 72 static int epclk_match(struct device *, struct cfdata *, void *); 73 static void epclk_attach(struct device *, struct device *, void *); 74 75 void rtcinit(void); 76 77 /* callback functions for intr_functions */ 78 static int epclk_intr(void* arg); 79 80 struct epclk_softc { 81 struct device sc_dev; 82 bus_addr_t sc_baseaddr; 83 bus_space_tag_t sc_iot; 84 bus_space_handle_t sc_ioh; 85 #if defined(HZ) && (HZ == 64) 86 bus_space_handle_t sc_teoi_ioh; 87 #endif 88 int sc_intr; 89 }; 90 91 static struct epclk_softc *epclk_sc = NULL; 92 static u_int32_t tmark; 93 94 95 /* This is a quick ARM way to multiply by 983040/1000000 */ 96 #define US_TO_TIMER4VAL(x) { \ 97 u_int32_t hi, lo, scalar = 4222124650UL; \ 98 __asm volatile ( \ 99 "umull %0, %1, %2, %3;" \ 100 : "=&r"(lo), "=&r"(hi) \ 101 : "r"((x)), "r"(scalar) \ 102 ); \ 103 (x) = hi; \ 104 } 105 106 /* This is a quick ARM way to multiply by 1000000/983040 */ 107 #define TIMER4VAL_TO_US(x) { \ 108 u_int32_t hi, lo, scalar = 2184533333UL; \ 109 __asm volatile ( \ 110 "umull %0, %1, %2, %3;" \ 111 "mov %1, %1, lsl #1;" \ 112 "mov %0, %0, lsr #31;" \ 113 "orr %1, %1, %0;" \ 114 : "=&r"(lo), "=&r"(hi) \ 115 : "r"((x)), "r"(scalar) \ 116 ); \ 117 (x) = hi; \ 118 } 119 120 121 CFATTACH_DECL(epclk, sizeof(struct epclk_softc), 122 epclk_match, epclk_attach, NULL, NULL); 123 124 #define TIMER4VAL() (*(volatile u_int32_t *)(EP93XX_APB_VBASE + \ 125 EP93XX_APB_TIMERS + EP93XX_TIMERS_Timer4ValueLow)) 126 127 static int 128 epclk_match(struct device *parent, struct cfdata *match, void *aux) 129 { 130 131 return 2; 132 } 133 134 static void 135 epclk_attach(struct device *parent, struct device *self, void *aux) 136 { 137 struct epclk_softc *sc; 138 struct epsoc_attach_args *sa; 139 140 printf("\n"); 141 142 sc = (struct epclk_softc*) self; 143 sa = aux; 144 sc->sc_iot = sa->sa_iot; 145 sc->sc_baseaddr = sa->sa_addr; 146 sc->sc_intr = sa->sa_intr; 147 148 if (epclk_sc == NULL) 149 epclk_sc = sc; 150 151 if (bus_space_map(sa->sa_iot, sa->sa_addr, sa->sa_size, 152 0, &sc->sc_ioh)) 153 panic("%s: Cannot map registers", self->dv_xname); 154 #if defined(HZ) && (HZ == 64) 155 if (bus_space_map(sa->sa_iot, EP93XX_APB_HWBASE + EP93XX_APB_SYSCON + 156 EP93XX_SYSCON_TEOI, 4, 0, &sc->sc_teoi_ioh)) 157 panic("%s: Cannot map registers", self->dv_xname); 158 #endif 159 160 /* clear and start the debug timer (Timer4) */ 161 bus_space_write_4(sc->sc_iot, sc->sc_ioh, EP93XX_TIMERS_Timer4Enable, 0); 162 bus_space_write_4(sc->sc_iot, sc->sc_ioh, EP93XX_TIMERS_Timer4Enable, 0x100); 163 } 164 165 /* 166 * epclk_intr: 167 * 168 * Handle the hardclock interrupt. 169 */ 170 static int 171 epclk_intr(void *arg) 172 { 173 struct epclk_softc* sc; 174 175 tmark = TIMER4VAL(); 176 sc = epclk_sc; 177 178 #if defined(HZ) && (HZ == 64) 179 bus_space_write_4(sc->sc_iot, sc->sc_teoi_ioh, 0, 1); 180 #else 181 bus_space_write_4(sc->sc_iot, sc->sc_ioh, EP93XX_TIMERS_Timer1Clear, 1); 182 #endif 183 hardclock((struct clockframe*) arg); 184 return (1); 185 } 186 187 /* 188 * setstatclockrate: 189 * 190 * Set the rate of the statistics clock. 191 * 192 * We assume that hz is either stathz or profhz, and that neither 193 * will change after being set by cpu_initclocks(). We could 194 * recalculate the intervals here, but that would be a pain. 195 */ 196 void 197 setstatclockrate(int newhz) 198 { 199 200 /* use hardclock */ 201 } 202 203 /* 204 * cpu_initclocks: 205 * 206 * Initialize the clock and get them going. 207 */ 208 void 209 cpu_initclocks(void) 210 { 211 struct epclk_softc* sc; 212 213 sc = epclk_sc; 214 stathz = profhz = 0; 215 216 #if defined(HZ) && (HZ == 64) 217 if (hz != 64) panic("HZ must be 64!"); 218 219 /* clear 64Hz interrupt status */ 220 bus_space_write_4(sc->sc_iot, sc->sc_teoi_ioh, 0, 1); 221 #else 222 #define CLOCK_SOURCE_RATE 14745600UL 223 #define CLOCK_TICK_DIV 29 224 #define CLOCK_TICK_RATE \ 225 (((CLOCK_SOURCE_RATE+(CLOCK_TICK_DIV*hz-1))/(CLOCK_TICK_DIV*hz))*hz) 226 #define LATCH ((CLOCK_TICK_RATE + hz/2) / hz) 227 /* setup and start the 16bit timer (Timer1) */ 228 bus_space_write_4(sc->sc_iot, sc->sc_ioh, 229 EP93XX_TIMERS_Timer1Control, 230 (TimerControl_MODE)|(TimerControl_CLKSEL)); 231 bus_space_write_4(sc->sc_iot, sc->sc_ioh, 232 EP93XX_TIMERS_Timer1Load, LATCH-1); 233 bus_space_write_4(sc->sc_iot, sc->sc_ioh, 234 EP93XX_TIMERS_Timer1Control, 235 (TimerControl_ENABLE)|(TimerControl_MODE)|(TimerControl_CLKSEL)); 236 #endif 237 238 ep93xx_intr_establish(sc->sc_intr, IPL_CLOCK, epclk_intr, NULL); 239 } 240 241 /* 242 * microtime: 243 * 244 * Fill in the specified timeval struct with the current time 245 * accurate to the microsecond. 246 */ 247 void 248 microtime(register struct timeval *tvp) 249 { 250 u_int oldirqstate; 251 u_int tmarknow, delta; 252 static struct timeval lasttv; 253 254 #ifdef DEBUG 255 if (epclk_sc == NULL) { 256 printf("microtime: called before initialize epclk\n"); 257 tvp->tv_sec = 0; 258 tvp->tv_usec = 0; 259 return; 260 } 261 #endif 262 263 oldirqstate = disable_interrupts(I32_bit); 264 tmarknow = TIMER4VAL(); 265 266 /* Fill in the timeval struct. */ 267 *tvp = time; 268 if (__predict_false(tmarknow < tmark)) { /* overflow */ 269 delta = tmarknow + (UINT_MAX - tmark); 270 } else { 271 delta = tmarknow - tmark; 272 } 273 274 TIMER4VAL_TO_US(delta); 275 276 tvp->tv_usec += delta; 277 278 /* Make sure microseconds doesn't overflow. */ 279 while (__predict_false(tvp->tv_usec >= 1000000)) { 280 tvp->tv_usec -= 1000000; 281 tvp->tv_sec++; 282 } 283 284 /* Make sure the time has advanced. */ 285 if (__predict_false(tvp->tv_sec == lasttv.tv_sec && 286 tvp->tv_usec <= lasttv.tv_usec)) { 287 tvp->tv_usec = lasttv.tv_usec + 1; 288 if (tvp->tv_usec >= 1000000) { 289 tvp->tv_usec -= 1000000; 290 tvp->tv_sec++; 291 } 292 } 293 294 lasttv = *tvp; 295 296 restore_interrupts(oldirqstate); 297 } 298 299 /* 300 * delay: 301 * 302 * Delay for at least N microseconds. 303 */ 304 void 305 delay(unsigned int len) 306 { 307 u_int32_t start, end, ticks; 308 309 #ifdef DEBUG 310 if (epclk_sc == NULL) { 311 printf("delay: called before start epclk\n"); 312 return; 313 } 314 #endif 315 316 ticks = start = TIMER4VAL(); 317 US_TO_TIMER4VAL(len); 318 end = start + len; 319 while (start <= ticks && ticks > end) { 320 /* wait for Timer4ValueLow wraparound */ 321 ticks = TIMER4VAL(); 322 } 323 while (ticks <= end) { 324 ticks = TIMER4VAL(); 325 } 326 } 327