Home | History | Annotate | Line # | Download | only in iomd
iomd_clock.c revision 1.3
      1 /*	$NetBSD: iomd_clock.c,v 1.3 2001/11/23 19:21:48 thorpej Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1994-1997 Mark Brinicombe.
      5  * Copyright (c) 1994 Brini.
      6  * All rights reserved.
      7  *
      8  * This code is derived from software written for Brini by Mark Brinicombe
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  * 3. All advertising materials mentioning features or use of this software
     19  *    must display the following acknowledgement:
     20  *	This product includes software developed by Mark Brinicombe.
     21  * 4. The name of the company nor the name of the author may be used to
     22  *    endorse or promote products derived from this software without specific
     23  *    prior written permission.
     24  *
     25  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     26  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     27  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     28  * IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
     29  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     30  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
     31  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     35  * SUCH DAMAGE.
     36  *
     37  * RiscBSD kernel project
     38  *
     39  * clock.c
     40  *
     41  * Timer related machine specific code
     42  *
     43  * Created      : 29/09/94
     44  */
     45 
     46 /* Include header files */
     47 
     48 #include <sys/types.h>
     49 #include <sys/param.h>
     50 #include <sys/systm.h>
     51 #include <sys/kernel.h>
     52 #include <sys/time.h>
     53 #include <sys/device.h>
     54 
     55 #include <machine/irqhandler.h>
     56 
     57 #include <arm/cpufunc.h>
     58 
     59 #include <arm/iomd/iomdvar.h>
     60 #include <arm/iomd/iomdreg.h>
     61 
     62 struct clock_softc {
     63 	struct device 		sc_dev;
     64 	bus_space_tag_t		sc_iot;
     65 	bus_space_handle_t	sc_ioh;
     66 };
     67 
     68 #define TIMER_FREQUENCY 2000000		/* 2MHz clock */
     69 #define TICKS_PER_MICROSECOND (TIMER_FREQUENCY / 1000000)
     70 
     71 static void *clockirq;
     72 static void *statclockirq;
     73 static struct clock_softc *clock_sc;
     74 static int timer0_count;
     75 
     76 static int clockmatch	__P((struct device *parent, struct cfdata *cf, void *aux));
     77 static void clockattach	__P((struct device *parent, struct device *self, void *aux));
     78 #ifdef DIAGNOSTIC
     79 static void checkdelay	__P((void));
     80 #endif
     81 
     82 struct cfattach clock_ca = {
     83 	sizeof(struct clock_softc), clockmatch, clockattach
     84 };
     85 
     86 /*
     87  * int clockmatch(struct device *parent, void *match, void *aux)
     88  *
     89  * Just return ok for this if it is device 0
     90  */
     91 
     92 static int
     93 clockmatch(parent, cf, aux)
     94 	struct device *parent;
     95 	struct cfdata *cf;
     96 	void *aux;
     97 {
     98 	struct clk_attach_args *ca = aux;
     99 
    100 	if (strcmp(ca->ca_name, "clk") == 0)
    101 		return(1);
    102 	return(0);
    103 }
    104 
    105 
    106 /*
    107  * void clockattach(struct device *parent, struct device *dev, void *aux)
    108  *
    109  * Map the IOMD and identify it.
    110  * Then configure the child devices based on the IOMD ID.
    111  */
    112 
    113 static void
    114 clockattach(parent, self, aux)
    115 	struct device *parent;
    116 	struct device *self;
    117 	void *aux;
    118 {
    119 	struct clock_softc *sc = (struct clock_softc *)self;
    120 	struct clk_attach_args *ca = aux;
    121 
    122 	sc->sc_iot = ca->ca_iot;
    123 	sc->sc_ioh = ca->ca_ioh; /* This is a handle for the whole IOMD */
    124 
    125 	clock_sc = sc;
    126 
    127 	/* Cannot do anything until cpu_initclocks() has been called */
    128 
    129 	printf("\n");
    130 }
    131 
    132 
    133 /*
    134  * int clockhandler(struct clockframe *frame)
    135  *
    136  * Function called by timer 0 interrupts. This just calls
    137  * hardclock(). Eventually the irqhandler can call hardclock() directly
    138  * but for now we use this function so that we can debug IRQ's
    139  */
    140 
    141 int
    142 clockhandler(frame)
    143 	struct clockframe *frame;
    144 {
    145 	hardclock(frame);
    146 	return(0);	/* Pass the interrupt on down the chain */
    147 }
    148 
    149 
    150 /*
    151  * int statclockhandler(struct clockframe *frame)
    152  *
    153  * Function called by timer 1 interrupts. This just calls
    154  * statclock(). Eventually the irqhandler can call statclock() directly
    155  * but for now we use this function so that we can debug IRQ's
    156  */
    157 
    158 int
    159 statclockhandler(frame)
    160 	struct clockframe *frame;
    161 {
    162 	statclock(frame);
    163 	return(0);	/* Pass the interrupt on down the chain */
    164 }
    165 
    166 
    167 /*
    168  * void setstatclockrate(int hz)
    169  *
    170  * Set the stat clock rate. The stat clock uses timer1
    171  */
    172 
    173 void
    174 setstatclockrate(hz)
    175 	int hz;
    176 {
    177 	int count;
    178 
    179 	count = TIMER_FREQUENCY / hz;
    180 
    181 	printf("Setting statclock to %dHz (%d ticks)\n", hz, count);
    182 
    183 	bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh,
    184 	    IOMD_T1LOW, (count >> 0) & 0xff);
    185 	bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh,
    186 	    IOMD_T1HIGH, (count >> 8) & 0xff);
    187 
    188 	/* reload the counter */
    189 
    190 	bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh,
    191 	    IOMD_T1GO, 0);
    192 }
    193 
    194 
    195 #ifdef DIAGNOSTIC
    196 static void
    197 checkdelay()
    198 {
    199 	struct timeval start, end, diff;
    200 
    201 	microtime(&start);
    202 	delay(10000);
    203 	microtime(&end);
    204 	timersub(&end, &start, &diff);
    205 	if (diff.tv_sec > 0)
    206 		return;
    207 	if (diff.tv_usec > 10000)
    208 		return;
    209 	printf("WARNING: delay(10000) took %ld us\n", diff.tv_usec);
    210 }
    211 #endif
    212 
    213 /*
    214  * void cpu_initclocks(void)
    215  *
    216  * Initialise the clocks.
    217  * This sets up the two timers in the IOMD and installs the IRQ handlers
    218  *
    219  * NOTE: Currently only timer 0 is setup and the IRQ handler is not installed
    220  */
    221 
    222 void
    223 cpu_initclocks()
    224 {
    225 	/*
    226 	 * Load timer 0 with count down value
    227 	 * This timer generates 100Hz interrupts for the system clock
    228 	 */
    229 
    230 	printf("clock: hz=%d stathz = %d profhz = %d\n", hz, stathz, profhz);
    231 
    232 	timer0_count = TIMER_FREQUENCY / hz;
    233 
    234 	bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh,
    235 	    IOMD_T0LOW, (timer0_count >> 0) & 0xff);
    236 	bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh,
    237 	    IOMD_T0HIGH, (timer0_count >> 8) & 0xff);
    238 
    239 	/* reload the counter */
    240 
    241 	bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh,
    242 	    IOMD_T0GO, 0);
    243 
    244 	clockirq = intr_claim(IRQ_TIMER0, IPL_CLOCK, "tmr0 hard clk",
    245 	    clockhandler, 0);
    246 
    247 	if (clockirq == NULL)
    248 		panic("%s: Cannot installer timer 0 IRQ handler\n",
    249 		    clock_sc->sc_dev.dv_xname);
    250 
    251 	if (stathz) {
    252 		setstatclockrate(stathz);
    253        		statclockirq = intr_claim(IRQ_TIMER1, IPL_CLOCK,
    254        		    "tmr1 stat clk", statclockhandler, 0);
    255 		if (statclockirq == NULL)
    256 			panic("%s: Cannot installer timer 1 IRQ handler\n",
    257 			    clock_sc->sc_dev.dv_xname);
    258 	}
    259 #ifdef DIAGNOSTIC
    260 	checkdelay();
    261 #endif
    262 }
    263 
    264 
    265 /*
    266  * void microtime(struct timeval *tvp)
    267  *
    268  * Fill in the specified timeval struct with the current time
    269  * accurate to the microsecond.
    270  */
    271 
    272 void
    273 microtime(tvp)
    274 	struct timeval *tvp;
    275 {
    276 	int s;
    277 	int tm;
    278 	int deltatm;
    279 	static struct timeval oldtv;
    280 
    281 	if (timer0_count == 0)
    282 		return;
    283 
    284 	s = splhigh();
    285 
    286 	/*
    287 	 * Latch the current value of the timer and then read it.
    288 	 * This garentees an atmoic reading of the time.
    289 	 */
    290 
    291 	bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh,
    292 	    IOMD_T0LATCH, 0);
    293 
    294 	tm = bus_space_read_1(clock_sc->sc_iot, clock_sc->sc_ioh,
    295 	    IOMD_T0LOW);
    296 	tm += (bus_space_read_1(clock_sc->sc_iot, clock_sc->sc_ioh,
    297 	    IOMD_T0HIGH) << 8);
    298 
    299 	deltatm = timer0_count - tm;
    300 	if (deltatm < 0)
    301 		printf("opps deltatm < 0 tm=%d deltatm=%d\n",
    302 		    tm, deltatm);
    303 
    304 	/* Fill in the timeval struct */
    305 	*tvp = time;
    306 
    307 	tvp->tv_usec += (deltatm / TICKS_PER_MICROSECOND);
    308 
    309 	/* Make sure the micro seconds don't overflow. */
    310 	while (tvp->tv_usec >= 1000000) {
    311 		tvp->tv_usec -= 1000000;
    312 		++tvp->tv_sec;
    313 	}
    314 
    315 	/* Make sure the time has advanced. */
    316 	if (tvp->tv_sec == oldtv.tv_sec &&
    317 	    tvp->tv_usec <= oldtv.tv_usec) {
    318 		tvp->tv_usec = oldtv.tv_usec + 1;
    319 		if (tvp->tv_usec >= 1000000) {
    320 			tvp->tv_usec -= 1000000;
    321 			++tvp->tv_sec;
    322 		}
    323 	}
    324 
    325 	oldtv = *tvp;
    326 	(void)splx(s);
    327 }
    328 
    329 /*
    330  * Estimated loop for n microseconds
    331  */
    332 
    333 /* Need to re-write this to use the timers */
    334 
    335 /* One day soon I will actually do this */
    336 
    337 int delaycount = 100;
    338 
    339 void
    340 delay(n)
    341 	u_int n;
    342 {
    343 	u_int i;
    344 
    345 	if (n == 0) return;
    346 	while (--n > 0) {
    347 		if (cputype == CPU_ID_SA110)	/* XXX - Seriously gross hack */
    348 			for (i = delaycount; --i;);
    349 		else
    350 			for (i = 8; --i;);
    351 	}
    352 }
    353 
    354 /* End of iomd_clock.c */
    355