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