xref: /netbsd-src/sys/arch/mvme68k/dev/clock_pcc.c (revision 8b0f9554ff8762542c4defc4f70e1eb76fb508fa)
1 /*	$NetBSD: clock_pcc.c,v 1.16 2005/12/11 12:18:17 christos Exp $	*/
2 
3 /*-
4  * Copyright (c) 1996 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Jason R. Thorpe.
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 timers and the
41  * Mostek clock chip found on the MVME-147.
42  */
43 
44 #include <sys/cdefs.h>
45 __KERNEL_RCSID(0, "$NetBSD: clock_pcc.c,v 1.16 2005/12/11 12:18:17 christos Exp $");
46 
47 #include <sys/param.h>
48 #include <sys/kernel.h>
49 #include <sys/systm.h>
50 #include <sys/device.h>
51 
52 #include <machine/psl.h>
53 #include <machine/bus.h>
54 
55 #include <dev/mvme/clockvar.h>
56 
57 #include <mvme68k/dev/pccreg.h>
58 #include <mvme68k/dev/pccvar.h>
59 
60 int clock_pcc_match __P((struct device *, struct cfdata *, void *));
61 void clock_pcc_attach __P((struct device *, struct device *, void *));
62 
63 struct clock_pcc_softc {
64 	struct device sc_dev;
65 	struct clock_attach_args sc_clock_args;
66 	u_char sc_clock_lvl;
67 };
68 
69 CFATTACH_DECL(clock_pcc, sizeof(struct clock_pcc_softc),
70     clock_pcc_match, clock_pcc_attach, NULL, NULL);
71 
72 extern struct cfdriver clock_cd;
73 
74 
75 static int clock_pcc_profintr __P((void *));
76 static int clock_pcc_statintr __P((void *));
77 static void clock_pcc_initclocks __P((void *, int, int));
78 static long clock_pcc_microtime __P((void *));
79 static void clock_pcc_shutdown __P((void *));
80 
81 static struct clock_pcc_softc *clock_pcc_sc;
82 
83 /* ARGSUSED */
84 int
85 clock_pcc_match(parent, cf, aux)
86 	struct device *parent;
87 	struct cfdata *cf;
88 	void *aux;
89 {
90 	struct pcc_attach_args *pa;
91 
92 	pa = aux;
93 
94 	/* Only one clock, please. */
95 	if (clock_pcc_sc)
96 		return (0);
97 
98 	if (strcmp(pa->pa_name, clock_cd.cd_name))
99 		return (0);
100 
101 	pa->pa_ipl = cf->pcccf_ipl;
102 
103 	return (1);
104 }
105 
106 /* ARGSUSED */
107 void
108 clock_pcc_attach(parent, self, aux)
109 	struct device *parent;
110 	struct device *self;
111 	void *aux;
112 {
113 	struct pcc_attach_args *pa;
114 	struct clock_pcc_softc *sc;
115 
116 	sc = (struct clock_pcc_softc *) self;
117 	pa = aux;
118 
119 	if (pa->pa_ipl != CLOCK_LEVEL)
120 		panic("clock_pcc_attach: wrong interrupt level");
121 
122 	clock_pcc_sc = sc;
123 	sc->sc_clock_args.ca_arg = sc;
124 	sc->sc_clock_args.ca_initfunc = clock_pcc_initclocks;
125 	sc->sc_clock_args.ca_microtime = clock_pcc_microtime;
126 
127 	/* Do common portions of clock config. */
128 	clock_config(self, &sc->sc_clock_args, pccintr_evcnt(pa->pa_ipl));
129 
130 	/* Ensure our interrupts get disabled at shutdown time. */
131 	(void) shutdownhook_establish(clock_pcc_shutdown, NULL);
132 
133 	/* Attach the interrupt handlers. */
134 	pccintr_establish(PCCV_TIMER1, clock_pcc_profintr, pa->pa_ipl,
135 	    NULL, &clock_profcnt);
136 	pccintr_establish(PCCV_TIMER2, clock_pcc_statintr, pa->pa_ipl,
137 	    NULL, &clock_statcnt);
138 	sc->sc_clock_lvl = pa->pa_ipl | PCC_IENABLE | PCC_TIMERACK;
139 }
140 
141 void
142 clock_pcc_initclocks(arg, prof_us, stat_us)
143 	void *arg;
144 	int prof_us;
145 	int stat_us;
146 {
147 	struct clock_pcc_softc *sc = arg;
148 
149 	pcc_reg_write16(sys_pcc, PCCREG_TMR1_PRELOAD,
150 	    pcc_timer_us2lim(prof_us));
151 	pcc_reg_write(sys_pcc, PCCREG_TMR1_CONTROL, PCC_TIMERCLEAR);
152 	pcc_reg_write(sys_pcc, PCCREG_TMR1_CONTROL, PCC_TIMERSTART);
153 	pcc_reg_write(sys_pcc, PCCREG_TMR1_INTR_CTRL, sc->sc_clock_lvl);
154 
155 	pcc_reg_write16(sys_pcc, PCCREG_TMR2_PRELOAD,
156 	    pcc_timer_us2lim(stat_us));
157 	pcc_reg_write(sys_pcc, PCCREG_TMR2_CONTROL, PCC_TIMERCLEAR);
158 	pcc_reg_write(sys_pcc, PCCREG_TMR2_CONTROL, PCC_TIMERSTART);
159 	pcc_reg_write(sys_pcc, PCCREG_TMR2_INTR_CTRL, sc->sc_clock_lvl);
160 }
161 
162 /* ARGSUSED */
163 long
164 clock_pcc_microtime(arg)
165 	void *arg;
166 {
167 	static int ovfl_adj[] = {
168 		0,       10000,  20000,  30000,
169 		40000,   50000,  60000,  70000,
170 		80000,   90000, 100000, 110000,
171 		120000, 130000, 140000, 150000};
172 	u_int8_t cr;
173 	u_int16_t tc, tc2;
174 
175 	/*
176 	 * There's no way to latch the counter and overflow registers
177 	 * without pausing the clock, so compensate for the possible
178 	 * race by checking for counter wrap-around and re-reading the
179 	 * overflow counter if necessary.
180 	 *
181 	 * Note: This only works because we're called at splhigh().
182 	 */
183 	tc = pcc_reg_read16(sys_pcc, PCCREG_TMR1_COUNT);
184 	cr = pcc_reg_read(sys_pcc, PCCREG_TMR1_CONTROL);
185 	if (tc > (tc2 = pcc_reg_read16(sys_pcc, PCCREG_TMR1_COUNT))) {
186 		cr = pcc_reg_read(sys_pcc, PCCREG_TMR1_CONTROL);
187 		tc = tc2;
188 	}
189 
190 	return ((long) pcc_timer_cnt2us(tc) + ovfl_adj[cr>>PCC_TIMEROVFLSHIFT]);
191 }
192 
193 int
194 clock_pcc_profintr(frame)
195 	void *frame;
196 {
197 	u_int8_t cr;
198 	u_int16_t tc;
199 	int s;
200 
201 	s = splhigh();
202 	tc = pcc_reg_read16(sys_pcc, PCCREG_TMR1_COUNT);
203 	cr = pcc_reg_read(sys_pcc, PCCREG_TMR1_CONTROL);
204 	if (tc > pcc_reg_read16(sys_pcc, PCCREG_TMR1_COUNT))
205 		cr = pcc_reg_read(sys_pcc, PCCREG_TMR1_CONTROL);
206 	pcc_reg_write(sys_pcc, PCCREG_TMR1_CONTROL, PCC_TIMERSTART);
207 	pcc_reg_write(sys_pcc, PCCREG_TMR1_INTR_CTRL,
208 	    clock_pcc_sc->sc_clock_lvl);
209 	splx(s);
210 
211 	for (cr >>= PCC_TIMEROVFLSHIFT; cr; cr--)
212 		hardclock(frame);
213 
214 	return (1);
215 }
216 
217 int
218 clock_pcc_statintr(frame)
219 	void *frame;
220 {
221 
222 	/* Disable the timer interrupt while we handle it. */
223 	pcc_reg_write(sys_pcc, PCCREG_TMR2_INTR_CTRL, 0);
224 
225 	statclock((struct clockframe *) frame);
226 
227 	pcc_reg_write16(sys_pcc, PCCREG_TMR2_PRELOAD,
228 	    pcc_timer_us2lim(CLOCK_NEWINT(clock_statvar, clock_statmin)));
229 	pcc_reg_write(sys_pcc, PCCREG_TMR2_CONTROL, PCC_TIMERCLEAR);
230 	pcc_reg_write(sys_pcc, PCCREG_TMR2_CONTROL, PCC_TIMERSTART);
231 
232 	pcc_reg_write(sys_pcc, PCCREG_TMR2_INTR_CTRL,
233 	    clock_pcc_sc->sc_clock_lvl);
234 
235 	return (1);
236 }
237 
238 /* ARGSUSED */
239 void
240 clock_pcc_shutdown(arg)
241 	void *arg;
242 {
243 
244 	/* Make sure the timer interrupts are turned off. */
245 	pcc_reg_write(sys_pcc, PCCREG_TMR1_CONTROL, PCC_TIMERCLEAR);
246 	pcc_reg_write(sys_pcc, PCCREG_TMR1_INTR_CTRL, 0);
247 	pcc_reg_write(sys_pcc, PCCREG_TMR2_CONTROL, PCC_TIMERCLEAR);
248 	pcc_reg_write(sys_pcc, PCCREG_TMR2_INTR_CTRL, 0);
249 }
250