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 /* 23 * Copyright 2007 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/systm.h> 30 #include <sys/cyclic.h> 31 #include <sys/cyclic_impl.h> 32 #include <sys/spl.h> 33 #include <sys/x_call.h> 34 #include <sys/kmem.h> 35 #include <sys/machsystm.h> 36 #include <sys/smp_impldefs.h> 37 #include <sys/psm_types.h> 38 #include <sys/psm.h> 39 #include <sys/atomic.h> 40 #include <sys/clock.h> 41 #include <sys/ddi_impldefs.h> 42 #include <sys/ddi_intr.h> 43 #include <sys/avintr.h> 44 45 static int cbe_vector; 46 static int cbe_ticks = 0; 47 48 static cyc_func_t volatile cbe_xcall_func; 49 static cpu_t *volatile cbe_xcall_cpu; 50 static void *cbe_xcall_farg; 51 static cpuset_t cbe_enabled; 52 53 static ddi_softint_hdl_impl_t cbe_low_hdl = 54 {0, NULL, NULL, NULL, 0, NULL, NULL, NULL}; 55 static ddi_softint_hdl_impl_t cbe_clock_hdl = 56 {0, NULL, NULL, NULL, 0, NULL, NULL, NULL}; 57 58 cyclic_id_t cbe_hres_cyclic; 59 int cbe_psm_timer_mode = TIMER_ONESHOT; 60 61 void cbe_hres_tick(void); 62 63 int 64 cbe_softclock(void) 65 { 66 cyclic_softint(CPU, CY_LOCK_LEVEL); 67 return (1); 68 } 69 70 int 71 cbe_low_level(void) 72 { 73 cpu_t *cpu = CPU; 74 75 cyclic_softint(cpu, CY_LOW_LEVEL); 76 return (1); 77 } 78 79 /* 80 * We can be in cbe_fire() either due to a cyclic-induced cross call, or due 81 * to the timer firing at level-14. Because cyclic_fire() can tolerate 82 * spurious calls, it would not matter if we called cyclic_fire() in both 83 * cases. 84 */ 85 int 86 cbe_fire(void) 87 { 88 cpu_t *cpu = CPU; 89 processorid_t me = cpu->cpu_id, i; 90 int cross_call = (cbe_xcall_func != NULL && cbe_xcall_cpu == cpu); 91 92 cyclic_fire(cpu); 93 94 if (cbe_psm_timer_mode != TIMER_ONESHOT && me == 0 && !cross_call) { 95 for (i = 1; i < NCPU; i++) { 96 if (CPU_IN_SET(cbe_enabled, i)) { 97 XC_TRACE(TT_XC_CBE_FIRE, -1, i); 98 send_dirint(i, CBE_HIGH_PIL); 99 } 100 } 101 } 102 103 if (cross_call) { 104 ASSERT(cbe_xcall_func != NULL && cbe_xcall_cpu == cpu); 105 (*cbe_xcall_func)(cbe_xcall_farg); 106 cbe_xcall_func = NULL; 107 cbe_xcall_cpu = NULL; 108 } 109 110 return (1); 111 } 112 113 /*ARGSUSED*/ 114 void 115 cbe_softint(void *arg, cyc_level_t level) 116 { 117 switch (level) { 118 case CY_LOW_LEVEL: 119 (*setsoftint)(CBE_LOW_PIL, cbe_low_hdl.ih_pending); 120 break; 121 case CY_LOCK_LEVEL: 122 (*setsoftint)(CBE_LOCK_PIL, cbe_clock_hdl.ih_pending); 123 break; 124 default: 125 panic("cbe_softint: unexpected soft level %d", level); 126 } 127 } 128 129 /*ARGSUSED*/ 130 void 131 cbe_reprogram(void *arg, hrtime_t time) 132 { 133 if (cbe_psm_timer_mode == TIMER_ONESHOT) 134 (*psm_timer_reprogram)(time); 135 } 136 137 /*ARGSUSED*/ 138 cyc_cookie_t 139 cbe_set_level(void *arg, cyc_level_t level) 140 { 141 int ipl; 142 143 switch (level) { 144 case CY_LOW_LEVEL: 145 ipl = CBE_LOW_PIL; 146 break; 147 case CY_LOCK_LEVEL: 148 ipl = CBE_LOCK_PIL; 149 break; 150 case CY_HIGH_LEVEL: 151 ipl = CBE_HIGH_PIL; 152 break; 153 default: 154 panic("cbe_set_level: unexpected level %d", level); 155 } 156 157 return (splr(ipltospl(ipl))); 158 } 159 160 /*ARGSUSED*/ 161 void 162 cbe_restore_level(void *arg, cyc_cookie_t cookie) 163 { 164 splx(cookie); 165 } 166 167 /*ARGSUSED*/ 168 void 169 cbe_xcall(void *arg, cpu_t *dest, cyc_func_t func, void *farg) 170 { 171 kpreempt_disable(); 172 173 if (dest == CPU) { 174 (*func)(farg); 175 kpreempt_enable(); 176 return; 177 } 178 179 ASSERT(cbe_xcall_func == NULL); 180 181 cbe_xcall_farg = farg; 182 membar_producer(); 183 cbe_xcall_cpu = dest; 184 cbe_xcall_func = func; 185 186 XC_TRACE(TT_XC_CBE_XCALL, -1, dest->cpu_id); 187 send_dirint(dest->cpu_id, CBE_HIGH_PIL); 188 189 while (cbe_xcall_func != NULL || cbe_xcall_cpu != NULL) 190 continue; 191 192 kpreempt_enable(); 193 194 ASSERT(cbe_xcall_func == NULL && cbe_xcall_cpu == NULL); 195 } 196 197 void * 198 cbe_configure(cpu_t *cpu) 199 { 200 return (cpu); 201 } 202 203 void 204 cbe_enable(void *arg) 205 { 206 processorid_t me = ((cpu_t *)arg)->cpu_id; 207 208 /* neither enable nor disable cpu0 if TIMER_PERIODIC is set */ 209 if ((cbe_psm_timer_mode != TIMER_ONESHOT) && (me == 0)) 210 return; 211 212 ASSERT(!CPU_IN_SET(cbe_enabled, me)); 213 CPUSET_ADD(cbe_enabled, me); 214 if (cbe_psm_timer_mode == TIMER_ONESHOT) 215 (*psm_timer_enable)(); 216 } 217 218 void 219 cbe_disable(void *arg) 220 { 221 processorid_t me = ((cpu_t *)arg)->cpu_id; 222 223 /* neither enable nor disable cpu0 if TIMER_PERIODIC is set */ 224 if ((cbe_psm_timer_mode != TIMER_ONESHOT) && (me == 0)) 225 return; 226 227 ASSERT(CPU_IN_SET(cbe_enabled, me)); 228 CPUSET_DEL(cbe_enabled, me); 229 if (cbe_psm_timer_mode == TIMER_ONESHOT) 230 (*psm_timer_disable)(); 231 } 232 233 /* 234 * Unbound cyclic, called once per tick (every nsec_per_tick ns). 235 */ 236 void 237 cbe_hres_tick(void) 238 { 239 int s; 240 241 dtrace_hres_tick(); 242 243 /* 244 * Because hres_tick effectively locks hres_lock, we must be at the 245 * same PIL as that used for CLOCK_LOCK. 246 */ 247 s = splr(ipltospl(XC_HI_PIL)); 248 hres_tick(); 249 splx(s); 250 251 if ((cbe_ticks % hz) == 0) 252 (*hrtime_tick)(); 253 254 cbe_ticks++; 255 256 } 257 258 void 259 cbe_init(void) 260 { 261 cyc_backend_t cbe = { 262 cbe_configure, /* cyb_configure */ 263 NULL, /* cyb_unconfigure */ 264 cbe_enable, /* cyb_enable */ 265 cbe_disable, /* cyb_disable */ 266 cbe_reprogram, /* cyb_reprogram */ 267 cbe_softint, /* cyb_softint */ 268 cbe_set_level, /* cyb_set_level */ 269 cbe_restore_level, /* cyb_restore_level */ 270 cbe_xcall, /* cyb_xcall */ 271 NULL, /* cyb_suspend */ 272 NULL /* cyb_resume */ 273 }; 274 hrtime_t resolution; 275 cyc_handler_t hdlr; 276 cyc_time_t when; 277 278 cbe_vector = (*psm_get_clockirq)(CBE_HIGH_PIL); 279 280 CPUSET_ZERO(cbe_enabled); 281 282 resolution = (*clkinitf)(TIMER_ONESHOT, &cbe_psm_timer_mode); 283 284 mutex_enter(&cpu_lock); 285 cyclic_init(&cbe, resolution); 286 mutex_exit(&cpu_lock); 287 288 (void) add_avintr(NULL, CBE_HIGH_PIL, (avfunc)cbe_fire, 289 "cbe_fire_master", cbe_vector, 0, NULL, NULL, NULL); 290 291 if (psm_get_ipivect != NULL) { 292 (void) add_avintr(NULL, CBE_HIGH_PIL, (avfunc)cbe_fire, 293 "cbe_fire_slave", 294 (*psm_get_ipivect)(CBE_HIGH_PIL, PSM_INTR_IPI_HI), 295 0, NULL, NULL, NULL); 296 } 297 298 (void) add_avsoftintr((void *)&cbe_clock_hdl, CBE_LOCK_PIL, 299 (avfunc)cbe_softclock, "softclock", NULL, NULL); 300 301 (void) add_avsoftintr((void *)&cbe_low_hdl, CBE_LOW_PIL, 302 (avfunc)cbe_low_level, "low level", NULL, NULL); 303 304 mutex_enter(&cpu_lock); 305 306 hdlr.cyh_level = CY_HIGH_LEVEL; 307 hdlr.cyh_func = (cyc_func_t)cbe_hres_tick; 308 hdlr.cyh_arg = NULL; 309 310 when.cyt_when = 0; 311 when.cyt_interval = nsec_per_tick; 312 313 cbe_hres_cyclic = cyclic_add(&hdlr, &when); 314 315 if (psm_post_cyclic_setup != NULL) 316 (*psm_post_cyclic_setup)(NULL); 317 318 mutex_exit(&cpu_lock); 319 } 320