Home | History | Annotate | Line # | Download | only in iomd
iomd_clock.c revision 1.19
      1 /*	$NetBSD: iomd_clock.c,v 1.19 2006/08/03 23:19:06 bjh21 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/param.h>
     49 
     50 __KERNEL_RCSID(0, "$NetBSD: iomd_clock.c,v 1.19 2006/08/03 23:19:06 bjh21 Exp $");
     51 
     52 #include <sys/systm.h>
     53 #include <sys/kernel.h>
     54 #include <sys/time.h>
     55 #include <sys/timetc.h>
     56 #include <sys/device.h>
     57 #include <sys/lock.h>
     58 
     59 #include <dev/clock_subr.h>
     60 
     61 #include <machine/intr.h>
     62 
     63 #include <arm/cpufunc.h>
     64 
     65 #include <arm/iomd/iomdvar.h>
     66 #include <arm/iomd/iomdreg.h>
     67 
     68 struct clock_softc {
     69 	struct device 		sc_dev;
     70 	bus_space_tag_t		sc_iot;
     71 	bus_space_handle_t	sc_ioh;
     72 };
     73 
     74 #define TIMER_FREQUENCY 2000000		/* 2MHz clock */
     75 #define TICKS_PER_MICROSECOND (TIMER_FREQUENCY / 1000000)
     76 
     77 static void *clockirq;
     78 static void *statclockirq;
     79 static struct clock_softc *clock_sc;
     80 static int timer0_count;
     81 static int timeset;
     82 
     83 static int clockmatch	__P((struct device *parent, struct cfdata *cf, void *aux));
     84 static void clockattach	__P((struct device *parent, struct device *self, void *aux));
     85 #ifdef DIAGNOSTIC
     86 static void checkdelay	__P((void));
     87 #endif
     88 
     89 static u_int iomd_timecounter0_get(struct timecounter *tc);
     90 
     91 
     92 static volatile uint32_t timer0_lastcount;
     93 static volatile uint32_t timer0_offset;
     94 static volatile int timer0_ticked;
     95 /* TODO: Get IRQ status */
     96 
     97 static struct simplelock tmr_lock = SIMPLELOCK_INITIALIZER;  /* protect TC timer variables */
     98 
     99 
    100 static struct timecounter iomd_timecounter = {
    101 	iomd_timecounter0_get,
    102 	0, /* No poll_pps */
    103 	~0, /* 32bit accuracy */
    104 	TIMER_FREQUENCY,
    105 	"iomd_timer0",
    106 	100
    107 };
    108 
    109 int clockhandler	__P((void *));
    110 int statclockhandler	__P((void *));
    111 
    112 CFATTACH_DECL(clock, sizeof(struct clock_softc),
    113     clockmatch, clockattach, NULL, NULL);
    114 
    115 /*
    116  * int clockmatch(struct device *parent, void *match, void *aux)
    117  *
    118  * Just return ok for this if it is device 0
    119  */
    120 
    121 static int
    122 clockmatch(parent, cf, aux)
    123 	struct device *parent;
    124 	struct cfdata *cf;
    125 	void *aux;
    126 {
    127 	struct clk_attach_args *ca = aux;
    128 
    129 	if (strcmp(ca->ca_name, "clk") == 0)
    130 		return(1);
    131 	return(0);
    132 }
    133 
    134 
    135 /*
    136  * void clockattach(struct device *parent, struct device *dev, void *aux)
    137  *
    138  * Map the IOMD and identify it.
    139  * Then configure the child devices based on the IOMD ID.
    140  */
    141 
    142 static void
    143 clockattach(parent, self, aux)
    144 	struct device *parent;
    145 	struct device *self;
    146 	void *aux;
    147 {
    148 	struct clock_softc *sc = (struct clock_softc *)self;
    149 	struct clk_attach_args *ca = aux;
    150 
    151 	sc->sc_iot = ca->ca_iot;
    152 	sc->sc_ioh = ca->ca_ioh; /* This is a handle for the whole IOMD */
    153 
    154 	clock_sc = sc;
    155 
    156 	/* Cannot do anything until cpu_initclocks() has been called */
    157 
    158 	printf("\n");
    159 }
    160 
    161 
    162 static void
    163 tickle_tc(void)
    164 {
    165 	if (timer0_count &&
    166 	    timecounter->tc_get_timecount == iomd_timecounter0_get) {
    167 		simple_lock(&tmr_lock);
    168 		if (timer0_ticked)
    169 			timer0_ticked    = 0;
    170 		else {
    171 			timer0_offset   += timer0_count;
    172 			timer0_lastcount = 0;
    173 		}
    174 		simple_unlock(&tmr_lock);
    175 	}
    176 
    177 }
    178 
    179 
    180 /*
    181  * int clockhandler(struct clockframe *frame)
    182  *
    183  * Function called by timer 0 interrupts. This just calls
    184  * hardclock(). Eventually the irqhandler can call hardclock() directly
    185  * but for now we use this function so that we can debug IRQ's
    186  */
    187 
    188 int
    189 clockhandler(cookie)
    190 	void *cookie;
    191 {
    192 	struct clockframe *frame = cookie;
    193 	tickle_tc();
    194 
    195 	hardclock(frame);
    196 	return(0);	/* Pass the interrupt on down the chain */
    197 }
    198 
    199 
    200 /*
    201  * int statclockhandler(struct clockframe *frame)
    202  *
    203  * Function called by timer 1 interrupts. This just calls
    204  * statclock(). Eventually the irqhandler can call statclock() directly
    205  * but for now we use this function so that we can debug IRQ's
    206  */
    207 
    208 int
    209 statclockhandler(cookie)
    210 	void *cookie;
    211 {
    212 	struct clockframe *frame = cookie;
    213 
    214 	statclock(frame);
    215 	return(0);	/* Pass the interrupt on down the chain */
    216 }
    217 
    218 
    219 /*
    220  * void setstatclockrate(int newhz)
    221  *
    222  * Set the stat clock rate. The stat clock uses timer1
    223  */
    224 
    225 void
    226 setstatclockrate(int newhz)
    227 {
    228 	int count;
    229 
    230 	count = TIMER_FREQUENCY / newhz;
    231 
    232 	printf("Setting statclock to %dHz (%d ticks)\n", newhz, count);
    233 
    234 	bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh,
    235 	    IOMD_T1LOW, (count >> 0) & 0xff);
    236 	bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh,
    237 	    IOMD_T1HIGH, (count >> 8) & 0xff);
    238 
    239 	/* reload the counter */
    240 
    241 	bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh,
    242 	    IOMD_T1GO, 0);
    243 }
    244 
    245 
    246 #ifdef DIAGNOSTIC
    247 static void
    248 checkdelay()
    249 {
    250 	struct timeval start, end, diff;
    251 
    252 	microtime(&start);
    253 	delay(10000);
    254 	microtime(&end);
    255 	timersub(&end, &start, &diff);
    256 	if (diff.tv_sec > 0)
    257 		return;
    258 	if (diff.tv_usec > 10000)
    259 		return;
    260 	printf("WARNING: delay(10000) took %ld us\n", diff.tv_usec);
    261 }
    262 #endif
    263 
    264 /*
    265  * void cpu_initclocks(void)
    266  *
    267  * Initialise the clocks.
    268  * This sets up the two timers in the IOMD and installs the IRQ handlers
    269  *
    270  * NOTE: Currently only timer 0 is setup and the IRQ handler is not installed
    271  */
    272 
    273 void
    274 cpu_initclocks()
    275 {
    276 	/*
    277 	 * Load timer 0 with count down value
    278 	 * This timer generates 100Hz interrupts for the system clock
    279 	 */
    280 
    281 	printf("clock: hz=%d stathz = %d profhz = %d\n", hz, stathz, profhz);
    282 
    283 	timer0_count = TIMER_FREQUENCY / hz;
    284 
    285 	bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh,
    286 	    IOMD_T0LOW, (timer0_count >> 0) & 0xff);
    287 	bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh,
    288 	    IOMD_T0HIGH, (timer0_count >> 8) & 0xff);
    289 
    290 	/* reload the counter */
    291 
    292 	bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh,
    293 	    IOMD_T0GO, 0);
    294 
    295 	clockirq = intr_claim(IRQ_TIMER0, IPL_CLOCK, "tmr0 hard clk",
    296 	    clockhandler, 0);
    297 
    298 	if (clockirq == NULL)
    299 		panic("%s: Cannot installer timer 0 IRQ handler",
    300 		    clock_sc->sc_dev.dv_xname);
    301 
    302 	if (stathz) {
    303 		setstatclockrate(stathz);
    304        		statclockirq = intr_claim(IRQ_TIMER1, IPL_CLOCK,
    305        		    "tmr1 stat clk", statclockhandler, 0);
    306 		if (statclockirq == NULL)
    307 			panic("%s: Cannot installer timer 1 IRQ handler",
    308 			    clock_sc->sc_dev.dv_xname);
    309 	}
    310 #ifdef DIAGNOSTIC
    311 	checkdelay();
    312 #endif
    313 	tc_init(&iomd_timecounter);
    314 }
    315 
    316 
    317 
    318 static u_int iomd_timecounter0_get(struct timecounter *tc)
    319 {
    320 	int s;
    321 	u_int tm;
    322 
    323 	/*
    324 	 * Latch the current value of the timer and then read it.
    325 	 * This garentees an atmoic reading of the time.
    326 	 */
    327 	s = splhigh();
    328 	bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh,
    329 	    IOMD_T0LATCH, 0);
    330 
    331 	tm = bus_space_read_1(clock_sc->sc_iot, clock_sc->sc_ioh,
    332 	    IOMD_T0LOW);
    333 	tm += (bus_space_read_1(clock_sc->sc_iot, clock_sc->sc_ioh,
    334 	    IOMD_T0HIGH) << 8);
    335 	splx(s);
    336 	simple_lock(&tmr_lock);
    337 
    338 	tm = timer0_count - tm;
    339 
    340 
    341 	if (timer0_count &&
    342 	    (tm < timer0_lastcount || (!timer0_ticked && FALSE/* XXX: clkintr_pending */))) {
    343 		timer0_ticked = 1;
    344 		timer0_offset += timer0_count;
    345 	}
    346 
    347 	timer0_lastcount = tm;
    348 	tm += timer0_offset;
    349 
    350 	simple_unlock(&tmr_lock);
    351 	return tm;
    352 }
    353 
    354 
    355 
    356 /*
    357  * Estimated loop for n microseconds
    358  */
    359 
    360 /* Need to re-write this to use the timers */
    361 
    362 /* One day soon I will actually do this */
    363 
    364 int delaycount = 100;
    365 
    366 void
    367 delay(n)
    368 	u_int n;
    369 {
    370 	volatile u_int n2;
    371 	volatile u_int i;
    372 
    373 	if (n == 0) return;
    374 	n2 = n;
    375 	while (n2-- > 0) {
    376 		if (cputype == CPU_ID_SA110)	/* XXX - Seriously gross hack */
    377 			for (i = delaycount; --i;);
    378 		else
    379 			for (i = 8; --i;);
    380 	}
    381 }
    382 
    383 todr_chip_handle_t todr_handle;
    384 
    385 /*
    386  * todr_attach:
    387  *
    388  *	Set the specified time-of-day register as the system real-time clock.
    389  */
    390 void
    391 todr_attach(todr_chip_handle_t todr)
    392 {
    393 
    394 	if (todr_handle)
    395 		panic("todr_attach: rtc already configured");
    396 	todr_handle = todr;
    397 }
    398 
    399 /*
    400  * inittodr:
    401  *
    402  *	Initialize time from the time-of-day register.
    403  */
    404 #define	MINYEAR		2003	/* minimum plausible year */
    405 void
    406 inittodr(time_t base)
    407 {
    408 	time_t deltat;
    409 	int badbase;
    410 	int badtime;
    411 	struct timeval time;
    412 	struct timespec tc_time; /* for timecounters */
    413 
    414 	/* Default is to assume that time from somewhere will be okay.
    415 	 * If not badtime will be set to 1 */
    416 	badtime = 0;
    417 	if (base < (MINYEAR - 1970) * SECYR) {
    418 		printf("WARNING: preposterous time in file system");
    419 		/* read the system clock anyway */
    420 		base = (MINYEAR - 1970) * SECYR;
    421 		badbase = 1;
    422 	} else
    423 		badbase = 0;
    424 
    425 	if (todr_handle == NULL ||
    426 	    todr_gettime(todr_handle, &time) != 0 ||
    427 	    time.tv_sec == 0) {
    428 		/*
    429 		 * Believe the time in the file system for lack of
    430 		 * anything better, resetting the TODR.
    431 		 */
    432 		time.tv_sec = base;
    433 		time.tv_usec = 0;
    434 		if (todr_handle != NULL && !badbase) {
    435 			printf("WARNING: preposterous clock chip time\n");
    436 			resettodr();
    437 		}
    438 		badtime = 1;
    439 	}
    440 
    441 	if (!badbase) {
    442 		/*
    443 		 * See if we gained/lost two or more days; if
    444 		 * so, assume something is amiss.
    445 		 */
    446 		deltat = time.tv_sec - base;
    447 		if (deltat < 0)
    448 			deltat = -deltat;
    449 		if (deltat >= 2 * SECDAY)
    450 		printf("WARNING: clock %s %ld days\n",
    451 		       time.tv_sec < base ? "lost" : "gained",
    452 		       (long)deltat / SECDAY);
    453 	}
    454 	/* At this point time is a time value we can initialise the system
    455 	 * clock with */
    456 	tc_time.tv_sec = time.tv_sec;
    457 	tc_time.tv_nsec = time.tv_usec * 1000;
    458 	tc_setclock(&tc_time);
    459 	timeset = 1;
    460 
    461 	if (badtime)
    462 		printf("WARNING: CHECK AND RESET THE DATE!\n");
    463 }
    464 
    465 /*
    466  * resettodr:
    467  *
    468  *	Reset the time-of-day register with the current time.
    469  */
    470 void
    471 resettodr(void)
    472 {
    473 	struct timeval time;
    474 	if (!timeset)
    475 		return;
    476 
    477 	getmicrotime(&time);
    478 	if (todr_handle != NULL &&
    479 	    todr_settime(todr_handle, &time) != 0)
    480 		printf("resettodr: failed to set time\n");
    481 }
    482 
    483 /* End of iomd_clock.c */
    484