xref: /onnv-gate/usr/src/uts/i86pc/io/cbe.c (revision 12004:93f274d4a367)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
53446Smrj  * Common Development and Distribution License (the "License").
63446Smrj  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
213446Smrj 
220Sstevel@tonic-gate /*
238566SMadhavan.Venkataraman@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
240Sstevel@tonic-gate  * Use is subject to license terms.
250Sstevel@tonic-gate  */
263446Smrj 
270Sstevel@tonic-gate #include <sys/systm.h>
280Sstevel@tonic-gate #include <sys/cyclic.h>
290Sstevel@tonic-gate #include <sys/cyclic_impl.h>
300Sstevel@tonic-gate #include <sys/spl.h>
310Sstevel@tonic-gate #include <sys/x_call.h>
320Sstevel@tonic-gate #include <sys/kmem.h>
330Sstevel@tonic-gate #include <sys/machsystm.h>
340Sstevel@tonic-gate #include <sys/smp_impldefs.h>
350Sstevel@tonic-gate #include <sys/psm_types.h>
363446Smrj #include <sys/psm.h>
370Sstevel@tonic-gate #include <sys/atomic.h>
380Sstevel@tonic-gate #include <sys/clock.h>
395295Srandyf #include <sys/x86_archext.h>
400Sstevel@tonic-gate #include <sys/ddi_impldefs.h>
410Sstevel@tonic-gate #include <sys/ddi_intr.h>
42999Slq150181 #include <sys/avintr.h>
43*12004Sjiang.liu@intel.com #include <sys/note.h>
440Sstevel@tonic-gate 
450Sstevel@tonic-gate static int cbe_vector;
460Sstevel@tonic-gate static int cbe_ticks = 0;
470Sstevel@tonic-gate 
488048SMadhavan.Venkataraman@Sun.COM /*
498048SMadhavan.Venkataraman@Sun.COM  * cbe_xcall_lock is used to protect the xcall globals since the cyclic
508048SMadhavan.Venkataraman@Sun.COM  * reprogramming API does not use cpu_lock.
518048SMadhavan.Venkataraman@Sun.COM  */
528048SMadhavan.Venkataraman@Sun.COM static kmutex_t cbe_xcall_lock;
530Sstevel@tonic-gate static cyc_func_t volatile cbe_xcall_func;
540Sstevel@tonic-gate static cpu_t *volatile cbe_xcall_cpu;
550Sstevel@tonic-gate static void *cbe_xcall_farg;
560Sstevel@tonic-gate static cpuset_t cbe_enabled;
570Sstevel@tonic-gate 
580Sstevel@tonic-gate static ddi_softint_hdl_impl_t cbe_low_hdl =
59999Slq150181 	{0, NULL, NULL, NULL, 0, NULL, NULL, NULL};
600Sstevel@tonic-gate static ddi_softint_hdl_impl_t cbe_clock_hdl =
61999Slq150181 	{0, NULL, NULL, NULL, 0, NULL, NULL, NULL};
620Sstevel@tonic-gate 
630Sstevel@tonic-gate cyclic_id_t cbe_hres_cyclic;
640Sstevel@tonic-gate int cbe_psm_timer_mode = TIMER_ONESHOT;
657039Smrj static hrtime_t cbe_timer_resolution;
660Sstevel@tonic-gate 
675295Srandyf extern int tsc_gethrtime_enable;
685295Srandyf 
690Sstevel@tonic-gate void cbe_hres_tick(void);
700Sstevel@tonic-gate 
710Sstevel@tonic-gate int
cbe_softclock(void)720Sstevel@tonic-gate cbe_softclock(void)
730Sstevel@tonic-gate {
740Sstevel@tonic-gate 	cyclic_softint(CPU, CY_LOCK_LEVEL);
750Sstevel@tonic-gate 	return (1);
760Sstevel@tonic-gate }
770Sstevel@tonic-gate 
780Sstevel@tonic-gate int
cbe_low_level(void)790Sstevel@tonic-gate cbe_low_level(void)
800Sstevel@tonic-gate {
810Sstevel@tonic-gate 	cpu_t *cpu = CPU;
820Sstevel@tonic-gate 
830Sstevel@tonic-gate 	cyclic_softint(cpu, CY_LOW_LEVEL);
840Sstevel@tonic-gate 	return (1);
850Sstevel@tonic-gate }
860Sstevel@tonic-gate 
870Sstevel@tonic-gate /*
880Sstevel@tonic-gate  * We can be in cbe_fire() either due to a cyclic-induced cross call, or due
890Sstevel@tonic-gate  * to the timer firing at level-14.  Because cyclic_fire() can tolerate
900Sstevel@tonic-gate  * spurious calls, it would not matter if we called cyclic_fire() in both
910Sstevel@tonic-gate  * cases.
920Sstevel@tonic-gate  */
930Sstevel@tonic-gate int
cbe_fire(void)940Sstevel@tonic-gate cbe_fire(void)
950Sstevel@tonic-gate {
960Sstevel@tonic-gate 	cpu_t *cpu = CPU;
970Sstevel@tonic-gate 	processorid_t me = cpu->cpu_id, i;
980Sstevel@tonic-gate 	int cross_call = (cbe_xcall_func != NULL && cbe_xcall_cpu == cpu);
990Sstevel@tonic-gate 
1000Sstevel@tonic-gate 	cyclic_fire(cpu);
1010Sstevel@tonic-gate 
1020Sstevel@tonic-gate 	if (cbe_psm_timer_mode != TIMER_ONESHOT && me == 0 && !cross_call) {
1030Sstevel@tonic-gate 		for (i = 1; i < NCPU; i++) {
1043446Smrj 			if (CPU_IN_SET(cbe_enabled, i)) {
1050Sstevel@tonic-gate 				send_dirint(i, CBE_HIGH_PIL);
1063446Smrj 			}
1070Sstevel@tonic-gate 		}
1080Sstevel@tonic-gate 	}
1090Sstevel@tonic-gate 
1100Sstevel@tonic-gate 	if (cross_call) {
1110Sstevel@tonic-gate 		ASSERT(cbe_xcall_func != NULL && cbe_xcall_cpu == cpu);
1120Sstevel@tonic-gate 		(*cbe_xcall_func)(cbe_xcall_farg);
1130Sstevel@tonic-gate 		cbe_xcall_func = NULL;
1140Sstevel@tonic-gate 		cbe_xcall_cpu = NULL;
1150Sstevel@tonic-gate 	}
1160Sstevel@tonic-gate 
1170Sstevel@tonic-gate 	return (1);
1180Sstevel@tonic-gate }
1190Sstevel@tonic-gate 
1200Sstevel@tonic-gate /*ARGSUSED*/
1210Sstevel@tonic-gate void
cbe_softint(void * arg,cyc_level_t level)1220Sstevel@tonic-gate cbe_softint(void *arg, cyc_level_t level)
1230Sstevel@tonic-gate {
1240Sstevel@tonic-gate 	switch (level) {
1250Sstevel@tonic-gate 	case CY_LOW_LEVEL:
126999Slq150181 		(*setsoftint)(CBE_LOW_PIL, cbe_low_hdl.ih_pending);
1270Sstevel@tonic-gate 		break;
1280Sstevel@tonic-gate 	case CY_LOCK_LEVEL:
129999Slq150181 		(*setsoftint)(CBE_LOCK_PIL, cbe_clock_hdl.ih_pending);
1300Sstevel@tonic-gate 		break;
1310Sstevel@tonic-gate 	default:
1320Sstevel@tonic-gate 		panic("cbe_softint: unexpected soft level %d", level);
1330Sstevel@tonic-gate 	}
1340Sstevel@tonic-gate }
1350Sstevel@tonic-gate 
1360Sstevel@tonic-gate /*ARGSUSED*/
1370Sstevel@tonic-gate void
cbe_reprogram(void * arg,hrtime_t time)1380Sstevel@tonic-gate cbe_reprogram(void *arg, hrtime_t time)
1390Sstevel@tonic-gate {
1400Sstevel@tonic-gate 	if (cbe_psm_timer_mode == TIMER_ONESHOT)
1410Sstevel@tonic-gate 		(*psm_timer_reprogram)(time);
1420Sstevel@tonic-gate }
1430Sstevel@tonic-gate 
1440Sstevel@tonic-gate /*ARGSUSED*/
1450Sstevel@tonic-gate cyc_cookie_t
cbe_set_level(void * arg,cyc_level_t level)1460Sstevel@tonic-gate cbe_set_level(void *arg, cyc_level_t level)
1470Sstevel@tonic-gate {
1480Sstevel@tonic-gate 	int ipl;
1490Sstevel@tonic-gate 
1500Sstevel@tonic-gate 	switch (level) {
1510Sstevel@tonic-gate 	case CY_LOW_LEVEL:
1520Sstevel@tonic-gate 		ipl = CBE_LOW_PIL;
1530Sstevel@tonic-gate 		break;
1540Sstevel@tonic-gate 	case CY_LOCK_LEVEL:
1550Sstevel@tonic-gate 		ipl = CBE_LOCK_PIL;
1560Sstevel@tonic-gate 		break;
1570Sstevel@tonic-gate 	case CY_HIGH_LEVEL:
1580Sstevel@tonic-gate 		ipl = CBE_HIGH_PIL;
1590Sstevel@tonic-gate 		break;
1600Sstevel@tonic-gate 	default:
1610Sstevel@tonic-gate 		panic("cbe_set_level: unexpected level %d", level);
1620Sstevel@tonic-gate 	}
1630Sstevel@tonic-gate 
1640Sstevel@tonic-gate 	return (splr(ipltospl(ipl)));
1650Sstevel@tonic-gate }
1660Sstevel@tonic-gate 
1670Sstevel@tonic-gate /*ARGSUSED*/
1680Sstevel@tonic-gate void
cbe_restore_level(void * arg,cyc_cookie_t cookie)1690Sstevel@tonic-gate cbe_restore_level(void *arg, cyc_cookie_t cookie)
1700Sstevel@tonic-gate {
1710Sstevel@tonic-gate 	splx(cookie);
1720Sstevel@tonic-gate }
1730Sstevel@tonic-gate 
1740Sstevel@tonic-gate /*ARGSUSED*/
1750Sstevel@tonic-gate void
cbe_xcall(void * arg,cpu_t * dest,cyc_func_t func,void * farg)1760Sstevel@tonic-gate cbe_xcall(void *arg, cpu_t *dest, cyc_func_t func, void *farg)
1770Sstevel@tonic-gate {
1780Sstevel@tonic-gate 	kpreempt_disable();
1790Sstevel@tonic-gate 
1800Sstevel@tonic-gate 	if (dest == CPU) {
1810Sstevel@tonic-gate 		(*func)(farg);
1820Sstevel@tonic-gate 		kpreempt_enable();
1830Sstevel@tonic-gate 		return;
1840Sstevel@tonic-gate 	}
1850Sstevel@tonic-gate 
1868048SMadhavan.Venkataraman@Sun.COM 	mutex_enter(&cbe_xcall_lock);
1878048SMadhavan.Venkataraman@Sun.COM 
1880Sstevel@tonic-gate 	ASSERT(cbe_xcall_func == NULL);
1890Sstevel@tonic-gate 
1900Sstevel@tonic-gate 	cbe_xcall_farg = farg;
1910Sstevel@tonic-gate 	membar_producer();
1920Sstevel@tonic-gate 	cbe_xcall_cpu = dest;
1930Sstevel@tonic-gate 	cbe_xcall_func = func;
1940Sstevel@tonic-gate 
1950Sstevel@tonic-gate 	send_dirint(dest->cpu_id, CBE_HIGH_PIL);
1960Sstevel@tonic-gate 
1970Sstevel@tonic-gate 	while (cbe_xcall_func != NULL || cbe_xcall_cpu != NULL)
1980Sstevel@tonic-gate 		continue;
1990Sstevel@tonic-gate 
2008048SMadhavan.Venkataraman@Sun.COM 	mutex_exit(&cbe_xcall_lock);
2018048SMadhavan.Venkataraman@Sun.COM 
2020Sstevel@tonic-gate 	kpreempt_enable();
2030Sstevel@tonic-gate }
2040Sstevel@tonic-gate 
2050Sstevel@tonic-gate void *
cbe_configure(cpu_t * cpu)2060Sstevel@tonic-gate cbe_configure(cpu_t *cpu)
2070Sstevel@tonic-gate {
2080Sstevel@tonic-gate 	return (cpu);
2090Sstevel@tonic-gate }
2100Sstevel@tonic-gate 
211*12004Sjiang.liu@intel.com void
cbe_unconfigure(void * arg)212*12004Sjiang.liu@intel.com cbe_unconfigure(void *arg)
213*12004Sjiang.liu@intel.com {
214*12004Sjiang.liu@intel.com 	_NOTE(ARGUNUSED(arg));
215*12004Sjiang.liu@intel.com 	ASSERT(!CPU_IN_SET(cbe_enabled, ((cpu_t *)arg)->cpu_id));
216*12004Sjiang.liu@intel.com }
217*12004Sjiang.liu@intel.com 
2185295Srandyf #ifndef __xpv
2195295Srandyf /*
2205295Srandyf  * declarations needed for time adjustment
2215295Srandyf  */
2225295Srandyf extern void	tsc_suspend(void);
2235295Srandyf extern void	tsc_resume(void);
2245295Srandyf /*
2255295Srandyf  * Call the resume function in the cyclic, instead of inline in the
2265295Srandyf  * resume path.
2275295Srandyf  */
2285295Srandyf extern int	tsc_resume_in_cyclic;
2295295Srandyf #endif
2305295Srandyf 
2315295Srandyf /*ARGSUSED*/
2325295Srandyf static void
cbe_suspend(cyb_arg_t arg)2335295Srandyf cbe_suspend(cyb_arg_t arg)
2345295Srandyf {
2355295Srandyf #ifndef __xpv
2365295Srandyf 	/*
2375295Srandyf 	 * This is an x86 backend, so let the tsc_suspend
2385295Srandyf 	 * that is specific to x86 platforms do the work.
2395295Srandyf 	 */
2405295Srandyf 	tsc_suspend();
2415295Srandyf #endif
2425295Srandyf }
2435295Srandyf 
2445295Srandyf /*ARGSUSED*/
2455295Srandyf static void
cbe_resume(cyb_arg_t arg)2465295Srandyf cbe_resume(cyb_arg_t arg)
2475295Srandyf {
2485295Srandyf #ifndef __xpv
2495295Srandyf 	if (tsc_resume_in_cyclic) {
2505295Srandyf 		tsc_resume();
2515295Srandyf 	}
2525295Srandyf #endif
2535295Srandyf }
2545295Srandyf 
2550Sstevel@tonic-gate void
cbe_enable(void * arg)2560Sstevel@tonic-gate cbe_enable(void *arg)
2570Sstevel@tonic-gate {
2580Sstevel@tonic-gate 	processorid_t me = ((cpu_t *)arg)->cpu_id;
2590Sstevel@tonic-gate 
2601389Sdmick 	/* neither enable nor disable cpu0 if TIMER_PERIODIC is set */
2610Sstevel@tonic-gate 	if ((cbe_psm_timer_mode != TIMER_ONESHOT) && (me == 0))
2620Sstevel@tonic-gate 		return;
2630Sstevel@tonic-gate 
2645295Srandyf 	/*
2655295Srandyf 	 * Added (me == 0) to the ASSERT because the timer isn't
2665295Srandyf 	 * disabled on CPU 0, and cbe_enable is called when we resume.
2675295Srandyf 	 */
2685295Srandyf 	ASSERT((me == 0) || !CPU_IN_SET(cbe_enabled, me));
2690Sstevel@tonic-gate 	CPUSET_ADD(cbe_enabled, me);
2700Sstevel@tonic-gate 	if (cbe_psm_timer_mode == TIMER_ONESHOT)
2710Sstevel@tonic-gate 		(*psm_timer_enable)();
2720Sstevel@tonic-gate }
2730Sstevel@tonic-gate 
2740Sstevel@tonic-gate void
cbe_disable(void * arg)2750Sstevel@tonic-gate cbe_disable(void *arg)
2760Sstevel@tonic-gate {
2770Sstevel@tonic-gate 	processorid_t me = ((cpu_t *)arg)->cpu_id;
2780Sstevel@tonic-gate 
2791389Sdmick 	/* neither enable nor disable cpu0 if TIMER_PERIODIC is set */
2801389Sdmick 	if ((cbe_psm_timer_mode != TIMER_ONESHOT) && (me == 0))
2810Sstevel@tonic-gate 		return;
2820Sstevel@tonic-gate 
2830Sstevel@tonic-gate 	ASSERT(CPU_IN_SET(cbe_enabled, me));
2840Sstevel@tonic-gate 	CPUSET_DEL(cbe_enabled, me);
2850Sstevel@tonic-gate 	if (cbe_psm_timer_mode == TIMER_ONESHOT)
2860Sstevel@tonic-gate 		(*psm_timer_disable)();
2870Sstevel@tonic-gate }
2880Sstevel@tonic-gate 
2890Sstevel@tonic-gate /*
2901389Sdmick  * Unbound cyclic, called once per tick (every nsec_per_tick ns).
2910Sstevel@tonic-gate  */
2920Sstevel@tonic-gate void
cbe_hres_tick(void)2930Sstevel@tonic-gate cbe_hres_tick(void)
2940Sstevel@tonic-gate {
2950Sstevel@tonic-gate 	int s;
2960Sstevel@tonic-gate 
2970Sstevel@tonic-gate 	dtrace_hres_tick();
2980Sstevel@tonic-gate 
2990Sstevel@tonic-gate 	/*
3000Sstevel@tonic-gate 	 * Because hres_tick effectively locks hres_lock, we must be at the
3010Sstevel@tonic-gate 	 * same PIL as that used for CLOCK_LOCK.
3020Sstevel@tonic-gate 	 */
3030Sstevel@tonic-gate 	s = splr(ipltospl(XC_HI_PIL));
3040Sstevel@tonic-gate 	hres_tick();
3050Sstevel@tonic-gate 	splx(s);
3060Sstevel@tonic-gate 
3070Sstevel@tonic-gate 	if ((cbe_ticks % hz) == 0)
3080Sstevel@tonic-gate 		(*hrtime_tick)();
3090Sstevel@tonic-gate 
3100Sstevel@tonic-gate 	cbe_ticks++;
3110Sstevel@tonic-gate 
3120Sstevel@tonic-gate }
3130Sstevel@tonic-gate 
3140Sstevel@tonic-gate void
cbe_init_pre(void)3157039Smrj cbe_init_pre(void)
3167039Smrj {
3177039Smrj 	cbe_vector = (*psm_get_clockirq)(CBE_HIGH_PIL);
3187039Smrj 
3197039Smrj 	CPUSET_ZERO(cbe_enabled);
3207039Smrj 
3217039Smrj 	cbe_timer_resolution = (*clkinitf)(TIMER_ONESHOT, &cbe_psm_timer_mode);
3227039Smrj }
3237039Smrj 
3247039Smrj void
cbe_init(void)3250Sstevel@tonic-gate cbe_init(void)
3260Sstevel@tonic-gate {
3270Sstevel@tonic-gate 	cyc_backend_t cbe = {
3280Sstevel@tonic-gate 		cbe_configure,		/* cyb_configure */
329*12004Sjiang.liu@intel.com 		cbe_unconfigure,	/* cyb_unconfigure */
3300Sstevel@tonic-gate 		cbe_enable,		/* cyb_enable */
3310Sstevel@tonic-gate 		cbe_disable,		/* cyb_disable */
3320Sstevel@tonic-gate 		cbe_reprogram,		/* cyb_reprogram */
3330Sstevel@tonic-gate 		cbe_softint,		/* cyb_softint */
3340Sstevel@tonic-gate 		cbe_set_level,		/* cyb_set_level */
3350Sstevel@tonic-gate 		cbe_restore_level,	/* cyb_restore_level */
3360Sstevel@tonic-gate 		cbe_xcall,		/* cyb_xcall */
3375295Srandyf 		cbe_suspend,		/* cyb_suspend */
3385295Srandyf 		cbe_resume		/* cyb_resume */
3390Sstevel@tonic-gate 	};
3400Sstevel@tonic-gate 	cyc_handler_t hdlr;
3410Sstevel@tonic-gate 	cyc_time_t when;
3420Sstevel@tonic-gate 
3438048SMadhavan.Venkataraman@Sun.COM 	mutex_init(&cbe_xcall_lock, NULL, MUTEX_DEFAULT, NULL);
3440Sstevel@tonic-gate 
3450Sstevel@tonic-gate 	mutex_enter(&cpu_lock);
3467039Smrj 	cyclic_init(&cbe, cbe_timer_resolution);
3470Sstevel@tonic-gate 	mutex_exit(&cpu_lock);
3480Sstevel@tonic-gate 
3490Sstevel@tonic-gate 	(void) add_avintr(NULL, CBE_HIGH_PIL, (avfunc)cbe_fire,
350916Sschwartz 	    "cbe_fire_master", cbe_vector, 0, NULL, NULL, NULL);
3510Sstevel@tonic-gate 
3520Sstevel@tonic-gate 	if (psm_get_ipivect != NULL) {
3530Sstevel@tonic-gate 		(void) add_avintr(NULL, CBE_HIGH_PIL, (avfunc)cbe_fire,
3540Sstevel@tonic-gate 		    "cbe_fire_slave",
3550Sstevel@tonic-gate 		    (*psm_get_ipivect)(CBE_HIGH_PIL, PSM_INTR_IPI_HI),
356916Sschwartz 		    0, NULL, NULL, NULL);
3570Sstevel@tonic-gate 	}
3580Sstevel@tonic-gate 
3590Sstevel@tonic-gate 	(void) add_avsoftintr((void *)&cbe_clock_hdl, CBE_LOCK_PIL,
3600Sstevel@tonic-gate 	    (avfunc)cbe_softclock, "softclock", NULL, NULL);
3610Sstevel@tonic-gate 
3620Sstevel@tonic-gate 	(void) add_avsoftintr((void *)&cbe_low_hdl, CBE_LOW_PIL,
3630Sstevel@tonic-gate 	    (avfunc)cbe_low_level, "low level", NULL, NULL);
3640Sstevel@tonic-gate 
3650Sstevel@tonic-gate 	mutex_enter(&cpu_lock);
3660Sstevel@tonic-gate 
3670Sstevel@tonic-gate 	hdlr.cyh_level = CY_HIGH_LEVEL;
3680Sstevel@tonic-gate 	hdlr.cyh_func = (cyc_func_t)cbe_hres_tick;
3690Sstevel@tonic-gate 	hdlr.cyh_arg = NULL;
3700Sstevel@tonic-gate 
3710Sstevel@tonic-gate 	when.cyt_when = 0;
3720Sstevel@tonic-gate 	when.cyt_interval = nsec_per_tick;
3730Sstevel@tonic-gate 
3740Sstevel@tonic-gate 	cbe_hres_cyclic = cyclic_add(&hdlr, &when);
3750Sstevel@tonic-gate 
3760Sstevel@tonic-gate 	if (psm_post_cyclic_setup != NULL)
3770Sstevel@tonic-gate 		(*psm_post_cyclic_setup)(NULL);
3780Sstevel@tonic-gate 
3790Sstevel@tonic-gate 	mutex_exit(&cpu_lock);
3800Sstevel@tonic-gate }
381