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