xref: /netbsd-src/sys/dev/mvme/clock_pcctwo.c (revision 8ac07aec990b9d2e483062509d0a9fa5b4f57cf2)
1 /*	$NetBSD: clock_pcctwo.c,v 1.12 2008/01/07 14:39:28 tsutsui Exp $	*/
2 
3 /*-
4  * Copyright (c) 1999, 2002 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Steve C. Woodford.
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 the NetBSD
21  *        Foundation, Inc. and its contributors.
22  * 4. Neither the name of The NetBSD Foundation nor the names of its
23  *    contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 
39 /*
40  * Glue for the Peripheral Channel Controller Two (PCCChip2) timers,
41  * the Memory Controller ASIC (MCchip, and the Mostek clock chip found
42  * on the MVME-1[67]7, MVME-1[67]2 and MVME-187 series of boards.
43  */
44 
45 #include <sys/cdefs.h>
46 __KERNEL_RCSID(0, "$NetBSD: clock_pcctwo.c,v 1.12 2008/01/07 14:39:28 tsutsui Exp $");
47 
48 #include <sys/param.h>
49 #include <sys/kernel.h>
50 #include <sys/systm.h>
51 #include <sys/device.h>
52 #include <sys/timetc.h>
53 
54 #include <machine/psl.h>
55 #include <sys/bus.h>
56 
57 #include <dev/mvme/clockvar.h>
58 #include <dev/mvme/pcctwovar.h>
59 #include <dev/mvme/pcctworeg.h>
60 
61 
62 int clock_pcctwo_match(struct device *, struct cfdata *, void *);
63 void clock_pcctwo_attach(struct device *, struct device *, void *);
64 
65 struct clock_pcctwo_softc {
66 	struct device sc_dev;
67 	struct clock_attach_args sc_clock_args;
68 	u_char sc_clock_lvl;
69 	struct timecounter sc_tc;
70 };
71 
72 CFATTACH_DECL(clock_pcctwo, sizeof(struct clock_pcctwo_softc),
73     clock_pcctwo_match, clock_pcctwo_attach, NULL, NULL);
74 
75 extern struct cfdriver clock_cd;
76 
77 static int clock_pcctwo_profintr(void *);
78 static int clock_pcctwo_statintr(void *);
79 static void clock_pcctwo_initclocks(void *, int, int);
80 static u_int clock_pcctwo_getcount(struct timecounter *);
81 static void clock_pcctwo_shutdown(void *);
82 
83 static struct clock_pcctwo_softc *clock_pcctwo_sc;
84 static uint32_t clock_pcctwo_count;
85 
86 /* ARGSUSED */
87 int
88 clock_pcctwo_match(parent, cf, aux)
89 	struct device *parent;
90 	struct cfdata *cf;
91 	void *aux;
92 {
93 	struct pcctwo_attach_args *pa = aux;
94 
95 	/* Only one clock, please. */
96 	if (clock_pcctwo_sc)
97 		return (0);
98 
99 	if (strcmp(pa->pa_name, clock_cd.cd_name))
100 		return (0);
101 
102 	pa->pa_ipl = cf->pcctwocf_ipl;
103 
104 	return (1);
105 }
106 
107 /* ARGSUSED */
108 void
109 clock_pcctwo_attach(parent, self, aux)
110 	struct device *parent;
111 	struct device *self;
112 	void *aux;
113 {
114 	struct clock_pcctwo_softc *sc;
115 	struct pcctwo_attach_args *pa;
116 
117 	sc = clock_pcctwo_sc = device_private(self);
118 	pa = aux;
119 
120 	if (pa->pa_ipl != CLOCK_LEVEL)
121 		panic("clock_pcctwo_attach: wrong interrupt level");
122 
123 	sc->sc_clock_args.ca_arg = sc;
124 	sc->sc_clock_args.ca_initfunc = clock_pcctwo_initclocks;
125 
126 	/* Do common portions of clock config. */
127 	clock_config(self, &sc->sc_clock_args, pcctwointr_evcnt(pa->pa_ipl));
128 
129 	/* Ensure our interrupts get disabled at shutdown time. */
130 	(void) shutdownhook_establish(clock_pcctwo_shutdown, NULL);
131 
132 	sc->sc_clock_lvl = (pa->pa_ipl & PCCTWO_ICR_LEVEL_MASK) |
133 	    PCCTWO_ICR_ICLR | PCCTWO_ICR_IEN;
134 
135 	/* Attach the interrupt handlers. */
136 	pcctwointr_establish(PCCTWOV_TIMER1, clock_pcctwo_profintr,
137 	    pa->pa_ipl, NULL, &clock_profcnt);
138 	pcctwointr_establish(PCCTWOV_TIMER2, clock_pcctwo_statintr,
139 	    pa->pa_ipl, NULL, &clock_statcnt);
140 }
141 
142 void
143 clock_pcctwo_initclocks(arg, prof_us, stat_us)
144 	void *arg;
145 	int prof_us;
146 	int stat_us;
147 {
148 	struct clock_pcctwo_softc *sc;
149 
150 	sc = arg;
151 
152 	pcc2_reg_write(sys_pcctwo, PCC2REG_TIMER1_CONTROL, PCCTWO_TT_CTRL_COVF);
153 	pcc2_reg_write32(sys_pcctwo, PCC2REG_TIMER1_COUNTER, 0);
154 	pcc2_reg_write32(sys_pcctwo, PCC2REG_TIMER1_COMPARE,
155 	    PCCTWO_US2LIM(prof_us));
156 	pcc2_reg_write(sys_pcctwo, PCC2REG_TIMER1_CONTROL,
157 	    PCCTWO_TT_CTRL_CEN | PCCTWO_TT_CTRL_COC | PCCTWO_TT_CTRL_COVF);
158 	pcc2_reg_write(sys_pcctwo, PCC2REG_TIMER1_ICSR, sc->sc_clock_lvl);
159 
160 	pcc2_reg_write(sys_pcctwo, PCC2REG_TIMER2_CONTROL, PCCTWO_TT_CTRL_COVF);
161 	pcc2_reg_write32(sys_pcctwo, PCC2REG_TIMER2_COUNTER, 0);
162 	pcc2_reg_write32(sys_pcctwo, PCC2REG_TIMER2_COMPARE,
163 	    PCCTWO_US2LIM(stat_us));
164 	pcc2_reg_write(sys_pcctwo, PCC2REG_TIMER2_CONTROL,
165 	    PCCTWO_TT_CTRL_CEN | PCCTWO_TT_CTRL_COC | PCCTWO_TT_CTRL_COVF);
166 	pcc2_reg_write(sys_pcctwo, PCC2REG_TIMER2_ICSR, sc->sc_clock_lvl);
167 
168 	sc->sc_tc.tc_get_timecount = clock_pcctwo_getcount;
169 	sc->sc_tc.tc_name = "pcctwo_count";
170 	sc->sc_tc.tc_frequency = PCCTWO_TIMERFREQ;
171 	sc->sc_tc.tc_quality = 100;
172 	sc->sc_tc.tc_counter_mask = ~0;
173 	tc_init(&sc->sc_tc);
174 }
175 
176 /* ARGSUSED */
177 u_int
178 clock_pcctwo_getcount(struct timecounter *tc)
179 {
180 	u_int cnt;
181 	uint32_t tc1, tc2;
182 	uint8_t cr;
183 	int s;
184 
185 	s = splhigh();
186 
187 	/*
188 	 * There's no way to latch the counter and overflow registers
189 	 * without pausing the clock, so compensate for the possible
190 	 * race by checking for counter wrap-around and re-reading the
191 	 * overflow counter if necessary.
192 	 *
193 	 * Note: This only works because we're at splhigh().
194 	 */
195 	tc1 = pcc2_reg_read32(sys_pcctwo, PCC2REG_TIMER1_COUNTER);
196 	cr = pcc2_reg_read(sys_pcctwo, PCC2REG_TIMER1_CONTROL);
197 	tc2 = pcc2_reg_read32(sys_pcctwo, PCC2REG_TIMER1_COUNTER);
198 	if (tc1 > tc2) {
199 		cr = pcc2_reg_read(sys_pcctwo, PCC2REG_TIMER1_CONTROL);
200 		tc1 = tc2;
201 	}
202 	cnt = clock_pcctwo_count;
203 	splx(s);
204 	/* XXX assume HZ == 100 */
205 	cnt += tc1 + (PCCTWO_TIMERFREQ / 100) * PCCTWO_TT_CTRL_OVF(cr);
206 
207 	return cnt;
208 }
209 
210 int
211 clock_pcctwo_profintr(frame)
212 	void *frame;
213 {
214 	u_int8_t cr;
215 	u_int32_t tc;
216 	int s;
217 
218 	s = splhigh();
219 	tc = pcc2_reg_read32(sys_pcctwo, PCC2REG_TIMER1_COUNTER);
220 	cr = pcc2_reg_read(sys_pcctwo, PCC2REG_TIMER1_CONTROL);
221 	if (tc > pcc2_reg_read32(sys_pcctwo, PCC2REG_TIMER1_COUNTER))
222 		cr = pcc2_reg_read(sys_pcctwo, PCC2REG_TIMER1_CONTROL);
223 	pcc2_reg_write(sys_pcctwo, PCC2REG_TIMER1_CONTROL,
224 	    PCCTWO_TT_CTRL_CEN | PCCTWO_TT_CTRL_COC | PCCTWO_TT_CTRL_COVF);
225 	pcc2_reg_write(sys_pcctwo, PCC2REG_TIMER1_ICSR,
226 	    clock_pcctwo_sc->sc_clock_lvl);
227 	splx(s);
228 
229 	for (cr = PCCTWO_TT_CTRL_OVF(cr); cr; cr--) {
230 		/* XXX assume HZ == 100 */
231 		clock_pcctwo_count += PCCTWO_TIMERFREQ / 100;
232 		hardclock(frame);
233 	}
234 
235 	return (1);
236 }
237 
238 int
239 clock_pcctwo_statintr(frame)
240 	void *frame;
241 {
242 
243 	/* Disable the timer interrupt while we handle it. */
244 	pcc2_reg_write(sys_pcctwo, PCC2REG_TIMER2_ICSR, 0);
245 
246 	statclock((struct clockframe *) frame);
247 
248 	pcc2_reg_write(sys_pcctwo, PCC2REG_TIMER2_CONTROL, PCCTWO_TT_CTRL_COVF);
249 	pcc2_reg_write32(sys_pcctwo, PCC2REG_TIMER2_COUNTER, 0);
250 	pcc2_reg_write32(sys_pcctwo, PCC2REG_TIMER2_COMPARE,
251 	    PCCTWO_US2LIM(CLOCK_NEWINT(clock_statvar, clock_statmin)));
252 	pcc2_reg_write(sys_pcctwo, PCC2REG_TIMER2_CONTROL,
253 	    PCCTWO_TT_CTRL_CEN | PCCTWO_TT_CTRL_COC | PCCTWO_TT_CTRL_COVF);
254 
255 	pcc2_reg_write(sys_pcctwo, PCC2REG_TIMER2_ICSR,
256 	    clock_pcctwo_sc->sc_clock_lvl);
257 
258 	return (1);
259 }
260 
261 /* ARGSUSED */
262 void
263 clock_pcctwo_shutdown(arg)
264 	void *arg;
265 {
266 
267 	/* Make sure the timer interrupts are turned off. */
268 	pcc2_reg_write(sys_pcctwo, PCC2REG_TIMER1_CONTROL, PCCTWO_TT_CTRL_COVF);
269 	pcc2_reg_write(sys_pcctwo, PCC2REG_TIMER1_ICSR, 0);
270 	pcc2_reg_write(sys_pcctwo, PCC2REG_TIMER2_CONTROL, PCCTWO_TT_CTRL_COVF);
271 	pcc2_reg_write(sys_pcctwo, PCC2REG_TIMER2_ICSR, 0);
272 }
273