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.
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.
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]
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #include <sys/systm.h>
28 #include <sys/cyclic.h>
29 #include <sys/cyclic_impl.h>
31 #include <sys/x_call.h>
33 #include <sys/machsystm.h>
34 #include <sys/smp_impldefs.h>
35 #include <sys/psm_types.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>
45 static int cbe_vector
;
46 static int cbe_ticks
= 0;
49 * cbe_xcall_lock is used to protect the xcall globals since the cyclic
50 * reprogramming API does not use cpu_lock.
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
;
58 static ddi_softint_hdl_impl_t cbe_low_hdl
=
59 {NULL
, 0, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
};
60 static ddi_softint_hdl_impl_t cbe_clock_hdl
=
61 {NULL
, 0, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
};
63 cyclic_id_t cbe_hres_cyclic
;
64 int cbe_psm_timer_mode
= TIMER_ONESHOT
;
65 static hrtime_t cbe_timer_resolution
;
67 extern int tsc_gethrtime_enable
;
69 void cbe_hres_tick(void);
74 cyclic_softint(CPU
, CY_LOCK_LEVEL
);
83 cyclic_softint(cpu
, CY_LOW_LEVEL
);
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
97 processorid_t me
= cpu
->cpu_id
, i
;
98 int cross_call
= (cbe_xcall_func
!= NULL
&& cbe_xcall_cpu
== cpu
);
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
);
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
;
122 cbe_softint(void *arg
, cyc_level_t level
)
126 (*setsoftint
)(CBE_LOW_PIL
, cbe_low_hdl
.ih_pending
);
129 (*setsoftint
)(CBE_LOCK_PIL
, cbe_clock_hdl
.ih_pending
);
132 panic("cbe_softint: unexpected soft level %d", level
);
138 cbe_reprogram(void *arg
, hrtime_t time
)
140 if (cbe_psm_timer_mode
== TIMER_ONESHOT
)
141 (*psm_timer_reprogram
)(time
);
146 cbe_set_level(void *arg
, cyc_level_t level
)
161 panic("cbe_set_level: unexpected level %d", level
);
164 return (splr(ipltospl(ipl
)));
169 cbe_restore_level(void *arg
, cyc_cookie_t cookie
)
176 cbe_xcall(void *arg
, cpu_t
*dest
, cyc_func_t func
, void *farg
)
186 mutex_enter(&cbe_xcall_lock
);
188 ASSERT(cbe_xcall_func
== NULL
);
190 cbe_xcall_farg
= farg
;
192 cbe_xcall_cpu
= dest
;
193 cbe_xcall_func
= func
;
195 send_dirint(dest
->cpu_id
, CBE_HIGH_PIL
);
197 while (cbe_xcall_func
!= NULL
|| cbe_xcall_cpu
!= NULL
)
200 mutex_exit(&cbe_xcall_lock
);
206 cbe_configure(cpu_t
*cpu
)
212 cbe_unconfigure(void *arg
)
214 _NOTE(ARGUNUSED(arg
));
215 ASSERT(!CPU_IN_SET(cbe_enabled
, ((cpu_t
*)arg
)->cpu_id
));
220 * declarations needed for time adjustment
222 extern void tsc_suspend(void);
223 extern void tsc_resume(void);
225 * Call the resume function in the cyclic, instead of inline in the
228 extern int tsc_resume_in_cyclic
;
233 cbe_suspend(cyb_arg_t arg
)
237 * This is an x86 backend, so let the tsc_suspend
238 * that is specific to x86 platforms do the work.
246 cbe_resume(cyb_arg_t arg
)
249 if (tsc_resume_in_cyclic
) {
256 cbe_enable(void *arg
)
258 processorid_t me
= ((cpu_t
*)arg
)->cpu_id
;
260 /* neither enable nor disable cpu0 if TIMER_PERIODIC is set */
261 if ((cbe_psm_timer_mode
!= TIMER_ONESHOT
) && (me
== 0))
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.
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
)();
275 cbe_disable(void *arg
)
277 processorid_t me
= ((cpu_t
*)arg
)->cpu_id
;
279 /* neither enable nor disable cpu0 if TIMER_PERIODIC is set */
280 if ((cbe_psm_timer_mode
!= TIMER_ONESHOT
) && (me
== 0))
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
)();
290 * Unbound cyclic, called once per tick (every nsec_per_tick ns).
300 * Because hres_tick effectively locks hres_lock, we must be at the
301 * same PIL as that used for CLOCK_LOCK.
303 s
= splr(ipltospl(XC_HI_PIL
));
307 if ((cbe_ticks
% hz
) == 0)
317 cbe_vector
= (*psm_get_clockirq
)(CBE_HIGH_PIL
);
319 CPUSET_ZERO(cbe_enabled
);
321 cbe_timer_resolution
= (*clkinitf
)(TIMER_ONESHOT
, &cbe_psm_timer_mode
);
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 */
343 mutex_init(&cbe_xcall_lock
, NULL
, MUTEX_DEFAULT
, NULL
);
345 mutex_enter(&cpu_lock
);
346 cyclic_init(&cbe
, cbe_timer_resolution
);
347 mutex_exit(&cpu_lock
);
349 (void) add_avintr(NULL
, CBE_HIGH_PIL
, (avfunc
)cbe_fire
,
350 "cbe_fire_master", cbe_vector
, 0, NULL
, NULL
, NULL
);
352 if (psm_get_ipivect
!= NULL
) {
353 (void) add_avintr(NULL
, CBE_HIGH_PIL
, (avfunc
)cbe_fire
,
355 (*psm_get_ipivect
)(CBE_HIGH_PIL
, PSM_INTR_IPI_HI
),
356 0, NULL
, NULL
, NULL
);
359 (void) add_avsoftintr((void *)&cbe_clock_hdl
, CBE_LOCK_PIL
,
360 (avfunc
)cbe_softclock
, "softclock", NULL
, NULL
);
362 (void) add_avsoftintr((void *)&cbe_low_hdl
, CBE_LOW_PIL
,
363 (avfunc
)cbe_low_level
, "low level", NULL
, NULL
);
365 mutex_enter(&cpu_lock
);
367 hdlr
.cyh_level
= CY_HIGH_LEVEL
;
368 hdlr
.cyh_func
= (cyc_func_t
)cbe_hres_tick
;
372 when
.cyt_interval
= nsec_per_tick
;
374 cbe_hres_cyclic
= cyclic_add(&hdlr
, &when
);
376 if (psm_post_cyclic_setup
!= NULL
)
377 (*psm_post_cyclic_setup
)(NULL
);
379 mutex_exit(&cpu_lock
);