xref: /netbsd/sys/arch/arm/iomd/iomd_clock.c (revision 6550d01e)
1 /*	$NetBSD: iomd_clock.c,v 1.25 2009/01/17 17:06:18 mjf 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.25 2009/01/17 17:06:18 mjf 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/simplelock.h>
58 #include <sys/intr.h>
59 
60 #include <dev/clock_subr.h>
61 
62 #include <arm/cpufunc.h>
63 
64 #include <arm/iomd/iomdvar.h>
65 #include <arm/iomd/iomdreg.h>
66 
67 struct clock_softc {
68 	struct device 		sc_dev;
69 	bus_space_tag_t		sc_iot;
70 	bus_space_handle_t	sc_ioh;
71 };
72 
73 #define TIMER_FREQUENCY 2000000		/* 2MHz clock */
74 #define TICKS_PER_MICROSECOND (TIMER_FREQUENCY / 1000000)
75 
76 static void *clockirq;
77 static void *statclockirq;
78 static struct clock_softc *clock_sc;
79 static int timer0_count;
80 
81 static int clockmatch(struct device *parent, struct cfdata *cf, void *aux);
82 static void clockattach(struct device *parent, struct device *self, void *aux);
83 #ifdef DIAGNOSTIC
84 static void checkdelay(void);
85 #endif
86 
87 static u_int iomd_timecounter0_get(struct timecounter *tc);
88 
89 
90 static volatile uint32_t timer0_lastcount;
91 static volatile uint32_t timer0_offset;
92 static volatile int timer0_ticked;
93 /* TODO: Get IRQ status */
94 
95 static struct simplelock tmr_lock = SIMPLELOCK_INITIALIZER;  /* protect TC timer variables */
96 
97 
98 static struct timecounter iomd_timecounter = {
99 	iomd_timecounter0_get,
100 	0, /* No poll_pps */
101 	~0, /* 32bit accuracy */
102 	TIMER_FREQUENCY,
103 	"iomd_timer0",
104 	100
105 };
106 
107 int clockhandler(void *);
108 int statclockhandler(void *);
109 
110 CFATTACH_DECL(clock, sizeof(struct clock_softc),
111     clockmatch, clockattach, NULL, NULL);
112 
113 /*
114  * int clockmatch(struct device *parent, void *match, void *aux)
115  *
116  * Just return ok for this if it is device 0
117  */
118 
119 static int
120 clockmatch(struct device *parent, struct cfdata *cf, void *aux)
121 {
122 	struct clk_attach_args *ca = aux;
123 
124 	if (strcmp(ca->ca_name, "clk") == 0)
125 		return(1);
126 	return(0);
127 }
128 
129 
130 /*
131  * void clockattach(struct device *parent, struct device *dev, void *aux)
132  *
133  * Map the IOMD and identify it.
134  * Then configure the child devices based on the IOMD ID.
135  */
136 
137 static void
138 clockattach(struct device *parent, struct device *self,	void *aux)
139 {
140 	struct clock_softc *sc = (struct clock_softc *)self;
141 	struct clk_attach_args *ca = aux;
142 
143 	sc->sc_iot = ca->ca_iot;
144 	sc->sc_ioh = ca->ca_ioh; /* This is a handle for the whole IOMD */
145 
146 	clock_sc = sc;
147 
148 	/* Cannot do anything until cpu_initclocks() has been called */
149 
150 	printf("\n");
151 }
152 
153 
154 static void
155 tickle_tc(void)
156 {
157 	if (timer0_count &&
158 	    timecounter->tc_get_timecount == iomd_timecounter0_get) {
159 		simple_lock(&tmr_lock);
160 		if (timer0_ticked)
161 			timer0_ticked    = 0;
162 		else {
163 			timer0_offset   += timer0_count;
164 			timer0_lastcount = 0;
165 		}
166 		simple_unlock(&tmr_lock);
167 	}
168 
169 }
170 
171 
172 /*
173  * int clockhandler(struct clockframe *frame)
174  *
175  * Function called by timer 0 interrupts. This just calls
176  * hardclock(). Eventually the irqhandler can call hardclock() directly
177  * but for now we use this function so that we can debug IRQ's
178  */
179 
180 int
181 clockhandler(void *cookie)
182 {
183 	struct clockframe *frame = cookie;
184 	tickle_tc();
185 
186 	hardclock(frame);
187 	return 0;	/* Pass the interrupt on down the chain */
188 }
189 
190 
191 /*
192  * int statclockhandler(struct clockframe *frame)
193  *
194  * Function called by timer 1 interrupts. This just calls
195  * statclock(). Eventually the irqhandler can call statclock() directly
196  * but for now we use this function so that we can debug IRQ's
197  */
198 
199 int
200 statclockhandler(void *cookie)
201 {
202 	struct clockframe *frame = cookie;
203 
204 	statclock(frame);
205 	return 0;	/* Pass the interrupt on down the chain */
206 }
207 
208 
209 /*
210  * void setstatclockrate(int newhz)
211  *
212  * Set the stat clock rate. The stat clock uses timer1
213  */
214 
215 void
216 setstatclockrate(int newhz)
217 {
218 	int count;
219 
220 	count = TIMER_FREQUENCY / newhz;
221 
222 	printf("Setting statclock to %dHz (%d ticks)\n", newhz, count);
223 
224 	bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh,
225 	    IOMD_T1LOW, (count >> 0) & 0xff);
226 	bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh,
227 	    IOMD_T1HIGH, (count >> 8) & 0xff);
228 
229 	/* reload the counter */
230 
231 	bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh,
232 	    IOMD_T1GO, 0);
233 }
234 
235 
236 #ifdef DIAGNOSTIC
237 static void
238 checkdelay(void)
239 {
240 	struct timeval start, end, diff;
241 
242 	microtime(&start);
243 	delay(10000);
244 	microtime(&end);
245 	timersub(&end, &start, &diff);
246 	if (diff.tv_sec > 0)
247 		return;
248 	if (diff.tv_usec > 10000)
249 		return;
250 	printf("WARNING: delay(10000) took %d us\n", diff.tv_usec);
251 }
252 #endif
253 
254 /*
255  * void cpu_initclocks(void)
256  *
257  * Initialise the clocks.
258  * This sets up the two timers in the IOMD and installs the IRQ handlers
259  *
260  * NOTE: Currently only timer 0 is setup and the IRQ handler is not installed
261  */
262 
263 void
264 cpu_initclocks(void)
265 {
266 	/*
267 	 * Load timer 0 with count down value
268 	 * This timer generates 100Hz interrupts for the system clock
269 	 */
270 
271 	printf("clock: hz=%d stathz = %d profhz = %d\n", hz, stathz, profhz);
272 
273 	timer0_count = TIMER_FREQUENCY / hz;
274 
275 	bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh,
276 	    IOMD_T0LOW, (timer0_count >> 0) & 0xff);
277 	bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh,
278 	    IOMD_T0HIGH, (timer0_count >> 8) & 0xff);
279 
280 	/* reload the counter */
281 
282 	bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh,
283 	    IOMD_T0GO, 0);
284 
285 	clockirq = intr_claim(IRQ_TIMER0, IPL_CLOCK, "tmr0 hard clk",
286 	    clockhandler, 0);
287 
288 	if (clockirq == NULL)
289 		panic("%s: Cannot installer timer 0 IRQ handler",
290 		    clock_sc->sc_dev.dv_xname);
291 
292 	if (stathz) {
293 		setstatclockrate(stathz);
294        		statclockirq = intr_claim(IRQ_TIMER1, IPL_CLOCK,
295        		    "tmr1 stat clk", statclockhandler, 0);
296 		if (statclockirq == NULL)
297 			panic("%s: Cannot installer timer 1 IRQ handler",
298 			    clock_sc->sc_dev.dv_xname);
299 	}
300 #ifdef DIAGNOSTIC
301 	checkdelay();
302 #endif
303 	tc_init(&iomd_timecounter);
304 }
305 
306 
307 
308 static u_int iomd_timecounter0_get(struct timecounter *tc)
309 {
310 	int s;
311 	u_int tm;
312 
313 	/*
314 	 * Latch the current value of the timer and then read it.
315 	 * This garentees an atmoic reading of the time.
316 	 */
317 	s = splhigh();
318 	bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh,
319 	    IOMD_T0LATCH, 0);
320 
321 	tm = bus_space_read_1(clock_sc->sc_iot, clock_sc->sc_ioh,
322 	    IOMD_T0LOW);
323 	tm += (bus_space_read_1(clock_sc->sc_iot, clock_sc->sc_ioh,
324 	    IOMD_T0HIGH) << 8);
325 	splx(s);
326 	simple_lock(&tmr_lock);
327 
328 	tm = timer0_count - tm;
329 
330 
331 	if (timer0_count &&
332 	    (tm < timer0_lastcount || (!timer0_ticked && false/* XXX: clkintr_pending */))) {
333 		timer0_ticked = 1;
334 		timer0_offset += timer0_count;
335 	}
336 
337 	timer0_lastcount = tm;
338 	tm += timer0_offset;
339 
340 	simple_unlock(&tmr_lock);
341 	return tm;
342 }
343 
344 
345 
346 /*
347  * Estimated loop for n microseconds
348  */
349 
350 /* Need to re-write this to use the timers */
351 
352 /* One day soon I will actually do this */
353 
354 int delaycount = 100;
355 
356 void
357 delay(u_int n)
358 {
359 	volatile u_int n2;
360 	volatile u_int i;
361 
362 	if (n == 0) return;
363 	n2 = n;
364 	while (n2-- > 0) {
365 		if (cputype == CPU_ID_SA110)	/* XXX - Seriously gross hack */
366 			for (i = delaycount; --i;);
367 		else
368 			for (i = 8; --i;);
369 	}
370 }
371 
372 /* End of iomd_clock.c */
373