Home | History | Annotate | Line # | Download | only in iq80310
iq80310_timer.c revision 1.12
      1 /*	$NetBSD: iq80310_timer.c,v 1.12 2003/07/15 00:25:03 lukem 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 IQ80310.
     40  *
     41  * The IQ80310 has a 22-bit reloadable timer implemented in the CPLD.
     42  * We use it to provide a hardclock interrupt.  There is no RTC on
     43  * the IQ80310.
     44  *
     45  * The timer uses the SPCI clock.  The timer uses the 33MHz clock by
     46  * reading the SPCI_66EN signal and dividing the clock if necessary.
     47  */
     48 
     49 #include <sys/cdefs.h>
     50 __KERNEL_RCSID(0, "$NetBSD: iq80310_timer.c,v 1.12 2003/07/15 00:25:03 lukem Exp $");
     51 
     52 #include <sys/param.h>
     53 #include <sys/systm.h>
     54 #include <sys/kernel.h>
     55 #include <sys/time.h>
     56 
     57 #include <machine/bus.h>
     58 #include <arm/cpufunc.h>
     59 
     60 #include <evbarm/iq80310/iq80310reg.h>
     61 #include <evbarm/iq80310/iq80310var.h>
     62 #include <evbarm/iq80310/obiovar.h>
     63 
     64 /*
     65  * Some IQ80310-based designs have fewer bits in the timer counter.
     66  * Deal with them here.
     67  */
     68 #if defined(IOP310_TEAMASA_NPWR)
     69 #define	COUNTER_MASK		0x0007ffff
     70 #else /* Default to stock IQ80310 */
     71 #define	COUNTER_MASK		0x003fffff
     72 #endif /* list of IQ80310-based designs */
     73 
     74 #define	COUNTS_PER_SEC		33000000	/* 33MHz */
     75 #define	COUNTS_PER_USEC		(COUNTS_PER_SEC / 1000000)
     76 
     77 static void *clock_ih;
     78 
     79 static uint32_t counts_per_hz;
     80 
     81 int	clockhandler(void *);
     82 
     83 static __inline void
     84 timer_enable(uint8_t bit)
     85 {
     86 
     87 	CPLD_WRITE(IQ80310_TIMER_ENABLE,
     88 	    CPLD_READ(IQ80310_TIMER_ENABLE) | bit);
     89 }
     90 
     91 static __inline void
     92 timer_disable(uint8_t bit)
     93 {
     94 
     95 	CPLD_WRITE(IQ80310_TIMER_ENABLE,
     96 	    CPLD_READ(IQ80310_TIMER_ENABLE) & ~bit);
     97 }
     98 
     99 static __inline uint32_t
    100 timer_read(void)
    101 {
    102 	uint32_t rv;
    103 	uint8_t la0, la1, la2, la3;
    104 
    105 	/*
    106 	 * First read latches count.
    107 	 *
    108 	 * From RedBoot: harware bug that causes invalid counts to be
    109 	 * latched.  The loop appears to work around the problem.
    110 	 */
    111 	do {
    112 		la0 = CPLD_READ(IQ80310_TIMER_LA0);
    113 	} while (la0 == 0);
    114 	la1 = CPLD_READ(IQ80310_TIMER_LA1);
    115 	la2 = CPLD_READ(IQ80310_TIMER_LA2);
    116 	la3 = CPLD_READ(IQ80310_TIMER_LA3);
    117 
    118 	rv  =  ((la0 & 0x40) >> 1) | (la0 & 0x1f);
    119 	rv |= (((la1 & 0x40) >> 1) | (la1 & 0x1f)) << 6;
    120 	rv |= (((la2 & 0x40) >> 1) | (la2 & 0x1f)) << 12;
    121 	rv |= (la3 & 0x0f) << 18;
    122 
    123 	return (rv);
    124 }
    125 
    126 static __inline void
    127 timer_write(uint32_t x)
    128 {
    129 
    130 	KASSERT((x & COUNTER_MASK) == x);
    131 
    132 	CPLD_WRITE(IQ80310_TIMER_LA0, x & 0xff);
    133 	CPLD_WRITE(IQ80310_TIMER_LA1, (x >> 8) & 0xff);
    134 	CPLD_WRITE(IQ80310_TIMER_LA2, (x >> 16) & 0x3f);
    135 }
    136 
    137 /*
    138  * iq80310_calibrate_delay:
    139  *
    140  *	Calibrate the delay loop.
    141  */
    142 void
    143 iq80310_calibrate_delay(void)
    144 {
    145 
    146 	/*
    147 	 * We'll use the CPLD timer for delay(), as well.  We go
    148 	 * ahead and start it up now, just don't enable interrupts
    149 	 * until cpu_initclocks().
    150 	 *
    151 	 * Just use hz=100 for now -- we'll adjust it, if necessary,
    152 	 * in cpu_initclocks().
    153 	 */
    154 	counts_per_hz = COUNTS_PER_SEC / 100;
    155 
    156 	timer_disable(TIMER_ENABLE_INTEN);
    157 	timer_disable(TIMER_ENABLE_EN);
    158 
    159 	timer_write(counts_per_hz);
    160 
    161 	timer_enable(TIMER_ENABLE_EN);
    162 }
    163 
    164 /*
    165  * cpu_initclocks:
    166  *
    167  *	Initialize the clock and get them going.
    168  */
    169 void
    170 cpu_initclocks(void)
    171 {
    172 	u_int oldirqstate;
    173 
    174 	if (hz < 50 || COUNTS_PER_SEC % hz) {
    175 		printf("Cannot get %d Hz clock; using 100 Hz\n", hz);
    176 		hz = 100;
    177 	}
    178 	tick = 1000000 / hz;	/* number of microseconds between interrupts */
    179 	tickfix = 1000000 - (hz * tick);
    180 	if (tickfix) {
    181 		int ftp;
    182 
    183 		ftp = min(ffs(tickfix), ffs(hz));
    184 		tickfix >>= (ftp - 1);
    185 		tickfixinterval = hz >> (ftp - 1);
    186 	}
    187 
    188 	/*
    189 	 * We only have one timer available; stathz and profhz are
    190 	 * always left as 0 (the upper-layer clock code deals with
    191 	 * this situation).
    192 	 */
    193 	if (stathz != 0)
    194 		printf("Cannot get %d Hz statclock\n", stathz);
    195 	stathz = 0;
    196 
    197 	if (profhz != 0)
    198 		printf("Cannot get %d Hz profclock\n", profhz);
    199 	profhz = 0;
    200 
    201 	/* Report the clock frequency. */
    202 	printf("clock: hz=%d stathz=%d profhz=%d\n", hz, stathz, profhz);
    203 
    204 	/* Hook up the clock interrupt handler. */
    205 	clock_ih = iq80310_intr_establish(XINT3_IRQ(XINT3_TIMER), IPL_CLOCK,
    206 	    clockhandler, NULL);
    207 	if (clock_ih == NULL)
    208 		panic("cpu_initclocks: unable to register timer interrupt");
    209 
    210 	/* Set up the new clock parameters. */
    211 	oldirqstate = disable_interrupts(I32_bit);
    212 
    213 	timer_disable(TIMER_ENABLE_EN);
    214 
    215 	counts_per_hz = COUNTS_PER_SEC / hz;
    216 	timer_write(counts_per_hz);
    217 
    218 	timer_enable(TIMER_ENABLE_INTEN);
    219 	timer_enable(TIMER_ENABLE_EN);
    220 
    221 	restore_interrupts(oldirqstate);
    222 }
    223 
    224 /*
    225  * setstatclockrate:
    226  *
    227  *	Set the rate of the statistics clock.
    228  *
    229  *	We assume that hz is either stathz or profhz, and that neither
    230  *	will change after being set by cpu_initclocks().  We could
    231  *	recalculate the intervals here, but that would be a pain.
    232  */
    233 void
    234 setstatclockrate(int hz)
    235 {
    236 
    237 	/*
    238 	 * Nothing to do, here; we can't change the statclock
    239 	 * rate on the IQ80310.
    240 	 */
    241 }
    242 
    243 /*
    244  * microtime:
    245  *
    246  *	Fill in the specified timeval struct with the current time
    247  *	accurate to the microsecond.
    248  */
    249 void
    250 microtime(struct timeval *tvp)
    251 {
    252 	static struct timeval lasttv;
    253 	u_int oldirqstate;
    254 	uint32_t counts;
    255 
    256 	oldirqstate = disable_interrupts(I32_bit);
    257 
    258 	counts = timer_read();
    259 
    260 	/* Fill in the timeval struct. */
    261 	*tvp = time;
    262 	tvp->tv_usec += (counts / COUNTS_PER_USEC);
    263 
    264 	/* Make sure microseconds doesn't overflow. */
    265 	while (tvp->tv_usec >= 1000000) {
    266 		tvp->tv_usec -= 1000000;
    267 		tvp->tv_sec++;
    268 	}
    269 
    270 	/* Make sure the time has advanced. */
    271 	if (tvp->tv_sec == lasttv.tv_sec &&
    272 	    tvp->tv_usec <= lasttv.tv_usec) {
    273 		tvp->tv_usec = lasttv.tv_usec + 1;
    274 		if (tvp->tv_usec >= 1000000) {
    275 			tvp->tv_usec -= 1000000;
    276 			tvp->tv_sec++;
    277 		}
    278 	}
    279 
    280 	lasttv = *tvp;
    281 
    282 	restore_interrupts(oldirqstate);
    283 }
    284 
    285 /*
    286  * delay:
    287  *
    288  *	Delay for at least N microseconds.
    289  */
    290 void
    291 delay(u_int n)
    292 {
    293 	uint32_t cur, last, delta, usecs;
    294 
    295 	/*
    296 	 * This works by polling the timer and counting the
    297 	 * number of microseconds that go by.
    298 	 */
    299 	last = timer_read();
    300 	delta = usecs = 0;
    301 
    302 	while (n > usecs) {
    303 		cur = timer_read();
    304 
    305 		/* Check to see if the timer has wrapped around. */
    306 		if (cur < last)
    307 			delta += ((counts_per_hz - last) + cur);
    308 		else
    309 			delta += (cur - last);
    310 
    311 		last = cur;
    312 
    313 		if (delta >= COUNTS_PER_USEC) {
    314 			usecs += delta / COUNTS_PER_USEC;
    315 			delta %= COUNTS_PER_USEC;
    316 		}
    317 	}
    318 }
    319 
    320 /*
    321  * inittodr:
    322  *
    323  *	Initialize time from the time-of-day register.
    324  */
    325 void
    326 inittodr(time_t base)
    327 {
    328 
    329 	time.tv_sec = base;
    330 	time.tv_usec = 0;
    331 }
    332 
    333 /*
    334  * resettodr:
    335  *
    336  *	Reset the time-of-day register with the current time.
    337  */
    338 void
    339 resettodr(void)
    340 {
    341 }
    342 
    343 /*
    344  * clockhandler:
    345  *
    346  *	Handle the hardclock interrupt.
    347  */
    348 int
    349 clockhandler(void *arg)
    350 {
    351 	struct clockframe *frame = arg;
    352 
    353 	timer_disable(TIMER_ENABLE_INTEN);
    354 	timer_enable(TIMER_ENABLE_INTEN);
    355 
    356 	hardclock(frame);
    357 
    358 	/*
    359 	 * Don't run the snake on IOP310-based systems that
    360 	 * don't have the 7-segment display.
    361 	 */
    362 #if !defined(IOP310_TEAMASA_NPWR)
    363 	{
    364 		static int snakefreq;
    365 
    366 		if ((snakefreq++ & 15) == 0)
    367 			iq80310_7seg_snake();
    368 	}
    369 #endif
    370 
    371 	return (1);
    372 }
    373