1 /* $NetBSD: clock_pcc.c,v 1.18 2008/01/12 09:54:21 tsutsui Exp $ */
4 * Copyright (c) 1996 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
33 * Glue for the Peripheral Channel Controller timers and the
34 * Mostek clock chip found on the MVME-147.
37 #include <sys/cdefs.h>
38 __KERNEL_RCSID(0, "$NetBSD: clock_pcc.c,v 1.18 2008/01/12 09:54:21 tsutsui Exp $");
40 #include <sys/param.h>
41 #include <sys/kernel.h>
42 #include <sys/systm.h>
43 #include <sys/device.h>
44 #include <sys/timetc.h>
46 #include <machine/psl.h>
47 #include <machine/bus.h>
49 #include <dev/mvme/clockvar.h>
51 #include <mvme68k/dev/pccreg.h>
52 #include <mvme68k/dev/pccvar.h>
56 int clock_pcc_match(struct device
*, struct cfdata
*, void *);
57 void clock_pcc_attach(struct device
*, struct device
*, void *);
59 struct clock_pcc_softc
{
61 struct clock_attach_args sc_clock_args
;
63 struct timecounter sc_tc
;
66 CFATTACH_DECL(clock_pcc
, sizeof(struct clock_pcc_softc
),
67 clock_pcc_match
, clock_pcc_attach
, NULL
, NULL
);
70 static int clock_pcc_profintr(void *);
71 static int clock_pcc_statintr(void *);
72 static void clock_pcc_initclocks(void *, int, int);
73 static u_int
clock_pcc_getcount(struct timecounter
*);
74 static void clock_pcc_shutdown(void *);
76 static struct clock_pcc_softc
*clock_pcc_sc
;
77 static uint32_t clock_pcc_count
;
78 static uint16_t clock_pcc_reload
;
82 clock_pcc_match(struct device
*parent
, struct cfdata
*cf
, void *aux
)
84 struct pcc_attach_args
*pa
;
88 /* Only one clock, please. */
92 if (strcmp(pa
->pa_name
, clock_cd
.cd_name
))
95 pa
->pa_ipl
= cf
->pcccf_ipl
;
102 clock_pcc_attach(struct device
*parent
, struct device
*self
, void *aux
)
104 struct pcc_attach_args
*pa
;
105 struct clock_pcc_softc
*sc
;
107 sc
= (struct clock_pcc_softc
*)self
;
110 if (pa
->pa_ipl
!= CLOCK_LEVEL
)
111 panic("clock_pcc_attach: wrong interrupt level");
114 sc
->sc_clock_args
.ca_arg
= sc
;
115 sc
->sc_clock_args
.ca_initfunc
= clock_pcc_initclocks
;
117 /* Do common portions of clock config. */
118 clock_config(self
, &sc
->sc_clock_args
, pccintr_evcnt(pa
->pa_ipl
));
120 /* Ensure our interrupts get disabled at shutdown time. */
121 (void)shutdownhook_establish(clock_pcc_shutdown
, NULL
);
123 /* Attach the interrupt handlers. */
124 pccintr_establish(PCCV_TIMER1
, clock_pcc_profintr
, pa
->pa_ipl
,
125 NULL
, &clock_profcnt
);
126 pccintr_establish(PCCV_TIMER2
, clock_pcc_statintr
, pa
->pa_ipl
,
127 NULL
, &clock_statcnt
);
128 sc
->sc_clock_lvl
= pa
->pa_ipl
| PCC_IENABLE
| PCC_TIMERACK
;
132 clock_pcc_initclocks(void *arg
, int prof_us
, int stat_us
)
134 struct clock_pcc_softc
*sc
= arg
;
136 clock_pcc_reload
= pcc_timer_us2lim(prof_us
);
137 pcc_reg_write16(sys_pcc
, PCCREG_TMR1_PRELOAD
, clock_pcc_reload
);
138 pcc_reg_write(sys_pcc
, PCCREG_TMR1_CONTROL
, PCC_TIMERCLEAR
);
139 pcc_reg_write(sys_pcc
, PCCREG_TMR1_CONTROL
, PCC_TIMERSTART
);
140 pcc_reg_write(sys_pcc
, PCCREG_TMR1_INTR_CTRL
, sc
->sc_clock_lvl
);
142 pcc_reg_write16(sys_pcc
, PCCREG_TMR2_PRELOAD
,
143 pcc_timer_us2lim(stat_us
));
144 pcc_reg_write(sys_pcc
, PCCREG_TMR2_CONTROL
, PCC_TIMERCLEAR
);
145 pcc_reg_write(sys_pcc
, PCCREG_TMR2_CONTROL
, PCC_TIMERSTART
);
146 pcc_reg_write(sys_pcc
, PCCREG_TMR2_INTR_CTRL
, sc
->sc_clock_lvl
);
148 sc
->sc_tc
.tc_get_timecount
= clock_pcc_getcount
;
149 sc
->sc_tc
.tc_name
= "pcc_count";
150 sc
->sc_tc
.tc_frequency
= PCC_TIMERFREQ
;
151 sc
->sc_tc
.tc_quality
= 100;
152 sc
->sc_tc
.tc_counter_mask
= ~0;
158 clock_pcc_getcount(struct timecounter
*tc
)
168 * There's no way to latch the counter and overflow registers
169 * without pausing the clock, so compensate for the possible
170 * race by checking for counter wrap-around and re-reading the
171 * overflow counter if necessary.
173 * Note: This only works because we're at splhigh().
175 tc1
= pcc_reg_read16(sys_pcc
, PCCREG_TMR1_COUNT
);
176 cr
= pcc_reg_read(sys_pcc
, PCCREG_TMR1_CONTROL
);
177 tc2
= pcc_reg_read16(sys_pcc
, PCCREG_TMR1_COUNT
);
179 cr
= pcc_reg_read(sys_pcc
, PCCREG_TMR1_CONTROL
);
182 cnt
= clock_pcc_count
;
184 /* XXX assume HZ == 100 */
185 cnt
+= (tc1
- clock_pcc_reload
) +
186 (PCC_TIMERFREQ
/ 100) * (cr
>> PCC_TIMEROVFLSHIFT
);
192 clock_pcc_profintr(void *frame
)
199 tc
= pcc_reg_read16(sys_pcc
, PCCREG_TMR1_COUNT
);
200 cr
= pcc_reg_read(sys_pcc
, PCCREG_TMR1_CONTROL
);
201 if (tc
> pcc_reg_read16(sys_pcc
, PCCREG_TMR1_COUNT
))
202 cr
= pcc_reg_read(sys_pcc
, PCCREG_TMR1_CONTROL
);
203 pcc_reg_write(sys_pcc
, PCCREG_TMR1_CONTROL
, PCC_TIMERSTART
);
204 pcc_reg_write(sys_pcc
, PCCREG_TMR1_INTR_CTRL
,
205 clock_pcc_sc
->sc_clock_lvl
);
208 for (cr
>>= PCC_TIMEROVFLSHIFT
; cr
; cr
--) {
209 /* XXX assume HZ == 100 */
210 clock_pcc_count
+= PCC_TIMERFREQ
/ 100;
218 clock_pcc_statintr(void *frame
)
221 /* Disable the timer interrupt while we handle it. */
222 pcc_reg_write(sys_pcc
, PCCREG_TMR2_INTR_CTRL
, 0);
224 statclock((struct clockframe
*) frame
);
226 pcc_reg_write16(sys_pcc
, PCCREG_TMR2_PRELOAD
,
227 pcc_timer_us2lim(CLOCK_NEWINT(clock_statvar
, clock_statmin
)));
228 pcc_reg_write(sys_pcc
, PCCREG_TMR2_CONTROL
, PCC_TIMERCLEAR
);
229 pcc_reg_write(sys_pcc
, PCCREG_TMR2_CONTROL
, PCC_TIMERSTART
);
231 pcc_reg_write(sys_pcc
, PCCREG_TMR2_INTR_CTRL
,
232 clock_pcc_sc
->sc_clock_lvl
);
239 clock_pcc_shutdown(void *arg
)
242 /* Make sure the timer interrupts are turned off. */
243 pcc_reg_write(sys_pcc
, PCCREG_TMR1_CONTROL
, PCC_TIMERCLEAR
);
244 pcc_reg_write(sys_pcc
, PCCREG_TMR1_INTR_CTRL
, 0);
245 pcc_reg_write(sys_pcc
, PCCREG_TMR2_CONTROL
, PCC_TIMERCLEAR
);
246 pcc_reg_write(sys_pcc
, PCCREG_TMR2_INTR_CTRL
, 0);