1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <sys/param.h> 29 #include <sys/time.h> 30 #include <sys/systm.h> 31 #include <sys/cmn_err.h> 32 #include <sys/debug.h> 33 #include <sys/clock.h> 34 #include <sys/x_call.h> 35 #include <sys/cpuvar.h> 36 #include <sys/promif.h> 37 #include <sys/kmem.h> 38 #include <sys/machsystm.h> 39 #include <sys/ivintr.h> 40 #include <sys/cyclic.h> 41 #include <sys/cyclic_impl.h> 42 43 uint64_t cbe_level14_inum; 44 cyclic_id_t cbe_hres_cyclic; 45 46 static hrtime_t cbe_hrtime_max; 47 static hrtime_t cbe_suspend_delta = 0; 48 static hrtime_t cbe_suspend_time = 0; 49 50 static uint64_t 51 hrtime2tick(hrtime_t ts) 52 { 53 hrtime_t q = ts / NANOSEC; 54 hrtime_t r = ts - (q * NANOSEC); 55 56 return (q * sys_tick_freq + ((r * sys_tick_freq) / NANOSEC)); 57 } 58 59 static uint64_t 60 unscalehrtime(hrtime_t ts) 61 { 62 uint64_t unscale = 0; 63 hrtime_t rescale; 64 hrtime_t diff = ts; 65 66 while (diff > nsec_per_sys_tick) { 67 unscale += hrtime2tick(diff); 68 rescale = unscale; 69 scalehrtime(&rescale); 70 diff = ts - rescale; 71 } 72 73 return (unscale); 74 } 75 76 static int 77 cbe_level1() 78 { 79 cyclic_softint(CPU, CY_LOW_LEVEL); 80 return (1); 81 } 82 83 static int 84 cbe_level10() 85 { 86 cyclic_softint(CPU, CY_LOCK_LEVEL); 87 return (1); 88 } 89 90 /*ARGSUSED*/ 91 static void 92 cbe_enable(cyb_arg_t arg) 93 { 94 int pstate_save = disable_vec_intr(); 95 96 intr_enqueue_req(PIL_14, cbe_level14_inum); 97 enable_vec_intr(pstate_save); 98 } 99 100 /*ARGSUSED*/ 101 static void 102 cbe_disable(cyb_arg_t arg) 103 { 104 int pstate_save = disable_vec_intr(); 105 106 tickcmpr_disable(); 107 intr_dequeue_req(PIL_14, cbe_level14_inum); 108 enable_vec_intr(pstate_save); 109 } 110 111 /*ARGSUSED*/ 112 static void 113 cbe_reprogram(cyb_arg_t arg, hrtime_t time) 114 { 115 if (time >= cbe_hrtime_max) 116 time = cbe_hrtime_max; 117 118 tickcmpr_set(unscalehrtime(time)); 119 } 120 121 static void 122 cbe_softint(cyb_arg_t arg, cyc_level_t level) 123 { 124 cbe_data_t *data = (cbe_data_t *)arg; 125 126 switch (level) { 127 case CY_LOW_LEVEL: 128 setsoftint(data->cbe_level1_inum); 129 break; 130 case CY_LOCK_LEVEL: 131 setsoftint(data->cbe_level10_inum); 132 break; 133 default: 134 panic("cbe_softint: unexpected soft level %d", level); 135 } 136 } 137 138 /*ARGSUSED*/ 139 static cyc_cookie_t 140 cbe_set_level(cyb_arg_t arg, cyc_level_t level) 141 { 142 int ipl; 143 144 switch (level) { 145 case CY_LOW_LEVEL: 146 ipl = CBE_LOW_PIL; 147 break; 148 case CY_LOCK_LEVEL: 149 ipl = CBE_LOCK_PIL; 150 break; 151 case CY_HIGH_LEVEL: 152 ipl = CBE_HIGH_PIL; 153 break; 154 default: 155 panic("cbe_set_level: unexpected level %d", level); 156 } 157 158 return (splr(ipl)); 159 } 160 161 /*ARGSUSED*/ 162 static void 163 cbe_restore_level(cyb_arg_t arg, cyc_cookie_t cookie) 164 { 165 splx(cookie); 166 } 167 168 static void 169 cbe_xcall_handler(uint64_t arg1, uint64_t arg2) 170 { 171 cyc_func_t func = (cyc_func_t)arg1; 172 void *arg = (void *)arg2; 173 174 (*func)(arg); 175 } 176 177 /*ARGSUSED*/ 178 static void 179 cbe_xcall(cyb_arg_t arg, cpu_t *dest, cyc_func_t func, void *farg) 180 { 181 kpreempt_disable(); 182 xc_one(dest->cpu_id, cbe_xcall_handler, (uint64_t)func, (uint64_t)farg); 183 kpreempt_enable(); 184 } 185 186 /*ARGSUSED*/ 187 static cyb_arg_t 188 cbe_configure(cpu_t *cpu) 189 { 190 cbe_data_t *new_data = kmem_alloc(sizeof (cbe_data_t), KM_SLEEP); 191 192 /* 193 * The setsoftint() code will refuse to post a soft interrupt if 194 * one is already pending for the specified inum. Given that we 195 * may have disjoint soft interrupts on different CPUs posted 196 * simultaneously, we allocate a new set of inums for each CPU. 197 */ 198 new_data->cbe_level10_inum = add_softintr(PIL_10, 199 (softintrfunc)cbe_level10, 0, SOFTINT_ST); 200 201 new_data->cbe_level1_inum = add_softintr(PIL_1, 202 (softintrfunc)cbe_level1, 0, SOFTINT_ST); 203 204 return (new_data); 205 } 206 207 static void 208 cbe_unconfigure(cyb_arg_t arg) 209 { 210 cbe_data_t *data = (cbe_data_t *)arg; 211 212 (void) rem_softintr(data->cbe_level10_inum); 213 (void) rem_softintr(data->cbe_level1_inum); 214 215 kmem_free(data, sizeof (cbe_data_t)); 216 } 217 218 /*ARGSUSED*/ 219 static void 220 cbe_suspend(cyb_arg_t arg) 221 { 222 cbe_suspend_time = gethrtime_unscaled(); 223 cbe_suspend_delta = 0; 224 } 225 226 /*ARGSUSED*/ 227 static void 228 cbe_resume(cyb_arg_t arg) 229 { 230 hrtime_t now; 231 232 /* 233 * If we're actually on a CPU which has apparently had %tick zeroed, 234 * we want to add cbe_suspend_delta to %tick. 235 */ 236 if ((now = gethrtime_unscaled()) < cbe_suspend_time) { 237 238 if (cbe_suspend_delta == 0) { 239 /* 240 * We're the first CPU to be resumed. We want %tick 241 * to be close to %tick when we suspended the system, 242 * so we'll figure out the delta which needs to be 243 * written to the register. All subsequent resumed 244 * CPUs will write the same delta. 245 */ 246 cbe_suspend_delta = cbe_suspend_time - now; 247 } 248 249 tick_write_delta(cbe_suspend_delta); 250 } 251 } 252 253 void 254 cbe_hres_tick(void) 255 { 256 dtrace_hres_tick(); 257 hres_tick(); 258 } 259 260 void 261 cbe_init_pre(void) 262 { 263 /* Nothing to do on sparc */ 264 } 265 266 void 267 cbe_init(void) 268 { 269 cyc_handler_t hdlr; 270 cyc_time_t when; 271 hrtime_t resolution = NANOSEC / sys_tick_freq; 272 273 cyc_backend_t cbe = { 274 cbe_configure, /* cyb_configure */ 275 cbe_unconfigure, /* cyb_unconfigure */ 276 cbe_enable, /* cyb_enable */ 277 cbe_disable, /* cyb_disable */ 278 cbe_reprogram, /* cyb_reprogram */ 279 cbe_softint, /* cyb_softint */ 280 cbe_set_level, /* cyb_set_level */ 281 cbe_restore_level, /* cyb_restore_level */ 282 cbe_xcall, /* cyb_xcall */ 283 cbe_suspend, /* cyb_suspend */ 284 cbe_resume /* cyb_resume */ 285 }; 286 287 cbe_level14_inum = add_softintr(CBE_HIGH_PIL, 288 (softintrfunc)cbe_level14, 0, SOFTINT_MT); 289 cbe_hrtime_max = gethrtime_max(); 290 291 /* 292 * If sys_tick_freq > NANOSEC (i.e. we're on a CPU with a clock rate 293 * which exceeds 1 GHz), we'll specify the minimum resolution, 294 * 1 nanosecond. 295 */ 296 if (resolution == 0) 297 resolution = 1; 298 299 mutex_enter(&cpu_lock); 300 cyclic_init(&cbe, resolution); 301 302 /* 303 * Initialize hrtime_base and hres_last_tick to reasonable starting 304 * values. 305 */ 306 hrtime_base = gethrtime(); 307 hres_last_tick = gethrtime_unscaled(); 308 309 hdlr.cyh_level = CY_HIGH_LEVEL; 310 hdlr.cyh_func = (cyc_func_t)cbe_hres_tick; 311 hdlr.cyh_arg = NULL; 312 313 when.cyt_when = 0; 314 when.cyt_interval = nsec_per_tick; 315 316 cbe_hres_cyclic = cyclic_add(&hdlr, &when); 317 318 mutex_exit(&cpu_lock); 319 320 clkstart(); 321 } 322