Home | History | Annotate | Line # | Download | only in dev
      1 /*	$NetBSD: clock_pcc.c,v 1.20 2012/10/27 17:18:04 chs 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  *
     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.
     30  */
     31 
     32 /*
     33  * Glue for the Peripheral Channel Controller timers and the
     34  * Mostek clock chip found on the MVME-147.
     35  */
     36 
     37 #include <sys/cdefs.h>
     38 __KERNEL_RCSID(0, "$NetBSD: clock_pcc.c,v 1.20 2012/10/27 17:18:04 chs Exp $");
     39 
     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>
     45 
     46 #include <machine/psl.h>
     47 #include <machine/bus.h>
     48 
     49 #include <dev/mvme/clockvar.h>
     50 
     51 #include <mvme68k/dev/pccreg.h>
     52 #include <mvme68k/dev/pccvar.h>
     53 
     54 #include "ioconf.h"
     55 
     56 int clock_pcc_match(device_t, cfdata_t, void *);
     57 void clock_pcc_attach(device_t, device_t, void *);
     58 
     59 struct clock_pcc_softc {
     60 	struct clock_attach_args sc_clock_args;
     61 	u_char sc_clock_lvl;
     62 	struct timecounter sc_tc;
     63 };
     64 
     65 CFATTACH_DECL_NEW(clock_pcc, sizeof(struct clock_pcc_softc),
     66     clock_pcc_match, clock_pcc_attach, NULL, NULL);
     67 
     68 
     69 static int clock_pcc_profintr(void *);
     70 static int clock_pcc_statintr(void *);
     71 static void clock_pcc_initclocks(void *, int, int);
     72 static u_int clock_pcc_getcount(struct timecounter *);
     73 static void clock_pcc_shutdown(void *);
     74 
     75 static struct clock_pcc_softc *clock_pcc_sc;
     76 static uint32_t clock_pcc_count;
     77 static uint16_t clock_pcc_reload;
     78 
     79 /* ARGSUSED */
     80 int
     81 clock_pcc_match(device_t parent, cfdata_t cf, void *aux)
     82 {
     83 	struct pcc_attach_args *pa;
     84 
     85 	pa = aux;
     86 
     87 	/* Only one clock, please. */
     88 	if (clock_pcc_sc)
     89 		return 0;
     90 
     91 	if (strcmp(pa->pa_name, clock_cd.cd_name))
     92 		return 0;
     93 
     94 	pa->pa_ipl = cf->pcccf_ipl;
     95 
     96 	return 1;
     97 }
     98 
     99 /* ARGSUSED */
    100 void
    101 clock_pcc_attach(device_t parent, device_t self, void *aux)
    102 {
    103 	struct pcc_attach_args *pa;
    104 	struct clock_pcc_softc *sc;
    105 
    106 	sc = device_private(self);
    107 	pa = aux;
    108 
    109 	if (pa->pa_ipl != CLOCK_LEVEL)
    110 		panic("clock_pcc_attach: wrong interrupt level");
    111 
    112 	clock_pcc_sc = sc;
    113 	sc->sc_clock_args.ca_arg = sc;
    114 	sc->sc_clock_args.ca_initfunc = clock_pcc_initclocks;
    115 
    116 	/* Do common portions of clock config. */
    117 	clock_config(self, &sc->sc_clock_args, pccintr_evcnt(pa->pa_ipl));
    118 
    119 	/* Ensure our interrupts get disabled at shutdown time. */
    120 	(void)shutdownhook_establish(clock_pcc_shutdown, NULL);
    121 
    122 	/* Attach the interrupt handlers. */
    123 	pccintr_establish(PCCV_TIMER1, clock_pcc_profintr, pa->pa_ipl,
    124 	    NULL, &clock_profcnt);
    125 	pccintr_establish(PCCV_TIMER2, clock_pcc_statintr, pa->pa_ipl,
    126 	    NULL, &clock_statcnt);
    127 	sc->sc_clock_lvl = pa->pa_ipl | PCC_IENABLE | PCC_TIMERACK;
    128 }
    129 
    130 void
    131 clock_pcc_initclocks(void *arg, int prof_us, int stat_us)
    132 {
    133 	struct clock_pcc_softc *sc = arg;
    134 
    135 	clock_pcc_reload = pcc_timer_us2lim(prof_us);
    136 	pcc_reg_write16(sys_pcc, PCCREG_TMR1_PRELOAD, clock_pcc_reload);
    137 	pcc_reg_write(sys_pcc, PCCREG_TMR1_CONTROL, PCC_TIMERCLEAR);
    138 	pcc_reg_write(sys_pcc, PCCREG_TMR1_CONTROL, PCC_TIMERSTART);
    139 	pcc_reg_write(sys_pcc, PCCREG_TMR1_INTR_CTRL, sc->sc_clock_lvl);
    140 
    141 	pcc_reg_write16(sys_pcc, PCCREG_TMR2_PRELOAD,
    142 	    pcc_timer_us2lim(stat_us));
    143 	pcc_reg_write(sys_pcc, PCCREG_TMR2_CONTROL, PCC_TIMERCLEAR);
    144 	pcc_reg_write(sys_pcc, PCCREG_TMR2_CONTROL, PCC_TIMERSTART);
    145 	pcc_reg_write(sys_pcc, PCCREG_TMR2_INTR_CTRL, sc->sc_clock_lvl);
    146 
    147 	sc->sc_tc.tc_get_timecount = clock_pcc_getcount;
    148 	sc->sc_tc.tc_name = "pcc_count";
    149 	sc->sc_tc.tc_frequency = PCC_TIMERFREQ;
    150 	sc->sc_tc.tc_quality = 100;
    151 	sc->sc_tc.tc_counter_mask = ~0;
    152 	tc_init(&sc->sc_tc);
    153 }
    154 
    155 /* ARGSUSED */
    156 u_int
    157 clock_pcc_getcount(struct timecounter *tc)
    158 {
    159 	u_int cnt;
    160 	uint16_t tc1, tc2;
    161 	uint8_t cr;
    162 	int s;
    163 
    164 	s = splhigh();
    165 
    166 	/*
    167 	 * There's no way to latch the counter and overflow registers
    168 	 * without pausing the clock, so compensate for the possible
    169 	 * race by checking for counter wrap-around and re-reading the
    170 	 * overflow counter if necessary.
    171 	 *
    172 	 * Note: This only works because we're at splhigh().
    173 	 */
    174 	tc1 = pcc_reg_read16(sys_pcc, PCCREG_TMR1_COUNT);
    175 	cr = pcc_reg_read(sys_pcc, PCCREG_TMR1_CONTROL);
    176 	tc2 = pcc_reg_read16(sys_pcc, PCCREG_TMR1_COUNT);
    177 	if (tc1 > tc2) {
    178 		cr = pcc_reg_read(sys_pcc, PCCREG_TMR1_CONTROL);
    179 		tc1 = tc2;
    180 	}
    181 	cnt = clock_pcc_count;
    182 	splx(s);
    183 	/* XXX assume HZ == 100 */
    184 	cnt += (tc1 - clock_pcc_reload) +
    185 	    (PCC_TIMERFREQ / 100) * (cr >> PCC_TIMEROVFLSHIFT);
    186 
    187 	return cnt;
    188 }
    189 
    190 int
    191 clock_pcc_profintr(void *frame)
    192 {
    193 	uint8_t cr;
    194 	uint16_t tc;
    195 	int s;
    196 
    197 	s = splhigh();
    198 	tc = pcc_reg_read16(sys_pcc, PCCREG_TMR1_COUNT);
    199 	cr = pcc_reg_read(sys_pcc, PCCREG_TMR1_CONTROL);
    200 	if (tc > pcc_reg_read16(sys_pcc, PCCREG_TMR1_COUNT))
    201 		cr = pcc_reg_read(sys_pcc, PCCREG_TMR1_CONTROL);
    202 	pcc_reg_write(sys_pcc, PCCREG_TMR1_CONTROL, PCC_TIMERSTART);
    203 	pcc_reg_write(sys_pcc, PCCREG_TMR1_INTR_CTRL,
    204 	    clock_pcc_sc->sc_clock_lvl);
    205 	splx(s);
    206 
    207 	for (cr >>= PCC_TIMEROVFLSHIFT; cr; cr--) {
    208 		/* XXX assume HZ == 100 */
    209 		clock_pcc_count += PCC_TIMERFREQ / 100;
    210 		hardclock(frame);
    211 	}
    212 
    213 	return 1;
    214 }
    215 
    216 int
    217 clock_pcc_statintr(void *frame)
    218 {
    219 
    220 	/* Disable the timer interrupt while we handle it. */
    221 	pcc_reg_write(sys_pcc, PCCREG_TMR2_INTR_CTRL, 0);
    222 
    223 	statclock((struct clockframe *) frame);
    224 
    225 	pcc_reg_write16(sys_pcc, PCCREG_TMR2_PRELOAD,
    226 	    pcc_timer_us2lim(CLOCK_NEWINT(clock_statvar, clock_statmin)));
    227 	pcc_reg_write(sys_pcc, PCCREG_TMR2_CONTROL, PCC_TIMERCLEAR);
    228 	pcc_reg_write(sys_pcc, PCCREG_TMR2_CONTROL, PCC_TIMERSTART);
    229 
    230 	pcc_reg_write(sys_pcc, PCCREG_TMR2_INTR_CTRL,
    231 	    clock_pcc_sc->sc_clock_lvl);
    232 
    233 	return 1;
    234 }
    235 
    236 /* ARGSUSED */
    237 void
    238 clock_pcc_shutdown(void *arg)
    239 {
    240 
    241 	/* Make sure the timer interrupts are turned off. */
    242 	pcc_reg_write(sys_pcc, PCCREG_TMR1_CONTROL, PCC_TIMERCLEAR);
    243 	pcc_reg_write(sys_pcc, PCCREG_TMR1_INTR_CTRL, 0);
    244 	pcc_reg_write(sys_pcc, PCCREG_TMR2_CONTROL, PCC_TIMERCLEAR);
    245 	pcc_reg_write(sys_pcc, PCCREG_TMR2_INTR_CTRL, 0);
    246 }
    247