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