1/*	$NetBSD: timer_msiiep.c,v 1.27 2012/07/31 16:38:37 martin Exp $	*/
2
3/*
4 * Copyright (c) 1992, 1993
5 *	The Regents of the University of California.  All rights reserved.
6 * Copyright (c) 1994 Gordon W. Ross
7 * Copyright (c) 1993 Adam Glass
8 * Copyright (c) 1996 Paul Kranenburg
9 * Copyright (c) 1996
10 * 	The President and Fellows of Harvard College. All rights reserved.
11 *
12 * This software was developed by the Computer Systems Engineering group
13 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
14 * contributed to Berkeley.
15 *
16 * All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 *	This product includes software developed by Harvard University.
19 *	This product includes software developed by the University of
20 *	California, Lawrence Berkeley Laboratory.
21 *
22 * Redistribution and use in source and binary forms, with or without
23 * modification, are permitted provided that the following conditions
24 * are met:
25 *
26 * 1. Redistributions of source code must retain the above copyright
27 *    notice, this list of conditions and the following disclaimer.
28 * 2. Redistributions in binary form must reproduce the above copyright
29 *    notice, this list of conditions and the following disclaimer in the
30 *    documentation and/or other materials provided with the distribution.
31 * 3. All advertising materials mentioning features or use of this software
32 *    must display the following acknowledgement:
33 *	This product includes software developed by the University of
34 *	California, Berkeley and its contributors.
35 *	This product includes software developed by Paul Kranenburg.
36 *	This product includes software developed by Harvard University.
37 * 4. Neither the name of the University nor the names of its contributors
38 *    may be used to endorse or promote products derived from this software
39 *    without specific prior written permission.
40 *
41 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
42 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
43 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
44 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
45 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
46 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
47 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
48 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
49 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
50 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
51 * SUCH DAMAGE.
52 *
53 *	@(#)clock.c	8.1 (Berkeley) 6/11/93
54 */
55
56/*
57 * MicroSPARC-IIep timer support.
58 */
59
60#include <sys/cdefs.h>
61__KERNEL_RCSID(0, "$NetBSD: timer_msiiep.c,v 1.27 2012/07/31 16:38:37 martin Exp $");
62
63#include <sys/param.h>
64#include <sys/kernel.h>
65#include <sys/device.h>
66#include <sys/systm.h>
67#include <sys/timetc.h>
68#include <sys/bus.h>
69#include <sys/intr.h>
70
71#include <sparc/sparc/msiiepreg.h>
72#include <sparc/sparc/msiiepvar.h>
73
74#include <sparc/sparc/timervar.h>
75
76
77static int	timermatch_msiiep(device_t, cfdata_t, void *);
78static void	timerattach_msiiep(device_t, device_t, void *);
79
80CFATTACH_DECL_NEW(timer_msiiep, 0,
81    timermatch_msiiep, timerattach_msiiep, NULL, NULL);
82
83
84static void	timer_init_msiiep(void);
85static int	clockintr_msiiep(void *);
86static int	statintr_msiiep(void *);
87static u_int	timer_get_timecount(struct timecounter *);
88
89void*	sched_cookie;
90
91static struct intrhand level10 = { .ih_fun = clockintr_msiiep };
92static struct intrhand level14 = { .ih_fun = statintr_msiiep  };
93
94/*
95 * timecounter local state
96 */
97static struct counter {
98	u_int limit;		/* limit we count up to */
99	u_int offset;		/* accumulated offet due to wraps */
100} cntr;
101
102/*
103 * define timecounter
104 */
105static struct timecounter counter_timecounter = {
106	.tc_get_timecount	= timer_get_timecount,
107	.tc_poll_pps		= NULL,
108	.tc_counter_mask	= ~0u,
109	.tc_frequency		= 25000000, /* Hz */
110	.tc_name		= "timer-counter",
111	.tc_quality		= 100,
112	.tc_priv		= &cntr,
113};
114
115
116/*
117 * ms-IIep counters tick every 4 CPU clocks @100MHz.
118 * counter is reset to 1 when new limit is written.
119 */
120#define	tmr_ustolimIIep(n)	((n) * 25 + 1)
121
122
123static int
124timermatch_msiiep(device_t parent, cfdata_t cf, void *aux)
125{
126	struct msiiep_attach_args *msa = aux;
127
128	return (strcmp(msa->msa_name, "timer") == 0);
129}
130
131
132/*
133 * Attach system and processor counters (kernel hard and stat clocks)
134 * for ms-IIep.  Counters are part of the PCIC and there's no PROM
135 * node for them.
136 */
137static void
138timerattach_msiiep(device_t parent, device_t self, void *aux)
139{
140
141	/* Put processor counter in "counter" mode */
142	mspcic_write_1(pcic_pc_ctl, 0); /* stop user timer (just in case) */
143	mspcic_write_1(pcic_pc_cfg, 0); /* timer mode disabled */
144
145	/*
146	 * Calibrate delay() by tweaking the magic constant until
147	 * delay(100) actually reads (at least) 100 us on the clock.
148	 */
149	for (timerblurb = 1; ; ++timerblurb) {
150		int t;
151
152		(void)mspcic_read_4(pcic_pclr); /* clear the limit bit */
153		mspcic_write_4(pcic_pclr, 0); /* reset to 1, free run */
154		delay(100);
155		t = mspcic_read_4(pcic_pccr);
156		if (t < 0)	/* limit bit set, cannot happen */
157			panic("delay calibration");
158
159		if (t >= 2501) /* (t - 1) / 25 >= 100 us */
160			break;
161	}
162
163	printf(": delay constant %d\n", timerblurb);
164
165	timer_init = timer_init_msiiep;
166
167	/*
168	 * Set counter interrupt priority assignment:
169	 * upper 4 bits are for system counter: level 10
170	 * lower 4 bits are for processor counter: level 14
171	 *
172	 * XXX: ensure that interrupt target mask doesn't mask them?
173	 */
174	mspcic_write_1(pcic_cipar, 0xae);
175
176	/* link interrupt handlers */
177	intr_establish(10, 0, &level10, NULL, false);
178	intr_establish(14, 0, &level14, NULL, false);
179
180	/* Establish a soft interrupt at a lower level for schedclock */
181	sched_cookie = sparc_softintr_establish(IPL_SCHED, schedintr, NULL);
182	if (sched_cookie == NULL)
183		panic("timerattach: cannot establish schedintr");
184}
185
186
187/*
188 * Set up the real-time and statistics clocks.
189 * Leave stathz 0 only if no alternative timer is available.
190 *
191 * The frequencies of these clocks must be an even number of microseconds.
192 */
193static void
194timer_init_msiiep(void)
195{
196
197	mspcic_write_4(pcic_sclr, tmr_ustolimIIep(tick));
198	mspcic_write_4(pcic_pclr, tmr_ustolimIIep(statint));
199
200	cntr.limit = tmr_ustolimIIep(tick);
201	tc_init(&counter_timecounter);
202}
203
204
205/*
206 * timer_get_timecount provide current counter value
207 */
208static u_int
209timer_get_timecount(struct timecounter *tc)
210{
211	struct counter *ctr = (struct counter *)tc->tc_priv;
212	u_int c, res;
213	int s;
214
215	s = splhigh();
216
217	/* XXX: consider re-reading until increment is detected */
218	c = mspcic_read_4(pcic_sccr);
219
220	res = c & ~MSIIEP_COUNTER_LIMIT;
221	if (res != c)
222		res += ctr->limit;
223
224	res += ctr->offset;
225
226	splx(s);
227
228	return res;
229}
230
231
232/*
233 * Level 10 (clock) interrupts from system counter.
234 */
235static int
236clockintr_msiiep(void *cap)
237{
238	(void)mspcic_read_4(pcic_sclr); /* clear the interrupt */
239
240	/*
241	 * XXX this needs to be fixed in a more general way
242	 * problem is that the kernel enables interrupts and THEN
243	 * sets up clocks. In between there's an opportunity to catch
244	 * a timer interrupt - if we call hardclock() at that point we'll
245	 * panic
246	 * so for now just bail when cold
247	 */
248	if (__predict_false(cold))
249		return 0;
250
251	if (timecounter->tc_get_timecount == timer_get_timecount)
252		cntr.offset += cntr.limit;
253
254	hardclock((struct clockframe *)cap);
255	return 1;
256}
257
258
259/*
260 * Level 14 (stat clock) interrupts from processor counter.
261 */
262static int
263statintr_msiiep(void *cap)
264{
265	struct clockframe *frame = cap;
266	u_long newint;
267
268	(void)mspcic_read_4(pcic_pclr); /* clear the interrupt */
269
270	statclock(frame);
271
272	/*
273	 * Compute new randomized interval.
274	 */
275	newint = new_interval();
276
277	/*
278	 * Use the `non-resetting' limit register, so that we don't
279	 * lose the counter ticks that happened since this interrupt
280	 * has been raised.
281	 */
282	mspcic_write_4(pcic_pclr_nr, tmr_ustolimIIep(newint));
283
284	/*
285	 * The factor 8 is only valid for stathz==100.
286	 * See also clock.c
287	 */
288	if ((++cpuinfo.ci_schedstate.spc_schedticks & 7) == 0) {
289		if (CLKF_LOPRI(frame, IPL_SCHED)) {
290			/* No need to schedule a soft interrupt */
291			spllowerschedclock();
292			schedintr(cap);
293		} else {
294			/*
295			 * We're interrupting a thread that may have the
296			 * scheduler lock; run schedintr() later.
297			 */
298			sparc_softintr_schedule(sched_cookie);
299		}
300	}
301
302	return 1;
303}
304