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 2009 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 #include <sys/systm.h>
  28 #include <sys/cyclic.h>
  29 #include <sys/cyclic_impl.h>
  30 #include <sys/spl.h>
  31 #include <sys/x_call.h>
  32 #include <sys/kmem.h>
  33 #include <sys/machsystm.h>
  34 #include <sys/smp_impldefs.h>
  35 #include <sys/psm_types.h>
  36 #include <sys/psm.h>
  37 #include <sys/atomic.h>
  38 #include <sys/clock.h>
  39 #include <sys/x86_archext.h>
  40 #include <sys/ddi_impldefs.h>
  41 #include <sys/ddi_intr.h>
  42 #include <sys/avintr.h>
  43 #include <sys/note.h>
  44 
  45 static int cbe_vector;
  46 static int cbe_ticks = 0;
  47 
  48 /*
  49  * cbe_xcall_lock is used to protect the xcall globals since the cyclic
  50  * reprogramming API does not use cpu_lock.
  51  */
  52 static kmutex_t cbe_xcall_lock;
  53 static cyc_func_t volatile cbe_xcall_func;
  54 static cpu_t *volatile cbe_xcall_cpu;
  55 static void *cbe_xcall_farg;
  56 static cpuset_t cbe_enabled;
  57 
  58 static ddi_softint_hdl_impl_t cbe_low_hdl =
  59         {0, NULL, NULL, NULL, 0, NULL, NULL, NULL};
  60 static ddi_softint_hdl_impl_t cbe_clock_hdl =
  61         {0, NULL, NULL, NULL, 0, NULL, NULL, NULL};
  62 
  63 cyclic_id_t cbe_hres_cyclic;
  64 int cbe_psm_timer_mode = TIMER_ONESHOT;
  65 static hrtime_t cbe_timer_resolution;
  66 
  67 extern int tsc_gethrtime_enable;
  68 
  69 void cbe_hres_tick(void);
  70 
  71 int
  72 cbe_softclock(void)
  73 {
  74         cyclic_softint(CPU, CY_LOCK_LEVEL);
  75         return (1);
  76 }
  77 
  78 int
  79 cbe_low_level(void)
  80 {
  81         cpu_t *cpu = CPU;
  82 
  83         cyclic_softint(cpu, CY_LOW_LEVEL);
  84         return (1);
  85 }
  86 
  87 /*
  88  * We can be in cbe_fire() either due to a cyclic-induced cross call, or due
  89  * to the timer firing at level-14.  Because cyclic_fire() can tolerate
  90  * spurious calls, it would not matter if we called cyclic_fire() in both
  91  * cases.
  92  */
  93 int
  94 cbe_fire(void)
  95 {
  96         cpu_t *cpu = CPU;
  97         processorid_t me = cpu->cpu_id, i;
  98         int cross_call = (cbe_xcall_func != NULL && cbe_xcall_cpu == cpu);
  99 
 100         cyclic_fire(cpu);
 101 
 102         if (cbe_psm_timer_mode != TIMER_ONESHOT && me == 0 && !cross_call) {
 103                 for (i = 1; i < NCPU; i++) {
 104                         if (CPU_IN_SET(cbe_enabled, i)) {
 105                                 send_dirint(i, CBE_HIGH_PIL);
 106                         }
 107                 }
 108         }
 109 
 110         if (cross_call) {
 111                 ASSERT(cbe_xcall_func != NULL && cbe_xcall_cpu == cpu);
 112                 (*cbe_xcall_func)(cbe_xcall_farg);
 113                 cbe_xcall_func = NULL;
 114                 cbe_xcall_cpu = NULL;
 115         }
 116 
 117         return (1);
 118 }
 119 
 120 /*ARGSUSED*/
 121 void
 122 cbe_softint(void *arg, cyc_level_t level)
 123 {
 124         switch (level) {
 125         case CY_LOW_LEVEL:
 126                 (*setsoftint)(CBE_LOW_PIL, cbe_low_hdl.ih_pending);
 127                 break;
 128         case CY_LOCK_LEVEL:
 129                 (*setsoftint)(CBE_LOCK_PIL, cbe_clock_hdl.ih_pending);
 130                 break;
 131         default:
 132                 panic("cbe_softint: unexpected soft level %d", level);
 133         }
 134 }
 135 
 136 /*ARGSUSED*/
 137 void
 138 cbe_reprogram(void *arg, hrtime_t time)
 139 {
 140         if (cbe_psm_timer_mode == TIMER_ONESHOT)
 141                 (*psm_timer_reprogram)(time);
 142 }
 143 
 144 /*ARGSUSED*/
 145 cyc_cookie_t
 146 cbe_set_level(void *arg, cyc_level_t level)
 147 {
 148         int ipl;
 149 
 150         switch (level) {
 151         case CY_LOW_LEVEL:
 152                 ipl = CBE_LOW_PIL;
 153                 break;
 154         case CY_LOCK_LEVEL:
 155                 ipl = CBE_LOCK_PIL;
 156                 break;
 157         case CY_HIGH_LEVEL:
 158                 ipl = CBE_HIGH_PIL;
 159                 break;
 160         default:
 161                 panic("cbe_set_level: unexpected level %d", level);
 162         }
 163 
 164         return (splr(ipltospl(ipl)));
 165 }
 166 
 167 /*ARGSUSED*/
 168 void
 169 cbe_restore_level(void *arg, cyc_cookie_t cookie)
 170 {
 171         splx(cookie);
 172 }
 173 
 174 /*ARGSUSED*/
 175 void
 176 cbe_xcall(void *arg, cpu_t *dest, cyc_func_t func, void *farg)
 177 {
 178         kpreempt_disable();
 179 
 180         if (dest == CPU) {
 181                 (*func)(farg);
 182                 kpreempt_enable();
 183                 return;
 184         }
 185 
 186         mutex_enter(&cbe_xcall_lock);
 187 
 188         ASSERT(cbe_xcall_func == NULL);
 189 
 190         cbe_xcall_farg = farg;
 191         membar_producer();
 192         cbe_xcall_cpu = dest;
 193         cbe_xcall_func = func;
 194 
 195         send_dirint(dest->cpu_id, CBE_HIGH_PIL);
 196 
 197         while (cbe_xcall_func != NULL || cbe_xcall_cpu != NULL)
 198                 continue;
 199 
 200         mutex_exit(&cbe_xcall_lock);
 201 
 202         kpreempt_enable();
 203 }
 204 
 205 void *
 206 cbe_configure(cpu_t *cpu)
 207 {
 208         return (cpu);
 209 }
 210 
 211 void
 212 cbe_unconfigure(void *arg)
 213 {
 214         _NOTE(ARGUNUSED(arg));
 215         ASSERT(!CPU_IN_SET(cbe_enabled, ((cpu_t *)arg)->cpu_id));
 216 }
 217 
 218 #ifndef __xpv
 219 /*
 220  * declarations needed for time adjustment
 221  */
 222 extern void     tsc_suspend(void);
 223 extern void     tsc_resume(void);
 224 /*
 225  * Call the resume function in the cyclic, instead of inline in the
 226  * resume path.
 227  */
 228 extern int      tsc_resume_in_cyclic;
 229 #endif
 230 
 231 /*ARGSUSED*/
 232 static void
 233 cbe_suspend(cyb_arg_t arg)
 234 {
 235 #ifndef __xpv
 236         /*
 237          * This is an x86 backend, so let the tsc_suspend
 238          * that is specific to x86 platforms do the work.
 239          */
 240         tsc_suspend();
 241 #endif
 242 }
 243 
 244 /*ARGSUSED*/
 245 static void
 246 cbe_resume(cyb_arg_t arg)
 247 {
 248 #ifndef __xpv
 249         if (tsc_resume_in_cyclic) {
 250                 tsc_resume();
 251         }
 252 #endif
 253 }
 254 
 255 void
 256 cbe_enable(void *arg)
 257 {
 258         processorid_t me = ((cpu_t *)arg)->cpu_id;
 259 
 260         /* neither enable nor disable cpu0 if TIMER_PERIODIC is set */
 261         if ((cbe_psm_timer_mode != TIMER_ONESHOT) && (me == 0))
 262                 return;
 263 
 264         /*
 265          * Added (me == 0) to the ASSERT because the timer isn't
 266          * disabled on CPU 0, and cbe_enable is called when we resume.
 267          */
 268         ASSERT((me == 0) || !CPU_IN_SET(cbe_enabled, me));
 269         CPUSET_ADD(cbe_enabled, me);
 270         if (cbe_psm_timer_mode == TIMER_ONESHOT)
 271                 (*psm_timer_enable)();
 272 }
 273 
 274 void
 275 cbe_disable(void *arg)
 276 {
 277         processorid_t me = ((cpu_t *)arg)->cpu_id;
 278 
 279         /* neither enable nor disable cpu0 if TIMER_PERIODIC is set */
 280         if ((cbe_psm_timer_mode != TIMER_ONESHOT) && (me == 0))
 281                 return;
 282 
 283         ASSERT(CPU_IN_SET(cbe_enabled, me));
 284         CPUSET_DEL(cbe_enabled, me);
 285         if (cbe_psm_timer_mode == TIMER_ONESHOT)
 286                 (*psm_timer_disable)();
 287 }
 288 
 289 /*
 290  * Unbound cyclic, called once per tick (every nsec_per_tick ns).
 291  */
 292 void
 293 cbe_hres_tick(void)
 294 {
 295         int s;
 296 
 297         dtrace_hres_tick();
 298 
 299         /*
 300          * Because hres_tick effectively locks hres_lock, we must be at the
 301          * same PIL as that used for CLOCK_LOCK.
 302          */
 303         s = splr(ipltospl(XC_HI_PIL));
 304         hres_tick();
 305         splx(s);
 306 
 307         if ((cbe_ticks % hz) == 0)
 308                 (*hrtime_tick)();
 309 
 310         cbe_ticks++;
 311 
 312 }
 313 
 314 void
 315 cbe_init_pre(void)
 316 {
 317         cbe_vector = (*psm_get_clockirq)(CBE_HIGH_PIL);
 318 
 319         CPUSET_ZERO(cbe_enabled);
 320 
 321         cbe_timer_resolution = (*clkinitf)(TIMER_ONESHOT, &cbe_psm_timer_mode);
 322 }
 323 
 324 void
 325 cbe_init(void)
 326 {
 327         cyc_backend_t cbe = {
 328                 cbe_configure,          /* cyb_configure */
 329                 cbe_unconfigure,        /* cyb_unconfigure */
 330                 cbe_enable,             /* cyb_enable */
 331                 cbe_disable,            /* cyb_disable */
 332                 cbe_reprogram,          /* cyb_reprogram */
 333                 cbe_softint,            /* cyb_softint */
 334                 cbe_set_level,          /* cyb_set_level */
 335                 cbe_restore_level,      /* cyb_restore_level */
 336                 cbe_xcall,              /* cyb_xcall */
 337                 cbe_suspend,            /* cyb_suspend */
 338                 cbe_resume              /* cyb_resume */
 339         };
 340         cyc_handler_t hdlr;
 341         cyc_time_t when;
 342 
 343         mutex_init(&cbe_xcall_lock, NULL, MUTEX_DEFAULT, NULL);
 344 
 345         mutex_enter(&cpu_lock);
 346         cyclic_init(&cbe, cbe_timer_resolution);
 347         mutex_exit(&cpu_lock);
 348 
 349         (void) add_avintr(NULL, CBE_HIGH_PIL, (avfunc)cbe_fire,
 350             "cbe_fire_master", cbe_vector, 0, NULL, NULL, NULL);
 351 
 352         if (psm_get_ipivect != NULL) {
 353                 (void) add_avintr(NULL, CBE_HIGH_PIL, (avfunc)cbe_fire,
 354                     "cbe_fire_slave",
 355                     (*psm_get_ipivect)(CBE_HIGH_PIL, PSM_INTR_IPI_HI),
 356                     0, NULL, NULL, NULL);
 357         }
 358 
 359         (void) add_avsoftintr((void *)&cbe_clock_hdl, CBE_LOCK_PIL,
 360             (avfunc)cbe_softclock, "softclock", NULL, NULL);
 361 
 362         (void) add_avsoftintr((void *)&cbe_low_hdl, CBE_LOW_PIL,
 363             (avfunc)cbe_low_level, "low level", NULL, NULL);
 364 
 365         mutex_enter(&cpu_lock);
 366 
 367         hdlr.cyh_level = CY_HIGH_LEVEL;
 368         hdlr.cyh_func = (cyc_func_t)cbe_hres_tick;
 369         hdlr.cyh_arg = NULL;
 370 
 371         when.cyt_when = 0;
 372         when.cyt_interval = nsec_per_tick;
 373 
 374         cbe_hres_cyclic = cyclic_add(&hdlr, &when);
 375 
 376         if (psm_post_cyclic_setup != NULL)
 377                 (*psm_post_cyclic_setup)(NULL);
 378 
 379         mutex_exit(&cpu_lock);
 380 }