1/* $NetBSD: timer_msiiep.c,v 1.27 2012/07/31 16:38:37 martin Exp $ */ 2 3/* 4 * Copyright (c) 1992, 1993 5 * The Regents of the University of California. All rights reserved. 6 * Copyright (c) 1994 Gordon W. Ross 7 * Copyright (c) 1993 Adam Glass 8 * Copyright (c) 1996 Paul Kranenburg 9 * Copyright (c) 1996 10 * The President and Fellows of Harvard College. All rights reserved. 11 * 12 * This software was developed by the Computer Systems Engineering group 13 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and 14 * contributed to Berkeley. 15 * 16 * All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by Harvard University. 19 * This product includes software developed by the University of 20 * California, Lawrence Berkeley Laboratory. 21 * 22 * Redistribution and use in source and binary forms, with or without 23 * modification, are permitted provided that the following conditions 24 * are met: 25 * 26 * 1. Redistributions of source code must retain the above copyright 27 * notice, this list of conditions and the following disclaimer. 28 * 2. Redistributions in binary form must reproduce the above copyright 29 * notice, this list of conditions and the following disclaimer in the 30 * documentation and/or other materials provided with the distribution. 31 * 3. All advertising materials mentioning features or use of this software 32 * must display the following acknowledgement: 33 * This product includes software developed by the University of 34 * California, Berkeley and its contributors. 35 * This product includes software developed by Paul Kranenburg. 36 * This product includes software developed by Harvard University. 37 * 4. Neither the name of the University nor the names of its contributors 38 * may be used to endorse or promote products derived from this software 39 * without specific prior written permission. 40 * 41 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 42 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 43 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 44 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 45 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 46 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 47 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 48 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 49 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 50 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 51 * SUCH DAMAGE. 52 * 53 * @(#)clock.c 8.1 (Berkeley) 6/11/93 54 */ 55 56/* 57 * MicroSPARC-IIep timer support. 58 */ 59 60#include <sys/cdefs.h> 61__KERNEL_RCSID(0, "$NetBSD: timer_msiiep.c,v 1.27 2012/07/31 16:38:37 martin Exp $"); 62 63#include <sys/param.h> 64#include <sys/kernel.h> 65#include <sys/device.h> 66#include <sys/systm.h> 67#include <sys/timetc.h> 68#include <sys/bus.h> 69#include <sys/intr.h> 70 71#include <sparc/sparc/msiiepreg.h> 72#include <sparc/sparc/msiiepvar.h> 73 74#include <sparc/sparc/timervar.h> 75 76 77static int timermatch_msiiep(device_t, cfdata_t, void *); 78static void timerattach_msiiep(device_t, device_t, void *); 79 80CFATTACH_DECL_NEW(timer_msiiep, 0, 81 timermatch_msiiep, timerattach_msiiep, NULL, NULL); 82 83 84static void timer_init_msiiep(void); 85static int clockintr_msiiep(void *); 86static int statintr_msiiep(void *); 87static u_int timer_get_timecount(struct timecounter *); 88 89void* sched_cookie; 90 91static struct intrhand level10 = { .ih_fun = clockintr_msiiep }; 92static struct intrhand level14 = { .ih_fun = statintr_msiiep }; 93 94/* 95 * timecounter local state 96 */ 97static struct counter { 98 u_int limit; /* limit we count up to */ 99 u_int offset; /* accumulated offet due to wraps */ 100} cntr; 101 102/* 103 * define timecounter 104 */ 105static struct timecounter counter_timecounter = { 106 .tc_get_timecount = timer_get_timecount, 107 .tc_poll_pps = NULL, 108 .tc_counter_mask = ~0u, 109 .tc_frequency = 25000000, /* Hz */ 110 .tc_name = "timer-counter", 111 .tc_quality = 100, 112 .tc_priv = &cntr, 113}; 114 115 116/* 117 * ms-IIep counters tick every 4 CPU clocks @100MHz. 118 * counter is reset to 1 when new limit is written. 119 */ 120#define tmr_ustolimIIep(n) ((n) * 25 + 1) 121 122 123static int 124timermatch_msiiep(device_t parent, cfdata_t cf, void *aux) 125{ 126 struct msiiep_attach_args *msa = aux; 127 128 return (strcmp(msa->msa_name, "timer") == 0); 129} 130 131 132/* 133 * Attach system and processor counters (kernel hard and stat clocks) 134 * for ms-IIep. Counters are part of the PCIC and there's no PROM 135 * node for them. 136 */ 137static void 138timerattach_msiiep(device_t parent, device_t self, void *aux) 139{ 140 141 /* Put processor counter in "counter" mode */ 142 mspcic_write_1(pcic_pc_ctl, 0); /* stop user timer (just in case) */ 143 mspcic_write_1(pcic_pc_cfg, 0); /* timer mode disabled */ 144 145 /* 146 * Calibrate delay() by tweaking the magic constant until 147 * delay(100) actually reads (at least) 100 us on the clock. 148 */ 149 for (timerblurb = 1; ; ++timerblurb) { 150 int t; 151 152 (void)mspcic_read_4(pcic_pclr); /* clear the limit bit */ 153 mspcic_write_4(pcic_pclr, 0); /* reset to 1, free run */ 154 delay(100); 155 t = mspcic_read_4(pcic_pccr); 156 if (t < 0) /* limit bit set, cannot happen */ 157 panic("delay calibration"); 158 159 if (t >= 2501) /* (t - 1) / 25 >= 100 us */ 160 break; 161 } 162 163 printf(": delay constant %d\n", timerblurb); 164 165 timer_init = timer_init_msiiep; 166 167 /* 168 * Set counter interrupt priority assignment: 169 * upper 4 bits are for system counter: level 10 170 * lower 4 bits are for processor counter: level 14 171 * 172 * XXX: ensure that interrupt target mask doesn't mask them? 173 */ 174 mspcic_write_1(pcic_cipar, 0xae); 175 176 /* link interrupt handlers */ 177 intr_establish(10, 0, &level10, NULL, false); 178 intr_establish(14, 0, &level14, NULL, false); 179 180 /* Establish a soft interrupt at a lower level for schedclock */ 181 sched_cookie = sparc_softintr_establish(IPL_SCHED, schedintr, NULL); 182 if (sched_cookie == NULL) 183 panic("timerattach: cannot establish schedintr"); 184} 185 186 187/* 188 * Set up the real-time and statistics clocks. 189 * Leave stathz 0 only if no alternative timer is available. 190 * 191 * The frequencies of these clocks must be an even number of microseconds. 192 */ 193static void 194timer_init_msiiep(void) 195{ 196 197 mspcic_write_4(pcic_sclr, tmr_ustolimIIep(tick)); 198 mspcic_write_4(pcic_pclr, tmr_ustolimIIep(statint)); 199 200 cntr.limit = tmr_ustolimIIep(tick); 201 tc_init(&counter_timecounter); 202} 203 204 205/* 206 * timer_get_timecount provide current counter value 207 */ 208static u_int 209timer_get_timecount(struct timecounter *tc) 210{ 211 struct counter *ctr = (struct counter *)tc->tc_priv; 212 u_int c, res; 213 int s; 214 215 s = splhigh(); 216 217 /* XXX: consider re-reading until increment is detected */ 218 c = mspcic_read_4(pcic_sccr); 219 220 res = c & ~MSIIEP_COUNTER_LIMIT; 221 if (res != c) 222 res += ctr->limit; 223 224 res += ctr->offset; 225 226 splx(s); 227 228 return res; 229} 230 231 232/* 233 * Level 10 (clock) interrupts from system counter. 234 */ 235static int 236clockintr_msiiep(void *cap) 237{ 238 (void)mspcic_read_4(pcic_sclr); /* clear the interrupt */ 239 240 /* 241 * XXX this needs to be fixed in a more general way 242 * problem is that the kernel enables interrupts and THEN 243 * sets up clocks. In between there's an opportunity to catch 244 * a timer interrupt - if we call hardclock() at that point we'll 245 * panic 246 * so for now just bail when cold 247 */ 248 if (__predict_false(cold)) 249 return 0; 250 251 if (timecounter->tc_get_timecount == timer_get_timecount) 252 cntr.offset += cntr.limit; 253 254 hardclock((struct clockframe *)cap); 255 return 1; 256} 257 258 259/* 260 * Level 14 (stat clock) interrupts from processor counter. 261 */ 262static int 263statintr_msiiep(void *cap) 264{ 265 struct clockframe *frame = cap; 266 u_long newint; 267 268 (void)mspcic_read_4(pcic_pclr); /* clear the interrupt */ 269 270 statclock(frame); 271 272 /* 273 * Compute new randomized interval. 274 */ 275 newint = new_interval(); 276 277 /* 278 * Use the `non-resetting' limit register, so that we don't 279 * lose the counter ticks that happened since this interrupt 280 * has been raised. 281 */ 282 mspcic_write_4(pcic_pclr_nr, tmr_ustolimIIep(newint)); 283 284 /* 285 * The factor 8 is only valid for stathz==100. 286 * See also clock.c 287 */ 288 if ((++cpuinfo.ci_schedstate.spc_schedticks & 7) == 0) { 289 if (CLKF_LOPRI(frame, IPL_SCHED)) { 290 /* No need to schedule a soft interrupt */ 291 spllowerschedclock(); 292 schedintr(cap); 293 } else { 294 /* 295 * We're interrupting a thread that may have the 296 * scheduler lock; run schedintr() later. 297 */ 298 sparc_softintr_schedule(sched_cookie); 299 } 300 } 301 302 return 1; 303} 304