xref: /netbsd-src/sys/arch/emips/ebus/clock_ebus.c (revision daf6c4152fcddc27c445489775ed1f66ab4ea9a9)
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