Home | History | Annotate | Line # | Download | only in xscale
becc_timer.c revision 1.3
      1 /*	$NetBSD: becc_timer.c,v 1.3 2003/07/15 00:24:52 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 ADI Engineering Big Endian Companion Chip.
     40  */
     41 
     42 #include <sys/cdefs.h>
     43 __KERNEL_RCSID(0, "$NetBSD: becc_timer.c,v 1.3 2003/07/15 00:24:52 lukem Exp $");
     44 
     45 #include <sys/param.h>
     46 #include <sys/systm.h>
     47 #include <sys/kernel.h>
     48 #include <sys/time.h>
     49 
     50 #include <machine/bus.h>
     51 #include <arm/cpufunc.h>
     52 
     53 #include <arm/xscale/beccreg.h>
     54 #include <arm/xscale/beccvar.h>
     55 
     56 void	(*becc_hardclock_hook)(void);
     57 
     58 /*
     59  * Note, since COUNTS_PER_USEC doesn't divide evenly, we round up.
     60  */
     61 #define	COUNTS_PER_SEC		BECC_PERIPH_CLOCK
     62 #define	COUNTS_PER_USEC		((COUNTS_PER_SEC / 1000000) + 1)
     63 
     64 static void *clock_ih;
     65 
     66 /*
     67  * Since the timer interrupts when the counter underflows, we need to
     68  * subtract 1 from counts_per_hz when loading the preload register.
     69  */
     70 static uint32_t counts_per_hz;
     71 
     72 int	clockhandler(void *);
     73 
     74 /*
     75  * becc_calibrate_delay:
     76  *
     77  *	Calibrate the delay loop.
     78  */
     79 void
     80 becc_calibrate_delay(void)
     81 {
     82 
     83 	/*
     84 	 * Just use hz=100 for now -- we'll adjust it, if necessary,
     85 	 * in cpu_initclocks().
     86 	 */
     87 	counts_per_hz = COUNTS_PER_SEC / 100;
     88 
     89 	/* Stop both timers, clear interrupts. */
     90 	BECC_CSR_WRITE(BECC_TSCRA, TSCRx_TIF);
     91 	BECC_CSR_WRITE(BECC_TSCRB, TSCRx_TIF);
     92 
     93 	/* Set the timer preload value. */
     94 	BECC_CSR_WRITE(BECC_TPRA, counts_per_hz - 1);
     95 
     96 	/* Start the timer. */
     97 	BECC_CSR_WRITE(BECC_TSCRA, TSCRx_TE | TSCRx_CM);
     98 }
     99 
    100 /*
    101  * cpu_initclocks:
    102  *
    103  *	Initialize the clock and get them going.
    104  */
    105 void
    106 cpu_initclocks(void)
    107 {
    108 	u_int oldirqstate;
    109 
    110 #if 0
    111 	if (hz < 50 || COUNTS_PER_SEC % hz) {
    112 		printf("Cannot get %d Hz clock; using 100 Hz\n", hz);
    113 		hz = 100;
    114 	}
    115 #endif
    116 	tick = 1000000 / hz;	/* number of microseconds between interrupts */
    117 	tickfix = 1000000 - (hz * tick);
    118 	if (tickfix) {
    119 		int ftp;
    120 
    121 		ftp = min(ffs(tickfix), ffs(hz));
    122 		tickfix >>= (ftp - 1);
    123 		tickfixinterval = hz >> (ftp - 1);
    124 	}
    125 
    126 	/*
    127 	 * We only have one timer available; stathz and profhz are
    128 	 * always left as 0 (the upper-layer clock code deals with
    129 	 * this situation).
    130 	 */
    131 	if (stathz != 0)
    132 		printf("Cannot get %d Hz statclock\n", stathz);
    133 	stathz = 0;
    134 
    135 	if (profhz != 0)
    136 		printf("Cannot get %d Hz profclock\n", profhz);
    137 	profhz = 0;
    138 
    139 	/* Report the clock frequency. */
    140 	aprint_normal("clock: hz=%d stathz=%d profhz=%d\n", hz, stathz, profhz);
    141 
    142 	oldirqstate = disable_interrupts(I32_bit);
    143 
    144 	/* Hook up the clock interrupt handler. */
    145 	clock_ih = becc_intr_establish(ICU_TIMERA, IPL_CLOCK,
    146 	    clockhandler, NULL);
    147 	if (clock_ih == NULL)
    148 		panic("cpu_initclocks: unable to register timer interrupt");
    149 
    150 	/* Set up the new clock parameters. */
    151 
    152 	/* Stop timer, clear interrupt */
    153 	BECC_CSR_WRITE(BECC_TSCRA, TSCRx_TIF);
    154 
    155 	counts_per_hz = COUNTS_PER_SEC / hz;
    156 
    157 	/* Set the timer preload value. */
    158 	BECC_CSR_WRITE(BECC_TPRA, counts_per_hz - 1);
    159 
    160 	/* ...and start it in motion. */
    161 	BECC_CSR_WRITE(BECC_TSCRA, TSCRx_TE | TSCRx_CM);
    162 
    163 	/* register soft interrupt handler as well */
    164 	becc_intr_establish(ICU_SOFT, IPL_SOFT, becc_softint, NULL);
    165 
    166 	restore_interrupts(oldirqstate);
    167 }
    168 
    169 /*
    170  * setstatclockrate:
    171  *
    172  *	Set the rate of the statistics clock.
    173  *
    174  *	We assume that hz is either stathz or profhz, and that neither
    175  *	will change after being set by cpu_initclocks().  We could
    176  *	recalculate the intervals here, but that would be a pain.
    177  */
    178 void
    179 setstatclockrate(int hz)
    180 {
    181 
    182 	/*
    183 	 * XXX Use TMR1?
    184 	 */
    185 }
    186 
    187 /*
    188  * microtime:
    189  *
    190  *	Fill in the specified timeval struct with the current time
    191  *	accurate to the microsecond.
    192  */
    193 void
    194 microtime(struct timeval *tvp)
    195 {
    196 	static struct timeval lasttv;
    197 	u_int oldirqstate;
    198 	uint32_t counts;
    199 
    200 	oldirqstate = disable_interrupts(I32_bit);
    201 
    202 	/*
    203 	 * XXX How do we compensate for the -1 behavior of the preload value?
    204 	 */
    205 	counts = counts_per_hz - BECC_CSR_READ(BECC_TCVRA);
    206 
    207 	/* Fill in the timeval struct. */
    208 	*tvp = time;
    209 	tvp->tv_usec += (counts / COUNTS_PER_USEC);
    210 
    211 	/* Make sure microseconds doesn't overflow. */
    212 	while (tvp->tv_usec >= 1000000) {
    213 		tvp->tv_usec -= 1000000;
    214 		tvp->tv_sec++;
    215 	}
    216 
    217 	/* Make sure the time has advanced. */
    218 	if (tvp->tv_sec == lasttv.tv_sec &&
    219 	    tvp->tv_usec <= lasttv.tv_usec) {
    220 		tvp->tv_usec = lasttv.tv_usec + 1;
    221 		if (tvp->tv_usec >= 1000000) {
    222 			tvp->tv_usec -= 1000000;
    223 			tvp->tv_sec++;
    224 		}
    225 	}
    226 
    227 	lasttv = *tvp;
    228 
    229 	restore_interrupts(oldirqstate);
    230 }
    231 
    232 /*
    233  * delay:
    234  *
    235  *	Delay for at least N microseconds.
    236  */
    237 void
    238 delay(u_int n)
    239 {
    240 	uint32_t cur, last, delta, usecs;
    241 
    242 	/*
    243 	 * This works by polling the timer and counting the
    244 	 * number of microseconds that go by.
    245 	 */
    246 	last = BECC_CSR_READ(BECC_TCVRA);
    247 	delta = usecs = 0;
    248 
    249 	while (n > usecs) {
    250 		cur = BECC_CSR_READ(BECC_TCVRA);
    251 
    252 		/* Check to see if the timer has wrapped around. */
    253 		if (last < cur)
    254 			delta += (last + (counts_per_hz - cur));
    255 		else
    256 			delta += (last - cur);
    257 
    258 		last = cur;
    259 
    260 		if (delta >= COUNTS_PER_USEC) {
    261 			usecs += delta / COUNTS_PER_USEC;
    262 			delta %= COUNTS_PER_USEC;
    263 		}
    264 	}
    265 }
    266 
    267 /*
    268  * inittodr:
    269  *
    270  *	Initialize time from the time-of-day register.
    271  */
    272 void
    273 inittodr(time_t base)
    274 {
    275 
    276 	time.tv_sec = base;
    277 	time.tv_usec = 0;
    278 }
    279 
    280 /*
    281  * resettodr:
    282  *
    283  *	Reset the time-of-day register with the current time.
    284  */
    285 void
    286 resettodr(void)
    287 {
    288 }
    289 
    290 /*
    291  * clockhandler:
    292  *
    293  *	Handle the hardclock interrupt.
    294  */
    295 int
    296 clockhandler(void *arg)
    297 {
    298 	struct clockframe *frame = arg;
    299 
    300 	/* ACK the interrupt. */
    301 	BECC_CSR_WRITE(BECC_TSCRA, TSCRx_TE | TSCRx_CM | TSCRx_TIF);
    302 
    303 	hardclock(frame);
    304 
    305 	if (becc_hardclock_hook != NULL)
    306 		(*becc_hardclock_hook)();
    307 
    308 	return (1);
    309 }
    310