Home | History | Annotate | Line # | Download | only in footbridge
footbridge_clock.c revision 1.22
      1 /*	$NetBSD: footbridge_clock.c,v 1.22 2006/09/11 06:02:30 gdamore Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1997 Mark Brinicombe.
      5  * Copyright (c) 1997 Causality Limited.
      6  * All rights reserved.
      7  *
      8  * Redistribution and use in source and binary forms, with or without
      9  * modification, are permitted provided that the following conditions
     10  * are met:
     11  * 1. Redistributions of source code must retain the above copyright
     12  *    notice, this list of conditions and the following disclaimer.
     13  * 2. Redistributions in binary form must reproduce the above copyright
     14  *    notice, this list of conditions and the following disclaimer in the
     15  *    documentation and/or other materials provided with the distribution.
     16  * 3. All advertising materials mentioning features or use of this software
     17  *    must display the following acknowledgement:
     18  *	This product includes software developed by Mark Brinicombe
     19  *	for the NetBSD Project.
     20  * 4. The name of the company nor the name of the author may be used to
     21  *    endorse or promote products derived from this software without specific
     22  *    prior written permission.
     23  *
     24  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     25  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     26  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     27  * IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
     28  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     29  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
     30  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     34  * SUCH DAMAGE.
     35  */
     36 
     37 #include <sys/cdefs.h>
     38 __KERNEL_RCSID(0, "$NetBSD: footbridge_clock.c,v 1.22 2006/09/11 06:02:30 gdamore Exp $");
     39 
     40 /* Include header files */
     41 
     42 #include <sys/types.h>
     43 #include <sys/param.h>
     44 #include <sys/systm.h>
     45 #include <sys/kernel.h>
     46 #include <sys/time.h>
     47 #include <sys/device.h>
     48 
     49 #include <machine/intr.h>
     50 
     51 #include <arm/cpufunc.h>
     52 
     53 #include <arm/footbridge/dc21285reg.h>
     54 #include <arm/footbridge/footbridgevar.h>
     55 #include <arm/footbridge/footbridge.h>
     56 
     57 extern struct footbridge_softc *clock_sc;
     58 extern u_int dc21285_fclk;
     59 
     60 int clockhandler(void *);
     61 int statclockhandler(void *);
     62 static int load_timer(int, int);
     63 
     64 /*
     65  * Statistics clock variance, in usec.  Variance must be a
     66  * power of two.  Since this gives us an even number, not an odd number,
     67  * we discard one case and compensate.  That is, a variance of 1024 would
     68  * give us offsets in [0..1023].  Instead, we take offsets in [1..1023].
     69  * This is symmetric about the point 512, or statvar/2, and thus averages
     70  * to that value (assuming uniform random numbers).
     71  */
     72 const int statvar = 1024;
     73 int statmin;			/* minimum stat clock count in ticks */
     74 int statcountperusec;		/* number of ticks per usec at current stathz */
     75 int statprev;			/* last value of we set statclock to */
     76 
     77 #if 0
     78 static int clockmatch(struct device *parent, struct cfdata *cf, void *aux);
     79 static void clockattach(struct device *parent, struct device *self, void *aux);
     80 
     81 CFATTACH_DECL(footbridge_clock, sizeof(struct clock_softc),
     82     clockmatch, clockattach, NULL, NULL);
     83 
     84 /*
     85  * int clockmatch(struct device *parent, void *match, void *aux)
     86  *
     87  * Just return ok for this if it is device 0
     88  */
     89 
     90 static int
     91 clockmatch(struct device *parent, struct cfdata *cf, void *aux)
     92 {
     93 	union footbridge_attach_args *fba = aux;
     94 
     95 	if (strcmp(fba->fba_ca.ca_name, "clk") == 0)
     96 		return 1;
     97 	return 0;
     98 }
     99 
    100 
    101 /*
    102  * void clockattach(struct device *parent, struct device *dev, void *aux)
    103  *
    104  */
    105 
    106 static void
    107 clockattach(struct device *parent, struct device *self, void *aux)
    108 {
    109 	struct clock_softc *sc = (struct clock_softc *)self;
    110 	union footbridge_attach_args *fba = aux;
    111 
    112 	sc->sc_iot = fba->fba_ca.ca_iot;
    113 	sc->sc_ioh = fba->fba_ca.ca_ioh;
    114 
    115 	clock_sc = sc;
    116 
    117 	/* Cannot do anything until cpu_initclocks() has been called */
    118 
    119 	printf("\n");
    120 }
    121 #endif
    122 
    123 /*
    124  * int clockhandler(struct clockframe *frame)
    125  *
    126  * Function called by timer 1 interrupts.
    127  * This just clears the interrupt condition and calls hardclock().
    128  */
    129 
    130 int
    131 clockhandler(void *aframe)
    132 {
    133 	struct clockframe *frame = aframe;
    134 	bus_space_write_4(clock_sc->sc_iot, clock_sc->sc_ioh,
    135 	    TIMER_1_CLEAR, 0);
    136 	hardclock(frame);
    137 	return 0;	/* Pass the interrupt on down the chain */
    138 }
    139 
    140 /*
    141  * int statclockhandler(struct clockframe *frame)
    142  *
    143  * Function called by timer 2 interrupts.
    144  * This just clears the interrupt condition and calls statclock().
    145  */
    146 
    147 int
    148 statclockhandler(void *aframe)
    149 {
    150 	struct clockframe *frame = aframe;
    151 	int newint, r;
    152 	int currentclock ;
    153 
    154 	/* start the clock off again */
    155 	bus_space_write_4(clock_sc->sc_iot, clock_sc->sc_ioh,
    156 			TIMER_2_CLEAR, 0);
    157 
    158 	do {
    159 		r = random() & (statvar-1);
    160 	} while (r == 0);
    161 	newint = statmin + (r * statcountperusec);
    162 
    163 	/* fetch the current count */
    164 	currentclock = bus_space_read_4(clock_sc->sc_iot, clock_sc->sc_ioh,
    165 		    TIMER_2_VALUE);
    166 
    167 	/*
    168 	 * work out how much time has run, add another usec for time spent
    169 	 * here
    170 	 */
    171 	r = ((statprev - currentclock) + statcountperusec);
    172 
    173 	if (r < newint) {
    174 		newint -= r;
    175 		r = 0;
    176 	}
    177 	else
    178 		printf("statclockhandler: Statclock overrun\n");
    179 
    180 
    181 	/*
    182 	 * update the clock to the new counter, this reloads the existing
    183 	 * timer
    184 	 */
    185 	bus_space_write_4(clock_sc->sc_iot, clock_sc->sc_ioh,
    186 	    		TIMER_2_LOAD, newint);
    187 	statprev = newint;
    188 	statclock(frame);
    189 	if (r)
    190 		/*
    191 		 * We've completely overrun the previous interval,
    192 		 * make sure we report the correct number of ticks.
    193 		 */
    194 		statclock(frame);
    195 
    196 	return 0;	/* Pass the interrupt on down the chain */
    197 }
    198 
    199 static int
    200 load_timer(int base, int herz)
    201 {
    202 	unsigned int timer_count;
    203 	int control;
    204 
    205 	timer_count = dc21285_fclk / herz;
    206 	if (timer_count > TIMER_MAX_VAL * 16) {
    207 		control = TIMER_FCLK_256;
    208 		timer_count >>= 8;
    209 	} else if (timer_count > TIMER_MAX_VAL) {
    210 		control = TIMER_FCLK_16;
    211 		timer_count >>= 4;
    212 	} else
    213 		control = TIMER_FCLK;
    214 
    215 	control |= (TIMER_ENABLE | TIMER_MODE_PERIODIC);
    216 	bus_space_write_4(clock_sc->sc_iot, clock_sc->sc_ioh,
    217 	    base + TIMER_LOAD, timer_count);
    218 	bus_space_write_4(clock_sc->sc_iot, clock_sc->sc_ioh,
    219 	    base + TIMER_CONTROL, control);
    220 	bus_space_write_4(clock_sc->sc_iot, clock_sc->sc_ioh,
    221 	    base + TIMER_CLEAR, 0);
    222 	return timer_count;
    223 }
    224 
    225 /*
    226  * void setstatclockrate(int herz)
    227  *
    228  * Set the stat clock rate. The stat clock uses timer2
    229  */
    230 
    231 void
    232 setstatclockrate(int herz)
    233 {
    234 	int statint;
    235 	int countpersecond;
    236 	int statvarticks;
    237 
    238 	/* statint == num in counter to drop by desired herz */
    239 	statint = statprev = clock_sc->sc_statclock_count =
    240 	    load_timer(TIMER_2_BASE, herz);
    241 
    242 	/* Get the total ticks a second */
    243 	countpersecond = statint * herz;
    244 
    245 	/* now work out how many ticks per usec */
    246 	statcountperusec = countpersecond / 1000000;
    247 
    248 	/* calculate a variance range of statvar */
    249 	statvarticks = statcountperusec * statvar;
    250 
    251 	/* minimum is statint - 50% of variant */
    252 	statmin = statint - (statvarticks / 2);
    253 }
    254 
    255 /*
    256  * void cpu_initclocks(void)
    257  *
    258  * Initialise the clocks.
    259  *
    260  * Timer 1 is used for the main system clock (hardclock)
    261  * Timer 2 is used for the statistics clock (statclock)
    262  */
    263 
    264 void
    265 cpu_initclocks(void)
    266 {
    267 	/* stathz and profhz should be set to something, we have the timer */
    268 	if (stathz == 0)
    269 		stathz = hz;
    270 
    271 	if (profhz == 0)
    272 		profhz = stathz * 5;
    273 
    274 	/* Report the clock frequencies */
    275 	printf("clock: hz=%d stathz = %d profhz = %d\n", hz, stathz, profhz);
    276 
    277 	/* Setup timer 1 and claim interrupt */
    278 	clock_sc->sc_clock_count = load_timer(TIMER_1_BASE, hz);
    279 
    280 	/*
    281 	 * Use ticks per 256us for accuracy since ticks per us is often
    282 	 * fractional e.g. @ 66MHz
    283 	 */
    284 	clock_sc->sc_clock_ticks_per_256us =
    285 	    ((((clock_sc->sc_clock_count * hz) / 1000) * 256) / 1000);
    286 	clock_sc->sc_clockintr = footbridge_intr_claim(IRQ_TIMER_1, IPL_CLOCK,
    287 	    "tmr1 hard clk", clockhandler, 0);
    288 
    289 	if (clock_sc->sc_clockintr == NULL)
    290 		panic("%s: Cannot install timer 1 interrupt handler",
    291 		    clock_sc->sc_dev.dv_xname);
    292 
    293 	/* If stathz is non-zero then setup the stat clock */
    294 	if (stathz) {
    295 		/* Setup timer 2 and claim interrupt */
    296 		setstatclockrate(stathz);
    297        		clock_sc->sc_statclockintr = footbridge_intr_claim(IRQ_TIMER_2, IPL_STATCLOCK,
    298        		    "tmr2 stat clk", statclockhandler, 0);
    299 		if (clock_sc->sc_statclockintr == NULL)
    300 			panic("%s: Cannot install timer 2 interrupt handler",
    301 			    clock_sc->sc_dev.dv_xname);
    302 	}
    303 }
    304 
    305 
    306 /*
    307  * void microtime(struct timeval *tvp)
    308  *
    309  * Fill in the specified timeval struct with the current time
    310  * accurate to the microsecond.
    311  */
    312 
    313 void
    314 microtime(struct timeval *tvp)
    315 {
    316 	int s;
    317 	int tm;
    318 	int deltatm;
    319 	static struct timeval oldtv;
    320 
    321 	if (clock_sc == NULL || clock_sc->sc_clock_count == 0)
    322 		return;
    323 
    324 	s = splhigh();
    325 
    326 	tm = bus_space_read_4(clock_sc->sc_iot, clock_sc->sc_ioh,
    327 	    TIMER_1_VALUE);
    328 
    329 	deltatm = clock_sc->sc_clock_count - tm;
    330 
    331 #ifdef DIAGNOSTIC
    332 	if (deltatm < 0)
    333 		panic("opps deltatm < 0 tm=%d deltatm=%d", tm, deltatm);
    334 #endif
    335 
    336 	/* Fill in the timeval struct */
    337 	*tvp = time;
    338 	tvp->tv_usec += ((deltatm << 8) / clock_sc->sc_clock_ticks_per_256us);
    339 
    340 	/* Make sure the micro seconds don't overflow. */
    341 	while (tvp->tv_usec >= 1000000) {
    342 		tvp->tv_usec -= 1000000;
    343 		++tvp->tv_sec;
    344 	}
    345 
    346 	/* Make sure the time has advanced. */
    347 	if (tvp->tv_sec == oldtv.tv_sec &&
    348 	    tvp->tv_usec <= oldtv.tv_usec) {
    349 		tvp->tv_usec = oldtv.tv_usec + 1;
    350 		if (tvp->tv_usec >= 1000000) {
    351 			tvp->tv_usec -= 1000000;
    352 			++tvp->tv_sec;
    353 		}
    354 	}
    355 
    356 	oldtv = *tvp;
    357 	(void)splx(s);
    358 }
    359 
    360 /*
    361  * Use a timer to track microseconds, if the footbridge hasn't been setup we
    362  * rely on an estimated loop, however footbridge is attached very early on.
    363  */
    364 
    365 static int delay_clock_count = 0;
    366 static int delay_count_per_usec = 0;
    367 
    368 void
    369 calibrate_delay(void)
    370 {
    371      delay_clock_count = load_timer(TIMER_3_BASE, 100);
    372      delay_count_per_usec = delay_clock_count/10000;
    373 #ifdef VERBOSE_DELAY_CALIBRATION
    374      printf("delay calibration: delay_cc = %d, delay_c/us=%d\n",
    375 		     delay_clock_count, delay_count_per_usec);
    376 
    377      printf("0..");
    378      delay(1000000);
    379      printf("1..");
    380      delay(1000000);
    381      printf("2..");
    382      delay(1000000);
    383      printf("3..");
    384      delay(1000000);
    385      printf("4..");
    386       delay(1000000);
    387      printf("5..");
    388       delay(1000000);
    389      printf("6..");
    390       delay(1000000);
    391      printf("7..");
    392       delay(1000000);
    393      printf("8..");
    394       delay(1000000);
    395      printf("9..");
    396       delay(1000000);
    397      printf("10\n");
    398 #endif
    399 }
    400 
    401 int delaycount = 25000;
    402 
    403 void
    404 delay(u_int n)
    405 {
    406 	volatile u_int i;
    407 	uint32_t cur, last, delta, usecs;
    408 
    409 	if (n == 0) return;
    410 
    411 	/*
    412 	 * not calibrated the timer yet, so try to live with this horrible
    413 	 * loop!
    414 	 */
    415 	if (delay_clock_count == 0)
    416 	{
    417 	    while (n-- > 0) {
    418 		for (i = delaycount; --i;);
    419 	    }
    420 	    return;
    421 	}
    422 
    423 	/*
    424 	 * read the current value (do not reset it as delay is reentrant)
    425 	 */
    426 	last = bus_space_read_4(clock_sc->sc_iot, clock_sc->sc_ioh,
    427 		    TIMER_3_VALUE);
    428 
    429 	delta = 0;
    430 
    431 	usecs = n * delay_count_per_usec;
    432 
    433 	while (usecs > delta)
    434 	{
    435 	    cur = bus_space_read_4(clock_sc->sc_iot, clock_sc->sc_ioh,
    436 		    TIMER_3_VALUE);
    437 	    if (last < cur)
    438 		/* timer has wrapped */
    439 		delta += ((delay_clock_count - cur) + last);
    440 	    else
    441 		delta += (last - cur);
    442 
    443 	    if (cur == 0)
    444 	    {
    445 		/*
    446 		 * reset the timer, note that if something blocks us for more
    447 		 * than 1/100s we may delay for too long, but I believe that
    448 		 * is fairly unlikely.
    449 		 */
    450 		bus_space_write_4(clock_sc->sc_iot, clock_sc->sc_ioh,
    451 			TIMER_3_CLEAR, 0);
    452 	    }
    453 	    last = cur;
    454 	}
    455 }
    456 
    457 /* End of footbridge_clock.c */
    458