xref: /onnv-gate/usr/src/uts/i86pc/io/pcplusmp/apic.c (revision 1389:8a6ef7ca5644)
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
50Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
60Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
70Sstevel@tonic-gate  * with the License.
80Sstevel@tonic-gate  *
90Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
100Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
110Sstevel@tonic-gate  * See the License for the specific language governing permissions
120Sstevel@tonic-gate  * and limitations under the License.
130Sstevel@tonic-gate  *
140Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
150Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
160Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
170Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
180Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
190Sstevel@tonic-gate  *
200Sstevel@tonic-gate  * CDDL HEADER END
210Sstevel@tonic-gate  */
220Sstevel@tonic-gate /*
23*1389Sdmick  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
240Sstevel@tonic-gate  * Use is subject to license terms.
250Sstevel@tonic-gate  */
260Sstevel@tonic-gate 
270Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
280Sstevel@tonic-gate 
290Sstevel@tonic-gate /*
300Sstevel@tonic-gate  * PSMI 1.1 extensions are supported only in 2.6 and later versions.
310Sstevel@tonic-gate  * PSMI 1.2 extensions are supported only in 2.7 and later versions.
320Sstevel@tonic-gate  * PSMI 1.3 and 1.4 extensions are supported in Solaris 10.
330Sstevel@tonic-gate  * PSMI 1.5 extensions are supported in Solaris Nevada.
340Sstevel@tonic-gate  */
350Sstevel@tonic-gate #define	PSMI_1_5
360Sstevel@tonic-gate 
370Sstevel@tonic-gate #include <sys/processor.h>
380Sstevel@tonic-gate #include <sys/time.h>
390Sstevel@tonic-gate #include <sys/psm.h>
400Sstevel@tonic-gate #include <sys/smp_impldefs.h>
410Sstevel@tonic-gate #include <sys/cram.h>
420Sstevel@tonic-gate #include <sys/acpi/acpi.h>
430Sstevel@tonic-gate #include <sys/acpica.h>
440Sstevel@tonic-gate #include <sys/psm_common.h>
450Sstevel@tonic-gate #include "apic.h"
460Sstevel@tonic-gate #include <sys/pit.h>
470Sstevel@tonic-gate #include <sys/ddi.h>
480Sstevel@tonic-gate #include <sys/sunddi.h>
490Sstevel@tonic-gate #include <sys/ddi_impldefs.h>
500Sstevel@tonic-gate #include <sys/pci.h>
510Sstevel@tonic-gate #include <sys/promif.h>
520Sstevel@tonic-gate #include <sys/x86_archext.h>
530Sstevel@tonic-gate #include <sys/cpc_impl.h>
540Sstevel@tonic-gate #include <sys/uadmin.h>
550Sstevel@tonic-gate #include <sys/panic.h>
560Sstevel@tonic-gate #include <sys/debug.h>
570Sstevel@tonic-gate #include <sys/archsystm.h>
580Sstevel@tonic-gate #include <sys/trap.h>
590Sstevel@tonic-gate #include <sys/machsystm.h>
600Sstevel@tonic-gate #include <sys/cpuvar.h>
610Sstevel@tonic-gate #include <sys/rm_platter.h>
620Sstevel@tonic-gate #include <sys/privregs.h>
630Sstevel@tonic-gate #include <sys/cyclic.h>
640Sstevel@tonic-gate #include <sys/note.h>
650Sstevel@tonic-gate #include <sys/pci_intr_lib.h>
660Sstevel@tonic-gate 
670Sstevel@tonic-gate /*
680Sstevel@tonic-gate  *	Local Function Prototypes
690Sstevel@tonic-gate  */
700Sstevel@tonic-gate static void apic_init_intr();
710Sstevel@tonic-gate static void apic_ret();
720Sstevel@tonic-gate static int apic_handle_defconf();
730Sstevel@tonic-gate static int apic_parse_mpct(caddr_t mpct, int bypass);
740Sstevel@tonic-gate static struct apic_mpfps_hdr *apic_find_fps_sig(caddr_t fptr, int size);
750Sstevel@tonic-gate static int apic_checksum(caddr_t bptr, int len);
760Sstevel@tonic-gate static int get_apic_cmd1();
770Sstevel@tonic-gate static int get_apic_pri();
780Sstevel@tonic-gate static int apic_find_bus_type(char *bus);
790Sstevel@tonic-gate static int apic_find_bus(int busid);
800Sstevel@tonic-gate static int apic_find_bus_id(int bustype);
810Sstevel@tonic-gate static struct apic_io_intr *apic_find_io_intr(int irqno);
820Sstevel@tonic-gate int apic_allocate_irq(int irq);
830Sstevel@tonic-gate static int apic_find_free_irq(int start, int end);
840Sstevel@tonic-gate static uchar_t apic_allocate_vector(int ipl, int irq, int pri);
850Sstevel@tonic-gate static void apic_modify_vector(uchar_t vector, int irq);
860Sstevel@tonic-gate static void apic_mark_vector(uchar_t oldvector, uchar_t newvector);
870Sstevel@tonic-gate static uchar_t apic_xlate_vector(uchar_t oldvector);
880Sstevel@tonic-gate static void apic_xlate_vector_free_timeout_handler(void *arg);
890Sstevel@tonic-gate static void apic_free_vector(uchar_t vector);
900Sstevel@tonic-gate static void apic_reprogram_timeout_handler(void *arg);
910Sstevel@tonic-gate static int apic_check_stuck_interrupt(apic_irq_t *irq_ptr, int old_bind_cpu,
920Sstevel@tonic-gate     int new_bind_cpu, volatile int32_t *ioapic, int intin_no, int which_irq);
930Sstevel@tonic-gate static int apic_setup_io_intr(apic_irq_t *irqptr, int irq);
940Sstevel@tonic-gate static int apic_setup_io_intr_deferred(apic_irq_t *irqptr, int irq);
950Sstevel@tonic-gate static void apic_record_rdt_entry(apic_irq_t *irqptr, int irq);
960Sstevel@tonic-gate static struct apic_io_intr *apic_find_io_intr_w_busid(int irqno, int busid);
970Sstevel@tonic-gate static int apic_find_intin(uchar_t ioapic, uchar_t intin);
980Sstevel@tonic-gate static int apic_handle_pci_pci_bridge(dev_info_t *idip, int child_devno,
990Sstevel@tonic-gate     int child_ipin, struct apic_io_intr **intrp);
1000Sstevel@tonic-gate static int apic_setup_irq_table(dev_info_t *dip, int irqno,
1010Sstevel@tonic-gate     struct apic_io_intr *intrp, struct intrspec *ispec, iflag_t *intr_flagp,
1020Sstevel@tonic-gate     int type);
1030Sstevel@tonic-gate static int apic_setup_sci_irq_table(int irqno, uchar_t ipl,
1040Sstevel@tonic-gate     iflag_t *intr_flagp);
1050Sstevel@tonic-gate static void apic_nmi_intr(caddr_t arg);
1060Sstevel@tonic-gate uchar_t apic_bind_intr(dev_info_t *dip, int irq, uchar_t ioapicid,
1070Sstevel@tonic-gate     uchar_t intin);
1080Sstevel@tonic-gate static int apic_rebind(apic_irq_t *irq_ptr, int bind_cpu, int acquire_lock,
1090Sstevel@tonic-gate     int when);
110916Sschwartz int apic_rebind_all(apic_irq_t *irq_ptr, int bind_cpu, int safe);
1110Sstevel@tonic-gate static void apic_intr_redistribute();
1120Sstevel@tonic-gate static void apic_cleanup_busy();
1130Sstevel@tonic-gate static void apic_set_pwroff_method_from_mpcnfhdr(struct apic_mp_cnf_hdr *hdrp);
1140Sstevel@tonic-gate int apic_introp_xlate(dev_info_t *dip, struct intrspec *ispec, int type);
1150Sstevel@tonic-gate 
1160Sstevel@tonic-gate /* ACPI support routines */
1170Sstevel@tonic-gate static int acpi_probe(void);
1180Sstevel@tonic-gate static int apic_acpi_irq_configure(acpi_psm_lnk_t *acpipsmlnkp, dev_info_t *dip,
1190Sstevel@tonic-gate     int *pci_irqp, iflag_t *intr_flagp);
1200Sstevel@tonic-gate 
1210Sstevel@tonic-gate static int apic_acpi_translate_pci_irq(dev_info_t *dip, int busid, int devid,
1220Sstevel@tonic-gate     int ipin, int *pci_irqp, iflag_t *intr_flagp);
1230Sstevel@tonic-gate static uchar_t acpi_find_ioapic(int irq);
1240Sstevel@tonic-gate static int acpi_intr_compatible(iflag_t iflag1, iflag_t iflag2);
1250Sstevel@tonic-gate 
1260Sstevel@tonic-gate /*
1270Sstevel@tonic-gate  *	standard MP entries
1280Sstevel@tonic-gate  */
1290Sstevel@tonic-gate static int	apic_probe();
1300Sstevel@tonic-gate static int	apic_clkinit();
1310Sstevel@tonic-gate static int	apic_getclkirq(int ipl);
1320Sstevel@tonic-gate static uint_t	apic_calibrate(volatile uint32_t *addr,
1330Sstevel@tonic-gate     uint16_t *pit_ticks_adj);
1340Sstevel@tonic-gate static hrtime_t apic_gettime();
1350Sstevel@tonic-gate static hrtime_t apic_gethrtime();
1360Sstevel@tonic-gate static void	apic_init();
1370Sstevel@tonic-gate static void	apic_picinit(void);
1380Sstevel@tonic-gate static void	apic_cpu_start(processorid_t cpun, caddr_t rm_code);
1390Sstevel@tonic-gate static int	apic_post_cpu_start(void);
1400Sstevel@tonic-gate static void	apic_send_ipi(int cpun, int ipl);
1410Sstevel@tonic-gate static void	apic_set_softintr(int softintr);
1420Sstevel@tonic-gate static void	apic_set_idlecpu(processorid_t cpun);
1430Sstevel@tonic-gate static void	apic_unset_idlecpu(processorid_t cpun);
1440Sstevel@tonic-gate static int	apic_softlvl_to_irq(int ipl);
1450Sstevel@tonic-gate static int	apic_intr_enter(int ipl, int *vect);
1460Sstevel@tonic-gate static void	apic_intr_exit(int ipl, int vect);
1470Sstevel@tonic-gate static void	apic_setspl(int ipl);
1480Sstevel@tonic-gate static int	apic_addspl(int ipl, int vector, int min_ipl, int max_ipl);
1490Sstevel@tonic-gate static int	apic_delspl(int ipl, int vector, int min_ipl, int max_ipl);
1500Sstevel@tonic-gate static void	apic_shutdown(int cmd, int fcn);
1510Sstevel@tonic-gate static void	apic_preshutdown(int cmd, int fcn);
1520Sstevel@tonic-gate static int	apic_disable_intr(processorid_t cpun);
1530Sstevel@tonic-gate static void	apic_enable_intr(processorid_t cpun);
1540Sstevel@tonic-gate static processorid_t	apic_get_next_processorid(processorid_t cpun);
1550Sstevel@tonic-gate static int		apic_get_ipivect(int ipl, int type);
1560Sstevel@tonic-gate static void	apic_timer_reprogram(hrtime_t time);
1570Sstevel@tonic-gate static void	apic_timer_enable(void);
1580Sstevel@tonic-gate static void	apic_timer_disable(void);
1590Sstevel@tonic-gate static void	apic_post_cyclic_setup(void *arg);
1600Sstevel@tonic-gate extern int	apic_intr_ops(dev_info_t *, ddi_intr_handle_impl_t *,
1610Sstevel@tonic-gate 		    psm_intr_op_t, int *);
1620Sstevel@tonic-gate 
1630Sstevel@tonic-gate static int	apic_oneshot = 0;
1640Sstevel@tonic-gate int	apic_oneshot_enable = 1; /* to allow disabling one-shot capability */
1650Sstevel@tonic-gate 
1660Sstevel@tonic-gate /*
1670Sstevel@tonic-gate  * These variables are frequently accessed in apic_intr_enter(),
1680Sstevel@tonic-gate  * apic_intr_exit and apic_setspl, so group them together
1690Sstevel@tonic-gate  */
1700Sstevel@tonic-gate volatile uint32_t *apicadr =  NULL;	/* virtual addr of local APIC	*/
1710Sstevel@tonic-gate int apic_setspl_delay = 1;		/* apic_setspl - delay enable	*/
1720Sstevel@tonic-gate int apic_clkvect;
1730Sstevel@tonic-gate 
1740Sstevel@tonic-gate /* ACPI SCI interrupt configuration; -1 if SCI not used */
1750Sstevel@tonic-gate int apic_sci_vect = -1;
1760Sstevel@tonic-gate iflag_t apic_sci_flags;
1770Sstevel@tonic-gate 
1780Sstevel@tonic-gate /* vector at which error interrupts come in */
1790Sstevel@tonic-gate int apic_errvect;
1800Sstevel@tonic-gate int apic_enable_error_intr = 1;
1810Sstevel@tonic-gate int apic_error_display_delay = 100;
1820Sstevel@tonic-gate 
1830Sstevel@tonic-gate /* vector at which performance counter overflow interrupts come in */
1840Sstevel@tonic-gate int apic_cpcovf_vect;
1850Sstevel@tonic-gate int apic_enable_cpcovf_intr = 1;
1860Sstevel@tonic-gate 
1870Sstevel@tonic-gate /* Max wait time (in microsecs) for flags to clear in an RDT entry. */
1880Sstevel@tonic-gate static int apic_max_usecs_clear_pending = 1000;
1890Sstevel@tonic-gate 
1900Sstevel@tonic-gate /* Amt of usecs to wait before checking if RDT flags have reset. */
1910Sstevel@tonic-gate #define	APIC_USECS_PER_WAIT_INTERVAL 100
1920Sstevel@tonic-gate 
1930Sstevel@tonic-gate /* Maximum number of times to retry reprogramming via the timeout */
1940Sstevel@tonic-gate #define	APIC_REPROGRAM_MAX_TIMEOUTS 10
1950Sstevel@tonic-gate 
1960Sstevel@tonic-gate /* timeout delay for IOAPIC delayed reprogramming */
1970Sstevel@tonic-gate #define	APIC_REPROGRAM_TIMEOUT_DELAY 5 /* microseconds */
1980Sstevel@tonic-gate 
1990Sstevel@tonic-gate /* Parameter to apic_rebind(): Should reprogramming be done now or later? */
2000Sstevel@tonic-gate #define	DEFERRED 1
2010Sstevel@tonic-gate #define	IMMEDIATE 0
2020Sstevel@tonic-gate 
2030Sstevel@tonic-gate /*
2040Sstevel@tonic-gate  * number of bits per byte, from <sys/param.h>
2050Sstevel@tonic-gate  */
2060Sstevel@tonic-gate #define	UCHAR_MAX	((1 << NBBY) - 1)
2070Sstevel@tonic-gate 
2080Sstevel@tonic-gate uchar_t	apic_reserved_irqlist[MAX_ISA_IRQ];
2090Sstevel@tonic-gate 
2100Sstevel@tonic-gate /*
2110Sstevel@tonic-gate  * The following vector assignments influence the value of ipltopri and
2120Sstevel@tonic-gate  * vectortoipl. Note that vectors 0 - 0x1f are not used. We can program
2130Sstevel@tonic-gate  * idle to 0 and IPL 0 to 0x10 to differentiate idle in case
2140Sstevel@tonic-gate  * we care to do so in future. Note some IPLs which are rarely used
2150Sstevel@tonic-gate  * will share the vector ranges and heavily used IPLs (5 and 6) have
2160Sstevel@tonic-gate  * a wide range.
2170Sstevel@tonic-gate  *	IPL		Vector range.		as passed to intr_enter
2180Sstevel@tonic-gate  *	0		none.
2190Sstevel@tonic-gate  *	1,2,3		0x20-0x2f		0x0-0xf
2200Sstevel@tonic-gate  *	4		0x30-0x3f		0x10-0x1f
2210Sstevel@tonic-gate  *	5		0x40-0x5f		0x20-0x3f
2220Sstevel@tonic-gate  *	6		0x60-0x7f		0x40-0x5f
2230Sstevel@tonic-gate  *	7,8,9		0x80-0x8f		0x60-0x6f
2240Sstevel@tonic-gate  *	10		0x90-0x9f		0x70-0x7f
2250Sstevel@tonic-gate  *	11		0xa0-0xaf		0x80-0x8f
2260Sstevel@tonic-gate  *	...		...
2270Sstevel@tonic-gate  *	16		0xf0-0xff		0xd0-0xdf
2280Sstevel@tonic-gate  */
2290Sstevel@tonic-gate uchar_t apic_vectortoipl[APIC_AVAIL_VECTOR / APIC_VECTOR_PER_IPL] = {
2300Sstevel@tonic-gate 	3, 4, 5, 5, 6, 6, 9, 10, 11, 12, 13, 14, 15, 16
2310Sstevel@tonic-gate };
2320Sstevel@tonic-gate 	/*
2330Sstevel@tonic-gate 	 * The ipl of an ISR at vector X is apic_vectortoipl[X<<4]
2340Sstevel@tonic-gate 	 * NOTE that this is vector as passed into intr_enter which is
2350Sstevel@tonic-gate 	 * programmed vector - 0x20 (APIC_BASE_VECT)
2360Sstevel@tonic-gate 	 */
2370Sstevel@tonic-gate 
2380Sstevel@tonic-gate uchar_t	apic_ipltopri[MAXIPL + 1];	/* unix ipl to apic pri	*/
2390Sstevel@tonic-gate 	/* The taskpri to be programmed into apic to mask given ipl */
2400Sstevel@tonic-gate 
2410Sstevel@tonic-gate #if defined(__amd64)
2420Sstevel@tonic-gate uchar_t	apic_cr8pri[MAXIPL + 1];	/* unix ipl to cr8 pri	*/
2430Sstevel@tonic-gate #endif
2440Sstevel@tonic-gate 
2450Sstevel@tonic-gate /*
2460Sstevel@tonic-gate  * Patchable global variables.
2470Sstevel@tonic-gate  */
2480Sstevel@tonic-gate int	apic_forceload = 0;
2490Sstevel@tonic-gate 
2500Sstevel@tonic-gate #define	INTR_ROUND_ROBIN_WITH_AFFINITY	0
2510Sstevel@tonic-gate #define	INTR_ROUND_ROBIN		1
2520Sstevel@tonic-gate #define	INTR_LOWEST_PRIORITY		2
2530Sstevel@tonic-gate 
2540Sstevel@tonic-gate int	apic_intr_policy = INTR_ROUND_ROBIN_WITH_AFFINITY;
2550Sstevel@tonic-gate 
2560Sstevel@tonic-gate static int	apic_next_bind_cpu = 2; /* For round robin assignment */
2570Sstevel@tonic-gate 					/* start with cpu 1 */
2580Sstevel@tonic-gate 
2590Sstevel@tonic-gate int	apic_coarse_hrtime = 1;		/* 0 - use accurate slow gethrtime() */
2600Sstevel@tonic-gate 					/* 1 - use gettime() for performance */
2610Sstevel@tonic-gate int	apic_flat_model = 0;		/* 0 - clustered. 1 - flat */
2620Sstevel@tonic-gate int	apic_enable_hwsoftint = 0;	/* 0 - disable, 1 - enable	*/
2630Sstevel@tonic-gate int	apic_enable_bind_log = 1;	/* 1 - display interrupt binding log */
2640Sstevel@tonic-gate int	apic_panic_on_nmi = 0;
2650Sstevel@tonic-gate int	apic_panic_on_apic_error = 0;
2660Sstevel@tonic-gate 
2670Sstevel@tonic-gate int	apic_verbose = 0;
2680Sstevel@tonic-gate 
2690Sstevel@tonic-gate /* Flag definitions for apic_verbose */
2700Sstevel@tonic-gate #define	APIC_VERBOSE_IOAPIC_FLAG		0x00000001
2710Sstevel@tonic-gate #define	APIC_VERBOSE_IRQ_FLAG			0x00000002
2720Sstevel@tonic-gate #define	APIC_VERBOSE_POWEROFF_FLAG		0x00000004
2730Sstevel@tonic-gate #define	APIC_VERBOSE_POWEROFF_PAUSE_FLAG	0x00000008
2740Sstevel@tonic-gate 
2750Sstevel@tonic-gate 
2760Sstevel@tonic-gate #define	APIC_VERBOSE_IOAPIC(fmt) \
2770Sstevel@tonic-gate 	if (apic_verbose & APIC_VERBOSE_IOAPIC_FLAG) \
2780Sstevel@tonic-gate 		cmn_err fmt;
2790Sstevel@tonic-gate 
2800Sstevel@tonic-gate #define	APIC_VERBOSE_IRQ(fmt) \
2810Sstevel@tonic-gate 	if (apic_verbose & APIC_VERBOSE_IRQ_FLAG) \
2820Sstevel@tonic-gate 		cmn_err fmt;
2830Sstevel@tonic-gate 
2840Sstevel@tonic-gate #define	APIC_VERBOSE_POWEROFF(fmt) \
2850Sstevel@tonic-gate 	if (apic_verbose & APIC_VERBOSE_POWEROFF_FLAG) \
2860Sstevel@tonic-gate 		prom_printf fmt;
2870Sstevel@tonic-gate 
2880Sstevel@tonic-gate 
2890Sstevel@tonic-gate /* Now the ones for Dynamic Interrupt distribution */
290916Sschwartz int	apic_enable_dynamic_migration = 0;
2910Sstevel@tonic-gate 
2920Sstevel@tonic-gate /*
2930Sstevel@tonic-gate  * If enabled, the distribution works as follows:
2940Sstevel@tonic-gate  * On every interrupt entry, the current ipl for the CPU is set in cpu_info
2950Sstevel@tonic-gate  * and the irq corresponding to the ipl is also set in the aci_current array.
2960Sstevel@tonic-gate  * interrupt exit and setspl (due to soft interrupts) will cause the current
2970Sstevel@tonic-gate  * ipl to be be changed. This is cache friendly as these frequently used
2980Sstevel@tonic-gate  * paths write into a per cpu structure.
2990Sstevel@tonic-gate  *
3000Sstevel@tonic-gate  * Sampling is done by checking the structures for all CPUs and incrementing
3010Sstevel@tonic-gate  * the busy field of the irq (if any) executing on each CPU and the busy field
3020Sstevel@tonic-gate  * of the corresponding CPU.
3030Sstevel@tonic-gate  * In periodic mode this is done on every clock interrupt.
3040Sstevel@tonic-gate  * In one-shot mode, this is done thru a cyclic with an interval of
3050Sstevel@tonic-gate  * apic_redistribute_sample_interval (default 10 milli sec).
3060Sstevel@tonic-gate  *
3070Sstevel@tonic-gate  * Every apic_sample_factor_redistribution times we sample, we do computations
3080Sstevel@tonic-gate  * to decide which interrupt needs to be migrated (see comments
3090Sstevel@tonic-gate  * before apic_intr_redistribute().
3100Sstevel@tonic-gate  */
3110Sstevel@tonic-gate 
3120Sstevel@tonic-gate /*
3130Sstevel@tonic-gate  * Following 3 variables start as % and can be patched or set using an
3140Sstevel@tonic-gate  * API to be defined in future. They will be scaled to
3150Sstevel@tonic-gate  * sample_factor_redistribution which is in turn set to hertz+1 (in periodic
3160Sstevel@tonic-gate  * mode), or 101 in one-shot mode to stagger it away from one sec processing
3170Sstevel@tonic-gate  */
3180Sstevel@tonic-gate 
3190Sstevel@tonic-gate int	apic_int_busy_mark = 60;
3200Sstevel@tonic-gate int	apic_int_free_mark = 20;
3210Sstevel@tonic-gate int	apic_diff_for_redistribution = 10;
3220Sstevel@tonic-gate 
3230Sstevel@tonic-gate /* sampling interval for interrupt redistribution for dynamic migration */
3240Sstevel@tonic-gate int	apic_redistribute_sample_interval = NANOSEC / 100; /* 10 millisec */
3250Sstevel@tonic-gate 
3260Sstevel@tonic-gate /*
3270Sstevel@tonic-gate  * number of times we sample before deciding to redistribute interrupts
3280Sstevel@tonic-gate  * for dynamic migration
3290Sstevel@tonic-gate  */
3300Sstevel@tonic-gate int	apic_sample_factor_redistribution = 101;
3310Sstevel@tonic-gate 
3320Sstevel@tonic-gate /* timeout for xlate_vector, mark_vector */
3330Sstevel@tonic-gate int	apic_revector_timeout = 16 * 10000; /* 160 millisec */
3340Sstevel@tonic-gate 
3350Sstevel@tonic-gate int	apic_redist_cpu_skip = 0;
3360Sstevel@tonic-gate int	apic_num_imbalance = 0;
3370Sstevel@tonic-gate int	apic_num_rebind = 0;
3380Sstevel@tonic-gate 
3390Sstevel@tonic-gate int	apic_nproc = 0;
3400Sstevel@tonic-gate int	apic_defconf = 0;
3410Sstevel@tonic-gate int	apic_irq_translate = 0;
3420Sstevel@tonic-gate int	apic_spec_rev = 0;
3430Sstevel@tonic-gate int	apic_imcrp = 0;
3440Sstevel@tonic-gate 
3450Sstevel@tonic-gate int	apic_use_acpi = 1;	/* 1 = use ACPI, 0 = don't use ACPI */
3460Sstevel@tonic-gate int	apic_use_acpi_madt_only = 0;	/* 1=ONLY use MADT from ACPI */
3470Sstevel@tonic-gate 
3480Sstevel@tonic-gate /*
3490Sstevel@tonic-gate  * For interrupt link devices, if apic_unconditional_srs is set, an irq resource
3500Sstevel@tonic-gate  * will be assigned (via _SRS). If it is not set, use the current
3510Sstevel@tonic-gate  * irq setting (via _CRS), but only if that irq is in the set of possible
3520Sstevel@tonic-gate  * irqs (returned by _PRS) for the device.
3530Sstevel@tonic-gate  */
3540Sstevel@tonic-gate int	apic_unconditional_srs = 1;
3550Sstevel@tonic-gate 
3560Sstevel@tonic-gate /*
3570Sstevel@tonic-gate  * For interrupt link devices, if apic_prefer_crs is set when we are
3580Sstevel@tonic-gate  * assigning an IRQ resource to a device, prefer the current IRQ setting
3590Sstevel@tonic-gate  * over other possible irq settings under same conditions.
3600Sstevel@tonic-gate  */
3610Sstevel@tonic-gate 
3620Sstevel@tonic-gate int	apic_prefer_crs = 1;
3630Sstevel@tonic-gate 
3640Sstevel@tonic-gate 
3650Sstevel@tonic-gate /* minimum number of timer ticks to program to */
3660Sstevel@tonic-gate int apic_min_timer_ticks = 1;
3670Sstevel@tonic-gate /*
3680Sstevel@tonic-gate  *	Local static data
3690Sstevel@tonic-gate  */
3700Sstevel@tonic-gate static struct	psm_ops apic_ops = {
3710Sstevel@tonic-gate 	apic_probe,
3720Sstevel@tonic-gate 
3730Sstevel@tonic-gate 	apic_init,
3740Sstevel@tonic-gate 	apic_picinit,
3750Sstevel@tonic-gate 	apic_intr_enter,
3760Sstevel@tonic-gate 	apic_intr_exit,
3770Sstevel@tonic-gate 	apic_setspl,
3780Sstevel@tonic-gate 	apic_addspl,
3790Sstevel@tonic-gate 	apic_delspl,
3800Sstevel@tonic-gate 	apic_disable_intr,
3810Sstevel@tonic-gate 	apic_enable_intr,
3820Sstevel@tonic-gate 	apic_softlvl_to_irq,
3830Sstevel@tonic-gate 	apic_set_softintr,
3840Sstevel@tonic-gate 
3850Sstevel@tonic-gate 	apic_set_idlecpu,
3860Sstevel@tonic-gate 	apic_unset_idlecpu,
3870Sstevel@tonic-gate 
3880Sstevel@tonic-gate 	apic_clkinit,
3890Sstevel@tonic-gate 	apic_getclkirq,
3900Sstevel@tonic-gate 	(void (*)(void))NULL,		/* psm_hrtimeinit */
3910Sstevel@tonic-gate 	apic_gethrtime,
3920Sstevel@tonic-gate 
3930Sstevel@tonic-gate 	apic_get_next_processorid,
3940Sstevel@tonic-gate 	apic_cpu_start,
3950Sstevel@tonic-gate 	apic_post_cpu_start,
3960Sstevel@tonic-gate 	apic_shutdown,
3970Sstevel@tonic-gate 	apic_get_ipivect,
3980Sstevel@tonic-gate 	apic_send_ipi,
3990Sstevel@tonic-gate 
4000Sstevel@tonic-gate 	(int (*)(dev_info_t *, int))NULL,	/* psm_translate_irq */
4010Sstevel@tonic-gate 	(int (*)(todinfo_t *))NULL,	/* psm_tod_get */
4020Sstevel@tonic-gate 	(int (*)(todinfo_t *))NULL,	/* psm_tod_set */
4030Sstevel@tonic-gate 	(void (*)(int, char *))NULL,	/* psm_notify_error */
4040Sstevel@tonic-gate 	(void (*)(int))NULL,		/* psm_notify_func */
4050Sstevel@tonic-gate 	apic_timer_reprogram,
4060Sstevel@tonic-gate 	apic_timer_enable,
4070Sstevel@tonic-gate 	apic_timer_disable,
4080Sstevel@tonic-gate 	apic_post_cyclic_setup,
4090Sstevel@tonic-gate 	apic_preshutdown,
4100Sstevel@tonic-gate 	apic_intr_ops			/* Advanced DDI Interrupt framework */
4110Sstevel@tonic-gate };
4120Sstevel@tonic-gate 
4130Sstevel@tonic-gate 
4140Sstevel@tonic-gate static struct	psm_info apic_psm_info = {
4150Sstevel@tonic-gate 	PSM_INFO_VER01_5,			/* version */
4160Sstevel@tonic-gate 	PSM_OWN_EXCLUSIVE,			/* ownership */
4170Sstevel@tonic-gate 	(struct psm_ops *)&apic_ops,		/* operation */
4180Sstevel@tonic-gate 	"pcplusmp",				/* machine name */
4190Sstevel@tonic-gate 	"pcplusmp v1.4 compatible %I%",
4200Sstevel@tonic-gate };
4210Sstevel@tonic-gate 
4220Sstevel@tonic-gate static void *apic_hdlp;
4230Sstevel@tonic-gate 
4240Sstevel@tonic-gate #ifdef DEBUG
4250Sstevel@tonic-gate #define	DENT		0x0001
4260Sstevel@tonic-gate int	apic_debug = 0;
4270Sstevel@tonic-gate /*
4280Sstevel@tonic-gate  * set apic_restrict_vector to the # of vectors we want to allow per range
4290Sstevel@tonic-gate  * useful in testing shared interrupt logic by setting it to 2 or 3
4300Sstevel@tonic-gate  */
4310Sstevel@tonic-gate int	apic_restrict_vector = 0;
4320Sstevel@tonic-gate 
4330Sstevel@tonic-gate #define	APIC_DEBUG_MSGBUFSIZE	2048
4340Sstevel@tonic-gate int	apic_debug_msgbuf[APIC_DEBUG_MSGBUFSIZE];
4350Sstevel@tonic-gate int	apic_debug_msgbufindex = 0;
4360Sstevel@tonic-gate 
4370Sstevel@tonic-gate /*
4380Sstevel@tonic-gate  * Put "int" info into debug buffer. No MP consistency, but light weight.
4390Sstevel@tonic-gate  * Good enough for most debugging.
4400Sstevel@tonic-gate  */
4410Sstevel@tonic-gate #define	APIC_DEBUG_BUF_PUT(x) \
4420Sstevel@tonic-gate 	apic_debug_msgbuf[apic_debug_msgbufindex++] = x; \
4430Sstevel@tonic-gate 	if (apic_debug_msgbufindex >= (APIC_DEBUG_MSGBUFSIZE - NCPU)) \
4440Sstevel@tonic-gate 		apic_debug_msgbufindex = 0;
4450Sstevel@tonic-gate 
4460Sstevel@tonic-gate #endif /* DEBUG */
4470Sstevel@tonic-gate 
4480Sstevel@tonic-gate apic_cpus_info_t	*apic_cpus;
4490Sstevel@tonic-gate 
4500Sstevel@tonic-gate static uint_t	apic_cpumask = 0;
4510Sstevel@tonic-gate static uint_t	apic_flag;
4520Sstevel@tonic-gate 
4530Sstevel@tonic-gate /* Flag to indicate that we need to shut down all processors */
4540Sstevel@tonic-gate static uint_t	apic_shutdown_processors;
4550Sstevel@tonic-gate 
4560Sstevel@tonic-gate uint_t apic_nsec_per_intr = 0;
4570Sstevel@tonic-gate 
4580Sstevel@tonic-gate /*
4590Sstevel@tonic-gate  * apic_let_idle_redistribute can have the following values:
4600Sstevel@tonic-gate  * 0 - If clock decremented it from 1 to 0, clock has to call redistribute.
4610Sstevel@tonic-gate  * apic_redistribute_lock prevents multiple idle cpus from redistributing
4620Sstevel@tonic-gate  */
4630Sstevel@tonic-gate int	apic_num_idle_redistributions = 0;
4640Sstevel@tonic-gate static	int apic_let_idle_redistribute = 0;
4650Sstevel@tonic-gate static	uint_t apic_nticks = 0;
4660Sstevel@tonic-gate static	uint_t apic_skipped_redistribute = 0;
4670Sstevel@tonic-gate 
4680Sstevel@tonic-gate /* to gather intr data and redistribute */
4690Sstevel@tonic-gate static void apic_redistribute_compute(void);
4700Sstevel@tonic-gate 
4710Sstevel@tonic-gate static	uint_t last_count_read = 0;
4720Sstevel@tonic-gate static	lock_t	apic_gethrtime_lock;
4730Sstevel@tonic-gate volatile int	apic_hrtime_stamp = 0;
4740Sstevel@tonic-gate volatile hrtime_t apic_nsec_since_boot = 0;
4750Sstevel@tonic-gate static uint_t apic_hertz_count, apic_nsec_per_tick;
4760Sstevel@tonic-gate static hrtime_t apic_nsec_max;
4770Sstevel@tonic-gate 
4780Sstevel@tonic-gate static	hrtime_t	apic_last_hrtime = 0;
4790Sstevel@tonic-gate int		apic_hrtime_error = 0;
4800Sstevel@tonic-gate int		apic_remote_hrterr = 0;
4810Sstevel@tonic-gate int		apic_num_nmis = 0;
4820Sstevel@tonic-gate int		apic_apic_error = 0;
4830Sstevel@tonic-gate int		apic_num_apic_errors = 0;
4840Sstevel@tonic-gate int		apic_num_cksum_errors = 0;
4850Sstevel@tonic-gate 
4860Sstevel@tonic-gate static	uchar_t	apic_io_id[MAX_IO_APIC];
4870Sstevel@tonic-gate static	uchar_t	apic_io_ver[MAX_IO_APIC];
4880Sstevel@tonic-gate static	uchar_t	apic_io_vectbase[MAX_IO_APIC];
4890Sstevel@tonic-gate static	uchar_t	apic_io_vectend[MAX_IO_APIC];
4900Sstevel@tonic-gate volatile int32_t *apicioadr[MAX_IO_APIC];
491881Sjohnny 
492881Sjohnny /*
493881Sjohnny  * First available slot to be used as IRQ index into the apic_irq_table
494881Sjohnny  * for those interrupts (like MSI/X) that don't have a physical IRQ.
495881Sjohnny  */
496881Sjohnny int apic_first_avail_irq  = APIC_FIRST_FREE_IRQ;
497881Sjohnny 
4980Sstevel@tonic-gate /*
4990Sstevel@tonic-gate  * apic_ioapic_lock protects the ioapics (reg select), the status, temp_bound
5000Sstevel@tonic-gate  * and bound elements of cpus_info and the temp_cpu element of irq_struct
5010Sstevel@tonic-gate  */
5020Sstevel@tonic-gate lock_t	apic_ioapic_lock;
5030Sstevel@tonic-gate 
5040Sstevel@tonic-gate /*
5050Sstevel@tonic-gate  * apic_ioapic_reprogram_lock prevents a CPU from exiting
5060Sstevel@tonic-gate  * apic_intr_exit before IOAPIC reprogramming information
5070Sstevel@tonic-gate  * is collected.
5080Sstevel@tonic-gate  */
5090Sstevel@tonic-gate static	lock_t	apic_ioapic_reprogram_lock;
5100Sstevel@tonic-gate static	int	apic_io_max = 0;	/* no. of i/o apics enabled */
5110Sstevel@tonic-gate 
5120Sstevel@tonic-gate static	struct apic_io_intr *apic_io_intrp = 0;
5130Sstevel@tonic-gate static	struct apic_bus	*apic_busp;
5140Sstevel@tonic-gate 
5150Sstevel@tonic-gate uchar_t	apic_vector_to_irq[APIC_MAX_VECTOR+1];
5160Sstevel@tonic-gate static	uchar_t	apic_resv_vector[MAXIPL+1];
5170Sstevel@tonic-gate 
5180Sstevel@tonic-gate static	char	apic_level_intr[APIC_MAX_VECTOR+1];
5190Sstevel@tonic-gate static	int	apic_error = 0;
5200Sstevel@tonic-gate /* values which apic_error can take. Not catastrophic, but may help debug */
5210Sstevel@tonic-gate #define	APIC_ERR_BOOT_EOI		0x1
5220Sstevel@tonic-gate #define	APIC_ERR_GET_IPIVECT_FAIL	0x2
5230Sstevel@tonic-gate #define	APIC_ERR_INVALID_INDEX		0x4
5240Sstevel@tonic-gate #define	APIC_ERR_MARK_VECTOR_FAIL	0x8
5250Sstevel@tonic-gate #define	APIC_ERR_APIC_ERROR		0x40000000
5260Sstevel@tonic-gate #define	APIC_ERR_NMI			0x80000000
5270Sstevel@tonic-gate 
5280Sstevel@tonic-gate static	int	apic_cmos_ssb_set = 0;
5290Sstevel@tonic-gate 
5300Sstevel@tonic-gate static	uint32_t	eisa_level_intr_mask = 0;
5310Sstevel@tonic-gate 	/* At least MSB will be set if EISA bus */
5320Sstevel@tonic-gate 
5330Sstevel@tonic-gate static	int	apic_pci_bus_total = 0;
5340Sstevel@tonic-gate static	uchar_t	apic_single_pci_busid = 0;
5350Sstevel@tonic-gate 
5360Sstevel@tonic-gate 
5370Sstevel@tonic-gate /*
5380Sstevel@tonic-gate  * airq_mutex protects additions to the apic_irq_table - the first
5390Sstevel@tonic-gate  * pointer and any airq_nexts off of that one. It also protects
5400Sstevel@tonic-gate  * apic_max_device_irq & apic_min_device_irq. It also guarantees
5410Sstevel@tonic-gate  * that share_id is unique as new ids are generated only when new
5420Sstevel@tonic-gate  * irq_t structs are linked in. Once linked in the structs are never
5430Sstevel@tonic-gate  * deleted. temp_cpu & mps_intr_index field indicate if it is programmed
5440Sstevel@tonic-gate  * or allocated. Note that there is a slight gap between allocating in
5450Sstevel@tonic-gate  * apic_introp_xlate and programming in addspl.
5460Sstevel@tonic-gate  */
5470Sstevel@tonic-gate kmutex_t	airq_mutex;
5480Sstevel@tonic-gate apic_irq_t	*apic_irq_table[APIC_MAX_VECTOR+1];
5490Sstevel@tonic-gate int		apic_max_device_irq = 0;
5500Sstevel@tonic-gate int		apic_min_device_irq = APIC_MAX_VECTOR;
5510Sstevel@tonic-gate 
5520Sstevel@tonic-gate /* use to make sure only one cpu handles the nmi */
5530Sstevel@tonic-gate static	lock_t	apic_nmi_lock;
5540Sstevel@tonic-gate /* use to make sure only one cpu handles the error interrupt */
5550Sstevel@tonic-gate static	lock_t	apic_error_lock;
5560Sstevel@tonic-gate 
5570Sstevel@tonic-gate /*
5580Sstevel@tonic-gate  * Following declarations are for revectoring; used when ISRs at different
5590Sstevel@tonic-gate  * IPLs share an irq.
5600Sstevel@tonic-gate  */
5610Sstevel@tonic-gate static	lock_t	apic_revector_lock;
5620Sstevel@tonic-gate static	int	apic_revector_pending = 0;
5630Sstevel@tonic-gate static	uchar_t	*apic_oldvec_to_newvec;
5640Sstevel@tonic-gate static	uchar_t	*apic_newvec_to_oldvec;
5650Sstevel@tonic-gate 
5660Sstevel@tonic-gate /* Ensures that the IOAPIC-reprogramming timeout is not reentrant */
5670Sstevel@tonic-gate static	kmutex_t	apic_reprogram_timeout_mutex;
5680Sstevel@tonic-gate 
5690Sstevel@tonic-gate static	struct	ioapic_reprogram_data {
5700Sstevel@tonic-gate 	int		valid;	 /* This entry is valid */
5710Sstevel@tonic-gate 	int		bindcpu; /* The CPU to which the int will be bound */
5720Sstevel@tonic-gate 	unsigned	timeouts; /* # times the reprogram timeout was called */
5730Sstevel@tonic-gate } apic_reprogram_info[APIC_MAX_VECTOR+1];
5740Sstevel@tonic-gate /*
5750Sstevel@tonic-gate  * APIC_MAX_VECTOR + 1 is the maximum # of IRQs as well. apic_reprogram_info
5760Sstevel@tonic-gate  * is indexed by IRQ number, NOT by vector number.
5770Sstevel@tonic-gate  */
5780Sstevel@tonic-gate 
5790Sstevel@tonic-gate 
5800Sstevel@tonic-gate /*
5810Sstevel@tonic-gate  * The following added to identify a software poweroff method if available.
5820Sstevel@tonic-gate  */
5830Sstevel@tonic-gate 
5840Sstevel@tonic-gate static struct {
5850Sstevel@tonic-gate 	int	poweroff_method;
5860Sstevel@tonic-gate 	char	oem_id[APIC_MPS_OEM_ID_LEN + 1];	/* MAX + 1 for NULL */
5870Sstevel@tonic-gate 	char	prod_id[APIC_MPS_PROD_ID_LEN + 1];	/* MAX + 1 for NULL */
5880Sstevel@tonic-gate } apic_mps_ids[] = {
5890Sstevel@tonic-gate 	{ APIC_POWEROFF_VIA_RTC,	"INTEL",	"ALDER" },   /* 4300 */
5900Sstevel@tonic-gate 	{ APIC_POWEROFF_VIA_RTC,	"NCR",		"AMC" },    /* 4300 */
5910Sstevel@tonic-gate 	{ APIC_POWEROFF_VIA_ASPEN_BMC,	"INTEL",	"A450NX" },  /* 4400? */
5920Sstevel@tonic-gate 	{ APIC_POWEROFF_VIA_ASPEN_BMC,	"INTEL",	"AD450NX" }, /* 4400 */
5930Sstevel@tonic-gate 	{ APIC_POWEROFF_VIA_ASPEN_BMC,	"INTEL",	"AC450NX" }, /* 4400R */
5940Sstevel@tonic-gate 	{ APIC_POWEROFF_VIA_SITKA_BMC,	"INTEL",	"S450NX" },  /* S50  */
5950Sstevel@tonic-gate 	{ APIC_POWEROFF_VIA_SITKA_BMC,	"INTEL",	"SC450NX" }  /* S50? */
5960Sstevel@tonic-gate };
5970Sstevel@tonic-gate 
5980Sstevel@tonic-gate int	apic_poweroff_method = APIC_POWEROFF_NONE;
5990Sstevel@tonic-gate 
6000Sstevel@tonic-gate static	struct {
6010Sstevel@tonic-gate 	uchar_t	cntl;
6020Sstevel@tonic-gate 	uchar_t	data;
6030Sstevel@tonic-gate } aspen_bmc[] = {
6040Sstevel@tonic-gate 	{ CC_SMS_WR_START,	0x18 },		/* NetFn/LUN */
6050Sstevel@tonic-gate 	{ CC_SMS_WR_NEXT,	0x24 },		/* Cmd SET_WATCHDOG_TIMER */
6060Sstevel@tonic-gate 	{ CC_SMS_WR_NEXT,	0x84 },		/* DataByte 1: SMS/OS no log */
6070Sstevel@tonic-gate 	{ CC_SMS_WR_NEXT,	0x2 },		/* DataByte 2: Power Down */
6080Sstevel@tonic-gate 	{ CC_SMS_WR_NEXT,	0x0 },		/* DataByte 3: no pre-timeout */
6090Sstevel@tonic-gate 	{ CC_SMS_WR_NEXT,	0x0 },		/* DataByte 4: timer expir. */
6100Sstevel@tonic-gate 	{ CC_SMS_WR_NEXT,	0xa },		/* DataByte 5: init countdown */
6110Sstevel@tonic-gate 	{ CC_SMS_WR_END,	0x0 },		/* DataByte 6: init countdown */
6120Sstevel@tonic-gate 
6130Sstevel@tonic-gate 	{ CC_SMS_WR_START,	0x18 },		/* NetFn/LUN */
6140Sstevel@tonic-gate 	{ CC_SMS_WR_END,	0x22 }		/* Cmd RESET_WATCHDOG_TIMER */
6150Sstevel@tonic-gate };
6160Sstevel@tonic-gate 
6170Sstevel@tonic-gate static	struct {
6180Sstevel@tonic-gate 	int	port;
6190Sstevel@tonic-gate 	uchar_t	data;
6200Sstevel@tonic-gate } sitka_bmc[] = {
6210Sstevel@tonic-gate 	{ SMS_COMMAND_REGISTER,	SMS_WRITE_START },
6220Sstevel@tonic-gate 	{ SMS_DATA_REGISTER,	0x18 },		/* NetFn/LUN */
6230Sstevel@tonic-gate 	{ SMS_DATA_REGISTER,	0x24 },		/* Cmd SET_WATCHDOG_TIMER */
6240Sstevel@tonic-gate 	{ SMS_DATA_REGISTER,	0x84 },		/* DataByte 1: SMS/OS no log */
6250Sstevel@tonic-gate 	{ SMS_DATA_REGISTER,	0x2 },		/* DataByte 2: Power Down */
6260Sstevel@tonic-gate 	{ SMS_DATA_REGISTER,	0x0 },		/* DataByte 3: no pre-timeout */
6270Sstevel@tonic-gate 	{ SMS_DATA_REGISTER,	0x0 },		/* DataByte 4: timer expir. */
6280Sstevel@tonic-gate 	{ SMS_DATA_REGISTER,	0xa },		/* DataByte 5: init countdown */
6290Sstevel@tonic-gate 	{ SMS_COMMAND_REGISTER,	SMS_WRITE_END },
6300Sstevel@tonic-gate 	{ SMS_DATA_REGISTER,	0x0 },		/* DataByte 6: init countdown */
6310Sstevel@tonic-gate 
6320Sstevel@tonic-gate 	{ SMS_COMMAND_REGISTER,	SMS_WRITE_START },
6330Sstevel@tonic-gate 	{ SMS_DATA_REGISTER,	0x18 },		/* NetFn/LUN */
6340Sstevel@tonic-gate 	{ SMS_COMMAND_REGISTER,	SMS_WRITE_END },
6350Sstevel@tonic-gate 	{ SMS_DATA_REGISTER,	0x22 }		/* Cmd RESET_WATCHDOG_TIMER */
6360Sstevel@tonic-gate };
6370Sstevel@tonic-gate 
6380Sstevel@tonic-gate 
6390Sstevel@tonic-gate /* Patchable global variables. */
6400Sstevel@tonic-gate int		apic_kmdb_on_nmi = 0;		/* 0 - no, 1 - yes enter kmdb */
6410Sstevel@tonic-gate int		apic_debug_mps_id = 0;		/* 1 - print MPS ID strings */
6420Sstevel@tonic-gate 
6430Sstevel@tonic-gate /*
6440Sstevel@tonic-gate  * ACPI definitions
6450Sstevel@tonic-gate  */
6460Sstevel@tonic-gate /* _PIC method arguments */
6470Sstevel@tonic-gate #define	ACPI_PIC_MODE	0
6480Sstevel@tonic-gate #define	ACPI_APIC_MODE	1
6490Sstevel@tonic-gate 
6500Sstevel@tonic-gate /* APIC error flags we care about */
6510Sstevel@tonic-gate #define	APIC_SEND_CS_ERROR	0x01
6520Sstevel@tonic-gate #define	APIC_RECV_CS_ERROR	0x02
6530Sstevel@tonic-gate #define	APIC_CS_ERRORS		(APIC_SEND_CS_ERROR|APIC_RECV_CS_ERROR)
6540Sstevel@tonic-gate 
6550Sstevel@tonic-gate /*
6560Sstevel@tonic-gate  * ACPI variables
6570Sstevel@tonic-gate  */
6580Sstevel@tonic-gate /* 1 = acpi is enabled & working, 0 = acpi is not enabled or not there */
6590Sstevel@tonic-gate static	int apic_enable_acpi = 0;
6600Sstevel@tonic-gate 
6610Sstevel@tonic-gate /* ACPI Multiple APIC Description Table ptr */
6620Sstevel@tonic-gate static	MULTIPLE_APIC_TABLE *acpi_mapic_dtp = NULL;
6630Sstevel@tonic-gate 
6640Sstevel@tonic-gate /* ACPI Interrupt Source Override Structure ptr */
6650Sstevel@tonic-gate static	MADT_INTERRUPT_OVERRIDE *acpi_isop = NULL;
6660Sstevel@tonic-gate static	int acpi_iso_cnt = 0;
6670Sstevel@tonic-gate 
6680Sstevel@tonic-gate /* ACPI Non-maskable Interrupt Sources ptr */
6690Sstevel@tonic-gate static	MADT_NMI_SOURCE *acpi_nmi_sp = NULL;
6700Sstevel@tonic-gate static	int acpi_nmi_scnt = 0;
6710Sstevel@tonic-gate static	MADT_LOCAL_APIC_NMI *acpi_nmi_cp = NULL;
6720Sstevel@tonic-gate static	int acpi_nmi_ccnt = 0;
6730Sstevel@tonic-gate 
6740Sstevel@tonic-gate /*
6750Sstevel@tonic-gate  * extern declarations
6760Sstevel@tonic-gate  */
6770Sstevel@tonic-gate extern	int	intr_clear(void);
6780Sstevel@tonic-gate extern	void	intr_restore(uint_t);
6790Sstevel@tonic-gate #if defined(__amd64)
6800Sstevel@tonic-gate extern	int	intpri_use_cr8;
6810Sstevel@tonic-gate #endif	/* __amd64 */
6820Sstevel@tonic-gate 
6830Sstevel@tonic-gate extern int	apic_pci_msi_enable_vector(dev_info_t *, int, int,
6840Sstevel@tonic-gate 		    int, int, int);
6850Sstevel@tonic-gate extern apic_irq_t *apic_find_irq(dev_info_t *, struct intrspec *, int);
6860Sstevel@tonic-gate 
6870Sstevel@tonic-gate /*
6880Sstevel@tonic-gate  *	This is the loadable module wrapper
6890Sstevel@tonic-gate  */
6900Sstevel@tonic-gate 
6910Sstevel@tonic-gate int
6920Sstevel@tonic-gate _init(void)
6930Sstevel@tonic-gate {
6940Sstevel@tonic-gate 	if (apic_coarse_hrtime)
6950Sstevel@tonic-gate 		apic_ops.psm_gethrtime = &apic_gettime;
6960Sstevel@tonic-gate 	return (psm_mod_init(&apic_hdlp, &apic_psm_info));
6970Sstevel@tonic-gate }
6980Sstevel@tonic-gate 
6990Sstevel@tonic-gate int
7000Sstevel@tonic-gate _fini(void)
7010Sstevel@tonic-gate {
7020Sstevel@tonic-gate 	return (psm_mod_fini(&apic_hdlp, &apic_psm_info));
7030Sstevel@tonic-gate }
7040Sstevel@tonic-gate 
7050Sstevel@tonic-gate int
7060Sstevel@tonic-gate _info(struct modinfo *modinfop)
7070Sstevel@tonic-gate {
7080Sstevel@tonic-gate 	return (psm_mod_info(&apic_hdlp, &apic_psm_info, modinfop));
7090Sstevel@tonic-gate }
7100Sstevel@tonic-gate 
7110Sstevel@tonic-gate /*
7120Sstevel@tonic-gate  * Auto-configuration routines
7130Sstevel@tonic-gate  */
7140Sstevel@tonic-gate 
7150Sstevel@tonic-gate /*
7160Sstevel@tonic-gate  * Look at MPSpec 1.4 (Intel Order # 242016-005) for details of what we do here
7170Sstevel@tonic-gate  * May work with 1.1 - but not guaranteed.
7180Sstevel@tonic-gate  * According to the MP Spec, the MP floating pointer structure
7190Sstevel@tonic-gate  * will be searched in the order described below:
7200Sstevel@tonic-gate  * 1. In the first kilobyte of Extended BIOS Data Area (EBDA)
7210Sstevel@tonic-gate  * 2. Within the last kilobyte of system base memory
7220Sstevel@tonic-gate  * 3. In the BIOS ROM address space between 0F0000h and 0FFFFh
7230Sstevel@tonic-gate  * Once we find the right signature with proper checksum, we call
7240Sstevel@tonic-gate  * either handle_defconf or parse_mpct to get all info necessary for
7250Sstevel@tonic-gate  * subsequent operations.
7260Sstevel@tonic-gate  */
7270Sstevel@tonic-gate static int
7280Sstevel@tonic-gate apic_probe()
7290Sstevel@tonic-gate {
7300Sstevel@tonic-gate 	uint32_t mpct_addr, ebda_start = 0, base_mem_end;
7310Sstevel@tonic-gate 	caddr_t	biosdatap;
7320Sstevel@tonic-gate 	caddr_t	mpct;
7330Sstevel@tonic-gate 	caddr_t	fptr;
7340Sstevel@tonic-gate 	int	i, mpct_size, mapsize, retval = PSM_FAILURE;
7350Sstevel@tonic-gate 	ushort_t	ebda_seg, base_mem_size;
7360Sstevel@tonic-gate 	struct	apic_mpfps_hdr	*fpsp;
7370Sstevel@tonic-gate 	struct	apic_mp_cnf_hdr	*hdrp;
7380Sstevel@tonic-gate 	int bypass_cpu_and_ioapics_in_mptables;
7390Sstevel@tonic-gate 	int acpi_user_options;
7400Sstevel@tonic-gate 
7410Sstevel@tonic-gate 	if (apic_forceload < 0)
7420Sstevel@tonic-gate 		return (retval);
7430Sstevel@tonic-gate 
7440Sstevel@tonic-gate 	/* Allow override for MADT-only mode */
7450Sstevel@tonic-gate 	acpi_user_options = ddi_prop_get_int(DDI_DEV_T_ANY, ddi_root_node(), 0,
7460Sstevel@tonic-gate 	    "acpi-user-options", 0);
7470Sstevel@tonic-gate 	apic_use_acpi_madt_only = ((acpi_user_options & ACPI_OUSER_MADT) != 0);
7480Sstevel@tonic-gate 
7490Sstevel@tonic-gate 	/* Allow apic_use_acpi to override MADT-only mode */
7500Sstevel@tonic-gate 	if (!apic_use_acpi)
7510Sstevel@tonic-gate 		apic_use_acpi_madt_only = 0;
7520Sstevel@tonic-gate 
7530Sstevel@tonic-gate 	retval = acpi_probe();
7540Sstevel@tonic-gate 
7550Sstevel@tonic-gate 	/*
7560Sstevel@tonic-gate 	 * mapin the bios data area 40:0
7570Sstevel@tonic-gate 	 * 40:13h - two-byte location reports the base memory size
7580Sstevel@tonic-gate 	 * 40:0Eh - two-byte location for the exact starting address of
7590Sstevel@tonic-gate 	 *	    the EBDA segment for EISA
7600Sstevel@tonic-gate 	 */
7610Sstevel@tonic-gate 	biosdatap = psm_map_phys(0x400, 0x20, PROT_READ);
7620Sstevel@tonic-gate 	if (!biosdatap)
7630Sstevel@tonic-gate 		return (retval);
7640Sstevel@tonic-gate 	fpsp = (struct apic_mpfps_hdr *)NULL;
7650Sstevel@tonic-gate 	mapsize = MPFPS_RAM_WIN_LEN;
7660Sstevel@tonic-gate 	/*LINTED: pointer cast may result in improper alignment */
7670Sstevel@tonic-gate 	ebda_seg = *((ushort_t *)(biosdatap+0xe));
7680Sstevel@tonic-gate 	/* check the 1k of EBDA */
7690Sstevel@tonic-gate 	if (ebda_seg) {
7700Sstevel@tonic-gate 		ebda_start = ((uint32_t)ebda_seg) << 4;
7710Sstevel@tonic-gate 		fptr = psm_map_phys(ebda_start, MPFPS_RAM_WIN_LEN, PROT_READ);
7720Sstevel@tonic-gate 		if (fptr) {
7730Sstevel@tonic-gate 			if (!(fpsp =
7740Sstevel@tonic-gate 			    apic_find_fps_sig(fptr, MPFPS_RAM_WIN_LEN)))
7750Sstevel@tonic-gate 				psm_unmap_phys(fptr, MPFPS_RAM_WIN_LEN);
7760Sstevel@tonic-gate 		}
7770Sstevel@tonic-gate 	}
7780Sstevel@tonic-gate 	/* If not in EBDA, check the last k of system base memory */
7790Sstevel@tonic-gate 	if (!fpsp) {
7800Sstevel@tonic-gate 		/*LINTED: pointer cast may result in improper alignment */
7810Sstevel@tonic-gate 		base_mem_size = *((ushort_t *)(biosdatap + 0x13));
7820Sstevel@tonic-gate 
7830Sstevel@tonic-gate 		if (base_mem_size > 512)
7840Sstevel@tonic-gate 			base_mem_end = 639 * 1024;
7850Sstevel@tonic-gate 		else
7860Sstevel@tonic-gate 			base_mem_end = 511 * 1024;
7870Sstevel@tonic-gate 		/* if ebda == last k of base mem, skip to check BIOS ROM */
7880Sstevel@tonic-gate 		if (base_mem_end != ebda_start) {
7890Sstevel@tonic-gate 
7900Sstevel@tonic-gate 			fptr = psm_map_phys(base_mem_end, MPFPS_RAM_WIN_LEN,
7910Sstevel@tonic-gate 			    PROT_READ);
7920Sstevel@tonic-gate 
7930Sstevel@tonic-gate 			if (fptr) {
7940Sstevel@tonic-gate 				if (!(fpsp = apic_find_fps_sig(fptr,
7950Sstevel@tonic-gate 				    MPFPS_RAM_WIN_LEN)))
7960Sstevel@tonic-gate 					psm_unmap_phys(fptr, MPFPS_RAM_WIN_LEN);
7970Sstevel@tonic-gate 			}
7980Sstevel@tonic-gate 		}
7990Sstevel@tonic-gate 	}
8000Sstevel@tonic-gate 	psm_unmap_phys(biosdatap, 0x20);
8010Sstevel@tonic-gate 
8020Sstevel@tonic-gate 	/* If still cannot find it, check the BIOS ROM space */
8030Sstevel@tonic-gate 	if (!fpsp) {
8040Sstevel@tonic-gate 		mapsize = MPFPS_ROM_WIN_LEN;
8050Sstevel@tonic-gate 		fptr = psm_map_phys(MPFPS_ROM_WIN_START,
8060Sstevel@tonic-gate 		    MPFPS_ROM_WIN_LEN, PROT_READ);
8070Sstevel@tonic-gate 		if (fptr) {
8080Sstevel@tonic-gate 			if (!(fpsp =
8090Sstevel@tonic-gate 			    apic_find_fps_sig(fptr, MPFPS_ROM_WIN_LEN))) {
8100Sstevel@tonic-gate 				psm_unmap_phys(fptr, MPFPS_ROM_WIN_LEN);
8110Sstevel@tonic-gate 				return (retval);
8120Sstevel@tonic-gate 			}
8130Sstevel@tonic-gate 		}
8140Sstevel@tonic-gate 	}
8150Sstevel@tonic-gate 
8160Sstevel@tonic-gate 	if (apic_checksum((caddr_t)fpsp, fpsp->mpfps_length * 16) != 0) {
8170Sstevel@tonic-gate 		psm_unmap_phys(fptr, MPFPS_ROM_WIN_LEN);
8180Sstevel@tonic-gate 		return (retval);
8190Sstevel@tonic-gate 	}
8200Sstevel@tonic-gate 
8210Sstevel@tonic-gate 	apic_spec_rev = fpsp->mpfps_spec_rev;
8220Sstevel@tonic-gate 	if ((apic_spec_rev != 04) && (apic_spec_rev != 01)) {
8230Sstevel@tonic-gate 		psm_unmap_phys(fptr, MPFPS_ROM_WIN_LEN);
8240Sstevel@tonic-gate 		return (retval);
8250Sstevel@tonic-gate 	}
8260Sstevel@tonic-gate 
8270Sstevel@tonic-gate 	/* check IMCR is present or not */
8280Sstevel@tonic-gate 	apic_imcrp = fpsp->mpfps_featinfo2 & MPFPS_FEATINFO2_IMCRP;
8290Sstevel@tonic-gate 
8300Sstevel@tonic-gate 	/* check default configuration (dual CPUs) */
8310Sstevel@tonic-gate 	if ((apic_defconf = fpsp->mpfps_featinfo1) != 0) {
8320Sstevel@tonic-gate 		psm_unmap_phys(fptr, mapsize);
8330Sstevel@tonic-gate 		return (apic_handle_defconf());
8340Sstevel@tonic-gate 	}
8350Sstevel@tonic-gate 
8360Sstevel@tonic-gate 	/* MP Configuration Table */
8370Sstevel@tonic-gate 	mpct_addr = (uint32_t)(fpsp->mpfps_mpct_paddr);
8380Sstevel@tonic-gate 
8390Sstevel@tonic-gate 	psm_unmap_phys(fptr, mapsize); /* unmap floating ptr struct */
8400Sstevel@tonic-gate 
8410Sstevel@tonic-gate 	/*
8420Sstevel@tonic-gate 	 * Map in enough memory for the MP Configuration Table Header.
8430Sstevel@tonic-gate 	 * Use this table to read the total length of the BIOS data and
8440Sstevel@tonic-gate 	 * map in all the info
8450Sstevel@tonic-gate 	 */
8460Sstevel@tonic-gate 	/*LINTED: pointer cast may result in improper alignment */
8470Sstevel@tonic-gate 	hdrp = (struct apic_mp_cnf_hdr *)psm_map_phys(mpct_addr,
8480Sstevel@tonic-gate 	    sizeof (struct apic_mp_cnf_hdr), PROT_READ);
8490Sstevel@tonic-gate 	if (!hdrp)
8500Sstevel@tonic-gate 		return (retval);
8510Sstevel@tonic-gate 
8520Sstevel@tonic-gate 	/* check mp configuration table signature PCMP */
8530Sstevel@tonic-gate 	if (hdrp->mpcnf_sig != 0x504d4350) {
8540Sstevel@tonic-gate 		psm_unmap_phys((caddr_t)hdrp, sizeof (struct apic_mp_cnf_hdr));
8550Sstevel@tonic-gate 		return (retval);
8560Sstevel@tonic-gate 	}
8570Sstevel@tonic-gate 	mpct_size = (int)hdrp->mpcnf_tbl_length;
8580Sstevel@tonic-gate 
8590Sstevel@tonic-gate 	apic_set_pwroff_method_from_mpcnfhdr(hdrp);
8600Sstevel@tonic-gate 
8610Sstevel@tonic-gate 	psm_unmap_phys((caddr_t)hdrp, sizeof (struct apic_mp_cnf_hdr));
8620Sstevel@tonic-gate 
8630Sstevel@tonic-gate 	if ((retval == PSM_SUCCESS) && !apic_use_acpi_madt_only) {
8640Sstevel@tonic-gate 		/* This is an ACPI machine No need for further checks */
8650Sstevel@tonic-gate 		return (retval);
8660Sstevel@tonic-gate 	}
8670Sstevel@tonic-gate 
8680Sstevel@tonic-gate 	/*
8690Sstevel@tonic-gate 	 * Map in the entries for this machine, ie. Processor
8700Sstevel@tonic-gate 	 * Entry Tables, Bus Entry Tables, etc.
8710Sstevel@tonic-gate 	 * They are in fixed order following one another
8720Sstevel@tonic-gate 	 */
8730Sstevel@tonic-gate 	mpct = psm_map_phys(mpct_addr, mpct_size, PROT_READ);
8740Sstevel@tonic-gate 	if (!mpct)
8750Sstevel@tonic-gate 		return (retval);
8760Sstevel@tonic-gate 
8770Sstevel@tonic-gate 	if (apic_checksum(mpct, mpct_size) != 0)
8780Sstevel@tonic-gate 		goto apic_fail1;
8790Sstevel@tonic-gate 
8800Sstevel@tonic-gate 
8810Sstevel@tonic-gate 	/*LINTED: pointer cast may result in improper alignment */
8820Sstevel@tonic-gate 	hdrp = (struct apic_mp_cnf_hdr *)mpct;
8830Sstevel@tonic-gate 	/*LINTED: pointer cast may result in improper alignment */
8840Sstevel@tonic-gate 	apicadr = (uint32_t *)psm_map_phys((uint32_t)hdrp->mpcnf_local_apic,
8850Sstevel@tonic-gate 	    APIC_LOCAL_MEMLEN, PROT_READ | PROT_WRITE);
8860Sstevel@tonic-gate 	if (!apicadr)
8870Sstevel@tonic-gate 		goto apic_fail1;
8880Sstevel@tonic-gate 
8890Sstevel@tonic-gate 	/* Parse all information in the tables */
8900Sstevel@tonic-gate 	bypass_cpu_and_ioapics_in_mptables = (retval == PSM_SUCCESS);
8910Sstevel@tonic-gate 	if (apic_parse_mpct(mpct, bypass_cpu_and_ioapics_in_mptables) ==
8920Sstevel@tonic-gate 	    PSM_SUCCESS)
8930Sstevel@tonic-gate 		return (PSM_SUCCESS);
8940Sstevel@tonic-gate 
8950Sstevel@tonic-gate 	for (i = 0; i < apic_io_max; i++)
8960Sstevel@tonic-gate 		psm_unmap_phys((caddr_t)apicioadr[i], APIC_IO_MEMLEN);
8970Sstevel@tonic-gate 	if (apic_cpus)
8980Sstevel@tonic-gate 		kmem_free(apic_cpus, sizeof (*apic_cpus) * apic_nproc);
8990Sstevel@tonic-gate 	if (apicadr)
9000Sstevel@tonic-gate 		psm_unmap_phys((caddr_t)apicadr, APIC_LOCAL_MEMLEN);
9010Sstevel@tonic-gate apic_fail1:
9020Sstevel@tonic-gate 	psm_unmap_phys(mpct, mpct_size);
9030Sstevel@tonic-gate 	return (retval);
9040Sstevel@tonic-gate }
9050Sstevel@tonic-gate 
9060Sstevel@tonic-gate static void
9070Sstevel@tonic-gate apic_set_pwroff_method_from_mpcnfhdr(struct apic_mp_cnf_hdr *hdrp)
9080Sstevel@tonic-gate {
9090Sstevel@tonic-gate 	int	i;
9100Sstevel@tonic-gate 
9110Sstevel@tonic-gate 	for (i = 0; i < (sizeof (apic_mps_ids) / sizeof (apic_mps_ids[0]));
9120Sstevel@tonic-gate 	    i++) {
9130Sstevel@tonic-gate 		if ((strncmp(hdrp->mpcnf_oem_str, apic_mps_ids[i].oem_id,
9140Sstevel@tonic-gate 		    strlen(apic_mps_ids[i].oem_id)) == 0) &&
9150Sstevel@tonic-gate 		    (strncmp(hdrp->mpcnf_prod_str, apic_mps_ids[i].prod_id,
9160Sstevel@tonic-gate 		    strlen(apic_mps_ids[i].prod_id)) == 0)) {
9170Sstevel@tonic-gate 
9180Sstevel@tonic-gate 			apic_poweroff_method = apic_mps_ids[i].poweroff_method;
9190Sstevel@tonic-gate 			break;
9200Sstevel@tonic-gate 		}
9210Sstevel@tonic-gate 	}
9220Sstevel@tonic-gate 
9230Sstevel@tonic-gate 	if (apic_debug_mps_id != 0) {
9240Sstevel@tonic-gate 		cmn_err(CE_CONT, "pcplusmp: MPS OEM ID = '%c%c%c%c%c%c%c%c'"
9250Sstevel@tonic-gate 		    "Product ID = '%c%c%c%c%c%c%c%c%c%c%c%c'\n",
9260Sstevel@tonic-gate 		    hdrp->mpcnf_oem_str[0],
9270Sstevel@tonic-gate 		    hdrp->mpcnf_oem_str[1],
9280Sstevel@tonic-gate 		    hdrp->mpcnf_oem_str[2],
9290Sstevel@tonic-gate 		    hdrp->mpcnf_oem_str[3],
9300Sstevel@tonic-gate 		    hdrp->mpcnf_oem_str[4],
9310Sstevel@tonic-gate 		    hdrp->mpcnf_oem_str[5],
9320Sstevel@tonic-gate 		    hdrp->mpcnf_oem_str[6],
9330Sstevel@tonic-gate 		    hdrp->mpcnf_oem_str[7],
9340Sstevel@tonic-gate 		    hdrp->mpcnf_prod_str[0],
9350Sstevel@tonic-gate 		    hdrp->mpcnf_prod_str[1],
9360Sstevel@tonic-gate 		    hdrp->mpcnf_prod_str[2],
9370Sstevel@tonic-gate 		    hdrp->mpcnf_prod_str[3],
9380Sstevel@tonic-gate 		    hdrp->mpcnf_prod_str[4],
9390Sstevel@tonic-gate 		    hdrp->mpcnf_prod_str[5],
9400Sstevel@tonic-gate 		    hdrp->mpcnf_prod_str[6],
9410Sstevel@tonic-gate 		    hdrp->mpcnf_prod_str[7],
9420Sstevel@tonic-gate 		    hdrp->mpcnf_prod_str[8],
9430Sstevel@tonic-gate 		    hdrp->mpcnf_prod_str[9],
9440Sstevel@tonic-gate 		    hdrp->mpcnf_prod_str[10],
9450Sstevel@tonic-gate 		    hdrp->mpcnf_prod_str[11]);
9460Sstevel@tonic-gate 	}
9470Sstevel@tonic-gate }
9480Sstevel@tonic-gate 
9490Sstevel@tonic-gate static int
9500Sstevel@tonic-gate acpi_probe(void)
9510Sstevel@tonic-gate {
9520Sstevel@tonic-gate 	int			i, id, intmax, ver, index, rv;
9530Sstevel@tonic-gate 	int			acpi_verboseflags = 0;
9540Sstevel@tonic-gate 	int			madt_seen, madt_size;
9550Sstevel@tonic-gate 	APIC_HEADER		*ap;
9560Sstevel@tonic-gate 	MADT_PROCESSOR_APIC	*mpa;
9570Sstevel@tonic-gate 	MADT_IO_APIC		*mia;
9580Sstevel@tonic-gate 	MADT_IO_SAPIC		*misa;
9590Sstevel@tonic-gate 	MADT_INTERRUPT_OVERRIDE	*mio;
9600Sstevel@tonic-gate 	MADT_NMI_SOURCE		*mns;
9610Sstevel@tonic-gate 	MADT_INTERRUPT_SOURCE	*mis;
9620Sstevel@tonic-gate 	MADT_LOCAL_APIC_NMI	*mlan;
9630Sstevel@tonic-gate 	MADT_ADDRESS_OVERRIDE	*mao;
9640Sstevel@tonic-gate 	ACPI_OBJECT_LIST 	arglist;
9650Sstevel@tonic-gate 	ACPI_OBJECT		arg;
9660Sstevel@tonic-gate 	int			sci;
9670Sstevel@tonic-gate 	iflag_t			sci_flags;
9680Sstevel@tonic-gate 	volatile int32_t	*ioapic;
9690Sstevel@tonic-gate 	char			local_ids[NCPU];
9700Sstevel@tonic-gate 	char			proc_ids[NCPU];
9710Sstevel@tonic-gate 	uchar_t			hid;
9720Sstevel@tonic-gate 
9730Sstevel@tonic-gate 	if (!apic_use_acpi)
9740Sstevel@tonic-gate 		return (PSM_FAILURE);
9750Sstevel@tonic-gate 
9760Sstevel@tonic-gate 	if (AcpiGetFirmwareTable(APIC_SIG, 1, ACPI_LOGICAL_ADDRESSING,
9770Sstevel@tonic-gate 	    (ACPI_TABLE_HEADER **) &acpi_mapic_dtp) != AE_OK)
9780Sstevel@tonic-gate 		return (PSM_FAILURE);
9790Sstevel@tonic-gate 
9800Sstevel@tonic-gate 	apicadr = (uint32_t *)psm_map_phys(
9810Sstevel@tonic-gate 	    (uint32_t)acpi_mapic_dtp->LocalApicAddress,
9820Sstevel@tonic-gate 	    APIC_LOCAL_MEMLEN, PROT_READ | PROT_WRITE);
9830Sstevel@tonic-gate 	if (!apicadr)
9840Sstevel@tonic-gate 		return (PSM_FAILURE);
9850Sstevel@tonic-gate 
9860Sstevel@tonic-gate 	id = apicadr[APIC_LID_REG];
9870Sstevel@tonic-gate 	local_ids[0] = (uchar_t)(((uint_t)id) >> 24);
9880Sstevel@tonic-gate 	apic_nproc = index = 1;
9890Sstevel@tonic-gate 	apic_io_max = 0;
9900Sstevel@tonic-gate 
9910Sstevel@tonic-gate 	ap = (APIC_HEADER *) (acpi_mapic_dtp + 1);
9920Sstevel@tonic-gate 	madt_size = acpi_mapic_dtp->Length;
9930Sstevel@tonic-gate 	madt_seen = sizeof (*acpi_mapic_dtp);
9940Sstevel@tonic-gate 
9950Sstevel@tonic-gate 	while (madt_seen < madt_size) {
9960Sstevel@tonic-gate 		switch (ap->Type) {
9970Sstevel@tonic-gate 		case APIC_PROCESSOR:
9980Sstevel@tonic-gate 			mpa = (MADT_PROCESSOR_APIC *) ap;
9990Sstevel@tonic-gate 			if (mpa->ProcessorEnabled) {
10000Sstevel@tonic-gate 				if (mpa->LocalApicId == local_ids[0])
10010Sstevel@tonic-gate 					proc_ids[0] = mpa->ProcessorId;
10020Sstevel@tonic-gate 				else if (apic_nproc < NCPU) {
10030Sstevel@tonic-gate 					local_ids[index] = mpa->LocalApicId;
10040Sstevel@tonic-gate 					proc_ids[index] = mpa->ProcessorId;
10050Sstevel@tonic-gate 					index++;
10060Sstevel@tonic-gate 					apic_nproc++;
10070Sstevel@tonic-gate 				} else
10080Sstevel@tonic-gate 					cmn_err(CE_WARN, "pcplusmp: exceeded "
10090Sstevel@tonic-gate 					    "maximum no. of CPUs (= %d)", NCPU);
10100Sstevel@tonic-gate 			}
10110Sstevel@tonic-gate 			break;
10120Sstevel@tonic-gate 
10130Sstevel@tonic-gate 		case APIC_IO:
10140Sstevel@tonic-gate 			mia = (MADT_IO_APIC *) ap;
10150Sstevel@tonic-gate 			if (apic_io_max < MAX_IO_APIC) {
10160Sstevel@tonic-gate 				apic_io_id[apic_io_max] = mia->IoApicId;
10170Sstevel@tonic-gate 				apic_io_vectbase[apic_io_max] =
10180Sstevel@tonic-gate 				    mia->Interrupt;
10190Sstevel@tonic-gate 				ioapic = apicioadr[apic_io_max] =
10200Sstevel@tonic-gate 				    (int32_t *)psm_map_phys(
10210Sstevel@tonic-gate 				    (uint32_t)mia->Address,
10220Sstevel@tonic-gate 				    APIC_IO_MEMLEN, PROT_READ | PROT_WRITE);
10230Sstevel@tonic-gate 				if (!ioapic)
10240Sstevel@tonic-gate 					goto cleanup;
10250Sstevel@tonic-gate 				apic_io_max++;
10260Sstevel@tonic-gate 			}
10270Sstevel@tonic-gate 			break;
10280Sstevel@tonic-gate 
10290Sstevel@tonic-gate 		case APIC_XRUPT_OVERRIDE:
10300Sstevel@tonic-gate 			mio = (MADT_INTERRUPT_OVERRIDE *) ap;
10310Sstevel@tonic-gate 			if (acpi_isop == NULL)
10320Sstevel@tonic-gate 				acpi_isop = mio;
10330Sstevel@tonic-gate 			acpi_iso_cnt++;
10340Sstevel@tonic-gate 			break;
10350Sstevel@tonic-gate 
10360Sstevel@tonic-gate 		case APIC_NMI:
10370Sstevel@tonic-gate 			/* UNIMPLEMENTED */
10380Sstevel@tonic-gate 			mns = (MADT_NMI_SOURCE *) ap;
10390Sstevel@tonic-gate 			if (acpi_nmi_sp == NULL)
10400Sstevel@tonic-gate 				acpi_nmi_sp = mns;
10410Sstevel@tonic-gate 			acpi_nmi_scnt++;
10420Sstevel@tonic-gate 
10430Sstevel@tonic-gate 			cmn_err(CE_NOTE, "!apic: nmi source: %d %d %d\n",
10440Sstevel@tonic-gate 				mns->Interrupt, mns->Polarity,
10450Sstevel@tonic-gate 				mns->TriggerMode);
10460Sstevel@tonic-gate 			break;
10470Sstevel@tonic-gate 
10480Sstevel@tonic-gate 		case APIC_LOCAL_NMI:
10490Sstevel@tonic-gate 			/* UNIMPLEMENTED */
10500Sstevel@tonic-gate 			mlan = (MADT_LOCAL_APIC_NMI *) ap;
10510Sstevel@tonic-gate 			if (acpi_nmi_cp == NULL)
10520Sstevel@tonic-gate 				acpi_nmi_cp = mlan;
10530Sstevel@tonic-gate 			acpi_nmi_ccnt++;
10540Sstevel@tonic-gate 
10550Sstevel@tonic-gate 			cmn_err(CE_NOTE, "!apic: local nmi: %d %d %d %d\n",
10560Sstevel@tonic-gate 				mlan->ProcessorId, mlan->Polarity,
10570Sstevel@tonic-gate 				mlan->TriggerMode, mlan->Lint);
10580Sstevel@tonic-gate 			break;
10590Sstevel@tonic-gate 
10600Sstevel@tonic-gate 		case APIC_ADDRESS_OVERRIDE:
10610Sstevel@tonic-gate 			/* UNIMPLEMENTED */
10620Sstevel@tonic-gate 			mao = (MADT_ADDRESS_OVERRIDE *) ap;
10630Sstevel@tonic-gate 			cmn_err(CE_NOTE, "!apic: address override: %lx\n",
10640Sstevel@tonic-gate 				(long)mao->Address);
10650Sstevel@tonic-gate 			break;
10660Sstevel@tonic-gate 
10670Sstevel@tonic-gate 		case APIC_IO_SAPIC:
10680Sstevel@tonic-gate 			/* UNIMPLEMENTED */
10690Sstevel@tonic-gate 			misa = (MADT_IO_SAPIC *) ap;
10700Sstevel@tonic-gate 
10710Sstevel@tonic-gate 			cmn_err(CE_NOTE, "!apic: io sapic: %d %d %lx\n",
10720Sstevel@tonic-gate 				misa->IoSapicId, misa->InterruptBase,
10730Sstevel@tonic-gate 				(long)misa->Address);
10740Sstevel@tonic-gate 			break;
10750Sstevel@tonic-gate 
10760Sstevel@tonic-gate 		case APIC_XRUPT_SOURCE:
10770Sstevel@tonic-gate 			/* UNIMPLEMENTED */
10780Sstevel@tonic-gate 			mis = (MADT_INTERRUPT_SOURCE *) ap;
10790Sstevel@tonic-gate 
10800Sstevel@tonic-gate 			cmn_err(CE_NOTE,
10810Sstevel@tonic-gate 				"!apic: irq source: %d %d %d %d %d %d %d\n",
10820Sstevel@tonic-gate 				mis->ProcessorId, mis->ProcessorEid,
10830Sstevel@tonic-gate 				mis->Interrupt, mis->Polarity,
10840Sstevel@tonic-gate 				mis->TriggerMode, mis->InterruptType,
10850Sstevel@tonic-gate 				mis->IoSapicVector);
10860Sstevel@tonic-gate 			break;
10870Sstevel@tonic-gate 		case APIC_RESERVED:
10880Sstevel@tonic-gate 		default:
10890Sstevel@tonic-gate 			goto cleanup;
10900Sstevel@tonic-gate 		}
10910Sstevel@tonic-gate 
10920Sstevel@tonic-gate 		/* advance to next entry */
10930Sstevel@tonic-gate 		madt_seen += ap->Length;
10940Sstevel@tonic-gate 		ap = (APIC_HEADER *)(((char *)ap) + ap->Length);
10950Sstevel@tonic-gate 	}
10960Sstevel@tonic-gate 
10970Sstevel@tonic-gate 	if ((apic_cpus = kmem_zalloc(sizeof (*apic_cpus) * apic_nproc,
10980Sstevel@tonic-gate 	    KM_NOSLEEP)) == NULL)
10990Sstevel@tonic-gate 		goto cleanup;
11000Sstevel@tonic-gate 
11010Sstevel@tonic-gate 	apic_cpumask = (1 << apic_nproc) - 1;
11020Sstevel@tonic-gate 
11030Sstevel@tonic-gate 	/*
11040Sstevel@tonic-gate 	 * ACPI doesn't provide the local apic ver, get it directly from the
11050Sstevel@tonic-gate 	 * local apic
11060Sstevel@tonic-gate 	 */
11070Sstevel@tonic-gate 	ver = apicadr[APIC_VERS_REG];
11080Sstevel@tonic-gate 	for (i = 0; i < apic_nproc; i++) {
11090Sstevel@tonic-gate 		apic_cpus[i].aci_local_id = local_ids[i];
11100Sstevel@tonic-gate 		apic_cpus[i].aci_local_ver = (uchar_t)(ver & 0xFF);
11110Sstevel@tonic-gate 	}
11120Sstevel@tonic-gate 	for (i = 0; i < apic_io_max; i++) {
11130Sstevel@tonic-gate 		ioapic = apicioadr[i];
11140Sstevel@tonic-gate 
11150Sstevel@tonic-gate 		/*
11160Sstevel@tonic-gate 		 * need to check Sitka on the following acpi problem
11170Sstevel@tonic-gate 		 * On the Sitka, the ioapic's apic_id field isn't reporting
11180Sstevel@tonic-gate 		 * the actual io apic id. We have reported this problem
11190Sstevel@tonic-gate 		 * to Intel. Until they fix the problem, we will get the
11200Sstevel@tonic-gate 		 * actual id directly from the ioapic.
11210Sstevel@tonic-gate 		 */
11220Sstevel@tonic-gate 		ioapic[APIC_IO_REG] = APIC_ID_CMD;
11230Sstevel@tonic-gate 		id = ioapic[APIC_IO_DATA];
11240Sstevel@tonic-gate 		hid = (uchar_t)(((uint_t)id) >> 24);
11250Sstevel@tonic-gate 
11260Sstevel@tonic-gate 		if (hid != apic_io_id[i]) {
11270Sstevel@tonic-gate 			if (apic_io_id[i] == 0)
11280Sstevel@tonic-gate 				apic_io_id[i] = hid;
11290Sstevel@tonic-gate 			else { /* set ioapic id to whatever reported by ACPI */
11300Sstevel@tonic-gate 				id = ((int32_t)apic_io_id[i]) << 24;
11310Sstevel@tonic-gate 				ioapic[APIC_IO_REG] = APIC_ID_CMD;
11320Sstevel@tonic-gate 				ioapic[APIC_IO_DATA] = id;
11330Sstevel@tonic-gate 			}
11340Sstevel@tonic-gate 		}
11350Sstevel@tonic-gate 		ioapic[APIC_IO_REG] = APIC_VERS_CMD;
11360Sstevel@tonic-gate 		ver = ioapic[APIC_IO_DATA];
11370Sstevel@tonic-gate 		apic_io_ver[i] = (uchar_t)(ver & 0xff);
11380Sstevel@tonic-gate 		intmax = (ver >> 16) & 0xff;
11390Sstevel@tonic-gate 		apic_io_vectend[i] = apic_io_vectbase[i] + intmax;
1140881Sjohnny 		if (apic_first_avail_irq <= apic_io_vectend[i])
1141881Sjohnny 			apic_first_avail_irq = apic_io_vectend[i] + 1;
11420Sstevel@tonic-gate 	}
11430Sstevel@tonic-gate 
11440Sstevel@tonic-gate 
11450Sstevel@tonic-gate 	/*
11460Sstevel@tonic-gate 	 * Process SCI configuration here
11470Sstevel@tonic-gate 	 * An error may be returned here if
11480Sstevel@tonic-gate 	 * acpi-user-options specifies legacy mode
11490Sstevel@tonic-gate 	 * (no SCI, no ACPI mode)
11500Sstevel@tonic-gate 	 */
11510Sstevel@tonic-gate 	if (acpica_get_sci(&sci, &sci_flags) != AE_OK)
11520Sstevel@tonic-gate 		sci = -1;
11530Sstevel@tonic-gate 
11540Sstevel@tonic-gate 	/*
11550Sstevel@tonic-gate 	 * Now call acpi_init() to generate namespaces
11560Sstevel@tonic-gate 	 * If this fails, we don't attempt to use ACPI
11570Sstevel@tonic-gate 	 * even if we were able to get a MADT above
11580Sstevel@tonic-gate 	 */
11590Sstevel@tonic-gate 	if (acpica_init() != AE_OK)
11600Sstevel@tonic-gate 		goto cleanup;
11610Sstevel@tonic-gate 
11620Sstevel@tonic-gate 	/*
11630Sstevel@tonic-gate 	 * Squirrel away the SCI and flags for later on
11640Sstevel@tonic-gate 	 * in apic_picinit() when we're ready
11650Sstevel@tonic-gate 	 */
11660Sstevel@tonic-gate 	apic_sci_vect = sci;
11670Sstevel@tonic-gate 	apic_sci_flags = sci_flags;
11680Sstevel@tonic-gate 
11690Sstevel@tonic-gate 	if (apic_verbose & APIC_VERBOSE_IRQ_FLAG)
11700Sstevel@tonic-gate 		acpi_verboseflags |= PSM_VERBOSE_IRQ_FLAG;
11710Sstevel@tonic-gate 
11720Sstevel@tonic-gate 	if (apic_verbose & APIC_VERBOSE_POWEROFF_FLAG)
11730Sstevel@tonic-gate 		acpi_verboseflags |= PSM_VERBOSE_POWEROFF_FLAG;
11740Sstevel@tonic-gate 
11750Sstevel@tonic-gate 	if (apic_verbose & APIC_VERBOSE_POWEROFF_PAUSE_FLAG)
11760Sstevel@tonic-gate 		acpi_verboseflags |= PSM_VERBOSE_POWEROFF_PAUSE_FLAG;
11770Sstevel@tonic-gate 
11780Sstevel@tonic-gate 	if (acpi_psm_init(apic_psm_info.p_mach_idstring, acpi_verboseflags) ==
11790Sstevel@tonic-gate 	    ACPI_PSM_FAILURE)
11800Sstevel@tonic-gate 		goto cleanup;
11810Sstevel@tonic-gate 
11820Sstevel@tonic-gate 	/* Enable ACPI APIC interrupt routing */
11830Sstevel@tonic-gate 	arglist.Count = 1;
11840Sstevel@tonic-gate 	arglist.Pointer = &arg;
11850Sstevel@tonic-gate 	arg.Type = ACPI_TYPE_INTEGER;
11860Sstevel@tonic-gate 	arg.Integer.Value = ACPI_APIC_MODE;	/* 1 */
11870Sstevel@tonic-gate 	rv = AcpiEvaluateObject(NULL, "\\_PIC", &arglist, NULL);
11880Sstevel@tonic-gate 	if (rv == AE_OK) {
11890Sstevel@tonic-gate 		build_reserved_irqlist((uchar_t *)apic_reserved_irqlist);
11900Sstevel@tonic-gate 		apic_enable_acpi = 1;
11910Sstevel@tonic-gate 		if (apic_use_acpi_madt_only) {
11920Sstevel@tonic-gate 			cmn_err(CE_CONT,
11930Sstevel@tonic-gate 			    "?Using ACPI for CPU/IOAPIC information ONLY\n");
11940Sstevel@tonic-gate 		}
11950Sstevel@tonic-gate 		return (PSM_SUCCESS);
11960Sstevel@tonic-gate 	}
11970Sstevel@tonic-gate 	/* if setting APIC mode failed above, we fall through to cleanup */
11980Sstevel@tonic-gate 
11990Sstevel@tonic-gate cleanup:
12000Sstevel@tonic-gate 	if (apicadr != NULL) {
12010Sstevel@tonic-gate 		psm_unmap_phys((caddr_t)apicadr, APIC_LOCAL_MEMLEN);
12020Sstevel@tonic-gate 		apicadr = NULL;
12030Sstevel@tonic-gate 	}
12040Sstevel@tonic-gate 	apic_nproc = 0;
12050Sstevel@tonic-gate 	for (i = 0; i < apic_io_max; i++) {
12060Sstevel@tonic-gate 		psm_unmap_phys((caddr_t)apicioadr[i], APIC_IO_MEMLEN);
12070Sstevel@tonic-gate 		apicioadr[i] = NULL;
12080Sstevel@tonic-gate 	}
12090Sstevel@tonic-gate 	apic_io_max = 0;
12100Sstevel@tonic-gate 	acpi_isop = NULL;
12110Sstevel@tonic-gate 	acpi_iso_cnt = 0;
12120Sstevel@tonic-gate 	acpi_nmi_sp = NULL;
12130Sstevel@tonic-gate 	acpi_nmi_scnt = 0;
12140Sstevel@tonic-gate 	acpi_nmi_cp = NULL;
12150Sstevel@tonic-gate 	acpi_nmi_ccnt = 0;
12160Sstevel@tonic-gate 	return (PSM_FAILURE);
12170Sstevel@tonic-gate }
12180Sstevel@tonic-gate 
12190Sstevel@tonic-gate /*
12200Sstevel@tonic-gate  * Handle default configuration. Fill in reqd global variables & tables
12210Sstevel@tonic-gate  * Fill all details as MP table does not give any more info
12220Sstevel@tonic-gate  */
12230Sstevel@tonic-gate static int
12240Sstevel@tonic-gate apic_handle_defconf()
12250Sstevel@tonic-gate {
12260Sstevel@tonic-gate 	uint_t	lid;
12270Sstevel@tonic-gate 
12280Sstevel@tonic-gate 	/*LINTED: pointer cast may result in improper alignment */
12290Sstevel@tonic-gate 	apicioadr[0] = (int32_t *)psm_map_phys(APIC_IO_ADDR,
12300Sstevel@tonic-gate 	    APIC_IO_MEMLEN, PROT_READ | PROT_WRITE);
12310Sstevel@tonic-gate 	/*LINTED: pointer cast may result in improper alignment */
12320Sstevel@tonic-gate 	apicadr = (uint32_t *)psm_map_phys(APIC_LOCAL_ADDR,
12330Sstevel@tonic-gate 	    APIC_LOCAL_MEMLEN, PROT_READ | PROT_WRITE);
12340Sstevel@tonic-gate 	apic_cpus = (apic_cpus_info_t *)
12350Sstevel@tonic-gate 	    kmem_zalloc(sizeof (*apic_cpus) * 2, KM_NOSLEEP);
12360Sstevel@tonic-gate 	if ((!apicadr) || (!apicioadr[0]) || (!apic_cpus))
12370Sstevel@tonic-gate 		goto apic_handle_defconf_fail;
12380Sstevel@tonic-gate 	apic_cpumask = 3;
12390Sstevel@tonic-gate 	apic_nproc = 2;
12400Sstevel@tonic-gate 	lid = apicadr[APIC_LID_REG];
12410Sstevel@tonic-gate 	apic_cpus[0].aci_local_id = (uchar_t)(lid >> APIC_ID_BIT_OFFSET);
12420Sstevel@tonic-gate 	/*
12430Sstevel@tonic-gate 	 * According to the PC+MP spec 1.1, the local ids
12440Sstevel@tonic-gate 	 * for the default configuration has to be 0 or 1
12450Sstevel@tonic-gate 	 */
12460Sstevel@tonic-gate 	if (apic_cpus[0].aci_local_id == 1)
12470Sstevel@tonic-gate 		apic_cpus[1].aci_local_id = 0;
12480Sstevel@tonic-gate 	else if (apic_cpus[0].aci_local_id == 0)
12490Sstevel@tonic-gate 		apic_cpus[1].aci_local_id = 1;
12500Sstevel@tonic-gate 	else
12510Sstevel@tonic-gate 		goto apic_handle_defconf_fail;
12520Sstevel@tonic-gate 
12530Sstevel@tonic-gate 	apic_io_id[0] = 2;
12540Sstevel@tonic-gate 	apic_io_max = 1;
12550Sstevel@tonic-gate 	if (apic_defconf >= 5) {
12560Sstevel@tonic-gate 		apic_cpus[0].aci_local_ver = APIC_INTEGRATED_VERS;
12570Sstevel@tonic-gate 		apic_cpus[1].aci_local_ver = APIC_INTEGRATED_VERS;
12580Sstevel@tonic-gate 		apic_io_ver[0] = APIC_INTEGRATED_VERS;
12590Sstevel@tonic-gate 	} else {
12600Sstevel@tonic-gate 		apic_cpus[0].aci_local_ver = 0;		/* 82489 DX */
12610Sstevel@tonic-gate 		apic_cpus[1].aci_local_ver = 0;
12620Sstevel@tonic-gate 		apic_io_ver[0] = 0;
12630Sstevel@tonic-gate 	}
12640Sstevel@tonic-gate 	if (apic_defconf == 2 || apic_defconf == 3 || apic_defconf == 6)
12650Sstevel@tonic-gate 		eisa_level_intr_mask = (inb(EISA_LEVEL_CNTL + 1) << 8) |
12660Sstevel@tonic-gate 		    inb(EISA_LEVEL_CNTL) | ((uint_t)INT32_MAX + 1);
12670Sstevel@tonic-gate 	return (PSM_SUCCESS);
12680Sstevel@tonic-gate 
12690Sstevel@tonic-gate apic_handle_defconf_fail:
12700Sstevel@tonic-gate 	if (apic_cpus)
12710Sstevel@tonic-gate 		kmem_free(apic_cpus, sizeof (*apic_cpus) * 2);
12720Sstevel@tonic-gate 	if (apicadr)
12730Sstevel@tonic-gate 		psm_unmap_phys((caddr_t)apicadr, APIC_LOCAL_MEMLEN);
12740Sstevel@tonic-gate 	if (apicioadr[0])
12750Sstevel@tonic-gate 		psm_unmap_phys((caddr_t)apicioadr[0], APIC_IO_MEMLEN);
12760Sstevel@tonic-gate 	return (PSM_FAILURE);
12770Sstevel@tonic-gate }
12780Sstevel@tonic-gate 
12790Sstevel@tonic-gate /* Parse the entries in MP configuration table and collect info that we need */
12800Sstevel@tonic-gate static int
12810Sstevel@tonic-gate apic_parse_mpct(caddr_t mpct, int bypass_cpus_and_ioapics)
12820Sstevel@tonic-gate {
12830Sstevel@tonic-gate 	struct	apic_procent	*procp;
12840Sstevel@tonic-gate 	struct	apic_bus	*busp;
12850Sstevel@tonic-gate 	struct	apic_io_entry	*ioapicp;
12860Sstevel@tonic-gate 	struct	apic_io_intr	*intrp;
12870Sstevel@tonic-gate 	volatile int32_t	*ioapic;
12880Sstevel@tonic-gate 	uint_t	lid;
12890Sstevel@tonic-gate 	int	id;
12900Sstevel@tonic-gate 	uchar_t hid;
12910Sstevel@tonic-gate 
12920Sstevel@tonic-gate 	/*LINTED: pointer cast may result in improper alignment */
12930Sstevel@tonic-gate 	procp = (struct apic_procent *)(mpct + sizeof (struct apic_mp_cnf_hdr));
12940Sstevel@tonic-gate 
12950Sstevel@tonic-gate 	/* No need to count cpu entries if we won't use them */
12960Sstevel@tonic-gate 	if (!bypass_cpus_and_ioapics) {
12970Sstevel@tonic-gate 
12980Sstevel@tonic-gate 		/* Find max # of CPUS and allocate structure accordingly */
12990Sstevel@tonic-gate 		apic_nproc = 0;
13000Sstevel@tonic-gate 		while (procp->proc_entry == APIC_CPU_ENTRY) {
13010Sstevel@tonic-gate 			if (procp->proc_cpuflags & CPUFLAGS_EN) {
13020Sstevel@tonic-gate 				apic_nproc++;
13030Sstevel@tonic-gate 			}
13040Sstevel@tonic-gate 			procp++;
13050Sstevel@tonic-gate 		}
13060Sstevel@tonic-gate 		if (apic_nproc > NCPU)
13070Sstevel@tonic-gate 			cmn_err(CE_WARN, "pcplusmp: exceeded "
13080Sstevel@tonic-gate 			    "maximum no. of CPUs (= %d)", NCPU);
13090Sstevel@tonic-gate 		if (!apic_nproc || !(apic_cpus = (apic_cpus_info_t *)
13100Sstevel@tonic-gate 		    kmem_zalloc(sizeof (*apic_cpus)*apic_nproc, KM_NOSLEEP)))
13110Sstevel@tonic-gate 			return (PSM_FAILURE);
13120Sstevel@tonic-gate 	}
13130Sstevel@tonic-gate 
13140Sstevel@tonic-gate 	/*LINTED: pointer cast may result in improper alignment */
13150Sstevel@tonic-gate 	procp = (struct apic_procent *)(mpct + sizeof (struct apic_mp_cnf_hdr));
13160Sstevel@tonic-gate 
13170Sstevel@tonic-gate 	/*
13180Sstevel@tonic-gate 	 * start with index 1 as 0 needs to be filled in with Boot CPU, but
13190Sstevel@tonic-gate 	 * if we're bypassing this information, it has already been filled
13200Sstevel@tonic-gate 	 * in by acpi_probe(), so don't overwrite it.
13210Sstevel@tonic-gate 	 */
13220Sstevel@tonic-gate 	if (!bypass_cpus_and_ioapics)
13230Sstevel@tonic-gate 		apic_nproc = 1;
13240Sstevel@tonic-gate 
13250Sstevel@tonic-gate 	while (procp->proc_entry == APIC_CPU_ENTRY) {
13260Sstevel@tonic-gate 		/* check whether the cpu exists or not */
13270Sstevel@tonic-gate 		if (!bypass_cpus_and_ioapics &&
13280Sstevel@tonic-gate 		    procp->proc_cpuflags & CPUFLAGS_EN) {
13290Sstevel@tonic-gate 			if (procp->proc_cpuflags & CPUFLAGS_BP) { /* Boot CPU */
13300Sstevel@tonic-gate 				lid = apicadr[APIC_LID_REG];
13310Sstevel@tonic-gate 				apic_cpus[0].aci_local_id = procp->proc_apicid;
13320Sstevel@tonic-gate 				if (apic_cpus[0].aci_local_id !=
13330Sstevel@tonic-gate 				    (uchar_t)(lid >> APIC_ID_BIT_OFFSET)) {
13340Sstevel@tonic-gate 					return (PSM_FAILURE);
13350Sstevel@tonic-gate 				}
13360Sstevel@tonic-gate 				apic_cpus[0].aci_local_ver =
13370Sstevel@tonic-gate 				    procp->proc_version;
13380Sstevel@tonic-gate 			} else {
13390Sstevel@tonic-gate 
13400Sstevel@tonic-gate 				apic_cpus[apic_nproc].aci_local_id =
13410Sstevel@tonic-gate 				    procp->proc_apicid;
13420Sstevel@tonic-gate 				apic_cpus[apic_nproc].aci_local_ver =
13430Sstevel@tonic-gate 				    procp->proc_version;
13440Sstevel@tonic-gate 				apic_nproc++;
13450Sstevel@tonic-gate 
13460Sstevel@tonic-gate 			}
13470Sstevel@tonic-gate 		}
13480Sstevel@tonic-gate 		procp++;
13490Sstevel@tonic-gate 	}
13500Sstevel@tonic-gate 
13510Sstevel@tonic-gate 	if (!bypass_cpus_and_ioapics) {
13520Sstevel@tonic-gate 		/* convert the number of processors into a cpumask */
13530Sstevel@tonic-gate 		apic_cpumask = (1 << apic_nproc) - 1;
13540Sstevel@tonic-gate 	}
13550Sstevel@tonic-gate 
13560Sstevel@tonic-gate 	/*
13570Sstevel@tonic-gate 	 * Save start of bus entries for later use.
13580Sstevel@tonic-gate 	 * Get EISA level cntrl if EISA bus is present.
13590Sstevel@tonic-gate 	 * Also get the CPI bus id for single CPI bus case
13600Sstevel@tonic-gate 	 */
13610Sstevel@tonic-gate 	apic_busp = busp = (struct apic_bus *)procp;
13620Sstevel@tonic-gate 	while (busp->bus_entry == APIC_BUS_ENTRY) {
13630Sstevel@tonic-gate 		lid = apic_find_bus_type((char *)&busp->bus_str1);
13640Sstevel@tonic-gate 		if (lid	== BUS_EISA) {
13650Sstevel@tonic-gate 			eisa_level_intr_mask = (inb(EISA_LEVEL_CNTL + 1) << 8) |
13660Sstevel@tonic-gate 			    inb(EISA_LEVEL_CNTL) | ((uint_t)INT32_MAX + 1);
13670Sstevel@tonic-gate 		} else if (lid == BUS_PCI) {
13680Sstevel@tonic-gate 			/*
13690Sstevel@tonic-gate 			 * apic_single_pci_busid will be used only if
13700Sstevel@tonic-gate 			 * apic_pic_bus_total is equal to 1
13710Sstevel@tonic-gate 			 */
13720Sstevel@tonic-gate 			apic_pci_bus_total++;
13730Sstevel@tonic-gate 			apic_single_pci_busid = busp->bus_id;
13740Sstevel@tonic-gate 		}
13750Sstevel@tonic-gate 		busp++;
13760Sstevel@tonic-gate 	}
13770Sstevel@tonic-gate 
13780Sstevel@tonic-gate 	ioapicp = (struct apic_io_entry *)busp;
13790Sstevel@tonic-gate 
13800Sstevel@tonic-gate 	if (!bypass_cpus_and_ioapics)
13810Sstevel@tonic-gate 		apic_io_max = 0;
13820Sstevel@tonic-gate 	do {
13830Sstevel@tonic-gate 		if (!bypass_cpus_and_ioapics && apic_io_max < MAX_IO_APIC) {
13840Sstevel@tonic-gate 			if (ioapicp->io_flags & IOAPIC_FLAGS_EN) {
13850Sstevel@tonic-gate 				apic_io_id[apic_io_max] = ioapicp->io_apicid;
13860Sstevel@tonic-gate 				apic_io_ver[apic_io_max] = ioapicp->io_version;
13870Sstevel@tonic-gate 		/*LINTED: pointer cast may result in improper alignment */
13880Sstevel@tonic-gate 				apicioadr[apic_io_max] =
13890Sstevel@tonic-gate 				    (int32_t *)psm_map_phys(
13900Sstevel@tonic-gate 				    (uint32_t)ioapicp->io_apic_addr,
13910Sstevel@tonic-gate 				    APIC_IO_MEMLEN, PROT_READ | PROT_WRITE);
13920Sstevel@tonic-gate 
13930Sstevel@tonic-gate 				if (!apicioadr[apic_io_max])
13940Sstevel@tonic-gate 					return (PSM_FAILURE);
13950Sstevel@tonic-gate 
13960Sstevel@tonic-gate 				ioapic = apicioadr[apic_io_max];
13970Sstevel@tonic-gate 				ioapic[APIC_IO_REG] = APIC_ID_CMD;
13980Sstevel@tonic-gate 				id = ioapic[APIC_IO_DATA];
13990Sstevel@tonic-gate 				hid = (uchar_t)(((uint_t)id) >> 24);
14000Sstevel@tonic-gate 
14010Sstevel@tonic-gate 				if (hid != apic_io_id[apic_io_max]) {
14020Sstevel@tonic-gate 					if (apic_io_id[apic_io_max] == 0)
14030Sstevel@tonic-gate 						apic_io_id[apic_io_max] = hid;
14040Sstevel@tonic-gate 					else {
14050Sstevel@tonic-gate 						/*
14060Sstevel@tonic-gate 						 * set ioapic id to whatever
14070Sstevel@tonic-gate 						 * reported by MPS
14080Sstevel@tonic-gate 						 *
14090Sstevel@tonic-gate 						 * may not need to set index
14100Sstevel@tonic-gate 						 * again ???
14110Sstevel@tonic-gate 						 * take it out and try
14120Sstevel@tonic-gate 						 */
14130Sstevel@tonic-gate 
14140Sstevel@tonic-gate 						id = ((int32_t)
14150Sstevel@tonic-gate 						    apic_io_id[apic_io_max]) <<
14160Sstevel@tonic-gate 						    24;
14170Sstevel@tonic-gate 
14180Sstevel@tonic-gate 						ioapic[APIC_IO_REG] =
14190Sstevel@tonic-gate 						    APIC_ID_CMD;
14200Sstevel@tonic-gate 
14210Sstevel@tonic-gate 						ioapic[APIC_IO_DATA] = id;
14220Sstevel@tonic-gate 
14230Sstevel@tonic-gate 					}
14240Sstevel@tonic-gate 				}
14250Sstevel@tonic-gate 				apic_io_max++;
14260Sstevel@tonic-gate 			}
14270Sstevel@tonic-gate 		}
14280Sstevel@tonic-gate 		ioapicp++;
14290Sstevel@tonic-gate 	} while (ioapicp->io_entry == APIC_IO_ENTRY);
14300Sstevel@tonic-gate 
14310Sstevel@tonic-gate 	apic_io_intrp = (struct apic_io_intr *)ioapicp;
14320Sstevel@tonic-gate 
14330Sstevel@tonic-gate 	intrp = apic_io_intrp;
14340Sstevel@tonic-gate 	while (intrp->intr_entry == APIC_IO_INTR_ENTRY) {
14350Sstevel@tonic-gate 		if ((intrp->intr_irq > APIC_MAX_ISA_IRQ) ||
14360Sstevel@tonic-gate 		    (apic_find_bus(intrp->intr_busid) == BUS_PCI)) {
14370Sstevel@tonic-gate 			apic_irq_translate = 1;
14380Sstevel@tonic-gate 			break;
14390Sstevel@tonic-gate 		}
14400Sstevel@tonic-gate 		intrp++;
14410Sstevel@tonic-gate 	}
14420Sstevel@tonic-gate 
14430Sstevel@tonic-gate 	return (PSM_SUCCESS);
14440Sstevel@tonic-gate }
14450Sstevel@tonic-gate 
1446916Sschwartz boolean_t
1447916Sschwartz apic_cpu_in_range(int cpu)
1448916Sschwartz {
1449916Sschwartz 	return ((cpu & ~IRQ_USER_BOUND) < apic_nproc);
1450916Sschwartz }
1451916Sschwartz 
14520Sstevel@tonic-gate static struct apic_mpfps_hdr *
14530Sstevel@tonic-gate apic_find_fps_sig(caddr_t cptr, int len)
14540Sstevel@tonic-gate {
14550Sstevel@tonic-gate 	int	i;
14560Sstevel@tonic-gate 
14570Sstevel@tonic-gate 	/* Look for the pattern "_MP_" */
14580Sstevel@tonic-gate 	for (i = 0; i < len; i += 16) {
14590Sstevel@tonic-gate 		if ((*(cptr+i) == '_') &&
14600Sstevel@tonic-gate 		    (*(cptr+i+1) == 'M') &&
14610Sstevel@tonic-gate 		    (*(cptr+i+2) == 'P') &&
14620Sstevel@tonic-gate 		    (*(cptr+i+3) == '_'))
14630Sstevel@tonic-gate 		    /*LINTED: pointer cast may result in improper alignment */
14640Sstevel@tonic-gate 			return ((struct apic_mpfps_hdr *)(cptr + i));
14650Sstevel@tonic-gate 	}
14660Sstevel@tonic-gate 	return (NULL);
14670Sstevel@tonic-gate }
14680Sstevel@tonic-gate 
14690Sstevel@tonic-gate static int
14700Sstevel@tonic-gate apic_checksum(caddr_t bptr, int len)
14710Sstevel@tonic-gate {
14720Sstevel@tonic-gate 	int	i;
14730Sstevel@tonic-gate 	uchar_t	cksum;
14740Sstevel@tonic-gate 
14750Sstevel@tonic-gate 	cksum = 0;
14760Sstevel@tonic-gate 	for (i = 0; i < len; i++)
14770Sstevel@tonic-gate 		cksum += *bptr++;
14780Sstevel@tonic-gate 	return ((int)cksum);
14790Sstevel@tonic-gate }
14800Sstevel@tonic-gate 
14810Sstevel@tonic-gate 
14820Sstevel@tonic-gate /*
14830Sstevel@tonic-gate  * Initialise vector->ipl and ipl->pri arrays. level_intr and irqtable
14840Sstevel@tonic-gate  * are also set to NULL. vector->irq is set to a value which cannot map
14850Sstevel@tonic-gate  * to a real irq to show that it is free.
14860Sstevel@tonic-gate  */
14870Sstevel@tonic-gate void
14880Sstevel@tonic-gate apic_init()
14890Sstevel@tonic-gate {
14900Sstevel@tonic-gate 	int	i;
14910Sstevel@tonic-gate 	int	*iptr;
14920Sstevel@tonic-gate 
14930Sstevel@tonic-gate 	int	j = 1;
14940Sstevel@tonic-gate 	apic_ipltopri[0] = APIC_VECTOR_PER_IPL; /* leave 0 for idle */
14950Sstevel@tonic-gate 	for (i = 0; i < (APIC_AVAIL_VECTOR / APIC_VECTOR_PER_IPL); i++) {
14960Sstevel@tonic-gate 		if ((i < ((APIC_AVAIL_VECTOR / APIC_VECTOR_PER_IPL) - 1)) &&
14970Sstevel@tonic-gate 		    (apic_vectortoipl[i + 1] == apic_vectortoipl[i]))
14980Sstevel@tonic-gate 			/* get to highest vector at the same ipl */
14990Sstevel@tonic-gate 			continue;
15000Sstevel@tonic-gate 		for (; j <= apic_vectortoipl[i]; j++) {
15010Sstevel@tonic-gate 			apic_ipltopri[j] = (i << APIC_IPL_SHIFT) +
15020Sstevel@tonic-gate 			    APIC_BASE_VECT;
15030Sstevel@tonic-gate 		}
15040Sstevel@tonic-gate 	}
15050Sstevel@tonic-gate 	for (; j < MAXIPL + 1; j++)
15060Sstevel@tonic-gate 		/* fill up any empty ipltopri slots */
15070Sstevel@tonic-gate 		apic_ipltopri[j] = (i << APIC_IPL_SHIFT) + APIC_BASE_VECT;
15080Sstevel@tonic-gate 
15090Sstevel@tonic-gate 	/* cpu 0 is always up */
15100Sstevel@tonic-gate 	apic_cpus[0].aci_status = APIC_CPU_ONLINE | APIC_CPU_INTR_ENABLE;
15110Sstevel@tonic-gate 
15120Sstevel@tonic-gate 	iptr = (int *)&apic_irq_table[0];
15130Sstevel@tonic-gate 	for (i = 0; i <= APIC_MAX_VECTOR; i++) {
15140Sstevel@tonic-gate 		apic_level_intr[i] = 0;
15150Sstevel@tonic-gate 		*iptr++ = NULL;
15160Sstevel@tonic-gate 		apic_vector_to_irq[i] = APIC_RESV_IRQ;
15170Sstevel@tonic-gate 		apic_reprogram_info[i].valid = 0;
15180Sstevel@tonic-gate 		apic_reprogram_info[i].bindcpu = 0;
15190Sstevel@tonic-gate 		apic_reprogram_info[i].timeouts = 0;
15200Sstevel@tonic-gate 	}
15210Sstevel@tonic-gate 
15220Sstevel@tonic-gate 	/*
15230Sstevel@tonic-gate 	 * Allocate a dummy irq table entry for the reserved entry.
15240Sstevel@tonic-gate 	 * This takes care of the race between removing an irq and
15250Sstevel@tonic-gate 	 * clock detecting a CPU in that irq during interrupt load
15260Sstevel@tonic-gate 	 * sampling.
15270Sstevel@tonic-gate 	 */
15280Sstevel@tonic-gate 	apic_irq_table[APIC_RESV_IRQ] =
15290Sstevel@tonic-gate 	    kmem_zalloc(sizeof (apic_irq_t), KM_NOSLEEP);
15300Sstevel@tonic-gate 
15310Sstevel@tonic-gate 	mutex_init(&airq_mutex, NULL, MUTEX_DEFAULT, NULL);
15320Sstevel@tonic-gate 	mutex_init(&apic_reprogram_timeout_mutex, NULL, MUTEX_DEFAULT, NULL);
15330Sstevel@tonic-gate #if defined(__amd64)
15340Sstevel@tonic-gate 	/*
15350Sstevel@tonic-gate 	 * Make cpu-specific interrupt info point to cr8pri vector
15360Sstevel@tonic-gate 	 */
15370Sstevel@tonic-gate 	for (i = 0; i <= MAXIPL; i++)
15380Sstevel@tonic-gate 		apic_cr8pri[i] = apic_ipltopri[i] >> APIC_IPL_SHIFT;
15390Sstevel@tonic-gate 	CPU->cpu_pri_data = apic_cr8pri;
15400Sstevel@tonic-gate 	intpri_use_cr8 = 1;
15410Sstevel@tonic-gate #endif	/* __amd64 */
15420Sstevel@tonic-gate }
15430Sstevel@tonic-gate 
15440Sstevel@tonic-gate /*
15450Sstevel@tonic-gate  * handler for APIC Error interrupt. Just print a warning and continue
15460Sstevel@tonic-gate  */
15470Sstevel@tonic-gate static int
15480Sstevel@tonic-gate apic_error_intr()
15490Sstevel@tonic-gate {
15500Sstevel@tonic-gate 	uint_t	error0, error1, error;
15510Sstevel@tonic-gate 	uint_t	i;
15520Sstevel@tonic-gate 
15530Sstevel@tonic-gate 	/*
15540Sstevel@tonic-gate 	 * We need to write before read as per 7.4.17 of system prog manual.
15550Sstevel@tonic-gate 	 * We do both and or the results to be safe
15560Sstevel@tonic-gate 	 */
15570Sstevel@tonic-gate 	error0 = apicadr[APIC_ERROR_STATUS];
15580Sstevel@tonic-gate 	apicadr[APIC_ERROR_STATUS] = 0;
15590Sstevel@tonic-gate 	error1 = apicadr[APIC_ERROR_STATUS];
15600Sstevel@tonic-gate 	error = error0 | error1;
15610Sstevel@tonic-gate 
15620Sstevel@tonic-gate 	/*
1563846Ssethg 	 * Clear the APIC error status (do this on all cpus that enter here)
1564846Ssethg 	 * (two writes are required due to the semantics of accessing the
1565846Ssethg 	 * error status register.)
1566846Ssethg 	 */
1567846Ssethg 	apicadr[APIC_ERROR_STATUS] = 0;
1568846Ssethg 	apicadr[APIC_ERROR_STATUS] = 0;
1569846Ssethg 
1570846Ssethg 	/*
15710Sstevel@tonic-gate 	 * Prevent more than 1 CPU from handling error interrupt causing
15720Sstevel@tonic-gate 	 * double printing (interleave of characters from multiple
15730Sstevel@tonic-gate 	 * CPU's when using prom_printf)
15740Sstevel@tonic-gate 	 */
15750Sstevel@tonic-gate 	if (lock_try(&apic_error_lock) == 0)
15760Sstevel@tonic-gate 		return (error ? DDI_INTR_CLAIMED : DDI_INTR_UNCLAIMED);
15770Sstevel@tonic-gate 	if (error) {
15780Sstevel@tonic-gate #if	DEBUG
15790Sstevel@tonic-gate 		if (apic_debug)
15800Sstevel@tonic-gate 			debug_enter("pcplusmp: APIC Error interrupt received");
15810Sstevel@tonic-gate #endif /* DEBUG */
15820Sstevel@tonic-gate 		if (apic_panic_on_apic_error)
15830Sstevel@tonic-gate 			cmn_err(CE_PANIC,
15840Sstevel@tonic-gate 			    "APIC Error interrupt on CPU %d. Status = %x\n",
15850Sstevel@tonic-gate 			    psm_get_cpu_id(), error);
15860Sstevel@tonic-gate 		else {
15870Sstevel@tonic-gate 			if ((error & ~APIC_CS_ERRORS) == 0) {
15880Sstevel@tonic-gate 				/* cksum error only */
15890Sstevel@tonic-gate 				apic_error |= APIC_ERR_APIC_ERROR;
15900Sstevel@tonic-gate 				apic_apic_error |= error;
15910Sstevel@tonic-gate 				apic_num_apic_errors++;
15920Sstevel@tonic-gate 				apic_num_cksum_errors++;
15930Sstevel@tonic-gate 			} else {
15940Sstevel@tonic-gate 				/*
15950Sstevel@tonic-gate 				 * prom_printf is the best shot we have of
15960Sstevel@tonic-gate 				 * something which is problem free from
15970Sstevel@tonic-gate 				 * high level/NMI type of interrupts
15980Sstevel@tonic-gate 				 */
15990Sstevel@tonic-gate 				prom_printf("APIC Error interrupt on CPU %d. "
16000Sstevel@tonic-gate 				    "Status 0 = %x, Status 1 = %x\n",
16010Sstevel@tonic-gate 				    psm_get_cpu_id(), error0, error1);
16020Sstevel@tonic-gate 				apic_error |= APIC_ERR_APIC_ERROR;
16030Sstevel@tonic-gate 				apic_apic_error |= error;
16040Sstevel@tonic-gate 				apic_num_apic_errors++;
16050Sstevel@tonic-gate 				for (i = 0; i < apic_error_display_delay; i++) {
16060Sstevel@tonic-gate 					tenmicrosec();
16070Sstevel@tonic-gate 				}
16080Sstevel@tonic-gate 				/*
16090Sstevel@tonic-gate 				 * provide more delay next time limited to
16100Sstevel@tonic-gate 				 * roughly 1 clock tick time
16110Sstevel@tonic-gate 				 */
16120Sstevel@tonic-gate 				if (apic_error_display_delay < 500)
16130Sstevel@tonic-gate 					apic_error_display_delay *= 2;
16140Sstevel@tonic-gate 			}
16150Sstevel@tonic-gate 		}
16160Sstevel@tonic-gate 		lock_clear(&apic_error_lock);
16170Sstevel@tonic-gate 		return (DDI_INTR_CLAIMED);
16180Sstevel@tonic-gate 	} else {
16190Sstevel@tonic-gate 		lock_clear(&apic_error_lock);
16200Sstevel@tonic-gate 		return (DDI_INTR_UNCLAIMED);
16210Sstevel@tonic-gate 	}
16220Sstevel@tonic-gate 	/* NOTREACHED */
16230Sstevel@tonic-gate }
16240Sstevel@tonic-gate 
16250Sstevel@tonic-gate /*
16260Sstevel@tonic-gate  * Turn off the mask bit in the performance counter Local Vector Table entry.
16270Sstevel@tonic-gate  */
16280Sstevel@tonic-gate static void
16290Sstevel@tonic-gate apic_cpcovf_mask_clear(void)
16300Sstevel@tonic-gate {
16310Sstevel@tonic-gate 	apicadr[APIC_PCINT_VECT] &= ~APIC_LVT_MASK;
16320Sstevel@tonic-gate }
16330Sstevel@tonic-gate 
16340Sstevel@tonic-gate static void
16350Sstevel@tonic-gate apic_init_intr()
16360Sstevel@tonic-gate {
16370Sstevel@tonic-gate 	processorid_t	cpun = psm_get_cpu_id();
16380Sstevel@tonic-gate 
16390Sstevel@tonic-gate #if defined(__amd64)
16400Sstevel@tonic-gate 	setcr8((ulong_t)(APIC_MASK_ALL >> APIC_IPL_SHIFT));
16410Sstevel@tonic-gate #else
16420Sstevel@tonic-gate 	apicadr[APIC_TASK_REG] = APIC_MASK_ALL;
16430Sstevel@tonic-gate #endif
16440Sstevel@tonic-gate 
16450Sstevel@tonic-gate 	if (apic_flat_model)
16460Sstevel@tonic-gate 		apicadr[APIC_FORMAT_REG] = APIC_FLAT_MODEL;
16470Sstevel@tonic-gate 	else
16480Sstevel@tonic-gate 		apicadr[APIC_FORMAT_REG] = APIC_CLUSTER_MODEL;
16490Sstevel@tonic-gate 	apicadr[APIC_DEST_REG] = AV_HIGH_ORDER >> cpun;
16500Sstevel@tonic-gate 
16510Sstevel@tonic-gate 	/* need to enable APIC before unmasking NMI */
16520Sstevel@tonic-gate 	apicadr[APIC_SPUR_INT_REG] = AV_UNIT_ENABLE | APIC_SPUR_INTR;
16530Sstevel@tonic-gate 
16540Sstevel@tonic-gate 	apicadr[APIC_LOCAL_TIMER] = AV_MASK;
16550Sstevel@tonic-gate 	apicadr[APIC_INT_VECT0]	= AV_MASK;	/* local intr reg 0 */
16560Sstevel@tonic-gate 	apicadr[APIC_INT_VECT1] = AV_NMI;	/* enable NMI */
16570Sstevel@tonic-gate 
16580Sstevel@tonic-gate 	if (apic_cpus[cpun].aci_local_ver < APIC_INTEGRATED_VERS)
16590Sstevel@tonic-gate 		return;
16600Sstevel@tonic-gate 
16610Sstevel@tonic-gate 	/* Enable performance counter overflow interrupt */
16620Sstevel@tonic-gate 
16630Sstevel@tonic-gate 	if ((x86_feature & X86_MSR) != X86_MSR)
16640Sstevel@tonic-gate 		apic_enable_cpcovf_intr = 0;
16650Sstevel@tonic-gate 	if (apic_enable_cpcovf_intr) {
16660Sstevel@tonic-gate 		if (apic_cpcovf_vect == 0) {
16670Sstevel@tonic-gate 			int ipl = APIC_PCINT_IPL;
16680Sstevel@tonic-gate 			int irq = apic_get_ipivect(ipl, -1);
16690Sstevel@tonic-gate 
16700Sstevel@tonic-gate 			ASSERT(irq != -1);
16710Sstevel@tonic-gate 			apic_cpcovf_vect = apic_irq_table[irq]->airq_vector;
16720Sstevel@tonic-gate 			ASSERT(apic_cpcovf_vect);
16730Sstevel@tonic-gate 			(void) add_avintr(NULL, ipl,
16740Sstevel@tonic-gate 			    (avfunc)kcpc_hw_overflow_intr,
1675916Sschwartz 			    "apic pcint", irq, NULL, NULL, NULL, NULL);
16760Sstevel@tonic-gate 			kcpc_hw_overflow_intr_installed = 1;
16770Sstevel@tonic-gate 			kcpc_hw_enable_cpc_intr = apic_cpcovf_mask_clear;
16780Sstevel@tonic-gate 		}
16790Sstevel@tonic-gate 		apicadr[APIC_PCINT_VECT] = apic_cpcovf_vect;
16800Sstevel@tonic-gate 	}
16810Sstevel@tonic-gate 
16820Sstevel@tonic-gate 	/* Enable error interrupt */
16830Sstevel@tonic-gate 
16840Sstevel@tonic-gate 	if (apic_enable_error_intr) {
16850Sstevel@tonic-gate 		if (apic_errvect == 0) {
16860Sstevel@tonic-gate 			int ipl = 0xf;	/* get highest priority intr */
16870Sstevel@tonic-gate 			int irq = apic_get_ipivect(ipl, -1);
16880Sstevel@tonic-gate 
16890Sstevel@tonic-gate 			ASSERT(irq != -1);
16900Sstevel@tonic-gate 			apic_errvect = apic_irq_table[irq]->airq_vector;
16910Sstevel@tonic-gate 			ASSERT(apic_errvect);
16920Sstevel@tonic-gate 			/*
16930Sstevel@tonic-gate 			 * Not PSMI compliant, but we are going to merge
16940Sstevel@tonic-gate 			 * with ON anyway
16950Sstevel@tonic-gate 			 */
16960Sstevel@tonic-gate 			(void) add_avintr((void *)NULL, ipl,
16970Sstevel@tonic-gate 			    (avfunc)apic_error_intr, "apic error intr",
1698916Sschwartz 			    irq, NULL, NULL, NULL, NULL);
16990Sstevel@tonic-gate 		}
17000Sstevel@tonic-gate 		apicadr[APIC_ERR_VECT] = apic_errvect;
17010Sstevel@tonic-gate 		apicadr[APIC_ERROR_STATUS] = 0;
17020Sstevel@tonic-gate 		apicadr[APIC_ERROR_STATUS] = 0;
17030Sstevel@tonic-gate 	}
17040Sstevel@tonic-gate }
17050Sstevel@tonic-gate 
17060Sstevel@tonic-gate static void
17070Sstevel@tonic-gate apic_disable_local_apic()
17080Sstevel@tonic-gate {
17090Sstevel@tonic-gate 	apicadr[APIC_TASK_REG] = APIC_MASK_ALL;
17100Sstevel@tonic-gate 	apicadr[APIC_LOCAL_TIMER] = AV_MASK;
17110Sstevel@tonic-gate 	apicadr[APIC_INT_VECT0] = AV_MASK;	/* local intr reg 0 */
17120Sstevel@tonic-gate 	apicadr[APIC_INT_VECT1] = AV_MASK;	/* disable NMI */
17130Sstevel@tonic-gate 	apicadr[APIC_ERR_VECT] = AV_MASK;	/* and error interrupt */
17140Sstevel@tonic-gate 	apicadr[APIC_PCINT_VECT] = AV_MASK;	/* and perf counter intr */
17150Sstevel@tonic-gate 	apicadr[APIC_SPUR_INT_REG] = APIC_SPUR_INTR;
17160Sstevel@tonic-gate }
17170Sstevel@tonic-gate 
17180Sstevel@tonic-gate static void
17190Sstevel@tonic-gate apic_picinit(void)
17200Sstevel@tonic-gate {
17210Sstevel@tonic-gate 	int i, j;
17220Sstevel@tonic-gate 	uint_t isr;
17230Sstevel@tonic-gate 	volatile int32_t *ioapic;
17240Sstevel@tonic-gate 	apic_irq_t	*irqptr;
1725347Smyers 	struct intrspec ispec;
17260Sstevel@tonic-gate 
17270Sstevel@tonic-gate 	/*
17280Sstevel@tonic-gate 	 * On UniSys Model 6520, the BIOS leaves vector 0x20 isr
17290Sstevel@tonic-gate 	 * bit on without clearing it with EOI.  Since softint
17300Sstevel@tonic-gate 	 * uses vector 0x20 to interrupt itself, so softint will
17310Sstevel@tonic-gate 	 * not work on this machine.  In order to fix this problem
17320Sstevel@tonic-gate 	 * a check is made to verify all the isr bits are clear.
17330Sstevel@tonic-gate 	 * If not, EOIs are issued to clear the bits.
17340Sstevel@tonic-gate 	 */
17350Sstevel@tonic-gate 	for (i = 7; i >= 1; i--) {
17360Sstevel@tonic-gate 		if ((isr = apicadr[APIC_ISR_REG + (i * 4)]) != 0)
17370Sstevel@tonic-gate 			for (j = 0; ((j < 32) && (isr != 0)); j++)
17380Sstevel@tonic-gate 				if (isr & (1 << j)) {
17390Sstevel@tonic-gate 					apicadr[APIC_EOI_REG] = 0;
17400Sstevel@tonic-gate 					isr &= ~(1 << j);
17410Sstevel@tonic-gate 					apic_error |= APIC_ERR_BOOT_EOI;
17420Sstevel@tonic-gate 				}
17430Sstevel@tonic-gate 	}
17440Sstevel@tonic-gate 
17450Sstevel@tonic-gate 	/* set a flag so we know we have run apic_picinit() */
17460Sstevel@tonic-gate 	apic_flag = 1;
17470Sstevel@tonic-gate 	LOCK_INIT_CLEAR(&apic_gethrtime_lock);
17480Sstevel@tonic-gate 	LOCK_INIT_CLEAR(&apic_ioapic_lock);
17490Sstevel@tonic-gate 	LOCK_INIT_CLEAR(&apic_revector_lock);
17500Sstevel@tonic-gate 	LOCK_INIT_CLEAR(&apic_ioapic_reprogram_lock);
17510Sstevel@tonic-gate 	LOCK_INIT_CLEAR(&apic_error_lock);
17520Sstevel@tonic-gate 
17530Sstevel@tonic-gate 	picsetup();	 /* initialise the 8259 */
17540Sstevel@tonic-gate 
17550Sstevel@tonic-gate 	/* add nmi handler - least priority nmi handler */
17560Sstevel@tonic-gate 	LOCK_INIT_CLEAR(&apic_nmi_lock);
17570Sstevel@tonic-gate 
17580Sstevel@tonic-gate 	if (!psm_add_nmintr(0, (avfunc) apic_nmi_intr,
17590Sstevel@tonic-gate 	    "pcplusmp NMI handler", (caddr_t)NULL))
17600Sstevel@tonic-gate 		cmn_err(CE_WARN, "pcplusmp: Unable to add nmi handler");
17610Sstevel@tonic-gate 
17620Sstevel@tonic-gate 	apic_init_intr();
17630Sstevel@tonic-gate 
17640Sstevel@tonic-gate 	/* enable apic mode if imcr present */
17650Sstevel@tonic-gate 	if (apic_imcrp) {
17660Sstevel@tonic-gate 		outb(APIC_IMCR_P1, (uchar_t)APIC_IMCR_SELECT);
17670Sstevel@tonic-gate 		outb(APIC_IMCR_P2, (uchar_t)APIC_IMCR_APIC);
17680Sstevel@tonic-gate 	}
17690Sstevel@tonic-gate 
17700Sstevel@tonic-gate 	/* mask interrupt vectors					*/
17710Sstevel@tonic-gate 	for (j = 0; j < apic_io_max; j++) {
17720Sstevel@tonic-gate 		int intin_max;
17730Sstevel@tonic-gate 		ioapic = apicioadr[j];
17740Sstevel@tonic-gate 		ioapic[APIC_IO_REG] = APIC_VERS_CMD;
17750Sstevel@tonic-gate 		/* Bits 23-16 define the maximum redirection entries */
17760Sstevel@tonic-gate 		intin_max = (ioapic[APIC_IO_DATA] >> 16) & 0xff;
17770Sstevel@tonic-gate 		for (i = 0; i < intin_max; i++) {
17780Sstevel@tonic-gate 			ioapic[APIC_IO_REG] = APIC_RDT_CMD + 2 * i;
17790Sstevel@tonic-gate 			ioapic[APIC_IO_DATA] = AV_MASK;
17800Sstevel@tonic-gate 		}
17810Sstevel@tonic-gate 	}
17820Sstevel@tonic-gate 
17830Sstevel@tonic-gate 	/*
17840Sstevel@tonic-gate 	 * Hack alert: deal with ACPI SCI interrupt chicken/egg here
17850Sstevel@tonic-gate 	 */
1786347Smyers 	if (apic_sci_vect > 0) {
17870Sstevel@tonic-gate 		/*
17880Sstevel@tonic-gate 		 * acpica has already done add_avintr(); we just
17890Sstevel@tonic-gate 		 * to finish the job by mimicing translate_irq()
1790347Smyers 		 *
1791347Smyers 		 * Fake up an intrspec and setup the tables
17920Sstevel@tonic-gate 		 */
1793347Smyers 		ispec.intrspec_vec = apic_sci_vect;
1794347Smyers 		ispec.intrspec_pri = SCI_IPL;
1795347Smyers 
1796347Smyers 		if (apic_setup_irq_table(NULL, apic_sci_vect, NULL,
1797347Smyers 		    &ispec, &apic_sci_flags, DDI_INTR_TYPE_FIXED) < 0) {
17980Sstevel@tonic-gate 			cmn_err(CE_WARN, "!apic: SCI setup failed");
17990Sstevel@tonic-gate 			return;
18000Sstevel@tonic-gate 		}
18010Sstevel@tonic-gate 		irqptr = apic_irq_table[apic_sci_vect];
18020Sstevel@tonic-gate 
18030Sstevel@tonic-gate 		/* Program I/O APIC */
18040Sstevel@tonic-gate 		(void) apic_setup_io_intr(irqptr, apic_sci_vect);
1805916Sschwartz 
1806916Sschwartz 		irqptr->airq_share++;
18070Sstevel@tonic-gate 	}
18080Sstevel@tonic-gate }
18090Sstevel@tonic-gate 
18100Sstevel@tonic-gate 
18110Sstevel@tonic-gate static void
18120Sstevel@tonic-gate apic_cpu_start(processorid_t cpun, caddr_t rm_code)
18130Sstevel@tonic-gate {
18140Sstevel@tonic-gate 	int		loop_count;
18150Sstevel@tonic-gate 	uint32_t	vector;
18160Sstevel@tonic-gate 	uint_t		cpu_id, iflag;
18170Sstevel@tonic-gate 
18180Sstevel@tonic-gate 	cpu_id = apic_cpus[cpun].aci_local_id;
18190Sstevel@tonic-gate 
18200Sstevel@tonic-gate 	apic_cmos_ssb_set = 1;
18210Sstevel@tonic-gate 
18220Sstevel@tonic-gate 	/*
18230Sstevel@tonic-gate 	 * Interrupts on BSP cpu will be disabled during these startup
18240Sstevel@tonic-gate 	 * steps in order to avoid unwanted side effects from
18250Sstevel@tonic-gate 	 * executing interrupt handlers on a problematic BIOS.
18260Sstevel@tonic-gate 	 */
18270Sstevel@tonic-gate 
18280Sstevel@tonic-gate 	iflag = intr_clear();
18290Sstevel@tonic-gate 	outb(CMOS_ADDR, SSB);
18300Sstevel@tonic-gate 	outb(CMOS_DATA, BIOS_SHUTDOWN);
18310Sstevel@tonic-gate 
18320Sstevel@tonic-gate 	while (get_apic_cmd1() & AV_PENDING)
18330Sstevel@tonic-gate 		apic_ret();
18340Sstevel@tonic-gate 
18350Sstevel@tonic-gate 	/* for integrated - make sure there is one INIT IPI in buffer */
18360Sstevel@tonic-gate 	/* for external - it will wake up the cpu */
18370Sstevel@tonic-gate 	apicadr[APIC_INT_CMD2] = cpu_id << APIC_ICR_ID_BIT_OFFSET;
18380Sstevel@tonic-gate 	apicadr[APIC_INT_CMD1] = AV_ASSERT | AV_RESET;
18390Sstevel@tonic-gate 
18400Sstevel@tonic-gate 	/* If only 1 CPU is installed, PENDING bit will not go low */
18410Sstevel@tonic-gate 	for (loop_count = 0x1000; loop_count; loop_count--)
18420Sstevel@tonic-gate 		if (get_apic_cmd1() & AV_PENDING)
18430Sstevel@tonic-gate 			apic_ret();
18440Sstevel@tonic-gate 		else
18450Sstevel@tonic-gate 			break;
18460Sstevel@tonic-gate 
18470Sstevel@tonic-gate 	apicadr[APIC_INT_CMD2] = cpu_id << APIC_ICR_ID_BIT_OFFSET;
18480Sstevel@tonic-gate 	apicadr[APIC_INT_CMD1] = AV_DEASSERT | AV_RESET;
18490Sstevel@tonic-gate 
18500Sstevel@tonic-gate 	drv_usecwait(20000);		/* 20 milli sec */
18510Sstevel@tonic-gate 
18520Sstevel@tonic-gate 	if (apic_cpus[cpun].aci_local_ver >= APIC_INTEGRATED_VERS) {
18530Sstevel@tonic-gate 		/* integrated apic */
18540Sstevel@tonic-gate 
18550Sstevel@tonic-gate 		rm_code = (caddr_t)(uintptr_t)rm_platter_pa;
18560Sstevel@tonic-gate 		vector = (rm_platter_pa >> MMU_PAGESHIFT) &
18570Sstevel@tonic-gate 		    (APIC_VECTOR_MASK | APIC_IPL_MASK);
18580Sstevel@tonic-gate 
18590Sstevel@tonic-gate 		/* to offset the INIT IPI queue up in the buffer */
18600Sstevel@tonic-gate 		apicadr[APIC_INT_CMD2] = cpu_id << APIC_ICR_ID_BIT_OFFSET;
18610Sstevel@tonic-gate 		apicadr[APIC_INT_CMD1] = vector | AV_STARTUP;
18620Sstevel@tonic-gate 
18630Sstevel@tonic-gate 		drv_usecwait(200);		/* 20 micro sec */
18640Sstevel@tonic-gate 
18650Sstevel@tonic-gate 		apicadr[APIC_INT_CMD2] = cpu_id << APIC_ICR_ID_BIT_OFFSET;
18660Sstevel@tonic-gate 		apicadr[APIC_INT_CMD1] = vector | AV_STARTUP;
18670Sstevel@tonic-gate 
18680Sstevel@tonic-gate 		drv_usecwait(200);		/* 20 micro sec */
18690Sstevel@tonic-gate 	}
18700Sstevel@tonic-gate 	intr_restore(iflag);
18710Sstevel@tonic-gate }
18720Sstevel@tonic-gate 
18730Sstevel@tonic-gate 
18740Sstevel@tonic-gate #ifdef	DEBUG
18750Sstevel@tonic-gate int	apic_break_on_cpu = 9;
18760Sstevel@tonic-gate int	apic_stretch_interrupts = 0;
18770Sstevel@tonic-gate int	apic_stretch_ISR = 1 << 3;	/* IPL of 3 matches nothing now */
18780Sstevel@tonic-gate 
18790Sstevel@tonic-gate void
18800Sstevel@tonic-gate apic_break()
18810Sstevel@tonic-gate {
18820Sstevel@tonic-gate }
18830Sstevel@tonic-gate #endif /* DEBUG */
18840Sstevel@tonic-gate 
18850Sstevel@tonic-gate /*
18860Sstevel@tonic-gate  * platform_intr_enter
18870Sstevel@tonic-gate  *
18880Sstevel@tonic-gate  *	Called at the beginning of the interrupt service routine to
18890Sstevel@tonic-gate  *	mask all level equal to and below the interrupt priority
18900Sstevel@tonic-gate  *	of the interrupting vector.  An EOI should be given to
18910Sstevel@tonic-gate  *	the interrupt controller to enable other HW interrupts.
18920Sstevel@tonic-gate  *
18930Sstevel@tonic-gate  *	Return -1 for spurious interrupts
18940Sstevel@tonic-gate  *
18950Sstevel@tonic-gate  */
18960Sstevel@tonic-gate /*ARGSUSED*/
18970Sstevel@tonic-gate static int
18980Sstevel@tonic-gate apic_intr_enter(int ipl, int *vectorp)
18990Sstevel@tonic-gate {
19000Sstevel@tonic-gate 	uchar_t vector;
19010Sstevel@tonic-gate 	int nipl;
19020Sstevel@tonic-gate 	int irq, iflag;
19030Sstevel@tonic-gate 	apic_cpus_info_t *cpu_infop;
19040Sstevel@tonic-gate 
19050Sstevel@tonic-gate 	/*
19060Sstevel@tonic-gate 	 * The real vector programmed in APIC is *vectorp + 0x20
19070Sstevel@tonic-gate 	 * But, cmnint code subtracts 0x20 before pushing it.
19080Sstevel@tonic-gate 	 * Hence APIC_BASE_VECT is 0x20.
19090Sstevel@tonic-gate 	 */
19100Sstevel@tonic-gate 
19110Sstevel@tonic-gate 	vector = (uchar_t)*vectorp;
19120Sstevel@tonic-gate 
19130Sstevel@tonic-gate 	/* if interrupted by the clock, increment apic_nsec_since_boot */
19140Sstevel@tonic-gate 	if (vector == apic_clkvect) {
19150Sstevel@tonic-gate 		if (!apic_oneshot) {
19160Sstevel@tonic-gate 			/* NOTE: this is not MT aware */
19170Sstevel@tonic-gate 			apic_hrtime_stamp++;
19180Sstevel@tonic-gate 			apic_nsec_since_boot += apic_nsec_per_intr;
19190Sstevel@tonic-gate 			apic_hrtime_stamp++;
19200Sstevel@tonic-gate 			last_count_read = apic_hertz_count;
19210Sstevel@tonic-gate 			apic_redistribute_compute();
19220Sstevel@tonic-gate 		}
19230Sstevel@tonic-gate 
19240Sstevel@tonic-gate 		/* We will avoid all the book keeping overhead for clock */
19250Sstevel@tonic-gate 		nipl = apic_vectortoipl[vector >> APIC_IPL_SHIFT];
19260Sstevel@tonic-gate #if defined(__amd64)
19270Sstevel@tonic-gate 		setcr8((ulong_t)apic_cr8pri[nipl]);
19280Sstevel@tonic-gate #else
19290Sstevel@tonic-gate 		apicadr[APIC_TASK_REG] = apic_ipltopri[nipl];
19300Sstevel@tonic-gate #endif
19310Sstevel@tonic-gate 		*vectorp = apic_vector_to_irq[vector + APIC_BASE_VECT];
19320Sstevel@tonic-gate 		apicadr[APIC_EOI_REG] = 0;
19330Sstevel@tonic-gate 		return (nipl);
19340Sstevel@tonic-gate 	}
19350Sstevel@tonic-gate 
19360Sstevel@tonic-gate 	cpu_infop = &apic_cpus[psm_get_cpu_id()];
19370Sstevel@tonic-gate 
19380Sstevel@tonic-gate 	if (vector == (APIC_SPUR_INTR - APIC_BASE_VECT)) {
19390Sstevel@tonic-gate 		cpu_infop->aci_spur_cnt++;
19400Sstevel@tonic-gate 		return (APIC_INT_SPURIOUS);
19410Sstevel@tonic-gate 	}
19420Sstevel@tonic-gate 
19430Sstevel@tonic-gate 	/* Check if the vector we got is really what we need */
19440Sstevel@tonic-gate 	if (apic_revector_pending) {
19450Sstevel@tonic-gate 		/*
19460Sstevel@tonic-gate 		 * Disable interrupts for the duration of
19470Sstevel@tonic-gate 		 * the vector translation to prevent a self-race for
19480Sstevel@tonic-gate 		 * the apic_revector_lock.  This cannot be done
19490Sstevel@tonic-gate 		 * in apic_xlate_vector because it is recursive and
19500Sstevel@tonic-gate 		 * we want the vector translation to be atomic with
19510Sstevel@tonic-gate 		 * respect to other (higher-priority) interrupts.
19520Sstevel@tonic-gate 		 */
19530Sstevel@tonic-gate 		iflag = intr_clear();
19540Sstevel@tonic-gate 		vector = apic_xlate_vector(vector + APIC_BASE_VECT) -
19550Sstevel@tonic-gate 		    APIC_BASE_VECT;
19560Sstevel@tonic-gate 		intr_restore(iflag);
19570Sstevel@tonic-gate 	}
19580Sstevel@tonic-gate 
19590Sstevel@tonic-gate 	nipl = apic_vectortoipl[vector >> APIC_IPL_SHIFT];
19600Sstevel@tonic-gate 	*vectorp = irq = apic_vector_to_irq[vector + APIC_BASE_VECT];
19610Sstevel@tonic-gate 
19620Sstevel@tonic-gate #if defined(__amd64)
19630Sstevel@tonic-gate 	setcr8((ulong_t)apic_cr8pri[nipl]);
19640Sstevel@tonic-gate #else
19650Sstevel@tonic-gate 	apicadr[APIC_TASK_REG] = apic_ipltopri[nipl];
19660Sstevel@tonic-gate #endif
19670Sstevel@tonic-gate 
19680Sstevel@tonic-gate 	cpu_infop->aci_current[nipl] = (uchar_t)irq;
19690Sstevel@tonic-gate 	cpu_infop->aci_curipl = (uchar_t)nipl;
19700Sstevel@tonic-gate 	cpu_infop->aci_ISR_in_progress |= 1 << nipl;
19710Sstevel@tonic-gate 
19720Sstevel@tonic-gate 	/*
19730Sstevel@tonic-gate 	 * apic_level_intr could have been assimilated into the irq struct.
19740Sstevel@tonic-gate 	 * but, having it as a character array is more efficient in terms of
19750Sstevel@tonic-gate 	 * cache usage. So, we leave it as is.
19760Sstevel@tonic-gate 	 */
19770Sstevel@tonic-gate 	if (!apic_level_intr[irq])
19780Sstevel@tonic-gate 		apicadr[APIC_EOI_REG] = 0;
19790Sstevel@tonic-gate 
19800Sstevel@tonic-gate #ifdef	DEBUG
19810Sstevel@tonic-gate 	APIC_DEBUG_BUF_PUT(vector);
19820Sstevel@tonic-gate 	APIC_DEBUG_BUF_PUT(irq);
19830Sstevel@tonic-gate 	APIC_DEBUG_BUF_PUT(nipl);
19840Sstevel@tonic-gate 	APIC_DEBUG_BUF_PUT(psm_get_cpu_id());
19850Sstevel@tonic-gate 	if ((apic_stretch_interrupts) && (apic_stretch_ISR & (1 << nipl)))
19860Sstevel@tonic-gate 		drv_usecwait(apic_stretch_interrupts);
19870Sstevel@tonic-gate 
19880Sstevel@tonic-gate 	if (apic_break_on_cpu == psm_get_cpu_id())
19890Sstevel@tonic-gate 		apic_break();
19900Sstevel@tonic-gate #endif /* DEBUG */
19910Sstevel@tonic-gate 	return (nipl);
19920Sstevel@tonic-gate }
19930Sstevel@tonic-gate 
19940Sstevel@tonic-gate static void
19950Sstevel@tonic-gate apic_intr_exit(int prev_ipl, int irq)
19960Sstevel@tonic-gate {
19970Sstevel@tonic-gate 	apic_cpus_info_t *cpu_infop;
19980Sstevel@tonic-gate 
19990Sstevel@tonic-gate #if defined(__amd64)
20000Sstevel@tonic-gate 	setcr8((ulong_t)apic_cr8pri[prev_ipl]);
20010Sstevel@tonic-gate #else
20020Sstevel@tonic-gate 	apicadr[APIC_TASK_REG] = apic_ipltopri[prev_ipl];
20030Sstevel@tonic-gate #endif
20040Sstevel@tonic-gate 
20050Sstevel@tonic-gate 	cpu_infop = &apic_cpus[psm_get_cpu_id()];
20060Sstevel@tonic-gate 	if (apic_level_intr[irq])
20070Sstevel@tonic-gate 		apicadr[APIC_EOI_REG] = 0;
20080Sstevel@tonic-gate 
20090Sstevel@tonic-gate 	cpu_infop->aci_curipl = (uchar_t)prev_ipl;
20100Sstevel@tonic-gate 	/* ISR above current pri could not be in progress */
20110Sstevel@tonic-gate 	cpu_infop->aci_ISR_in_progress &= (2 << prev_ipl) - 1;
20120Sstevel@tonic-gate }
20130Sstevel@tonic-gate 
20140Sstevel@tonic-gate /*
20150Sstevel@tonic-gate  * Mask all interrupts below or equal to the given IPL
20160Sstevel@tonic-gate  */
20170Sstevel@tonic-gate static void
20180Sstevel@tonic-gate apic_setspl(int ipl)
20190Sstevel@tonic-gate {
20200Sstevel@tonic-gate 
20210Sstevel@tonic-gate #if defined(__amd64)
20220Sstevel@tonic-gate 	setcr8((ulong_t)apic_cr8pri[ipl]);
20230Sstevel@tonic-gate #else
20240Sstevel@tonic-gate 	apicadr[APIC_TASK_REG] = apic_ipltopri[ipl];
20250Sstevel@tonic-gate #endif
20260Sstevel@tonic-gate 
20270Sstevel@tonic-gate 	/* interrupts at ipl above this cannot be in progress */
20280Sstevel@tonic-gate 	apic_cpus[psm_get_cpu_id()].aci_ISR_in_progress &= (2 << ipl) - 1;
20290Sstevel@tonic-gate 	/*
20300Sstevel@tonic-gate 	 * this is a patch fix for the ALR QSMP P5 machine, so that interrupts
20310Sstevel@tonic-gate 	 * have enough time to come in before the priority is raised again
20320Sstevel@tonic-gate 	 * during the idle() loop.
20330Sstevel@tonic-gate 	 */
20340Sstevel@tonic-gate 	if (apic_setspl_delay)
20350Sstevel@tonic-gate 		(void) get_apic_pri();
20360Sstevel@tonic-gate }
20370Sstevel@tonic-gate 
20380Sstevel@tonic-gate /*
20390Sstevel@tonic-gate  * trigger a software interrupt at the given IPL
20400Sstevel@tonic-gate  */
20410Sstevel@tonic-gate static void
20420Sstevel@tonic-gate apic_set_softintr(int ipl)
20430Sstevel@tonic-gate {
20440Sstevel@tonic-gate 	int vector;
20450Sstevel@tonic-gate 	uint_t flag;
20460Sstevel@tonic-gate 
20470Sstevel@tonic-gate 	vector = apic_resv_vector[ipl];
20480Sstevel@tonic-gate 
20490Sstevel@tonic-gate 	flag = intr_clear();
20500Sstevel@tonic-gate 
20510Sstevel@tonic-gate 	while (get_apic_cmd1() & AV_PENDING)
20520Sstevel@tonic-gate 		apic_ret();
20530Sstevel@tonic-gate 
20540Sstevel@tonic-gate 	/* generate interrupt at vector on itself only */
20550Sstevel@tonic-gate 	apicadr[APIC_INT_CMD1] = AV_SH_SELF | vector;
20560Sstevel@tonic-gate 
20570Sstevel@tonic-gate 	intr_restore(flag);
20580Sstevel@tonic-gate }
20590Sstevel@tonic-gate 
20600Sstevel@tonic-gate /*
20610Sstevel@tonic-gate  * generates an interprocessor interrupt to another CPU
20620Sstevel@tonic-gate  */
20630Sstevel@tonic-gate static void
20640Sstevel@tonic-gate apic_send_ipi(int cpun, int ipl)
20650Sstevel@tonic-gate {
20660Sstevel@tonic-gate 	int vector;
20670Sstevel@tonic-gate 	uint_t flag;
20680Sstevel@tonic-gate 
20690Sstevel@tonic-gate 	vector = apic_resv_vector[ipl];
20700Sstevel@tonic-gate 
20710Sstevel@tonic-gate 	flag = intr_clear();
20720Sstevel@tonic-gate 
20730Sstevel@tonic-gate 	while (get_apic_cmd1() & AV_PENDING)
20740Sstevel@tonic-gate 		apic_ret();
20750Sstevel@tonic-gate 
20760Sstevel@tonic-gate 	apicadr[APIC_INT_CMD2] =
20770Sstevel@tonic-gate 	    apic_cpus[cpun].aci_local_id << APIC_ICR_ID_BIT_OFFSET;
20780Sstevel@tonic-gate 	apicadr[APIC_INT_CMD1] = vector;
20790Sstevel@tonic-gate 
20800Sstevel@tonic-gate 	intr_restore(flag);
20810Sstevel@tonic-gate }
20820Sstevel@tonic-gate 
20830Sstevel@tonic-gate 
20840Sstevel@tonic-gate /*ARGSUSED*/
20850Sstevel@tonic-gate static void
20860Sstevel@tonic-gate apic_set_idlecpu(processorid_t cpun)
20870Sstevel@tonic-gate {
20880Sstevel@tonic-gate }
20890Sstevel@tonic-gate 
20900Sstevel@tonic-gate /*ARGSUSED*/
20910Sstevel@tonic-gate static void
20920Sstevel@tonic-gate apic_unset_idlecpu(processorid_t cpun)
20930Sstevel@tonic-gate {
20940Sstevel@tonic-gate }
20950Sstevel@tonic-gate 
20960Sstevel@tonic-gate 
20970Sstevel@tonic-gate static void
20980Sstevel@tonic-gate apic_ret()
20990Sstevel@tonic-gate {
21000Sstevel@tonic-gate }
21010Sstevel@tonic-gate 
21020Sstevel@tonic-gate static int
21030Sstevel@tonic-gate get_apic_cmd1()
21040Sstevel@tonic-gate {
21050Sstevel@tonic-gate 	return (apicadr[APIC_INT_CMD1]);
21060Sstevel@tonic-gate }
21070Sstevel@tonic-gate 
21080Sstevel@tonic-gate static int
21090Sstevel@tonic-gate get_apic_pri()
21100Sstevel@tonic-gate {
21110Sstevel@tonic-gate #if defined(__amd64)
21120Sstevel@tonic-gate 	return ((int)getcr8());
21130Sstevel@tonic-gate #else
21140Sstevel@tonic-gate 	return (apicadr[APIC_TASK_REG]);
21150Sstevel@tonic-gate #endif
21160Sstevel@tonic-gate }
21170Sstevel@tonic-gate 
21180Sstevel@tonic-gate /*
21190Sstevel@tonic-gate  * If apic_coarse_time == 1, then apic_gettime() is used instead of
21200Sstevel@tonic-gate  * apic_gethrtime().  This is used for performance instead of accuracy.
21210Sstevel@tonic-gate  */
21220Sstevel@tonic-gate 
21230Sstevel@tonic-gate static hrtime_t
21240Sstevel@tonic-gate apic_gettime()
21250Sstevel@tonic-gate {
21260Sstevel@tonic-gate 	int old_hrtime_stamp;
21270Sstevel@tonic-gate 	hrtime_t temp;
21280Sstevel@tonic-gate 
21290Sstevel@tonic-gate 	/*
21300Sstevel@tonic-gate 	 * In one-shot mode, we do not keep time, so if anyone
21310Sstevel@tonic-gate 	 * calls psm_gettime() directly, we vector over to
21320Sstevel@tonic-gate 	 * gethrtime().
21330Sstevel@tonic-gate 	 * one-shot mode MUST NOT be enabled if this psm is the source of
21340Sstevel@tonic-gate 	 * hrtime.
21350Sstevel@tonic-gate 	 */
21360Sstevel@tonic-gate 
21370Sstevel@tonic-gate 	if (apic_oneshot)
21380Sstevel@tonic-gate 		return (gethrtime());
21390Sstevel@tonic-gate 
21400Sstevel@tonic-gate 
21410Sstevel@tonic-gate gettime_again:
21420Sstevel@tonic-gate 	while ((old_hrtime_stamp = apic_hrtime_stamp) & 1)
21430Sstevel@tonic-gate 		apic_ret();
21440Sstevel@tonic-gate 
21450Sstevel@tonic-gate 	temp = apic_nsec_since_boot;
21460Sstevel@tonic-gate 
21470Sstevel@tonic-gate 	if (apic_hrtime_stamp != old_hrtime_stamp) {	/* got an interrupt */
21480Sstevel@tonic-gate 		goto gettime_again;
21490Sstevel@tonic-gate 	}
21500Sstevel@tonic-gate 	return (temp);
21510Sstevel@tonic-gate }
21520Sstevel@tonic-gate 
21530Sstevel@tonic-gate /*
21540Sstevel@tonic-gate  * Here we return the number of nanoseconds since booting.  Note every
21550Sstevel@tonic-gate  * clock interrupt increments apic_nsec_since_boot by the appropriate
21560Sstevel@tonic-gate  * amount.
21570Sstevel@tonic-gate  */
21580Sstevel@tonic-gate static hrtime_t
21590Sstevel@tonic-gate apic_gethrtime()
21600Sstevel@tonic-gate {
21610Sstevel@tonic-gate 	int curr_timeval, countval, elapsed_ticks, oflags;
21620Sstevel@tonic-gate 	int old_hrtime_stamp, status;
21630Sstevel@tonic-gate 	hrtime_t temp;
21640Sstevel@tonic-gate 	uchar_t	cpun;
21650Sstevel@tonic-gate 
21660Sstevel@tonic-gate 
21670Sstevel@tonic-gate 	/*
21680Sstevel@tonic-gate 	 * In one-shot mode, we do not keep time, so if anyone
21690Sstevel@tonic-gate 	 * calls psm_gethrtime() directly, we vector over to
21700Sstevel@tonic-gate 	 * gethrtime().
21710Sstevel@tonic-gate 	 * one-shot mode MUST NOT be enabled if this psm is the source of
21720Sstevel@tonic-gate 	 * hrtime.
21730Sstevel@tonic-gate 	 */
21740Sstevel@tonic-gate 
21750Sstevel@tonic-gate 	if (apic_oneshot)
21760Sstevel@tonic-gate 		return (gethrtime());
21770Sstevel@tonic-gate 
21780Sstevel@tonic-gate 	oflags = intr_clear();	/* prevent migration */
21790Sstevel@tonic-gate 
21800Sstevel@tonic-gate 	cpun = (uchar_t)((uint_t)apicadr[APIC_LID_REG] >> APIC_ID_BIT_OFFSET);
21810Sstevel@tonic-gate 
21820Sstevel@tonic-gate 	lock_set(&apic_gethrtime_lock);
21830Sstevel@tonic-gate 
21840Sstevel@tonic-gate gethrtime_again:
21850Sstevel@tonic-gate 	while ((old_hrtime_stamp = apic_hrtime_stamp) & 1)
21860Sstevel@tonic-gate 		apic_ret();
21870Sstevel@tonic-gate 
21880Sstevel@tonic-gate 	/*
21890Sstevel@tonic-gate 	 * Check to see which CPU we are on.  Note the time is kept on
21900Sstevel@tonic-gate 	 * the local APIC of CPU 0.  If on CPU 0, simply read the current
21910Sstevel@tonic-gate 	 * counter.  If on another CPU, issue a remote read command to CPU 0.
21920Sstevel@tonic-gate 	 */
21930Sstevel@tonic-gate 	if (cpun == apic_cpus[0].aci_local_id) {
21940Sstevel@tonic-gate 		countval = apicadr[APIC_CURR_COUNT];
21950Sstevel@tonic-gate 	} else {
21960Sstevel@tonic-gate 		while (get_apic_cmd1() & AV_PENDING)
21970Sstevel@tonic-gate 			apic_ret();
21980Sstevel@tonic-gate 
21990Sstevel@tonic-gate 		apicadr[APIC_INT_CMD2] =
22000Sstevel@tonic-gate 		    apic_cpus[0].aci_local_id << APIC_ICR_ID_BIT_OFFSET;
22010Sstevel@tonic-gate 		apicadr[APIC_INT_CMD1] = APIC_CURR_ADD|AV_REMOTE;
22020Sstevel@tonic-gate 
22030Sstevel@tonic-gate 		while ((status = get_apic_cmd1()) & AV_READ_PENDING)
22040Sstevel@tonic-gate 			apic_ret();
22050Sstevel@tonic-gate 
22060Sstevel@tonic-gate 		if (status & AV_REMOTE_STATUS)	/* 1 = valid */
22070Sstevel@tonic-gate 			countval = apicadr[APIC_REMOTE_READ];
22080Sstevel@tonic-gate 		else {	/* 0 = invalid */
22090Sstevel@tonic-gate 			apic_remote_hrterr++;
22100Sstevel@tonic-gate 			/*
22110Sstevel@tonic-gate 			 * return last hrtime right now, will need more
22120Sstevel@tonic-gate 			 * testing if change to retry
22130Sstevel@tonic-gate 			 */
22140Sstevel@tonic-gate 			temp = apic_last_hrtime;
22150Sstevel@tonic-gate 
22160Sstevel@tonic-gate 			lock_clear(&apic_gethrtime_lock);
22170Sstevel@tonic-gate 
22180Sstevel@tonic-gate 			intr_restore(oflags);
22190Sstevel@tonic-gate 
22200Sstevel@tonic-gate 			return (temp);
22210Sstevel@tonic-gate 		}
22220Sstevel@tonic-gate 	}
22230Sstevel@tonic-gate 	if (countval > last_count_read)
22240Sstevel@tonic-gate 		countval = 0;
22250Sstevel@tonic-gate 	else
22260Sstevel@tonic-gate 		last_count_read = countval;
22270Sstevel@tonic-gate 
22280Sstevel@tonic-gate 	elapsed_ticks = apic_hertz_count - countval;
22290Sstevel@tonic-gate 
22300Sstevel@tonic-gate 	curr_timeval = elapsed_ticks * apic_nsec_per_tick;
22310Sstevel@tonic-gate 	temp = apic_nsec_since_boot + curr_timeval;
22320Sstevel@tonic-gate 
22330Sstevel@tonic-gate 	if (apic_hrtime_stamp != old_hrtime_stamp) {	/* got an interrupt */
22340Sstevel@tonic-gate 		/* we might have clobbered last_count_read. Restore it */
22350Sstevel@tonic-gate 		last_count_read = apic_hertz_count;
22360Sstevel@tonic-gate 		goto gethrtime_again;
22370Sstevel@tonic-gate 	}
22380Sstevel@tonic-gate 
22390Sstevel@tonic-gate 	if (temp < apic_last_hrtime) {
22400Sstevel@tonic-gate 		/* return last hrtime if error occurs */
22410Sstevel@tonic-gate 		apic_hrtime_error++;
22420Sstevel@tonic-gate 		temp = apic_last_hrtime;
22430Sstevel@tonic-gate 	}
22440Sstevel@tonic-gate 	else
22450Sstevel@tonic-gate 		apic_last_hrtime = temp;
22460Sstevel@tonic-gate 
22470Sstevel@tonic-gate 	lock_clear(&apic_gethrtime_lock);
22480Sstevel@tonic-gate 	intr_restore(oflags);
22490Sstevel@tonic-gate 
22500Sstevel@tonic-gate 	return (temp);
22510Sstevel@tonic-gate }
22520Sstevel@tonic-gate 
22530Sstevel@tonic-gate /* apic NMI handler */
22540Sstevel@tonic-gate /*ARGSUSED*/
22550Sstevel@tonic-gate static void
22560Sstevel@tonic-gate apic_nmi_intr(caddr_t arg)
22570Sstevel@tonic-gate {
22580Sstevel@tonic-gate 	if (apic_shutdown_processors) {
22590Sstevel@tonic-gate 		apic_disable_local_apic();
22600Sstevel@tonic-gate 		return;
22610Sstevel@tonic-gate 	}
22620Sstevel@tonic-gate 
22630Sstevel@tonic-gate 	if (lock_try(&apic_nmi_lock)) {
22640Sstevel@tonic-gate 		if (apic_kmdb_on_nmi) {
22650Sstevel@tonic-gate 			if (psm_debugger() == 0) {
22660Sstevel@tonic-gate 				cmn_err(CE_PANIC,
22670Sstevel@tonic-gate 				    "NMI detected, kmdb is not available.");
22680Sstevel@tonic-gate 			} else {
22690Sstevel@tonic-gate 				debug_enter("\nNMI detected, entering kmdb.\n");
22700Sstevel@tonic-gate 			}
22710Sstevel@tonic-gate 		} else {
22720Sstevel@tonic-gate 			if (apic_panic_on_nmi) {
22730Sstevel@tonic-gate 				/* Keep panic from entering kmdb. */
22740Sstevel@tonic-gate 				nopanicdebug = 1;
22750Sstevel@tonic-gate 				cmn_err(CE_PANIC, "pcplusmp: NMI received");
22760Sstevel@tonic-gate 			} else {
22770Sstevel@tonic-gate 				/*
22780Sstevel@tonic-gate 				 * prom_printf is the best shot we have
22790Sstevel@tonic-gate 				 * of something which is problem free from
22800Sstevel@tonic-gate 				 * high level/NMI type of interrupts
22810Sstevel@tonic-gate 				 */
22820Sstevel@tonic-gate 				prom_printf("pcplusmp: NMI received\n");
22830Sstevel@tonic-gate 				apic_error |= APIC_ERR_NMI;
22840Sstevel@tonic-gate 				apic_num_nmis++;
22850Sstevel@tonic-gate 			}
22860Sstevel@tonic-gate 		}
22870Sstevel@tonic-gate 		lock_clear(&apic_nmi_lock);
22880Sstevel@tonic-gate 	}
22890Sstevel@tonic-gate }
22900Sstevel@tonic-gate 
22910Sstevel@tonic-gate /*
22920Sstevel@tonic-gate  * Add mask bits to disable interrupt vector from happening
22930Sstevel@tonic-gate  * at or above IPL. In addition, it should remove mask bits
22940Sstevel@tonic-gate  * to enable interrupt vectors below the given IPL.
22950Sstevel@tonic-gate  *
22960Sstevel@tonic-gate  * Both add and delspl are complicated by the fact that different interrupts
22970Sstevel@tonic-gate  * may share IRQs. This can happen in two ways.
22980Sstevel@tonic-gate  * 1. The same H/W line is shared by more than 1 device
22990Sstevel@tonic-gate  * 1a. with interrupts at different IPLs
23000Sstevel@tonic-gate  * 1b. with interrupts at same IPL
23010Sstevel@tonic-gate  * 2. We ran out of vectors at a given IPL and started sharing vectors.
23020Sstevel@tonic-gate  * 1b and 2 should be handled gracefully, except for the fact some ISRs
23030Sstevel@tonic-gate  * will get called often when no interrupt is pending for the device.
23040Sstevel@tonic-gate  * For 1a, we just hope that the machine blows up with the person who
23050Sstevel@tonic-gate  * set it up that way!. In the meantime, we handle it at the higher IPL.
23060Sstevel@tonic-gate  */
23070Sstevel@tonic-gate /*ARGSUSED*/
23080Sstevel@tonic-gate static int
23090Sstevel@tonic-gate apic_addspl(int irqno, int ipl, int min_ipl, int max_ipl)
23100Sstevel@tonic-gate {
23110Sstevel@tonic-gate 	uchar_t vector;
23120Sstevel@tonic-gate 	int iflag;
23130Sstevel@tonic-gate 	apic_irq_t *irqptr, *irqheadptr;
23140Sstevel@tonic-gate 	int irqindex;
23150Sstevel@tonic-gate 
23160Sstevel@tonic-gate 	ASSERT(max_ipl <= UCHAR_MAX);
23170Sstevel@tonic-gate 	irqindex = IRQINDEX(irqno);
23180Sstevel@tonic-gate 
23190Sstevel@tonic-gate 	if ((irqindex == -1) || (!apic_irq_table[irqindex]))
23200Sstevel@tonic-gate 		return (PSM_FAILURE);
23210Sstevel@tonic-gate 
23220Sstevel@tonic-gate 	irqptr = irqheadptr = apic_irq_table[irqindex];
23230Sstevel@tonic-gate 
23240Sstevel@tonic-gate 	DDI_INTR_IMPLDBG((CE_CONT, "apic_addspl: dip=0x%p type=%d irqno=0x%x "
23250Sstevel@tonic-gate 	    "vector=0x%x\n", (void *)irqptr->airq_dip,
23260Sstevel@tonic-gate 	    irqptr->airq_mps_intr_index, irqno, irqptr->airq_vector));
23270Sstevel@tonic-gate 
23280Sstevel@tonic-gate 	while (irqptr) {
23290Sstevel@tonic-gate 		if (VIRTIRQ(irqindex, irqptr->airq_share_id) == irqno)
23300Sstevel@tonic-gate 			break;
23310Sstevel@tonic-gate 		irqptr = irqptr->airq_next;
23320Sstevel@tonic-gate 	}
23330Sstevel@tonic-gate 	irqptr->airq_share++;
23340Sstevel@tonic-gate 
23350Sstevel@tonic-gate 	/* return if it is not hardware interrupt */
23360Sstevel@tonic-gate 	if (irqptr->airq_mps_intr_index == RESERVE_INDEX)
23370Sstevel@tonic-gate 		return (PSM_SUCCESS);
23380Sstevel@tonic-gate 
23390Sstevel@tonic-gate 	/* Or if there are more interupts at a higher IPL */
23400Sstevel@tonic-gate 	if (ipl != max_ipl)
23410Sstevel@tonic-gate 		return (PSM_SUCCESS);
23420Sstevel@tonic-gate 
23430Sstevel@tonic-gate 	/*
23440Sstevel@tonic-gate 	 * if apic_picinit() has not been called yet, just return.
23450Sstevel@tonic-gate 	 * At the end of apic_picinit(), we will call setup_io_intr().
23460Sstevel@tonic-gate 	 */
23470Sstevel@tonic-gate 
23480Sstevel@tonic-gate 	if (!apic_flag)
23490Sstevel@tonic-gate 		return (PSM_SUCCESS);
23500Sstevel@tonic-gate 
23510Sstevel@tonic-gate 	iflag = intr_clear();
23520Sstevel@tonic-gate 
23530Sstevel@tonic-gate 	/*
23540Sstevel@tonic-gate 	 * Upgrade vector if max_ipl is not earlier ipl. If we cannot allocate,
23550Sstevel@tonic-gate 	 * return failure. Not very elegant, but then we hope the
23560Sstevel@tonic-gate 	 * machine will blow up with ...
23570Sstevel@tonic-gate 	 */
23580Sstevel@tonic-gate 	if (irqptr->airq_ipl != max_ipl) {
23590Sstevel@tonic-gate 		vector = apic_allocate_vector(max_ipl, irqindex, 1);
23600Sstevel@tonic-gate 		if (vector == 0) {
23610Sstevel@tonic-gate 			intr_restore(iflag);
23620Sstevel@tonic-gate 			irqptr->airq_share--;
23630Sstevel@tonic-gate 			return (PSM_FAILURE);
23640Sstevel@tonic-gate 		}
23650Sstevel@tonic-gate 		irqptr = irqheadptr;
23660Sstevel@tonic-gate 		apic_mark_vector(irqptr->airq_vector, vector);
23670Sstevel@tonic-gate 		while (irqptr) {
23680Sstevel@tonic-gate 			irqptr->airq_vector = vector;
23690Sstevel@tonic-gate 			irqptr->airq_ipl = (uchar_t)max_ipl;
23700Sstevel@tonic-gate 			/*
23710Sstevel@tonic-gate 			 * reprogram irq being added and every one else
23720Sstevel@tonic-gate 			 * who is not in the UNINIT state
23730Sstevel@tonic-gate 			 */
23740Sstevel@tonic-gate 			if ((VIRTIRQ(irqindex, irqptr->airq_share_id) ==
23750Sstevel@tonic-gate 			    irqno) || (irqptr->airq_temp_cpu != IRQ_UNINIT)) {
23760Sstevel@tonic-gate 				apic_record_rdt_entry(irqptr, irqindex);
23770Sstevel@tonic-gate 				(void) apic_setup_io_intr(irqptr, irqindex);
23780Sstevel@tonic-gate 			}
23790Sstevel@tonic-gate 			irqptr = irqptr->airq_next;
23800Sstevel@tonic-gate 		}
23810Sstevel@tonic-gate 		intr_restore(iflag);
23820Sstevel@tonic-gate 		return (PSM_SUCCESS);
23830Sstevel@tonic-gate 	}
23840Sstevel@tonic-gate 
23850Sstevel@tonic-gate 	ASSERT(irqptr);
23860Sstevel@tonic-gate 	(void) apic_setup_io_intr(irqptr, irqindex);
23870Sstevel@tonic-gate 	intr_restore(iflag);
23880Sstevel@tonic-gate 	return (PSM_SUCCESS);
23890Sstevel@tonic-gate }
23900Sstevel@tonic-gate 
23910Sstevel@tonic-gate /*
23920Sstevel@tonic-gate  * Recompute mask bits for the given interrupt vector.
23930Sstevel@tonic-gate  * If there is no interrupt servicing routine for this
23940Sstevel@tonic-gate  * vector, this function should disable interrupt vector
23950Sstevel@tonic-gate  * from happening at all IPLs. If there are still
23960Sstevel@tonic-gate  * handlers using the given vector, this function should
23970Sstevel@tonic-gate  * disable the given vector from happening below the lowest
23980Sstevel@tonic-gate  * IPL of the remaining hadlers.
23990Sstevel@tonic-gate  */
24000Sstevel@tonic-gate /*ARGSUSED*/
24010Sstevel@tonic-gate static int
24020Sstevel@tonic-gate apic_delspl(int irqno, int ipl, int min_ipl, int max_ipl)
24030Sstevel@tonic-gate {
24040Sstevel@tonic-gate 	uchar_t vector, bind_cpu;
24050Sstevel@tonic-gate 	int	iflag, intin, irqindex;
24060Sstevel@tonic-gate 	volatile int32_t *ioapic;
24070Sstevel@tonic-gate 	apic_irq_t	*irqptr, *irqheadptr;
24080Sstevel@tonic-gate 
24090Sstevel@tonic-gate 	irqindex = IRQINDEX(irqno);
24100Sstevel@tonic-gate 	irqptr = irqheadptr = apic_irq_table[irqindex];
24110Sstevel@tonic-gate 
24120Sstevel@tonic-gate 	DDI_INTR_IMPLDBG((CE_CONT, "apic_delspl: dip=0x%p type=%d irqno=0x%x "
24130Sstevel@tonic-gate 	    "vector=0x%x\n", (void *)irqptr->airq_dip,
24140Sstevel@tonic-gate 	    irqptr->airq_mps_intr_index, irqno, irqptr->airq_vector));
24150Sstevel@tonic-gate 
24160Sstevel@tonic-gate 	while (irqptr) {
24170Sstevel@tonic-gate 		if (VIRTIRQ(irqindex, irqptr->airq_share_id) == irqno)
24180Sstevel@tonic-gate 			break;
24190Sstevel@tonic-gate 		irqptr = irqptr->airq_next;
24200Sstevel@tonic-gate 	}
24210Sstevel@tonic-gate 	ASSERT(irqptr);
24220Sstevel@tonic-gate 
24230Sstevel@tonic-gate 	irqptr->airq_share--;
24240Sstevel@tonic-gate 
24250Sstevel@tonic-gate 	if (ipl < max_ipl)
24260Sstevel@tonic-gate 		return (PSM_SUCCESS);
24270Sstevel@tonic-gate 
24280Sstevel@tonic-gate 	/* return if it is not hardware interrupt */
24290Sstevel@tonic-gate 	if (irqptr->airq_mps_intr_index == RESERVE_INDEX)
24300Sstevel@tonic-gate 		return (PSM_SUCCESS);
24310Sstevel@tonic-gate 
24320Sstevel@tonic-gate 	if (!apic_flag) {
24330Sstevel@tonic-gate 		/*
24340Sstevel@tonic-gate 		 * Clear irq_struct. If two devices shared an intpt
24350Sstevel@tonic-gate 		 * line & 1 unloaded before picinit, we are hosed. But, then
24360Sstevel@tonic-gate 		 * we hope the machine will ...
24370Sstevel@tonic-gate 		 */
24380Sstevel@tonic-gate 		irqptr->airq_mps_intr_index = FREE_INDEX;
24390Sstevel@tonic-gate 		irqptr->airq_temp_cpu = IRQ_UNINIT;
24400Sstevel@tonic-gate 		apic_free_vector(irqptr->airq_vector);
24410Sstevel@tonic-gate 		return (PSM_SUCCESS);
24420Sstevel@tonic-gate 	}
24430Sstevel@tonic-gate 	/*
24440Sstevel@tonic-gate 	 * Downgrade vector to new max_ipl if needed.If we cannot allocate,
24450Sstevel@tonic-gate 	 * use old IPL. Not very elegant, but then we hope ...
24460Sstevel@tonic-gate 	 */
24470Sstevel@tonic-gate 	if ((irqptr->airq_ipl != max_ipl) && (max_ipl != PSM_INVALID_IPL)) {
24480Sstevel@tonic-gate 		apic_irq_t	*irqp;
24490Sstevel@tonic-gate 		if (vector = apic_allocate_vector(max_ipl, irqno, 1)) {
24500Sstevel@tonic-gate 			apic_mark_vector(irqheadptr->airq_vector, vector);
24510Sstevel@tonic-gate 			irqp = irqheadptr;
24520Sstevel@tonic-gate 			while (irqp) {
24530Sstevel@tonic-gate 				irqp->airq_vector = vector;
24540Sstevel@tonic-gate 				irqp->airq_ipl = (uchar_t)max_ipl;
24550Sstevel@tonic-gate 				if (irqp->airq_temp_cpu != IRQ_UNINIT) {
24560Sstevel@tonic-gate 					apic_record_rdt_entry(irqp, irqindex);
24570Sstevel@tonic-gate 					(void) apic_setup_io_intr(irqp,
24580Sstevel@tonic-gate 					    irqindex);
24590Sstevel@tonic-gate 				}
24600Sstevel@tonic-gate 				irqp = irqp->airq_next;
24610Sstevel@tonic-gate 			}
24620Sstevel@tonic-gate 		}
24630Sstevel@tonic-gate 	}
24640Sstevel@tonic-gate 
24650Sstevel@tonic-gate 	if (irqptr->airq_share)
24660Sstevel@tonic-gate 		return (PSM_SUCCESS);
24670Sstevel@tonic-gate 
24680Sstevel@tonic-gate 	ioapic = apicioadr[irqptr->airq_ioapicindex];
24690Sstevel@tonic-gate 	intin = irqptr->airq_intin_no;
24700Sstevel@tonic-gate 	iflag = intr_clear();
24710Sstevel@tonic-gate 	lock_set(&apic_ioapic_lock);
24720Sstevel@tonic-gate 	ioapic[APIC_IO_REG] = APIC_RDT_CMD + 2 * intin;
24730Sstevel@tonic-gate 	ioapic[APIC_IO_DATA] = AV_MASK;
24740Sstevel@tonic-gate 
24750Sstevel@tonic-gate 	/* Disable the MSI/X vector */
24760Sstevel@tonic-gate 	if (APIC_IS_MSI_OR_MSIX_INDEX(irqptr->airq_mps_intr_index)) {
24770Sstevel@tonic-gate 		int type = (irqptr->airq_mps_intr_index == MSI_INDEX) ?
24780Sstevel@tonic-gate 		    DDI_INTR_TYPE_MSI : DDI_INTR_TYPE_MSIX;
24790Sstevel@tonic-gate 
24800Sstevel@tonic-gate 		/*
24810Sstevel@tonic-gate 		 * Make sure we only disable on the last
24820Sstevel@tonic-gate 		 * of the multi-MSI support
24830Sstevel@tonic-gate 		 */
24840Sstevel@tonic-gate 		if (i_ddi_intr_get_current_nintrs(irqptr->airq_dip) == 1) {
24850Sstevel@tonic-gate 			(void) pci_msi_unconfigure(irqptr->airq_dip, type,
24860Sstevel@tonic-gate 			    irqptr->airq_ioapicindex);
24870Sstevel@tonic-gate 
24880Sstevel@tonic-gate 			(void) pci_msi_disable_mode(irqptr->airq_dip, type,
24890Sstevel@tonic-gate 			    irqptr->airq_ioapicindex);
24900Sstevel@tonic-gate 		}
24910Sstevel@tonic-gate 	}
24920Sstevel@tonic-gate 
24930Sstevel@tonic-gate 	if (max_ipl == PSM_INVALID_IPL) {
24940Sstevel@tonic-gate 		ASSERT(irqheadptr == irqptr);
24950Sstevel@tonic-gate 		bind_cpu = irqptr->airq_temp_cpu;
24960Sstevel@tonic-gate 		if (((uchar_t)bind_cpu != IRQ_UNBOUND) &&
24970Sstevel@tonic-gate 		    ((uchar_t)bind_cpu != IRQ_UNINIT)) {
24980Sstevel@tonic-gate 			ASSERT((bind_cpu & ~IRQ_USER_BOUND) < apic_nproc);
24990Sstevel@tonic-gate 			if (bind_cpu & IRQ_USER_BOUND) {
25000Sstevel@tonic-gate 				/* If hardbound, temp_cpu == cpu */
25010Sstevel@tonic-gate 				bind_cpu &= ~IRQ_USER_BOUND;
25020Sstevel@tonic-gate 				apic_cpus[bind_cpu].aci_bound--;
25030Sstevel@tonic-gate 			} else
25040Sstevel@tonic-gate 				apic_cpus[bind_cpu].aci_temp_bound--;
25050Sstevel@tonic-gate 		}
25060Sstevel@tonic-gate 		lock_clear(&apic_ioapic_lock);
25070Sstevel@tonic-gate 		intr_restore(iflag);
25080Sstevel@tonic-gate 		irqptr->airq_temp_cpu = IRQ_UNINIT;
25090Sstevel@tonic-gate 		irqptr->airq_mps_intr_index = FREE_INDEX;
25100Sstevel@tonic-gate 		apic_free_vector(irqptr->airq_vector);
25110Sstevel@tonic-gate 		return (PSM_SUCCESS);
25120Sstevel@tonic-gate 	}
25130Sstevel@tonic-gate 	lock_clear(&apic_ioapic_lock);
25140Sstevel@tonic-gate 	intr_restore(iflag);
25150Sstevel@tonic-gate 
25160Sstevel@tonic-gate 	mutex_enter(&airq_mutex);
25170Sstevel@tonic-gate 	if ((irqptr == apic_irq_table[irqindex])) {
25180Sstevel@tonic-gate 		apic_irq_t	*oldirqptr;
25190Sstevel@tonic-gate 		/* Move valid irq entry to the head */
25200Sstevel@tonic-gate 		irqheadptr = oldirqptr = irqptr;
25210Sstevel@tonic-gate 		irqptr = irqptr->airq_next;
25220Sstevel@tonic-gate 		ASSERT(irqptr);
25230Sstevel@tonic-gate 		while (irqptr) {
25240Sstevel@tonic-gate 			if (irqptr->airq_mps_intr_index != FREE_INDEX)
25250Sstevel@tonic-gate 				break;
25260Sstevel@tonic-gate 			oldirqptr = irqptr;
25270Sstevel@tonic-gate 			irqptr = irqptr->airq_next;
25280Sstevel@tonic-gate 		}
25290Sstevel@tonic-gate 		/* remove all invalid ones from the beginning */
25300Sstevel@tonic-gate 		apic_irq_table[irqindex] = irqptr;
25310Sstevel@tonic-gate 		/*
25320Sstevel@tonic-gate 		 * and link them back after the head. The invalid ones
25330Sstevel@tonic-gate 		 * begin with irqheadptr and end at oldirqptr
25340Sstevel@tonic-gate 		 */
25350Sstevel@tonic-gate 		oldirqptr->airq_next = irqptr->airq_next;
25360Sstevel@tonic-gate 		irqptr->airq_next = irqheadptr;
25370Sstevel@tonic-gate 	}
25380Sstevel@tonic-gate 	mutex_exit(&airq_mutex);
25390Sstevel@tonic-gate 
25400Sstevel@tonic-gate 	irqptr->airq_temp_cpu = IRQ_UNINIT;
25410Sstevel@tonic-gate 	irqptr->airq_mps_intr_index = FREE_INDEX;
25420Sstevel@tonic-gate 	return (PSM_SUCCESS);
25430Sstevel@tonic-gate }
25440Sstevel@tonic-gate 
25450Sstevel@tonic-gate /*
25460Sstevel@tonic-gate  * Return HW interrupt number corresponding to the given IPL
25470Sstevel@tonic-gate  */
25480Sstevel@tonic-gate /*ARGSUSED*/
25490Sstevel@tonic-gate static int
25500Sstevel@tonic-gate apic_softlvl_to_irq(int ipl)
25510Sstevel@tonic-gate {
25520Sstevel@tonic-gate 	/*
25530Sstevel@tonic-gate 	 * Do not use apic to trigger soft interrupt.
25540Sstevel@tonic-gate 	 * It will cause the system to hang when 2 hardware interrupts
25550Sstevel@tonic-gate 	 * at the same priority with the softint are already accepted
25560Sstevel@tonic-gate 	 * by the apic.  Cause the AV_PENDING bit will not be cleared
25570Sstevel@tonic-gate 	 * until one of the hardware interrupt is eoi'ed.  If we need
25580Sstevel@tonic-gate 	 * to send an ipi at this time, we will end up looping forever
25590Sstevel@tonic-gate 	 * to wait for the AV_PENDING bit to clear.
25600Sstevel@tonic-gate 	 */
25610Sstevel@tonic-gate 	return (PSM_SV_SOFTWARE);
25620Sstevel@tonic-gate }
25630Sstevel@tonic-gate 
25640Sstevel@tonic-gate static int
25650Sstevel@tonic-gate apic_post_cpu_start()
25660Sstevel@tonic-gate {
25670Sstevel@tonic-gate 	int i, cpun;
25680Sstevel@tonic-gate 	apic_irq_t *irq_ptr;
25690Sstevel@tonic-gate 
25700Sstevel@tonic-gate 	apic_init_intr();
25710Sstevel@tonic-gate 
25720Sstevel@tonic-gate 	/*
25730Sstevel@tonic-gate 	 * since some systems don't enable the internal cache on the non-boot
25740Sstevel@tonic-gate 	 * cpus, so we have to enable them here
25750Sstevel@tonic-gate 	 */
25760Sstevel@tonic-gate 	setcr0(getcr0() & ~(0x60000000));
25770Sstevel@tonic-gate 
25780Sstevel@tonic-gate 	while (get_apic_cmd1() & AV_PENDING)
25790Sstevel@tonic-gate 		apic_ret();
25800Sstevel@tonic-gate 
25810Sstevel@tonic-gate 	cpun = psm_get_cpu_id();
25820Sstevel@tonic-gate 	apic_cpus[cpun].aci_status = APIC_CPU_ONLINE | APIC_CPU_INTR_ENABLE;
25830Sstevel@tonic-gate 
25840Sstevel@tonic-gate 	for (i = apic_min_device_irq; i <= apic_max_device_irq; i++) {
25850Sstevel@tonic-gate 		irq_ptr = apic_irq_table[i];
25860Sstevel@tonic-gate 		if ((irq_ptr == NULL) ||
25870Sstevel@tonic-gate 		    ((irq_ptr->airq_cpu & ~IRQ_USER_BOUND) != cpun))
25880Sstevel@tonic-gate 			continue;
25890Sstevel@tonic-gate 
25900Sstevel@tonic-gate 		while (irq_ptr) {
25910Sstevel@tonic-gate 			if (irq_ptr->airq_temp_cpu != IRQ_UNINIT)
25920Sstevel@tonic-gate 				(void) apic_rebind(irq_ptr, cpun, 1, IMMEDIATE);
25930Sstevel@tonic-gate 			irq_ptr = irq_ptr->airq_next;
25940Sstevel@tonic-gate 		}
25950Sstevel@tonic-gate 	}
25960Sstevel@tonic-gate 
25970Sstevel@tonic-gate 	return (PSM_SUCCESS);
25980Sstevel@tonic-gate }
25990Sstevel@tonic-gate 
26000Sstevel@tonic-gate processorid_t
26010Sstevel@tonic-gate apic_get_next_processorid(processorid_t cpu_id)
26020Sstevel@tonic-gate {
26030Sstevel@tonic-gate 
26040Sstevel@tonic-gate 	int i;
26050Sstevel@tonic-gate 
26060Sstevel@tonic-gate 	if (cpu_id == -1)
26070Sstevel@tonic-gate 		return ((processorid_t)0);
26080Sstevel@tonic-gate 
26090Sstevel@tonic-gate 	for (i = cpu_id + 1; i < NCPU; i++) {
26100Sstevel@tonic-gate 		if (apic_cpumask & (1 << i))
26110Sstevel@tonic-gate 			return (i);
26120Sstevel@tonic-gate 	}
26130Sstevel@tonic-gate 
26140Sstevel@tonic-gate 	return ((processorid_t)-1);
26150Sstevel@tonic-gate }
26160Sstevel@tonic-gate 
26170Sstevel@tonic-gate 
26180Sstevel@tonic-gate /*
26190Sstevel@tonic-gate  * type == -1 indicates it is an internal request. Do not change
26200Sstevel@tonic-gate  * resv_vector for these requests
26210Sstevel@tonic-gate  */
26220Sstevel@tonic-gate static int
26230Sstevel@tonic-gate apic_get_ipivect(int ipl, int type)
26240Sstevel@tonic-gate {
26250Sstevel@tonic-gate 	uchar_t vector;
26260Sstevel@tonic-gate 	int irq;
26270Sstevel@tonic-gate 
26280Sstevel@tonic-gate 	if (irq = apic_allocate_irq(APIC_VECTOR(ipl))) {
26290Sstevel@tonic-gate 		if (vector = apic_allocate_vector(ipl, irq, 1)) {
26300Sstevel@tonic-gate 			apic_irq_table[irq]->airq_mps_intr_index =
26310Sstevel@tonic-gate 			    RESERVE_INDEX;
26320Sstevel@tonic-gate 			apic_irq_table[irq]->airq_vector = vector;
26330Sstevel@tonic-gate 			if (type != -1) {
26340Sstevel@tonic-gate 				apic_resv_vector[ipl] = vector;
26350Sstevel@tonic-gate 			}
26360Sstevel@tonic-gate 			return (irq);
26370Sstevel@tonic-gate 		}
26380Sstevel@tonic-gate 	}
26390Sstevel@tonic-gate 	apic_error |= APIC_ERR_GET_IPIVECT_FAIL;
26400Sstevel@tonic-gate 	return (-1);	/* shouldn't happen */
26410Sstevel@tonic-gate }
26420Sstevel@tonic-gate 
26430Sstevel@tonic-gate static int
26440Sstevel@tonic-gate apic_getclkirq(int ipl)
26450Sstevel@tonic-gate {
26460Sstevel@tonic-gate 	int	irq;
26470Sstevel@tonic-gate 
26480Sstevel@tonic-gate 	if ((irq = apic_get_ipivect(ipl, -1)) == -1)
26490Sstevel@tonic-gate 		return (-1);
26500Sstevel@tonic-gate 	/*
26510Sstevel@tonic-gate 	 * Note the vector in apic_clkvect for per clock handling.
26520Sstevel@tonic-gate 	 */
26530Sstevel@tonic-gate 	apic_clkvect = apic_irq_table[irq]->airq_vector - APIC_BASE_VECT;
26540Sstevel@tonic-gate 	APIC_VERBOSE_IOAPIC((CE_NOTE, "get_clkirq: vector = %x\n",
26550Sstevel@tonic-gate 	    apic_clkvect));
26560Sstevel@tonic-gate 	return (irq);
26570Sstevel@tonic-gate }
26580Sstevel@tonic-gate 
26590Sstevel@tonic-gate /*
26600Sstevel@tonic-gate  * Return the number of APIC clock ticks elapsed for 8245 to decrement
26610Sstevel@tonic-gate  * (APIC_TIME_COUNT + pit_ticks_adj) ticks.
26620Sstevel@tonic-gate  */
26630Sstevel@tonic-gate static uint_t
26640Sstevel@tonic-gate apic_calibrate(volatile uint32_t *addr, uint16_t *pit_ticks_adj)
26650Sstevel@tonic-gate {
26660Sstevel@tonic-gate 	uint8_t		pit_tick_lo;
26670Sstevel@tonic-gate 	uint16_t	pit_tick, target_pit_tick;
26680Sstevel@tonic-gate 	uint32_t	start_apic_tick, end_apic_tick;
26690Sstevel@tonic-gate 	int		iflag;
26700Sstevel@tonic-gate 
26710Sstevel@tonic-gate 	addr += APIC_CURR_COUNT;
26720Sstevel@tonic-gate 
26730Sstevel@tonic-gate 	iflag = intr_clear();
26740Sstevel@tonic-gate 
26750Sstevel@tonic-gate 	do {
26760Sstevel@tonic-gate 		pit_tick_lo = inb(PITCTR0_PORT);
26770Sstevel@tonic-gate 		pit_tick = (inb(PITCTR0_PORT) << 8) | pit_tick_lo;
26780Sstevel@tonic-gate 	} while (pit_tick < APIC_TIME_MIN ||
26790Sstevel@tonic-gate 	    pit_tick_lo <= APIC_LB_MIN || pit_tick_lo >= APIC_LB_MAX);
26800Sstevel@tonic-gate 
26810Sstevel@tonic-gate 	/*
26820Sstevel@tonic-gate 	 * Wait for the 8254 to decrement by 5 ticks to ensure
26830Sstevel@tonic-gate 	 * we didn't start in the middle of a tick.
26840Sstevel@tonic-gate 	 * Compare with 0x10 for the wrap around case.
26850Sstevel@tonic-gate 	 */
26860Sstevel@tonic-gate 	target_pit_tick = pit_tick - 5;
26870Sstevel@tonic-gate 	do {
26880Sstevel@tonic-gate 		pit_tick_lo = inb(PITCTR0_PORT);
26890Sstevel@tonic-gate 		pit_tick = (inb(PITCTR0_PORT) << 8) | pit_tick_lo;
26900Sstevel@tonic-gate 	} while (pit_tick > target_pit_tick || pit_tick_lo < 0x10);
26910Sstevel@tonic-gate 
26920Sstevel@tonic-gate 	start_apic_tick = *addr;
26930Sstevel@tonic-gate 
26940Sstevel@tonic-gate 	/*
26950Sstevel@tonic-gate 	 * Wait for the 8254 to decrement by
26960Sstevel@tonic-gate 	 * (APIC_TIME_COUNT + pit_ticks_adj) ticks
26970Sstevel@tonic-gate 	 */
26980Sstevel@tonic-gate 	target_pit_tick = pit_tick - APIC_TIME_COUNT;
26990Sstevel@tonic-gate 	do {
27000Sstevel@tonic-gate 		pit_tick_lo = inb(PITCTR0_PORT);
27010Sstevel@tonic-gate 		pit_tick = (inb(PITCTR0_PORT) << 8) | pit_tick_lo;
27020Sstevel@tonic-gate 	} while (pit_tick > target_pit_tick || pit_tick_lo < 0x10);
27030Sstevel@tonic-gate 
27040Sstevel@tonic-gate 	end_apic_tick = *addr;
27050Sstevel@tonic-gate 
27060Sstevel@tonic-gate 	*pit_ticks_adj = target_pit_tick - pit_tick;
27070Sstevel@tonic-gate 
27080Sstevel@tonic-gate 	intr_restore(iflag);
27090Sstevel@tonic-gate 
27100Sstevel@tonic-gate 	return (start_apic_tick - end_apic_tick);
27110Sstevel@tonic-gate }
27120Sstevel@tonic-gate 
27130Sstevel@tonic-gate /*
27140Sstevel@tonic-gate  * Initialise the APIC timer on the local APIC of CPU 0 to the desired
27150Sstevel@tonic-gate  * frequency.  Note at this stage in the boot sequence, the boot processor
27160Sstevel@tonic-gate  * is the only active processor.
27170Sstevel@tonic-gate  * hertz value of 0 indicates a one-shot mode request.  In this case
27180Sstevel@tonic-gate  * the function returns the resolution (in nanoseconds) for the hardware
27190Sstevel@tonic-gate  * timer interrupt.  If one-shot mode capability is not available,
27200Sstevel@tonic-gate  * the return value will be 0. apic_enable_oneshot is a global switch
27210Sstevel@tonic-gate  * for disabling the functionality.
27220Sstevel@tonic-gate  * A non-zero positive value for hertz indicates a periodic mode request.
27230Sstevel@tonic-gate  * In this case the hardware will be programmed to generate clock interrupts
27240Sstevel@tonic-gate  * at hertz frequency and returns the resolution of interrupts in
27250Sstevel@tonic-gate  * nanosecond.
27260Sstevel@tonic-gate  */
27270Sstevel@tonic-gate 
27280Sstevel@tonic-gate static int
27290Sstevel@tonic-gate apic_clkinit(int hertz)
27300Sstevel@tonic-gate {
27310Sstevel@tonic-gate 
27320Sstevel@tonic-gate 	uint_t		apic_ticks = 0;
27330Sstevel@tonic-gate 	uint_t		pit_time;
27340Sstevel@tonic-gate 	int		ret;
27350Sstevel@tonic-gate 	uint16_t	pit_ticks_adj;
27360Sstevel@tonic-gate 	static int	firsttime = 1;
27370Sstevel@tonic-gate 
27380Sstevel@tonic-gate 	if (firsttime) {
27390Sstevel@tonic-gate 		/* first time calibrate */
27400Sstevel@tonic-gate 
27410Sstevel@tonic-gate 		apicadr[APIC_DIVIDE_REG] = 0x0;
27420Sstevel@tonic-gate 		apicadr[APIC_INIT_COUNT] = APIC_MAXVAL;
27430Sstevel@tonic-gate 
27440Sstevel@tonic-gate 		/* set periodic interrupt based on CLKIN */
27450Sstevel@tonic-gate 		apicadr[APIC_LOCAL_TIMER] =
27460Sstevel@tonic-gate 		    (apic_clkvect + APIC_BASE_VECT) | AV_TIME;
27470Sstevel@tonic-gate 		tenmicrosec();
27480Sstevel@tonic-gate 
27490Sstevel@tonic-gate 		apic_ticks = apic_calibrate(apicadr, &pit_ticks_adj);
27500Sstevel@tonic-gate 
27510Sstevel@tonic-gate 		apicadr[APIC_LOCAL_TIMER] =
27520Sstevel@tonic-gate 		    (apic_clkvect + APIC_BASE_VECT) | AV_MASK;
27530Sstevel@tonic-gate 		/*
27540Sstevel@tonic-gate 		 * pit time is the amount of real time (in nanoseconds ) it took
27550Sstevel@tonic-gate 		 * the 8254 to decrement (APIC_TIME_COUNT + pit_ticks_adj) ticks
27560Sstevel@tonic-gate 		 */
27570Sstevel@tonic-gate 		pit_time = ((longlong_t)(APIC_TIME_COUNT +
27580Sstevel@tonic-gate 		    pit_ticks_adj) * NANOSEC) / PIT_HZ;
27590Sstevel@tonic-gate 
27600Sstevel@tonic-gate 		/*
27610Sstevel@tonic-gate 		 * Determine the number of nanoseconds per APIC clock tick
27620Sstevel@tonic-gate 		 * and then determine how many APIC ticks to interrupt at the
27630Sstevel@tonic-gate 		 * desired frequency
27640Sstevel@tonic-gate 		 */
27650Sstevel@tonic-gate 		apic_nsec_per_tick = pit_time / apic_ticks;
27660Sstevel@tonic-gate 		if (apic_nsec_per_tick == 0)
27670Sstevel@tonic-gate 			apic_nsec_per_tick = 1;
27680Sstevel@tonic-gate 
27690Sstevel@tonic-gate 		/* the interval timer initial count is 32 bit max */
27700Sstevel@tonic-gate 		apic_nsec_max = (hrtime_t)apic_nsec_per_tick * APIC_MAXVAL;
27710Sstevel@tonic-gate 		firsttime = 0;
27720Sstevel@tonic-gate 	}
27730Sstevel@tonic-gate 
27740Sstevel@tonic-gate 	if (hertz != 0) {
27750Sstevel@tonic-gate 		/* periodic */
27760Sstevel@tonic-gate 		apic_nsec_per_intr = NANOSEC / hertz;
27770Sstevel@tonic-gate 		apic_hertz_count = (longlong_t)apic_nsec_per_intr /
27780Sstevel@tonic-gate 		    apic_nsec_per_tick;
27790Sstevel@tonic-gate 		apic_sample_factor_redistribution = hertz + 1;
27800Sstevel@tonic-gate 	}
27810Sstevel@tonic-gate 
27820Sstevel@tonic-gate 	apic_int_busy_mark = (apic_int_busy_mark *
27830Sstevel@tonic-gate 	    apic_sample_factor_redistribution) / 100;
27840Sstevel@tonic-gate 	apic_int_free_mark = (apic_int_free_mark *
27850Sstevel@tonic-gate 	    apic_sample_factor_redistribution) / 100;
27860Sstevel@tonic-gate 	apic_diff_for_redistribution = (apic_diff_for_redistribution *
27870Sstevel@tonic-gate 	    apic_sample_factor_redistribution) / 100;
27880Sstevel@tonic-gate 
27890Sstevel@tonic-gate 	if (hertz == 0) {
27900Sstevel@tonic-gate 		/* requested one_shot */
27910Sstevel@tonic-gate 		if (!apic_oneshot_enable)
27920Sstevel@tonic-gate 			return (0);
27930Sstevel@tonic-gate 		apic_oneshot = 1;
27940Sstevel@tonic-gate 		ret = (int)apic_nsec_per_tick;
27950Sstevel@tonic-gate 	} else {
27960Sstevel@tonic-gate 		/* program the local APIC to interrupt at the given frequency */
27970Sstevel@tonic-gate 		apicadr[APIC_INIT_COUNT] = apic_hertz_count;
27980Sstevel@tonic-gate 		apicadr[APIC_LOCAL_TIMER] =
27990Sstevel@tonic-gate 		    (apic_clkvect + APIC_BASE_VECT) | AV_TIME;
28000Sstevel@tonic-gate 		apic_oneshot = 0;
28010Sstevel@tonic-gate 		ret = NANOSEC / hertz;
28020Sstevel@tonic-gate 	}
28030Sstevel@tonic-gate 
28040Sstevel@tonic-gate 	return (ret);
28050Sstevel@tonic-gate 
28060Sstevel@tonic-gate }
28070Sstevel@tonic-gate 
28080Sstevel@tonic-gate /*
28090Sstevel@tonic-gate  * apic_preshutdown:
28100Sstevel@tonic-gate  * Called early in shutdown whilst we can still access filesystems to do
28110Sstevel@tonic-gate  * things like loading modules which will be required to complete shutdown
28120Sstevel@tonic-gate  * after filesystems are all unmounted.
28130Sstevel@tonic-gate  */
28140Sstevel@tonic-gate static void
28150Sstevel@tonic-gate apic_preshutdown(int cmd, int fcn)
28160Sstevel@tonic-gate {
28170Sstevel@tonic-gate 	APIC_VERBOSE_POWEROFF(("apic_preshutdown(%d,%d); m=%d a=%d\n",
28180Sstevel@tonic-gate 	    cmd, fcn, apic_poweroff_method, apic_enable_acpi));
28190Sstevel@tonic-gate 
28200Sstevel@tonic-gate 	if ((cmd != A_SHUTDOWN) || (fcn != AD_POWEROFF)) {
28210Sstevel@tonic-gate 		return;
28220Sstevel@tonic-gate 	}
28230Sstevel@tonic-gate }
28240Sstevel@tonic-gate 
28250Sstevel@tonic-gate static void
28260Sstevel@tonic-gate apic_shutdown(int cmd, int fcn)
28270Sstevel@tonic-gate {
28280Sstevel@tonic-gate 	int iflag, restarts, attempts;
28290Sstevel@tonic-gate 	int i, j;
28300Sstevel@tonic-gate 	volatile int32_t *ioapic;
28310Sstevel@tonic-gate 	uchar_t	byte;
28320Sstevel@tonic-gate 
28330Sstevel@tonic-gate 	/* Send NMI to all CPUs except self to do per processor shutdown */
28340Sstevel@tonic-gate 	iflag = intr_clear();
28350Sstevel@tonic-gate 	while (get_apic_cmd1() & AV_PENDING)
28360Sstevel@tonic-gate 		apic_ret();
28370Sstevel@tonic-gate 	apic_shutdown_processors = 1;
28380Sstevel@tonic-gate 	apicadr[APIC_INT_CMD1] = AV_NMI | AV_LEVEL | AV_SH_ALL_EXCSELF;
28390Sstevel@tonic-gate 
28400Sstevel@tonic-gate 	/* restore cmos shutdown byte before reboot */
28410Sstevel@tonic-gate 	if (apic_cmos_ssb_set) {
28420Sstevel@tonic-gate 		outb(CMOS_ADDR, SSB);
28430Sstevel@tonic-gate 		outb(CMOS_DATA, 0);
28440Sstevel@tonic-gate 	}
28450Sstevel@tonic-gate 	/* Disable the I/O APIC redirection entries */
28460Sstevel@tonic-gate 	for (j = 0; j < apic_io_max; j++) {
28470Sstevel@tonic-gate 		int intin_max;
28480Sstevel@tonic-gate 		ioapic = apicioadr[j];
28490Sstevel@tonic-gate 		ioapic[APIC_IO_REG] = APIC_VERS_CMD;
28500Sstevel@tonic-gate 		/* Bits 23-16 define the maximum redirection entries */
28510Sstevel@tonic-gate 		intin_max = (ioapic[APIC_IO_DATA] >> 16) & 0xff;
28520Sstevel@tonic-gate 		for (i = 0; i < intin_max; i++) {
28530Sstevel@tonic-gate 			ioapic[APIC_IO_REG] = APIC_RDT_CMD + 2 * i;
28540Sstevel@tonic-gate 			ioapic[APIC_IO_DATA] = AV_MASK;
28550Sstevel@tonic-gate 		}
28560Sstevel@tonic-gate 	}
28570Sstevel@tonic-gate 
28580Sstevel@tonic-gate 	/*	disable apic mode if imcr present	*/
28590Sstevel@tonic-gate 	if (apic_imcrp) {
28600Sstevel@tonic-gate 		outb(APIC_IMCR_P1, (uchar_t)APIC_IMCR_SELECT);
28610Sstevel@tonic-gate 		outb(APIC_IMCR_P2, (uchar_t)APIC_IMCR_PIC);
28620Sstevel@tonic-gate 	}
28630Sstevel@tonic-gate 
28640Sstevel@tonic-gate 	apic_disable_local_apic();
28650Sstevel@tonic-gate 
28660Sstevel@tonic-gate 	intr_restore(iflag);
28670Sstevel@tonic-gate 
28680Sstevel@tonic-gate 	if ((cmd != A_SHUTDOWN) || (fcn != AD_POWEROFF)) {
28690Sstevel@tonic-gate 		return;
28700Sstevel@tonic-gate 	}
28710Sstevel@tonic-gate 
28720Sstevel@tonic-gate 	switch (apic_poweroff_method) {
28730Sstevel@tonic-gate 		case APIC_POWEROFF_VIA_RTC:
28740Sstevel@tonic-gate 
28750Sstevel@tonic-gate 			/* select the extended NVRAM bank in the RTC */
28760Sstevel@tonic-gate 			outb(CMOS_ADDR, RTC_REGA);
28770Sstevel@tonic-gate 			byte = inb(CMOS_DATA);
28780Sstevel@tonic-gate 			outb(CMOS_DATA, (byte | EXT_BANK));
28790Sstevel@tonic-gate 
28800Sstevel@tonic-gate 			outb(CMOS_ADDR, PFR_REG);
28810Sstevel@tonic-gate 
28820Sstevel@tonic-gate 			/* for Predator must toggle the PAB bit */
28830Sstevel@tonic-gate 			byte = inb(CMOS_DATA);
28840Sstevel@tonic-gate 
28850Sstevel@tonic-gate 			/*
28860Sstevel@tonic-gate 			 * clear power active bar, wakeup alarm and
28870Sstevel@tonic-gate 			 * kickstart
28880Sstevel@tonic-gate 			 */
28890Sstevel@tonic-gate 			byte &= ~(PAB_CBIT | WF_FLAG | KS_FLAG);
28900Sstevel@tonic-gate 			outb(CMOS_DATA, byte);
28910Sstevel@tonic-gate 
28920Sstevel@tonic-gate 			/* delay before next write */
28930Sstevel@tonic-gate 			drv_usecwait(1000);
28940Sstevel@tonic-gate 
28950Sstevel@tonic-gate 			/* for S40 the following would suffice */
28960Sstevel@tonic-gate 			byte = inb(CMOS_DATA);
28970Sstevel@tonic-gate 
28980Sstevel@tonic-gate 			/* power active bar control bit */
28990Sstevel@tonic-gate 			byte |= PAB_CBIT;
29000Sstevel@tonic-gate 			outb(CMOS_DATA, byte);
29010Sstevel@tonic-gate 
29020Sstevel@tonic-gate 			break;
29030Sstevel@tonic-gate 
29040Sstevel@tonic-gate 		case APIC_POWEROFF_VIA_ASPEN_BMC:
29050Sstevel@tonic-gate 			restarts = 0;
29060Sstevel@tonic-gate restart_aspen_bmc:
29070Sstevel@tonic-gate 			if (++restarts == 3)
29080Sstevel@tonic-gate 				break;
29090Sstevel@tonic-gate 			attempts = 0;
29100Sstevel@tonic-gate 			do {
29110Sstevel@tonic-gate 				byte = inb(MISMIC_FLAG_REGISTER);
29120Sstevel@tonic-gate 				byte &= MISMIC_BUSY_MASK;
29130Sstevel@tonic-gate 				if (byte != 0) {
29140Sstevel@tonic-gate 					drv_usecwait(1000);
29150Sstevel@tonic-gate 					if (attempts >= 3)
29160Sstevel@tonic-gate 						goto restart_aspen_bmc;
29170Sstevel@tonic-gate 					++attempts;
29180Sstevel@tonic-gate 				}
29190Sstevel@tonic-gate 			} while (byte != 0);
29200Sstevel@tonic-gate 			outb(MISMIC_CNTL_REGISTER, CC_SMS_GET_STATUS);
29210Sstevel@tonic-gate 			byte = inb(MISMIC_FLAG_REGISTER);
29220Sstevel@tonic-gate 			byte |= 0x1;
29230Sstevel@tonic-gate 			outb(MISMIC_FLAG_REGISTER, byte);
29240Sstevel@tonic-gate 			i = 0;
29250Sstevel@tonic-gate 			for (; i < (sizeof (aspen_bmc)/sizeof (aspen_bmc[0]));
29260Sstevel@tonic-gate 			    i++) {
29270Sstevel@tonic-gate 				attempts = 0;
29280Sstevel@tonic-gate 				do {
29290Sstevel@tonic-gate 					byte = inb(MISMIC_FLAG_REGISTER);
29300Sstevel@tonic-gate 					byte &= MISMIC_BUSY_MASK;
29310Sstevel@tonic-gate 					if (byte != 0) {
29320Sstevel@tonic-gate 						drv_usecwait(1000);
29330Sstevel@tonic-gate 						if (attempts >= 3)
29340Sstevel@tonic-gate 							goto restart_aspen_bmc;
29350Sstevel@tonic-gate 						++attempts;
29360Sstevel@tonic-gate 					}
29370Sstevel@tonic-gate 				} while (byte != 0);
29380Sstevel@tonic-gate 				outb(MISMIC_CNTL_REGISTER, aspen_bmc[i].cntl);
29390Sstevel@tonic-gate 				outb(MISMIC_DATA_REGISTER, aspen_bmc[i].data);
29400Sstevel@tonic-gate 				byte = inb(MISMIC_FLAG_REGISTER);
29410Sstevel@tonic-gate 				byte |= 0x1;
29420Sstevel@tonic-gate 				outb(MISMIC_FLAG_REGISTER, byte);
29430Sstevel@tonic-gate 			}
29440Sstevel@tonic-gate 			break;
29450Sstevel@tonic-gate 
29460Sstevel@tonic-gate 		case APIC_POWEROFF_VIA_SITKA_BMC:
29470Sstevel@tonic-gate 			restarts = 0;
29480Sstevel@tonic-gate restart_sitka_bmc:
29490Sstevel@tonic-gate 			if (++restarts == 3)
29500Sstevel@tonic-gate 				break;
29510Sstevel@tonic-gate 			attempts = 0;
29520Sstevel@tonic-gate 			do {
29530Sstevel@tonic-gate 				byte = inb(SMS_STATUS_REGISTER);
29540Sstevel@tonic-gate 				byte &= SMS_STATE_MASK;
29550Sstevel@tonic-gate 				if ((byte == SMS_READ_STATE) ||
29560Sstevel@tonic-gate 				    (byte == SMS_WRITE_STATE)) {
29570Sstevel@tonic-gate 					drv_usecwait(1000);
29580Sstevel@tonic-gate 					if (attempts >= 3)
29590Sstevel@tonic-gate 						goto restart_sitka_bmc;
29600Sstevel@tonic-gate 					++attempts;
29610Sstevel@tonic-gate 				}
29620Sstevel@tonic-gate 			} while ((byte == SMS_READ_STATE) ||
29630Sstevel@tonic-gate 			    (byte == SMS_WRITE_STATE));
29640Sstevel@tonic-gate 			outb(SMS_COMMAND_REGISTER, SMS_GET_STATUS);
29650Sstevel@tonic-gate 			i = 0;
29660Sstevel@tonic-gate 			for (; i < (sizeof (sitka_bmc)/sizeof (sitka_bmc[0]));
29670Sstevel@tonic-gate 			    i++) {
29680Sstevel@tonic-gate 				attempts = 0;
29690Sstevel@tonic-gate 				do {
29700Sstevel@tonic-gate 					byte = inb(SMS_STATUS_REGISTER);
29710Sstevel@tonic-gate 					byte &= SMS_IBF_MASK;
29720Sstevel@tonic-gate 					if (byte != 0) {
29730Sstevel@tonic-gate 						drv_usecwait(1000);
29740Sstevel@tonic-gate 						if (attempts >= 3)
29750Sstevel@tonic-gate 							goto restart_sitka_bmc;
29760Sstevel@tonic-gate 						++attempts;
29770Sstevel@tonic-gate 					}
29780Sstevel@tonic-gate 				} while (byte != 0);
29790Sstevel@tonic-gate 				outb(sitka_bmc[i].port, sitka_bmc[i].data);
29800Sstevel@tonic-gate 			}
29810Sstevel@tonic-gate 			break;
29820Sstevel@tonic-gate 
29830Sstevel@tonic-gate 		case APIC_POWEROFF_NONE:
29840Sstevel@tonic-gate 
29850Sstevel@tonic-gate 			/* If no APIC direct method, we will try using ACPI */
29860Sstevel@tonic-gate 			if (apic_enable_acpi) {
29870Sstevel@tonic-gate 				if (acpi_poweroff() == 1)
29880Sstevel@tonic-gate 					return;
29890Sstevel@tonic-gate 			} else
29900Sstevel@tonic-gate 				return;
29910Sstevel@tonic-gate 
29920Sstevel@tonic-gate 			break;
29930Sstevel@tonic-gate 	}
29940Sstevel@tonic-gate 	/*
29950Sstevel@tonic-gate 	 * Wait a limited time here for power to go off.
29960Sstevel@tonic-gate 	 * If the power does not go off, then there was a
29970Sstevel@tonic-gate 	 * problem and we should continue to the halt which
29980Sstevel@tonic-gate 	 * prints a message for the user to press a key to
29990Sstevel@tonic-gate 	 * reboot.
30000Sstevel@tonic-gate 	 */
30010Sstevel@tonic-gate 	drv_usecwait(7000000); /* wait seven seconds */
30020Sstevel@tonic-gate 
30030Sstevel@tonic-gate }
30040Sstevel@tonic-gate 
30050Sstevel@tonic-gate /*
30060Sstevel@tonic-gate  * Try and disable all interrupts. We just assign interrupts to other
30070Sstevel@tonic-gate  * processors based on policy. If any were bound by user request, we
30080Sstevel@tonic-gate  * let them continue and return failure. We do not bother to check
30090Sstevel@tonic-gate  * for cache affinity while rebinding.
30100Sstevel@tonic-gate  */
30110Sstevel@tonic-gate 
30120Sstevel@tonic-gate static int
30130Sstevel@tonic-gate apic_disable_intr(processorid_t cpun)
30140Sstevel@tonic-gate {
30150Sstevel@tonic-gate 	int bind_cpu = 0, i, hardbound = 0, iflag;
30160Sstevel@tonic-gate 	apic_irq_t *irq_ptr;
30170Sstevel@tonic-gate 
30180Sstevel@tonic-gate 	iflag = intr_clear();
30190Sstevel@tonic-gate 	lock_set(&apic_ioapic_lock);
30200Sstevel@tonic-gate 	apic_cpus[cpun].aci_status &= ~APIC_CPU_INTR_ENABLE;
30210Sstevel@tonic-gate 	lock_clear(&apic_ioapic_lock);
30220Sstevel@tonic-gate 	intr_restore(iflag);
30230Sstevel@tonic-gate 	apic_cpus[cpun].aci_curipl = 0;
30240Sstevel@tonic-gate 	i = apic_min_device_irq;
30250Sstevel@tonic-gate 	for (; i <= apic_max_device_irq; i++) {
30260Sstevel@tonic-gate 		/*
30270Sstevel@tonic-gate 		 * If there are bound interrupts on this cpu, then
30280Sstevel@tonic-gate 		 * rebind them to other processors.
30290Sstevel@tonic-gate 		 */
30300Sstevel@tonic-gate 		if ((irq_ptr = apic_irq_table[i]) != NULL) {
30310Sstevel@tonic-gate 			ASSERT((irq_ptr->airq_temp_cpu == IRQ_UNBOUND) ||
30320Sstevel@tonic-gate 			    (irq_ptr->airq_temp_cpu == IRQ_UNINIT) ||
30330Sstevel@tonic-gate 			    ((irq_ptr->airq_temp_cpu & ~IRQ_USER_BOUND) <
30340Sstevel@tonic-gate 			    apic_nproc));
30350Sstevel@tonic-gate 
30360Sstevel@tonic-gate 			if (irq_ptr->airq_temp_cpu == (cpun | IRQ_USER_BOUND)) {
30370Sstevel@tonic-gate 				hardbound = 1;
30380Sstevel@tonic-gate 				continue;
30390Sstevel@tonic-gate 			}
30400Sstevel@tonic-gate 
30410Sstevel@tonic-gate 			if (irq_ptr->airq_temp_cpu == cpun) {
30420Sstevel@tonic-gate 				do {
30430Sstevel@tonic-gate 					apic_next_bind_cpu += 2;
30440Sstevel@tonic-gate 					bind_cpu = apic_next_bind_cpu / 2;
30450Sstevel@tonic-gate 					if (bind_cpu >= apic_nproc) {
30460Sstevel@tonic-gate 						apic_next_bind_cpu = 1;
30470Sstevel@tonic-gate 						bind_cpu = 0;
30480Sstevel@tonic-gate 
30490Sstevel@tonic-gate 					}
30500Sstevel@tonic-gate 				} while (apic_rebind_all(irq_ptr, bind_cpu, 1));
30510Sstevel@tonic-gate 			}
30520Sstevel@tonic-gate 		}
30530Sstevel@tonic-gate 	}
30540Sstevel@tonic-gate 	if (hardbound) {
30550Sstevel@tonic-gate 		cmn_err(CE_WARN, "Could not disable interrupts on %d"
30560Sstevel@tonic-gate 		    "due to user bound interrupts", cpun);
30570Sstevel@tonic-gate 		return (PSM_FAILURE);
30580Sstevel@tonic-gate 	}
30590Sstevel@tonic-gate 	else
30600Sstevel@tonic-gate 		return (PSM_SUCCESS);
30610Sstevel@tonic-gate }
30620Sstevel@tonic-gate 
30630Sstevel@tonic-gate static void
30640Sstevel@tonic-gate apic_enable_intr(processorid_t cpun)
30650Sstevel@tonic-gate {
30660Sstevel@tonic-gate 	int	i, iflag;
30670Sstevel@tonic-gate 	apic_irq_t *irq_ptr;
30680Sstevel@tonic-gate 
30690Sstevel@tonic-gate 	iflag = intr_clear();
30700Sstevel@tonic-gate 	lock_set(&apic_ioapic_lock);
30710Sstevel@tonic-gate 	apic_cpus[cpun].aci_status |= APIC_CPU_INTR_ENABLE;
30720Sstevel@tonic-gate 	lock_clear(&apic_ioapic_lock);
30730Sstevel@tonic-gate 	intr_restore(iflag);
30740Sstevel@tonic-gate 
30750Sstevel@tonic-gate 	i = apic_min_device_irq;
30760Sstevel@tonic-gate 	for (i = apic_min_device_irq; i <= apic_max_device_irq; i++) {
30770Sstevel@tonic-gate 		if ((irq_ptr = apic_irq_table[i]) != NULL) {
30780Sstevel@tonic-gate 			if ((irq_ptr->airq_cpu & ~IRQ_USER_BOUND) == cpun) {
30790Sstevel@tonic-gate 				(void) apic_rebind_all(irq_ptr,
30800Sstevel@tonic-gate 				    irq_ptr->airq_cpu, 1);
30810Sstevel@tonic-gate 			}
30820Sstevel@tonic-gate 		}
30830Sstevel@tonic-gate 	}
30840Sstevel@tonic-gate }
30850Sstevel@tonic-gate 
30860Sstevel@tonic-gate /*
30870Sstevel@tonic-gate  * apic_introp_xlate() replaces apic_translate_irq() and is
30880Sstevel@tonic-gate  * called only from apic_intr_ops().  With the new ADII framework,
30890Sstevel@tonic-gate  * the priority can no longer be retrived through i_ddi_get_intrspec().
30900Sstevel@tonic-gate  * It has to be passed in from the caller.
30910Sstevel@tonic-gate  */
30920Sstevel@tonic-gate int
30930Sstevel@tonic-gate apic_introp_xlate(dev_info_t *dip, struct intrspec *ispec, int type)
30940Sstevel@tonic-gate {
30950Sstevel@tonic-gate 	char dev_type[16];
30960Sstevel@tonic-gate 	int dev_len, pci_irq, newirq, bustype, devid, busid, i;
30970Sstevel@tonic-gate 	int irqno = ispec->intrspec_vec;
30980Sstevel@tonic-gate 	ddi_acc_handle_t cfg_handle;
30990Sstevel@tonic-gate 	uchar_t ipin;
31000Sstevel@tonic-gate 	struct apic_io_intr *intrp;
31010Sstevel@tonic-gate 	iflag_t intr_flag;
31020Sstevel@tonic-gate 	APIC_HEADER	*hp;
31030Sstevel@tonic-gate 	MADT_INTERRUPT_OVERRIDE	*isop;
31040Sstevel@tonic-gate 	apic_irq_t *airqp;
31050Sstevel@tonic-gate 
31060Sstevel@tonic-gate 	DDI_INTR_IMPLDBG((CE_CONT, "apic_introp_xlate: dip=0x%p name=%s "
31070Sstevel@tonic-gate 	    "type=%d irqno=0x%x\n", (void *)dip, ddi_get_name(dip), type,
31080Sstevel@tonic-gate 	    irqno));
31090Sstevel@tonic-gate 
31100Sstevel@tonic-gate 	if (DDI_INTR_IS_MSI_OR_MSIX(type)) {
31110Sstevel@tonic-gate 		if ((airqp = apic_find_irq(dip, ispec, type)) != NULL)
31120Sstevel@tonic-gate 			return (apic_vector_to_irq[airqp->airq_vector]);
31130Sstevel@tonic-gate 		return (apic_setup_irq_table(dip, irqno, NULL, ispec,
31140Sstevel@tonic-gate 		    NULL, type));
31150Sstevel@tonic-gate 	}
31160Sstevel@tonic-gate 
31170Sstevel@tonic-gate 	bustype = 0;
31180Sstevel@tonic-gate 
31190Sstevel@tonic-gate 	/* check if we have already translated this irq */
31200Sstevel@tonic-gate 	mutex_enter(&airq_mutex);
31210Sstevel@tonic-gate 	newirq = apic_min_device_irq;
31220Sstevel@tonic-gate 	for (; newirq <= apic_max_device_irq; newirq++) {
31230Sstevel@tonic-gate 		airqp = apic_irq_table[newirq];
31240Sstevel@tonic-gate 		while (airqp) {
31250Sstevel@tonic-gate 			if ((airqp->airq_dip == dip) &&
31260Sstevel@tonic-gate 			    (airqp->airq_origirq == irqno) &&
31270Sstevel@tonic-gate 			    (airqp->airq_mps_intr_index != FREE_INDEX)) {
31280Sstevel@tonic-gate 
31290Sstevel@tonic-gate 				mutex_exit(&airq_mutex);
31300Sstevel@tonic-gate 				return (VIRTIRQ(newirq, airqp->airq_share_id));
31310Sstevel@tonic-gate 			}
31320Sstevel@tonic-gate 			airqp = airqp->airq_next;
31330Sstevel@tonic-gate 		}
31340Sstevel@tonic-gate 	}
31350Sstevel@tonic-gate 	mutex_exit(&airq_mutex);
31360Sstevel@tonic-gate 
31370Sstevel@tonic-gate 	if (apic_defconf)
31380Sstevel@tonic-gate 		goto defconf;
31390Sstevel@tonic-gate 
31400Sstevel@tonic-gate 	if ((dip == NULL) || (!apic_irq_translate && !apic_enable_acpi))
31410Sstevel@tonic-gate 		goto nonpci;
31420Sstevel@tonic-gate 
31430Sstevel@tonic-gate 	dev_len = sizeof (dev_type);
3144506Scth 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, ddi_get_parent(dip),
31450Sstevel@tonic-gate 	    DDI_PROP_DONTPASS, "device_type", (caddr_t)dev_type,
31460Sstevel@tonic-gate 	    &dev_len) != DDI_PROP_SUCCESS) {
31470Sstevel@tonic-gate 		goto nonpci;
31480Sstevel@tonic-gate 	}
31490Sstevel@tonic-gate 
3150881Sjohnny 	if ((strcmp(dev_type, "pci") == 0) ||
3151881Sjohnny 	    (strcmp(dev_type, "pciex") == 0)) {
31520Sstevel@tonic-gate 		/* pci device */
31530Sstevel@tonic-gate 		if (acpica_get_bdf(dip, &busid, &devid, NULL) != 0)
31540Sstevel@tonic-gate 			goto nonpci;
31550Sstevel@tonic-gate 		if (busid == 0 && apic_pci_bus_total == 1)
31560Sstevel@tonic-gate 			busid = (int)apic_single_pci_busid;
31570Sstevel@tonic-gate 
31580Sstevel@tonic-gate 		if (pci_config_setup(dip, &cfg_handle) != DDI_SUCCESS)
31590Sstevel@tonic-gate 			goto nonpci;
31600Sstevel@tonic-gate 		ipin = pci_config_get8(cfg_handle, PCI_CONF_IPIN) - PCI_INTA;
31610Sstevel@tonic-gate 		pci_config_teardown(&cfg_handle);
31620Sstevel@tonic-gate 		if (apic_enable_acpi && !apic_use_acpi_madt_only) {
31630Sstevel@tonic-gate 			if (apic_acpi_translate_pci_irq(dip, busid, devid,
31640Sstevel@tonic-gate 			    ipin, &pci_irq, &intr_flag) != ACPI_PSM_SUCCESS)
31650Sstevel@tonic-gate 				goto nonpci;
31660Sstevel@tonic-gate 
31670Sstevel@tonic-gate 			intr_flag.bustype = BUS_PCI;
31680Sstevel@tonic-gate 			if ((newirq = apic_setup_irq_table(dip, pci_irq, NULL,
31690Sstevel@tonic-gate 			    ispec, &intr_flag, type)) == -1)
31700Sstevel@tonic-gate 				goto nonpci;
31710Sstevel@tonic-gate 			return (newirq);
31720Sstevel@tonic-gate 		} else {
31730Sstevel@tonic-gate 			pci_irq = ((devid & 0x1f) << 2) | (ipin & 0x3);
31740Sstevel@tonic-gate 			if ((intrp = apic_find_io_intr_w_busid(pci_irq, busid))
31750Sstevel@tonic-gate 			    == NULL) {
31760Sstevel@tonic-gate 				if ((pci_irq = apic_handle_pci_pci_bridge(dip,
31770Sstevel@tonic-gate 				    devid, ipin, &intrp)) == -1)
31780Sstevel@tonic-gate 					goto nonpci;
31790Sstevel@tonic-gate 			}
31800Sstevel@tonic-gate 			if ((newirq = apic_setup_irq_table(dip, pci_irq, intrp,
31810Sstevel@tonic-gate 			    ispec, NULL, type)) == -1)
31820Sstevel@tonic-gate 				goto nonpci;
31830Sstevel@tonic-gate 			return (newirq);
31840Sstevel@tonic-gate 		}
31850Sstevel@tonic-gate 	} else if (strcmp(dev_type, "isa") == 0)
31860Sstevel@tonic-gate 		bustype = BUS_ISA;
31870Sstevel@tonic-gate 	else if (strcmp(dev_type, "eisa") == 0)
31880Sstevel@tonic-gate 		bustype = BUS_EISA;
31890Sstevel@tonic-gate 
31900Sstevel@tonic-gate nonpci:
31910Sstevel@tonic-gate 	if (apic_enable_acpi && !apic_use_acpi_madt_only) {
31920Sstevel@tonic-gate 		/* search iso entries first */
31930Sstevel@tonic-gate 		if (acpi_iso_cnt != 0) {
31940Sstevel@tonic-gate 			hp = (APIC_HEADER *)acpi_isop;
31950Sstevel@tonic-gate 			i = 0;
31960Sstevel@tonic-gate 			while (i < acpi_iso_cnt) {
31970Sstevel@tonic-gate 				if (hp->Type == APIC_XRUPT_OVERRIDE) {
31980Sstevel@tonic-gate 					isop = (MADT_INTERRUPT_OVERRIDE *)hp;
31990Sstevel@tonic-gate 					if (isop->Bus == 0 &&
32000Sstevel@tonic-gate 					    isop->Source == irqno) {
32010Sstevel@tonic-gate 						newirq = isop->Interrupt;
32020Sstevel@tonic-gate 						intr_flag.intr_po =
32030Sstevel@tonic-gate 						    isop->Polarity;
32040Sstevel@tonic-gate 						intr_flag.intr_el =
32050Sstevel@tonic-gate 						    isop->TriggerMode;
32060Sstevel@tonic-gate 						intr_flag.bustype = BUS_ISA;
32070Sstevel@tonic-gate 
32080Sstevel@tonic-gate 						return (apic_setup_irq_table(
32090Sstevel@tonic-gate 						    dip, newirq, NULL, ispec,
32100Sstevel@tonic-gate 						    &intr_flag, type));
32110Sstevel@tonic-gate 
32120Sstevel@tonic-gate 					}
32130Sstevel@tonic-gate 					i++;
32140Sstevel@tonic-gate 				}
32150Sstevel@tonic-gate 				hp = (APIC_HEADER *)(((char *)hp) +
32160Sstevel@tonic-gate 				    hp->Length);
32170Sstevel@tonic-gate 			}
32180Sstevel@tonic-gate 		}
32190Sstevel@tonic-gate 		intr_flag.intr_po = INTR_PO_ACTIVE_HIGH;
32200Sstevel@tonic-gate 		intr_flag.intr_el = INTR_EL_EDGE;
32210Sstevel@tonic-gate 		intr_flag.bustype = BUS_ISA;
32220Sstevel@tonic-gate 		return (apic_setup_irq_table(dip, irqno, NULL, ispec,
32230Sstevel@tonic-gate 		    &intr_flag, type));
32240Sstevel@tonic-gate 	} else {
32250Sstevel@tonic-gate 		if (bustype == 0)
32260Sstevel@tonic-gate 			bustype = eisa_level_intr_mask ? BUS_EISA : BUS_ISA;
32270Sstevel@tonic-gate 		for (i = 0; i < 2; i++) {
32280Sstevel@tonic-gate 			if (((busid = apic_find_bus_id(bustype)) != -1) &&
32290Sstevel@tonic-gate 			    ((intrp = apic_find_io_intr_w_busid(irqno, busid))
32300Sstevel@tonic-gate 			    != NULL)) {
32310Sstevel@tonic-gate 				if ((newirq = apic_setup_irq_table(dip, irqno,
32320Sstevel@tonic-gate 				    intrp, ispec, NULL, type)) != -1) {
32330Sstevel@tonic-gate 					return (newirq);
32340Sstevel@tonic-gate 				}
32350Sstevel@tonic-gate 				goto defconf;
32360Sstevel@tonic-gate 			}
32370Sstevel@tonic-gate 			bustype = (bustype == BUS_EISA) ? BUS_ISA : BUS_EISA;
32380Sstevel@tonic-gate 		}
32390Sstevel@tonic-gate 	}
32400Sstevel@tonic-gate 
32410Sstevel@tonic-gate /* MPS default configuration */
32420Sstevel@tonic-gate defconf:
32430Sstevel@tonic-gate 	newirq = apic_setup_irq_table(dip, irqno, NULL, ispec, NULL, type);
32440Sstevel@tonic-gate 	if (newirq == -1)
32450Sstevel@tonic-gate 		return (newirq);
32460Sstevel@tonic-gate 	ASSERT(IRQINDEX(newirq) == irqno);
32470Sstevel@tonic-gate 	ASSERT(apic_irq_table[irqno]);
32480Sstevel@tonic-gate 	return (newirq);
32490Sstevel@tonic-gate }
32500Sstevel@tonic-gate 
32510Sstevel@tonic-gate 
32520Sstevel@tonic-gate 
32530Sstevel@tonic-gate 
32540Sstevel@tonic-gate 
32550Sstevel@tonic-gate 
32560Sstevel@tonic-gate /*
32570Sstevel@tonic-gate  * On machines with PCI-PCI bridges, a device behind a PCI-PCI bridge
32580Sstevel@tonic-gate  * needs special handling.  We may need to chase up the device tree,
32590Sstevel@tonic-gate  * using the PCI-PCI Bridge specification's "rotating IPIN assumptions",
32600Sstevel@tonic-gate  * to find the IPIN at the root bus that relates to the IPIN on the
32610Sstevel@tonic-gate  * subsidiary bus (for ACPI or MP).  We may, however, have an entry
32620Sstevel@tonic-gate  * in the MP table or the ACPI namespace for this device itself.
32630Sstevel@tonic-gate  * We handle both cases in the search below.
32640Sstevel@tonic-gate  */
32650Sstevel@tonic-gate /* this is the non-acpi version */
32660Sstevel@tonic-gate static int
32670Sstevel@tonic-gate apic_handle_pci_pci_bridge(dev_info_t *idip, int child_devno, int child_ipin,
32680Sstevel@tonic-gate 			struct apic_io_intr **intrp)
32690Sstevel@tonic-gate {
32700Sstevel@tonic-gate 	dev_info_t *dipp, *dip;
32710Sstevel@tonic-gate 	int pci_irq;
32720Sstevel@tonic-gate 	ddi_acc_handle_t cfg_handle;
32730Sstevel@tonic-gate 	int bridge_devno, bridge_bus;
32740Sstevel@tonic-gate 	int ipin;
32750Sstevel@tonic-gate 
32760Sstevel@tonic-gate 	dip = idip;
32770Sstevel@tonic-gate 
32780Sstevel@tonic-gate 	/*CONSTCOND*/
32790Sstevel@tonic-gate 	while (1) {
32800Sstevel@tonic-gate 		if ((dipp = ddi_get_parent(dip)) == (dev_info_t *)NULL)
32810Sstevel@tonic-gate 			return (-1);
32820Sstevel@tonic-gate 		if ((pci_config_setup(dipp, &cfg_handle) == DDI_SUCCESS) &&
32830Sstevel@tonic-gate 		    (pci_config_get8(cfg_handle, PCI_CONF_BASCLASS) ==
32840Sstevel@tonic-gate 		    PCI_CLASS_BRIDGE) && (pci_config_get8(cfg_handle,
32850Sstevel@tonic-gate 		    PCI_CONF_SUBCLASS) == PCI_BRIDGE_PCI)) {
32860Sstevel@tonic-gate 			pci_config_teardown(&cfg_handle);
32870Sstevel@tonic-gate 			if (acpica_get_bdf(dipp, &bridge_bus, &bridge_devno,
32880Sstevel@tonic-gate 			    NULL) != 0)
32890Sstevel@tonic-gate 				return (-1);
32900Sstevel@tonic-gate 			/*
32910Sstevel@tonic-gate 			 * This is the rotating scheme that Compaq is using
32920Sstevel@tonic-gate 			 * and documented in the pci to pci spec.  Also, if
32930Sstevel@tonic-gate 			 * the pci to pci bridge is behind another pci to
32940Sstevel@tonic-gate 			 * pci bridge, then it need to keep transversing
32950Sstevel@tonic-gate 			 * up until an interrupt entry is found or reach
32960Sstevel@tonic-gate 			 * the top of the tree
32970Sstevel@tonic-gate 			 */
32980Sstevel@tonic-gate 			ipin = (child_devno + child_ipin) % PCI_INTD;
32990Sstevel@tonic-gate 				if (bridge_bus == 0 && apic_pci_bus_total == 1)
33000Sstevel@tonic-gate 					bridge_bus = (int)apic_single_pci_busid;
33010Sstevel@tonic-gate 				pci_irq = ((bridge_devno & 0x1f) << 2) |
33020Sstevel@tonic-gate 				    (ipin & 0x3);
33030Sstevel@tonic-gate 				if ((*intrp = apic_find_io_intr_w_busid(pci_irq,
33040Sstevel@tonic-gate 				    bridge_bus)) != NULL) {
33050Sstevel@tonic-gate 					return (pci_irq);
33060Sstevel@tonic-gate 				}
33070Sstevel@tonic-gate 			dip = dipp;
33080Sstevel@tonic-gate 			child_devno = bridge_devno;
33090Sstevel@tonic-gate 			child_ipin = ipin;
33100Sstevel@tonic-gate 		} else
33110Sstevel@tonic-gate 			return (-1);
33120Sstevel@tonic-gate 	}
33130Sstevel@tonic-gate 	/*LINTED: function will not fall off the bottom */
33140Sstevel@tonic-gate }
33150Sstevel@tonic-gate 
33160Sstevel@tonic-gate 
33170Sstevel@tonic-gate 
33180Sstevel@tonic-gate 
33190Sstevel@tonic-gate static uchar_t
33200Sstevel@tonic-gate acpi_find_ioapic(int irq)
33210Sstevel@tonic-gate {
33220Sstevel@tonic-gate 	int i;
33230Sstevel@tonic-gate 
33240Sstevel@tonic-gate 	for (i = 0; i < apic_io_max; i++) {
33250Sstevel@tonic-gate 		if (irq >= apic_io_vectbase[i] && irq <= apic_io_vectend[i])
33260Sstevel@tonic-gate 			return (i);
33270Sstevel@tonic-gate 	}
33280Sstevel@tonic-gate 	return (0xFF);	/* shouldn't happen */
33290Sstevel@tonic-gate }
33300Sstevel@tonic-gate 
33310Sstevel@tonic-gate /*
33320Sstevel@tonic-gate  * See if two irqs are compatible for sharing a vector.
33330Sstevel@tonic-gate  * Currently we only support sharing of PCI devices.
33340Sstevel@tonic-gate  */
33350Sstevel@tonic-gate static int
33360Sstevel@tonic-gate acpi_intr_compatible(iflag_t iflag1, iflag_t iflag2)
33370Sstevel@tonic-gate {
33380Sstevel@tonic-gate 	uint_t	level1, po1;
33390Sstevel@tonic-gate 	uint_t	level2, po2;
33400Sstevel@tonic-gate 
33410Sstevel@tonic-gate 	/* Assume active high by default */
33420Sstevel@tonic-gate 	po1 = 0;
33430Sstevel@tonic-gate 	po2 = 0;
33440Sstevel@tonic-gate 
33450Sstevel@tonic-gate 	if (iflag1.bustype != iflag2.bustype || iflag1.bustype != BUS_PCI)
33460Sstevel@tonic-gate 		return (0);
33470Sstevel@tonic-gate 
33480Sstevel@tonic-gate 	if (iflag1.intr_el == INTR_EL_CONFORM)
33490Sstevel@tonic-gate 		level1 = AV_LEVEL;
33500Sstevel@tonic-gate 	else
33510Sstevel@tonic-gate 		level1 = (iflag1.intr_el == INTR_EL_LEVEL) ? AV_LEVEL : 0;
33520Sstevel@tonic-gate 
33530Sstevel@tonic-gate 	if (level1 && ((iflag1.intr_po == INTR_PO_ACTIVE_LOW) ||
33540Sstevel@tonic-gate 	    (iflag1.intr_po == INTR_PO_CONFORM)))
33550Sstevel@tonic-gate 		po1 = AV_ACTIVE_LOW;
33560Sstevel@tonic-gate 
33570Sstevel@tonic-gate 	if (iflag2.intr_el == INTR_EL_CONFORM)
33580Sstevel@tonic-gate 		level2 = AV_LEVEL;
33590Sstevel@tonic-gate 	else
33600Sstevel@tonic-gate 		level2 = (iflag2.intr_el == INTR_EL_LEVEL) ? AV_LEVEL : 0;
33610Sstevel@tonic-gate 
33620Sstevel@tonic-gate 	if (level2 && ((iflag2.intr_po == INTR_PO_ACTIVE_LOW) ||
33630Sstevel@tonic-gate 	    (iflag2.intr_po == INTR_PO_CONFORM)))
33640Sstevel@tonic-gate 		po2 = AV_ACTIVE_LOW;
33650Sstevel@tonic-gate 
33660Sstevel@tonic-gate 	if ((level1 == level2) && (po1 == po2))
33670Sstevel@tonic-gate 		return (1);
33680Sstevel@tonic-gate 
33690Sstevel@tonic-gate 	return (0);
33700Sstevel@tonic-gate }
33710Sstevel@tonic-gate 
33720Sstevel@tonic-gate /*
33730Sstevel@tonic-gate  * Attempt to share vector with someone else
33740Sstevel@tonic-gate  */
33750Sstevel@tonic-gate static int
33760Sstevel@tonic-gate apic_share_vector(int irqno, iflag_t *intr_flagp, short intr_index, int ipl,
33770Sstevel@tonic-gate 	uchar_t ioapicindex, uchar_t ipin, apic_irq_t **irqptrp)
33780Sstevel@tonic-gate {
33790Sstevel@tonic-gate #ifdef DEBUG
33800Sstevel@tonic-gate 	apic_irq_t *tmpirqp = NULL;
33810Sstevel@tonic-gate #endif /* DEBUG */
33820Sstevel@tonic-gate 	apic_irq_t *irqptr, dummyirq;
33830Sstevel@tonic-gate 	int	newirq, chosen_irq = -1, share = 127;
33840Sstevel@tonic-gate 	int	lowest, highest, i;
33850Sstevel@tonic-gate 	uchar_t	share_id;
33860Sstevel@tonic-gate 
33870Sstevel@tonic-gate 	DDI_INTR_IMPLDBG((CE_CONT, "apic_share_vector: irqno=0x%x "
33880Sstevel@tonic-gate 	    "intr_index=0x%x ipl=0x%x\n", irqno, intr_index, ipl));
33890Sstevel@tonic-gate 
33900Sstevel@tonic-gate 	highest = apic_ipltopri[ipl] + APIC_VECTOR_MASK;
33910Sstevel@tonic-gate 	lowest = apic_ipltopri[ipl-1] + APIC_VECTOR_PER_IPL;
33920Sstevel@tonic-gate 
33930Sstevel@tonic-gate 	if (highest < lowest) /* Both ipl and ipl-1 map to same pri */
33940Sstevel@tonic-gate 		lowest -= APIC_VECTOR_PER_IPL;
33950Sstevel@tonic-gate 	dummyirq.airq_mps_intr_index = intr_index;
33960Sstevel@tonic-gate 	dummyirq.airq_ioapicindex = ioapicindex;
33970Sstevel@tonic-gate 	dummyirq.airq_intin_no = ipin;
33980Sstevel@tonic-gate 	if (intr_flagp)
33990Sstevel@tonic-gate 		dummyirq.airq_iflag = *intr_flagp;
34000Sstevel@tonic-gate 	apic_record_rdt_entry(&dummyirq, irqno);
34010Sstevel@tonic-gate 	for (i = lowest; i <= highest; i++) {
34020Sstevel@tonic-gate 		newirq = apic_vector_to_irq[i];
34030Sstevel@tonic-gate 		if (newirq == APIC_RESV_IRQ)
34040Sstevel@tonic-gate 			continue;
34050Sstevel@tonic-gate 		irqptr = apic_irq_table[newirq];
34060Sstevel@tonic-gate 
34070Sstevel@tonic-gate 		if ((dummyirq.airq_rdt_entry & 0xFF00) !=
34080Sstevel@tonic-gate 		    (irqptr->airq_rdt_entry & 0xFF00))
34090Sstevel@tonic-gate 			/* not compatible */
34100Sstevel@tonic-gate 			continue;
34110Sstevel@tonic-gate 
34120Sstevel@tonic-gate 		if (irqptr->airq_share < share) {
34130Sstevel@tonic-gate 			share = irqptr->airq_share;
34140Sstevel@tonic-gate 			chosen_irq = newirq;
34150Sstevel@tonic-gate 		}
34160Sstevel@tonic-gate 	}
34170Sstevel@tonic-gate 	if (chosen_irq != -1) {
34180Sstevel@tonic-gate 		/*
34190Sstevel@tonic-gate 		 * Assign a share id which is free or which is larger
34200Sstevel@tonic-gate 		 * than the largest one.
34210Sstevel@tonic-gate 		 */
34220Sstevel@tonic-gate 		share_id = 1;
34230Sstevel@tonic-gate 		mutex_enter(&airq_mutex);
34240Sstevel@tonic-gate 		irqptr = apic_irq_table[chosen_irq];
34250Sstevel@tonic-gate 		while (irqptr) {
34260Sstevel@tonic-gate 			if (irqptr->airq_mps_intr_index == FREE_INDEX) {
34270Sstevel@tonic-gate 				share_id = irqptr->airq_share_id;
34280Sstevel@tonic-gate 				break;
34290Sstevel@tonic-gate 			}
34300Sstevel@tonic-gate 			if (share_id <= irqptr->airq_share_id)
34310Sstevel@tonic-gate 				share_id = irqptr->airq_share_id + 1;
34320Sstevel@tonic-gate #ifdef DEBUG
34330Sstevel@tonic-gate 			tmpirqp = irqptr;
34340Sstevel@tonic-gate #endif /* DEBUG */
34350Sstevel@tonic-gate 			irqptr = irqptr->airq_next;
34360Sstevel@tonic-gate 		}
34370Sstevel@tonic-gate 		if (!irqptr) {
34380Sstevel@tonic-gate 			irqptr = kmem_zalloc(sizeof (apic_irq_t), KM_SLEEP);
34390Sstevel@tonic-gate 			irqptr->airq_temp_cpu = IRQ_UNINIT;
34400Sstevel@tonic-gate 			irqptr->airq_next =
34410Sstevel@tonic-gate 			    apic_irq_table[chosen_irq]->airq_next;
34420Sstevel@tonic-gate 			apic_irq_table[chosen_irq]->airq_next = irqptr;
34430Sstevel@tonic-gate #ifdef	DEBUG
34440Sstevel@tonic-gate 			tmpirqp = apic_irq_table[chosen_irq];
34450Sstevel@tonic-gate #endif /* DEBUG */
34460Sstevel@tonic-gate 		}
34470Sstevel@tonic-gate 		irqptr->airq_mps_intr_index = intr_index;
34480Sstevel@tonic-gate 		irqptr->airq_ioapicindex = ioapicindex;
34490Sstevel@tonic-gate 		irqptr->airq_intin_no = ipin;
34500Sstevel@tonic-gate 		if (intr_flagp)
34510Sstevel@tonic-gate 			irqptr->airq_iflag = *intr_flagp;
34520Sstevel@tonic-gate 		irqptr->airq_vector = apic_irq_table[chosen_irq]->airq_vector;
34530Sstevel@tonic-gate 		irqptr->airq_share_id = share_id;
34540Sstevel@tonic-gate 		apic_record_rdt_entry(irqptr, irqno);
34550Sstevel@tonic-gate 		*irqptrp = irqptr;
34560Sstevel@tonic-gate #ifdef	DEBUG
34570Sstevel@tonic-gate 		/* shuffle the pointers to test apic_delspl path */
34580Sstevel@tonic-gate 		if (tmpirqp) {
34590Sstevel@tonic-gate 			tmpirqp->airq_next = irqptr->airq_next;
34600Sstevel@tonic-gate 			irqptr->airq_next = apic_irq_table[chosen_irq];
34610Sstevel@tonic-gate 			apic_irq_table[chosen_irq] = irqptr;
34620Sstevel@tonic-gate 		}
34630Sstevel@tonic-gate #endif /* DEBUG */
34640Sstevel@tonic-gate 		mutex_exit(&airq_mutex);
34650Sstevel@tonic-gate 		return (VIRTIRQ(chosen_irq, share_id));
34660Sstevel@tonic-gate 	}
34670Sstevel@tonic-gate 	return (-1);
34680Sstevel@tonic-gate }
34690Sstevel@tonic-gate 
34700Sstevel@tonic-gate /*
34710Sstevel@tonic-gate  *
34720Sstevel@tonic-gate  */
34730Sstevel@tonic-gate static int
34740Sstevel@tonic-gate apic_setup_irq_table(dev_info_t *dip, int irqno, struct apic_io_intr *intrp,
34750Sstevel@tonic-gate     struct intrspec *ispec, iflag_t *intr_flagp, int type)
34760Sstevel@tonic-gate {
34770Sstevel@tonic-gate 	int origirq = ispec->intrspec_vec;
34780Sstevel@tonic-gate 	uchar_t ipl = ispec->intrspec_pri;
34790Sstevel@tonic-gate 	int	newirq, intr_index;
34800Sstevel@tonic-gate 	uchar_t	ipin, ioapic, ioapicindex, vector;
34810Sstevel@tonic-gate 	apic_irq_t *irqptr;
34820Sstevel@tonic-gate 	major_t	major;
34830Sstevel@tonic-gate 	dev_info_t	*sdip;
34840Sstevel@tonic-gate 
34850Sstevel@tonic-gate 	DDI_INTR_IMPLDBG((CE_CONT, "apic_setup_irq_table: dip=0x%p type=%d "
34860Sstevel@tonic-gate 	    "irqno=0x%x origirq=0x%x\n", (void *)dip, type, irqno, origirq));
34870Sstevel@tonic-gate 
34880Sstevel@tonic-gate 	ASSERT(ispec != NULL);
34890Sstevel@tonic-gate 
34900Sstevel@tonic-gate 	major =  (dip != NULL) ? ddi_name_to_major(ddi_get_name(dip)) : 0;
34910Sstevel@tonic-gate 
34920Sstevel@tonic-gate 	if (DDI_INTR_IS_MSI_OR_MSIX(type)) {
34930Sstevel@tonic-gate 		/* MSI/X doesn't need to setup ioapic stuffs */
34940Sstevel@tonic-gate 		ioapicindex = 0xff;
34950Sstevel@tonic-gate 		ioapic = 0xff;
34960Sstevel@tonic-gate 		ipin = (uchar_t)0xff;
34970Sstevel@tonic-gate 		intr_index = (type == DDI_INTR_TYPE_MSI) ? MSI_INDEX :
34980Sstevel@tonic-gate 		    MSIX_INDEX;
34990Sstevel@tonic-gate 		mutex_enter(&airq_mutex);
3500881Sjohnny 		if ((irqno = apic_allocate_irq(apic_first_avail_irq)) == -1) {
35010Sstevel@tonic-gate 			mutex_exit(&airq_mutex);
35020Sstevel@tonic-gate 			/* need an irq for MSI/X to index into autovect[] */
35030Sstevel@tonic-gate 			cmn_err(CE_WARN, "No interrupt irq: %s instance %d",
35040Sstevel@tonic-gate 			    ddi_get_name(dip), ddi_get_instance(dip));
35050Sstevel@tonic-gate 			return (-1);
35060Sstevel@tonic-gate 		}
35070Sstevel@tonic-gate 		mutex_exit(&airq_mutex);
35080Sstevel@tonic-gate 
35090Sstevel@tonic-gate 	} else if (intrp != NULL) {
35100Sstevel@tonic-gate 		intr_index = (int)(intrp - apic_io_intrp);
35110Sstevel@tonic-gate 		ioapic = intrp->intr_destid;
35120Sstevel@tonic-gate 		ipin = intrp->intr_destintin;
35130Sstevel@tonic-gate 		/* Find ioapicindex. If destid was ALL, we will exit with 0. */
35140Sstevel@tonic-gate 		for (ioapicindex = apic_io_max - 1; ioapicindex; ioapicindex--)
35150Sstevel@tonic-gate 			if (apic_io_id[ioapicindex] == ioapic)
35160Sstevel@tonic-gate 				break;
35170Sstevel@tonic-gate 		ASSERT((ioapic == apic_io_id[ioapicindex]) ||
35180Sstevel@tonic-gate 		    (ioapic == INTR_ALL_APIC));
35190Sstevel@tonic-gate 
35200Sstevel@tonic-gate 		/* check whether this intin# has been used by another irqno */
35210Sstevel@tonic-gate 		if ((newirq = apic_find_intin(ioapicindex, ipin)) != -1) {
35220Sstevel@tonic-gate 			return (newirq);
35230Sstevel@tonic-gate 		}
35240Sstevel@tonic-gate 
35250Sstevel@tonic-gate 	} else if (intr_flagp != NULL) {
35260Sstevel@tonic-gate 		/* ACPI case */
35270Sstevel@tonic-gate 		intr_index = ACPI_INDEX;
35280Sstevel@tonic-gate 		ioapicindex = acpi_find_ioapic(irqno);
35290Sstevel@tonic-gate 		ASSERT(ioapicindex != 0xFF);
35300Sstevel@tonic-gate 		ioapic = apic_io_id[ioapicindex];
35310Sstevel@tonic-gate 		ipin = irqno - apic_io_vectbase[ioapicindex];
35320Sstevel@tonic-gate 		if (apic_irq_table[irqno] &&
35330Sstevel@tonic-gate 		    apic_irq_table[irqno]->airq_mps_intr_index == ACPI_INDEX) {
35340Sstevel@tonic-gate 			ASSERT(apic_irq_table[irqno]->airq_intin_no == ipin &&
35350Sstevel@tonic-gate 			    apic_irq_table[irqno]->airq_ioapicindex ==
35360Sstevel@tonic-gate 			    ioapicindex);
35370Sstevel@tonic-gate 			return (irqno);
35380Sstevel@tonic-gate 		}
35390Sstevel@tonic-gate 
35400Sstevel@tonic-gate 	} else {
35410Sstevel@tonic-gate 		/* default configuration */
35420Sstevel@tonic-gate 		ioapicindex = 0;
35430Sstevel@tonic-gate 		ioapic = apic_io_id[ioapicindex];
35440Sstevel@tonic-gate 		ipin = (uchar_t)irqno;
35450Sstevel@tonic-gate 		intr_index = DEFAULT_INDEX;
35460Sstevel@tonic-gate 	}
35470Sstevel@tonic-gate 
35480Sstevel@tonic-gate 	if (ispec == NULL) {
35490Sstevel@tonic-gate 		APIC_VERBOSE_IOAPIC((CE_WARN, "No intrspec for irqno = %x\n",
35500Sstevel@tonic-gate 		    irqno));
35510Sstevel@tonic-gate 	} else if ((vector = apic_allocate_vector(ipl, irqno, 0)) == 0) {
35520Sstevel@tonic-gate 		if ((newirq = apic_share_vector(irqno, intr_flagp, intr_index,
35530Sstevel@tonic-gate 		    ipl, ioapicindex, ipin, &irqptr)) != -1) {
35540Sstevel@tonic-gate 			irqptr->airq_ipl = ipl;
35550Sstevel@tonic-gate 			irqptr->airq_origirq = (uchar_t)origirq;
35560Sstevel@tonic-gate 			irqptr->airq_dip = dip;
35570Sstevel@tonic-gate 			irqptr->airq_major = major;
35580Sstevel@tonic-gate 			sdip = apic_irq_table[IRQINDEX(newirq)]->airq_dip;
3559347Smyers 			/* This is OK to do really */
35600Sstevel@tonic-gate 			if (sdip == NULL) {
35610Sstevel@tonic-gate 				cmn_err(CE_WARN, "Sharing vectors: %s"
35620Sstevel@tonic-gate 				    " instance %d and SCI",
35630Sstevel@tonic-gate 				    ddi_get_name(dip), ddi_get_instance(dip));
35640Sstevel@tonic-gate 			} else {
35650Sstevel@tonic-gate 				cmn_err(CE_WARN, "Sharing vectors: %s"
35660Sstevel@tonic-gate 				    " instance %d and %s instance %d",
35670Sstevel@tonic-gate 				    ddi_get_name(sdip), ddi_get_instance(sdip),
35680Sstevel@tonic-gate 				    ddi_get_name(dip), ddi_get_instance(dip));
35690Sstevel@tonic-gate 			}
35700Sstevel@tonic-gate 			return (newirq);
35710Sstevel@tonic-gate 		}
35720Sstevel@tonic-gate 		/* try high priority allocation now  that share has failed */
35730Sstevel@tonic-gate 		if ((vector = apic_allocate_vector(ipl, irqno, 1)) == 0) {
35740Sstevel@tonic-gate 			cmn_err(CE_WARN, "No interrupt vector: %s instance %d",
35750Sstevel@tonic-gate 			    ddi_get_name(dip), ddi_get_instance(dip));
35760Sstevel@tonic-gate 			return (-1);
35770Sstevel@tonic-gate 		}
35780Sstevel@tonic-gate 	}
35790Sstevel@tonic-gate 
35800Sstevel@tonic-gate 	mutex_enter(&airq_mutex);
35810Sstevel@tonic-gate 	if (apic_irq_table[irqno] == NULL) {
35820Sstevel@tonic-gate 		irqptr = kmem_zalloc(sizeof (apic_irq_t), KM_SLEEP);
35830Sstevel@tonic-gate 		irqptr->airq_temp_cpu = IRQ_UNINIT;
35840Sstevel@tonic-gate 		apic_irq_table[irqno] = irqptr;
35850Sstevel@tonic-gate 	} else {
35860Sstevel@tonic-gate 		irqptr = apic_irq_table[irqno];
35870Sstevel@tonic-gate 		if (irqptr->airq_mps_intr_index != FREE_INDEX) {
35880Sstevel@tonic-gate 			/*
35890Sstevel@tonic-gate 			 * The slot is used by another irqno, so allocate
35900Sstevel@tonic-gate 			 * a free irqno for this interrupt
35910Sstevel@tonic-gate 			 */
3592881Sjohnny 			newirq = apic_allocate_irq(apic_first_avail_irq);
35930Sstevel@tonic-gate 			if (newirq == -1) {
35940Sstevel@tonic-gate 				mutex_exit(&airq_mutex);
35950Sstevel@tonic-gate 				return (-1);
35960Sstevel@tonic-gate 			}
35970Sstevel@tonic-gate 			irqno = newirq;
35980Sstevel@tonic-gate 			irqptr = apic_irq_table[irqno];
35990Sstevel@tonic-gate 			if (irqptr == NULL) {
36000Sstevel@tonic-gate 				irqptr = kmem_zalloc(sizeof (apic_irq_t),
36010Sstevel@tonic-gate 				    KM_SLEEP);
36020Sstevel@tonic-gate 				irqptr->airq_temp_cpu = IRQ_UNINIT;
36030Sstevel@tonic-gate 				apic_irq_table[irqno] = irqptr;
36040Sstevel@tonic-gate 			}
36050Sstevel@tonic-gate 			apic_modify_vector(vector, newirq);
36060Sstevel@tonic-gate 		}
36070Sstevel@tonic-gate 	}
36080Sstevel@tonic-gate 	apic_max_device_irq = max(irqno, apic_max_device_irq);
36090Sstevel@tonic-gate 	apic_min_device_irq = min(irqno, apic_min_device_irq);
36100Sstevel@tonic-gate 	mutex_exit(&airq_mutex);
36110Sstevel@tonic-gate 	irqptr->airq_ioapicindex = ioapicindex;
36120Sstevel@tonic-gate 	irqptr->airq_intin_no = ipin;
36130Sstevel@tonic-gate 	irqptr->airq_ipl = ipl;
36140Sstevel@tonic-gate 	irqptr->airq_vector = vector;
36150Sstevel@tonic-gate 	irqptr->airq_origirq = (uchar_t)origirq;
36160Sstevel@tonic-gate 	irqptr->airq_share_id = 0;
36170Sstevel@tonic-gate 	irqptr->airq_mps_intr_index = (short)intr_index;
36180Sstevel@tonic-gate 	irqptr->airq_dip = dip;
36190Sstevel@tonic-gate 	irqptr->airq_major = major;
36200Sstevel@tonic-gate 	irqptr->airq_cpu = apic_bind_intr(dip, irqno, ioapic, ipin);
36210Sstevel@tonic-gate 	if (intr_flagp)
36220Sstevel@tonic-gate 		irqptr->airq_iflag = *intr_flagp;
36230Sstevel@tonic-gate 
36240Sstevel@tonic-gate 	if (!DDI_INTR_IS_MSI_OR_MSIX(type)) {
36250Sstevel@tonic-gate 		/* setup I/O APIC entry for non-MSI/X interrupts */
36260Sstevel@tonic-gate 		apic_record_rdt_entry(irqptr, irqno);
36270Sstevel@tonic-gate 	}
36280Sstevel@tonic-gate 	return (irqno);
36290Sstevel@tonic-gate }
36300Sstevel@tonic-gate 
36310Sstevel@tonic-gate /*
36320Sstevel@tonic-gate  * return the cpu to which this intr should be bound.
36330Sstevel@tonic-gate  * Check properties or any other mechanism to see if user wants it
36340Sstevel@tonic-gate  * bound to a specific CPU. If so, return the cpu id with high bit set.
36350Sstevel@tonic-gate  * If not, use the policy to choose a cpu and return the id.
36360Sstevel@tonic-gate  */
36370Sstevel@tonic-gate uchar_t
36380Sstevel@tonic-gate apic_bind_intr(dev_info_t *dip, int irq, uchar_t ioapicid, uchar_t intin)
36390Sstevel@tonic-gate {
36400Sstevel@tonic-gate 	int	instance, instno, prop_len, bind_cpu, count;
36410Sstevel@tonic-gate 	uint_t	i, rc;
36420Sstevel@tonic-gate 	uchar_t	cpu;
36430Sstevel@tonic-gate 	major_t	major;
36440Sstevel@tonic-gate 	char	*name, *drv_name, *prop_val, *cptr;
36450Sstevel@tonic-gate 	char	prop_name[32];
36460Sstevel@tonic-gate 
36470Sstevel@tonic-gate 
36480Sstevel@tonic-gate 	if (apic_intr_policy == INTR_LOWEST_PRIORITY)
36490Sstevel@tonic-gate 		return (IRQ_UNBOUND);
36500Sstevel@tonic-gate 
36510Sstevel@tonic-gate 	drv_name = NULL;
36520Sstevel@tonic-gate 	rc = DDI_PROP_NOT_FOUND;
36530Sstevel@tonic-gate 	major = (major_t)-1;
36540Sstevel@tonic-gate 	if (dip != NULL) {
36550Sstevel@tonic-gate 		name = ddi_get_name(dip);
36560Sstevel@tonic-gate 		major = ddi_name_to_major(name);
36570Sstevel@tonic-gate 		drv_name = ddi_major_to_name(major);
36580Sstevel@tonic-gate 		instance = ddi_get_instance(dip);
36590Sstevel@tonic-gate 		if (apic_intr_policy == INTR_ROUND_ROBIN_WITH_AFFINITY) {
36600Sstevel@tonic-gate 			i = apic_min_device_irq;
36610Sstevel@tonic-gate 			for (; i <= apic_max_device_irq; i++) {
36620Sstevel@tonic-gate 
36630Sstevel@tonic-gate 				if ((i == irq) || (apic_irq_table[i] == NULL) ||
36640Sstevel@tonic-gate 				    (apic_irq_table[i]->airq_mps_intr_index
36650Sstevel@tonic-gate 				    == FREE_INDEX))
36660Sstevel@tonic-gate 					continue;
36670Sstevel@tonic-gate 
36680Sstevel@tonic-gate 				if ((apic_irq_table[i]->airq_major == major) &&
36690Sstevel@tonic-gate 				    (!(apic_irq_table[i]->airq_cpu &
36700Sstevel@tonic-gate 				    IRQ_USER_BOUND))) {
36710Sstevel@tonic-gate 
36720Sstevel@tonic-gate 					cpu = apic_irq_table[i]->airq_cpu;
36730Sstevel@tonic-gate 
36740Sstevel@tonic-gate 					cmn_err(CE_CONT,
36750Sstevel@tonic-gate 					    "!pcplusmp: %s (%s) instance #%d "
36760Sstevel@tonic-gate 					    "vector 0x%x ioapic 0x%x "
36770Sstevel@tonic-gate 					    "intin 0x%x is bound to cpu %d\n",
36780Sstevel@tonic-gate 					    name, drv_name, instance, irq,
36790Sstevel@tonic-gate 					    ioapicid, intin, cpu);
36800Sstevel@tonic-gate 					return (cpu);
36810Sstevel@tonic-gate 				}
36820Sstevel@tonic-gate 			}
36830Sstevel@tonic-gate 		}
36840Sstevel@tonic-gate 		/*
36850Sstevel@tonic-gate 		 * search for "drvname"_intpt_bind_cpus property first, the
36860Sstevel@tonic-gate 		 * syntax of the property should be "a[,b,c,...]" where
36870Sstevel@tonic-gate 		 * instance 0 binds to cpu a, instance 1 binds to cpu b,
36880Sstevel@tonic-gate 		 * instance 3 binds to cpu c...
36890Sstevel@tonic-gate 		 * ddi_getlongprop() will search /option first, then /
36900Sstevel@tonic-gate 		 * if "drvname"_intpt_bind_cpus doesn't exist, then find
36910Sstevel@tonic-gate 		 * intpt_bind_cpus property.  The syntax is the same, and
36920Sstevel@tonic-gate 		 * it applies to all the devices if its "drvname" specific
36930Sstevel@tonic-gate 		 * property doesn't exist
36940Sstevel@tonic-gate 		 */
36950Sstevel@tonic-gate 		(void) strcpy(prop_name, drv_name);
36960Sstevel@tonic-gate 		(void) strcat(prop_name, "_intpt_bind_cpus");
36970Sstevel@tonic-gate 		rc = ddi_getlongprop(DDI_DEV_T_ANY, dip, 0, prop_name,
36980Sstevel@tonic-gate 		    (caddr_t)&prop_val, &prop_len);
36990Sstevel@tonic-gate 		if (rc != DDI_PROP_SUCCESS) {
37000Sstevel@tonic-gate 			rc = ddi_getlongprop(DDI_DEV_T_ANY, dip, 0,
37010Sstevel@tonic-gate 			    "intpt_bind_cpus", (caddr_t)&prop_val, &prop_len);
37020Sstevel@tonic-gate 		}
37030Sstevel@tonic-gate 	}
37040Sstevel@tonic-gate 	if (rc == DDI_PROP_SUCCESS) {
37050Sstevel@tonic-gate 		for (i = count = 0; i < (prop_len - 1); i++)
37060Sstevel@tonic-gate 			if (prop_val[i] == ',')
37070Sstevel@tonic-gate 				count++;
37080Sstevel@tonic-gate 		if (prop_val[i-1] != ',')
37090Sstevel@tonic-gate 			count++;
37100Sstevel@tonic-gate 		/*
37110Sstevel@tonic-gate 		 * if somehow the binding instances defined in the
37120Sstevel@tonic-gate 		 * property are not enough for this instno., then
37130Sstevel@tonic-gate 		 * reuse the pattern for the next instance until
37140Sstevel@tonic-gate 		 * it reaches the requested instno
37150Sstevel@tonic-gate 		 */
37160Sstevel@tonic-gate 		instno = instance % count;
37170Sstevel@tonic-gate 		i = 0;
37180Sstevel@tonic-gate 		cptr = prop_val;
37190Sstevel@tonic-gate 		while (i < instno)
37200Sstevel@tonic-gate 			if (*cptr++ == ',')
37210Sstevel@tonic-gate 				i++;
37220Sstevel@tonic-gate 		bind_cpu = stoi(&cptr);
37230Sstevel@tonic-gate 		kmem_free(prop_val, prop_len);
37240Sstevel@tonic-gate 		/* if specific cpu is bogus, then default to cpu 0 */
37250Sstevel@tonic-gate 		if (bind_cpu >= apic_nproc) {
37260Sstevel@tonic-gate 			cmn_err(CE_WARN, "pcplusmp: %s=%s: CPU %d not present",
37270Sstevel@tonic-gate 			    prop_name, prop_val, bind_cpu);
37280Sstevel@tonic-gate 			bind_cpu = 0;
37290Sstevel@tonic-gate 		} else {
37300Sstevel@tonic-gate 			/* indicate that we are bound at user request */
37310Sstevel@tonic-gate 			bind_cpu |= IRQ_USER_BOUND;
37320Sstevel@tonic-gate 		}
37330Sstevel@tonic-gate 		/*
37340Sstevel@tonic-gate 		 * no need to check apic_cpus[].aci_status, if specific cpu is
37350Sstevel@tonic-gate 		 * not up, then post_cpu_start will handle it.
37360Sstevel@tonic-gate 		 */
37370Sstevel@tonic-gate 	} else {
37380Sstevel@tonic-gate 		/*
37390Sstevel@tonic-gate 		 * We change bind_cpu only for every two calls
37400Sstevel@tonic-gate 		 * as most drivers still do 2 add_intrs for every
37410Sstevel@tonic-gate 		 * interrupt
37420Sstevel@tonic-gate 		 */
37430Sstevel@tonic-gate 		bind_cpu = (apic_next_bind_cpu++) / 2;
37440Sstevel@tonic-gate 		if (bind_cpu >= apic_nproc) {
37450Sstevel@tonic-gate 			apic_next_bind_cpu = 1;
37460Sstevel@tonic-gate 			bind_cpu = 0;
37470Sstevel@tonic-gate 		}
37480Sstevel@tonic-gate 	}
37490Sstevel@tonic-gate 	if (drv_name != NULL)
37500Sstevel@tonic-gate 		cmn_err(CE_CONT, "!pcplusmp: %s (%s) instance %d "
37510Sstevel@tonic-gate 		    "vector 0x%x ioapic 0x%x intin 0x%x is bound to cpu %d\n",
37520Sstevel@tonic-gate 		    name, drv_name, instance,
37530Sstevel@tonic-gate 		    irq, ioapicid, intin, bind_cpu & ~IRQ_USER_BOUND);
37540Sstevel@tonic-gate 	else
37550Sstevel@tonic-gate 		cmn_err(CE_CONT, "!pcplusmp: "
37560Sstevel@tonic-gate 		    "vector 0x%x ioapic 0x%x intin 0x%x is bound to cpu %d\n",
37570Sstevel@tonic-gate 		    irq, ioapicid, intin, bind_cpu & ~IRQ_USER_BOUND);
37580Sstevel@tonic-gate 
37590Sstevel@tonic-gate 	return ((uchar_t)bind_cpu);
37600Sstevel@tonic-gate }
37610Sstevel@tonic-gate 
37620Sstevel@tonic-gate static struct apic_io_intr *
37630Sstevel@tonic-gate apic_find_io_intr_w_busid(int irqno, int busid)
37640Sstevel@tonic-gate {
37650Sstevel@tonic-gate 	struct	apic_io_intr	*intrp;
37660Sstevel@tonic-gate 
37670Sstevel@tonic-gate 	/*
37680Sstevel@tonic-gate 	 * It can have more than 1 entry with same source bus IRQ,
37690Sstevel@tonic-gate 	 * but unique with the source bus id
37700Sstevel@tonic-gate 	 */
37710Sstevel@tonic-gate 	intrp = apic_io_intrp;
37720Sstevel@tonic-gate 	if (intrp != NULL) {
37730Sstevel@tonic-gate 		while (intrp->intr_entry == APIC_IO_INTR_ENTRY) {
37740Sstevel@tonic-gate 			if (intrp->intr_irq == irqno &&
37750Sstevel@tonic-gate 			    intrp->intr_busid == busid &&
37760Sstevel@tonic-gate 			    intrp->intr_type == IO_INTR_INT)
37770Sstevel@tonic-gate 				return (intrp);
37780Sstevel@tonic-gate 			intrp++;
37790Sstevel@tonic-gate 		}
37800Sstevel@tonic-gate 	}
37810Sstevel@tonic-gate 	APIC_VERBOSE_IOAPIC((CE_NOTE, "Did not find io intr for irqno:"
37820Sstevel@tonic-gate 	    "busid %x:%x\n", irqno, busid));
37830Sstevel@tonic-gate 	return ((struct apic_io_intr *)NULL);
37840Sstevel@tonic-gate }
37850Sstevel@tonic-gate 
37860Sstevel@tonic-gate 
37870Sstevel@tonic-gate struct mps_bus_info {
37880Sstevel@tonic-gate 	char	*bus_name;
37890Sstevel@tonic-gate 	int	bus_id;
37900Sstevel@tonic-gate } bus_info_array[] = {
37910Sstevel@tonic-gate 	"ISA ", BUS_ISA,
37920Sstevel@tonic-gate 	"PCI ", BUS_PCI,
37930Sstevel@tonic-gate 	"EISA ", BUS_EISA,
37940Sstevel@tonic-gate 	"XPRESS", BUS_XPRESS,
37950Sstevel@tonic-gate 	"PCMCIA", BUS_PCMCIA,
37960Sstevel@tonic-gate 	"VL ", BUS_VL,
37970Sstevel@tonic-gate 	"CBUS ", BUS_CBUS,
37980Sstevel@tonic-gate 	"CBUSII", BUS_CBUSII,
37990Sstevel@tonic-gate 	"FUTURE", BUS_FUTURE,
38000Sstevel@tonic-gate 	"INTERN", BUS_INTERN,
38010Sstevel@tonic-gate 	"MBI ", BUS_MBI,
38020Sstevel@tonic-gate 	"MBII ", BUS_MBII,
38030Sstevel@tonic-gate 	"MPI ", BUS_MPI,
38040Sstevel@tonic-gate 	"MPSA ", BUS_MPSA,
38050Sstevel@tonic-gate 	"NUBUS ", BUS_NUBUS,
38060Sstevel@tonic-gate 	"TC ", BUS_TC,
38070Sstevel@tonic-gate 	"VME ", BUS_VME
38080Sstevel@tonic-gate };
38090Sstevel@tonic-gate 
38100Sstevel@tonic-gate static int
38110Sstevel@tonic-gate apic_find_bus_type(char *bus)
38120Sstevel@tonic-gate {
38130Sstevel@tonic-gate 	int	i = 0;
38140Sstevel@tonic-gate 
38150Sstevel@tonic-gate 	for (; i < sizeof (bus_info_array)/sizeof (struct mps_bus_info); i++)
38160Sstevel@tonic-gate 		if (strncmp(bus, bus_info_array[i].bus_name,
38170Sstevel@tonic-gate 		    strlen(bus_info_array[i].bus_name)) == 0)
38180Sstevel@tonic-gate 			return (bus_info_array[i].bus_id);
38190Sstevel@tonic-gate 	APIC_VERBOSE_IOAPIC((CE_WARN, "Did not find bus type for bus %s", bus));
38200Sstevel@tonic-gate 	return (0);
38210Sstevel@tonic-gate }
38220Sstevel@tonic-gate 
38230Sstevel@tonic-gate static int
38240Sstevel@tonic-gate apic_find_bus(int busid)
38250Sstevel@tonic-gate {
38260Sstevel@tonic-gate 	struct	apic_bus	*busp;
38270Sstevel@tonic-gate 
38280Sstevel@tonic-gate 	busp = apic_busp;
38290Sstevel@tonic-gate 	while (busp->bus_entry == APIC_BUS_ENTRY) {
38300Sstevel@tonic-gate 		if (busp->bus_id == busid)
38310Sstevel@tonic-gate 			return (apic_find_bus_type((char *)&busp->bus_str1));
38320Sstevel@tonic-gate 		busp++;
38330Sstevel@tonic-gate 	}
38340Sstevel@tonic-gate 	APIC_VERBOSE_IOAPIC((CE_WARN, "Did not find bus for bus id %x", busid));
38350Sstevel@tonic-gate 	return (0);
38360Sstevel@tonic-gate }
38370Sstevel@tonic-gate 
38380Sstevel@tonic-gate static int
38390Sstevel@tonic-gate apic_find_bus_id(int bustype)
38400Sstevel@tonic-gate {
38410Sstevel@tonic-gate 	struct	apic_bus	*busp;
38420Sstevel@tonic-gate 
38430Sstevel@tonic-gate 	busp = apic_busp;
38440Sstevel@tonic-gate 	while (busp->bus_entry == APIC_BUS_ENTRY) {
38450Sstevel@tonic-gate 		if (apic_find_bus_type((char *)&busp->bus_str1) == bustype)
38460Sstevel@tonic-gate 			return (busp->bus_id);
38470Sstevel@tonic-gate 		busp++;
38480Sstevel@tonic-gate 	}
38490Sstevel@tonic-gate 	APIC_VERBOSE_IOAPIC((CE_WARN, "Did not find bus id for bustype %x",
38500Sstevel@tonic-gate 	    bustype));
38510Sstevel@tonic-gate 	return (-1);
38520Sstevel@tonic-gate }
38530Sstevel@tonic-gate 
38540Sstevel@tonic-gate /*
38550Sstevel@tonic-gate  * Check if a particular irq need to be reserved for any io_intr
38560Sstevel@tonic-gate  */
38570Sstevel@tonic-gate static struct apic_io_intr *
38580Sstevel@tonic-gate apic_find_io_intr(int irqno)
38590Sstevel@tonic-gate {
38600Sstevel@tonic-gate 	struct	apic_io_intr	*intrp;
38610Sstevel@tonic-gate 
38620Sstevel@tonic-gate 	intrp = apic_io_intrp;
38630Sstevel@tonic-gate 	if (intrp != NULL) {
38640Sstevel@tonic-gate 		while (intrp->intr_entry == APIC_IO_INTR_ENTRY) {
38650Sstevel@tonic-gate 			if (intrp->intr_irq == irqno &&
38660Sstevel@tonic-gate 			    intrp->intr_type == IO_INTR_INT)
38670Sstevel@tonic-gate 				return (intrp);
38680Sstevel@tonic-gate 			intrp++;
38690Sstevel@tonic-gate 		}
38700Sstevel@tonic-gate 	}
38710Sstevel@tonic-gate 	return ((struct apic_io_intr *)NULL);
38720Sstevel@tonic-gate }
38730Sstevel@tonic-gate 
38740Sstevel@tonic-gate /*
38750Sstevel@tonic-gate  * Check if the given ioapicindex intin combination has already been assigned
38760Sstevel@tonic-gate  * an irq. If so return irqno. Else -1
38770Sstevel@tonic-gate  */
38780Sstevel@tonic-gate static int
38790Sstevel@tonic-gate apic_find_intin(uchar_t ioapic, uchar_t intin)
38800Sstevel@tonic-gate {
38810Sstevel@tonic-gate 	apic_irq_t *irqptr;
38820Sstevel@tonic-gate 	int	i;
38830Sstevel@tonic-gate 
38840Sstevel@tonic-gate 	/* find ioapic and intin in the apic_irq_table[] and return the index */
38850Sstevel@tonic-gate 	for (i = apic_min_device_irq; i <= apic_max_device_irq; i++) {
38860Sstevel@tonic-gate 		irqptr = apic_irq_table[i];
38870Sstevel@tonic-gate 		while (irqptr) {
38880Sstevel@tonic-gate 			if ((irqptr->airq_mps_intr_index >= 0) &&
38890Sstevel@tonic-gate 			    (irqptr->airq_intin_no == intin) &&
38900Sstevel@tonic-gate 			    (irqptr->airq_ioapicindex == ioapic)) {
38910Sstevel@tonic-gate 				APIC_VERBOSE_IOAPIC((CE_NOTE, "!Found irq "
38920Sstevel@tonic-gate 				    "entry for ioapic:intin %x:%x "
38930Sstevel@tonic-gate 				    "shared interrupts ?", ioapic, intin));
38940Sstevel@tonic-gate 				return (i);
38950Sstevel@tonic-gate 			}
38960Sstevel@tonic-gate 			irqptr = irqptr->airq_next;
38970Sstevel@tonic-gate 		}
38980Sstevel@tonic-gate 	}
38990Sstevel@tonic-gate 	return (-1);
39000Sstevel@tonic-gate }
39010Sstevel@tonic-gate 
39020Sstevel@tonic-gate int
39030Sstevel@tonic-gate apic_allocate_irq(int irq)
39040Sstevel@tonic-gate {
39050Sstevel@tonic-gate 	int	freeirq, i;
39060Sstevel@tonic-gate 
39070Sstevel@tonic-gate 	if ((freeirq = apic_find_free_irq(irq, (APIC_RESV_IRQ - 1))) == -1)
39080Sstevel@tonic-gate 		if ((freeirq = apic_find_free_irq(APIC_FIRST_FREE_IRQ,
39090Sstevel@tonic-gate 		    (irq - 1))) == -1) {
39100Sstevel@tonic-gate 			/*
39110Sstevel@tonic-gate 			 * if BIOS really defines every single irq in the mps
39120Sstevel@tonic-gate 			 * table, then don't worry about conflicting with
39130Sstevel@tonic-gate 			 * them, just use any free slot in apic_irq_table
39140Sstevel@tonic-gate 			 */
39150Sstevel@tonic-gate 			for (i = APIC_FIRST_FREE_IRQ; i < APIC_RESV_IRQ; i++) {
39160Sstevel@tonic-gate 				if ((apic_irq_table[i] == NULL) ||
39170Sstevel@tonic-gate 				    apic_irq_table[i]->airq_mps_intr_index ==
39180Sstevel@tonic-gate 				    FREE_INDEX) {
39190Sstevel@tonic-gate 				freeirq = i;
39200Sstevel@tonic-gate 				break;
39210Sstevel@tonic-gate 			}
39220Sstevel@tonic-gate 		}
39230Sstevel@tonic-gate 		if (freeirq == -1) {
39240Sstevel@tonic-gate 			/* This shouldn't happen, but just in case */
39250Sstevel@tonic-gate 			cmn_err(CE_WARN, "pcplusmp: NO available IRQ");
39260Sstevel@tonic-gate 			return (-1);
39270Sstevel@tonic-gate 		}
39280Sstevel@tonic-gate 	}
39290Sstevel@tonic-gate 	if (apic_irq_table[freeirq] == NULL) {
39300Sstevel@tonic-gate 		apic_irq_table[freeirq] =
39310Sstevel@tonic-gate 		    kmem_zalloc(sizeof (apic_irq_t), KM_NOSLEEP);
39320Sstevel@tonic-gate 		if (apic_irq_table[freeirq] == NULL) {
39330Sstevel@tonic-gate 			cmn_err(CE_WARN, "pcplusmp: NO memory to allocate IRQ");
39340Sstevel@tonic-gate 			return (-1);
39350Sstevel@tonic-gate 		}
39360Sstevel@tonic-gate 		apic_irq_table[freeirq]->airq_mps_intr_index = FREE_INDEX;
39370Sstevel@tonic-gate 	}
39380Sstevel@tonic-gate 	return (freeirq);
39390Sstevel@tonic-gate }
39400Sstevel@tonic-gate 
39410Sstevel@tonic-gate static int
39420Sstevel@tonic-gate apic_find_free_irq(int start, int end)
39430Sstevel@tonic-gate {
39440Sstevel@tonic-gate 	int	i;
39450Sstevel@tonic-gate 
39460Sstevel@tonic-gate 	for (i = start; i <= end; i++)
39470Sstevel@tonic-gate 		/* Check if any I/O entry needs this IRQ */
39480Sstevel@tonic-gate 		if (apic_find_io_intr(i) == NULL) {
39490Sstevel@tonic-gate 			/* Then see if it is free */
39500Sstevel@tonic-gate 			if ((apic_irq_table[i] == NULL) ||
39510Sstevel@tonic-gate 			    (apic_irq_table[i]->airq_mps_intr_index ==
39520Sstevel@tonic-gate 			    FREE_INDEX)) {
39530Sstevel@tonic-gate 				return (i);
39540Sstevel@tonic-gate 			}
39550Sstevel@tonic-gate 		}
39560Sstevel@tonic-gate 	return (-1);
39570Sstevel@tonic-gate }
39580Sstevel@tonic-gate 
39590Sstevel@tonic-gate /*
39600Sstevel@tonic-gate  * Allocate a free vector for irq at ipl. Takes care of merging of multiple
39610Sstevel@tonic-gate  * IPLs into a single APIC level as well as stretching some IPLs onto multiple
39620Sstevel@tonic-gate  * levels. APIC_HI_PRI_VECTS interrupts are reserved for high priority
39630Sstevel@tonic-gate  * requests and allocated only when pri is set.
39640Sstevel@tonic-gate  */
39650Sstevel@tonic-gate static uchar_t
39660Sstevel@tonic-gate apic_allocate_vector(int ipl, int irq, int pri)
39670Sstevel@tonic-gate {
39680Sstevel@tonic-gate 	int	lowest, highest, i;
39690Sstevel@tonic-gate 
39700Sstevel@tonic-gate 	highest = apic_ipltopri[ipl] + APIC_VECTOR_MASK;
39710Sstevel@tonic-gate 	lowest = apic_ipltopri[ipl - 1] + APIC_VECTOR_PER_IPL;
39720Sstevel@tonic-gate 
39730Sstevel@tonic-gate 	if (highest < lowest) /* Both ipl and ipl - 1 map to same pri */
39740Sstevel@tonic-gate 		lowest -= APIC_VECTOR_PER_IPL;
39750Sstevel@tonic-gate 
39760Sstevel@tonic-gate #ifdef	DEBUG
39770Sstevel@tonic-gate 	if (apic_restrict_vector)	/* for testing shared interrupt logic */
39780Sstevel@tonic-gate 		highest = lowest + apic_restrict_vector + APIC_HI_PRI_VECTS;
39790Sstevel@tonic-gate #endif /* DEBUG */
39800Sstevel@tonic-gate 	if (pri == 0)
39810Sstevel@tonic-gate 		highest -= APIC_HI_PRI_VECTS;
39820Sstevel@tonic-gate 
39830Sstevel@tonic-gate 	for (i = lowest; i < highest; i++) {
39840Sstevel@tonic-gate 		if ((i == T_FASTTRAP) || (i == APIC_SPUR_INTR) ||
39850Sstevel@tonic-gate 			(i == T_SYSCALLINT) || (i == T_DTRACE_PROBE) ||
39860Sstevel@tonic-gate 			(i == T_DTRACE_RET))
39870Sstevel@tonic-gate 			continue;
39880Sstevel@tonic-gate 		if (apic_vector_to_irq[i] == APIC_RESV_IRQ) {
39890Sstevel@tonic-gate 			apic_vector_to_irq[i] = (uchar_t)irq;
39900Sstevel@tonic-gate 			return (i);
39910Sstevel@tonic-gate 		}
39920Sstevel@tonic-gate 	}
39930Sstevel@tonic-gate 
39940Sstevel@tonic-gate 	return (0);
39950Sstevel@tonic-gate }
39960Sstevel@tonic-gate 
39970Sstevel@tonic-gate static void
39980Sstevel@tonic-gate apic_modify_vector(uchar_t vector, int irq)
39990Sstevel@tonic-gate {
40000Sstevel@tonic-gate 	apic_vector_to_irq[vector] = (uchar_t)irq;
40010Sstevel@tonic-gate }
40020Sstevel@tonic-gate 
40030Sstevel@tonic-gate /*
40040Sstevel@tonic-gate  * Mark vector as being in the process of being deleted. Interrupts
40050Sstevel@tonic-gate  * may still come in on some CPU. The moment an interrupt comes with
40060Sstevel@tonic-gate  * the new vector, we know we can free the old one. Called only from
40070Sstevel@tonic-gate  * addspl and delspl with interrupts disabled. Because an interrupt
40080Sstevel@tonic-gate  * can be shared, but no interrupt from either device may come in,
40090Sstevel@tonic-gate  * we also use a timeout mechanism, which we arbitrarily set to
40100Sstevel@tonic-gate  * apic_revector_timeout microseconds.
40110Sstevel@tonic-gate  */
40120Sstevel@tonic-gate static void
40130Sstevel@tonic-gate apic_mark_vector(uchar_t oldvector, uchar_t newvector)
40140Sstevel@tonic-gate {
40150Sstevel@tonic-gate 	int iflag = intr_clear();
40160Sstevel@tonic-gate 	lock_set(&apic_revector_lock);
40170Sstevel@tonic-gate 	if (!apic_oldvec_to_newvec) {
40180Sstevel@tonic-gate 		apic_oldvec_to_newvec =
40190Sstevel@tonic-gate 		    kmem_zalloc(sizeof (newvector) * APIC_MAX_VECTOR * 2,
40200Sstevel@tonic-gate 		    KM_NOSLEEP);
40210Sstevel@tonic-gate 
40220Sstevel@tonic-gate 		if (!apic_oldvec_to_newvec) {
40230Sstevel@tonic-gate 			/*
40240Sstevel@tonic-gate 			 * This failure is not catastrophic.
40250Sstevel@tonic-gate 			 * But, the oldvec will never be freed.
40260Sstevel@tonic-gate 			 */
40270Sstevel@tonic-gate 			apic_error |= APIC_ERR_MARK_VECTOR_FAIL;
40280Sstevel@tonic-gate 			lock_clear(&apic_revector_lock);
40290Sstevel@tonic-gate 			intr_restore(iflag);
40300Sstevel@tonic-gate 			return;
40310Sstevel@tonic-gate 		}
40320Sstevel@tonic-gate 		apic_newvec_to_oldvec = &apic_oldvec_to_newvec[APIC_MAX_VECTOR];
40330Sstevel@tonic-gate 	}
40340Sstevel@tonic-gate 
40350Sstevel@tonic-gate 	/* See if we already did this for drivers which do double addintrs */
40360Sstevel@tonic-gate 	if (apic_oldvec_to_newvec[oldvector] != newvector) {
40370Sstevel@tonic-gate 		apic_oldvec_to_newvec[oldvector] = newvector;
40380Sstevel@tonic-gate 		apic_newvec_to_oldvec[newvector] = oldvector;
40390Sstevel@tonic-gate 		apic_revector_pending++;
40400Sstevel@tonic-gate 	}
40410Sstevel@tonic-gate 	lock_clear(&apic_revector_lock);
40420Sstevel@tonic-gate 	intr_restore(iflag);
40430Sstevel@tonic-gate 	(void) timeout(apic_xlate_vector_free_timeout_handler,
40440Sstevel@tonic-gate 	    (void *)(uintptr_t)oldvector, drv_usectohz(apic_revector_timeout));
40450Sstevel@tonic-gate }
40460Sstevel@tonic-gate 
40470Sstevel@tonic-gate /*
40480Sstevel@tonic-gate  * xlate_vector is called from intr_enter if revector_pending is set.
40490Sstevel@tonic-gate  * It will xlate it if needed and mark the old vector as free.
40500Sstevel@tonic-gate  */
40510Sstevel@tonic-gate static uchar_t
40520Sstevel@tonic-gate apic_xlate_vector(uchar_t vector)
40530Sstevel@tonic-gate {
40540Sstevel@tonic-gate 	uchar_t	newvector, oldvector = 0;
40550Sstevel@tonic-gate 
40560Sstevel@tonic-gate 	lock_set(&apic_revector_lock);
40570Sstevel@tonic-gate 	/* Do we really need to do this ? */
40580Sstevel@tonic-gate 	if (!apic_revector_pending) {
40590Sstevel@tonic-gate 		lock_clear(&apic_revector_lock);
40600Sstevel@tonic-gate 		return (vector);
40610Sstevel@tonic-gate 	}
40620Sstevel@tonic-gate 	if ((newvector = apic_oldvec_to_newvec[vector]) != 0)
40630Sstevel@tonic-gate 		oldvector = vector;
40640Sstevel@tonic-gate 	else {
40650Sstevel@tonic-gate 		/*
40660Sstevel@tonic-gate 		 * The incoming vector is new . See if a stale entry is
40670Sstevel@tonic-gate 		 * remaining
40680Sstevel@tonic-gate 		 */
40690Sstevel@tonic-gate 		if ((oldvector = apic_newvec_to_oldvec[vector]) != 0)
40700Sstevel@tonic-gate 			newvector = vector;
40710Sstevel@tonic-gate 	}
40720Sstevel@tonic-gate 
40730Sstevel@tonic-gate 	if (oldvector) {
40740Sstevel@tonic-gate 		apic_revector_pending--;
40750Sstevel@tonic-gate 		apic_oldvec_to_newvec[oldvector] = 0;
40760Sstevel@tonic-gate 		apic_newvec_to_oldvec[newvector] = 0;
40770Sstevel@tonic-gate 		apic_free_vector(oldvector);
40780Sstevel@tonic-gate 		lock_clear(&apic_revector_lock);
40790Sstevel@tonic-gate 		/* There could have been more than one reprogramming! */
40800Sstevel@tonic-gate 		return (apic_xlate_vector(newvector));
40810Sstevel@tonic-gate 	}
40820Sstevel@tonic-gate 	lock_clear(&apic_revector_lock);
40830Sstevel@tonic-gate 	return (vector);
40840Sstevel@tonic-gate }
40850Sstevel@tonic-gate 
40860Sstevel@tonic-gate void
40870Sstevel@tonic-gate apic_xlate_vector_free_timeout_handler(void *arg)
40880Sstevel@tonic-gate {
40890Sstevel@tonic-gate 	int iflag;
40900Sstevel@tonic-gate 	uchar_t oldvector, newvector;
40910Sstevel@tonic-gate 
40920Sstevel@tonic-gate 	oldvector = (uchar_t)(uintptr_t)arg;
40930Sstevel@tonic-gate 	iflag = intr_clear();
40940Sstevel@tonic-gate 	lock_set(&apic_revector_lock);
40950Sstevel@tonic-gate 	if ((newvector = apic_oldvec_to_newvec[oldvector]) != 0) {
40960Sstevel@tonic-gate 		apic_free_vector(oldvector);
40970Sstevel@tonic-gate 		apic_oldvec_to_newvec[oldvector] = 0;
40980Sstevel@tonic-gate 		apic_newvec_to_oldvec[newvector] = 0;
40990Sstevel@tonic-gate 		apic_revector_pending--;
41000Sstevel@tonic-gate 	}
41010Sstevel@tonic-gate 
41020Sstevel@tonic-gate 	lock_clear(&apic_revector_lock);
41030Sstevel@tonic-gate 	intr_restore(iflag);
41040Sstevel@tonic-gate }
41050Sstevel@tonic-gate 
41060Sstevel@tonic-gate 
41070Sstevel@tonic-gate /* Mark vector as not being used by any irq */
41080Sstevel@tonic-gate static void
41090Sstevel@tonic-gate apic_free_vector(uchar_t vector)
41100Sstevel@tonic-gate {
41110Sstevel@tonic-gate 	apic_vector_to_irq[vector] = APIC_RESV_IRQ;
41120Sstevel@tonic-gate }
41130Sstevel@tonic-gate 
41140Sstevel@tonic-gate /*
41150Sstevel@tonic-gate  * compute the polarity, trigger mode and vector for programming into
41160Sstevel@tonic-gate  * the I/O apic and record in airq_rdt_entry.
41170Sstevel@tonic-gate  */
41180Sstevel@tonic-gate static void
41190Sstevel@tonic-gate apic_record_rdt_entry(apic_irq_t *irqptr, int irq)
41200Sstevel@tonic-gate {
41210Sstevel@tonic-gate 	int	ioapicindex, bus_type, vector;
41220Sstevel@tonic-gate 	short	intr_index;
41230Sstevel@tonic-gate 	uint_t	level, po, io_po;
41240Sstevel@tonic-gate 	struct apic_io_intr *iointrp;
41250Sstevel@tonic-gate 
41260Sstevel@tonic-gate 	intr_index = irqptr->airq_mps_intr_index;
41270Sstevel@tonic-gate 	DDI_INTR_IMPLDBG((CE_CONT, "apic_record_rdt_entry: intr_index=%d "
41280Sstevel@tonic-gate 	    "irq = 0x%x dip = 0x%p vector = 0x%x\n", intr_index, irq,
41290Sstevel@tonic-gate 	    (void *)irqptr->airq_dip, irqptr->airq_vector));
41300Sstevel@tonic-gate 
41310Sstevel@tonic-gate 	if (intr_index == RESERVE_INDEX) {
41320Sstevel@tonic-gate 		apic_error |= APIC_ERR_INVALID_INDEX;
41330Sstevel@tonic-gate 		return;
41340Sstevel@tonic-gate 	} else if (APIC_IS_MSI_OR_MSIX_INDEX(intr_index)) {
41350Sstevel@tonic-gate 		return;
41360Sstevel@tonic-gate 	}
41370Sstevel@tonic-gate 
41380Sstevel@tonic-gate 	vector = irqptr->airq_vector;
41390Sstevel@tonic-gate 	ioapicindex = irqptr->airq_ioapicindex;
41400Sstevel@tonic-gate 	/* Assume edge triggered by default */
41410Sstevel@tonic-gate 	level = 0;
41420Sstevel@tonic-gate 	/* Assume active high by default */
41430Sstevel@tonic-gate 	po = 0;
41440Sstevel@tonic-gate 
41450Sstevel@tonic-gate 	if (intr_index == DEFAULT_INDEX || intr_index == FREE_INDEX) {
41460Sstevel@tonic-gate 		ASSERT(irq < 16);
41470Sstevel@tonic-gate 		if (eisa_level_intr_mask & (1 << irq))
41480Sstevel@tonic-gate 			level = AV_LEVEL;
41490Sstevel@tonic-gate 		if (intr_index == FREE_INDEX && apic_defconf == 0)
41500Sstevel@tonic-gate 			apic_error |= APIC_ERR_INVALID_INDEX;
4151347Smyers 	} else if (intr_index == ACPI_INDEX) {
41520Sstevel@tonic-gate 		bus_type = irqptr->airq_iflag.bustype;
41530Sstevel@tonic-gate 		if (irqptr->airq_iflag.intr_el == INTR_EL_CONFORM) {
41540Sstevel@tonic-gate 			if (bus_type == BUS_PCI)
41550Sstevel@tonic-gate 				level = AV_LEVEL;
41560Sstevel@tonic-gate 		} else
41570Sstevel@tonic-gate 			level = (irqptr->airq_iflag.intr_el == INTR_EL_LEVEL) ?
41580Sstevel@tonic-gate 			    AV_LEVEL : 0;
41590Sstevel@tonic-gate 		if (level &&
41600Sstevel@tonic-gate 		    ((irqptr->airq_iflag.intr_po == INTR_PO_ACTIVE_LOW) ||
41610Sstevel@tonic-gate 		    (irqptr->airq_iflag.intr_po == INTR_PO_CONFORM &&
41620Sstevel@tonic-gate 		    bus_type == BUS_PCI)))
41630Sstevel@tonic-gate 			po = AV_ACTIVE_LOW;
41640Sstevel@tonic-gate 	} else {
41650Sstevel@tonic-gate 		iointrp = apic_io_intrp + intr_index;
41660Sstevel@tonic-gate 		bus_type = apic_find_bus(iointrp->intr_busid);
41670Sstevel@tonic-gate 		if (iointrp->intr_el == INTR_EL_CONFORM) {
41680Sstevel@tonic-gate 			if ((irq < 16) && (eisa_level_intr_mask & (1 << irq)))
41690Sstevel@tonic-gate 				level = AV_LEVEL;
41700Sstevel@tonic-gate 			else if (bus_type == BUS_PCI)
41710Sstevel@tonic-gate 				level = AV_LEVEL;
41720Sstevel@tonic-gate 		} else
41730Sstevel@tonic-gate 			level = (iointrp->intr_el == INTR_EL_LEVEL) ?
41740Sstevel@tonic-gate 			    AV_LEVEL : 0;
41750Sstevel@tonic-gate 		if (level && ((iointrp->intr_po == INTR_PO_ACTIVE_LOW) ||
41760Sstevel@tonic-gate 		    (iointrp->intr_po == INTR_PO_CONFORM &&
41770Sstevel@tonic-gate 		    bus_type == BUS_PCI)))
41780Sstevel@tonic-gate 			po = AV_ACTIVE_LOW;
41790Sstevel@tonic-gate 	}
41800Sstevel@tonic-gate 	if (level)
41810Sstevel@tonic-gate 		apic_level_intr[irq] = 1;
41820Sstevel@tonic-gate 	/*
41830Sstevel@tonic-gate 	 * The 82489DX External APIC cannot do active low polarity interrupts.
41840Sstevel@tonic-gate 	 */
41850Sstevel@tonic-gate 	if (po && (apic_io_ver[ioapicindex] != IOAPIC_VER_82489DX))
41860Sstevel@tonic-gate 		io_po = po;
41870Sstevel@tonic-gate 	else
41880Sstevel@tonic-gate 		io_po = 0;
41890Sstevel@tonic-gate 
41900Sstevel@tonic-gate 	if (apic_verbose & APIC_VERBOSE_IOAPIC_FLAG)
41910Sstevel@tonic-gate 		printf("setio: ioapic=%x intin=%x level=%x po=%x vector=%x\n",
41920Sstevel@tonic-gate 		    ioapicindex, irqptr->airq_intin_no, level, io_po, vector);
41930Sstevel@tonic-gate 
41940Sstevel@tonic-gate 	irqptr->airq_rdt_entry = level|io_po|vector;
41950Sstevel@tonic-gate }
41960Sstevel@tonic-gate 
41970Sstevel@tonic-gate /*
41980Sstevel@tonic-gate  * Call rebind to do the actual programming.
41990Sstevel@tonic-gate  */
42000Sstevel@tonic-gate static int
42010Sstevel@tonic-gate apic_setup_io_intr(apic_irq_t *irqptr, int irq)
42020Sstevel@tonic-gate {
42030Sstevel@tonic-gate 	int rv;
42040Sstevel@tonic-gate 
42050Sstevel@tonic-gate 	if (rv = apic_rebind(irqptr, apic_irq_table[irq]->airq_cpu, 1,
42060Sstevel@tonic-gate 	    IMMEDIATE))
42070Sstevel@tonic-gate 		/* CPU is not up or interrupt is disabled. Fall back to 0 */
42080Sstevel@tonic-gate 		rv = apic_rebind(irqptr, 0, 1, IMMEDIATE);
42090Sstevel@tonic-gate 
42100Sstevel@tonic-gate 	return (rv);
42110Sstevel@tonic-gate }
42120Sstevel@tonic-gate 
42130Sstevel@tonic-gate /*
42140Sstevel@tonic-gate  * Deferred reprogramming: Call apic_rebind to do the real work.
42150Sstevel@tonic-gate  */
42160Sstevel@tonic-gate static int
42170Sstevel@tonic-gate apic_setup_io_intr_deferred(apic_irq_t *irqptr, int irq)
42180Sstevel@tonic-gate {
42190Sstevel@tonic-gate 	int rv;
42200Sstevel@tonic-gate 
42210Sstevel@tonic-gate 	if (rv = apic_rebind(irqptr, apic_irq_table[irq]->airq_cpu, 1,
42220Sstevel@tonic-gate 	    DEFERRED))
42230Sstevel@tonic-gate 		/* CPU is not up or interrupt is disabled. Fall back to 0 */
42240Sstevel@tonic-gate 		rv = apic_rebind(irqptr, 0, 1, DEFERRED);
42250Sstevel@tonic-gate 
42260Sstevel@tonic-gate 	return (rv);
42270Sstevel@tonic-gate }
42280Sstevel@tonic-gate 
42290Sstevel@tonic-gate /*
42300Sstevel@tonic-gate  * Bind interrupt corresponding to irq_ptr to bind_cpu. acquire_lock
42310Sstevel@tonic-gate  * if false (0) means lock is already held (e.g: in rebind_all).
42320Sstevel@tonic-gate  */
42330Sstevel@tonic-gate static int
42340Sstevel@tonic-gate apic_rebind(apic_irq_t *irq_ptr, int bind_cpu, int acquire_lock, int when)
42350Sstevel@tonic-gate {
42360Sstevel@tonic-gate 	int			intin_no;
42370Sstevel@tonic-gate 	volatile int32_t	*ioapic;
42380Sstevel@tonic-gate 	uchar_t			airq_temp_cpu;
42390Sstevel@tonic-gate 	apic_cpus_info_t	*cpu_infop;
42400Sstevel@tonic-gate 	int			iflag;
42410Sstevel@tonic-gate 	int		which_irq = apic_vector_to_irq[irq_ptr->airq_vector];
42420Sstevel@tonic-gate 
42430Sstevel@tonic-gate 	intin_no = irq_ptr->airq_intin_no;
42440Sstevel@tonic-gate 	ioapic = apicioadr[irq_ptr->airq_ioapicindex];
42450Sstevel@tonic-gate 	airq_temp_cpu = irq_ptr->airq_temp_cpu;
42460Sstevel@tonic-gate 	if (airq_temp_cpu != IRQ_UNINIT && airq_temp_cpu != IRQ_UNBOUND) {
42470Sstevel@tonic-gate 		if (airq_temp_cpu & IRQ_USER_BOUND)
42480Sstevel@tonic-gate 			/* Mask off high bit so it can be used as array index */
42490Sstevel@tonic-gate 			airq_temp_cpu &= ~IRQ_USER_BOUND;
42500Sstevel@tonic-gate 
42510Sstevel@tonic-gate 		ASSERT(airq_temp_cpu < apic_nproc);
42520Sstevel@tonic-gate 	}
42530Sstevel@tonic-gate 
42540Sstevel@tonic-gate 	iflag = intr_clear();
42550Sstevel@tonic-gate 
42560Sstevel@tonic-gate 	if (acquire_lock)
42570Sstevel@tonic-gate 		lock_set(&apic_ioapic_lock);
42580Sstevel@tonic-gate 
42590Sstevel@tonic-gate 	/*
42600Sstevel@tonic-gate 	 * Can't bind to a CPU that's not online:
42610Sstevel@tonic-gate 	 */
42620Sstevel@tonic-gate 	cpu_infop = &apic_cpus[bind_cpu & ~IRQ_USER_BOUND];
42630Sstevel@tonic-gate 	if (!(cpu_infop->aci_status & APIC_CPU_INTR_ENABLE)) {
42640Sstevel@tonic-gate 
42650Sstevel@tonic-gate 		if (acquire_lock)
42660Sstevel@tonic-gate 			lock_clear(&apic_ioapic_lock);
42670Sstevel@tonic-gate 
42680Sstevel@tonic-gate 		intr_restore(iflag);
42690Sstevel@tonic-gate 		return (1);
42700Sstevel@tonic-gate 	}
42710Sstevel@tonic-gate 
42720Sstevel@tonic-gate 	/*
42730Sstevel@tonic-gate 	 * If this is a deferred reprogramming attempt, ensure we have
42740Sstevel@tonic-gate 	 * not been passed stale data:
42750Sstevel@tonic-gate 	 */
42760Sstevel@tonic-gate 	if ((when == DEFERRED) &&
42770Sstevel@tonic-gate 	    (apic_reprogram_info[which_irq].valid == 0)) {
42780Sstevel@tonic-gate 		/* stale info, so just return */
42790Sstevel@tonic-gate 		if (acquire_lock)
42800Sstevel@tonic-gate 			lock_clear(&apic_ioapic_lock);
42810Sstevel@tonic-gate 
42820Sstevel@tonic-gate 		intr_restore(iflag);
42830Sstevel@tonic-gate 		return (0);
42840Sstevel@tonic-gate 	}
42850Sstevel@tonic-gate 
42860Sstevel@tonic-gate 	/*
42870Sstevel@tonic-gate 	 * If this interrupt has been delivered to a CPU and that CPU
42880Sstevel@tonic-gate 	 * has not handled it yet, we cannot reprogram the IOAPIC now:
42890Sstevel@tonic-gate 	 */
42900Sstevel@tonic-gate 	if (!APIC_IS_MSI_OR_MSIX_INDEX(irq_ptr->airq_mps_intr_index) &&
42910Sstevel@tonic-gate 	    apic_check_stuck_interrupt(irq_ptr, airq_temp_cpu, bind_cpu,
42920Sstevel@tonic-gate 	    ioapic, intin_no, which_irq) != 0) {
42930Sstevel@tonic-gate 
42940Sstevel@tonic-gate 		if (acquire_lock)
42950Sstevel@tonic-gate 			lock_clear(&apic_ioapic_lock);
42960Sstevel@tonic-gate 
42970Sstevel@tonic-gate 		intr_restore(iflag);
42980Sstevel@tonic-gate 		return (0);
42990Sstevel@tonic-gate 	}
43000Sstevel@tonic-gate 
43010Sstevel@tonic-gate 	/*
43020Sstevel@tonic-gate 	 * NOTE: We do not unmask the RDT here, as an interrupt MAY still
43030Sstevel@tonic-gate 	 * come in before we have a chance to reprogram it below.  The
43040Sstevel@tonic-gate 	 * reprogramming below will simultaneously change and unmask the
43050Sstevel@tonic-gate 	 * RDT entry.
43060Sstevel@tonic-gate 	 */
43070Sstevel@tonic-gate 
43080Sstevel@tonic-gate 	if ((uchar_t)bind_cpu == IRQ_UNBOUND) {
43090Sstevel@tonic-gate 		/* Write the RDT entry -- no specific CPU binding */
43100Sstevel@tonic-gate 		WRITE_IOAPIC_RDT_ENTRY_HIGH_DWORD(ioapic, intin_no, AV_TOALL);
43110Sstevel@tonic-gate 
43120Sstevel@tonic-gate 		if (airq_temp_cpu != IRQ_UNINIT && airq_temp_cpu != IRQ_UNBOUND)
43130Sstevel@tonic-gate 			apic_cpus[airq_temp_cpu].aci_temp_bound--;
43140Sstevel@tonic-gate 
43150Sstevel@tonic-gate 		/* Write the vector, trigger, and polarity portion of the RDT */
43160Sstevel@tonic-gate 		WRITE_IOAPIC_RDT_ENTRY_LOW_DWORD(ioapic, intin_no,
43170Sstevel@tonic-gate 		    AV_LDEST | AV_LOPRI | irq_ptr->airq_rdt_entry);
43180Sstevel@tonic-gate 		if (acquire_lock)
43190Sstevel@tonic-gate 			lock_clear(&apic_ioapic_lock);
43200Sstevel@tonic-gate 		irq_ptr->airq_temp_cpu = IRQ_UNBOUND;
43210Sstevel@tonic-gate 		intr_restore(iflag);
43220Sstevel@tonic-gate 		return (0);
43230Sstevel@tonic-gate 	}
43240Sstevel@tonic-gate 
43250Sstevel@tonic-gate 	if (bind_cpu & IRQ_USER_BOUND) {
43260Sstevel@tonic-gate 		cpu_infop->aci_bound++;
43270Sstevel@tonic-gate 	} else {
43280Sstevel@tonic-gate 		cpu_infop->aci_temp_bound++;
43290Sstevel@tonic-gate 	}
43300Sstevel@tonic-gate 	ASSERT((bind_cpu & ~IRQ_USER_BOUND) < apic_nproc);
43310Sstevel@tonic-gate 	if (!APIC_IS_MSI_OR_MSIX_INDEX(irq_ptr->airq_mps_intr_index)) {
43320Sstevel@tonic-gate 		/* Write the RDT entry -- bind to a specific CPU: */
43330Sstevel@tonic-gate 		WRITE_IOAPIC_RDT_ENTRY_HIGH_DWORD(ioapic, intin_no,
43340Sstevel@tonic-gate 		    cpu_infop->aci_local_id << APIC_ID_BIT_OFFSET);
43350Sstevel@tonic-gate 	}
43360Sstevel@tonic-gate 	if ((airq_temp_cpu != IRQ_UNBOUND) && (airq_temp_cpu != IRQ_UNINIT)) {
43370Sstevel@tonic-gate 		apic_cpus[airq_temp_cpu].aci_temp_bound--;
43380Sstevel@tonic-gate 	}
43390Sstevel@tonic-gate 	if (!APIC_IS_MSI_OR_MSIX_INDEX(irq_ptr->airq_mps_intr_index)) {
43400Sstevel@tonic-gate 		/* Write the vector, trigger, and polarity portion of the RDT */
43410Sstevel@tonic-gate 		WRITE_IOAPIC_RDT_ENTRY_LOW_DWORD(ioapic, intin_no,
43420Sstevel@tonic-gate 		    AV_PDEST | AV_FIXED | irq_ptr->airq_rdt_entry);
43430Sstevel@tonic-gate 	} else {
4344916Sschwartz 		int type = (irq_ptr->airq_mps_intr_index == MSI_INDEX) ?
4345916Sschwartz 		    DDI_INTR_TYPE_MSI : DDI_INTR_TYPE_MSIX;
4346916Sschwartz 		(void) pci_msi_disable_mode(irq_ptr->airq_dip, type,
4347916Sschwartz 		    irq_ptr->airq_ioapicindex);
43480Sstevel@tonic-gate 		if (irq_ptr->airq_ioapicindex == irq_ptr->airq_origirq) {
43490Sstevel@tonic-gate 			/* first one */
43500Sstevel@tonic-gate 			DDI_INTR_IMPLDBG((CE_CONT, "apic_rebind: call "
43510Sstevel@tonic-gate 			    "apic_pci_msi_enable_vector\n"));
4352916Sschwartz 			if (apic_pci_msi_enable_vector(irq_ptr->airq_dip, type,
4353916Sschwartz 			    which_irq, irq_ptr->airq_vector,
4354916Sschwartz 			    irq_ptr->airq_intin_no,
43550Sstevel@tonic-gate 			    cpu_infop->aci_local_id) != PSM_SUCCESS) {
43560Sstevel@tonic-gate 				cmn_err(CE_WARN, "pcplusmp: "
43570Sstevel@tonic-gate 					"apic_pci_msi_enable_vector "
43580Sstevel@tonic-gate 					"returned PSM_FAILURE");
43590Sstevel@tonic-gate 			}
43600Sstevel@tonic-gate 		}
43610Sstevel@tonic-gate 		if ((irq_ptr->airq_ioapicindex + irq_ptr->airq_intin_no - 1) ==
43620Sstevel@tonic-gate 		    irq_ptr->airq_origirq) { /* last one */
43630Sstevel@tonic-gate 			DDI_INTR_IMPLDBG((CE_CONT, "apic_rebind: call "
43640Sstevel@tonic-gate 			    "pci_msi_enable_mode\n"));
4365916Sschwartz 			if (pci_msi_enable_mode(irq_ptr->airq_dip, type,
43660Sstevel@tonic-gate 			    which_irq) != DDI_SUCCESS) {
43670Sstevel@tonic-gate 				DDI_INTR_IMPLDBG((CE_CONT, "pcplusmp: "
43680Sstevel@tonic-gate 				    "pci_msi_enable failed\n"));
43690Sstevel@tonic-gate 				(void) pci_msi_unconfigure(irq_ptr->airq_dip,
43700Sstevel@tonic-gate 				(irq_ptr->airq_mps_intr_index == MSI_INDEX) ?
43710Sstevel@tonic-gate 				DDI_INTR_TYPE_MSI : DDI_INTR_TYPE_MSIX,
43720Sstevel@tonic-gate 				which_irq);
43730Sstevel@tonic-gate 			}
43740Sstevel@tonic-gate 		}
43750Sstevel@tonic-gate 	}
43760Sstevel@tonic-gate 	if (acquire_lock)
43770Sstevel@tonic-gate 		lock_clear(&apic_ioapic_lock);
43780Sstevel@tonic-gate 	irq_ptr->airq_temp_cpu = (uchar_t)bind_cpu;
43790Sstevel@tonic-gate 	apic_redist_cpu_skip &= ~(1 << (bind_cpu & ~IRQ_USER_BOUND));
43800Sstevel@tonic-gate 	intr_restore(iflag);
43810Sstevel@tonic-gate 	return (0);
43820Sstevel@tonic-gate }
43830Sstevel@tonic-gate 
43840Sstevel@tonic-gate /*
43850Sstevel@tonic-gate  * Checks to see if the IOAPIC interrupt entry specified has its Remote IRR
43860Sstevel@tonic-gate  * bit set.  Sets up a timeout to perform the reprogramming at a later time
43870Sstevel@tonic-gate  * if it cannot wait for the Remote IRR bit to clear (or if waiting did not
43880Sstevel@tonic-gate  * result in the bit's clearing).
43890Sstevel@tonic-gate  *
43900Sstevel@tonic-gate  * This function will mask the RDT entry if the Remote IRR bit is set.
43910Sstevel@tonic-gate  *
43920Sstevel@tonic-gate  * Returns non-zero if the caller should defer IOAPIC reprogramming.
43930Sstevel@tonic-gate  */
43940Sstevel@tonic-gate static int
43950Sstevel@tonic-gate apic_check_stuck_interrupt(apic_irq_t *irq_ptr, int old_bind_cpu,
43960Sstevel@tonic-gate 	int new_bind_cpu, volatile int32_t *ioapic, int intin_no, int which_irq)
43970Sstevel@tonic-gate {
43980Sstevel@tonic-gate 	int32_t			rdt_entry;
43990Sstevel@tonic-gate 	int			waited;
44000Sstevel@tonic-gate 
44010Sstevel@tonic-gate 	/* Mask the RDT entry, but only if it's a level-triggered interrupt */
44020Sstevel@tonic-gate 	rdt_entry = READ_IOAPIC_RDT_ENTRY_LOW_DWORD(ioapic, intin_no);
44030Sstevel@tonic-gate 	if ((rdt_entry & (AV_LEVEL|AV_MASK)) == AV_LEVEL) {
44040Sstevel@tonic-gate 
44050Sstevel@tonic-gate 		/* Mask it */
44060Sstevel@tonic-gate 		WRITE_IOAPIC_RDT_ENTRY_LOW_DWORD(ioapic, intin_no,
44070Sstevel@tonic-gate 		    AV_MASK | rdt_entry);
44080Sstevel@tonic-gate 	}
44090Sstevel@tonic-gate 
44100Sstevel@tonic-gate 	/*
44110Sstevel@tonic-gate 	 * Wait for the delivery pending bit to clear.
44120Sstevel@tonic-gate 	 */
44130Sstevel@tonic-gate 	if ((READ_IOAPIC_RDT_ENTRY_LOW_DWORD(ioapic, intin_no) &
44140Sstevel@tonic-gate 	    (AV_LEVEL|AV_PENDING)) == (AV_LEVEL|AV_PENDING)) {
44150Sstevel@tonic-gate 
44160Sstevel@tonic-gate 		/*
44170Sstevel@tonic-gate 		 * If we're still waiting on the delivery of this interrupt,
44180Sstevel@tonic-gate 		 * continue to wait here until it is delivered (this should be
44190Sstevel@tonic-gate 		 * a very small amount of time, but include a timeout just in
44200Sstevel@tonic-gate 		 * case).
44210Sstevel@tonic-gate 		 */
44220Sstevel@tonic-gate 		for (waited = 0; waited < apic_max_usecs_clear_pending;
44230Sstevel@tonic-gate 		    waited += APIC_USECS_PER_WAIT_INTERVAL) {
44240Sstevel@tonic-gate 			if ((READ_IOAPIC_RDT_ENTRY_LOW_DWORD(ioapic, intin_no)
44250Sstevel@tonic-gate 			    & AV_PENDING) == 0) {
44260Sstevel@tonic-gate 				break;
44270Sstevel@tonic-gate 			}
44280Sstevel@tonic-gate 			drv_usecwait(APIC_USECS_PER_WAIT_INTERVAL);
44290Sstevel@tonic-gate 		}
44300Sstevel@tonic-gate 
44310Sstevel@tonic-gate 		if ((READ_IOAPIC_RDT_ENTRY_LOW_DWORD(ioapic, intin_no) &
44320Sstevel@tonic-gate 		    AV_PENDING) != 0) {
44330Sstevel@tonic-gate 			cmn_err(CE_WARN, "!IOAPIC %d intin %d: Could not "
44340Sstevel@tonic-gate 			    "deliver interrupt to local APIC within "
44350Sstevel@tonic-gate 			    "%d usecs.", irq_ptr->airq_ioapicindex,
44360Sstevel@tonic-gate 			    irq_ptr->airq_intin_no,
44370Sstevel@tonic-gate 			    apic_max_usecs_clear_pending);
44380Sstevel@tonic-gate 		}
44390Sstevel@tonic-gate 	}
44400Sstevel@tonic-gate 
44410Sstevel@tonic-gate 	/*
44420Sstevel@tonic-gate 	 * If the remote IRR bit is set, then the interrupt has been sent
44430Sstevel@tonic-gate 	 * to a CPU for processing.  We have no choice but to wait for
44440Sstevel@tonic-gate 	 * that CPU to process the interrupt, at which point the remote IRR
44450Sstevel@tonic-gate 	 * bit will be cleared.
44460Sstevel@tonic-gate 	 */
44470Sstevel@tonic-gate 	if ((READ_IOAPIC_RDT_ENTRY_LOW_DWORD(ioapic, intin_no) &
44480Sstevel@tonic-gate 	    (AV_LEVEL|AV_REMOTE_IRR)) == (AV_LEVEL|AV_REMOTE_IRR)) {
44490Sstevel@tonic-gate 
44500Sstevel@tonic-gate 		/*
44510Sstevel@tonic-gate 		 * If the CPU that this RDT is bound to is NOT the current
44520Sstevel@tonic-gate 		 * CPU, wait until that CPU handles the interrupt and ACKs
44530Sstevel@tonic-gate 		 * it.  If this interrupt is not bound to any CPU (that is,
44540Sstevel@tonic-gate 		 * if it's bound to the logical destination of "anyone"), it
44550Sstevel@tonic-gate 		 * may have been delivered to the current CPU so handle that
44560Sstevel@tonic-gate 		 * case by deferring the reprogramming (below).
44570Sstevel@tonic-gate 		 */
44580Sstevel@tonic-gate 		kpreempt_disable();
44590Sstevel@tonic-gate 		if ((old_bind_cpu != IRQ_UNBOUND) &&
44600Sstevel@tonic-gate 		    (old_bind_cpu != IRQ_UNINIT) &&
44610Sstevel@tonic-gate 		    (old_bind_cpu != psm_get_cpu_id())) {
44620Sstevel@tonic-gate 			for (waited = 0; waited < apic_max_usecs_clear_pending;
44630Sstevel@tonic-gate 			    waited += APIC_USECS_PER_WAIT_INTERVAL) {
44640Sstevel@tonic-gate 				if ((READ_IOAPIC_RDT_ENTRY_LOW_DWORD(ioapic,
44650Sstevel@tonic-gate 				    intin_no) & AV_REMOTE_IRR) == 0) {
44660Sstevel@tonic-gate 
44670Sstevel@tonic-gate 					/* Clear the reprogramming state: */
44680Sstevel@tonic-gate 					lock_set(&apic_ioapic_reprogram_lock);
44690Sstevel@tonic-gate 
44700Sstevel@tonic-gate 					apic_reprogram_info[which_irq].valid
44710Sstevel@tonic-gate 					    = 0;
44720Sstevel@tonic-gate 					apic_reprogram_info[which_irq].bindcpu
44730Sstevel@tonic-gate 					    = 0;
44740Sstevel@tonic-gate 					apic_reprogram_info[which_irq].timeouts
44750Sstevel@tonic-gate 					    = 0;
44760Sstevel@tonic-gate 
44770Sstevel@tonic-gate 					lock_clear(&apic_ioapic_reprogram_lock);
44780Sstevel@tonic-gate 
44790Sstevel@tonic-gate 					/* Remote IRR has cleared! */
44800Sstevel@tonic-gate 					kpreempt_enable();
44810Sstevel@tonic-gate 					return (0);
44820Sstevel@tonic-gate 				}
44830Sstevel@tonic-gate 				drv_usecwait(APIC_USECS_PER_WAIT_INTERVAL);
44840Sstevel@tonic-gate 			}
44850Sstevel@tonic-gate 		}
44860Sstevel@tonic-gate 		kpreempt_enable();
44870Sstevel@tonic-gate 
44880Sstevel@tonic-gate 		/*
44890Sstevel@tonic-gate 		 * If we waited and the Remote IRR bit is still not cleared,
44900Sstevel@tonic-gate 		 * AND if we've invoked the timeout APIC_REPROGRAM_MAX_TIMEOUTS
44910Sstevel@tonic-gate 		 * times for this interrupt, try the last-ditch workarounds:
44920Sstevel@tonic-gate 		 */
44930Sstevel@tonic-gate 		if (apic_reprogram_info[which_irq].timeouts >=
44940Sstevel@tonic-gate 		    APIC_REPROGRAM_MAX_TIMEOUTS) {
44950Sstevel@tonic-gate 
44960Sstevel@tonic-gate 			if ((READ_IOAPIC_RDT_ENTRY_LOW_DWORD(ioapic, intin_no)
44970Sstevel@tonic-gate 			    & AV_REMOTE_IRR) != 0) {
44980Sstevel@tonic-gate 				/*
44990Sstevel@tonic-gate 				 * Trying to clear the bit through normal
45000Sstevel@tonic-gate 				 * channels has failed.  So as a last-ditch
45010Sstevel@tonic-gate 				 * effort, try to set the trigger mode to
45020Sstevel@tonic-gate 				 * edge, then to level.  This has been
45030Sstevel@tonic-gate 				 * observed to work on many systems.
45040Sstevel@tonic-gate 				 */
45050Sstevel@tonic-gate 				WRITE_IOAPIC_RDT_ENTRY_LOW_DWORD(ioapic,
45060Sstevel@tonic-gate 				    intin_no,
45070Sstevel@tonic-gate 				    READ_IOAPIC_RDT_ENTRY_LOW_DWORD(ioapic,
45080Sstevel@tonic-gate 				    intin_no) & ~AV_LEVEL);
45090Sstevel@tonic-gate 
45100Sstevel@tonic-gate 				WRITE_IOAPIC_RDT_ENTRY_LOW_DWORD(ioapic,
45110Sstevel@tonic-gate 				    intin_no,
45120Sstevel@tonic-gate 				    READ_IOAPIC_RDT_ENTRY_LOW_DWORD(ioapic,
45130Sstevel@tonic-gate 				    intin_no) | AV_LEVEL);
45140Sstevel@tonic-gate 
45150Sstevel@tonic-gate 				/*
45160Sstevel@tonic-gate 				 * If the bit's STILL set, declare total and
45170Sstevel@tonic-gate 				 * utter failure
45180Sstevel@tonic-gate 				 */
45190Sstevel@tonic-gate 				if ((READ_IOAPIC_RDT_ENTRY_LOW_DWORD(ioapic,
45200Sstevel@tonic-gate 				    intin_no) & AV_REMOTE_IRR) != 0) {
45210Sstevel@tonic-gate 					cmn_err(CE_WARN, "!IOAPIC %d intin %d: "
45220Sstevel@tonic-gate 					    "Remote IRR failed to reset "
45230Sstevel@tonic-gate 					    "within %d usecs.  Interrupts to "
45240Sstevel@tonic-gate 					    "this pin may cease to function.",
45250Sstevel@tonic-gate 					    irq_ptr->airq_ioapicindex,
45260Sstevel@tonic-gate 					    irq_ptr->airq_intin_no,
45270Sstevel@tonic-gate 					    apic_max_usecs_clear_pending);
45280Sstevel@tonic-gate 				}
45290Sstevel@tonic-gate 			}
45300Sstevel@tonic-gate 			/* Clear the reprogramming state: */
45310Sstevel@tonic-gate 			lock_set(&apic_ioapic_reprogram_lock);
45320Sstevel@tonic-gate 
45330Sstevel@tonic-gate 			apic_reprogram_info[which_irq].valid = 0;
45340Sstevel@tonic-gate 			apic_reprogram_info[which_irq].bindcpu = 0;
45350Sstevel@tonic-gate 			apic_reprogram_info[which_irq].timeouts = 0;
45360Sstevel@tonic-gate 
45370Sstevel@tonic-gate 			lock_clear(&apic_ioapic_reprogram_lock);
45380Sstevel@tonic-gate 		} else {
45390Sstevel@tonic-gate #ifdef DEBUG
45400Sstevel@tonic-gate 			cmn_err(CE_WARN, "Deferring reprogramming of irq %d",
45410Sstevel@tonic-gate 			    which_irq);
45420Sstevel@tonic-gate #endif	/* DEBUG */
45430Sstevel@tonic-gate 			/*
45440Sstevel@tonic-gate 			 * If waiting for the Remote IRR bit (above) didn't
45450Sstevel@tonic-gate 			 * allow it to clear, defer the reprogramming:
45460Sstevel@tonic-gate 			 */
45470Sstevel@tonic-gate 			lock_set(&apic_ioapic_reprogram_lock);
45480Sstevel@tonic-gate 
45490Sstevel@tonic-gate 			apic_reprogram_info[which_irq].valid = 1;
45500Sstevel@tonic-gate 			apic_reprogram_info[which_irq].bindcpu = new_bind_cpu;
45510Sstevel@tonic-gate 			apic_reprogram_info[which_irq].timeouts++;
45520Sstevel@tonic-gate 
45530Sstevel@tonic-gate 			lock_clear(&apic_ioapic_reprogram_lock);
45540Sstevel@tonic-gate 
45550Sstevel@tonic-gate 			/* Fire up a timeout to handle this later */
45560Sstevel@tonic-gate 			(void) timeout(apic_reprogram_timeout_handler,
45570Sstevel@tonic-gate 			    (void *) 0,
45580Sstevel@tonic-gate 			    drv_usectohz(APIC_REPROGRAM_TIMEOUT_DELAY));
45590Sstevel@tonic-gate 
45600Sstevel@tonic-gate 			/* Inform caller to defer IOAPIC programming: */
45610Sstevel@tonic-gate 			return (1);
45620Sstevel@tonic-gate 		}
45630Sstevel@tonic-gate 	}
45640Sstevel@tonic-gate 	return (0);
45650Sstevel@tonic-gate }
45660Sstevel@tonic-gate 
45670Sstevel@tonic-gate /*
45680Sstevel@tonic-gate  * Timeout handler that performs the APIC reprogramming
45690Sstevel@tonic-gate  */
45700Sstevel@tonic-gate /*ARGSUSED*/
45710Sstevel@tonic-gate static void
45720Sstevel@tonic-gate apic_reprogram_timeout_handler(void *arg)
45730Sstevel@tonic-gate {
45740Sstevel@tonic-gate 	/*LINTED: set but not used in function*/
45750Sstevel@tonic-gate 	int i, result;
45760Sstevel@tonic-gate 
45770Sstevel@tonic-gate 	/* Serialize access to this function */
45780Sstevel@tonic-gate 	mutex_enter(&apic_reprogram_timeout_mutex);
45790Sstevel@tonic-gate 
45800Sstevel@tonic-gate 	/*
45810Sstevel@tonic-gate 	 * For each entry in the reprogramming state that's valid,
45820Sstevel@tonic-gate 	 * try the reprogramming again:
45830Sstevel@tonic-gate 	 */
45840Sstevel@tonic-gate 	for (i = 0; i < APIC_MAX_VECTOR; i++) {
45850Sstevel@tonic-gate 		if (apic_reprogram_info[i].valid == 0)
45860Sstevel@tonic-gate 			continue;
45870Sstevel@tonic-gate 		/*
45880Sstevel@tonic-gate 		 * Though we can't really do anything about errors
45890Sstevel@tonic-gate 		 * at this point, keep track of them for reporting.
45900Sstevel@tonic-gate 		 * Note that it is very possible for apic_setup_io_intr
45910Sstevel@tonic-gate 		 * to re-register this very timeout if the Remote IRR bit
45920Sstevel@tonic-gate 		 * has not yet cleared.
45930Sstevel@tonic-gate 		 */
45940Sstevel@tonic-gate 		result = apic_setup_io_intr_deferred(apic_irq_table[i], i);
45950Sstevel@tonic-gate 
45960Sstevel@tonic-gate #ifdef DEBUG
45970Sstevel@tonic-gate 		if (result)
45980Sstevel@tonic-gate 			cmn_err(CE_WARN, "apic_reprogram_timeout: "
45990Sstevel@tonic-gate 			    "apic_setup_io_intr returned nonzero for "
46000Sstevel@tonic-gate 			    "irq=%d!", i);
46010Sstevel@tonic-gate #endif	/* DEBUG */
46020Sstevel@tonic-gate 	}
46030Sstevel@tonic-gate 
46040Sstevel@tonic-gate 	mutex_exit(&apic_reprogram_timeout_mutex);
46050Sstevel@tonic-gate }
46060Sstevel@tonic-gate 
46070Sstevel@tonic-gate 
46080Sstevel@tonic-gate /*
46090Sstevel@tonic-gate  * Called to migrate all interrupts at an irq to another cpu. safe
46100Sstevel@tonic-gate  * if true means we are not being called from an interrupt
46110Sstevel@tonic-gate  * context and hence it is safe to do a lock_set. If false
46120Sstevel@tonic-gate  * do only a lock_try and return failure ( non 0 ) if we cannot get it
46130Sstevel@tonic-gate  */
4614916Sschwartz int
46150Sstevel@tonic-gate apic_rebind_all(apic_irq_t *irq_ptr, int bind_cpu, int safe)
46160Sstevel@tonic-gate {
46170Sstevel@tonic-gate 	apic_irq_t	*irqptr = irq_ptr;
46180Sstevel@tonic-gate 	int		retval = 0;
46190Sstevel@tonic-gate 	int		iflag;
46200Sstevel@tonic-gate 
46210Sstevel@tonic-gate 	iflag = intr_clear();
46220Sstevel@tonic-gate 	if (!safe) {
46230Sstevel@tonic-gate 		if (lock_try(&apic_ioapic_lock) == 0) {
46240Sstevel@tonic-gate 			intr_restore(iflag);
46250Sstevel@tonic-gate 			return (1);
46260Sstevel@tonic-gate 		}
46270Sstevel@tonic-gate 	} else
46280Sstevel@tonic-gate 		lock_set(&apic_ioapic_lock);
46290Sstevel@tonic-gate 
46300Sstevel@tonic-gate 	while (irqptr) {
46310Sstevel@tonic-gate 		if (irqptr->airq_temp_cpu != IRQ_UNINIT)
46320Sstevel@tonic-gate 			retval |= apic_rebind(irqptr, bind_cpu, 0, IMMEDIATE);
46330Sstevel@tonic-gate 		irqptr = irqptr->airq_next;
46340Sstevel@tonic-gate 	}
46350Sstevel@tonic-gate 	lock_clear(&apic_ioapic_lock);
46360Sstevel@tonic-gate 	intr_restore(iflag);
46370Sstevel@tonic-gate 	return (retval);
46380Sstevel@tonic-gate }
46390Sstevel@tonic-gate 
46400Sstevel@tonic-gate /*
46410Sstevel@tonic-gate  * apic_intr_redistribute does all the messy computations for identifying
46420Sstevel@tonic-gate  * which interrupt to move to which CPU. Currently we do just one interrupt
46430Sstevel@tonic-gate  * at a time. This reduces the time we spent doing all this within clock
46440Sstevel@tonic-gate  * interrupt. When it is done in idle, we could do more than 1.
46450Sstevel@tonic-gate  * First we find the most busy and the most free CPU (time in ISR only)
46460Sstevel@tonic-gate  * skipping those CPUs that has been identified as being ineligible (cpu_skip)
46470Sstevel@tonic-gate  * Then we look for IRQs which are closest to the difference between the
46480Sstevel@tonic-gate  * most busy CPU and the average ISR load. We try to find one whose load
46490Sstevel@tonic-gate  * is less than difference.If none exists, then we chose one larger than the
46500Sstevel@tonic-gate  * difference, provided it does not make the most idle CPU worse than the
46510Sstevel@tonic-gate  * most busy one. In the end, we clear all the busy fields for CPUs. For
46520Sstevel@tonic-gate  * IRQs, they are cleared as they are scanned.
46530Sstevel@tonic-gate  */
46540Sstevel@tonic-gate static void
46550Sstevel@tonic-gate apic_intr_redistribute()
46560Sstevel@tonic-gate {
46570Sstevel@tonic-gate 	int busiest_cpu, most_free_cpu;
46580Sstevel@tonic-gate 	int cpu_free, cpu_busy, max_busy, min_busy;
46590Sstevel@tonic-gate 	int min_free, diff;
46600Sstevel@tonic-gate 	int	average_busy, cpus_online;
46610Sstevel@tonic-gate 	int i, busy;
46620Sstevel@tonic-gate 	apic_cpus_info_t *cpu_infop;
46630Sstevel@tonic-gate 	apic_irq_t *min_busy_irq = NULL;
46640Sstevel@tonic-gate 	apic_irq_t *max_busy_irq = NULL;
46650Sstevel@tonic-gate 
46660Sstevel@tonic-gate 	busiest_cpu = most_free_cpu = -1;
46670Sstevel@tonic-gate 	cpu_free = cpu_busy = max_busy = average_busy = 0;
46680Sstevel@tonic-gate 	min_free = apic_sample_factor_redistribution;
46690Sstevel@tonic-gate 	cpus_online = 0;
46700Sstevel@tonic-gate 	/*
46710Sstevel@tonic-gate 	 * Below we will check for CPU_INTR_ENABLE, bound, temp_bound, temp_cpu
46720Sstevel@tonic-gate 	 * without ioapic_lock. That is OK as we are just doing statistical
46730Sstevel@tonic-gate 	 * sampling anyway and any inaccuracy now will get corrected next time
46740Sstevel@tonic-gate 	 * The call to rebind which actually changes things will make sure
46750Sstevel@tonic-gate 	 * we are consistent.
46760Sstevel@tonic-gate 	 */
46770Sstevel@tonic-gate 	for (i = 0; i < apic_nproc; i++) {
46780Sstevel@tonic-gate 		if (!(apic_redist_cpu_skip & (1 << i)) &&
46790Sstevel@tonic-gate 		    (apic_cpus[i].aci_status & APIC_CPU_INTR_ENABLE)) {
46800Sstevel@tonic-gate 
46810Sstevel@tonic-gate 			cpu_infop = &apic_cpus[i];
46820Sstevel@tonic-gate 			/*
46830Sstevel@tonic-gate 			 * If no unbound interrupts or only 1 total on this
46840Sstevel@tonic-gate 			 * CPU, skip
46850Sstevel@tonic-gate 			 */
46860Sstevel@tonic-gate 			if (!cpu_infop->aci_temp_bound ||
46870Sstevel@tonic-gate 			    (cpu_infop->aci_bound + cpu_infop->aci_temp_bound)
46880Sstevel@tonic-gate 			    == 1) {
46890Sstevel@tonic-gate 				apic_redist_cpu_skip |= 1 << i;
46900Sstevel@tonic-gate 				continue;
46910Sstevel@tonic-gate 			}
46920Sstevel@tonic-gate 
46930Sstevel@tonic-gate 			busy = cpu_infop->aci_busy;
46940Sstevel@tonic-gate 			average_busy += busy;
46950Sstevel@tonic-gate 			cpus_online++;
46960Sstevel@tonic-gate 			if (max_busy < busy) {
46970Sstevel@tonic-gate 				max_busy = busy;
46980Sstevel@tonic-gate 				busiest_cpu = i;
46990Sstevel@tonic-gate 			}
47000Sstevel@tonic-gate 			if (min_free > busy) {
47010Sstevel@tonic-gate 				min_free = busy;
47020Sstevel@tonic-gate 				most_free_cpu = i;
47030Sstevel@tonic-gate 			}
47040Sstevel@tonic-gate 			if (busy > apic_int_busy_mark) {
47050Sstevel@tonic-gate 				cpu_busy |= 1 << i;
47060Sstevel@tonic-gate 			} else {
47070Sstevel@tonic-gate 				if (busy < apic_int_free_mark)
47080Sstevel@tonic-gate 					cpu_free |= 1 << i;
47090Sstevel@tonic-gate 			}
47100Sstevel@tonic-gate 		}
47110Sstevel@tonic-gate 	}
47120Sstevel@tonic-gate 	if ((cpu_busy && cpu_free) ||
47130Sstevel@tonic-gate 	    (max_busy >= (min_free + apic_diff_for_redistribution))) {
47140Sstevel@tonic-gate 
47150Sstevel@tonic-gate 		apic_num_imbalance++;
47160Sstevel@tonic-gate #ifdef	DEBUG
47170Sstevel@tonic-gate 		if (apic_verbose & APIC_VERBOSE_IOAPIC_FLAG) {
47180Sstevel@tonic-gate 			prom_printf(
47190Sstevel@tonic-gate 			    "redistribute busy=%x free=%x max=%x min=%x",
47200Sstevel@tonic-gate 			    cpu_busy, cpu_free, max_busy, min_free);
47210Sstevel@tonic-gate 		}
47220Sstevel@tonic-gate #endif /* DEBUG */
47230Sstevel@tonic-gate 
47240Sstevel@tonic-gate 
47250Sstevel@tonic-gate 		average_busy /= cpus_online;
47260Sstevel@tonic-gate 
47270Sstevel@tonic-gate 		diff = max_busy - average_busy;
47280Sstevel@tonic-gate 		min_busy = max_busy; /* start with the max possible value */
47290Sstevel@tonic-gate 		max_busy = 0;
47300Sstevel@tonic-gate 		min_busy_irq = max_busy_irq = NULL;
47310Sstevel@tonic-gate 		i = apic_min_device_irq;
47320Sstevel@tonic-gate 		for (; i < apic_max_device_irq; i++) {
47330Sstevel@tonic-gate 			apic_irq_t *irq_ptr;
47340Sstevel@tonic-gate 			/* Change to linked list per CPU ? */
47350Sstevel@tonic-gate 			if ((irq_ptr = apic_irq_table[i]) == NULL)
47360Sstevel@tonic-gate 				continue;
47370Sstevel@tonic-gate 			/* Check for irq_busy & decide which one to move */
47380Sstevel@tonic-gate 			/* Also zero them for next round */
47390Sstevel@tonic-gate 			if ((irq_ptr->airq_temp_cpu == busiest_cpu) &&
47400Sstevel@tonic-gate 			    irq_ptr->airq_busy) {
47410Sstevel@tonic-gate 				if (irq_ptr->airq_busy < diff) {
47420Sstevel@tonic-gate 					/*
47430Sstevel@tonic-gate 					 * Check for least busy CPU,
47440Sstevel@tonic-gate 					 * best fit or what ?
47450Sstevel@tonic-gate 					 */
47460Sstevel@tonic-gate 					if (max_busy < irq_ptr->airq_busy) {
47470Sstevel@tonic-gate 						/*
47480Sstevel@tonic-gate 						 * Most busy within the
47490Sstevel@tonic-gate 						 * required differential
47500Sstevel@tonic-gate 						 */
47510Sstevel@tonic-gate 						max_busy = irq_ptr->airq_busy;
47520Sstevel@tonic-gate 						max_busy_irq = irq_ptr;
47530Sstevel@tonic-gate 					}
47540Sstevel@tonic-gate 				} else {
47550Sstevel@tonic-gate 					if (min_busy > irq_ptr->airq_busy) {
47560Sstevel@tonic-gate 						/*
47570Sstevel@tonic-gate 						 * least busy, but more than
47580Sstevel@tonic-gate 						 * the reqd diff
47590Sstevel@tonic-gate 						 */
47600Sstevel@tonic-gate 						if (min_busy <
47610Sstevel@tonic-gate 						    (diff + average_busy -
47620Sstevel@tonic-gate 						    min_free)) {
47630Sstevel@tonic-gate 							/*
47640Sstevel@tonic-gate 							 * Making sure new cpu
47650Sstevel@tonic-gate 							 * will not end up
47660Sstevel@tonic-gate 							 * worse
47670Sstevel@tonic-gate 							 */
47680Sstevel@tonic-gate 							min_busy =
47690Sstevel@tonic-gate 							    irq_ptr->airq_busy;
47700Sstevel@tonic-gate 
47710Sstevel@tonic-gate 							min_busy_irq = irq_ptr;
47720Sstevel@tonic-gate 						}
47730Sstevel@tonic-gate 					}
47740Sstevel@tonic-gate 				}
47750Sstevel@tonic-gate 			}
47760Sstevel@tonic-gate 			irq_ptr->airq_busy = 0;
47770Sstevel@tonic-gate 		}
47780Sstevel@tonic-gate 
47790Sstevel@tonic-gate 		if (max_busy_irq != NULL) {
47800Sstevel@tonic-gate #ifdef	DEBUG
47810Sstevel@tonic-gate 			if (apic_verbose & APIC_VERBOSE_IOAPIC_FLAG) {
47820Sstevel@tonic-gate 				prom_printf("rebinding %x to %x",
47830Sstevel@tonic-gate 				    max_busy_irq->airq_vector, most_free_cpu);
47840Sstevel@tonic-gate 			}
47850Sstevel@tonic-gate #endif /* DEBUG */
47860Sstevel@tonic-gate 			if (apic_rebind_all(max_busy_irq, most_free_cpu, 0)
47870Sstevel@tonic-gate 			    == 0)
47880Sstevel@tonic-gate 				/* Make change permenant */
47890Sstevel@tonic-gate 				max_busy_irq->airq_cpu = (uchar_t)most_free_cpu;
47900Sstevel@tonic-gate 		} else if (min_busy_irq != NULL) {
47910Sstevel@tonic-gate #ifdef	DEBUG
47920Sstevel@tonic-gate 			if (apic_verbose & APIC_VERBOSE_IOAPIC_FLAG) {
47930Sstevel@tonic-gate 				prom_printf("rebinding %x to %x",
47940Sstevel@tonic-gate 				    min_busy_irq->airq_vector, most_free_cpu);
47950Sstevel@tonic-gate 			}
47960Sstevel@tonic-gate #endif /* DEBUG */
47970Sstevel@tonic-gate 
47980Sstevel@tonic-gate 			if (apic_rebind_all(min_busy_irq, most_free_cpu, 0) ==
47990Sstevel@tonic-gate 			    0)
48000Sstevel@tonic-gate 				/* Make change permenant */
48010Sstevel@tonic-gate 				min_busy_irq->airq_cpu = (uchar_t)most_free_cpu;
48020Sstevel@tonic-gate 		} else {
48030Sstevel@tonic-gate 			if (cpu_busy != (1 << busiest_cpu)) {
48040Sstevel@tonic-gate 				apic_redist_cpu_skip |= 1 << busiest_cpu;
48050Sstevel@tonic-gate 				/*
48060Sstevel@tonic-gate 				 * We leave cpu_skip set so that next time we
48070Sstevel@tonic-gate 				 * can choose another cpu
48080Sstevel@tonic-gate 				 */
48090Sstevel@tonic-gate 			}
48100Sstevel@tonic-gate 		}
48110Sstevel@tonic-gate 		apic_num_rebind++;
48120Sstevel@tonic-gate 	} else {
48130Sstevel@tonic-gate 		/*
48140Sstevel@tonic-gate 		 * found nothing. Could be that we skipped over valid CPUs
48150Sstevel@tonic-gate 		 * or we have balanced everything. If we had a variable
48160Sstevel@tonic-gate 		 * ticks_for_redistribution, it could be increased here.
48170Sstevel@tonic-gate 		 * apic_int_busy, int_free etc would also need to be
48180Sstevel@tonic-gate 		 * changed.
48190Sstevel@tonic-gate 		 */
48200Sstevel@tonic-gate 		if (apic_redist_cpu_skip)
48210Sstevel@tonic-gate 			apic_redist_cpu_skip = 0;
48220Sstevel@tonic-gate 	}
48230Sstevel@tonic-gate 	for (i = 0; i < apic_nproc; i++) {
48240Sstevel@tonic-gate 		apic_cpus[i].aci_busy = 0;
48250Sstevel@tonic-gate 	}
48260Sstevel@tonic-gate }
48270Sstevel@tonic-gate 
48280Sstevel@tonic-gate static void
48290Sstevel@tonic-gate apic_cleanup_busy()
48300Sstevel@tonic-gate {
48310Sstevel@tonic-gate 	int i;
48320Sstevel@tonic-gate 	apic_irq_t *irq_ptr;
48330Sstevel@tonic-gate 
48340Sstevel@tonic-gate 	for (i = 0; i < apic_nproc; i++) {
48350Sstevel@tonic-gate 		apic_cpus[i].aci_busy = 0;
48360Sstevel@tonic-gate 	}
48370Sstevel@tonic-gate 
48380Sstevel@tonic-gate 	for (i = apic_min_device_irq; i < apic_max_device_irq; i++) {
48390Sstevel@tonic-gate 		if ((irq_ptr = apic_irq_table[i]) != NULL)
48400Sstevel@tonic-gate 			irq_ptr->airq_busy = 0;
48410Sstevel@tonic-gate 	}
48420Sstevel@tonic-gate 	apic_skipped_redistribute = 0;
48430Sstevel@tonic-gate }
48440Sstevel@tonic-gate 
48450Sstevel@tonic-gate 
48460Sstevel@tonic-gate /*
48470Sstevel@tonic-gate  * This function will reprogram the timer.
48480Sstevel@tonic-gate  *
48490Sstevel@tonic-gate  * When in oneshot mode the argument is the absolute time in future to
48500Sstevel@tonic-gate  * generate the interrupt at.
48510Sstevel@tonic-gate  *
48520Sstevel@tonic-gate  * When in periodic mode, the argument is the interval at which the
48530Sstevel@tonic-gate  * interrupts should be generated. There is no need to support the periodic
48540Sstevel@tonic-gate  * mode timer change at this time.
48550Sstevel@tonic-gate  */
48560Sstevel@tonic-gate static void
48570Sstevel@tonic-gate apic_timer_reprogram(hrtime_t time)
48580Sstevel@tonic-gate {
48590Sstevel@tonic-gate 	hrtime_t now;
48600Sstevel@tonic-gate 	uint_t ticks;
48610Sstevel@tonic-gate 
48620Sstevel@tonic-gate 	/*
48630Sstevel@tonic-gate 	 * We should be called from high PIL context (CBE_HIGH_PIL),
48640Sstevel@tonic-gate 	 * so kpreempt is disabled.
48650Sstevel@tonic-gate 	 */
48660Sstevel@tonic-gate 
48670Sstevel@tonic-gate 	if (!apic_oneshot) {
48680Sstevel@tonic-gate 		/* time is the interval for periodic mode */
48690Sstevel@tonic-gate 		ticks = (uint_t)((time) / apic_nsec_per_tick);
48700Sstevel@tonic-gate 	} else {
48710Sstevel@tonic-gate 		/* one shot mode */
48720Sstevel@tonic-gate 
48730Sstevel@tonic-gate 		now = gethrtime();
48740Sstevel@tonic-gate 
48750Sstevel@tonic-gate 		if (time <= now) {
48760Sstevel@tonic-gate 			/*
48770Sstevel@tonic-gate 			 * requested to generate an interrupt in the past
48780Sstevel@tonic-gate 			 * generate an interrupt as soon as possible
48790Sstevel@tonic-gate 			 */
48800Sstevel@tonic-gate 			ticks = apic_min_timer_ticks;
48810Sstevel@tonic-gate 		} else if ((time - now) > apic_nsec_max) {
48820Sstevel@tonic-gate 			/*
48830Sstevel@tonic-gate 			 * requested to generate an interrupt at a time
48840Sstevel@tonic-gate 			 * further than what we are capable of. Set to max
48850Sstevel@tonic-gate 			 * the hardware can handle
48860Sstevel@tonic-gate 			 */
48870Sstevel@tonic-gate 
48880Sstevel@tonic-gate 			ticks = APIC_MAXVAL;
48890Sstevel@tonic-gate #ifdef DEBUG
48900Sstevel@tonic-gate 			cmn_err(CE_CONT, "apic_timer_reprogram, request at"
48910Sstevel@tonic-gate 			    "  %lld  too far in future, current time"
48920Sstevel@tonic-gate 			    "  %lld \n", time, now);
48930Sstevel@tonic-gate #endif	/* DEBUG */
48940Sstevel@tonic-gate 		} else
48950Sstevel@tonic-gate 			ticks = (uint_t)((time - now) / apic_nsec_per_tick);
48960Sstevel@tonic-gate 	}
48970Sstevel@tonic-gate 
48980Sstevel@tonic-gate 	if (ticks < apic_min_timer_ticks)
48990Sstevel@tonic-gate 		ticks = apic_min_timer_ticks;
49000Sstevel@tonic-gate 
49010Sstevel@tonic-gate 	apicadr[APIC_INIT_COUNT] = ticks;
49020Sstevel@tonic-gate 
49030Sstevel@tonic-gate }
49040Sstevel@tonic-gate 
49050Sstevel@tonic-gate /*
49060Sstevel@tonic-gate  * This function will enable timer interrupts.
49070Sstevel@tonic-gate  */
49080Sstevel@tonic-gate static void
49090Sstevel@tonic-gate apic_timer_enable(void)
49100Sstevel@tonic-gate {
49110Sstevel@tonic-gate 	/*
49120Sstevel@tonic-gate 	 * We should be Called from high PIL context (CBE_HIGH_PIL),
49130Sstevel@tonic-gate 	 * so kpreempt is disabled.
49140Sstevel@tonic-gate 	 */
49150Sstevel@tonic-gate 
49160Sstevel@tonic-gate 	if (!apic_oneshot)
49170Sstevel@tonic-gate 		apicadr[APIC_LOCAL_TIMER] =
49180Sstevel@tonic-gate 		    (apic_clkvect + APIC_BASE_VECT) | AV_TIME;
49190Sstevel@tonic-gate 	else {
49200Sstevel@tonic-gate 		/* one shot */
49210Sstevel@tonic-gate 		apicadr[APIC_LOCAL_TIMER] = (apic_clkvect + APIC_BASE_VECT);
49220Sstevel@tonic-gate 	}
49230Sstevel@tonic-gate }
49240Sstevel@tonic-gate 
49250Sstevel@tonic-gate /*
49260Sstevel@tonic-gate  * This function will disable timer interrupts.
49270Sstevel@tonic-gate  */
49280Sstevel@tonic-gate static void
49290Sstevel@tonic-gate apic_timer_disable(void)
49300Sstevel@tonic-gate {
49310Sstevel@tonic-gate 	/*
49320Sstevel@tonic-gate 	 * We should be Called from high PIL context (CBE_HIGH_PIL),
49330Sstevel@tonic-gate 	 * so kpreempt is disabled.
49340Sstevel@tonic-gate 	 */
49350Sstevel@tonic-gate 
49360Sstevel@tonic-gate 	apicadr[APIC_LOCAL_TIMER] = (apic_clkvect + APIC_BASE_VECT) | AV_MASK;
49370Sstevel@tonic-gate }
49380Sstevel@tonic-gate 
49390Sstevel@tonic-gate 
49400Sstevel@tonic-gate cyclic_id_t apic_cyclic_id;
49410Sstevel@tonic-gate 
49420Sstevel@tonic-gate /*
49430Sstevel@tonic-gate  * If this module needs to be a consumer of cyclic subsystem, they
49440Sstevel@tonic-gate  * can be added here, since at this time kernel cyclic subsystem is initialized
49450Sstevel@tonic-gate  * argument is not currently used, and is reserved for future.
49460Sstevel@tonic-gate  */
49470Sstevel@tonic-gate static void
49480Sstevel@tonic-gate apic_post_cyclic_setup(void *arg)
49490Sstevel@tonic-gate {
49500Sstevel@tonic-gate _NOTE(ARGUNUSED(arg))
49510Sstevel@tonic-gate 	cyc_handler_t hdlr;
49520Sstevel@tonic-gate 	cyc_time_t when;
49530Sstevel@tonic-gate 
49540Sstevel@tonic-gate 	/* cpu_lock is held */
49550Sstevel@tonic-gate 
49560Sstevel@tonic-gate 	/* set up cyclics for intr redistribution */
49570Sstevel@tonic-gate 
49580Sstevel@tonic-gate 	/*
49590Sstevel@tonic-gate 	 * In peridoc mode intr redistribution processing is done in
49600Sstevel@tonic-gate 	 * apic_intr_enter during clk intr processing
49610Sstevel@tonic-gate 	 */
49620Sstevel@tonic-gate 	if (!apic_oneshot)
49630Sstevel@tonic-gate 		return;
49640Sstevel@tonic-gate 
49650Sstevel@tonic-gate 	hdlr.cyh_level = CY_LOW_LEVEL;
49660Sstevel@tonic-gate 	hdlr.cyh_func = (cyc_func_t)apic_redistribute_compute;
49670Sstevel@tonic-gate 	hdlr.cyh_arg = NULL;
49680Sstevel@tonic-gate 
49690Sstevel@tonic-gate 	when.cyt_when = 0;
49700Sstevel@tonic-gate 	when.cyt_interval = apic_redistribute_sample_interval;
49710Sstevel@tonic-gate 	apic_cyclic_id = cyclic_add(&hdlr, &when);
49720Sstevel@tonic-gate 
49730Sstevel@tonic-gate 
49740Sstevel@tonic-gate }
49750Sstevel@tonic-gate 
49760Sstevel@tonic-gate static void
49770Sstevel@tonic-gate apic_redistribute_compute(void)
49780Sstevel@tonic-gate {
49790Sstevel@tonic-gate 	int	i, j, max_busy;
49800Sstevel@tonic-gate 
49810Sstevel@tonic-gate 	if (apic_enable_dynamic_migration) {
49820Sstevel@tonic-gate 		if (++apic_nticks == apic_sample_factor_redistribution) {
49830Sstevel@tonic-gate 			/*
49840Sstevel@tonic-gate 			 * Time to call apic_intr_redistribute().
49850Sstevel@tonic-gate 			 * reset apic_nticks. This will cause max_busy
49860Sstevel@tonic-gate 			 * to be calculated below and if it is more than
49870Sstevel@tonic-gate 			 * apic_int_busy, we will do the whole thing
49880Sstevel@tonic-gate 			 */
49890Sstevel@tonic-gate 			apic_nticks = 0;
49900Sstevel@tonic-gate 		}
49910Sstevel@tonic-gate 		max_busy = 0;
49920Sstevel@tonic-gate 		for (i = 0; i < apic_nproc; i++) {
49930Sstevel@tonic-gate 
49940Sstevel@tonic-gate 			/*
49950Sstevel@tonic-gate 			 * Check if curipl is non zero & if ISR is in
49960Sstevel@tonic-gate 			 * progress
49970Sstevel@tonic-gate 			 */
49980Sstevel@tonic-gate 			if (((j = apic_cpus[i].aci_curipl) != 0) &&
49990Sstevel@tonic-gate 			    (apic_cpus[i].aci_ISR_in_progress & (1 << j))) {
50000Sstevel@tonic-gate 
50010Sstevel@tonic-gate 				int	irq;
50020Sstevel@tonic-gate 				apic_cpus[i].aci_busy++;
50030Sstevel@tonic-gate 				irq = apic_cpus[i].aci_current[j];
50040Sstevel@tonic-gate 				apic_irq_table[irq]->airq_busy++;
50050Sstevel@tonic-gate 			}
50060Sstevel@tonic-gate 
50070Sstevel@tonic-gate 			if (!apic_nticks &&
50080Sstevel@tonic-gate 			    (apic_cpus[i].aci_busy > max_busy))
50090Sstevel@tonic-gate 				max_busy = apic_cpus[i].aci_busy;
50100Sstevel@tonic-gate 		}
50110Sstevel@tonic-gate 		if (!apic_nticks) {
50120Sstevel@tonic-gate 			if (max_busy > apic_int_busy_mark) {
50130Sstevel@tonic-gate 			/*
50140Sstevel@tonic-gate 			 * We could make the following check be
50150Sstevel@tonic-gate 			 * skipped > 1 in which case, we get a
50160Sstevel@tonic-gate 			 * redistribution at half the busy mark (due to
50170Sstevel@tonic-gate 			 * double interval). Need to be able to collect
50180Sstevel@tonic-gate 			 * more empirical data to decide if that is a
50190Sstevel@tonic-gate 			 * good strategy. Punt for now.
50200Sstevel@tonic-gate 			 */
50210Sstevel@tonic-gate 				if (apic_skipped_redistribute)
50220Sstevel@tonic-gate 					apic_cleanup_busy();
50230Sstevel@tonic-gate 				else
50240Sstevel@tonic-gate 					apic_intr_redistribute();
50250Sstevel@tonic-gate 			} else
50260Sstevel@tonic-gate 				apic_skipped_redistribute++;
50270Sstevel@tonic-gate 		}
50280Sstevel@tonic-gate 	}
50290Sstevel@tonic-gate }
50300Sstevel@tonic-gate 
50310Sstevel@tonic-gate 
50320Sstevel@tonic-gate static int
50330Sstevel@tonic-gate apic_acpi_translate_pci_irq(dev_info_t *dip, int busid, int devid,
50340Sstevel@tonic-gate     int ipin, int *pci_irqp, iflag_t *intr_flagp)
50350Sstevel@tonic-gate {
50360Sstevel@tonic-gate 
50370Sstevel@tonic-gate 	int status;
50380Sstevel@tonic-gate 	acpi_psm_lnk_t acpipsmlnk;
50390Sstevel@tonic-gate 
50400Sstevel@tonic-gate 	if ((status = acpi_get_irq_cache_ent(busid, devid, ipin, pci_irqp,
50410Sstevel@tonic-gate 	    intr_flagp)) == ACPI_PSM_SUCCESS) {
50420Sstevel@tonic-gate 		APIC_VERBOSE_IRQ((CE_CONT, "!pcplusmp: Found irqno %d "
50430Sstevel@tonic-gate 		    "from cache for device %s, instance #%d\n", *pci_irqp,
50440Sstevel@tonic-gate 		    ddi_get_name(dip), ddi_get_instance(dip)));
50450Sstevel@tonic-gate 		return (status);
50460Sstevel@tonic-gate 	}
50470Sstevel@tonic-gate 
50480Sstevel@tonic-gate 	bzero(&acpipsmlnk, sizeof (acpi_psm_lnk_t));
50490Sstevel@tonic-gate 
50500Sstevel@tonic-gate 	if ((status = acpi_translate_pci_irq(dip, ipin, pci_irqp, intr_flagp,
50510Sstevel@tonic-gate 	    &acpipsmlnk)) == ACPI_PSM_FAILURE) {
50520Sstevel@tonic-gate 		APIC_VERBOSE_IRQ((CE_WARN, "pcplusmp: "
50530Sstevel@tonic-gate 		    " acpi_translate_pci_irq failed for device %s, instance"
50540Sstevel@tonic-gate 		    " #%d", ddi_get_name(dip), ddi_get_instance(dip)));
50550Sstevel@tonic-gate 		return (status);
50560Sstevel@tonic-gate 	}
50570Sstevel@tonic-gate 
50580Sstevel@tonic-gate 	if (status == ACPI_PSM_PARTIAL && acpipsmlnk.lnkobj != NULL) {
50590Sstevel@tonic-gate 		status = apic_acpi_irq_configure(&acpipsmlnk, dip, pci_irqp,
50600Sstevel@tonic-gate 		    intr_flagp);
50610Sstevel@tonic-gate 		if (status != ACPI_PSM_SUCCESS) {
50620Sstevel@tonic-gate 			status = acpi_get_current_irq_resource(&acpipsmlnk,
50630Sstevel@tonic-gate 			    pci_irqp, intr_flagp);
50640Sstevel@tonic-gate 		}
50650Sstevel@tonic-gate 	}
50660Sstevel@tonic-gate 
50670Sstevel@tonic-gate 	if (status == ACPI_PSM_SUCCESS) {
50680Sstevel@tonic-gate 		acpi_new_irq_cache_ent(busid, devid, ipin, *pci_irqp,
50690Sstevel@tonic-gate 		    intr_flagp, &acpipsmlnk);
50700Sstevel@tonic-gate 
50710Sstevel@tonic-gate 		APIC_VERBOSE_IRQ((CE_CONT, "pcplusmp: [ACPI] "
50720Sstevel@tonic-gate 		    "new irq %d for device %s, instance #%d\n",
50730Sstevel@tonic-gate 		    *pci_irqp, ddi_get_name(dip), ddi_get_instance(dip)));
50740Sstevel@tonic-gate 	}
50750Sstevel@tonic-gate 
50760Sstevel@tonic-gate 	return (status);
50770Sstevel@tonic-gate }
50780Sstevel@tonic-gate 
50790Sstevel@tonic-gate /*
50800Sstevel@tonic-gate  * Configures the irq for the interrupt link device identified by
50810Sstevel@tonic-gate  * acpipsmlnkp.
50820Sstevel@tonic-gate  *
50830Sstevel@tonic-gate  * Gets the current and the list of possible irq settings for the
50840Sstevel@tonic-gate  * device. If apic_unconditional_srs is not set, and the current
50850Sstevel@tonic-gate  * resource setting is in the list of possible irq settings,
50860Sstevel@tonic-gate  * current irq resource setting is passed to the caller.
50870Sstevel@tonic-gate  *
50880Sstevel@tonic-gate  * Otherwise, picks an irq number from the list of possible irq
50890Sstevel@tonic-gate  * settings, and sets the irq of the device to this value.
50900Sstevel@tonic-gate  * If prefer_crs is set, among a set of irq numbers in the list that have
50910Sstevel@tonic-gate  * the least number of devices sharing the interrupt, we pick current irq
50920Sstevel@tonic-gate  * resource setting if it is a member of this set.
50930Sstevel@tonic-gate  *
50940Sstevel@tonic-gate  * Passes the irq number in the value pointed to by pci_irqp, and
50950Sstevel@tonic-gate  * polarity and sensitivity in the structure pointed to by dipintrflagp
50960Sstevel@tonic-gate  * to the caller.
50970Sstevel@tonic-gate  *
50980Sstevel@tonic-gate  * Note that if setting the irq resource failed, but successfuly obtained
50990Sstevel@tonic-gate  * the current irq resource settings, passes the current irq resources
51000Sstevel@tonic-gate  * and considers it a success.
51010Sstevel@tonic-gate  *
51020Sstevel@tonic-gate  * Returns:
51030Sstevel@tonic-gate  * ACPI_PSM_SUCCESS on success.
51040Sstevel@tonic-gate  *
51050Sstevel@tonic-gate  * ACPI_PSM_FAILURE if an error occured during the configuration or
51060Sstevel@tonic-gate  * if a suitable irq was not found for this device, or if setting the
51070Sstevel@tonic-gate  * irq resource and obtaining the current resource fails.
51080Sstevel@tonic-gate  *
51090Sstevel@tonic-gate  */
51100Sstevel@tonic-gate static int
51110Sstevel@tonic-gate apic_acpi_irq_configure(acpi_psm_lnk_t *acpipsmlnkp, dev_info_t *dip,
51120Sstevel@tonic-gate     int *pci_irqp, iflag_t *dipintr_flagp)
51130Sstevel@tonic-gate {
51140Sstevel@tonic-gate 
51150Sstevel@tonic-gate 	int i, min_share, foundnow, done = 0;
51160Sstevel@tonic-gate 	int32_t irq;
51170Sstevel@tonic-gate 	int32_t share_irq = -1;
51180Sstevel@tonic-gate 	int32_t chosen_irq = -1;
51190Sstevel@tonic-gate 	int cur_irq = -1;
51200Sstevel@tonic-gate 	acpi_irqlist_t *irqlistp;
51210Sstevel@tonic-gate 	acpi_irqlist_t *irqlistent;
51220Sstevel@tonic-gate 
51230Sstevel@tonic-gate 	if ((acpi_get_possible_irq_resources(acpipsmlnkp, &irqlistp))
51240Sstevel@tonic-gate 	    == ACPI_PSM_FAILURE) {
51250Sstevel@tonic-gate 		APIC_VERBOSE_IRQ((CE_WARN, "!pcplusmp: Unable to determine "
51260Sstevel@tonic-gate 		    "or assign IRQ for device %s, instance #%d: The system was "
51270Sstevel@tonic-gate 		    "unable to get the list of potential IRQs from ACPI.",
51280Sstevel@tonic-gate 		    ddi_get_name(dip), ddi_get_instance(dip)));
51290Sstevel@tonic-gate 
51300Sstevel@tonic-gate 		return (ACPI_PSM_FAILURE);
51310Sstevel@tonic-gate 	}
51320Sstevel@tonic-gate 
51330Sstevel@tonic-gate 	if ((acpi_get_current_irq_resource(acpipsmlnkp, &cur_irq,
51340Sstevel@tonic-gate 	    dipintr_flagp) == ACPI_PSM_SUCCESS) && (!apic_unconditional_srs) &&
51350Sstevel@tonic-gate 	    (cur_irq > 0)) {
51360Sstevel@tonic-gate 		/*
51370Sstevel@tonic-gate 		 * If an IRQ is set in CRS and that IRQ exists in the set
51380Sstevel@tonic-gate 		 * returned from _PRS, return that IRQ, otherwise print
51390Sstevel@tonic-gate 		 * a warning
51400Sstevel@tonic-gate 		 */
51410Sstevel@tonic-gate 
51420Sstevel@tonic-gate 		if (acpi_irqlist_find_irq(irqlistp, cur_irq, NULL)
51430Sstevel@tonic-gate 		    == ACPI_PSM_SUCCESS) {
51440Sstevel@tonic-gate 
51450Sstevel@tonic-gate 			acpi_free_irqlist(irqlistp);
51460Sstevel@tonic-gate 			ASSERT(pci_irqp != NULL);
51470Sstevel@tonic-gate 			*pci_irqp = cur_irq;
51480Sstevel@tonic-gate 			return (ACPI_PSM_SUCCESS);
51490Sstevel@tonic-gate 		}
51500Sstevel@tonic-gate 
51510Sstevel@tonic-gate 		APIC_VERBOSE_IRQ((CE_WARN, "!pcplusmp: Could not find the "
51520Sstevel@tonic-gate 		    "current irq %d for device %s, instance #%d in ACPI's "
51530Sstevel@tonic-gate 		    "list of possible irqs for this device. Picking one from "
51540Sstevel@tonic-gate 		    " the latter list.", cur_irq, ddi_get_name(dip),
51550Sstevel@tonic-gate 		    ddi_get_instance(dip)));
51560Sstevel@tonic-gate 	}
51570Sstevel@tonic-gate 
51580Sstevel@tonic-gate 	irqlistent = irqlistp;
51590Sstevel@tonic-gate 	min_share = 255;
51600Sstevel@tonic-gate 
51610Sstevel@tonic-gate 	while (irqlistent != NULL) {
51620Sstevel@tonic-gate 		irqlistent->intr_flags.bustype = BUS_PCI;
51630Sstevel@tonic-gate 
51640Sstevel@tonic-gate 		for (foundnow = 0, i = 0; i < irqlistent->num_irqs; i++) {
51650Sstevel@tonic-gate 
51660Sstevel@tonic-gate 			irq = irqlistent->irqs[i];
51670Sstevel@tonic-gate 
51680Sstevel@tonic-gate 			if ((irq < 16) && (apic_reserved_irqlist[irq]))
51690Sstevel@tonic-gate 				continue;
51700Sstevel@tonic-gate 
51710Sstevel@tonic-gate 			if (irq == 0) {
51720Sstevel@tonic-gate 				/* invalid irq number */
51730Sstevel@tonic-gate 				continue;
51740Sstevel@tonic-gate 			}
51750Sstevel@tonic-gate 
51760Sstevel@tonic-gate 			if ((apic_irq_table[irq] == NULL) ||
51770Sstevel@tonic-gate 			    (apic_irq_table[irq]->airq_dip == dip)) {
51780Sstevel@tonic-gate 				chosen_irq = irq;
51790Sstevel@tonic-gate 				foundnow = 1;
51800Sstevel@tonic-gate 				/*
51810Sstevel@tonic-gate 				 * If we do not prefer current irq from crs
51820Sstevel@tonic-gate 				 * or if we do and this irq is the same as
51830Sstevel@tonic-gate 				 * current irq from crs, this is the one
51840Sstevel@tonic-gate 				 * to pick.
51850Sstevel@tonic-gate 				 */
51860Sstevel@tonic-gate 				if (!(apic_prefer_crs) || (irq == cur_irq)) {
51870Sstevel@tonic-gate 					done = 1;
51880Sstevel@tonic-gate 					break;
51890Sstevel@tonic-gate 				}
51900Sstevel@tonic-gate 				continue;
51910Sstevel@tonic-gate 			}
51920Sstevel@tonic-gate 
51930Sstevel@tonic-gate 			if (irqlistent->intr_flags.intr_el == INTR_EL_EDGE)
51940Sstevel@tonic-gate 				continue;
51950Sstevel@tonic-gate 
51960Sstevel@tonic-gate 			if (!acpi_intr_compatible(irqlistent->intr_flags,
51970Sstevel@tonic-gate 			    apic_irq_table[irq]->airq_iflag))
51980Sstevel@tonic-gate 				continue;
51990Sstevel@tonic-gate 
52000Sstevel@tonic-gate 			if ((apic_irq_table[irq]->airq_share < min_share) ||
52010Sstevel@tonic-gate 			    ((apic_irq_table[irq]->airq_share == min_share) &&
52020Sstevel@tonic-gate 			    (cur_irq == irq) && (apic_prefer_crs))) {
52030Sstevel@tonic-gate 				min_share = apic_irq_table[irq]->airq_share;
52040Sstevel@tonic-gate 				share_irq = irq;
52050Sstevel@tonic-gate 				foundnow = 1;
52060Sstevel@tonic-gate 			}
52070Sstevel@tonic-gate 		}
52080Sstevel@tonic-gate 
52090Sstevel@tonic-gate 		/*
52100Sstevel@tonic-gate 		 * If we found an IRQ in the inner loop this time, save the
52110Sstevel@tonic-gate 		 * details from the irqlist for later use.
52120Sstevel@tonic-gate 		 */
52130Sstevel@tonic-gate 		if (foundnow && ((chosen_irq != -1) || (share_irq != -1))) {
52140Sstevel@tonic-gate 			/*
52150Sstevel@tonic-gate 			 * Copy the acpi_prs_private_t and flags from this
52160Sstevel@tonic-gate 			 * irq list entry, since we found an irq from this
52170Sstevel@tonic-gate 			 * entry.
52180Sstevel@tonic-gate 			 */
52190Sstevel@tonic-gate 			acpipsmlnkp->acpi_prs_prv = irqlistent->acpi_prs_prv;
52200Sstevel@tonic-gate 			*dipintr_flagp = irqlistent->intr_flags;
52210Sstevel@tonic-gate 		}
52220Sstevel@tonic-gate 
52230Sstevel@tonic-gate 		if (done)
52240Sstevel@tonic-gate 			break;
52250Sstevel@tonic-gate 
52260Sstevel@tonic-gate 		/* Go to the next irqlist entry */
52270Sstevel@tonic-gate 		irqlistent = irqlistent->next;
52280Sstevel@tonic-gate 	}
52290Sstevel@tonic-gate 
52300Sstevel@tonic-gate 
52310Sstevel@tonic-gate 	acpi_free_irqlist(irqlistp);
52320Sstevel@tonic-gate 	if (chosen_irq != -1)
52330Sstevel@tonic-gate 		irq = chosen_irq;
52340Sstevel@tonic-gate 	else if (share_irq != -1)
52350Sstevel@tonic-gate 		irq = share_irq;
52360Sstevel@tonic-gate 	else {
52370Sstevel@tonic-gate 		APIC_VERBOSE_IRQ((CE_WARN, "!pcplusmp: Could not find a "
52380Sstevel@tonic-gate 		    "suitable irq from the list of possible irqs for device "
52390Sstevel@tonic-gate 		    "%s, instance #%d in ACPI's list of possible irqs",
52400Sstevel@tonic-gate 		    ddi_get_name(dip), ddi_get_instance(dip)));
52410Sstevel@tonic-gate 		return (ACPI_PSM_FAILURE);
52420Sstevel@tonic-gate 	}
52430Sstevel@tonic-gate 
52440Sstevel@tonic-gate 	APIC_VERBOSE_IRQ((CE_CONT, "!pcplusmp: Setting irq %d for device %s "
52450Sstevel@tonic-gate 	    "instance #%d\n", irq, ddi_get_name(dip), ddi_get_instance(dip)));
52460Sstevel@tonic-gate 
52470Sstevel@tonic-gate 	if ((acpi_set_irq_resource(acpipsmlnkp, irq)) == ACPI_PSM_SUCCESS) {
52480Sstevel@tonic-gate 		/*
52490Sstevel@tonic-gate 		 * setting irq was successful, check to make sure CRS
52500Sstevel@tonic-gate 		 * reflects that. If CRS does not agree with what we
52510Sstevel@tonic-gate 		 * set, return the irq that was set.
52520Sstevel@tonic-gate 		 */
52530Sstevel@tonic-gate 
52540Sstevel@tonic-gate 		if (acpi_get_current_irq_resource(acpipsmlnkp, &cur_irq,
52550Sstevel@tonic-gate 		    dipintr_flagp) == ACPI_PSM_SUCCESS) {
52560Sstevel@tonic-gate 
52570Sstevel@tonic-gate 			if (cur_irq != irq)
52580Sstevel@tonic-gate 				APIC_VERBOSE_IRQ((CE_WARN, "!pcplusmp: "
52590Sstevel@tonic-gate 				    "IRQ resource set (irqno %d) for device %s "
52600Sstevel@tonic-gate 				    "instance #%d, differs from current "
52610Sstevel@tonic-gate 				    "setting irqno %d",
52620Sstevel@tonic-gate 				    irq, ddi_get_name(dip),
52630Sstevel@tonic-gate 				    ddi_get_instance(dip), cur_irq));
52640Sstevel@tonic-gate 		}
52650Sstevel@tonic-gate 
52660Sstevel@tonic-gate 		/*
52670Sstevel@tonic-gate 		 * return the irq that was set, and not what CRS reports,
52680Sstevel@tonic-gate 		 * since CRS has been seen to be bogus on some systems
52690Sstevel@tonic-gate 		 */
52700Sstevel@tonic-gate 		cur_irq = irq;
52710Sstevel@tonic-gate 	} else {
52720Sstevel@tonic-gate 		APIC_VERBOSE_IRQ((CE_WARN, "!pcplusmp: set resource irq %d "
52730Sstevel@tonic-gate 		    "failed for device %s instance #%d",
52740Sstevel@tonic-gate 		    irq, ddi_get_name(dip), ddi_get_instance(dip)));
52750Sstevel@tonic-gate 
52760Sstevel@tonic-gate 		if (cur_irq == -1)
52770Sstevel@tonic-gate 			return (ACPI_PSM_FAILURE);
52780Sstevel@tonic-gate 	}
52790Sstevel@tonic-gate 
52800Sstevel@tonic-gate 	ASSERT(pci_irqp != NULL);
52810Sstevel@tonic-gate 	*pci_irqp = cur_irq;
52820Sstevel@tonic-gate 	return (ACPI_PSM_SUCCESS);
52830Sstevel@tonic-gate }
5284