1 /* $NetBSD: epclk.c,v 1.6 2005/06/04 22:37:51 he 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.6 2005/06/04 22:37:51 he 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/ep93xxvar.h> 67 #include <dev/clock_subr.h> 68 69 static int epclk_match(struct device *, struct cfdata *, void *); 70 static void epclk_attach(struct device *, struct device *, void *); 71 72 void rtcinit(void); 73 74 /* callback functions for intr_functions */ 75 static int epclk_intr(void* arg); 76 77 struct epclk_softc { 78 struct device sc_dev; 79 bus_addr_t sc_baseaddr; 80 bus_space_tag_t sc_iot; 81 bus_space_handle_t sc_ioh; 82 bus_space_handle_t sc_teoi_ioh; 83 int sc_intr; 84 }; 85 86 static struct epclk_softc *epclk_sc = NULL; 87 static u_int32_t tmark; 88 89 90 /* This is a quick ARM way to multiply by 983040/1000000 */ 91 #define US_TO_TIMER4VAL(x) { \ 92 u_int32_t hi, lo, scalar = 4222124650UL; \ 93 asm volatile ( \ 94 "umull %0, %1, %2, %3;" \ 95 : "=&r"(lo), "=&r"(hi) \ 96 : "r"((x)), "r"(scalar) \ 97 ); \ 98 (x) = hi; \ 99 } 100 101 /* This is a quick ARM way to multiply by 1000000/983040 */ 102 #define TIMER4VAL_TO_US(x) { \ 103 u_int32_t hi, lo, scalar = 2184533333UL; \ 104 asm volatile ( \ 105 "umull %0, %1, %2, %3;" \ 106 "mov %1, %1, lsl #1;" \ 107 "mov %0, %0, lsr #31;" \ 108 "orr %1, %1, %0;" \ 109 : "=&r"(lo), "=&r"(hi) \ 110 : "r"((x)), "r"(scalar) \ 111 ); \ 112 (x) = hi; \ 113 } 114 115 116 CFATTACH_DECL(epclk, sizeof(struct epclk_softc), 117 epclk_match, epclk_attach, NULL, NULL); 118 119 #define TIMER4VAL() (*(volatile u_int32_t *)(EP93XX_APB_VBASE + \ 120 EP93XX_APB_TIMERS + EP93XX_TIMERS_Timer4ValueLow)) 121 122 static int 123 epclk_match(struct device *parent, struct cfdata *match, void *aux) 124 { 125 126 return 2; 127 } 128 129 static void 130 epclk_attach(struct device *parent, struct device *self, void *aux) 131 { 132 struct epclk_softc *sc; 133 struct epsoc_attach_args *sa; 134 135 printf("\n"); 136 137 sc = (struct epclk_softc*) self; 138 sa = aux; 139 sc->sc_iot = sa->sa_iot; 140 sc->sc_baseaddr = sa->sa_addr; 141 sc->sc_intr = sa->sa_intr; 142 143 if (epclk_sc == NULL) 144 epclk_sc = sc; 145 146 if (bus_space_map(sa->sa_iot, sa->sa_addr, sa->sa_size, 147 0, &sc->sc_ioh)) 148 panic("%s: Cannot map registers", self->dv_xname); 149 if (bus_space_map(sa->sa_iot, EP93XX_APB_HWBASE + EP93XX_APB_SYSCON + 150 EP93XX_SYSCON_TEOI, 4, 0, &sc->sc_teoi_ioh)) 151 panic("%s: Cannot map registers", self->dv_xname); 152 153 /* clear and start the debug timer (Timer4) */ 154 bus_space_write_4(sc->sc_iot, sc->sc_ioh, EP93XX_TIMERS_Timer4Enable, 0); 155 bus_space_write_4(sc->sc_iot, sc->sc_ioh, EP93XX_TIMERS_Timer4Enable, 0x100); 156 } 157 158 /* 159 * epclk_intr: 160 * 161 * Handle the hardclock interrupt. 162 */ 163 static int 164 epclk_intr(void *arg) 165 { 166 struct epclk_softc* sc; 167 168 tmark = TIMER4VAL(); 169 sc = epclk_sc; 170 171 bus_space_write_4(sc->sc_iot, sc->sc_teoi_ioh, 0, 1); 172 hardclock((struct clockframe*) arg); 173 return (1); 174 } 175 176 /* 177 * setstatclockrate: 178 * 179 * Set the rate of the statistics clock. 180 * 181 * We assume that hz is either stathz or profhz, and that neither 182 * will change after being set by cpu_initclocks(). We could 183 * recalculate the intervals here, but that would be a pain. 184 */ 185 void 186 setstatclockrate(int newhz) 187 { 188 189 /* use hardclock */ 190 } 191 192 /* 193 * cpu_initclocks: 194 * 195 * Initialize the clock and get them going. 196 */ 197 void 198 cpu_initclocks(void) 199 { 200 struct epclk_softc* sc; 201 202 sc = epclk_sc; 203 stathz = profhz = 0; 204 205 if (hz != 64) panic("HZ must be 64!"); 206 207 /* clear 64Hz interrupt status */ 208 bus_space_write_4(sc->sc_iot, sc->sc_teoi_ioh, 0, 1); 209 210 ep93xx_intr_establish(sc->sc_intr, IPL_CLOCK, epclk_intr, NULL); 211 } 212 213 /* 214 * microtime: 215 * 216 * Fill in the specified timeval struct with the current time 217 * accurate to the microsecond. 218 */ 219 void 220 microtime(register struct timeval *tvp) 221 { 222 u_int oldirqstate; 223 u_int tmarknow, delta; 224 static struct timeval lasttv; 225 226 #ifdef DEBUG 227 if (epclk_sc == NULL) { 228 printf("microtime: called before initialize epclk\n"); 229 tvp->tv_sec = 0; 230 tvp->tv_usec = 0; 231 return; 232 } 233 #endif 234 235 oldirqstate = disable_interrupts(I32_bit); 236 tmarknow = TIMER4VAL(); 237 238 /* Fill in the timeval struct. */ 239 *tvp = time; 240 if (__predict_false(tmarknow < tmark)) { /* overflow */ 241 delta = tmarknow + (UINT_MAX - tmark); 242 } else { 243 delta = tmarknow - tmark; 244 } 245 246 TIMER4VAL_TO_US(delta); 247 248 tvp->tv_usec += delta; 249 250 /* Make sure microseconds doesn't overflow. */ 251 while (__predict_false(tvp->tv_usec >= 1000000)) { 252 tvp->tv_usec -= 1000000; 253 tvp->tv_sec++; 254 } 255 256 /* Make sure the time has advanced. */ 257 if (__predict_false(tvp->tv_sec == lasttv.tv_sec && 258 tvp->tv_usec <= lasttv.tv_usec)) { 259 tvp->tv_usec = lasttv.tv_usec + 1; 260 if (tvp->tv_usec >= 1000000) { 261 tvp->tv_usec -= 1000000; 262 tvp->tv_sec++; 263 } 264 } 265 266 lasttv = *tvp; 267 268 restore_interrupts(oldirqstate); 269 } 270 271 /* 272 * delay: 273 * 274 * Delay for at least N microseconds. 275 */ 276 void 277 delay(unsigned int len) 278 { 279 u_int32_t start, end, ticks; 280 281 #ifdef DEBUG 282 if (epclk_sc == NULL) { 283 printf("delay: called before start epclk\n"); 284 return; 285 } 286 #endif 287 288 ticks = start = TIMER4VAL(); 289 US_TO_TIMER4VAL(len); 290 end = start + len; 291 while (start <= ticks && ticks > end) { 292 /* wait for Timer4ValueLow wraparound */ 293 ticks = TIMER4VAL(); 294 } 295 while (ticks <= end) { 296 ticks = TIMER4VAL(); 297 } 298 } 299 300 todr_chip_handle_t todr_handle; 301 302 /* 303 * todr_attach: 304 * 305 * Set the specified time-of-day register as the system real-time clock. 306 */ 307 void 308 todr_attach(todr_chip_handle_t todr) 309 { 310 311 if (todr_handle) 312 panic("todr_attach: rtc already configured"); 313 todr_handle = todr; 314 } 315 316 /* 317 * inittodr: 318 * 319 * Initialize time from the time-of-day register. 320 */ 321 #define MINYEAR 2003 /* minimum plausible year */ 322 void 323 inittodr(time_t base) 324 { 325 time_t deltat; 326 int badbase; 327 328 if (base < (MINYEAR - 1970) * SECYR) { 329 printf("WARNING: preposterous time in file system\n"); 330 /* read the system clock anyway */ 331 base = (MINYEAR - 1970) * SECYR; 332 badbase = 1; 333 } else 334 badbase = 0; 335 336 if (todr_handle == NULL || 337 todr_gettime(todr_handle, &time) != 0 || 338 time.tv_sec == 0) { 339 /* 340 * Believe the time in the file system for lack of 341 * anything better, resetting the TODR. 342 */ 343 time.tv_sec = base; 344 time.tv_usec = 0; 345 if (todr_handle != NULL && !badbase) { 346 printf("WARNING: preposterous clock chip time\n"); 347 resettodr(); 348 } 349 goto bad; 350 } 351 352 if (!badbase) { 353 /* 354 * See if we gained/lost two or more days; if 355 * so, assume something is amiss. 356 */ 357 deltat = time.tv_sec - base; 358 if (deltat < 0) 359 deltat = -deltat; 360 if (deltat < 2 * SECDAY) 361 return; /* all is well */ 362 printf("WARNING: clock %s %ld days\n", 363 time.tv_sec < base ? "lost" : "gained", 364 (long)deltat / SECDAY); 365 } 366 bad: 367 printf("WARNING: CHECK AND RESET THE DATE!\n"); 368 } 369 370 /* 371 * resettodr: 372 * 373 * Reset the time-of-day register with the current time. 374 */ 375 void 376 resettodr(void) 377 { 378 379 if (time.tv_sec == 0) 380 return; 381 382 if (todr_handle != NULL && 383 todr_settime(todr_handle, &time) != 0) 384 printf("resettodr: failed to set time\n"); 385 } 386