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