Home | History | Annotate | Line # | Download | only in ebus
clock_ebus.c revision 1.1
      1 /*	$NetBSD: clock_ebus.c,v 1.1 2011/01/26 01:18:50 pooka Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2010 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code was written by Alessandro Forin and Neil Pittman
      8  * at Microsoft Research and contributed to The NetBSD Foundation
      9  * by Microsoft Corporation.
     10  *
     11  * Redistribution and use in source and binary forms, with or without
     12  * modification, are permitted provided that the following conditions
     13  * are met:
     14  * 1. Redistributions of source code must retain the above copyright
     15  *    notice, this list of conditions and the following disclaimer.
     16  * 2. Redistributions in binary form must reproduce the above copyright
     17  *    notice, this list of conditions and the following disclaimer in the
     18  *    documentation and/or other materials provided with the distribution.
     19  *
     20  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     30  * POSSIBILITY OF SUCH DAMAGE.
     31  */
     32 
     33 #include <sys/cdefs.h>			/* RCS ID & Copyright macro defns */
     34 __KERNEL_RCSID(0, "$NetBSD: clock_ebus.c,v 1.1 2011/01/26 01:18:50 pooka Exp $");
     35 
     36 #include <sys/param.h>
     37 #include <sys/kernel.h>
     38 #include <sys/device.h>
     39 #include <sys/systm.h>
     40 #include <sys/timetc.h>
     41 
     42 #include <dev/clock_subr.h>
     43 
     44 #include <emips/ebus/ebusvar.h>
     45 #include <emips/emips/machdep.h>
     46 #include <machine/emipsreg.h>
     47 
     48 /*
     49  * Device softc
     50  */
     51 struct eclock_softc {
     52 	struct device sc_dev;
     53 	struct _Tc *sc_dp;
     54     uint32_t reload;
     55     struct timecounter sc_tc;
     56 #ifdef __HAVE_GENERIC_TODR
     57     struct todr_chip_handle sc_todr;
     58 #endif
     59 };
     60 
     61 static int	eclock_ebus_match (struct device *, struct cfdata *, void *);
     62 static void	eclock_ebus_attach (struct device *, struct device *, void *);
     63 
     64 CFATTACH_DECL(eclock_ebus, sizeof (struct eclock_softc),
     65     eclock_ebus_match, eclock_ebus_attach, NULL, NULL);
     66 
     67        void	eclock_init(struct device *);
     68 static void	__eclock_init(struct device *);
     69 static int	eclock_gettime(struct todr_chip_handle *,
     70                            struct timeval *);
     71 static int	eclock_settime(struct todr_chip_handle *,
     72                            struct timeval *);
     73 static int  eclock_ebus_intr(void *cookie, void *f);
     74 static u_int eclock_counter(struct timecounter *tc);
     75 
     76 struct device *clockdev = NULL; /* BUGBUG resolve the gap between cpu_initclocks() and eclock_init(x) */
     77 
     78 void
     79 eclock_init(struct device *dev)
     80 {
     81     if (dev == NULL)
     82         dev = clockdev;
     83     if (dev == NULL)
     84         panic("eclock_init");
     85     __eclock_init(dev);
     86 }
     87 
     88 static void
     89 __eclock_init(struct device *dev)
     90 {
     91 	struct eclock_softc *sc = (struct eclock_softc *)dev;
     92     struct _Tc *tc = sc->sc_dp;
     93     uint32_t reload = 10*1000000; /* 1sec in 100ns units (10MHz clock) */
     94 
     95     /* Compute reload according to whatever value passed in, Warn if fractional */
     96     if (hz > 1) {
     97         uint32_t r = reload / hz;
     98         if ((r * hz) != reload)
     99             printf("%s: %d Hz clock will cause roundoffs with 10MHz xtal (%d)\n",
    100                    sc->sc_dev.dv_xname, hz, reload - (r * hz));
    101 		reload = r;
    102 	}
    103 
    104     sc->reload = reload;
    105 
    106     /* Start the counter */
    107     tc->DownCounterHigh = 0;
    108     tc->DownCounter = sc->reload;
    109     tc->Control = TCCT_ENABLE | TCCT_INT_ENABLE;
    110 }
    111 
    112 /*
    113  * Get the time of day, based on the clock's value and/or the base value.
    114  * NB: At 10MHz, our 64bits FreeRunning is worth 58,426 years.
    115  */
    116 
    117 extern u_quad_t __qdivrem(u_quad_t uq, u_quad_t vq, u_quad_t *arq);
    118 
    119 
    120 static int
    121 eclock_gettime(struct todr_chip_handle *todr, struct timeval *tv)
    122 {
    123 	struct eclock_softc *sc = (struct eclock_softc *) todr->cookie;
    124     struct _Tc *tc = sc->sc_dp;
    125     uint64_t free;
    126     int s;
    127 
    128     /* 32bit processor, guard against interrupts in the middle of reading this 64bit entity
    129      * BUGBUG Should read it "twice" to guard against rollover too.
    130      */
    131 	s = splhigh();
    132     free = tc->FreeRunning;
    133 	splx(s);
    134 
    135     /* Big fight with the compiler here, it gets very confused by 64bits.
    136      */
    137 #if 0
    138     /* This is in C:
    139      */
    140     {
    141         uint64_t freeS, freeU;
    142         freeS = free / (10*1000*1000);
    143         freeU = free % (10*1000*1000);
    144         tv->tv_sec  = freeS;
    145         tv->tv_usec = freeU / 10;
    146         //printf("egt: s x%lx u x%lx (fs %lld fu %lld f %lld)\n",tv->tv_sec,tv->tv_usec,freeS,freeU,free);
    147     }
    148 #else
    149     /* And this is in assembly :-)
    150      */
    151     {
    152         u_quad_t r;
    153         u_quad_t d = __qdivrem(free,(u_quad_t)10000000,&r);
    154         uint32_t su, uu;
    155         su = (uint32_t) d;
    156         uu = (uint32_t) r;
    157         uu = uu / 10; /* in usecs */
    158         tv->tv_sec  = su;
    159         tv->tv_usec = uu;
    160         //printf("egt: s x%lx u x%lx  (d %lld r %lld  f %lld)\n",tv->tv_sec,tv->tv_usec,d,r,free);
    161     }
    162 #endif
    163 
    164     return 0;
    165 }
    166 
    167 /*
    168  * Reset the TODR based on the time value.
    169  */
    170 static int
    171 eclock_settime(struct todr_chip_handle *todr, struct timeval *tv)
    172 {
    173 	struct eclock_softc *sc = (struct eclock_softc *) todr->cookie;
    174     struct _Tc *tc = sc->sc_dp;
    175     uint64_t free;
    176     uint32_t su, uu;
    177     int s;
    178 
    179     /* Careful with what we do here, else the compilerbugs hit hard */
    180 	s = splhigh();
    181 
    182     su = (uint32_t) tv->tv_sec;  //0(tv)
    183     uu = (uint32_t) tv->tv_usec; //4(tv)
    184 
    185 
    186     free  = 10*1000*1000 * (uint64_t)su;
    187     free += uu * 10;
    188 
    189     tc->FreeRunning = free;
    190 	splx(s);
    191 
    192 #if 0
    193 Should compile to something like this:
    194 80260c84 <eclock_settime>:
    195 80260c84:	27bdffc0 	addiu	sp,sp,-64
    196 80260c88:	afbf0038 	sw	ra,56(sp)
    197 80260c8c:	afb40030 	sw	s4,48(sp)
    198 80260c90:	afb3002c 	sw	s3,44(sp)
    199 80260c94:	afb20028 	sw	s2,40(sp)
    200 80260c98:	afb10024 	sw	s1,36(sp)
    201 80260c9c:	afb00020 	sw	s0,32(sp)
    202 80260ca0:	afb50034 	sw	s5,52(sp)
    203 80260ca4:	8c820000 	lw	v0,0(a0)
    204 80260ca8:	00a09021 	move	s2,a1
    205 80260cac:	8c55003c 	lw	s5,60(v0)        //s5=tc
    206 80260cb0:	0c004122 	jal	80010488 <_splraise>
    207 80260cb4:	3404ff00 	li	a0,0xff00
    208 80260cb8:	8e540000 	lw	s4,0(s2)         //s4=tv->tv_sec=us
    209 80260cbc:	3c060098 	lui	a2,0x98
    210 80260cc0:	34c69680 	ori	a2,a2,0x9680     //a2=10000000
    211 80260cc4:	02860019 	multu	s4,a2        //free=us*10000000
    212 80260cc8:	8e530004 	lw	s3,4(s2)         //s3=uu
    213 80260ccc:	00402021 	move	a0,v0        //s=splhigh()
    214 80260cd0:	001328c0 	sll	a1,s3,0x3
    215 80260cd4:	00131040 	sll	v0,s3,0x1
    216 80260cd8:	00451021 	addu	v0,v0,a1
    217 80260cdc:	00401821 	move	v1,v0        //v1 = uu*10
    218 80260ce0:	00001021 	move	v0,zero
    219 80260ce4:	00003812 	mflo	a3           //a3=low(free)
    220 80260ce8:	00e38821 	addu	s1,a3,v1     //s1=low(free)+(uu*10)
    221 80260cec:	0227282b 	sltu	a1,s1,a3     //a1=overflow bit
    222 80260cf0:	00003010 	mfhi	a2           //a2=high(free)
    223 80260cf4:	00c28021 	addu	s0,a2,v0     //s0=a2=high(free) [useless, v0=0]
    224 80260cf8:	00b08021 	addu	s0,a1,s0     //s0+=overflow bit
    225 80260cfc:	aeb1000c 	sw	s1,12(s5)
    226 80260d00:	aeb00008 	sw	s0,8(s5)
    227 80260d04:	0c00413f 	jal	800104fc <_splset>
    228 80260d08:	00000000 	nop
    229 
    230 #endif
    231 
    232     //printf("est: s x%lx u x%lx (%d %d), free %lld\n",tv->tv_sec,tv->tv_usec,su,uu,free);
    233 
    234     return 0;
    235 }
    236 
    237 static int
    238 eclock_ebus_intr(void *cookie, void *f)
    239 {
    240 	struct eclock_softc *sc = cookie;
    241     struct _Tc *tc = sc->sc_dp;
    242     struct clockframe *cf = f;
    243     volatile uint32_t x;
    244 
    245     x = tc->Control;
    246     tc->DownCounterHigh = 0;
    247     tc->DownCounter = sc->reload;
    248 
    249     hardclock(cf);
    250     emips_clock_evcnt.ev_count++;
    251 
    252 	return (0);
    253 }
    254 
    255 static u_int
    256 eclock_counter(struct timecounter *tc)
    257 {
    258 	struct eclock_softc *sc = tc->tc_priv;
    259     struct _Tc *Tc = sc->sc_dp;
    260     return (u_int)Tc->FreeRunning; /* NB: chops to 32bits */
    261 }
    262 
    263 
    264 static int
    265 eclock_ebus_match(struct device *parent, struct cfdata *match, void *aux)
    266 {
    267 	struct ebus_attach_args *ia = aux;
    268     struct _Tc *mc = (struct _Tc *)ia->ia_vaddr;
    269 
    270 	if (strcmp("eclock", ia->ia_name) != 0)
    271 		return (0);
    272     if ((mc == NULL) ||
    273         (mc->Tag != PMTTAG_TIMER))
    274 		return (0);
    275 
    276 	return (1);
    277 }
    278 
    279 static void
    280 eclock_ebus_attach(struct device *parent, struct device *self, void *aux)
    281 {
    282 	struct ebus_attach_args *ia =aux;
    283 	struct eclock_softc *sc = (struct eclock_softc *)self;
    284 
    285 	sc->sc_dp = (struct _Tc*)ia->ia_vaddr;
    286 
    287     /* NB: We are chopping our 64bit free-running  down to 32bits */
    288     sc->sc_tc.tc_get_timecount = eclock_counter;
    289     sc->sc_tc.tc_poll_pps = 0;
    290     sc->sc_tc.tc_counter_mask = 0xffffffff;
    291     sc->sc_tc.tc_frequency = 10*1000*1000; /* 10 MHz */
    292     sc->sc_tc.tc_name = "eclock"; /* BUGBUG is it unique per instance?? */
    293     sc->sc_tc.tc_quality = 2000; /* uhu? */
    294     sc->sc_tc.tc_priv = sc;
    295     sc->sc_tc.tc_next = NULL;
    296 
    297 #if DEBUG
    298     printf(" virt=%p ", (void*)sc->sc_dp);
    299 #endif
    300 	printf(": eMIPS clock\n");
    301 
    302 	/* Turn interrupts off, just in case. */
    303     sc->sc_dp->Control &= ~(TCCT_INT_ENABLE|TCCT_INTERRUPT);
    304 
    305 	ebus_intr_establish(parent, (void *)ia->ia_cookie, IPL_CLOCK,
    306 	    eclock_ebus_intr, sc);
    307 
    308 #ifdef EVCNT_COUNTERS
    309 	evcnt_attach_dynamic(&clock_intr_evcnt, EVCNT_TYPE_INTR, NULL,
    310 	    sc->sc_dev->dv_xname, "intr");
    311 #endif
    312 
    313 #ifdef __HAVE_GENERIC_TODR
    314     clockdev = self;
    315     memset(&sc->sc_todr,0,sizeof sc->sc_todr);
    316     sc->sc_todr.cookie = sc;
    317     sc->sc_todr.todr_gettime = eclock_gettime;
    318     sc->sc_todr.todr_settime = eclock_settime;
    319     todr_attach(&sc->sc_todr);
    320 #endif
    321 
    322     tc_init(&sc->sc_tc);
    323 }
    324