xref: /dragonfly/sys/platform/pc64/apic/lapic.c (revision db2ec6f8)
13e8e985fSSepherosa Ziehau /*
23e8e985fSSepherosa Ziehau  * Copyright (c) 1996, by Steve Passe
33e8e985fSSepherosa Ziehau  * All rights reserved.
43e8e985fSSepherosa Ziehau  *
53e8e985fSSepherosa Ziehau  * Redistribution and use in source and binary forms, with or without
63e8e985fSSepherosa Ziehau  * modification, are permitted provided that the following conditions
73e8e985fSSepherosa Ziehau  * are met:
83e8e985fSSepherosa Ziehau  * 1. Redistributions of source code must retain the above copyright
93e8e985fSSepherosa Ziehau  *    notice, this list of conditions and the following disclaimer.
103e8e985fSSepherosa Ziehau  * 2. The name of the developer may NOT be used to endorse or promote products
113e8e985fSSepherosa Ziehau  *    derived from this software without specific prior written permission.
123e8e985fSSepherosa Ziehau  *
133e8e985fSSepherosa Ziehau  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
143e8e985fSSepherosa Ziehau  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
153e8e985fSSepherosa Ziehau  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
163e8e985fSSepherosa Ziehau  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
173e8e985fSSepherosa Ziehau  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
183e8e985fSSepherosa Ziehau  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
193e8e985fSSepherosa Ziehau  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
203e8e985fSSepherosa Ziehau  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
213e8e985fSSepherosa Ziehau  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
223e8e985fSSepherosa Ziehau  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
233e8e985fSSepherosa Ziehau  * SUCH DAMAGE.
243e8e985fSSepherosa Ziehau  *
253e8e985fSSepherosa Ziehau  * $FreeBSD: src/sys/i386/i386/mpapic.c,v 1.37.2.7 2003/01/25 02:31:47 peter Exp $
263e8e985fSSepherosa Ziehau  */
273e8e985fSSepherosa Ziehau 
283e8e985fSSepherosa Ziehau #include <sys/param.h>
293e8e985fSSepherosa Ziehau #include <sys/systm.h>
303e8e985fSSepherosa Ziehau #include <sys/kernel.h>
313c38fc60SSepherosa Ziehau #include <sys/ktr.h>
323e8e985fSSepherosa Ziehau #include <sys/bus.h>
333e8e985fSSepherosa Ziehau #include <sys/machintr.h>
34e2164e29Szrj #include <sys/malloc.h>
35f89b4a45SSepherosa Ziehau #include <sys/sysctl.h>
363e8e985fSSepherosa Ziehau #include <machine/globaldata.h>
3767534613SMatthew Dillon #include <machine/clock.h>
38185b27faSImre Vadász #include <machine/limits.h>
393e8e985fSSepherosa Ziehau #include <machine/smp.h>
403e8e985fSSepherosa Ziehau #include <machine/md_var.h>
413e8e985fSSepherosa Ziehau #include <machine/pmap.h>
42056fad5aSSepherosa Ziehau #include <machine/specialreg.h>
432b6cd37eSSepherosa Ziehau #include <machine_base/apic/lapic.h>
44ed4d621dSSepherosa Ziehau #include <machine_base/apic/ioapic.h>
453e8e985fSSepherosa Ziehau #include <machine_base/apic/ioapic_abi.h>
46f3960bc3SSepherosa Ziehau #include <machine_base/apic/apicvar.h>
473a69c113SSepherosa Ziehau #include <machine_base/icu/icu_var.h>
483e8e985fSSepherosa Ziehau #include <machine/segments.h>
4967534613SMatthew Dillon #include <sys/spinlock2.h>
503e8e985fSSepherosa Ziehau 
5186b7a03aSSascha Wildner #include <machine/cputypes.h>
523e8e985fSSepherosa Ziehau #include <machine/intr_machdep.h>
533e8e985fSSepherosa Ziehau 
543c38fc60SSepherosa Ziehau #if !defined(KTR_LAPIC)
553c38fc60SSepherosa Ziehau #define KTR_LAPIC	KTR_ALL
563c38fc60SSepherosa Ziehau #endif
573c38fc60SSepherosa Ziehau KTR_INFO_MASTER(lapic);
588afc0c3dSSepherosa Ziehau KTR_INFO(KTR_LAPIC, lapic, mem_eoi, 0, "mem_eoi");
59f89b4a45SSepherosa Ziehau KTR_INFO(KTR_LAPIC, lapic, msr_eoi, 0, "msr_eoi");
603c38fc60SSepherosa Ziehau #define log_lapic(name)     KTR_LOG(lapic_ ## name)
613c38fc60SSepherosa Ziehau 
62637df2f6SSepherosa Ziehau extern int naps;
63637df2f6SSepherosa Ziehau 
648afc0c3dSSepherosa Ziehau volatile lapic_t *lapic_mem;
653e8e985fSSepherosa Ziehau 
663e8e985fSSepherosa Ziehau static void	lapic_timer_calibrate(void);
673e8e985fSSepherosa Ziehau static void	lapic_timer_set_divisor(int);
683e8e985fSSepherosa Ziehau static void	lapic_timer_fixup_handler(void *);
693e8e985fSSepherosa Ziehau static void	lapic_timer_restart_handler(void *);
703e8e985fSSepherosa Ziehau 
71b0e79059SSepherosa Ziehau static int	lapic_timer_c1e_test = -1;	/* auto-detect */
72cb46bbd1SMatthew Dillon TUNABLE_INT("hw.lapic_timer_c1e_test", &lapic_timer_c1e_test);
733e8e985fSSepherosa Ziehau 
743e8e985fSSepherosa Ziehau static int	lapic_timer_enable = 1;
753e8e985fSSepherosa Ziehau TUNABLE_INT("hw.lapic_timer_enable", &lapic_timer_enable);
763e8e985fSSepherosa Ziehau 
77185b27faSImre Vadász static int	lapic_timer_tscdeadline = 1;
78185b27faSImre Vadász TUNABLE_INT("hw.lapic_timer_tscdeadline", &lapic_timer_tscdeadline);
79185b27faSImre Vadász 
804098a6e5SImre Vadász static int	lapic_calibrate_test = 0;
814098a6e5SImre Vadász TUNABLE_INT("hw.lapic_calibrate_test", &lapic_calibrate_test);
824098a6e5SImre Vadász 
834098a6e5SImre Vadász static int	lapic_calibrate_fast = 1;
844098a6e5SImre Vadász TUNABLE_INT("hw.lapic_calibrate_fast", &lapic_calibrate_fast);
854098a6e5SImre Vadász 
86185b27faSImre Vadász static void	lapic_timer_tscdlt_reload(struct cputimer_intr *, sysclock_t);
878afc0c3dSSepherosa Ziehau static void	lapic_mem_timer_intr_reload(struct cputimer_intr *, sysclock_t);
88f89b4a45SSepherosa Ziehau static void	lapic_msr_timer_intr_reload(struct cputimer_intr *, sysclock_t);
893e8e985fSSepherosa Ziehau static void	lapic_timer_intr_enable(struct cputimer_intr *);
903e8e985fSSepherosa Ziehau static void	lapic_timer_intr_restart(struct cputimer_intr *);
913e8e985fSSepherosa Ziehau static void	lapic_timer_intr_pmfixup(struct cputimer_intr *);
923e8e985fSSepherosa Ziehau 
933e8e985fSSepherosa Ziehau static struct cputimer_intr lapic_cputimer_intr = {
943e8e985fSSepherosa Ziehau 	.freq = 0,
958afc0c3dSSepherosa Ziehau 	.reload = lapic_mem_timer_intr_reload,
963e8e985fSSepherosa Ziehau 	.enable = lapic_timer_intr_enable,
973e8e985fSSepherosa Ziehau 	.config = cputimer_intr_default_config,
983e8e985fSSepherosa Ziehau 	.restart = lapic_timer_intr_restart,
993e8e985fSSepherosa Ziehau 	.pmfixup = lapic_timer_intr_pmfixup,
1003e8e985fSSepherosa Ziehau 	.initclock = cputimer_intr_default_initclock,
10142098fc3SSepherosa Ziehau 	.pcpuhand = NULL,
1023e8e985fSSepherosa Ziehau 	.next = SLIST_ENTRY_INITIALIZER,
1033e8e985fSSepherosa Ziehau 	.name = "lapic",
1043e8e985fSSepherosa Ziehau 	.type = CPUTIMER_INTR_LAPIC,
1053e8e985fSSepherosa Ziehau 	.prio = CPUTIMER_INTR_PRIO_LAPIC,
10642098fc3SSepherosa Ziehau 	.caps = CPUTIMER_INTR_CAP_NONE,
10742098fc3SSepherosa Ziehau 	.priv = NULL
1083e8e985fSSepherosa Ziehau };
1093e8e985fSSepherosa Ziehau 
1103e8e985fSSepherosa Ziehau static int		lapic_timer_divisor_idx = -1;
1113e8e985fSSepherosa Ziehau static const uint32_t	lapic_timer_divisors[] = {
1123e8e985fSSepherosa Ziehau 	APIC_TDCR_2,	APIC_TDCR_4,	APIC_TDCR_8,	APIC_TDCR_16,
1133e8e985fSSepherosa Ziehau 	APIC_TDCR_32,	APIC_TDCR_64,	APIC_TDCR_128,	APIC_TDCR_1
1143e8e985fSSepherosa Ziehau };
1153e8e985fSSepherosa Ziehau #define APIC_TIMER_NDIVISORS (int)(NELEM(lapic_timer_divisors))
1163e8e985fSSepherosa Ziehau 
117185b27faSImre Vadász static int	lapic_use_tscdeadline = 0;
118185b27faSImre Vadász 
119511ba355SSepherosa Ziehau /*
120fbac0dc4SSepherosa Ziehau  * APIC ID <-> CPU ID mapping structures.
121511ba355SSepherosa Ziehau  */
122fbac0dc4SSepherosa Ziehau int	cpu_id_to_apic_id[NAPICID];
123fbac0dc4SSepherosa Ziehau int	apic_id_to_cpu_id[NAPICID];
1242e0ed166SSepherosa Ziehau int	lapic_enable = 1;
1258afc0c3dSSepherosa Ziehau int	lapic_usable = 0;
126e87f025fSSepherosa Ziehau int	x2apic_enable = 1;
127f89b4a45SSepherosa Ziehau 
128f89b4a45SSepherosa Ziehau SYSCTL_INT(_hw, OID_AUTO, x2apic_enable, CTLFLAG_RD, &x2apic_enable, 0, "");
129511ba355SSepherosa Ziehau 
130185b27faSImre Vadász /* Separate cachelines for each cpu's info. */
131185b27faSImre Vadász struct deadlines {
132185b27faSImre Vadász 	uint64_t timestamp;
133185b27faSImre Vadász 	uint64_t downcount_time;
134185b27faSImre Vadász 	uint64_t padding[6];
135185b27faSImre Vadász };
136*db2ec6f8SSascha Wildner static struct deadlines *tsc_deadlines = NULL;
137185b27faSImre Vadász 
1388afc0c3dSSepherosa Ziehau static void	lapic_mem_eoi(void);
1398afc0c3dSSepherosa Ziehau static int	lapic_mem_ipi(int dest_type, int vector, int delivery_mode);
1408afc0c3dSSepherosa Ziehau static void	lapic_mem_single_ipi(int cpu, int vector, int delivery_mode);
1413c38fc60SSepherosa Ziehau 
142f89b4a45SSepherosa Ziehau static void	lapic_msr_eoi(void);
143f89b4a45SSepherosa Ziehau static int	lapic_msr_ipi(int dest_type, int vector, int delivery_mode);
144f89b4a45SSepherosa Ziehau static void	lapic_msr_single_ipi(int cpu, int vector, int delivery_mode);
145f89b4a45SSepherosa Ziehau 
1463c38fc60SSepherosa Ziehau void		(*lapic_eoi)(void);
1478afc0c3dSSepherosa Ziehau int		(*apic_ipi)(int dest_type, int vector, int delivery_mode);
1488afc0c3dSSepherosa Ziehau void		(*single_apic_ipi)(int cpu, int vector, int delivery_mode);
1498afc0c3dSSepherosa Ziehau 
1508afc0c3dSSepherosa Ziehau static __inline void
lapic_mem_icr_set(uint32_t apic_id,uint32_t icr_lo_val)1518afc0c3dSSepherosa Ziehau lapic_mem_icr_set(uint32_t apic_id, uint32_t icr_lo_val)
1528afc0c3dSSepherosa Ziehau {
1538afc0c3dSSepherosa Ziehau 	uint32_t icr_lo, icr_hi;
1548afc0c3dSSepherosa Ziehau 
1558afc0c3dSSepherosa Ziehau 	icr_hi = (LAPIC_MEM_READ(icr_hi) & ~APIC_ID_MASK) |
1568afc0c3dSSepherosa Ziehau 	    (apic_id << APIC_ID_SHIFT);
1578afc0c3dSSepherosa Ziehau 	icr_lo = (LAPIC_MEM_READ(icr_lo) & APIC_ICRLO_RESV_MASK) | icr_lo_val;
1588afc0c3dSSepherosa Ziehau 
1598afc0c3dSSepherosa Ziehau 	LAPIC_MEM_WRITE(icr_hi, icr_hi);
1608afc0c3dSSepherosa Ziehau 	LAPIC_MEM_WRITE(icr_lo, icr_lo);
1618afc0c3dSSepherosa Ziehau }
1623c38fc60SSepherosa Ziehau 
163f89b4a45SSepherosa Ziehau static __inline void
lapic_msr_icr_set(uint32_t apic_id,uint32_t icr_lo_val)164f89b4a45SSepherosa Ziehau lapic_msr_icr_set(uint32_t apic_id, uint32_t icr_lo_val)
165f89b4a45SSepherosa Ziehau {
166f89b4a45SSepherosa Ziehau 	LAPIC_MSR_WRITE(MSR_X2APIC_ICR,
167f89b4a45SSepherosa Ziehau 	    ((uint64_t)apic_id << 32) | ((uint64_t)icr_lo_val));
168f89b4a45SSepherosa Ziehau }
169f89b4a45SSepherosa Ziehau 
1703e8e985fSSepherosa Ziehau /*
1713e8e985fSSepherosa Ziehau  * Enable LAPIC, configure interrupts.
1723e8e985fSSepherosa Ziehau  */
1733e8e985fSSepherosa Ziehau void
lapic_init(boolean_t bsp)1743e8e985fSSepherosa Ziehau lapic_init(boolean_t bsp)
1753e8e985fSSepherosa Ziehau {
1763e8e985fSSepherosa Ziehau 	uint32_t timer;
1773e8e985fSSepherosa Ziehau 	u_int   temp;
1783e8e985fSSepherosa Ziehau 
179185b27faSImre Vadász 	if (bsp) {
180185b27faSImre Vadász 		/* Decide whether we want to use TSC Deadline mode. */
181185b27faSImre Vadász 		if (lapic_timer_tscdeadline != 0 &&
182185b27faSImre Vadász 		    (cpu_feature2 & CPUID2_TSCDLT) &&
183185b27faSImre Vadász 		    tsc_invariant && tsc_frequency != 0) {
184185b27faSImre Vadász 			lapic_use_tscdeadline = 1;
18562938642SMatthew Dillon 			tsc_deadlines =
18662938642SMatthew Dillon 				kmalloc(sizeof(struct deadlines) * (naps + 1),
18762938642SMatthew Dillon 					M_DEVBUF,
18862938642SMatthew Dillon 					M_WAITOK | M_ZERO | M_CACHEALIGN);
189185b27faSImre Vadász 		}
190185b27faSImre Vadász 	}
191185b27faSImre Vadász 
1923e8e985fSSepherosa Ziehau 	/*
1933e8e985fSSepherosa Ziehau 	 * Install vectors
1943e8e985fSSepherosa Ziehau 	 *
1953e8e985fSSepherosa Ziehau 	 * Since IDT is shared between BSP and APs, these vectors
1963e8e985fSSepherosa Ziehau 	 * only need to be installed once; we do it on BSP.
1973e8e985fSSepherosa Ziehau 	 */
1983e8e985fSSepherosa Ziehau 	if (bsp) {
199056fad5aSSepherosa Ziehau 		if (cpu_vendor_id == CPU_VENDOR_AMD &&
20067534613SMatthew Dillon 		    CPUID_TO_FAMILY(cpu_id) >= 0x0f &&
20167534613SMatthew Dillon 		    CPUID_TO_FAMILY(cpu_id) < 0x17) {	/* XXX */
202056fad5aSSepherosa Ziehau 			uint32_t tcr;
203056fad5aSSepherosa Ziehau 
204056fad5aSSepherosa Ziehau 			/*
205056fad5aSSepherosa Ziehau 			 * Set the LINTEN bit in the HyperTransport
206056fad5aSSepherosa Ziehau 			 * Transaction Control Register.
207056fad5aSSepherosa Ziehau 			 *
208056fad5aSSepherosa Ziehau 			 * This will cause EXTINT and NMI interrupts
209056fad5aSSepherosa Ziehau 			 * routed over the hypertransport bus to be
210056fad5aSSepherosa Ziehau 			 * fed into the LAPIC LINT0/LINT1.  If the bit
211056fad5aSSepherosa Ziehau 			 * isn't set, the interrupts will go to the
212056fad5aSSepherosa Ziehau 			 * general cpu INTR/NMI pins.  On a dual-core
213056fad5aSSepherosa Ziehau 			 * cpu the interrupt winds up going to BOTH cpus.
214056fad5aSSepherosa Ziehau 			 * The first cpu that does the interrupt ack
215056fad5aSSepherosa Ziehau 			 * cycle will get the correct interrupt.  The
216056fad5aSSepherosa Ziehau 			 * second cpu that does it will get a spurious
217056fad5aSSepherosa Ziehau 			 * interrupt vector (typically IRQ 7).
218056fad5aSSepherosa Ziehau 			 */
219056fad5aSSepherosa Ziehau 			outl(0x0cf8,
220056fad5aSSepherosa Ziehau 			    (1 << 31) |	/* enable */
221056fad5aSSepherosa Ziehau 			    (0 << 16) |	/* bus */
222056fad5aSSepherosa Ziehau 			    (0x18 << 11) | /* dev (cpu + 0x18) */
223056fad5aSSepherosa Ziehau 			    (0 << 8) |	/* func */
224056fad5aSSepherosa Ziehau 			    0x68	/* reg */
225056fad5aSSepherosa Ziehau 			    );
226056fad5aSSepherosa Ziehau 			tcr = inl(0xcfc);
227056fad5aSSepherosa Ziehau 			if ((tcr & 0x00010000) == 0) {
228056fad5aSSepherosa Ziehau 				kprintf("LAPIC: AMD LINTEN on\n");
229056fad5aSSepherosa Ziehau 				outl(0xcfc, tcr|0x00010000);
230056fad5aSSepherosa Ziehau 			}
231056fad5aSSepherosa Ziehau 			outl(0x0cf8, 0);
232056fad5aSSepherosa Ziehau 		}
233056fad5aSSepherosa Ziehau 
2343e8e985fSSepherosa Ziehau 		/* Install a 'Spurious INTerrupt' vector */
2358a06c6eeSSepherosa Ziehau 		setidt_global(XSPURIOUSINT_OFFSET, Xspuriousint,
2363e8e985fSSepherosa Ziehau 		    SDT_SYSIGT, SEL_KPL, 0);
2373e8e985fSSepherosa Ziehau 
2389a4bd8f3SSepherosa Ziehau 		/* Install a timer vector */
2398a06c6eeSSepherosa Ziehau 		setidt_global(XTIMER_OFFSET, Xtimer,
2409a4bd8f3SSepherosa Ziehau 		    SDT_SYSIGT, SEL_KPL, 0);
2419a4bd8f3SSepherosa Ziehau 
2423e8e985fSSepherosa Ziehau 		/* Install an inter-CPU IPI for TLB invalidation */
2438a06c6eeSSepherosa Ziehau 		setidt_global(XINVLTLB_OFFSET, Xinvltlb,
2443e8e985fSSepherosa Ziehau 		    SDT_SYSIGT, SEL_KPL, 0);
2453e8e985fSSepherosa Ziehau 
2463e8e985fSSepherosa Ziehau 		/* Install an inter-CPU IPI for IPIQ messaging */
2478a06c6eeSSepherosa Ziehau 		setidt_global(XIPIQ_OFFSET, Xipiq,
2483e8e985fSSepherosa Ziehau 		    SDT_SYSIGT, SEL_KPL, 0);
2493e8e985fSSepherosa Ziehau 
2503e8e985fSSepherosa Ziehau 		/* Install an inter-CPU IPI for CPU stop/restart */
2518a06c6eeSSepherosa Ziehau 		setidt_global(XCPUSTOP_OFFSET, Xcpustop,
2523e8e985fSSepherosa Ziehau 		    SDT_SYSIGT, SEL_KPL, 0);
253e32d3244SMatthew Dillon 
254e32d3244SMatthew Dillon 		/* Install an inter-CPU IPI for TLB invalidation */
255e32d3244SMatthew Dillon 		setidt_global(XSNIFF_OFFSET, Xsniff,
256e32d3244SMatthew Dillon 		    SDT_SYSIGT, SEL_KPL, 0);
2573e8e985fSSepherosa Ziehau 	}
2583e8e985fSSepherosa Ziehau 
2593e8e985fSSepherosa Ziehau 	/*
2603e8e985fSSepherosa Ziehau 	 * Setup LINT0 as ExtINT on the BSP.  This is theoretically an
2613e8e985fSSepherosa Ziehau 	 * aggregate interrupt input from the 8259.  The INTA cycle
2623e8e985fSSepherosa Ziehau 	 * will be routed to the external controller (the 8259) which
2633e8e985fSSepherosa Ziehau 	 * is expected to supply the vector.
2643e8e985fSSepherosa Ziehau 	 *
2653e8e985fSSepherosa Ziehau 	 * Must be setup edge triggered, active high.
2663e8e985fSSepherosa Ziehau 	 *
2673e8e985fSSepherosa Ziehau 	 * Disable LINT0 on BSP, if I/O APIC is enabled.
2683e8e985fSSepherosa Ziehau 	 *
2693e8e985fSSepherosa Ziehau 	 * Disable LINT0 on the APs.  It doesn't matter what delivery
2703e8e985fSSepherosa Ziehau 	 * mode we use because we leave it masked.
2713e8e985fSSepherosa Ziehau 	 */
2728afc0c3dSSepherosa Ziehau 	temp = LAPIC_READ(lvt_lint0);
2733e8e985fSSepherosa Ziehau 	temp &= ~(APIC_LVT_MASKED | APIC_LVT_TRIG_MASK |
2743e8e985fSSepherosa Ziehau 		  APIC_LVT_POLARITY_MASK | APIC_LVT_DM_MASK);
2753e8e985fSSepherosa Ziehau 	if (bsp) {
2763e8e985fSSepherosa Ziehau 		temp |= APIC_LVT_DM_EXTINT;
277f45bfca0SSepherosa Ziehau 		if (ioapic_enable)
2783e8e985fSSepherosa Ziehau 			temp |= APIC_LVT_MASKED;
2793e8e985fSSepherosa Ziehau 	} else {
2803e8e985fSSepherosa Ziehau 		temp |= APIC_LVT_DM_FIXED | APIC_LVT_MASKED;
2813e8e985fSSepherosa Ziehau 	}
2828afc0c3dSSepherosa Ziehau 	LAPIC_WRITE(lvt_lint0, temp);
2833e8e985fSSepherosa Ziehau 
2843e8e985fSSepherosa Ziehau 	/*
2853e8e985fSSepherosa Ziehau 	 * Setup LINT1 as NMI.
2863e8e985fSSepherosa Ziehau 	 *
2873e8e985fSSepherosa Ziehau 	 * Must be setup edge trigger, active high.
2883e8e985fSSepherosa Ziehau 	 *
2893e8e985fSSepherosa Ziehau 	 * Enable LINT1 on BSP, if I/O APIC is enabled.
2903e8e985fSSepherosa Ziehau 	 *
2913e8e985fSSepherosa Ziehau 	 * Disable LINT1 on the APs.
2923e8e985fSSepherosa Ziehau 	 */
2938afc0c3dSSepherosa Ziehau 	temp = LAPIC_READ(lvt_lint1);
2943e8e985fSSepherosa Ziehau 	temp &= ~(APIC_LVT_MASKED | APIC_LVT_TRIG_MASK |
2953e8e985fSSepherosa Ziehau 		  APIC_LVT_POLARITY_MASK | APIC_LVT_DM_MASK);
2963e8e985fSSepherosa Ziehau 	temp |= APIC_LVT_MASKED | APIC_LVT_DM_NMI;
297f45bfca0SSepherosa Ziehau 	if (bsp && ioapic_enable)
2983e8e985fSSepherosa Ziehau 		temp &= ~APIC_LVT_MASKED;
2998afc0c3dSSepherosa Ziehau 	LAPIC_WRITE(lvt_lint1, temp);
3003e8e985fSSepherosa Ziehau 
3013e8e985fSSepherosa Ziehau 	/*
3023e8e985fSSepherosa Ziehau 	 * Mask the LAPIC error interrupt, LAPIC performance counter
3033e8e985fSSepherosa Ziehau 	 * interrupt.
3043e8e985fSSepherosa Ziehau 	 */
3058afc0c3dSSepherosa Ziehau 	LAPIC_WRITE(lvt_error, LAPIC_READ(lvt_error) | APIC_LVT_MASKED);
3068afc0c3dSSepherosa Ziehau 	LAPIC_WRITE(lvt_pcint, LAPIC_READ(lvt_pcint) | APIC_LVT_MASKED);
3073e8e985fSSepherosa Ziehau 
3083e8e985fSSepherosa Ziehau 	/*
3093e8e985fSSepherosa Ziehau 	 * Set LAPIC timer vector and mask the LAPIC timer interrupt.
3103e8e985fSSepherosa Ziehau 	 */
3118afc0c3dSSepherosa Ziehau 	timer = LAPIC_READ(lvt_timer);
3123e8e985fSSepherosa Ziehau 	timer &= ~APIC_LVTT_VECTOR;
3133e8e985fSSepherosa Ziehau 	timer |= XTIMER_OFFSET;
3143e8e985fSSepherosa Ziehau 	timer |= APIC_LVTT_MASKED;
3158afc0c3dSSepherosa Ziehau 	LAPIC_WRITE(lvt_timer, timer);
3163e8e985fSSepherosa Ziehau 
3173e8e985fSSepherosa Ziehau 	/*
3183e8e985fSSepherosa Ziehau 	 * Set the Task Priority Register as needed.   At the moment allow
3193e8e985fSSepherosa Ziehau 	 * interrupts on all cpus (the APs will remain CLId until they are
32031d433ffSSepherosa Ziehau 	 * ready to deal).
3213e8e985fSSepherosa Ziehau 	 */
3228afc0c3dSSepherosa Ziehau 	temp = LAPIC_READ(tpr);
3233e8e985fSSepherosa Ziehau 	temp &= ~APIC_TPR_PRIO;		/* clear priority field */
3248afc0c3dSSepherosa Ziehau 	LAPIC_WRITE(tpr, temp);
3253e8e985fSSepherosa Ziehau 
3263e8e985fSSepherosa Ziehau 	/*
32767534613SMatthew Dillon 	 * AMD specific setup
32867534613SMatthew Dillon 	 */
3298afc0c3dSSepherosa Ziehau 	if (cpu_vendor_id == CPU_VENDOR_AMD && lapic_mem != NULL &&
3308afc0c3dSSepherosa Ziehau 	    (LAPIC_MEM_READ(version) & APIC_VER_AMD_EXT_SPACE)) {
33167534613SMatthew Dillon 		uint32_t ext_feat;
33267534613SMatthew Dillon 		uint32_t count;
33367534613SMatthew Dillon 		uint32_t max_count;
33467534613SMatthew Dillon 		uint32_t lvt;
33567534613SMatthew Dillon 		uint32_t i;
33667534613SMatthew Dillon 
3378afc0c3dSSepherosa Ziehau 		ext_feat = LAPIC_MEM_READ(ext_feat);
33867534613SMatthew Dillon 		count = (ext_feat & APIC_EXTFEAT_MASK) >> APIC_EXTFEAT_SHIFT;
3398afc0c3dSSepherosa Ziehau 		max_count = sizeof(lapic_mem->ext_lvt) /
3408afc0c3dSSepherosa Ziehau 		    sizeof(lapic_mem->ext_lvt[0]);
34167534613SMatthew Dillon 		if (count > max_count)
34267534613SMatthew Dillon 			count = max_count;
34367534613SMatthew Dillon 		for (i = 0; i < count; ++i) {
3448afc0c3dSSepherosa Ziehau 			lvt = LAPIC_MEM_READ(ext_lvt[i].lvt);
34567534613SMatthew Dillon 
34667534613SMatthew Dillon 			lvt &= ~(APIC_LVT_POLARITY_MASK | APIC_LVT_TRIG_MASK |
34767534613SMatthew Dillon 				 APIC_LVT_DM_MASK | APIC_LVT_MASKED);
34867534613SMatthew Dillon 			lvt |= APIC_LVT_MASKED | APIC_LVT_DM_FIXED;
34967534613SMatthew Dillon 
35067534613SMatthew Dillon 			switch(i) {
35167534613SMatthew Dillon 			case APIC_EXTLVT_IBS:
35267534613SMatthew Dillon 				break;
35367534613SMatthew Dillon 			case APIC_EXTLVT_MCA:
35467534613SMatthew Dillon 				break;
35567534613SMatthew Dillon 			case APIC_EXTLVT_DEI:
35667534613SMatthew Dillon 				break;
35767534613SMatthew Dillon 			case APIC_EXTLVT_SBI:
35867534613SMatthew Dillon 				break;
35967534613SMatthew Dillon 			default:
36067534613SMatthew Dillon 				break;
36167534613SMatthew Dillon 			}
36267534613SMatthew Dillon 			if (bsp) {
36367534613SMatthew Dillon 				kprintf("   LAPIC AMD elvt%d: 0x%08x",
3648afc0c3dSSepherosa Ziehau 					i, LAPIC_MEM_READ(ext_lvt[i].lvt));
3658afc0c3dSSepherosa Ziehau 				if (LAPIC_MEM_READ(ext_lvt[i].lvt) != lvt)
36667534613SMatthew Dillon 					kprintf(" -> 0x%08x", lvt);
36767534613SMatthew Dillon 				kprintf("\n");
36867534613SMatthew Dillon 			}
3698afc0c3dSSepherosa Ziehau 			LAPIC_MEM_WRITE(ext_lvt[i].lvt, lvt);
37067534613SMatthew Dillon 		}
37167534613SMatthew Dillon 	}
37267534613SMatthew Dillon 
37367534613SMatthew Dillon 	/*
3743e8e985fSSepherosa Ziehau 	 * Enable the LAPIC
3753e8e985fSSepherosa Ziehau 	 */
3768afc0c3dSSepherosa Ziehau 	temp = LAPIC_READ(svr);
3773e8e985fSSepherosa Ziehau 	temp |= APIC_SVR_ENABLE;	/* enable the LAPIC */
3783e8e985fSSepherosa Ziehau 	temp &= ~APIC_SVR_FOCUS_DISABLE; /* enable lopri focus processor */
3793e8e985fSSepherosa Ziehau 
3808afc0c3dSSepherosa Ziehau 	if (LAPIC_READ(version) & APIC_VER_EOI_SUPP) {
38167534613SMatthew Dillon 		if (temp & APIC_SVR_EOI_SUPP) {
38267534613SMatthew Dillon 			temp &= ~APIC_SVR_EOI_SUPP;
38367534613SMatthew Dillon 			if (bsp)
38467534613SMatthew Dillon 				kprintf("    LAPIC disabling EOI supp\n");
38567534613SMatthew Dillon 		}
3867ed7e1a5SMatthew Dillon 		/* (future, on KVM auto-EOI must be disabled) */
3877ed7e1a5SMatthew Dillon 		if (vmm_guest == VMM_GUEST_KVM)
3887ed7e1a5SMatthew Dillon 			temp &= ~APIC_SVR_EOI_SUPP;
38967534613SMatthew Dillon 	}
39067534613SMatthew Dillon 
3913e8e985fSSepherosa Ziehau 	/*
3923e8e985fSSepherosa Ziehau 	 * Set the spurious interrupt vector.  The low 4 bits of the vector
3933e8e985fSSepherosa Ziehau 	 * must be 1111.
3943e8e985fSSepherosa Ziehau 	 */
3953e8e985fSSepherosa Ziehau 	if ((XSPURIOUSINT_OFFSET & 0x0F) != 0x0F)
3963e8e985fSSepherosa Ziehau 		panic("bad XSPURIOUSINT_OFFSET: 0x%08x", XSPURIOUSINT_OFFSET);
3973e8e985fSSepherosa Ziehau 	temp &= ~APIC_SVR_VECTOR;
3983e8e985fSSepherosa Ziehau 	temp |= XSPURIOUSINT_OFFSET;
3993e8e985fSSepherosa Ziehau 
4008afc0c3dSSepherosa Ziehau 	LAPIC_WRITE(svr, temp);
4013e8e985fSSepherosa Ziehau 
4023e8e985fSSepherosa Ziehau 	/*
4033e8e985fSSepherosa Ziehau 	 * Pump out a few EOIs to clean out interrupts that got through
4043e8e985fSSepherosa Ziehau 	 * before we were able to set the TPR.
4053e8e985fSSepherosa Ziehau 	 */
4068afc0c3dSSepherosa Ziehau 	LAPIC_WRITE(eoi, 0);
4078afc0c3dSSepherosa Ziehau 	LAPIC_WRITE(eoi, 0);
4088afc0c3dSSepherosa Ziehau 	LAPIC_WRITE(eoi, 0);
4093e8e985fSSepherosa Ziehau 
4103e8e985fSSepherosa Ziehau 	if (bsp) {
4113e8e985fSSepherosa Ziehau 		lapic_timer_calibrate();
4123e8e985fSSepherosa Ziehau 		if (lapic_timer_enable) {
4138d16855aSSepherosa Ziehau 			if (cpu_thermal_feature & CPUID_THERMAL_ARAT) {
4148d16855aSSepherosa Ziehau 				/*
4158d16855aSSepherosa Ziehau 				 * Local APIC timer will not stop
4168d16855aSSepherosa Ziehau 				 * in deep C-state.
4178d16855aSSepherosa Ziehau 				 */
4188d16855aSSepherosa Ziehau 				lapic_cputimer_intr.caps |=
4198d16855aSSepherosa Ziehau 				    CPUTIMER_INTR_CAP_PS;
4208d16855aSSepherosa Ziehau 			}
421185b27faSImre Vadász 			if (lapic_use_tscdeadline) {
422185b27faSImre Vadász 				lapic_cputimer_intr.reload =
423185b27faSImre Vadász 				    lapic_timer_tscdlt_reload;
424185b27faSImre Vadász 			}
4253e8e985fSSepherosa Ziehau 			cputimer_intr_register(&lapic_cputimer_intr);
4263e8e985fSSepherosa Ziehau 			cputimer_intr_select(&lapic_cputimer_intr, 0);
4273e8e985fSSepherosa Ziehau 		}
428185b27faSImre Vadász 	} else if (!lapic_use_tscdeadline) {
4293e8e985fSSepherosa Ziehau 		lapic_timer_set_divisor(lapic_timer_divisor_idx);
4303e8e985fSSepherosa Ziehau 	}
4313e8e985fSSepherosa Ziehau 
4323e8e985fSSepherosa Ziehau 	if (bootverbose)
4333e8e985fSSepherosa Ziehau 		apic_dump("apic_initialize()");
4343e8e985fSSepherosa Ziehau }
4353e8e985fSSepherosa Ziehau 
4363e8e985fSSepherosa Ziehau static void
lapic_timer_set_divisor(int divisor_idx)4373e8e985fSSepherosa Ziehau lapic_timer_set_divisor(int divisor_idx)
4383e8e985fSSepherosa Ziehau {
4393e8e985fSSepherosa Ziehau 	KKASSERT(divisor_idx >= 0 && divisor_idx < APIC_TIMER_NDIVISORS);
4408afc0c3dSSepherosa Ziehau 	LAPIC_WRITE(dcr_timer, lapic_timer_divisors[divisor_idx]);
4413e8e985fSSepherosa Ziehau }
4423e8e985fSSepherosa Ziehau 
4433e8e985fSSepherosa Ziehau static void
lapic_timer_oneshot(u_int count)4443e8e985fSSepherosa Ziehau lapic_timer_oneshot(u_int count)
4453e8e985fSSepherosa Ziehau {
4463e8e985fSSepherosa Ziehau 	uint32_t value;
4473e8e985fSSepherosa Ziehau 
4488afc0c3dSSepherosa Ziehau 	value = LAPIC_READ(lvt_timer);
449185b27faSImre Vadász 	value &= ~(APIC_LVTT_PERIODIC | APIC_LVTT_TSCDLT);
4508afc0c3dSSepherosa Ziehau 	LAPIC_WRITE(lvt_timer, value);
4518afc0c3dSSepherosa Ziehau 	LAPIC_WRITE(icr_timer, count);
4523e8e985fSSepherosa Ziehau }
4533e8e985fSSepherosa Ziehau 
4543e8e985fSSepherosa Ziehau static void
lapic_timer_oneshot_quick(u_int count)4553e8e985fSSepherosa Ziehau lapic_timer_oneshot_quick(u_int count)
4563e8e985fSSepherosa Ziehau {
4578afc0c3dSSepherosa Ziehau 	LAPIC_WRITE(icr_timer, count);
4583e8e985fSSepherosa Ziehau }
4593e8e985fSSepherosa Ziehau 
4603e8e985fSSepherosa Ziehau static void
lapic_timer_tscdeadline_quick(uint64_t diff)461185b27faSImre Vadász lapic_timer_tscdeadline_quick(uint64_t diff)
462185b27faSImre Vadász {
463185b27faSImre Vadász 	uint64_t val = rdtsc() + diff;
464185b27faSImre Vadász 
465185b27faSImre Vadász 	wrmsr(MSR_TSC_DEADLINE, val);
466185b27faSImre Vadász 	tsc_deadlines[mycpuid].timestamp = val;
467185b27faSImre Vadász }
468185b27faSImre Vadász 
469185b27faSImre Vadász static uint64_t
lapic_scale_to_tsc(unsigned value,unsigned scale)470185b27faSImre Vadász lapic_scale_to_tsc(unsigned value, unsigned scale)
471185b27faSImre Vadász {
472185b27faSImre Vadász 	uint64_t val;
473185b27faSImre Vadász 
474185b27faSImre Vadász 	val = value;
475185b27faSImre Vadász 	val *= tsc_frequency;
476185b27faSImre Vadász 	val += (scale - 1);
477185b27faSImre Vadász 	val /= scale;
478185b27faSImre Vadász 	return val;
479185b27faSImre Vadász }
480185b27faSImre Vadász 
4814098a6e5SImre Vadász #define MAX_MEASURE_RETRIES	100
4824098a6e5SImre Vadász 
4834098a6e5SImre Vadász static u_int64_t
do_tsc_calibration(u_int us,u_int64_t apic_delay_tsc)4844098a6e5SImre Vadász do_tsc_calibration(u_int us, u_int64_t apic_delay_tsc)
4854098a6e5SImre Vadász {
4864098a6e5SImre Vadász 	u_int64_t old_tsc1, old_tsc2, new_tsc1, new_tsc2;
4874098a6e5SImre Vadász 	u_int64_t diff, count;
4884098a6e5SImre Vadász 	u_int64_t a;
4894098a6e5SImre Vadász 	u_int32_t start, end;
4904098a6e5SImre Vadász 	int retries1 = 0, retries2 = 0;
4914098a6e5SImre Vadász 
4924098a6e5SImre Vadász retry1:
4934098a6e5SImre Vadász 	lapic_timer_oneshot_quick(APIC_TIMER_MAX_COUNT);
4944098a6e5SImre Vadász 	old_tsc1 = rdtsc_ordered();
4958afc0c3dSSepherosa Ziehau 	start = LAPIC_READ(ccr_timer);
4964098a6e5SImre Vadász 	old_tsc2 = rdtsc_ordered();
4974098a6e5SImre Vadász 	if (apic_delay_tsc > 0 && retries1 < MAX_MEASURE_RETRIES &&
4984098a6e5SImre Vadász 	    old_tsc2 - old_tsc1 > 2 * apic_delay_tsc) {
4994098a6e5SImre Vadász 		retries1++;
5004098a6e5SImre Vadász 		goto retry1;
5014098a6e5SImre Vadász 	}
5024098a6e5SImre Vadász 	DELAY(us);
5034098a6e5SImre Vadász retry2:
5044098a6e5SImre Vadász 	new_tsc1 = rdtsc_ordered();
5058afc0c3dSSepherosa Ziehau 	end = LAPIC_READ(ccr_timer);
5064098a6e5SImre Vadász 	new_tsc2 = rdtsc_ordered();
5074098a6e5SImre Vadász 	if (apic_delay_tsc > 0 && retries2 < MAX_MEASURE_RETRIES &&
5084098a6e5SImre Vadász 	    new_tsc2 - new_tsc1 > 2 * apic_delay_tsc) {
5094098a6e5SImre Vadász 		retries2++;
5104098a6e5SImre Vadász 		goto retry2;
5114098a6e5SImre Vadász 	}
5124098a6e5SImre Vadász 	if (end == 0)
5134098a6e5SImre Vadász 		return 0;
5144098a6e5SImre Vadász 
5154098a6e5SImre Vadász 	count = start - end;
5164098a6e5SImre Vadász 
5174098a6e5SImre Vadász 	/* Make sure the lapic can count for up to 2s */
5184098a6e5SImre Vadász 	a = (unsigned)APIC_TIMER_MAX_COUNT;
5194098a6e5SImre Vadász 	if (us < 2000000 && (u_int64_t)count * 2000000 >= a * us)
5204098a6e5SImre Vadász 		return 0;
5214098a6e5SImre Vadász 
5224098a6e5SImre Vadász 	if (lapic_calibrate_test > 0 && (retries1 > 0 || retries2 > 0)) {
5234098a6e5SImre Vadász 		kprintf("%s: retries1=%d retries2=%d\n",
5244098a6e5SImre Vadász 		    __func__, retries1, retries2);
5254098a6e5SImre Vadász 	}
5264098a6e5SImre Vadász 
5274098a6e5SImre Vadász 	diff = (new_tsc1 - old_tsc1) + (new_tsc2 - old_tsc2);
5284098a6e5SImre Vadász 	/* XXX First estimate if the total TSC diff value makes sense */
5294098a6e5SImre Vadász 	/* This will almost overflow, but only almost :) */
5304098a6e5SImre Vadász 	count = (2 * count * tsc_frequency) / diff;
5314098a6e5SImre Vadász 
5324098a6e5SImre Vadász 	return count;
5334098a6e5SImre Vadász }
5344098a6e5SImre Vadász 
5354098a6e5SImre Vadász static uint64_t
do_cputimer_calibration(u_int us)5364098a6e5SImre Vadász do_cputimer_calibration(u_int us)
5374098a6e5SImre Vadász {
5384098a6e5SImre Vadász 	sysclock_t value;
5398fbc264dSMatthew Dillon 	sysclock_t start, end;
5408fbc264dSMatthew Dillon 	uint32_t beginning, finish;
5414098a6e5SImre Vadász 
5424098a6e5SImre Vadász 	lapic_timer_oneshot(APIC_TIMER_MAX_COUNT);
5438afc0c3dSSepherosa Ziehau 	beginning = LAPIC_READ(ccr_timer);
5444098a6e5SImre Vadász 	start = sys_cputimer->count();
5454098a6e5SImre Vadász 	DELAY(us);
5464098a6e5SImre Vadász 	end = sys_cputimer->count();
5478afc0c3dSSepherosa Ziehau 	finish = LAPIC_READ(ccr_timer);
5484098a6e5SImre Vadász 	if (finish == 0)
5494098a6e5SImre Vadász 		return 0;
5504098a6e5SImre Vadász 	/* value is the LAPIC timer difference. */
5518fbc264dSMatthew Dillon 	value = (uint32_t)(beginning - finish);
5524098a6e5SImre Vadász 	/* end is the sys_cputimer difference. */
5534098a6e5SImre Vadász 	end -= start;
5544098a6e5SImre Vadász 	if (end == 0)
5554098a6e5SImre Vadász 		return 0;
5568fbc264dSMatthew Dillon 	value = muldivu64(value, sys_cputimer->freq, end);
5578fbc264dSMatthew Dillon 
5584098a6e5SImre Vadász 	return value;
5594098a6e5SImre Vadász }
5604098a6e5SImre Vadász 
561185b27faSImre Vadász static void
lapic_timer_calibrate(void)5623e8e985fSSepherosa Ziehau lapic_timer_calibrate(void)
5633e8e985fSSepherosa Ziehau {
5643e8e985fSSepherosa Ziehau 	sysclock_t value;
5654098a6e5SImre Vadász 	u_int64_t apic_delay_tsc = 0;
5664098a6e5SImre Vadász 	int use_tsc_calibration = 0;
5673e8e985fSSepherosa Ziehau 
568185b27faSImre Vadász 	/* No need to calibrate lapic_timer, if we will use TSC Deadline mode */
569185b27faSImre Vadász 	if (lapic_use_tscdeadline) {
5708fbc264dSMatthew Dillon 		lapic_cputimer_intr.freq = tsc_frequency;
571185b27faSImre Vadász 		kprintf(
5728fbc264dSMatthew Dillon 		    "lapic: TSC Deadline Mode: frequency %lu Hz\n",
5738fbc264dSMatthew Dillon 		    lapic_cputimer_intr.freq);
574185b27faSImre Vadász 		return;
575185b27faSImre Vadász 	}
576185b27faSImre Vadász 
5774098a6e5SImre Vadász 	/*
5784098a6e5SImre Vadász 	 * On real hardware, tsc_invariant == 0 wouldn't be an issue, but in
5794098a6e5SImre Vadász 	 * a virtual machine the frequency may get changed by the host.
5804098a6e5SImre Vadász 	 */
5814098a6e5SImre Vadász 	if (tsc_frequency != 0 && tsc_invariant && lapic_calibrate_fast)
5824098a6e5SImre Vadász 		use_tsc_calibration = 1;
5834098a6e5SImre Vadász 
5844098a6e5SImre Vadász 	if (use_tsc_calibration) {
5854098a6e5SImre Vadász 		u_int64_t min_apic_tsc = 0, max_apic_tsc = 0;
5864098a6e5SImre Vadász 		u_int64_t old_tsc, new_tsc;
5878fbc264dSMatthew Dillon 		uint32_t val;
5884098a6e5SImre Vadász 		int i;
5894098a6e5SImre Vadász 
5904098a6e5SImre Vadász 		/* warm up */
5914098a6e5SImre Vadász 		lapic_timer_oneshot(APIC_TIMER_MAX_COUNT);
5924098a6e5SImre Vadász 		for (i = 0; i < 10; i++)
5938afc0c3dSSepherosa Ziehau 			val = LAPIC_READ(ccr_timer);
5944098a6e5SImre Vadász 
5954098a6e5SImre Vadász 		for (i = 0; i < 100; i++) {
5964098a6e5SImre Vadász 			old_tsc = rdtsc_ordered();
5978afc0c3dSSepherosa Ziehau 			val = LAPIC_READ(ccr_timer);
5984098a6e5SImre Vadász 			new_tsc = rdtsc_ordered();
5994098a6e5SImre Vadász 			new_tsc -= old_tsc;
6004098a6e5SImre Vadász 			apic_delay_tsc += new_tsc;
6014098a6e5SImre Vadász 			if (min_apic_tsc == 0 ||
6024098a6e5SImre Vadász 			    min_apic_tsc > new_tsc) {
6034098a6e5SImre Vadász 				min_apic_tsc = new_tsc;
6044098a6e5SImre Vadász 			}
6054098a6e5SImre Vadász 			if (max_apic_tsc < new_tsc)
6064098a6e5SImre Vadász 				max_apic_tsc = new_tsc;
6074098a6e5SImre Vadász 		}
6084098a6e5SImre Vadász 		apic_delay_tsc /= 100;
6094098a6e5SImre Vadász 		kprintf(
6104098a6e5SImre Vadász 		    "LAPIC latency (in TSC ticks): %lu min: %lu max: %lu\n",
6114098a6e5SImre Vadász 		    apic_delay_tsc, min_apic_tsc, max_apic_tsc);
6124098a6e5SImre Vadász 		apic_delay_tsc = min_apic_tsc;
6134098a6e5SImre Vadász 	}
6144098a6e5SImre Vadász 
6154098a6e5SImre Vadász 	if (!use_tsc_calibration) {
6164098a6e5SImre Vadász 		int i;
6174098a6e5SImre Vadász 
6184098a6e5SImre Vadász 		/*
6194098a6e5SImre Vadász 		 * Do some exercising of the lapic timer access. This improves
6204098a6e5SImre Vadász 		 * precision of the subsequent calibration run in at least some
6214098a6e5SImre Vadász 		 * virtualization cases.
6224098a6e5SImre Vadász 		 */
6234098a6e5SImre Vadász 		lapic_timer_set_divisor(0);
6244098a6e5SImre Vadász 		for (i = 0; i < 10; i++)
6254098a6e5SImre Vadász 			(void)do_cputimer_calibration(100);
6264098a6e5SImre Vadász 	}
6273e8e985fSSepherosa Ziehau 	/* Try to calibrate the local APIC timer. */
6283e8e985fSSepherosa Ziehau 	for (lapic_timer_divisor_idx = 0;
6293e8e985fSSepherosa Ziehau 	     lapic_timer_divisor_idx < APIC_TIMER_NDIVISORS;
6303e8e985fSSepherosa Ziehau 	     lapic_timer_divisor_idx++) {
6313e8e985fSSepherosa Ziehau 		lapic_timer_set_divisor(lapic_timer_divisor_idx);
6324098a6e5SImre Vadász 		if (use_tsc_calibration) {
6334098a6e5SImre Vadász 			value = do_tsc_calibration(200*1000, apic_delay_tsc);
6344098a6e5SImre Vadász 		} else {
6354098a6e5SImre Vadász 			value = do_cputimer_calibration(2*1000*1000);
6364098a6e5SImre Vadász 		}
6374098a6e5SImre Vadász 		if (value != 0)
6383e8e985fSSepherosa Ziehau 			break;
6393e8e985fSSepherosa Ziehau 	}
6403e8e985fSSepherosa Ziehau 	if (lapic_timer_divisor_idx >= APIC_TIMER_NDIVISORS)
641ed20d0e3SSascha Wildner 		panic("lapic: no proper timer divisor?!");
6424098a6e5SImre Vadász 	lapic_cputimer_intr.freq = value;
6433e8e985fSSepherosa Ziehau 
6448fbc264dSMatthew Dillon 	kprintf("lapic: divisor index %d, frequency %lu Hz\n",
6453e8e985fSSepherosa Ziehau 		lapic_timer_divisor_idx, lapic_cputimer_intr.freq);
6464098a6e5SImre Vadász 
6474098a6e5SImre Vadász 	if (lapic_calibrate_test > 0) {
6484098a6e5SImre Vadász 		uint64_t freq;
6494098a6e5SImre Vadász 		int i;
6504098a6e5SImre Vadász 
6514098a6e5SImre Vadász 		for (i = 1; i <= 20; i++) {
6524098a6e5SImre Vadász 			if (use_tsc_calibration) {
6534098a6e5SImre Vadász 				freq = do_tsc_calibration(i*100*1000,
6544098a6e5SImre Vadász 							  apic_delay_tsc);
6554098a6e5SImre Vadász 			} else {
6564098a6e5SImre Vadász 				freq = do_cputimer_calibration(i*100*1000);
6574098a6e5SImre Vadász 			}
6584098a6e5SImre Vadász 			if (freq != 0)
6594098a6e5SImre Vadász 				kprintf("%ums: %lu\n", i * 100, freq);
6604098a6e5SImre Vadász 		}
6614098a6e5SImre Vadász 	}
6623e8e985fSSepherosa Ziehau }
6633e8e985fSSepherosa Ziehau 
6643e8e985fSSepherosa Ziehau static void
lapic_timer_tscdlt_reload(struct cputimer_intr * cti,sysclock_t reload)665185b27faSImre Vadász lapic_timer_tscdlt_reload(struct cputimer_intr *cti, sysclock_t reload)
666185b27faSImre Vadász {
667185b27faSImre Vadász 	struct globaldata *gd = mycpu;
668185b27faSImre Vadász 	uint64_t diff, now, val;
669185b27faSImre Vadász 
6708fbc264dSMatthew Dillon 	/*
6718fbc264dSMatthew Dillon 	 * Set maximum deadline to 60 seconds
6728fbc264dSMatthew Dillon 	 */
6738fbc264dSMatthew Dillon 	if (reload > sys_cputimer->freq * 60)
6748fbc264dSMatthew Dillon 		reload = sys_cputimer->freq * 60;
6758fbc264dSMatthew Dillon 	diff = muldivu64(reload, tsc_frequency, sys_cputimer->freq);
676185b27faSImre Vadász 	if (diff < 4)
677185b27faSImre Vadász 		diff = 4;
678185b27faSImre Vadász 	if (cpu_vendor_id == CPU_VENDOR_INTEL)
679185b27faSImre Vadász 		cpu_lfence();
680185b27faSImre Vadász 	else
681185b27faSImre Vadász 		cpu_mfence();
682185b27faSImre Vadász 	now = rdtsc();
683185b27faSImre Vadász 	val = now + diff;
684185b27faSImre Vadász 	if (gd->gd_timer_running) {
685185b27faSImre Vadász 		uint64_t deadline = tsc_deadlines[mycpuid].timestamp;
686185b27faSImre Vadász 		if (deadline == 0 || now > deadline || val < deadline) {
687185b27faSImre Vadász 			wrmsr(MSR_TSC_DEADLINE, val);
688185b27faSImre Vadász 			tsc_deadlines[mycpuid].timestamp = val;
689185b27faSImre Vadász 		}
690185b27faSImre Vadász 	} else {
691185b27faSImre Vadász 		gd->gd_timer_running = 1;
692185b27faSImre Vadász 		wrmsr(MSR_TSC_DEADLINE, val);
693185b27faSImre Vadász 		tsc_deadlines[mycpuid].timestamp = val;
694185b27faSImre Vadász 	}
695185b27faSImre Vadász }
696185b27faSImre Vadász 
697185b27faSImre Vadász static void
lapic_mem_timer_intr_reload(struct cputimer_intr * cti,sysclock_t reload)6988afc0c3dSSepherosa Ziehau lapic_mem_timer_intr_reload(struct cputimer_intr *cti, sysclock_t reload)
6993e8e985fSSepherosa Ziehau {
7003e8e985fSSepherosa Ziehau 	struct globaldata *gd = mycpu;
7013e8e985fSSepherosa Ziehau 
702feadd4aeSMatthew Dillon 	if ((ssysclock_t)reload < 0)
703feadd4aeSMatthew Dillon 		reload = 1;
7048fbc264dSMatthew Dillon 	reload = muldivu64(reload, cti->freq, sys_cputimer->freq);
7053e8e985fSSepherosa Ziehau 	if (reload < 2)
7063e8e985fSSepherosa Ziehau 		reload = 2;
7078fbc264dSMatthew Dillon 	if (reload > 0xFFFFFFFF)
7088fbc264dSMatthew Dillon 		reload = 0xFFFFFFFF;
7093e8e985fSSepherosa Ziehau 
7103e8e985fSSepherosa Ziehau 	if (gd->gd_timer_running) {
7118afc0c3dSSepherosa Ziehau 		if (reload < LAPIC_MEM_READ(ccr_timer))
7128fbc264dSMatthew Dillon 			LAPIC_MEM_WRITE(icr_timer, (uint32_t)reload);
7133e8e985fSSepherosa Ziehau 	} else {
7143e8e985fSSepherosa Ziehau 		gd->gd_timer_running = 1;
7158fbc264dSMatthew Dillon 		LAPIC_MEM_WRITE(icr_timer, (uint32_t)reload);
7163e8e985fSSepherosa Ziehau 	}
7173e8e985fSSepherosa Ziehau }
7183e8e985fSSepherosa Ziehau 
7193e8e985fSSepherosa Ziehau static void
lapic_msr_timer_intr_reload(struct cputimer_intr * cti,sysclock_t reload)720f89b4a45SSepherosa Ziehau lapic_msr_timer_intr_reload(struct cputimer_intr *cti, sysclock_t reload)
721f89b4a45SSepherosa Ziehau {
722f89b4a45SSepherosa Ziehau 	struct globaldata *gd = mycpu;
723f89b4a45SSepherosa Ziehau 
724feadd4aeSMatthew Dillon 	if ((ssysclock_t)reload < 0)
725feadd4aeSMatthew Dillon 		reload = 1;
7268fbc264dSMatthew Dillon 	reload = muldivu64(reload, cti->freq, sys_cputimer->freq);
727f89b4a45SSepherosa Ziehau 	if (reload < 2)
728f89b4a45SSepherosa Ziehau 		reload = 2;
7298fbc264dSMatthew Dillon 	if (reload > 0xFFFFFFFF)
7308fbc264dSMatthew Dillon 		reload = 0xFFFFFFFF;
731f89b4a45SSepherosa Ziehau 
732f89b4a45SSepherosa Ziehau 	if (gd->gd_timer_running) {
733f89b4a45SSepherosa Ziehau 		if (reload < LAPIC_MSR_READ(MSR_X2APIC_CCR_TIMER))
7348fbc264dSMatthew Dillon 			LAPIC_MSR_WRITE(MSR_X2APIC_ICR_TIMER, (uint32_t)reload);
735f89b4a45SSepherosa Ziehau 	} else {
736f89b4a45SSepherosa Ziehau 		gd->gd_timer_running = 1;
7378fbc264dSMatthew Dillon 		LAPIC_MSR_WRITE(MSR_X2APIC_ICR_TIMER, (uint32_t)reload);
738f89b4a45SSepherosa Ziehau 	}
739f89b4a45SSepherosa Ziehau }
740f89b4a45SSepherosa Ziehau 
741f89b4a45SSepherosa Ziehau static void
lapic_timer_intr_enable(struct cputimer_intr * cti __unused)7423e8e985fSSepherosa Ziehau lapic_timer_intr_enable(struct cputimer_intr *cti __unused)
7433e8e985fSSepherosa Ziehau {
7443e8e985fSSepherosa Ziehau 	uint32_t timer;
7453e8e985fSSepherosa Ziehau 
7468afc0c3dSSepherosa Ziehau 	timer = LAPIC_READ(lvt_timer);
747185b27faSImre Vadász 	timer &= ~(APIC_LVTT_MASKED | APIC_LVTT_PERIODIC | APIC_LVTT_TSCDLT);
748185b27faSImre Vadász 	if (lapic_use_tscdeadline)
749185b27faSImre Vadász 		timer |= APIC_LVTT_TSCDLT;
7508afc0c3dSSepherosa Ziehau 	LAPIC_WRITE(lvt_timer, timer);
751185b27faSImre Vadász 	if (lapic_use_tscdeadline)
752315d9939SImre Vadász 		cpu_mfence();
7533e8e985fSSepherosa Ziehau 
7543e8e985fSSepherosa Ziehau 	lapic_timer_fixup_handler(NULL);
7553e8e985fSSepherosa Ziehau }
7563e8e985fSSepherosa Ziehau 
7573e8e985fSSepherosa Ziehau static void
lapic_timer_fixup_handler(void * arg)7583e8e985fSSepherosa Ziehau lapic_timer_fixup_handler(void *arg)
7593e8e985fSSepherosa Ziehau {
7603e8e985fSSepherosa Ziehau 	int *started = arg;
7613e8e985fSSepherosa Ziehau 
7623e8e985fSSepherosa Ziehau 	if (started != NULL)
7633e8e985fSSepherosa Ziehau 		*started = 0;
7643e8e985fSSepherosa Ziehau 
76586b7a03aSSascha Wildner 	if (cpu_vendor_id == CPU_VENDOR_AMD) {
766b0e79059SSepherosa Ziehau 		int c1e_test = lapic_timer_c1e_test;
767b0e79059SSepherosa Ziehau 
768b0e79059SSepherosa Ziehau 		if (c1e_test < 0) {
769b0e79059SSepherosa Ziehau 			if (vmm_guest == VMM_GUEST_NONE) {
770b0e79059SSepherosa Ziehau 				c1e_test = 1;
771b0e79059SSepherosa Ziehau 			} else {
772b0e79059SSepherosa Ziehau 				/*
773b0e79059SSepherosa Ziehau 				 * Don't do this C1E testing and adjustment
774b0e79059SSepherosa Ziehau 				 * on virtual machines, the best case for
775b0e79059SSepherosa Ziehau 				 * accessing this MSR is a NOOP; the worst
776b0e79059SSepherosa Ziehau 				 * cases could be pretty nasty, e.g. crash.
777b0e79059SSepherosa Ziehau 				 */
778b0e79059SSepherosa Ziehau 				c1e_test = 0;
779b0e79059SSepherosa Ziehau 			}
780b0e79059SSepherosa Ziehau 		}
781b0e79059SSepherosa Ziehau 
7823e8e985fSSepherosa Ziehau 		/*
7833e8e985fSSepherosa Ziehau 		 * Detect the presence of C1E capability mostly on latest
7843e8e985fSSepherosa Ziehau 		 * dual-cores (or future) k8 family.  This feature renders
7853e8e985fSSepherosa Ziehau 		 * the local APIC timer dead, so we disable it by reading
7863e8e985fSSepherosa Ziehau 		 * the Interrupt Pending Message register and clearing both
7873e8e985fSSepherosa Ziehau 		 * C1eOnCmpHalt (bit 28) and SmiOnCmpHalt (bit 27).
7883e8e985fSSepherosa Ziehau 		 *
7893e8e985fSSepherosa Ziehau 		 * Reference:
7903e8e985fSSepherosa Ziehau 		 *   "BIOS and Kernel Developer's Guide for AMD NPT
7913e8e985fSSepherosa Ziehau 		 *    Family 0Fh Processors"
7923e8e985fSSepherosa Ziehau 		 *   #32559 revision 3.00
7933e8e985fSSepherosa Ziehau 		 */
7943e8e985fSSepherosa Ziehau 		if ((cpu_id & 0x00000f00) == 0x00000f00 &&
795cb46bbd1SMatthew Dillon 		    (cpu_id & 0x0fff0000) >= 0x00040000 &&
796b0e79059SSepherosa Ziehau 		    c1e_test) {
7973e8e985fSSepherosa Ziehau 			uint64_t msr;
7983e8e985fSSepherosa Ziehau 
7993e8e985fSSepherosa Ziehau 			msr = rdmsr(0xc0010055);
8003e8e985fSSepherosa Ziehau 			if (msr & 0x18000000) {
8013e8e985fSSepherosa Ziehau 				struct globaldata *gd = mycpu;
8023e8e985fSSepherosa Ziehau 
8033e8e985fSSepherosa Ziehau 				kprintf("cpu%d: AMD C1E detected\n",
8043e8e985fSSepherosa Ziehau 					gd->gd_cpuid);
8053e8e985fSSepherosa Ziehau 				wrmsr(0xc0010055, msr & ~0x18000000ULL);
8063e8e985fSSepherosa Ziehau 
8073e8e985fSSepherosa Ziehau 				/*
8083e8e985fSSepherosa Ziehau 				 * We are kinda stalled;
8093e8e985fSSepherosa Ziehau 				 * kick start again.
8103e8e985fSSepherosa Ziehau 				 */
8113e8e985fSSepherosa Ziehau 				gd->gd_timer_running = 1;
812185b27faSImre Vadász 				if (lapic_use_tscdeadline) {
813185b27faSImre Vadász 					/* Maybe reached in Virtual Machines? */
814185b27faSImre Vadász 					lapic_timer_tscdeadline_quick(5000);
815185b27faSImre Vadász 				} else {
8163e8e985fSSepherosa Ziehau 					lapic_timer_oneshot_quick(2);
817185b27faSImre Vadász 				}
8183e8e985fSSepherosa Ziehau 
8193e8e985fSSepherosa Ziehau 				if (started != NULL)
8203e8e985fSSepherosa Ziehau 					*started = 1;
8213e8e985fSSepherosa Ziehau 			}
8223e8e985fSSepherosa Ziehau 		}
8233e8e985fSSepherosa Ziehau 	}
8243e8e985fSSepherosa Ziehau }
8253e8e985fSSepherosa Ziehau 
8263e8e985fSSepherosa Ziehau static void
lapic_timer_restart_handler(void * dummy __unused)8273e8e985fSSepherosa Ziehau lapic_timer_restart_handler(void *dummy __unused)
8283e8e985fSSepherosa Ziehau {
8293e8e985fSSepherosa Ziehau 	int started;
8303e8e985fSSepherosa Ziehau 
8313e8e985fSSepherosa Ziehau 	lapic_timer_fixup_handler(&started);
8323e8e985fSSepherosa Ziehau 	if (!started) {
8333e8e985fSSepherosa Ziehau 		struct globaldata *gd = mycpu;
8343e8e985fSSepherosa Ziehau 
8353e8e985fSSepherosa Ziehau 		gd->gd_timer_running = 1;
836185b27faSImre Vadász 		if (lapic_use_tscdeadline) {
837185b27faSImre Vadász 			/* Maybe reached in Virtual Machines? */
838185b27faSImre Vadász 			lapic_timer_tscdeadline_quick(5000);
839185b27faSImre Vadász 		} else {
8403e8e985fSSepherosa Ziehau 			lapic_timer_oneshot_quick(2);
8413e8e985fSSepherosa Ziehau 		}
8423e8e985fSSepherosa Ziehau 	}
843185b27faSImre Vadász }
8443e8e985fSSepherosa Ziehau 
8453e8e985fSSepherosa Ziehau /*
8469d1f0c52SSascha Wildner  * This function is called only by ACPICA code currently:
8473e8e985fSSepherosa Ziehau  * - AMD C1E fixup.  AMD C1E only seems to happen after ACPI
8489d1f0c52SSascha Wildner  *   module controls PM.  So once ACPICA is attached, we try
8493e8e985fSSepherosa Ziehau  *   to apply the fixup to prevent LAPIC timer from hanging.
8503e8e985fSSepherosa Ziehau  */
8513e8e985fSSepherosa Ziehau static void
lapic_timer_intr_pmfixup(struct cputimer_intr * cti __unused)8523e8e985fSSepherosa Ziehau lapic_timer_intr_pmfixup(struct cputimer_intr *cti __unused)
8533e8e985fSSepherosa Ziehau {
8543e8e985fSSepherosa Ziehau 	lwkt_send_ipiq_mask(smp_active_mask,
8553e8e985fSSepherosa Ziehau 			    lapic_timer_fixup_handler, NULL);
8563e8e985fSSepherosa Ziehau }
8573e8e985fSSepherosa Ziehau 
8583e8e985fSSepherosa Ziehau static void
lapic_timer_intr_restart(struct cputimer_intr * cti __unused)8593e8e985fSSepherosa Ziehau lapic_timer_intr_restart(struct cputimer_intr *cti __unused)
8603e8e985fSSepherosa Ziehau {
8613e8e985fSSepherosa Ziehau 	lwkt_send_ipiq_mask(smp_active_mask, lapic_timer_restart_handler, NULL);
8623e8e985fSSepherosa Ziehau }
8633e8e985fSSepherosa Ziehau 
8643e8e985fSSepherosa Ziehau 
8653e8e985fSSepherosa Ziehau /*
8663e8e985fSSepherosa Ziehau  * dump contents of local APIC registers
8673e8e985fSSepherosa Ziehau  */
8683e8e985fSSepherosa Ziehau void
apic_dump(char * str)8693e8e985fSSepherosa Ziehau apic_dump(char* str)
8703e8e985fSSepherosa Ziehau {
8713e8e985fSSepherosa Ziehau 	kprintf("SMP: CPU%d %s:\n", mycpu->gd_cpuid, str);
8723e8e985fSSepherosa Ziehau 	kprintf("     lint0: 0x%08x lint1: 0x%08x TPR: 0x%08x SVR: 0x%08x\n",
8738afc0c3dSSepherosa Ziehau 		LAPIC_READ(lvt_lint0), LAPIC_READ(lvt_lint1), LAPIC_READ(tpr),
8748afc0c3dSSepherosa Ziehau 		LAPIC_READ(svr));
8753e8e985fSSepherosa Ziehau }
8763e8e985fSSepherosa Ziehau 
8773e8e985fSSepherosa Ziehau /*
8783e8e985fSSepherosa Ziehau  * Inter Processor Interrupt functions.
8793e8e985fSSepherosa Ziehau  */
8803e8e985fSSepherosa Ziehau 
8818afc0c3dSSepherosa Ziehau static __inline void
lapic_mem_icr_unpend(const char * func)8828afc0c3dSSepherosa Ziehau lapic_mem_icr_unpend(const char *func)
8838afc0c3dSSepherosa Ziehau {
8848afc0c3dSSepherosa Ziehau 	if (LAPIC_MEM_READ(icr_lo) & APIC_DELSTAT_PEND) {
8858afc0c3dSSepherosa Ziehau 		int64_t tsc;
8868afc0c3dSSepherosa Ziehau 		int loops = 1;
8878afc0c3dSSepherosa Ziehau 
8888afc0c3dSSepherosa Ziehau 		tsc = rdtsc();
8898afc0c3dSSepherosa Ziehau 		while (LAPIC_MEM_READ(icr_lo) & APIC_DELSTAT_PEND) {
8908afc0c3dSSepherosa Ziehau 			cpu_pause();
8918afc0c3dSSepherosa Ziehau 			if ((tsc_sclock_t)(rdtsc() -
8928afc0c3dSSepherosa Ziehau 					   (tsc + tsc_frequency)) > 0) {
8938afc0c3dSSepherosa Ziehau 				tsc = rdtsc();
8948afc0c3dSSepherosa Ziehau 				if (++loops > 30) {
8958afc0c3dSSepherosa Ziehau 					panic("%s: cpu%d apic stalled",
8968afc0c3dSSepherosa Ziehau 					    func, mycpuid);
8978afc0c3dSSepherosa Ziehau 				} else {
8988afc0c3dSSepherosa Ziehau 					kprintf("%s: cpu%d apic stalled\n",
8998afc0c3dSSepherosa Ziehau 					    func, mycpuid);
9008afc0c3dSSepherosa Ziehau 				}
9018afc0c3dSSepherosa Ziehau 			}
9028afc0c3dSSepherosa Ziehau 		}
9038afc0c3dSSepherosa Ziehau 	}
9048afc0c3dSSepherosa Ziehau }
9058afc0c3dSSepherosa Ziehau 
9063e8e985fSSepherosa Ziehau /*
9073e8e985fSSepherosa Ziehau  * Send APIC IPI 'vector' to 'destType' via 'deliveryMode'.
9083e8e985fSSepherosa Ziehau  *
9093e8e985fSSepherosa Ziehau  *  destType is 1 of: APIC_DEST_SELF, APIC_DEST_ALLISELF, APIC_DEST_ALLESELF
9103e8e985fSSepherosa Ziehau  *  vector is any valid SYSTEM INT vector
9113e8e985fSSepherosa Ziehau  *  delivery_mode is 1 of: APIC_DELMODE_FIXED, APIC_DELMODE_LOWPRIO
9123e8e985fSSepherosa Ziehau  *
913c17a6852SMatthew Dillon  * WARNINGS!
914c17a6852SMatthew Dillon  *
915c17a6852SMatthew Dillon  * We now implement a per-cpu interlock (gd->gd_npoll) to prevent more than
916c17a6852SMatthew Dillon  * one IPI from being sent to any given cpu at a time.  Thus we no longer
917c17a6852SMatthew Dillon  * have to process incoming IPIs while waiting for the status to clear.
918c17a6852SMatthew Dillon  * No deadlock should be possible.
919c17a6852SMatthew Dillon  *
920c17a6852SMatthew Dillon  * We now physically disable interrupts for the lapic ICR operation.  If
921c17a6852SMatthew Dillon  * we do not do this then it looks like an EOI sent to the lapic (which
922c17a6852SMatthew Dillon  * occurs even with a critical section) can interfere with the command
923c17a6852SMatthew Dillon  * register ready status and cause an IPI to be lost.
924c17a6852SMatthew Dillon  *
925c17a6852SMatthew Dillon  * e.g. an interrupt can occur, issue the EOI, IRET, and cause the command
926c17a6852SMatthew Dillon  * register to busy just before we write to icr_lo, resulting in a lost
927c17a6852SMatthew Dillon  * issuance.  This only appears to occur on Intel cpus and is not
928c17a6852SMatthew Dillon  * documented.  It could simply be that cpus are so fast these days that
929c17a6852SMatthew Dillon  * it was always an issue, but is only now rearing its ugly head.  This
930c17a6852SMatthew Dillon  * is conjecture.
9313e8e985fSSepherosa Ziehau  */
9328afc0c3dSSepherosa Ziehau static int
lapic_mem_ipi(int dest_type,int vector,int delivery_mode)9338afc0c3dSSepherosa Ziehau lapic_mem_ipi(int dest_type, int vector, int delivery_mode)
9343e8e985fSSepherosa Ziehau {
9358afc0c3dSSepherosa Ziehau 	lapic_mem_icr_unpend(__func__);
9368afc0c3dSSepherosa Ziehau 	lapic_mem_icr_set(0,
9378afc0c3dSSepherosa Ziehau 	    dest_type | APIC_LEVEL_ASSERT | delivery_mode | vector);
9383e8e985fSSepherosa Ziehau 	return 0;
9393e8e985fSSepherosa Ziehau }
9403e8e985fSSepherosa Ziehau 
941f89b4a45SSepherosa Ziehau static int
lapic_msr_ipi(int dest_type,int vector,int delivery_mode)942f89b4a45SSepherosa Ziehau lapic_msr_ipi(int dest_type, int vector, int delivery_mode)
943f89b4a45SSepherosa Ziehau {
944f89b4a45SSepherosa Ziehau 	lapic_msr_icr_set(0,
945f89b4a45SSepherosa Ziehau 	    dest_type | APIC_LEVEL_ASSERT | delivery_mode | vector);
946f89b4a45SSepherosa Ziehau 	return 0;
947f89b4a45SSepherosa Ziehau }
948f89b4a45SSepherosa Ziehau 
94967534613SMatthew Dillon /*
95067534613SMatthew Dillon  * Interrupts must be hard-disabled by caller
95167534613SMatthew Dillon  */
9528afc0c3dSSepherosa Ziehau static void
lapic_mem_single_ipi(int cpu,int vector,int delivery_mode)9538afc0c3dSSepherosa Ziehau lapic_mem_single_ipi(int cpu, int vector, int delivery_mode)
9543e8e985fSSepherosa Ziehau {
9558afc0c3dSSepherosa Ziehau 	lapic_mem_icr_unpend(__func__);
9568afc0c3dSSepherosa Ziehau 	lapic_mem_icr_set(CPUID_TO_APICID(cpu),
9578afc0c3dSSepherosa Ziehau 	    APIC_DEST_DESTFLD | APIC_LEVEL_ASSERT | delivery_mode | vector);
95867534613SMatthew Dillon }
9593e8e985fSSepherosa Ziehau 
960f89b4a45SSepherosa Ziehau static void
lapic_msr_single_ipi(int cpu,int vector,int delivery_mode)961f89b4a45SSepherosa Ziehau lapic_msr_single_ipi(int cpu, int vector, int delivery_mode)
962f89b4a45SSepherosa Ziehau {
963f89b4a45SSepherosa Ziehau 	lapic_msr_icr_set(CPUID_TO_APICID(cpu),
964f89b4a45SSepherosa Ziehau 	    APIC_DEST_DESTFLD | APIC_LEVEL_ASSERT | delivery_mode | vector);
965f89b4a45SSepherosa Ziehau }
966f89b4a45SSepherosa Ziehau 
9673e8e985fSSepherosa Ziehau /*
9683e8e985fSSepherosa Ziehau  * Send APIC IPI 'vector' to 'target's via 'delivery_mode'.
9693e8e985fSSepherosa Ziehau  *
9703e8e985fSSepherosa Ziehau  * target is a bitmask of destination cpus.  Vector is any
9713e8e985fSSepherosa Ziehau  * valid system INT vector.  Delivery mode may be either
9723e8e985fSSepherosa Ziehau  * APIC_DELMODE_FIXED or APIC_DELMODE_LOWPRIO.
97367534613SMatthew Dillon  *
97467534613SMatthew Dillon  * Interrupts must be hard-disabled by caller
9753e8e985fSSepherosa Ziehau  */
9763e8e985fSSepherosa Ziehau void
selected_apic_ipi(cpumask_t target,int vector,int delivery_mode)9773e8e985fSSepherosa Ziehau selected_apic_ipi(cpumask_t target, int vector, int delivery_mode)
9783e8e985fSSepherosa Ziehau {
979c07315c4SMatthew Dillon 	while (CPUMASK_TESTNZERO(target)) {
9803e8e985fSSepherosa Ziehau 		int n = BSFCPUMASK(target);
981c07315c4SMatthew Dillon 		CPUMASK_NANDBIT(target, n);
9823e8e985fSSepherosa Ziehau 		single_apic_ipi(n, vector, delivery_mode);
9833e8e985fSSepherosa Ziehau 	}
9843e8e985fSSepherosa Ziehau }
9853e8e985fSSepherosa Ziehau 
9863e8e985fSSepherosa Ziehau /*
9873e8e985fSSepherosa Ziehau  * Load a 'downcount time' in uSeconds.
9883e8e985fSSepherosa Ziehau  */
9893e8e985fSSepherosa Ziehau void
set_apic_timer(int us)9903e8e985fSSepherosa Ziehau set_apic_timer(int us)
9913e8e985fSSepherosa Ziehau {
9923e8e985fSSepherosa Ziehau 	u_int count;
9933e8e985fSSepherosa Ziehau 
994185b27faSImre Vadász 	if (lapic_use_tscdeadline) {
995185b27faSImre Vadász 		uint64_t val;
996185b27faSImre Vadász 
997185b27faSImre Vadász 		val = lapic_scale_to_tsc(us, 1000000);
998185b27faSImre Vadász 		val += rdtsc();
999185b27faSImre Vadász 		/* No need to arm the lapic here, just track the timeout. */
1000185b27faSImre Vadász 		tsc_deadlines[mycpuid].downcount_time = val;
1001185b27faSImre Vadász 		return;
1002185b27faSImre Vadász 	}
1003185b27faSImre Vadász 
10043e8e985fSSepherosa Ziehau 	/*
10053e8e985fSSepherosa Ziehau 	 * When we reach here, lapic timer's frequency
10063e8e985fSSepherosa Ziehau 	 * must have been calculated as well as the
10073e8e985fSSepherosa Ziehau 	 * divisor (lapic->dcr_timer is setup during the
10083e8e985fSSepherosa Ziehau 	 * divisor calculation).
10093e8e985fSSepherosa Ziehau 	 */
10103e8e985fSSepherosa Ziehau 	KKASSERT(lapic_cputimer_intr.freq != 0 &&
10113e8e985fSSepherosa Ziehau 		 lapic_timer_divisor_idx >= 0);
10123e8e985fSSepherosa Ziehau 
10133e8e985fSSepherosa Ziehau 	count = ((us * (int64_t)lapic_cputimer_intr.freq) + 999999) / 1000000;
10143e8e985fSSepherosa Ziehau 	lapic_timer_oneshot(count);
10153e8e985fSSepherosa Ziehau }
10163e8e985fSSepherosa Ziehau 
10173e8e985fSSepherosa Ziehau 
10183e8e985fSSepherosa Ziehau /*
10191a8f6d13SImre Vadász  * Read remaining time in timer, in microseconds (rounded up).
10203e8e985fSSepherosa Ziehau  */
10213e8e985fSSepherosa Ziehau int
read_apic_timer(void)10223e8e985fSSepherosa Ziehau read_apic_timer(void)
10233e8e985fSSepherosa Ziehau {
10241a8f6d13SImre Vadász 	uint64_t val;
10251a8f6d13SImre Vadász 
1026185b27faSImre Vadász 	if (lapic_use_tscdeadline) {
1027185b27faSImre Vadász 		uint64_t now;
1028185b27faSImre Vadász 
1029185b27faSImre Vadász 		val = tsc_deadlines[mycpuid].downcount_time;
1030185b27faSImre Vadász 		now = rdtsc();
1031185b27faSImre Vadász 		if (val == 0 || now > val) {
1032185b27faSImre Vadász 			return 0;
1033185b27faSImre Vadász 		} else {
1034185b27faSImre Vadász 			val -= now;
1035185b27faSImre Vadász 			val *= 1000000;
1036185b27faSImre Vadász 			val += (tsc_frequency - 1);
1037185b27faSImre Vadász 			val /= tsc_frequency;
1038185b27faSImre Vadász 			if (val > INT_MAX)
1039185b27faSImre Vadász 				val = INT_MAX;
1040185b27faSImre Vadász 			return val;
1041185b27faSImre Vadász 		}
1042185b27faSImre Vadász 	}
1043185b27faSImre Vadász 
10448afc0c3dSSepherosa Ziehau 	val = LAPIC_READ(ccr_timer);
10451a8f6d13SImre Vadász 	if (val == 0)
10461a8f6d13SImre Vadász 		return 0;
1047185b27faSImre Vadász 
10481a8f6d13SImre Vadász 	KKASSERT(lapic_cputimer_intr.freq > 0);
10491a8f6d13SImre Vadász 	val *= 1000000;
10501a8f6d13SImre Vadász 	val += (lapic_cputimer_intr.freq - 1);
10511a8f6d13SImre Vadász 	val /= lapic_cputimer_intr.freq;
1052185b27faSImre Vadász 	if (val > INT_MAX)
1053185b27faSImre Vadász 		val = INT_MAX;
10541a8f6d13SImre Vadász 	return val;
10553e8e985fSSepherosa Ziehau }
10563e8e985fSSepherosa Ziehau 
10573e8e985fSSepherosa Ziehau 
10583e8e985fSSepherosa Ziehau /*
10593e8e985fSSepherosa Ziehau  * Spin-style delay, set delay time in uS, spin till it drains.
10603e8e985fSSepherosa Ziehau  */
10613e8e985fSSepherosa Ziehau void
u_sleep(int count)10623e8e985fSSepherosa Ziehau u_sleep(int count)
10633e8e985fSSepherosa Ziehau {
10643e8e985fSSepherosa Ziehau 	set_apic_timer(count);
10653e8e985fSSepherosa Ziehau 	while (read_apic_timer())
10663e8e985fSSepherosa Ziehau 		 /* spin */ ;
10673e8e985fSSepherosa Ziehau }
10683e8e985fSSepherosa Ziehau 
10693e8e985fSSepherosa Ziehau int
lapic_unused_apic_id(int start)10703e8e985fSSepherosa Ziehau lapic_unused_apic_id(int start)
10713e8e985fSSepherosa Ziehau {
10723e8e985fSSepherosa Ziehau 	int i;
10733e8e985fSSepherosa Ziehau 
1074740024d3SSepherosa Ziehau 	for (i = start; i < APICID_MAX; ++i) {
1075fbac0dc4SSepherosa Ziehau 		if (APICID_TO_CPUID(i) == -1)
10763e8e985fSSepherosa Ziehau 			return i;
10773e8e985fSSepherosa Ziehau 	}
10783e8e985fSSepherosa Ziehau 	return NAPICID;
10793e8e985fSSepherosa Ziehau }
10803e8e985fSSepherosa Ziehau 
10813e8e985fSSepherosa Ziehau void
lapic_map(vm_paddr_t lapic_addr)108213481c87SSepherosa Ziehau lapic_map(vm_paddr_t lapic_addr)
10833e8e985fSSepherosa Ziehau {
10848afc0c3dSSepherosa Ziehau 	lapic_mem = pmap_mapdev_uncacheable(lapic_addr, sizeof(struct LAPIC));
10853e8e985fSSepherosa Ziehau }
10863e8e985fSSepherosa Ziehau 
1087f89b4a45SSepherosa Ziehau void
lapic_x2apic_enter(boolean_t bsp)1088f89b4a45SSepherosa Ziehau lapic_x2apic_enter(boolean_t bsp)
1089f89b4a45SSepherosa Ziehau {
1090f89b4a45SSepherosa Ziehau 	uint64_t apic_base;
1091f89b4a45SSepherosa Ziehau 
1092f89b4a45SSepherosa Ziehau 	KASSERT(x2apic_enable, ("X2APIC mode is not enabled"));
1093f89b4a45SSepherosa Ziehau 
1094f89b4a45SSepherosa Ziehau 	/*
1095f89b4a45SSepherosa Ziehau 	 * X2APIC mode is requested, if it has not been enabled by the BIOS,
1096f89b4a45SSepherosa Ziehau 	 * enable it now.
1097f89b4a45SSepherosa Ziehau 	 */
1098f89b4a45SSepherosa Ziehau 	apic_base = rdmsr(MSR_APICBASE);
1099f89b4a45SSepherosa Ziehau 	if ((apic_base & APICBASE_X2APIC) == 0) {
1100f89b4a45SSepherosa Ziehau 		wrmsr(MSR_APICBASE,
1101f89b4a45SSepherosa Ziehau 		    apic_base | APICBASE_X2APIC | APICBASE_ENABLED);
1102f89b4a45SSepherosa Ziehau 	}
1103f89b4a45SSepherosa Ziehau 	if (bsp) {
1104f89b4a45SSepherosa Ziehau 		lapic_eoi = lapic_msr_eoi;
1105f89b4a45SSepherosa Ziehau 		apic_ipi = lapic_msr_ipi;
1106f89b4a45SSepherosa Ziehau 		single_apic_ipi = lapic_msr_single_ipi;
1107f89b4a45SSepherosa Ziehau 		lapic_cputimer_intr.reload = lapic_msr_timer_intr_reload;
1108f89b4a45SSepherosa Ziehau 	}
1109f89b4a45SSepherosa Ziehau }
1110f89b4a45SSepherosa Ziehau 
11113e8e985fSSepherosa Ziehau static TAILQ_HEAD(, lapic_enumerator) lapic_enumerators =
11123e8e985fSSepherosa Ziehau 	TAILQ_HEAD_INITIALIZER(lapic_enumerators);
11133e8e985fSSepherosa Ziehau 
11143566408bSSepherosa Ziehau int
lapic_config(void)11153e8e985fSSepherosa Ziehau lapic_config(void)
11163e8e985fSSepherosa Ziehau {
11173e8e985fSSepherosa Ziehau 	struct lapic_enumerator *e;
1118f89b4a45SSepherosa Ziehau 	uint64_t apic_base;
11192e0ed166SSepherosa Ziehau 	int error, i, ap_max;
11202e0ed166SSepherosa Ziehau 
11212e0ed166SSepherosa Ziehau 	KKASSERT(lapic_enable);
11223e8e985fSSepherosa Ziehau 
1123f89b4a45SSepherosa Ziehau 	lapic_eoi = lapic_mem_eoi;
1124f89b4a45SSepherosa Ziehau 	apic_ipi = lapic_mem_ipi;
1125f89b4a45SSepherosa Ziehau 	single_apic_ipi = lapic_mem_single_ipi;
1126f89b4a45SSepherosa Ziehau 
1127f89b4a45SSepherosa Ziehau 	TUNABLE_INT_FETCH("hw.x2apic_enable", &x2apic_enable);
1128f89b4a45SSepherosa Ziehau 	if (x2apic_enable < 0)
1129f89b4a45SSepherosa Ziehau 		x2apic_enable = 1;
1130f89b4a45SSepherosa Ziehau 	if ((cpu_feature2 & CPUID2_X2APIC) == 0) {
1131f89b4a45SSepherosa Ziehau 		/* X2APIC is not supported. */
1132f89b4a45SSepherosa Ziehau 		x2apic_enable = 0;
1133add2647eSMatthew Dillon 	} else {
1134f89b4a45SSepherosa Ziehau 		/*
1135f89b4a45SSepherosa Ziehau 		 * If the BIOS enabled the X2APIC mode, then we would stick
1136f89b4a45SSepherosa Ziehau 		 * with the X2APIC mode.
1137f89b4a45SSepherosa Ziehau 		 */
1138f89b4a45SSepherosa Ziehau 		apic_base = rdmsr(MSR_APICBASE);
1139f89b4a45SSepherosa Ziehau 		if (apic_base & APICBASE_X2APIC) {
1140add2647eSMatthew Dillon 			if (x2apic_enable == 0)
1141add2647eSMatthew Dillon 				kprintf("LAPIC: BIOS enabled X2APIC mode, force on\n");
1142add2647eSMatthew Dillon 			else
1143f89b4a45SSepherosa Ziehau 				kprintf("LAPIC: BIOS enabled X2APIC mode\n");
1144f89b4a45SSepherosa Ziehau 			x2apic_enable = 1;
1145f89b4a45SSepherosa Ziehau 		}
1146f89b4a45SSepherosa Ziehau 	}
1147add2647eSMatthew Dillon 	if (cpu_feature2 & CPUID2_X2APIC) {
1148add2647eSMatthew Dillon 		apic_base = rdmsr(MSR_APICBASE);
1149add2647eSMatthew Dillon 		if (apic_base & APICBASE_X2APIC)
1150add2647eSMatthew Dillon 			kprintf("LAPIC: BIOS already enabled X2APIC mode\n");
1151add2647eSMatthew Dillon 	}
1152f89b4a45SSepherosa Ziehau 
1153f89b4a45SSepherosa Ziehau 	if (x2apic_enable) {
1154f89b4a45SSepherosa Ziehau 		/*
1155f89b4a45SSepherosa Ziehau 		 * Enter X2APIC mode.
1156f89b4a45SSepherosa Ziehau 		 */
1157f89b4a45SSepherosa Ziehau 		kprintf("LAPIC: enter X2APIC mode\n");
1158f89b4a45SSepherosa Ziehau 		lapic_x2apic_enter(TRUE);
1159f89b4a45SSepherosa Ziehau 	}
1160f89b4a45SSepherosa Ziehau 
11613e8e985fSSepherosa Ziehau 	for (i = 0; i < NAPICID; ++i)
1162fbac0dc4SSepherosa Ziehau 		APICID_TO_CPUID(i) = -1;
11633e8e985fSSepherosa Ziehau 
11643e8e985fSSepherosa Ziehau 	TAILQ_FOREACH(e, &lapic_enumerators, lapic_link) {
11653e8e985fSSepherosa Ziehau 		error = e->lapic_probe(e);
11663e8e985fSSepherosa Ziehau 		if (!error)
11673e8e985fSSepherosa Ziehau 			break;
11683e8e985fSSepherosa Ziehau 	}
11693566408bSSepherosa Ziehau 	if (e == NULL) {
11703566408bSSepherosa Ziehau 		kprintf("LAPIC: Can't find LAPIC\n");
11713566408bSSepherosa Ziehau 		return ENXIO;
11723566408bSSepherosa Ziehau 	}
11733e8e985fSSepherosa Ziehau 
1174f8ae0475SSepherosa Ziehau 	error = e->lapic_enumerate(e);
1175f8ae0475SSepherosa Ziehau 	if (error) {
1176f8ae0475SSepherosa Ziehau 		kprintf("LAPIC: enumeration failed\n");
1177f8ae0475SSepherosa Ziehau 		return ENXIO;
1178f8ae0475SSepherosa Ziehau 	}
117999ddb995SSepherosa Ziehau 
11808afc0c3dSSepherosa Ziehau 	/* LAPIC is usable now. */
11818afc0c3dSSepherosa Ziehau 	lapic_usable = 1;
11828afc0c3dSSepherosa Ziehau 
118399ddb995SSepherosa Ziehau 	ap_max = MAXCPU - 1;
118499ddb995SSepherosa Ziehau 	TUNABLE_INT_FETCH("hw.ap_max", &ap_max);
118599ddb995SSepherosa Ziehau 	if (ap_max > MAXCPU - 1)
118699ddb995SSepherosa Ziehau 		ap_max = MAXCPU - 1;
118799ddb995SSepherosa Ziehau 
1188637df2f6SSepherosa Ziehau 	if (naps > ap_max) {
118999ddb995SSepherosa Ziehau 		kprintf("LAPIC: Warning use only %d out of %d "
119099ddb995SSepherosa Ziehau 			"available APs\n",
1191637df2f6SSepherosa Ziehau 			ap_max, naps);
1192637df2f6SSepherosa Ziehau 		naps = ap_max;
119399ddb995SSepherosa Ziehau 	}
119499ddb995SSepherosa Ziehau 
11953566408bSSepherosa Ziehau 	return 0;
11963e8e985fSSepherosa Ziehau }
11973e8e985fSSepherosa Ziehau 
11983e8e985fSSepherosa Ziehau void
lapic_enumerator_register(struct lapic_enumerator * ne)11993e8e985fSSepherosa Ziehau lapic_enumerator_register(struct lapic_enumerator *ne)
12003e8e985fSSepherosa Ziehau {
12013e8e985fSSepherosa Ziehau 	struct lapic_enumerator *e;
12023e8e985fSSepherosa Ziehau 
12033e8e985fSSepherosa Ziehau 	TAILQ_FOREACH(e, &lapic_enumerators, lapic_link) {
12043e8e985fSSepherosa Ziehau 		if (e->lapic_prio < ne->lapic_prio) {
12053e8e985fSSepherosa Ziehau 			TAILQ_INSERT_BEFORE(e, ne, lapic_link);
12063e8e985fSSepherosa Ziehau 			return;
12073e8e985fSSepherosa Ziehau 		}
12083e8e985fSSepherosa Ziehau 	}
12093e8e985fSSepherosa Ziehau 	TAILQ_INSERT_TAIL(&lapic_enumerators, ne, lapic_link);
12103e8e985fSSepherosa Ziehau }
12110e44391aSSepherosa Ziehau 
12120e44391aSSepherosa Ziehau void
lapic_set_cpuid(int cpu_id,int apic_id)12130e44391aSSepherosa Ziehau lapic_set_cpuid(int cpu_id, int apic_id)
12140e44391aSSepherosa Ziehau {
1215fbac0dc4SSepherosa Ziehau 	CPUID_TO_APICID(cpu_id) = apic_id;
1216fbac0dc4SSepherosa Ziehau 	APICID_TO_CPUID(apic_id) = cpu_id;
12170e44391aSSepherosa Ziehau }
1218944562dfSSepherosa Ziehau 
1219944562dfSSepherosa Ziehau void
lapic_fixup_noioapic(void)1220944562dfSSepherosa Ziehau lapic_fixup_noioapic(void)
1221944562dfSSepherosa Ziehau {
1222944562dfSSepherosa Ziehau 	u_int   temp;
1223944562dfSSepherosa Ziehau 
1224944562dfSSepherosa Ziehau 	/* Only allowed on BSP */
1225944562dfSSepherosa Ziehau 	KKASSERT(mycpuid == 0);
1226f45bfca0SSepherosa Ziehau 	KKASSERT(!ioapic_enable);
1227944562dfSSepherosa Ziehau 
12288afc0c3dSSepherosa Ziehau 	temp = LAPIC_READ(lvt_lint0);
1229944562dfSSepherosa Ziehau 	temp &= ~APIC_LVT_MASKED;
12308afc0c3dSSepherosa Ziehau 	LAPIC_WRITE(lvt_lint0, temp);
1231944562dfSSepherosa Ziehau 
12328afc0c3dSSepherosa Ziehau 	temp = LAPIC_READ(lvt_lint1);
1233944562dfSSepherosa Ziehau 	temp |= APIC_LVT_MASKED;
12348afc0c3dSSepherosa Ziehau 	LAPIC_WRITE(lvt_lint1, temp);
1235944562dfSSepherosa Ziehau }
12363a69c113SSepherosa Ziehau 
12373a69c113SSepherosa Ziehau static void
lapic_mem_eoi(void)12388afc0c3dSSepherosa Ziehau lapic_mem_eoi(void)
12393c38fc60SSepherosa Ziehau {
12408afc0c3dSSepherosa Ziehau 	log_lapic(mem_eoi);
12418afc0c3dSSepherosa Ziehau 	LAPIC_MEM_WRITE(eoi, 0);
12428afc0c3dSSepherosa Ziehau }
12438afc0c3dSSepherosa Ziehau 
12448afc0c3dSSepherosa Ziehau static void
lapic_msr_eoi(void)1245f89b4a45SSepherosa Ziehau lapic_msr_eoi(void)
1246f89b4a45SSepherosa Ziehau {
1247f89b4a45SSepherosa Ziehau 	log_lapic(msr_eoi);
1248f89b4a45SSepherosa Ziehau 	LAPIC_MSR_WRITE(MSR_X2APIC_EOI, 0);
1249f89b4a45SSepherosa Ziehau }
1250f89b4a45SSepherosa Ziehau 
1251f89b4a45SSepherosa Ziehau static void
lapic_mem_seticr_sync(uint32_t apic_id,uint32_t icr_lo_val)12528afc0c3dSSepherosa Ziehau lapic_mem_seticr_sync(uint32_t apic_id, uint32_t icr_lo_val)
12538afc0c3dSSepherosa Ziehau {
12548afc0c3dSSepherosa Ziehau 	lapic_mem_icr_set(apic_id, icr_lo_val);
12558afc0c3dSSepherosa Ziehau 	while (LAPIC_MEM_READ(icr_lo) & APIC_DELSTAT_PEND)
12568afc0c3dSSepherosa Ziehau 		/* spin */;
12578afc0c3dSSepherosa Ziehau }
12588afc0c3dSSepherosa Ziehau 
12598afc0c3dSSepherosa Ziehau void
lapic_seticr_sync(uint32_t apic_id,uint32_t icr_lo_val)12608afc0c3dSSepherosa Ziehau lapic_seticr_sync(uint32_t apic_id, uint32_t icr_lo_val)
12618afc0c3dSSepherosa Ziehau {
1262f89b4a45SSepherosa Ziehau 	if (x2apic_enable)
1263f89b4a45SSepherosa Ziehau 		lapic_msr_icr_set(apic_id, icr_lo_val);
1264f89b4a45SSepherosa Ziehau 	else
12658afc0c3dSSepherosa Ziehau 		lapic_mem_seticr_sync(apic_id, icr_lo_val);
12663c38fc60SSepherosa Ziehau }
12673c38fc60SSepherosa Ziehau 
12683c38fc60SSepherosa Ziehau static void
lapic_sysinit(void * dummy __unused)12693a69c113SSepherosa Ziehau lapic_sysinit(void *dummy __unused)
12703a69c113SSepherosa Ziehau {
12713a69c113SSepherosa Ziehau 	if (lapic_enable) {
12723a69c113SSepherosa Ziehau 		int error;
12733a69c113SSepherosa Ziehau 
12743a69c113SSepherosa Ziehau 		error = lapic_config();
12753a69c113SSepherosa Ziehau 		if (error)
12763a69c113SSepherosa Ziehau 			lapic_enable = 0;
12773a69c113SSepherosa Ziehau 	}
1278f89b4a45SSepherosa Ziehau 	if (!lapic_enable)
1279f89b4a45SSepherosa Ziehau 		x2apic_enable = 0;
12803a69c113SSepherosa Ziehau 
12813a69c113SSepherosa Ziehau 	if (lapic_enable) {
12823a69c113SSepherosa Ziehau 		/* Initialize BSP's local APIC */
12833a69c113SSepherosa Ziehau 		lapic_init(TRUE);
12843a69c113SSepherosa Ziehau 	} else if (ioapic_enable) {
12857ed7e1a5SMatthew Dillon 		kprintf("IOAPIC disabled - lapic was not enabled\n");
12863a69c113SSepherosa Ziehau 		ioapic_enable = 0;
12873a69c113SSepherosa Ziehau 		icu_reinit_noioapic();
12883a69c113SSepherosa Ziehau 	}
12893a69c113SSepherosa Ziehau }
1290f3f3eadbSSascha Wildner SYSINIT(lapic, SI_BOOT2_LAPIC, SI_ORDER_FIRST, lapic_sysinit, NULL);
1291