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