xref: /netbsd-src/sys/arch/sparc/sparc/timer_msiiep.c (revision 2a627e63bd7e6a73a587538b9573afd70ddc9a4a)
1 /*	$NetBSD: timer_msiiep.c,v 1.28 2013/11/16 23:54:01 mrg 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.28 2013/11/16 23:54:01 mrg 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 
77 static int	timermatch_msiiep(device_t, cfdata_t, void *);
78 static void	timerattach_msiiep(device_t, device_t, void *);
79 
80 CFATTACH_DECL_NEW(timer_msiiep, 0,
81     timermatch_msiiep, timerattach_msiiep, NULL, NULL);
82 
83 
84 static void	timer_init_msiiep(void);
85 static int	clockintr_msiiep(void *);
86 static int	statintr_msiiep(void *);
87 static u_int	timer_get_timecount(struct timecounter *);
88 
89 void*	sched_cookie;
90 
91 static struct intrhand level10 = { .ih_fun = clockintr_msiiep };
92 static struct intrhand level14 = { .ih_fun = statintr_msiiep  };
93 
94 /*
95  * timecounter local state
96  */
97 static 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  */
105 static 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 
123 static int
timermatch_msiiep(device_t parent,cfdata_t cf,void * aux)124 timermatch_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  */
137 static void
timerattach_msiiep(device_t parent,device_t self,void * aux)138 timerattach_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  */
193 static void
timer_init_msiiep(void)194 timer_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  */
208 static u_int
timer_get_timecount(struct timecounter * tc)209 timer_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  */
235 static int
clockintr_msiiep(void * cap)236 clockintr_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  */
262 static int
statintr_msiiep(void * cap)263 statintr_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