i80321_timer.c revision 278613
1/* $NetBSD: i80321_timer.c,v 1.7 2003/07/27 04:52:28 thorpej Exp $ */ 2 3/*- 4 * Copyright (c) 2001, 2002 Wasabi Systems, Inc. 5 * All rights reserved. 6 * 7 * Written by Jason R. Thorpe for Wasabi Systems, Inc. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed for the NetBSD Project by 20 * Wasabi Systems, Inc. 21 * 4. The name of Wasabi Systems, Inc. may not be used to endorse 22 * or promote products derived from this software without specific prior 23 * written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 27 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 28 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC 29 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35 * POSSIBILITY OF SUCH DAMAGE. 36 */ 37 38/* 39 * Timer/clock support for the Intel i80321 I/O processor. 40 */ 41 42#include <sys/cdefs.h> 43__FBSDID("$FreeBSD: stable/10/sys/arm/xscale/i80321/i80321_timer.c 278613 2015-02-12 03:50:33Z ian $"); 44 45#include <sys/param.h> 46#include <sys/systm.h> 47#include <sys/kernel.h> 48#include <sys/module.h> 49#include <sys/time.h> 50#include <sys/bus.h> 51#include <sys/resource.h> 52#include <sys/rman.h> 53#include <sys/timetc.h> 54 55#include <machine/armreg.h> 56#include <machine/bus.h> 57#include <machine/cpu.h> 58#include <machine/cpufunc.h> 59#include <machine/frame.h> 60#include <machine/resource.h> 61#include <machine/intr.h> 62#include <arm/xscale/i80321/i80321reg.h> 63#include <arm/xscale/i80321/i80321var.h> 64 65#ifdef CPU_XSCALE_81342 66#define ICU_INT_TIMER0 (8) /* XXX: Can't include i81342reg.h because 67 definitions overrides the ones from i80321reg.h 68 */ 69#endif 70#include "opt_timer.h" 71 72void (*i80321_hardclock_hook)(void) = NULL; 73struct i80321_timer_softc { 74 device_t dev; 75} timer_softc; 76 77 78static unsigned i80321_timer_get_timecount(struct timecounter *tc); 79 80 81static uint32_t counts_per_hz; 82 83#if defined(XSCALE_DISABLE_CCNT) || defined(CPU_XSCALE_81342) 84static uint32_t offset; 85static uint32_t last = -1; 86#endif 87 88static int ticked = 0; 89 90#ifndef COUNTS_PER_SEC 91#define COUNTS_PER_SEC 200000000 /* 200MHz */ 92#endif 93 94#define COUNTS_PER_USEC (COUNTS_PER_SEC / 1000000) 95 96static struct timecounter i80321_timer_timecounter = { 97 i80321_timer_get_timecount, /* get_timecount */ 98 NULL, /* no poll_pps */ 99 ~0u, /* counter_mask */ 100#if defined(XSCALE_DISABLE_CCNT) || defined(CPU_XSCALE_81342) 101 COUNTS_PER_SEC, 102#else 103 COUNTS_PER_SEC * 3, /* frequency */ 104#endif 105 "i80321 timer", /* name */ 106 1000 /* quality */ 107}; 108 109static int 110i80321_timer_probe(device_t dev) 111{ 112 113 device_set_desc(dev, "i80321 timer"); 114 return (0); 115} 116 117static int 118i80321_timer_attach(device_t dev) 119{ 120 timer_softc.dev = dev; 121 122 return (0); 123} 124 125static device_method_t i80321_timer_methods[] = { 126 DEVMETHOD(device_probe, i80321_timer_probe), 127 DEVMETHOD(device_attach, i80321_timer_attach), 128 {0, 0}, 129}; 130 131static driver_t i80321_timer_driver = { 132 "itimer", 133 i80321_timer_methods, 134 sizeof(struct i80321_timer_softc), 135}; 136static devclass_t i80321_timer_devclass; 137 138DRIVER_MODULE(itimer, iq, i80321_timer_driver, i80321_timer_devclass, 0, 0); 139 140int clockhandler(void *); 141 142 143static __inline uint32_t 144tmr1_read(void) 145{ 146 uint32_t rv; 147 148#ifdef CPU_XSCALE_81342 149 __asm __volatile("mrc p6, 0, %0, c1, c9, 0" 150#else 151 __asm __volatile("mrc p6, 0, %0, c1, c1, 0" 152#endif 153 : "=r" (rv)); 154 return (rv); 155} 156 157static __inline void 158tmr1_write(uint32_t val) 159{ 160 161 162#ifdef CPU_XSCALE_81342 163 __asm __volatile("mcr p6, 0, %0, c1, c9, 0" 164#else 165 __asm __volatile("mcr p6, 0, %0, c1, c1, 0" 166#endif 167 : 168 : "r" (val)); 169} 170 171static __inline uint32_t 172tcr1_read(void) 173{ 174 uint32_t rv; 175 176#ifdef CPU_XSCALE_81342 177 __asm __volatile("mrc p6, 0, %0, c3, c9, 0" 178#else 179 __asm __volatile("mrc p6, 0, %0, c3, c1, 0" 180#endif 181 : "=r" (rv)); 182 return (rv); 183} 184static __inline void 185tcr1_write(uint32_t val) 186{ 187 188#ifdef CPU_XSCALE_81342 189 __asm __volatile("mcr p6, 0, %0, c3, c9, 0" 190#else 191 __asm __volatile("mcr p6, 0, %0, c3, c1, 0" 192#endif 193 : 194 : "r" (val)); 195} 196 197static __inline void 198trr1_write(uint32_t val) 199{ 200 201#ifdef CPU_XSCALE_81342 202 __asm __volatile("mcr p6, 0, %0, c5, c9, 0" 203#else 204 __asm __volatile("mcr p6, 0, %0, c5, c1, 0" 205#endif 206 : 207 : "r" (val)); 208} 209 210static __inline uint32_t 211tmr0_read(void) 212{ 213 uint32_t rv; 214 215#ifdef CPU_XSCALE_81342 216 __asm __volatile("mrc p6, 0, %0, c0, c9, 0" 217#else 218 __asm __volatile("mrc p6, 0, %0, c0, c1, 0" 219#endif 220 : "=r" (rv)); 221 return (rv); 222} 223 224static __inline void 225tmr0_write(uint32_t val) 226{ 227 228#ifdef CPU_XSCALE_81342 229 __asm __volatile("mcr p6, 0, %0, c0, c9, 0" 230#else 231 __asm __volatile("mcr p6, 0, %0, c0, c1, 0" 232#endif 233 : 234 : "r" (val)); 235} 236 237static __inline uint32_t 238tcr0_read(void) 239{ 240 uint32_t rv; 241 242#ifdef CPU_XSCALE_81342 243 __asm __volatile("mrc p6, 0, %0, c2, c9, 0" 244#else 245 __asm __volatile("mrc p6, 0, %0, c2, c1, 0" 246#endif 247 : "=r" (rv)); 248 return (rv); 249} 250static __inline void 251tcr0_write(uint32_t val) 252{ 253 254#ifdef CPU_XSCALE_81342 255 __asm __volatile("mcr p6, 0, %0, c2, c9, 0" 256#else 257 __asm __volatile("mcr p6, 0, %0, c2, c1, 0" 258#endif 259 : 260 : "r" (val)); 261} 262 263static __inline void 264trr0_write(uint32_t val) 265{ 266 267#ifdef CPU_XSCALE_81342 268 __asm __volatile("mcr p6, 0, %0, c4, c9, 0" 269#else 270 __asm __volatile("mcr p6, 0, %0, c4, c1, 0" 271#endif 272 : 273 : "r" (val)); 274} 275 276static __inline void 277tisr_write(uint32_t val) 278{ 279 280#ifdef CPU_XSCALE_81342 281 __asm __volatile("mcr p6, 0, %0, c6, c9, 0" 282#else 283 __asm __volatile("mcr p6, 0, %0, c6, c1, 0" 284#endif 285 : 286 : "r" (val)); 287} 288 289static __inline uint32_t 290tisr_read(void) 291{ 292 int ret; 293 294#ifdef CPU_XSCALE_81342 295 __asm __volatile("mrc p6, 0, %0, c6, c9, 0" : "=r" (ret)); 296#else 297 __asm __volatile("mrc p6, 0, %0, c6, c1, 0" : "=r" (ret)); 298#endif 299 return (ret); 300} 301 302static unsigned 303i80321_timer_get_timecount(struct timecounter *tc) 304{ 305#if defined(XSCALE_DISABLE_CCNT) || defined(CPU_XSCALE_81342) 306 uint32_t cur = tcr0_read(); 307 308 if (cur > last && last != -1) { 309 offset += counts_per_hz; 310 if (ticked > 0) 311 ticked--; 312 } 313 if (ticked) { 314 offset += ticked * counts_per_hz; 315 ticked = 0; 316 } 317 return (counts_per_hz - cur + offset); 318#else 319 uint32_t ret; 320 321 __asm __volatile("mrc p14, 0, %0, c1, c0, 0\n" 322 : "=r" (ret)); 323 return (ret); 324#endif 325} 326 327/* 328 * i80321_calibrate_delay: 329 * 330 * Calibrate the delay loop. 331 */ 332void 333i80321_calibrate_delay(void) 334{ 335 336 /* 337 * Just use hz=100 for now -- we'll adjust it, if necessary, 338 * in cpu_initclocks(). 339 */ 340 counts_per_hz = COUNTS_PER_SEC / 100; 341 342 tmr0_write(0); /* stop timer */ 343 tisr_write(TISR_TMR0); /* clear interrupt */ 344 trr0_write(counts_per_hz); /* reload value */ 345 tcr0_write(counts_per_hz); /* current value */ 346 347 tmr0_write(TMRx_ENABLE|TMRx_RELOAD|TMRx_CSEL_CORE); 348} 349 350/* 351 * cpu_initclocks: 352 * 353 * Initialize the clock and get them going. 354 */ 355void 356cpu_initclocks(void) 357{ 358 u_int oldirqstate; 359 struct resource *irq; 360 int rid = 0; 361 void *ihl; 362 device_t dev = timer_softc.dev; 363 364 if (hz < 50 || COUNTS_PER_SEC % hz) { 365 printf("Cannot get %d Hz clock; using 100 Hz\n", hz); 366 hz = 100; 367 } 368 tick = 1000000 / hz; /* number of microseconds between interrupts */ 369 370 /* 371 * We only have one timer available; stathz and profhz are 372 * always left as 0 (the upper-layer clock code deals with 373 * this situation). 374 */ 375 if (stathz != 0) 376 printf("Cannot get %d Hz statclock\n", stathz); 377 stathz = 0; 378 379 if (profhz != 0) 380 printf("Cannot get %d Hz profclock\n", profhz); 381 profhz = 0; 382 383 /* Report the clock frequency. */ 384 385 oldirqstate = disable_interrupts(PSR_I); 386 387 irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 388#ifdef CPU_XSCALE_81342 389 ICU_INT_TIMER0, ICU_INT_TIMER0, 390#else 391 ICU_INT_TMR0, ICU_INT_TMR0, 392#endif 393 1, RF_ACTIVE); 394 if (!irq) 395 panic("Unable to setup the clock irq handler.\n"); 396 else 397 bus_setup_intr(dev, irq, INTR_TYPE_CLK, clockhandler, NULL, 398 NULL, &ihl); 399 tmr0_write(0); /* stop timer */ 400 tisr_write(TISR_TMR0); /* clear interrupt */ 401 402 counts_per_hz = COUNTS_PER_SEC / hz; 403 404 trr0_write(counts_per_hz); /* reload value */ 405 tcr0_write(counts_per_hz); /* current value */ 406 tmr0_write(TMRx_ENABLE|TMRx_RELOAD|TMRx_CSEL_CORE); 407 408 tc_init(&i80321_timer_timecounter); 409 restore_interrupts(oldirqstate); 410 rid = 0; 411#if !defined(XSCALE_DISABLE_CCNT) && !defined(CPU_XSCALE_81342) 412 /* Enable the clock count register. */ 413 __asm __volatile("mrc p14, 0, %0, c0, c0, 0\n" : "=r" (rid)); 414 rid &= ~(1 << 3); 415 rid |= (1 << 2) | 1; 416 __asm __volatile("mcr p14, 0, %0, c0, c0, 0\n" 417 : : "r" (rid)); 418#endif 419} 420 421 422/* 423 * DELAY: 424 * 425 * Delay for at least N microseconds. 426 */ 427void 428DELAY(int n) 429{ 430 uint32_t cur, last, delta, usecs; 431 432 /* 433 * This works by polling the timer and counting the 434 * number of microseconds that go by. 435 */ 436 last = tcr0_read(); 437 delta = usecs = 0; 438 439 while (n > usecs) { 440 cur = tcr0_read(); 441 442 /* Check to see if the timer has wrapped around. */ 443 if (last < cur) 444 delta += (last + (counts_per_hz - cur)); 445 else 446 delta += (last - cur); 447 448 last = cur; 449 450 if (delta >= COUNTS_PER_USEC) { 451 usecs += delta / COUNTS_PER_USEC; 452 delta %= COUNTS_PER_USEC; 453 } 454 } 455} 456 457/* 458 * clockhandler: 459 * 460 * Handle the hardclock interrupt. 461 */ 462int 463clockhandler(void *arg) 464{ 465 struct trapframe *frame = arg; 466 467 ticked++; 468 tisr_write(TISR_TMR0); 469 hardclock(TRAPF_USERMODE(frame), TRAPF_PC(frame)); 470 471 if (i80321_hardclock_hook != NULL) 472 (*i80321_hardclock_hook)(); 473 return (FILTER_HANDLED); 474} 475 476void 477cpu_startprofclock(void) 478{ 479} 480 481void 482cpu_stopprofclock(void) 483{ 484 485} 486