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 51456Sdmick * Common Development and Distribution License (the "License"). 61456Sdmick * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 221389Sdmick * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 270Sstevel@tonic-gate 280Sstevel@tonic-gate /* 290Sstevel@tonic-gate * PSMI 1.1 extensions are supported only in 2.6 and later versions. 300Sstevel@tonic-gate * PSMI 1.2 extensions are supported only in 2.7 and later versions. 310Sstevel@tonic-gate * PSMI 1.3 and 1.4 extensions are supported in Solaris 10. 320Sstevel@tonic-gate * PSMI 1.5 extensions are supported in Solaris Nevada. 330Sstevel@tonic-gate */ 340Sstevel@tonic-gate #define PSMI_1_5 350Sstevel@tonic-gate 360Sstevel@tonic-gate #include <sys/processor.h> 370Sstevel@tonic-gate #include <sys/time.h> 380Sstevel@tonic-gate #include <sys/psm.h> 390Sstevel@tonic-gate #include <sys/smp_impldefs.h> 400Sstevel@tonic-gate #include <sys/cram.h> 410Sstevel@tonic-gate #include <sys/acpi/acpi.h> 420Sstevel@tonic-gate #include <sys/acpica.h> 430Sstevel@tonic-gate #include <sys/psm_common.h> 440Sstevel@tonic-gate #include "apic.h" 450Sstevel@tonic-gate #include <sys/pit.h> 460Sstevel@tonic-gate #include <sys/ddi.h> 470Sstevel@tonic-gate #include <sys/sunddi.h> 480Sstevel@tonic-gate #include <sys/ddi_impldefs.h> 490Sstevel@tonic-gate #include <sys/pci.h> 500Sstevel@tonic-gate #include <sys/promif.h> 510Sstevel@tonic-gate #include <sys/x86_archext.h> 520Sstevel@tonic-gate #include <sys/cpc_impl.h> 530Sstevel@tonic-gate #include <sys/uadmin.h> 540Sstevel@tonic-gate #include <sys/panic.h> 550Sstevel@tonic-gate #include <sys/debug.h> 560Sstevel@tonic-gate #include <sys/archsystm.h> 570Sstevel@tonic-gate #include <sys/trap.h> 580Sstevel@tonic-gate #include <sys/machsystm.h> 590Sstevel@tonic-gate #include <sys/cpuvar.h> 600Sstevel@tonic-gate #include <sys/rm_platter.h> 610Sstevel@tonic-gate #include <sys/privregs.h> 620Sstevel@tonic-gate #include <sys/cyclic.h> 630Sstevel@tonic-gate #include <sys/note.h> 640Sstevel@tonic-gate #include <sys/pci_intr_lib.h> 650Sstevel@tonic-gate 660Sstevel@tonic-gate /* 670Sstevel@tonic-gate * Local Function Prototypes 680Sstevel@tonic-gate */ 690Sstevel@tonic-gate static void apic_init_intr(); 700Sstevel@tonic-gate static void apic_ret(); 710Sstevel@tonic-gate static int apic_handle_defconf(); 720Sstevel@tonic-gate static int apic_parse_mpct(caddr_t mpct, int bypass); 730Sstevel@tonic-gate static struct apic_mpfps_hdr *apic_find_fps_sig(caddr_t fptr, int size); 740Sstevel@tonic-gate static int apic_checksum(caddr_t bptr, int len); 750Sstevel@tonic-gate static int get_apic_cmd1(); 760Sstevel@tonic-gate static int get_apic_pri(); 770Sstevel@tonic-gate static int apic_find_bus_type(char *bus); 780Sstevel@tonic-gate static int apic_find_bus(int busid); 790Sstevel@tonic-gate static int apic_find_bus_id(int bustype); 800Sstevel@tonic-gate static struct apic_io_intr *apic_find_io_intr(int irqno); 810Sstevel@tonic-gate int apic_allocate_irq(int irq); 820Sstevel@tonic-gate static int apic_find_free_irq(int start, int end); 830Sstevel@tonic-gate static uchar_t apic_allocate_vector(int ipl, int irq, int pri); 840Sstevel@tonic-gate static void apic_modify_vector(uchar_t vector, int irq); 850Sstevel@tonic-gate static void apic_mark_vector(uchar_t oldvector, uchar_t newvector); 860Sstevel@tonic-gate static uchar_t apic_xlate_vector(uchar_t oldvector); 870Sstevel@tonic-gate static void apic_xlate_vector_free_timeout_handler(void *arg); 880Sstevel@tonic-gate static void apic_free_vector(uchar_t vector); 890Sstevel@tonic-gate static void apic_reprogram_timeout_handler(void *arg); 900Sstevel@tonic-gate static int apic_check_stuck_interrupt(apic_irq_t *irq_ptr, int old_bind_cpu, 910Sstevel@tonic-gate int new_bind_cpu, volatile int32_t *ioapic, int intin_no, int which_irq); 920Sstevel@tonic-gate static int apic_setup_io_intr(apic_irq_t *irqptr, int irq); 930Sstevel@tonic-gate static int apic_setup_io_intr_deferred(apic_irq_t *irqptr, int irq); 940Sstevel@tonic-gate static void apic_record_rdt_entry(apic_irq_t *irqptr, int irq); 950Sstevel@tonic-gate static struct apic_io_intr *apic_find_io_intr_w_busid(int irqno, int busid); 960Sstevel@tonic-gate static int apic_find_intin(uchar_t ioapic, uchar_t intin); 970Sstevel@tonic-gate static int apic_handle_pci_pci_bridge(dev_info_t *idip, int child_devno, 980Sstevel@tonic-gate int child_ipin, struct apic_io_intr **intrp); 990Sstevel@tonic-gate static int apic_setup_irq_table(dev_info_t *dip, int irqno, 1000Sstevel@tonic-gate struct apic_io_intr *intrp, struct intrspec *ispec, iflag_t *intr_flagp, 1010Sstevel@tonic-gate int type); 1020Sstevel@tonic-gate static int apic_setup_sci_irq_table(int irqno, uchar_t ipl, 1030Sstevel@tonic-gate iflag_t *intr_flagp); 1040Sstevel@tonic-gate static void apic_nmi_intr(caddr_t arg); 1050Sstevel@tonic-gate uchar_t apic_bind_intr(dev_info_t *dip, int irq, uchar_t ioapicid, 1060Sstevel@tonic-gate uchar_t intin); 1070Sstevel@tonic-gate static int apic_rebind(apic_irq_t *irq_ptr, int bind_cpu, int acquire_lock, 1080Sstevel@tonic-gate int when); 109916Sschwartz int apic_rebind_all(apic_irq_t *irq_ptr, int bind_cpu, int safe); 1100Sstevel@tonic-gate static void apic_intr_redistribute(); 1110Sstevel@tonic-gate static void apic_cleanup_busy(); 1120Sstevel@tonic-gate static void apic_set_pwroff_method_from_mpcnfhdr(struct apic_mp_cnf_hdr *hdrp); 1130Sstevel@tonic-gate int apic_introp_xlate(dev_info_t *dip, struct intrspec *ispec, int type); 1140Sstevel@tonic-gate 1150Sstevel@tonic-gate /* ACPI support routines */ 1160Sstevel@tonic-gate static int acpi_probe(void); 1170Sstevel@tonic-gate static int apic_acpi_irq_configure(acpi_psm_lnk_t *acpipsmlnkp, dev_info_t *dip, 1180Sstevel@tonic-gate int *pci_irqp, iflag_t *intr_flagp); 1190Sstevel@tonic-gate 1200Sstevel@tonic-gate static int apic_acpi_translate_pci_irq(dev_info_t *dip, int busid, int devid, 1210Sstevel@tonic-gate int ipin, int *pci_irqp, iflag_t *intr_flagp); 1220Sstevel@tonic-gate static uchar_t acpi_find_ioapic(int irq); 1230Sstevel@tonic-gate static int acpi_intr_compatible(iflag_t iflag1, iflag_t iflag2); 1240Sstevel@tonic-gate 1250Sstevel@tonic-gate /* 1260Sstevel@tonic-gate * standard MP entries 1270Sstevel@tonic-gate */ 1280Sstevel@tonic-gate static int apic_probe(); 1290Sstevel@tonic-gate static int apic_clkinit(); 1300Sstevel@tonic-gate static int apic_getclkirq(int ipl); 1310Sstevel@tonic-gate static uint_t apic_calibrate(volatile uint32_t *addr, 1320Sstevel@tonic-gate uint16_t *pit_ticks_adj); 1330Sstevel@tonic-gate static hrtime_t apic_gettime(); 1340Sstevel@tonic-gate static hrtime_t apic_gethrtime(); 1350Sstevel@tonic-gate static void apic_init(); 1360Sstevel@tonic-gate static void apic_picinit(void); 1370Sstevel@tonic-gate static void apic_cpu_start(processorid_t cpun, caddr_t rm_code); 1380Sstevel@tonic-gate static int apic_post_cpu_start(void); 1390Sstevel@tonic-gate static void apic_send_ipi(int cpun, int ipl); 1400Sstevel@tonic-gate static void apic_set_softintr(int softintr); 1410Sstevel@tonic-gate static void apic_set_idlecpu(processorid_t cpun); 1420Sstevel@tonic-gate static void apic_unset_idlecpu(processorid_t cpun); 1430Sstevel@tonic-gate static int apic_softlvl_to_irq(int ipl); 1440Sstevel@tonic-gate static int apic_intr_enter(int ipl, int *vect); 1450Sstevel@tonic-gate static void apic_intr_exit(int ipl, int vect); 1460Sstevel@tonic-gate static void apic_setspl(int ipl); 1470Sstevel@tonic-gate static int apic_addspl(int ipl, int vector, int min_ipl, int max_ipl); 1480Sstevel@tonic-gate static int apic_delspl(int ipl, int vector, int min_ipl, int max_ipl); 1490Sstevel@tonic-gate static void apic_shutdown(int cmd, int fcn); 1500Sstevel@tonic-gate static void apic_preshutdown(int cmd, int fcn); 1510Sstevel@tonic-gate static int apic_disable_intr(processorid_t cpun); 1520Sstevel@tonic-gate static void apic_enable_intr(processorid_t cpun); 1530Sstevel@tonic-gate static processorid_t apic_get_next_processorid(processorid_t cpun); 1540Sstevel@tonic-gate static int apic_get_ipivect(int ipl, int type); 1550Sstevel@tonic-gate static void apic_timer_reprogram(hrtime_t time); 1560Sstevel@tonic-gate static void apic_timer_enable(void); 1570Sstevel@tonic-gate static void apic_timer_disable(void); 1580Sstevel@tonic-gate static void apic_post_cyclic_setup(void *arg); 1590Sstevel@tonic-gate extern int apic_intr_ops(dev_info_t *, ddi_intr_handle_impl_t *, 1600Sstevel@tonic-gate psm_intr_op_t, int *); 1610Sstevel@tonic-gate 1620Sstevel@tonic-gate static int apic_oneshot = 0; 1630Sstevel@tonic-gate int apic_oneshot_enable = 1; /* to allow disabling one-shot capability */ 1640Sstevel@tonic-gate 1650Sstevel@tonic-gate /* 1660Sstevel@tonic-gate * These variables are frequently accessed in apic_intr_enter(), 1670Sstevel@tonic-gate * apic_intr_exit and apic_setspl, so group them together 1680Sstevel@tonic-gate */ 1690Sstevel@tonic-gate volatile uint32_t *apicadr = NULL; /* virtual addr of local APIC */ 1700Sstevel@tonic-gate int apic_setspl_delay = 1; /* apic_setspl - delay enable */ 1710Sstevel@tonic-gate int apic_clkvect; 1720Sstevel@tonic-gate 1730Sstevel@tonic-gate /* ACPI SCI interrupt configuration; -1 if SCI not used */ 1740Sstevel@tonic-gate int apic_sci_vect = -1; 1750Sstevel@tonic-gate iflag_t apic_sci_flags; 1760Sstevel@tonic-gate 1770Sstevel@tonic-gate /* vector at which error interrupts come in */ 1780Sstevel@tonic-gate int apic_errvect; 1790Sstevel@tonic-gate int apic_enable_error_intr = 1; 1800Sstevel@tonic-gate int apic_error_display_delay = 100; 1810Sstevel@tonic-gate 1820Sstevel@tonic-gate /* vector at which performance counter overflow interrupts come in */ 1830Sstevel@tonic-gate int apic_cpcovf_vect; 1840Sstevel@tonic-gate int apic_enable_cpcovf_intr = 1; 1850Sstevel@tonic-gate 1860Sstevel@tonic-gate /* Max wait time (in microsecs) for flags to clear in an RDT entry. */ 1870Sstevel@tonic-gate static int apic_max_usecs_clear_pending = 1000; 1880Sstevel@tonic-gate 1890Sstevel@tonic-gate /* Amt of usecs to wait before checking if RDT flags have reset. */ 1900Sstevel@tonic-gate #define APIC_USECS_PER_WAIT_INTERVAL 100 1910Sstevel@tonic-gate 1920Sstevel@tonic-gate /* Maximum number of times to retry reprogramming via the timeout */ 1930Sstevel@tonic-gate #define APIC_REPROGRAM_MAX_TIMEOUTS 10 1940Sstevel@tonic-gate 1950Sstevel@tonic-gate /* timeout delay for IOAPIC delayed reprogramming */ 1960Sstevel@tonic-gate #define APIC_REPROGRAM_TIMEOUT_DELAY 5 /* microseconds */ 1970Sstevel@tonic-gate 1980Sstevel@tonic-gate /* Parameter to apic_rebind(): Should reprogramming be done now or later? */ 1990Sstevel@tonic-gate #define DEFERRED 1 2000Sstevel@tonic-gate #define IMMEDIATE 0 2010Sstevel@tonic-gate 2020Sstevel@tonic-gate /* 2030Sstevel@tonic-gate * number of bits per byte, from <sys/param.h> 2040Sstevel@tonic-gate */ 2050Sstevel@tonic-gate #define UCHAR_MAX ((1 << NBBY) - 1) 2060Sstevel@tonic-gate 2071456Sdmick uchar_t apic_reserved_irqlist[MAX_ISA_IRQ + 1]; 2080Sstevel@tonic-gate 2090Sstevel@tonic-gate /* 2100Sstevel@tonic-gate * The following vector assignments influence the value of ipltopri and 2110Sstevel@tonic-gate * vectortoipl. Note that vectors 0 - 0x1f are not used. We can program 2120Sstevel@tonic-gate * idle to 0 and IPL 0 to 0x10 to differentiate idle in case 2130Sstevel@tonic-gate * we care to do so in future. Note some IPLs which are rarely used 2140Sstevel@tonic-gate * will share the vector ranges and heavily used IPLs (5 and 6) have 2150Sstevel@tonic-gate * a wide range. 2160Sstevel@tonic-gate * IPL Vector range. as passed to intr_enter 2170Sstevel@tonic-gate * 0 none. 2180Sstevel@tonic-gate * 1,2,3 0x20-0x2f 0x0-0xf 2190Sstevel@tonic-gate * 4 0x30-0x3f 0x10-0x1f 2200Sstevel@tonic-gate * 5 0x40-0x5f 0x20-0x3f 2210Sstevel@tonic-gate * 6 0x60-0x7f 0x40-0x5f 2220Sstevel@tonic-gate * 7,8,9 0x80-0x8f 0x60-0x6f 2230Sstevel@tonic-gate * 10 0x90-0x9f 0x70-0x7f 2240Sstevel@tonic-gate * 11 0xa0-0xaf 0x80-0x8f 2250Sstevel@tonic-gate * ... ... 2260Sstevel@tonic-gate * 16 0xf0-0xff 0xd0-0xdf 2270Sstevel@tonic-gate */ 2280Sstevel@tonic-gate uchar_t apic_vectortoipl[APIC_AVAIL_VECTOR / APIC_VECTOR_PER_IPL] = { 2290Sstevel@tonic-gate 3, 4, 5, 5, 6, 6, 9, 10, 11, 12, 13, 14, 15, 16 2300Sstevel@tonic-gate }; 2310Sstevel@tonic-gate /* 2320Sstevel@tonic-gate * The ipl of an ISR at vector X is apic_vectortoipl[X<<4] 2330Sstevel@tonic-gate * NOTE that this is vector as passed into intr_enter which is 2340Sstevel@tonic-gate * programmed vector - 0x20 (APIC_BASE_VECT) 2350Sstevel@tonic-gate */ 2360Sstevel@tonic-gate 2370Sstevel@tonic-gate uchar_t apic_ipltopri[MAXIPL + 1]; /* unix ipl to apic pri */ 2380Sstevel@tonic-gate /* The taskpri to be programmed into apic to mask given ipl */ 2390Sstevel@tonic-gate 2400Sstevel@tonic-gate #if defined(__amd64) 2410Sstevel@tonic-gate uchar_t apic_cr8pri[MAXIPL + 1]; /* unix ipl to cr8 pri */ 2420Sstevel@tonic-gate #endif 2430Sstevel@tonic-gate 2440Sstevel@tonic-gate /* 2450Sstevel@tonic-gate * Patchable global variables. 2460Sstevel@tonic-gate */ 2470Sstevel@tonic-gate int apic_forceload = 0; 2480Sstevel@tonic-gate 2490Sstevel@tonic-gate #define INTR_ROUND_ROBIN_WITH_AFFINITY 0 2500Sstevel@tonic-gate #define INTR_ROUND_ROBIN 1 2510Sstevel@tonic-gate #define INTR_LOWEST_PRIORITY 2 2520Sstevel@tonic-gate 2530Sstevel@tonic-gate int apic_intr_policy = INTR_ROUND_ROBIN_WITH_AFFINITY; 2540Sstevel@tonic-gate 2551701Sjohnny static int apic_next_bind_cpu = 1; /* For round robin assignment */ 2560Sstevel@tonic-gate /* start with cpu 1 */ 2570Sstevel@tonic-gate 2580Sstevel@tonic-gate int apic_coarse_hrtime = 1; /* 0 - use accurate slow gethrtime() */ 2590Sstevel@tonic-gate /* 1 - use gettime() for performance */ 2600Sstevel@tonic-gate int apic_flat_model = 0; /* 0 - clustered. 1 - flat */ 2610Sstevel@tonic-gate int apic_enable_hwsoftint = 0; /* 0 - disable, 1 - enable */ 2620Sstevel@tonic-gate int apic_enable_bind_log = 1; /* 1 - display interrupt binding log */ 2630Sstevel@tonic-gate int apic_panic_on_nmi = 0; 2640Sstevel@tonic-gate int apic_panic_on_apic_error = 0; 2650Sstevel@tonic-gate 2660Sstevel@tonic-gate int apic_verbose = 0; 2670Sstevel@tonic-gate 2680Sstevel@tonic-gate /* Flag definitions for apic_verbose */ 2690Sstevel@tonic-gate #define APIC_VERBOSE_IOAPIC_FLAG 0x00000001 2700Sstevel@tonic-gate #define APIC_VERBOSE_IRQ_FLAG 0x00000002 2710Sstevel@tonic-gate #define APIC_VERBOSE_POWEROFF_FLAG 0x00000004 2720Sstevel@tonic-gate #define APIC_VERBOSE_POWEROFF_PAUSE_FLAG 0x00000008 2730Sstevel@tonic-gate 2740Sstevel@tonic-gate 2750Sstevel@tonic-gate #define APIC_VERBOSE_IOAPIC(fmt) \ 2760Sstevel@tonic-gate if (apic_verbose & APIC_VERBOSE_IOAPIC_FLAG) \ 2770Sstevel@tonic-gate cmn_err fmt; 2780Sstevel@tonic-gate 2790Sstevel@tonic-gate #define APIC_VERBOSE_IRQ(fmt) \ 2800Sstevel@tonic-gate if (apic_verbose & APIC_VERBOSE_IRQ_FLAG) \ 2810Sstevel@tonic-gate cmn_err fmt; 2820Sstevel@tonic-gate 2830Sstevel@tonic-gate #define APIC_VERBOSE_POWEROFF(fmt) \ 2840Sstevel@tonic-gate if (apic_verbose & APIC_VERBOSE_POWEROFF_FLAG) \ 2850Sstevel@tonic-gate prom_printf fmt; 2860Sstevel@tonic-gate 2870Sstevel@tonic-gate 2880Sstevel@tonic-gate /* Now the ones for Dynamic Interrupt distribution */ 289916Sschwartz int apic_enable_dynamic_migration = 0; 2900Sstevel@tonic-gate 2910Sstevel@tonic-gate /* 2920Sstevel@tonic-gate * If enabled, the distribution works as follows: 2930Sstevel@tonic-gate * On every interrupt entry, the current ipl for the CPU is set in cpu_info 2940Sstevel@tonic-gate * and the irq corresponding to the ipl is also set in the aci_current array. 2950Sstevel@tonic-gate * interrupt exit and setspl (due to soft interrupts) will cause the current 2960Sstevel@tonic-gate * ipl to be be changed. This is cache friendly as these frequently used 2970Sstevel@tonic-gate * paths write into a per cpu structure. 2980Sstevel@tonic-gate * 2990Sstevel@tonic-gate * Sampling is done by checking the structures for all CPUs and incrementing 3000Sstevel@tonic-gate * the busy field of the irq (if any) executing on each CPU and the busy field 3010Sstevel@tonic-gate * of the corresponding CPU. 3020Sstevel@tonic-gate * In periodic mode this is done on every clock interrupt. 3030Sstevel@tonic-gate * In one-shot mode, this is done thru a cyclic with an interval of 3040Sstevel@tonic-gate * apic_redistribute_sample_interval (default 10 milli sec). 3050Sstevel@tonic-gate * 3060Sstevel@tonic-gate * Every apic_sample_factor_redistribution times we sample, we do computations 3070Sstevel@tonic-gate * to decide which interrupt needs to be migrated (see comments 3080Sstevel@tonic-gate * before apic_intr_redistribute(). 3090Sstevel@tonic-gate */ 3100Sstevel@tonic-gate 3110Sstevel@tonic-gate /* 3120Sstevel@tonic-gate * Following 3 variables start as % and can be patched or set using an 3130Sstevel@tonic-gate * API to be defined in future. They will be scaled to 3140Sstevel@tonic-gate * sample_factor_redistribution which is in turn set to hertz+1 (in periodic 3150Sstevel@tonic-gate * mode), or 101 in one-shot mode to stagger it away from one sec processing 3160Sstevel@tonic-gate */ 3170Sstevel@tonic-gate 3180Sstevel@tonic-gate int apic_int_busy_mark = 60; 3190Sstevel@tonic-gate int apic_int_free_mark = 20; 3200Sstevel@tonic-gate int apic_diff_for_redistribution = 10; 3210Sstevel@tonic-gate 3220Sstevel@tonic-gate /* sampling interval for interrupt redistribution for dynamic migration */ 3230Sstevel@tonic-gate int apic_redistribute_sample_interval = NANOSEC / 100; /* 10 millisec */ 3240Sstevel@tonic-gate 3250Sstevel@tonic-gate /* 3260Sstevel@tonic-gate * number of times we sample before deciding to redistribute interrupts 3270Sstevel@tonic-gate * for dynamic migration 3280Sstevel@tonic-gate */ 3290Sstevel@tonic-gate int apic_sample_factor_redistribution = 101; 3300Sstevel@tonic-gate 3310Sstevel@tonic-gate /* timeout for xlate_vector, mark_vector */ 3320Sstevel@tonic-gate int apic_revector_timeout = 16 * 10000; /* 160 millisec */ 3330Sstevel@tonic-gate 3340Sstevel@tonic-gate int apic_redist_cpu_skip = 0; 3350Sstevel@tonic-gate int apic_num_imbalance = 0; 3360Sstevel@tonic-gate int apic_num_rebind = 0; 3370Sstevel@tonic-gate 3380Sstevel@tonic-gate int apic_nproc = 0; 3390Sstevel@tonic-gate int apic_defconf = 0; 3400Sstevel@tonic-gate int apic_irq_translate = 0; 3410Sstevel@tonic-gate int apic_spec_rev = 0; 3420Sstevel@tonic-gate int apic_imcrp = 0; 3430Sstevel@tonic-gate 3440Sstevel@tonic-gate int apic_use_acpi = 1; /* 1 = use ACPI, 0 = don't use ACPI */ 3450Sstevel@tonic-gate int apic_use_acpi_madt_only = 0; /* 1=ONLY use MADT from ACPI */ 3460Sstevel@tonic-gate 3470Sstevel@tonic-gate /* 3480Sstevel@tonic-gate * For interrupt link devices, if apic_unconditional_srs is set, an irq resource 3490Sstevel@tonic-gate * will be assigned (via _SRS). If it is not set, use the current 3500Sstevel@tonic-gate * irq setting (via _CRS), but only if that irq is in the set of possible 3510Sstevel@tonic-gate * irqs (returned by _PRS) for the device. 3520Sstevel@tonic-gate */ 3530Sstevel@tonic-gate int apic_unconditional_srs = 1; 3540Sstevel@tonic-gate 3550Sstevel@tonic-gate /* 3560Sstevel@tonic-gate * For interrupt link devices, if apic_prefer_crs is set when we are 3570Sstevel@tonic-gate * assigning an IRQ resource to a device, prefer the current IRQ setting 3580Sstevel@tonic-gate * over other possible irq settings under same conditions. 3590Sstevel@tonic-gate */ 3600Sstevel@tonic-gate 3610Sstevel@tonic-gate int apic_prefer_crs = 1; 3620Sstevel@tonic-gate 3630Sstevel@tonic-gate 3640Sstevel@tonic-gate /* minimum number of timer ticks to program to */ 3650Sstevel@tonic-gate int apic_min_timer_ticks = 1; 3660Sstevel@tonic-gate /* 3670Sstevel@tonic-gate * Local static data 3680Sstevel@tonic-gate */ 3690Sstevel@tonic-gate static struct psm_ops apic_ops = { 3700Sstevel@tonic-gate apic_probe, 3710Sstevel@tonic-gate 3720Sstevel@tonic-gate apic_init, 3730Sstevel@tonic-gate apic_picinit, 3740Sstevel@tonic-gate apic_intr_enter, 3750Sstevel@tonic-gate apic_intr_exit, 3760Sstevel@tonic-gate apic_setspl, 3770Sstevel@tonic-gate apic_addspl, 3780Sstevel@tonic-gate apic_delspl, 3790Sstevel@tonic-gate apic_disable_intr, 3800Sstevel@tonic-gate apic_enable_intr, 3810Sstevel@tonic-gate apic_softlvl_to_irq, 3820Sstevel@tonic-gate apic_set_softintr, 3830Sstevel@tonic-gate 3840Sstevel@tonic-gate apic_set_idlecpu, 3850Sstevel@tonic-gate apic_unset_idlecpu, 3860Sstevel@tonic-gate 3870Sstevel@tonic-gate apic_clkinit, 3880Sstevel@tonic-gate apic_getclkirq, 3890Sstevel@tonic-gate (void (*)(void))NULL, /* psm_hrtimeinit */ 3900Sstevel@tonic-gate apic_gethrtime, 3910Sstevel@tonic-gate 3920Sstevel@tonic-gate apic_get_next_processorid, 3930Sstevel@tonic-gate apic_cpu_start, 3940Sstevel@tonic-gate apic_post_cpu_start, 3950Sstevel@tonic-gate apic_shutdown, 3960Sstevel@tonic-gate apic_get_ipivect, 3970Sstevel@tonic-gate apic_send_ipi, 3980Sstevel@tonic-gate 3990Sstevel@tonic-gate (int (*)(dev_info_t *, int))NULL, /* psm_translate_irq */ 4000Sstevel@tonic-gate (int (*)(todinfo_t *))NULL, /* psm_tod_get */ 4010Sstevel@tonic-gate (int (*)(todinfo_t *))NULL, /* psm_tod_set */ 4020Sstevel@tonic-gate (void (*)(int, char *))NULL, /* psm_notify_error */ 4030Sstevel@tonic-gate (void (*)(int))NULL, /* psm_notify_func */ 4040Sstevel@tonic-gate apic_timer_reprogram, 4050Sstevel@tonic-gate apic_timer_enable, 4060Sstevel@tonic-gate apic_timer_disable, 4070Sstevel@tonic-gate apic_post_cyclic_setup, 4080Sstevel@tonic-gate apic_preshutdown, 4090Sstevel@tonic-gate apic_intr_ops /* Advanced DDI Interrupt framework */ 4100Sstevel@tonic-gate }; 4110Sstevel@tonic-gate 4120Sstevel@tonic-gate 4130Sstevel@tonic-gate static struct psm_info apic_psm_info = { 4140Sstevel@tonic-gate PSM_INFO_VER01_5, /* version */ 4150Sstevel@tonic-gate PSM_OWN_EXCLUSIVE, /* ownership */ 4160Sstevel@tonic-gate (struct psm_ops *)&apic_ops, /* operation */ 4170Sstevel@tonic-gate "pcplusmp", /* machine name */ 4180Sstevel@tonic-gate "pcplusmp v1.4 compatible %I%", 4190Sstevel@tonic-gate }; 4200Sstevel@tonic-gate 4210Sstevel@tonic-gate static void *apic_hdlp; 4220Sstevel@tonic-gate 4230Sstevel@tonic-gate #ifdef DEBUG 4240Sstevel@tonic-gate #define DENT 0x0001 4250Sstevel@tonic-gate int apic_debug = 0; 4260Sstevel@tonic-gate /* 4270Sstevel@tonic-gate * set apic_restrict_vector to the # of vectors we want to allow per range 4280Sstevel@tonic-gate * useful in testing shared interrupt logic by setting it to 2 or 3 4290Sstevel@tonic-gate */ 4300Sstevel@tonic-gate int apic_restrict_vector = 0; 4310Sstevel@tonic-gate 4320Sstevel@tonic-gate #define APIC_DEBUG_MSGBUFSIZE 2048 4330Sstevel@tonic-gate int apic_debug_msgbuf[APIC_DEBUG_MSGBUFSIZE]; 4340Sstevel@tonic-gate int apic_debug_msgbufindex = 0; 4350Sstevel@tonic-gate 4360Sstevel@tonic-gate /* 4370Sstevel@tonic-gate * Put "int" info into debug buffer. No MP consistency, but light weight. 4380Sstevel@tonic-gate * Good enough for most debugging. 4390Sstevel@tonic-gate */ 4400Sstevel@tonic-gate #define APIC_DEBUG_BUF_PUT(x) \ 4410Sstevel@tonic-gate apic_debug_msgbuf[apic_debug_msgbufindex++] = x; \ 4420Sstevel@tonic-gate if (apic_debug_msgbufindex >= (APIC_DEBUG_MSGBUFSIZE - NCPU)) \ 4430Sstevel@tonic-gate apic_debug_msgbufindex = 0; 4440Sstevel@tonic-gate 4450Sstevel@tonic-gate #endif /* DEBUG */ 4460Sstevel@tonic-gate 4470Sstevel@tonic-gate apic_cpus_info_t *apic_cpus; 4480Sstevel@tonic-gate 449*2006Sandrei static cpuset_t apic_cpumask; 4500Sstevel@tonic-gate static uint_t apic_flag; 4510Sstevel@tonic-gate 4520Sstevel@tonic-gate /* Flag to indicate that we need to shut down all processors */ 4530Sstevel@tonic-gate static uint_t apic_shutdown_processors; 4540Sstevel@tonic-gate 4550Sstevel@tonic-gate uint_t apic_nsec_per_intr = 0; 4560Sstevel@tonic-gate 4570Sstevel@tonic-gate /* 4580Sstevel@tonic-gate * apic_let_idle_redistribute can have the following values: 4590Sstevel@tonic-gate * 0 - If clock decremented it from 1 to 0, clock has to call redistribute. 4600Sstevel@tonic-gate * apic_redistribute_lock prevents multiple idle cpus from redistributing 4610Sstevel@tonic-gate */ 4620Sstevel@tonic-gate int apic_num_idle_redistributions = 0; 4630Sstevel@tonic-gate static int apic_let_idle_redistribute = 0; 4640Sstevel@tonic-gate static uint_t apic_nticks = 0; 4650Sstevel@tonic-gate static uint_t apic_skipped_redistribute = 0; 4660Sstevel@tonic-gate 4670Sstevel@tonic-gate /* to gather intr data and redistribute */ 4680Sstevel@tonic-gate static void apic_redistribute_compute(void); 4690Sstevel@tonic-gate 4700Sstevel@tonic-gate static uint_t last_count_read = 0; 4710Sstevel@tonic-gate static lock_t apic_gethrtime_lock; 4720Sstevel@tonic-gate volatile int apic_hrtime_stamp = 0; 4730Sstevel@tonic-gate volatile hrtime_t apic_nsec_since_boot = 0; 4740Sstevel@tonic-gate static uint_t apic_hertz_count, apic_nsec_per_tick; 4750Sstevel@tonic-gate static hrtime_t apic_nsec_max; 4760Sstevel@tonic-gate 4770Sstevel@tonic-gate static hrtime_t apic_last_hrtime = 0; 4780Sstevel@tonic-gate int apic_hrtime_error = 0; 4790Sstevel@tonic-gate int apic_remote_hrterr = 0; 4800Sstevel@tonic-gate int apic_num_nmis = 0; 4810Sstevel@tonic-gate int apic_apic_error = 0; 4820Sstevel@tonic-gate int apic_num_apic_errors = 0; 4830Sstevel@tonic-gate int apic_num_cksum_errors = 0; 4840Sstevel@tonic-gate 4850Sstevel@tonic-gate static uchar_t apic_io_id[MAX_IO_APIC]; 4860Sstevel@tonic-gate static uchar_t apic_io_ver[MAX_IO_APIC]; 4870Sstevel@tonic-gate static uchar_t apic_io_vectbase[MAX_IO_APIC]; 4880Sstevel@tonic-gate static uchar_t apic_io_vectend[MAX_IO_APIC]; 4890Sstevel@tonic-gate volatile int32_t *apicioadr[MAX_IO_APIC]; 490881Sjohnny 491881Sjohnny /* 492881Sjohnny * First available slot to be used as IRQ index into the apic_irq_table 493881Sjohnny * for those interrupts (like MSI/X) that don't have a physical IRQ. 494881Sjohnny */ 495881Sjohnny int apic_first_avail_irq = APIC_FIRST_FREE_IRQ; 496881Sjohnny 4970Sstevel@tonic-gate /* 4980Sstevel@tonic-gate * apic_ioapic_lock protects the ioapics (reg select), the status, temp_bound 4990Sstevel@tonic-gate * and bound elements of cpus_info and the temp_cpu element of irq_struct 5000Sstevel@tonic-gate */ 5010Sstevel@tonic-gate lock_t apic_ioapic_lock; 5020Sstevel@tonic-gate 5030Sstevel@tonic-gate /* 5040Sstevel@tonic-gate * apic_ioapic_reprogram_lock prevents a CPU from exiting 5050Sstevel@tonic-gate * apic_intr_exit before IOAPIC reprogramming information 5060Sstevel@tonic-gate * is collected. 5070Sstevel@tonic-gate */ 5080Sstevel@tonic-gate static lock_t apic_ioapic_reprogram_lock; 5090Sstevel@tonic-gate static int apic_io_max = 0; /* no. of i/o apics enabled */ 5100Sstevel@tonic-gate 5110Sstevel@tonic-gate static struct apic_io_intr *apic_io_intrp = 0; 5120Sstevel@tonic-gate static struct apic_bus *apic_busp; 5130Sstevel@tonic-gate 5140Sstevel@tonic-gate uchar_t apic_vector_to_irq[APIC_MAX_VECTOR+1]; 5150Sstevel@tonic-gate static uchar_t apic_resv_vector[MAXIPL+1]; 5160Sstevel@tonic-gate 5170Sstevel@tonic-gate static char apic_level_intr[APIC_MAX_VECTOR+1]; 5180Sstevel@tonic-gate static int apic_error = 0; 5190Sstevel@tonic-gate /* values which apic_error can take. Not catastrophic, but may help debug */ 5200Sstevel@tonic-gate #define APIC_ERR_BOOT_EOI 0x1 5210Sstevel@tonic-gate #define APIC_ERR_GET_IPIVECT_FAIL 0x2 5220Sstevel@tonic-gate #define APIC_ERR_INVALID_INDEX 0x4 5230Sstevel@tonic-gate #define APIC_ERR_MARK_VECTOR_FAIL 0x8 5240Sstevel@tonic-gate #define APIC_ERR_APIC_ERROR 0x40000000 5250Sstevel@tonic-gate #define APIC_ERR_NMI 0x80000000 5260Sstevel@tonic-gate 5270Sstevel@tonic-gate static int apic_cmos_ssb_set = 0; 5280Sstevel@tonic-gate 5290Sstevel@tonic-gate static uint32_t eisa_level_intr_mask = 0; 5300Sstevel@tonic-gate /* At least MSB will be set if EISA bus */ 5310Sstevel@tonic-gate 5320Sstevel@tonic-gate static int apic_pci_bus_total = 0; 5330Sstevel@tonic-gate static uchar_t apic_single_pci_busid = 0; 5340Sstevel@tonic-gate 5350Sstevel@tonic-gate 5360Sstevel@tonic-gate /* 5370Sstevel@tonic-gate * airq_mutex protects additions to the apic_irq_table - the first 5380Sstevel@tonic-gate * pointer and any airq_nexts off of that one. It also protects 5390Sstevel@tonic-gate * apic_max_device_irq & apic_min_device_irq. It also guarantees 5400Sstevel@tonic-gate * that share_id is unique as new ids are generated only when new 5410Sstevel@tonic-gate * irq_t structs are linked in. Once linked in the structs are never 5420Sstevel@tonic-gate * deleted. temp_cpu & mps_intr_index field indicate if it is programmed 5430Sstevel@tonic-gate * or allocated. Note that there is a slight gap between allocating in 5440Sstevel@tonic-gate * apic_introp_xlate and programming in addspl. 5450Sstevel@tonic-gate */ 5460Sstevel@tonic-gate kmutex_t airq_mutex; 5470Sstevel@tonic-gate apic_irq_t *apic_irq_table[APIC_MAX_VECTOR+1]; 5480Sstevel@tonic-gate int apic_max_device_irq = 0; 5490Sstevel@tonic-gate int apic_min_device_irq = APIC_MAX_VECTOR; 5500Sstevel@tonic-gate 5510Sstevel@tonic-gate /* use to make sure only one cpu handles the nmi */ 5520Sstevel@tonic-gate static lock_t apic_nmi_lock; 5530Sstevel@tonic-gate /* use to make sure only one cpu handles the error interrupt */ 5540Sstevel@tonic-gate static lock_t apic_error_lock; 5550Sstevel@tonic-gate 5560Sstevel@tonic-gate /* 5570Sstevel@tonic-gate * Following declarations are for revectoring; used when ISRs at different 5580Sstevel@tonic-gate * IPLs share an irq. 5590Sstevel@tonic-gate */ 5600Sstevel@tonic-gate static lock_t apic_revector_lock; 5610Sstevel@tonic-gate static int apic_revector_pending = 0; 5620Sstevel@tonic-gate static uchar_t *apic_oldvec_to_newvec; 5630Sstevel@tonic-gate static uchar_t *apic_newvec_to_oldvec; 5640Sstevel@tonic-gate 5650Sstevel@tonic-gate /* Ensures that the IOAPIC-reprogramming timeout is not reentrant */ 5660Sstevel@tonic-gate static kmutex_t apic_reprogram_timeout_mutex; 5670Sstevel@tonic-gate 5680Sstevel@tonic-gate static struct ioapic_reprogram_data { 5690Sstevel@tonic-gate int valid; /* This entry is valid */ 5700Sstevel@tonic-gate int bindcpu; /* The CPU to which the int will be bound */ 5710Sstevel@tonic-gate unsigned timeouts; /* # times the reprogram timeout was called */ 5720Sstevel@tonic-gate } apic_reprogram_info[APIC_MAX_VECTOR+1]; 5730Sstevel@tonic-gate /* 5740Sstevel@tonic-gate * APIC_MAX_VECTOR + 1 is the maximum # of IRQs as well. apic_reprogram_info 5750Sstevel@tonic-gate * is indexed by IRQ number, NOT by vector number. 5760Sstevel@tonic-gate */ 5770Sstevel@tonic-gate 5780Sstevel@tonic-gate 5790Sstevel@tonic-gate /* 5800Sstevel@tonic-gate * The following added to identify a software poweroff method if available. 5810Sstevel@tonic-gate */ 5820Sstevel@tonic-gate 5830Sstevel@tonic-gate static struct { 5840Sstevel@tonic-gate int poweroff_method; 5850Sstevel@tonic-gate char oem_id[APIC_MPS_OEM_ID_LEN + 1]; /* MAX + 1 for NULL */ 5860Sstevel@tonic-gate char prod_id[APIC_MPS_PROD_ID_LEN + 1]; /* MAX + 1 for NULL */ 5870Sstevel@tonic-gate } apic_mps_ids[] = { 5880Sstevel@tonic-gate { APIC_POWEROFF_VIA_RTC, "INTEL", "ALDER" }, /* 4300 */ 5890Sstevel@tonic-gate { APIC_POWEROFF_VIA_RTC, "NCR", "AMC" }, /* 4300 */ 5900Sstevel@tonic-gate { APIC_POWEROFF_VIA_ASPEN_BMC, "INTEL", "A450NX" }, /* 4400? */ 5910Sstevel@tonic-gate { APIC_POWEROFF_VIA_ASPEN_BMC, "INTEL", "AD450NX" }, /* 4400 */ 5920Sstevel@tonic-gate { APIC_POWEROFF_VIA_ASPEN_BMC, "INTEL", "AC450NX" }, /* 4400R */ 5930Sstevel@tonic-gate { APIC_POWEROFF_VIA_SITKA_BMC, "INTEL", "S450NX" }, /* S50 */ 5940Sstevel@tonic-gate { APIC_POWEROFF_VIA_SITKA_BMC, "INTEL", "SC450NX" } /* S50? */ 5950Sstevel@tonic-gate }; 5960Sstevel@tonic-gate 5970Sstevel@tonic-gate int apic_poweroff_method = APIC_POWEROFF_NONE; 5980Sstevel@tonic-gate 5990Sstevel@tonic-gate static struct { 6000Sstevel@tonic-gate uchar_t cntl; 6010Sstevel@tonic-gate uchar_t data; 6020Sstevel@tonic-gate } aspen_bmc[] = { 6030Sstevel@tonic-gate { CC_SMS_WR_START, 0x18 }, /* NetFn/LUN */ 6040Sstevel@tonic-gate { CC_SMS_WR_NEXT, 0x24 }, /* Cmd SET_WATCHDOG_TIMER */ 6050Sstevel@tonic-gate { CC_SMS_WR_NEXT, 0x84 }, /* DataByte 1: SMS/OS no log */ 6060Sstevel@tonic-gate { CC_SMS_WR_NEXT, 0x2 }, /* DataByte 2: Power Down */ 6070Sstevel@tonic-gate { CC_SMS_WR_NEXT, 0x0 }, /* DataByte 3: no pre-timeout */ 6080Sstevel@tonic-gate { CC_SMS_WR_NEXT, 0x0 }, /* DataByte 4: timer expir. */ 6090Sstevel@tonic-gate { CC_SMS_WR_NEXT, 0xa }, /* DataByte 5: init countdown */ 6100Sstevel@tonic-gate { CC_SMS_WR_END, 0x0 }, /* DataByte 6: init countdown */ 6110Sstevel@tonic-gate 6120Sstevel@tonic-gate { CC_SMS_WR_START, 0x18 }, /* NetFn/LUN */ 6130Sstevel@tonic-gate { CC_SMS_WR_END, 0x22 } /* Cmd RESET_WATCHDOG_TIMER */ 6140Sstevel@tonic-gate }; 6150Sstevel@tonic-gate 6160Sstevel@tonic-gate static struct { 6170Sstevel@tonic-gate int port; 6180Sstevel@tonic-gate uchar_t data; 6190Sstevel@tonic-gate } sitka_bmc[] = { 6200Sstevel@tonic-gate { SMS_COMMAND_REGISTER, SMS_WRITE_START }, 6210Sstevel@tonic-gate { SMS_DATA_REGISTER, 0x18 }, /* NetFn/LUN */ 6220Sstevel@tonic-gate { SMS_DATA_REGISTER, 0x24 }, /* Cmd SET_WATCHDOG_TIMER */ 6230Sstevel@tonic-gate { SMS_DATA_REGISTER, 0x84 }, /* DataByte 1: SMS/OS no log */ 6240Sstevel@tonic-gate { SMS_DATA_REGISTER, 0x2 }, /* DataByte 2: Power Down */ 6250Sstevel@tonic-gate { SMS_DATA_REGISTER, 0x0 }, /* DataByte 3: no pre-timeout */ 6260Sstevel@tonic-gate { SMS_DATA_REGISTER, 0x0 }, /* DataByte 4: timer expir. */ 6270Sstevel@tonic-gate { SMS_DATA_REGISTER, 0xa }, /* DataByte 5: init countdown */ 6280Sstevel@tonic-gate { SMS_COMMAND_REGISTER, SMS_WRITE_END }, 6290Sstevel@tonic-gate { SMS_DATA_REGISTER, 0x0 }, /* DataByte 6: init countdown */ 6300Sstevel@tonic-gate 6310Sstevel@tonic-gate { SMS_COMMAND_REGISTER, SMS_WRITE_START }, 6320Sstevel@tonic-gate { SMS_DATA_REGISTER, 0x18 }, /* NetFn/LUN */ 6330Sstevel@tonic-gate { SMS_COMMAND_REGISTER, SMS_WRITE_END }, 6340Sstevel@tonic-gate { SMS_DATA_REGISTER, 0x22 } /* Cmd RESET_WATCHDOG_TIMER */ 6350Sstevel@tonic-gate }; 6360Sstevel@tonic-gate 6370Sstevel@tonic-gate 6380Sstevel@tonic-gate /* Patchable global variables. */ 6390Sstevel@tonic-gate int apic_kmdb_on_nmi = 0; /* 0 - no, 1 - yes enter kmdb */ 6400Sstevel@tonic-gate int apic_debug_mps_id = 0; /* 1 - print MPS ID strings */ 6410Sstevel@tonic-gate 6420Sstevel@tonic-gate /* 6430Sstevel@tonic-gate * ACPI definitions 6440Sstevel@tonic-gate */ 6450Sstevel@tonic-gate /* _PIC method arguments */ 6460Sstevel@tonic-gate #define ACPI_PIC_MODE 0 6470Sstevel@tonic-gate #define ACPI_APIC_MODE 1 6480Sstevel@tonic-gate 6490Sstevel@tonic-gate /* APIC error flags we care about */ 6500Sstevel@tonic-gate #define APIC_SEND_CS_ERROR 0x01 6510Sstevel@tonic-gate #define APIC_RECV_CS_ERROR 0x02 6520Sstevel@tonic-gate #define APIC_CS_ERRORS (APIC_SEND_CS_ERROR|APIC_RECV_CS_ERROR) 6530Sstevel@tonic-gate 6540Sstevel@tonic-gate /* 6550Sstevel@tonic-gate * ACPI variables 6560Sstevel@tonic-gate */ 6570Sstevel@tonic-gate /* 1 = acpi is enabled & working, 0 = acpi is not enabled or not there */ 6580Sstevel@tonic-gate static int apic_enable_acpi = 0; 6590Sstevel@tonic-gate 6600Sstevel@tonic-gate /* ACPI Multiple APIC Description Table ptr */ 6610Sstevel@tonic-gate static MULTIPLE_APIC_TABLE *acpi_mapic_dtp = NULL; 6620Sstevel@tonic-gate 6630Sstevel@tonic-gate /* ACPI Interrupt Source Override Structure ptr */ 6640Sstevel@tonic-gate static MADT_INTERRUPT_OVERRIDE *acpi_isop = NULL; 6650Sstevel@tonic-gate static int acpi_iso_cnt = 0; 6660Sstevel@tonic-gate 6670Sstevel@tonic-gate /* ACPI Non-maskable Interrupt Sources ptr */ 6680Sstevel@tonic-gate static MADT_NMI_SOURCE *acpi_nmi_sp = NULL; 6690Sstevel@tonic-gate static int acpi_nmi_scnt = 0; 6700Sstevel@tonic-gate static MADT_LOCAL_APIC_NMI *acpi_nmi_cp = NULL; 6710Sstevel@tonic-gate static int acpi_nmi_ccnt = 0; 6720Sstevel@tonic-gate 6730Sstevel@tonic-gate /* 6740Sstevel@tonic-gate * extern declarations 6750Sstevel@tonic-gate */ 6760Sstevel@tonic-gate extern int intr_clear(void); 6770Sstevel@tonic-gate extern void intr_restore(uint_t); 6780Sstevel@tonic-gate #if defined(__amd64) 6790Sstevel@tonic-gate extern int intpri_use_cr8; 6800Sstevel@tonic-gate #endif /* __amd64 */ 6810Sstevel@tonic-gate 6820Sstevel@tonic-gate extern int apic_pci_msi_enable_vector(dev_info_t *, int, int, 6830Sstevel@tonic-gate int, int, int); 6840Sstevel@tonic-gate extern apic_irq_t *apic_find_irq(dev_info_t *, struct intrspec *, int); 6851997Sanish extern int apic_pci_msi_unconfigure(dev_info_t *, int, int); 6861997Sanish extern int apic_pci_msi_disable_mode(dev_info_t *, int, int); 6871997Sanish extern int apic_pci_msi_enable_mode(dev_info_t *, int, int); 6880Sstevel@tonic-gate 6890Sstevel@tonic-gate /* 6900Sstevel@tonic-gate * This is the loadable module wrapper 6910Sstevel@tonic-gate */ 6920Sstevel@tonic-gate 6930Sstevel@tonic-gate int 6940Sstevel@tonic-gate _init(void) 6950Sstevel@tonic-gate { 6960Sstevel@tonic-gate if (apic_coarse_hrtime) 6970Sstevel@tonic-gate apic_ops.psm_gethrtime = &apic_gettime; 6980Sstevel@tonic-gate return (psm_mod_init(&apic_hdlp, &apic_psm_info)); 6990Sstevel@tonic-gate } 7000Sstevel@tonic-gate 7010Sstevel@tonic-gate int 7020Sstevel@tonic-gate _fini(void) 7030Sstevel@tonic-gate { 7040Sstevel@tonic-gate return (psm_mod_fini(&apic_hdlp, &apic_psm_info)); 7050Sstevel@tonic-gate } 7060Sstevel@tonic-gate 7070Sstevel@tonic-gate int 7080Sstevel@tonic-gate _info(struct modinfo *modinfop) 7090Sstevel@tonic-gate { 7100Sstevel@tonic-gate return (psm_mod_info(&apic_hdlp, &apic_psm_info, modinfop)); 7110Sstevel@tonic-gate } 7120Sstevel@tonic-gate 7130Sstevel@tonic-gate /* 7140Sstevel@tonic-gate * Auto-configuration routines 7150Sstevel@tonic-gate */ 7160Sstevel@tonic-gate 7170Sstevel@tonic-gate /* 7180Sstevel@tonic-gate * Look at MPSpec 1.4 (Intel Order # 242016-005) for details of what we do here 7190Sstevel@tonic-gate * May work with 1.1 - but not guaranteed. 7200Sstevel@tonic-gate * According to the MP Spec, the MP floating pointer structure 7210Sstevel@tonic-gate * will be searched in the order described below: 7220Sstevel@tonic-gate * 1. In the first kilobyte of Extended BIOS Data Area (EBDA) 7230Sstevel@tonic-gate * 2. Within the last kilobyte of system base memory 7240Sstevel@tonic-gate * 3. In the BIOS ROM address space between 0F0000h and 0FFFFh 7250Sstevel@tonic-gate * Once we find the right signature with proper checksum, we call 7260Sstevel@tonic-gate * either handle_defconf or parse_mpct to get all info necessary for 7270Sstevel@tonic-gate * subsequent operations. 7280Sstevel@tonic-gate */ 7290Sstevel@tonic-gate static int 7300Sstevel@tonic-gate apic_probe() 7310Sstevel@tonic-gate { 7320Sstevel@tonic-gate uint32_t mpct_addr, ebda_start = 0, base_mem_end; 7330Sstevel@tonic-gate caddr_t biosdatap; 7340Sstevel@tonic-gate caddr_t mpct; 7350Sstevel@tonic-gate caddr_t fptr; 7360Sstevel@tonic-gate int i, mpct_size, mapsize, retval = PSM_FAILURE; 7370Sstevel@tonic-gate ushort_t ebda_seg, base_mem_size; 7380Sstevel@tonic-gate struct apic_mpfps_hdr *fpsp; 7390Sstevel@tonic-gate struct apic_mp_cnf_hdr *hdrp; 7400Sstevel@tonic-gate int bypass_cpu_and_ioapics_in_mptables; 7410Sstevel@tonic-gate int acpi_user_options; 7420Sstevel@tonic-gate 7430Sstevel@tonic-gate if (apic_forceload < 0) 7440Sstevel@tonic-gate return (retval); 7450Sstevel@tonic-gate 7460Sstevel@tonic-gate /* Allow override for MADT-only mode */ 7470Sstevel@tonic-gate acpi_user_options = ddi_prop_get_int(DDI_DEV_T_ANY, ddi_root_node(), 0, 7480Sstevel@tonic-gate "acpi-user-options", 0); 7490Sstevel@tonic-gate apic_use_acpi_madt_only = ((acpi_user_options & ACPI_OUSER_MADT) != 0); 7500Sstevel@tonic-gate 7510Sstevel@tonic-gate /* Allow apic_use_acpi to override MADT-only mode */ 7520Sstevel@tonic-gate if (!apic_use_acpi) 7530Sstevel@tonic-gate apic_use_acpi_madt_only = 0; 7540Sstevel@tonic-gate 7550Sstevel@tonic-gate retval = acpi_probe(); 7560Sstevel@tonic-gate 7570Sstevel@tonic-gate /* 7580Sstevel@tonic-gate * mapin the bios data area 40:0 7590Sstevel@tonic-gate * 40:13h - two-byte location reports the base memory size 7600Sstevel@tonic-gate * 40:0Eh - two-byte location for the exact starting address of 7610Sstevel@tonic-gate * the EBDA segment for EISA 7620Sstevel@tonic-gate */ 7630Sstevel@tonic-gate biosdatap = psm_map_phys(0x400, 0x20, PROT_READ); 7640Sstevel@tonic-gate if (!biosdatap) 7650Sstevel@tonic-gate return (retval); 7660Sstevel@tonic-gate fpsp = (struct apic_mpfps_hdr *)NULL; 7670Sstevel@tonic-gate mapsize = MPFPS_RAM_WIN_LEN; 7680Sstevel@tonic-gate /*LINTED: pointer cast may result in improper alignment */ 7690Sstevel@tonic-gate ebda_seg = *((ushort_t *)(biosdatap+0xe)); 7700Sstevel@tonic-gate /* check the 1k of EBDA */ 7710Sstevel@tonic-gate if (ebda_seg) { 7720Sstevel@tonic-gate ebda_start = ((uint32_t)ebda_seg) << 4; 7730Sstevel@tonic-gate fptr = psm_map_phys(ebda_start, MPFPS_RAM_WIN_LEN, PROT_READ); 7740Sstevel@tonic-gate if (fptr) { 7750Sstevel@tonic-gate if (!(fpsp = 7760Sstevel@tonic-gate apic_find_fps_sig(fptr, MPFPS_RAM_WIN_LEN))) 7770Sstevel@tonic-gate psm_unmap_phys(fptr, MPFPS_RAM_WIN_LEN); 7780Sstevel@tonic-gate } 7790Sstevel@tonic-gate } 7800Sstevel@tonic-gate /* If not in EBDA, check the last k of system base memory */ 7810Sstevel@tonic-gate if (!fpsp) { 7820Sstevel@tonic-gate /*LINTED: pointer cast may result in improper alignment */ 7830Sstevel@tonic-gate base_mem_size = *((ushort_t *)(biosdatap + 0x13)); 7840Sstevel@tonic-gate 7850Sstevel@tonic-gate if (base_mem_size > 512) 7860Sstevel@tonic-gate base_mem_end = 639 * 1024; 7870Sstevel@tonic-gate else 7880Sstevel@tonic-gate base_mem_end = 511 * 1024; 7890Sstevel@tonic-gate /* if ebda == last k of base mem, skip to check BIOS ROM */ 7900Sstevel@tonic-gate if (base_mem_end != ebda_start) { 7910Sstevel@tonic-gate 7920Sstevel@tonic-gate fptr = psm_map_phys(base_mem_end, MPFPS_RAM_WIN_LEN, 7930Sstevel@tonic-gate PROT_READ); 7940Sstevel@tonic-gate 7950Sstevel@tonic-gate if (fptr) { 7960Sstevel@tonic-gate if (!(fpsp = apic_find_fps_sig(fptr, 7970Sstevel@tonic-gate MPFPS_RAM_WIN_LEN))) 7980Sstevel@tonic-gate psm_unmap_phys(fptr, MPFPS_RAM_WIN_LEN); 7990Sstevel@tonic-gate } 8000Sstevel@tonic-gate } 8010Sstevel@tonic-gate } 8020Sstevel@tonic-gate psm_unmap_phys(biosdatap, 0x20); 8030Sstevel@tonic-gate 8040Sstevel@tonic-gate /* If still cannot find it, check the BIOS ROM space */ 8050Sstevel@tonic-gate if (!fpsp) { 8060Sstevel@tonic-gate mapsize = MPFPS_ROM_WIN_LEN; 8070Sstevel@tonic-gate fptr = psm_map_phys(MPFPS_ROM_WIN_START, 8080Sstevel@tonic-gate MPFPS_ROM_WIN_LEN, PROT_READ); 8090Sstevel@tonic-gate if (fptr) { 8100Sstevel@tonic-gate if (!(fpsp = 8110Sstevel@tonic-gate apic_find_fps_sig(fptr, MPFPS_ROM_WIN_LEN))) { 8120Sstevel@tonic-gate psm_unmap_phys(fptr, MPFPS_ROM_WIN_LEN); 8130Sstevel@tonic-gate return (retval); 8140Sstevel@tonic-gate } 8150Sstevel@tonic-gate } 8160Sstevel@tonic-gate } 8170Sstevel@tonic-gate 8180Sstevel@tonic-gate if (apic_checksum((caddr_t)fpsp, fpsp->mpfps_length * 16) != 0) { 8190Sstevel@tonic-gate psm_unmap_phys(fptr, MPFPS_ROM_WIN_LEN); 8200Sstevel@tonic-gate return (retval); 8210Sstevel@tonic-gate } 8220Sstevel@tonic-gate 8230Sstevel@tonic-gate apic_spec_rev = fpsp->mpfps_spec_rev; 8240Sstevel@tonic-gate if ((apic_spec_rev != 04) && (apic_spec_rev != 01)) { 8250Sstevel@tonic-gate psm_unmap_phys(fptr, MPFPS_ROM_WIN_LEN); 8260Sstevel@tonic-gate return (retval); 8270Sstevel@tonic-gate } 8280Sstevel@tonic-gate 8290Sstevel@tonic-gate /* check IMCR is present or not */ 8300Sstevel@tonic-gate apic_imcrp = fpsp->mpfps_featinfo2 & MPFPS_FEATINFO2_IMCRP; 8310Sstevel@tonic-gate 8320Sstevel@tonic-gate /* check default configuration (dual CPUs) */ 8330Sstevel@tonic-gate if ((apic_defconf = fpsp->mpfps_featinfo1) != 0) { 8340Sstevel@tonic-gate psm_unmap_phys(fptr, mapsize); 8350Sstevel@tonic-gate return (apic_handle_defconf()); 8360Sstevel@tonic-gate } 8370Sstevel@tonic-gate 8380Sstevel@tonic-gate /* MP Configuration Table */ 8390Sstevel@tonic-gate mpct_addr = (uint32_t)(fpsp->mpfps_mpct_paddr); 8400Sstevel@tonic-gate 8410Sstevel@tonic-gate psm_unmap_phys(fptr, mapsize); /* unmap floating ptr struct */ 8420Sstevel@tonic-gate 8430Sstevel@tonic-gate /* 8440Sstevel@tonic-gate * Map in enough memory for the MP Configuration Table Header. 8450Sstevel@tonic-gate * Use this table to read the total length of the BIOS data and 8460Sstevel@tonic-gate * map in all the info 8470Sstevel@tonic-gate */ 8480Sstevel@tonic-gate /*LINTED: pointer cast may result in improper alignment */ 8490Sstevel@tonic-gate hdrp = (struct apic_mp_cnf_hdr *)psm_map_phys(mpct_addr, 8500Sstevel@tonic-gate sizeof (struct apic_mp_cnf_hdr), PROT_READ); 8510Sstevel@tonic-gate if (!hdrp) 8520Sstevel@tonic-gate return (retval); 8530Sstevel@tonic-gate 8540Sstevel@tonic-gate /* check mp configuration table signature PCMP */ 8550Sstevel@tonic-gate if (hdrp->mpcnf_sig != 0x504d4350) { 8560Sstevel@tonic-gate psm_unmap_phys((caddr_t)hdrp, sizeof (struct apic_mp_cnf_hdr)); 8570Sstevel@tonic-gate return (retval); 8580Sstevel@tonic-gate } 8590Sstevel@tonic-gate mpct_size = (int)hdrp->mpcnf_tbl_length; 8600Sstevel@tonic-gate 8610Sstevel@tonic-gate apic_set_pwroff_method_from_mpcnfhdr(hdrp); 8620Sstevel@tonic-gate 8630Sstevel@tonic-gate psm_unmap_phys((caddr_t)hdrp, sizeof (struct apic_mp_cnf_hdr)); 8640Sstevel@tonic-gate 8650Sstevel@tonic-gate if ((retval == PSM_SUCCESS) && !apic_use_acpi_madt_only) { 8660Sstevel@tonic-gate /* This is an ACPI machine No need for further checks */ 8670Sstevel@tonic-gate return (retval); 8680Sstevel@tonic-gate } 8690Sstevel@tonic-gate 8700Sstevel@tonic-gate /* 8710Sstevel@tonic-gate * Map in the entries for this machine, ie. Processor 8720Sstevel@tonic-gate * Entry Tables, Bus Entry Tables, etc. 8730Sstevel@tonic-gate * They are in fixed order following one another 8740Sstevel@tonic-gate */ 8750Sstevel@tonic-gate mpct = psm_map_phys(mpct_addr, mpct_size, PROT_READ); 8760Sstevel@tonic-gate if (!mpct) 8770Sstevel@tonic-gate return (retval); 8780Sstevel@tonic-gate 8790Sstevel@tonic-gate if (apic_checksum(mpct, mpct_size) != 0) 8800Sstevel@tonic-gate goto apic_fail1; 8810Sstevel@tonic-gate 8820Sstevel@tonic-gate 8830Sstevel@tonic-gate /*LINTED: pointer cast may result in improper alignment */ 8840Sstevel@tonic-gate hdrp = (struct apic_mp_cnf_hdr *)mpct; 8850Sstevel@tonic-gate /*LINTED: pointer cast may result in improper alignment */ 8860Sstevel@tonic-gate apicadr = (uint32_t *)psm_map_phys((uint32_t)hdrp->mpcnf_local_apic, 8870Sstevel@tonic-gate APIC_LOCAL_MEMLEN, PROT_READ | PROT_WRITE); 8880Sstevel@tonic-gate if (!apicadr) 8890Sstevel@tonic-gate goto apic_fail1; 8900Sstevel@tonic-gate 8910Sstevel@tonic-gate /* Parse all information in the tables */ 8920Sstevel@tonic-gate bypass_cpu_and_ioapics_in_mptables = (retval == PSM_SUCCESS); 8930Sstevel@tonic-gate if (apic_parse_mpct(mpct, bypass_cpu_and_ioapics_in_mptables) == 8940Sstevel@tonic-gate PSM_SUCCESS) 8950Sstevel@tonic-gate return (PSM_SUCCESS); 8960Sstevel@tonic-gate 8970Sstevel@tonic-gate for (i = 0; i < apic_io_max; i++) 8980Sstevel@tonic-gate psm_unmap_phys((caddr_t)apicioadr[i], APIC_IO_MEMLEN); 8990Sstevel@tonic-gate if (apic_cpus) 9000Sstevel@tonic-gate kmem_free(apic_cpus, sizeof (*apic_cpus) * apic_nproc); 9010Sstevel@tonic-gate if (apicadr) 9020Sstevel@tonic-gate psm_unmap_phys((caddr_t)apicadr, APIC_LOCAL_MEMLEN); 9030Sstevel@tonic-gate apic_fail1: 9040Sstevel@tonic-gate psm_unmap_phys(mpct, mpct_size); 9050Sstevel@tonic-gate return (retval); 9060Sstevel@tonic-gate } 9070Sstevel@tonic-gate 9080Sstevel@tonic-gate static void 9090Sstevel@tonic-gate apic_set_pwroff_method_from_mpcnfhdr(struct apic_mp_cnf_hdr *hdrp) 9100Sstevel@tonic-gate { 9110Sstevel@tonic-gate int i; 9120Sstevel@tonic-gate 9130Sstevel@tonic-gate for (i = 0; i < (sizeof (apic_mps_ids) / sizeof (apic_mps_ids[0])); 9140Sstevel@tonic-gate i++) { 9150Sstevel@tonic-gate if ((strncmp(hdrp->mpcnf_oem_str, apic_mps_ids[i].oem_id, 9160Sstevel@tonic-gate strlen(apic_mps_ids[i].oem_id)) == 0) && 9170Sstevel@tonic-gate (strncmp(hdrp->mpcnf_prod_str, apic_mps_ids[i].prod_id, 9180Sstevel@tonic-gate strlen(apic_mps_ids[i].prod_id)) == 0)) { 9190Sstevel@tonic-gate 9200Sstevel@tonic-gate apic_poweroff_method = apic_mps_ids[i].poweroff_method; 9210Sstevel@tonic-gate break; 9220Sstevel@tonic-gate } 9230Sstevel@tonic-gate } 9240Sstevel@tonic-gate 9250Sstevel@tonic-gate if (apic_debug_mps_id != 0) { 9260Sstevel@tonic-gate cmn_err(CE_CONT, "pcplusmp: MPS OEM ID = '%c%c%c%c%c%c%c%c'" 9270Sstevel@tonic-gate "Product ID = '%c%c%c%c%c%c%c%c%c%c%c%c'\n", 9280Sstevel@tonic-gate hdrp->mpcnf_oem_str[0], 9290Sstevel@tonic-gate hdrp->mpcnf_oem_str[1], 9300Sstevel@tonic-gate hdrp->mpcnf_oem_str[2], 9310Sstevel@tonic-gate hdrp->mpcnf_oem_str[3], 9320Sstevel@tonic-gate hdrp->mpcnf_oem_str[4], 9330Sstevel@tonic-gate hdrp->mpcnf_oem_str[5], 9340Sstevel@tonic-gate hdrp->mpcnf_oem_str[6], 9350Sstevel@tonic-gate hdrp->mpcnf_oem_str[7], 9360Sstevel@tonic-gate hdrp->mpcnf_prod_str[0], 9370Sstevel@tonic-gate hdrp->mpcnf_prod_str[1], 9380Sstevel@tonic-gate hdrp->mpcnf_prod_str[2], 9390Sstevel@tonic-gate hdrp->mpcnf_prod_str[3], 9400Sstevel@tonic-gate hdrp->mpcnf_prod_str[4], 9410Sstevel@tonic-gate hdrp->mpcnf_prod_str[5], 9420Sstevel@tonic-gate hdrp->mpcnf_prod_str[6], 9430Sstevel@tonic-gate hdrp->mpcnf_prod_str[7], 9440Sstevel@tonic-gate hdrp->mpcnf_prod_str[8], 9450Sstevel@tonic-gate hdrp->mpcnf_prod_str[9], 9460Sstevel@tonic-gate hdrp->mpcnf_prod_str[10], 9470Sstevel@tonic-gate hdrp->mpcnf_prod_str[11]); 9480Sstevel@tonic-gate } 9490Sstevel@tonic-gate } 9500Sstevel@tonic-gate 9510Sstevel@tonic-gate static int 9520Sstevel@tonic-gate acpi_probe(void) 9530Sstevel@tonic-gate { 9540Sstevel@tonic-gate int i, id, intmax, ver, index, rv; 9550Sstevel@tonic-gate int acpi_verboseflags = 0; 9560Sstevel@tonic-gate int madt_seen, madt_size; 9570Sstevel@tonic-gate APIC_HEADER *ap; 9580Sstevel@tonic-gate MADT_PROCESSOR_APIC *mpa; 9590Sstevel@tonic-gate MADT_IO_APIC *mia; 9600Sstevel@tonic-gate MADT_IO_SAPIC *misa; 9610Sstevel@tonic-gate MADT_INTERRUPT_OVERRIDE *mio; 9620Sstevel@tonic-gate MADT_NMI_SOURCE *mns; 9630Sstevel@tonic-gate MADT_INTERRUPT_SOURCE *mis; 9640Sstevel@tonic-gate MADT_LOCAL_APIC_NMI *mlan; 9650Sstevel@tonic-gate MADT_ADDRESS_OVERRIDE *mao; 9660Sstevel@tonic-gate ACPI_OBJECT_LIST arglist; 9670Sstevel@tonic-gate ACPI_OBJECT arg; 9680Sstevel@tonic-gate int sci; 9690Sstevel@tonic-gate iflag_t sci_flags; 9700Sstevel@tonic-gate volatile int32_t *ioapic; 9710Sstevel@tonic-gate char local_ids[NCPU]; 9720Sstevel@tonic-gate char proc_ids[NCPU]; 9730Sstevel@tonic-gate uchar_t hid; 9740Sstevel@tonic-gate 9750Sstevel@tonic-gate if (!apic_use_acpi) 9760Sstevel@tonic-gate return (PSM_FAILURE); 9770Sstevel@tonic-gate 9780Sstevel@tonic-gate if (AcpiGetFirmwareTable(APIC_SIG, 1, ACPI_LOGICAL_ADDRESSING, 9790Sstevel@tonic-gate (ACPI_TABLE_HEADER **) &acpi_mapic_dtp) != AE_OK) 9800Sstevel@tonic-gate return (PSM_FAILURE); 9810Sstevel@tonic-gate 9820Sstevel@tonic-gate apicadr = (uint32_t *)psm_map_phys( 9830Sstevel@tonic-gate (uint32_t)acpi_mapic_dtp->LocalApicAddress, 9840Sstevel@tonic-gate APIC_LOCAL_MEMLEN, PROT_READ | PROT_WRITE); 9850Sstevel@tonic-gate if (!apicadr) 9860Sstevel@tonic-gate return (PSM_FAILURE); 9870Sstevel@tonic-gate 9880Sstevel@tonic-gate id = apicadr[APIC_LID_REG]; 9890Sstevel@tonic-gate local_ids[0] = (uchar_t)(((uint_t)id) >> 24); 9900Sstevel@tonic-gate apic_nproc = index = 1; 991*2006Sandrei CPUSET_ONLY(apic_cpumask, 0); 9920Sstevel@tonic-gate apic_io_max = 0; 9930Sstevel@tonic-gate 9940Sstevel@tonic-gate ap = (APIC_HEADER *) (acpi_mapic_dtp + 1); 9950Sstevel@tonic-gate madt_size = acpi_mapic_dtp->Length; 9960Sstevel@tonic-gate madt_seen = sizeof (*acpi_mapic_dtp); 9970Sstevel@tonic-gate 9980Sstevel@tonic-gate while (madt_seen < madt_size) { 9990Sstevel@tonic-gate switch (ap->Type) { 10000Sstevel@tonic-gate case APIC_PROCESSOR: 10010Sstevel@tonic-gate mpa = (MADT_PROCESSOR_APIC *) ap; 10020Sstevel@tonic-gate if (mpa->ProcessorEnabled) { 10030Sstevel@tonic-gate if (mpa->LocalApicId == local_ids[0]) 10040Sstevel@tonic-gate proc_ids[0] = mpa->ProcessorId; 10050Sstevel@tonic-gate else if (apic_nproc < NCPU) { 10060Sstevel@tonic-gate local_ids[index] = mpa->LocalApicId; 10070Sstevel@tonic-gate proc_ids[index] = mpa->ProcessorId; 1008*2006Sandrei CPUSET_ADD(apic_cpumask, index); 10090Sstevel@tonic-gate index++; 10100Sstevel@tonic-gate apic_nproc++; 10110Sstevel@tonic-gate } else 10120Sstevel@tonic-gate cmn_err(CE_WARN, "pcplusmp: exceeded " 10130Sstevel@tonic-gate "maximum no. of CPUs (= %d)", NCPU); 10140Sstevel@tonic-gate } 10150Sstevel@tonic-gate break; 10160Sstevel@tonic-gate 10170Sstevel@tonic-gate case APIC_IO: 10180Sstevel@tonic-gate mia = (MADT_IO_APIC *) ap; 10190Sstevel@tonic-gate if (apic_io_max < MAX_IO_APIC) { 10200Sstevel@tonic-gate apic_io_id[apic_io_max] = mia->IoApicId; 10210Sstevel@tonic-gate apic_io_vectbase[apic_io_max] = 10220Sstevel@tonic-gate mia->Interrupt; 10230Sstevel@tonic-gate ioapic = apicioadr[apic_io_max] = 10240Sstevel@tonic-gate (int32_t *)psm_map_phys( 10250Sstevel@tonic-gate (uint32_t)mia->Address, 10260Sstevel@tonic-gate APIC_IO_MEMLEN, PROT_READ | PROT_WRITE); 10270Sstevel@tonic-gate if (!ioapic) 10280Sstevel@tonic-gate goto cleanup; 10290Sstevel@tonic-gate apic_io_max++; 10300Sstevel@tonic-gate } 10310Sstevel@tonic-gate break; 10320Sstevel@tonic-gate 10330Sstevel@tonic-gate case APIC_XRUPT_OVERRIDE: 10340Sstevel@tonic-gate mio = (MADT_INTERRUPT_OVERRIDE *) ap; 10350Sstevel@tonic-gate if (acpi_isop == NULL) 10360Sstevel@tonic-gate acpi_isop = mio; 10370Sstevel@tonic-gate acpi_iso_cnt++; 10380Sstevel@tonic-gate break; 10390Sstevel@tonic-gate 10400Sstevel@tonic-gate case APIC_NMI: 10410Sstevel@tonic-gate /* UNIMPLEMENTED */ 10420Sstevel@tonic-gate mns = (MADT_NMI_SOURCE *) ap; 10430Sstevel@tonic-gate if (acpi_nmi_sp == NULL) 10440Sstevel@tonic-gate acpi_nmi_sp = mns; 10450Sstevel@tonic-gate acpi_nmi_scnt++; 10460Sstevel@tonic-gate 10470Sstevel@tonic-gate cmn_err(CE_NOTE, "!apic: nmi source: %d %d %d\n", 10480Sstevel@tonic-gate mns->Interrupt, mns->Polarity, 10490Sstevel@tonic-gate mns->TriggerMode); 10500Sstevel@tonic-gate break; 10510Sstevel@tonic-gate 10520Sstevel@tonic-gate case APIC_LOCAL_NMI: 10530Sstevel@tonic-gate /* UNIMPLEMENTED */ 10540Sstevel@tonic-gate mlan = (MADT_LOCAL_APIC_NMI *) ap; 10550Sstevel@tonic-gate if (acpi_nmi_cp == NULL) 10560Sstevel@tonic-gate acpi_nmi_cp = mlan; 10570Sstevel@tonic-gate acpi_nmi_ccnt++; 10580Sstevel@tonic-gate 10590Sstevel@tonic-gate cmn_err(CE_NOTE, "!apic: local nmi: %d %d %d %d\n", 10600Sstevel@tonic-gate mlan->ProcessorId, mlan->Polarity, 10610Sstevel@tonic-gate mlan->TriggerMode, mlan->Lint); 10620Sstevel@tonic-gate break; 10630Sstevel@tonic-gate 10640Sstevel@tonic-gate case APIC_ADDRESS_OVERRIDE: 10650Sstevel@tonic-gate /* UNIMPLEMENTED */ 10660Sstevel@tonic-gate mao = (MADT_ADDRESS_OVERRIDE *) ap; 10670Sstevel@tonic-gate cmn_err(CE_NOTE, "!apic: address override: %lx\n", 10680Sstevel@tonic-gate (long)mao->Address); 10690Sstevel@tonic-gate break; 10700Sstevel@tonic-gate 10710Sstevel@tonic-gate case APIC_IO_SAPIC: 10720Sstevel@tonic-gate /* UNIMPLEMENTED */ 10730Sstevel@tonic-gate misa = (MADT_IO_SAPIC *) ap; 10740Sstevel@tonic-gate 10750Sstevel@tonic-gate cmn_err(CE_NOTE, "!apic: io sapic: %d %d %lx\n", 10760Sstevel@tonic-gate misa->IoSapicId, misa->InterruptBase, 10770Sstevel@tonic-gate (long)misa->Address); 10780Sstevel@tonic-gate break; 10790Sstevel@tonic-gate 10800Sstevel@tonic-gate case APIC_XRUPT_SOURCE: 10810Sstevel@tonic-gate /* UNIMPLEMENTED */ 10820Sstevel@tonic-gate mis = (MADT_INTERRUPT_SOURCE *) ap; 10830Sstevel@tonic-gate 10840Sstevel@tonic-gate cmn_err(CE_NOTE, 10850Sstevel@tonic-gate "!apic: irq source: %d %d %d %d %d %d %d\n", 10860Sstevel@tonic-gate mis->ProcessorId, mis->ProcessorEid, 10870Sstevel@tonic-gate mis->Interrupt, mis->Polarity, 10880Sstevel@tonic-gate mis->TriggerMode, mis->InterruptType, 10890Sstevel@tonic-gate mis->IoSapicVector); 10900Sstevel@tonic-gate break; 10910Sstevel@tonic-gate case APIC_RESERVED: 10920Sstevel@tonic-gate default: 10931681Smyers break; /* ignore unknown items as per ACPI spec */ 10940Sstevel@tonic-gate } 10950Sstevel@tonic-gate 10960Sstevel@tonic-gate /* advance to next entry */ 10970Sstevel@tonic-gate madt_seen += ap->Length; 10980Sstevel@tonic-gate ap = (APIC_HEADER *)(((char *)ap) + ap->Length); 10990Sstevel@tonic-gate } 11000Sstevel@tonic-gate 11010Sstevel@tonic-gate if ((apic_cpus = kmem_zalloc(sizeof (*apic_cpus) * apic_nproc, 11020Sstevel@tonic-gate KM_NOSLEEP)) == NULL) 11030Sstevel@tonic-gate goto cleanup; 11040Sstevel@tonic-gate 11050Sstevel@tonic-gate /* 11060Sstevel@tonic-gate * ACPI doesn't provide the local apic ver, get it directly from the 11070Sstevel@tonic-gate * local apic 11080Sstevel@tonic-gate */ 11090Sstevel@tonic-gate ver = apicadr[APIC_VERS_REG]; 11100Sstevel@tonic-gate for (i = 0; i < apic_nproc; i++) { 11110Sstevel@tonic-gate apic_cpus[i].aci_local_id = local_ids[i]; 11120Sstevel@tonic-gate apic_cpus[i].aci_local_ver = (uchar_t)(ver & 0xFF); 11130Sstevel@tonic-gate } 11140Sstevel@tonic-gate for (i = 0; i < apic_io_max; i++) { 11150Sstevel@tonic-gate ioapic = apicioadr[i]; 11160Sstevel@tonic-gate 11170Sstevel@tonic-gate /* 11180Sstevel@tonic-gate * need to check Sitka on the following acpi problem 11190Sstevel@tonic-gate * On the Sitka, the ioapic's apic_id field isn't reporting 11200Sstevel@tonic-gate * the actual io apic id. We have reported this problem 11210Sstevel@tonic-gate * to Intel. Until they fix the problem, we will get the 11220Sstevel@tonic-gate * actual id directly from the ioapic. 11230Sstevel@tonic-gate */ 11240Sstevel@tonic-gate ioapic[APIC_IO_REG] = APIC_ID_CMD; 11250Sstevel@tonic-gate id = ioapic[APIC_IO_DATA]; 11260Sstevel@tonic-gate hid = (uchar_t)(((uint_t)id) >> 24); 11270Sstevel@tonic-gate 11280Sstevel@tonic-gate if (hid != apic_io_id[i]) { 11290Sstevel@tonic-gate if (apic_io_id[i] == 0) 11300Sstevel@tonic-gate apic_io_id[i] = hid; 11310Sstevel@tonic-gate else { /* set ioapic id to whatever reported by ACPI */ 11320Sstevel@tonic-gate id = ((int32_t)apic_io_id[i]) << 24; 11330Sstevel@tonic-gate ioapic[APIC_IO_REG] = APIC_ID_CMD; 11340Sstevel@tonic-gate ioapic[APIC_IO_DATA] = id; 11350Sstevel@tonic-gate } 11360Sstevel@tonic-gate } 11370Sstevel@tonic-gate ioapic[APIC_IO_REG] = APIC_VERS_CMD; 11380Sstevel@tonic-gate ver = ioapic[APIC_IO_DATA]; 11390Sstevel@tonic-gate apic_io_ver[i] = (uchar_t)(ver & 0xff); 11400Sstevel@tonic-gate intmax = (ver >> 16) & 0xff; 11410Sstevel@tonic-gate apic_io_vectend[i] = apic_io_vectbase[i] + intmax; 1142881Sjohnny if (apic_first_avail_irq <= apic_io_vectend[i]) 1143881Sjohnny apic_first_avail_irq = apic_io_vectend[i] + 1; 11440Sstevel@tonic-gate } 11450Sstevel@tonic-gate 11460Sstevel@tonic-gate 11470Sstevel@tonic-gate /* 11480Sstevel@tonic-gate * Process SCI configuration here 11490Sstevel@tonic-gate * An error may be returned here if 11500Sstevel@tonic-gate * acpi-user-options specifies legacy mode 11510Sstevel@tonic-gate * (no SCI, no ACPI mode) 11520Sstevel@tonic-gate */ 11530Sstevel@tonic-gate if (acpica_get_sci(&sci, &sci_flags) != AE_OK) 11540Sstevel@tonic-gate sci = -1; 11550Sstevel@tonic-gate 11560Sstevel@tonic-gate /* 11570Sstevel@tonic-gate * Now call acpi_init() to generate namespaces 11580Sstevel@tonic-gate * If this fails, we don't attempt to use ACPI 11590Sstevel@tonic-gate * even if we were able to get a MADT above 11600Sstevel@tonic-gate */ 11610Sstevel@tonic-gate if (acpica_init() != AE_OK) 11620Sstevel@tonic-gate goto cleanup; 11630Sstevel@tonic-gate 11640Sstevel@tonic-gate /* 11650Sstevel@tonic-gate * Squirrel away the SCI and flags for later on 11660Sstevel@tonic-gate * in apic_picinit() when we're ready 11670Sstevel@tonic-gate */ 11680Sstevel@tonic-gate apic_sci_vect = sci; 11690Sstevel@tonic-gate apic_sci_flags = sci_flags; 11700Sstevel@tonic-gate 11710Sstevel@tonic-gate if (apic_verbose & APIC_VERBOSE_IRQ_FLAG) 11720Sstevel@tonic-gate acpi_verboseflags |= PSM_VERBOSE_IRQ_FLAG; 11730Sstevel@tonic-gate 11740Sstevel@tonic-gate if (apic_verbose & APIC_VERBOSE_POWEROFF_FLAG) 11750Sstevel@tonic-gate acpi_verboseflags |= PSM_VERBOSE_POWEROFF_FLAG; 11760Sstevel@tonic-gate 11770Sstevel@tonic-gate if (apic_verbose & APIC_VERBOSE_POWEROFF_PAUSE_FLAG) 11780Sstevel@tonic-gate acpi_verboseflags |= PSM_VERBOSE_POWEROFF_PAUSE_FLAG; 11790Sstevel@tonic-gate 11800Sstevel@tonic-gate if (acpi_psm_init(apic_psm_info.p_mach_idstring, acpi_verboseflags) == 11810Sstevel@tonic-gate ACPI_PSM_FAILURE) 11820Sstevel@tonic-gate goto cleanup; 11830Sstevel@tonic-gate 11840Sstevel@tonic-gate /* Enable ACPI APIC interrupt routing */ 11850Sstevel@tonic-gate arglist.Count = 1; 11860Sstevel@tonic-gate arglist.Pointer = &arg; 11870Sstevel@tonic-gate arg.Type = ACPI_TYPE_INTEGER; 11880Sstevel@tonic-gate arg.Integer.Value = ACPI_APIC_MODE; /* 1 */ 11890Sstevel@tonic-gate rv = AcpiEvaluateObject(NULL, "\\_PIC", &arglist, NULL); 11900Sstevel@tonic-gate if (rv == AE_OK) { 11910Sstevel@tonic-gate build_reserved_irqlist((uchar_t *)apic_reserved_irqlist); 11920Sstevel@tonic-gate apic_enable_acpi = 1; 11930Sstevel@tonic-gate if (apic_use_acpi_madt_only) { 11940Sstevel@tonic-gate cmn_err(CE_CONT, 11950Sstevel@tonic-gate "?Using ACPI for CPU/IOAPIC information ONLY\n"); 11960Sstevel@tonic-gate } 11970Sstevel@tonic-gate return (PSM_SUCCESS); 11980Sstevel@tonic-gate } 11990Sstevel@tonic-gate /* if setting APIC mode failed above, we fall through to cleanup */ 12000Sstevel@tonic-gate 12010Sstevel@tonic-gate cleanup: 12020Sstevel@tonic-gate if (apicadr != NULL) { 12030Sstevel@tonic-gate psm_unmap_phys((caddr_t)apicadr, APIC_LOCAL_MEMLEN); 12040Sstevel@tonic-gate apicadr = NULL; 12050Sstevel@tonic-gate } 12060Sstevel@tonic-gate apic_nproc = 0; 12070Sstevel@tonic-gate for (i = 0; i < apic_io_max; i++) { 12080Sstevel@tonic-gate psm_unmap_phys((caddr_t)apicioadr[i], APIC_IO_MEMLEN); 12090Sstevel@tonic-gate apicioadr[i] = NULL; 12100Sstevel@tonic-gate } 12110Sstevel@tonic-gate apic_io_max = 0; 12120Sstevel@tonic-gate acpi_isop = NULL; 12130Sstevel@tonic-gate acpi_iso_cnt = 0; 12140Sstevel@tonic-gate acpi_nmi_sp = NULL; 12150Sstevel@tonic-gate acpi_nmi_scnt = 0; 12160Sstevel@tonic-gate acpi_nmi_cp = NULL; 12170Sstevel@tonic-gate acpi_nmi_ccnt = 0; 12180Sstevel@tonic-gate return (PSM_FAILURE); 12190Sstevel@tonic-gate } 12200Sstevel@tonic-gate 12210Sstevel@tonic-gate /* 12220Sstevel@tonic-gate * Handle default configuration. Fill in reqd global variables & tables 12230Sstevel@tonic-gate * Fill all details as MP table does not give any more info 12240Sstevel@tonic-gate */ 12250Sstevel@tonic-gate static int 12260Sstevel@tonic-gate apic_handle_defconf() 12270Sstevel@tonic-gate { 12280Sstevel@tonic-gate uint_t lid; 12290Sstevel@tonic-gate 12300Sstevel@tonic-gate /*LINTED: pointer cast may result in improper alignment */ 12310Sstevel@tonic-gate apicioadr[0] = (int32_t *)psm_map_phys(APIC_IO_ADDR, 12320Sstevel@tonic-gate APIC_IO_MEMLEN, PROT_READ | PROT_WRITE); 12330Sstevel@tonic-gate /*LINTED: pointer cast may result in improper alignment */ 12340Sstevel@tonic-gate apicadr = (uint32_t *)psm_map_phys(APIC_LOCAL_ADDR, 12350Sstevel@tonic-gate APIC_LOCAL_MEMLEN, PROT_READ | PROT_WRITE); 12360Sstevel@tonic-gate apic_cpus = (apic_cpus_info_t *) 12370Sstevel@tonic-gate kmem_zalloc(sizeof (*apic_cpus) * 2, KM_NOSLEEP); 12380Sstevel@tonic-gate if ((!apicadr) || (!apicioadr[0]) || (!apic_cpus)) 12390Sstevel@tonic-gate goto apic_handle_defconf_fail; 1240*2006Sandrei CPUSET_ONLY(apic_cpumask, 0); 1241*2006Sandrei CPUSET_ADD(apic_cpumask, 1); 12420Sstevel@tonic-gate apic_nproc = 2; 12430Sstevel@tonic-gate lid = apicadr[APIC_LID_REG]; 12440Sstevel@tonic-gate apic_cpus[0].aci_local_id = (uchar_t)(lid >> APIC_ID_BIT_OFFSET); 12450Sstevel@tonic-gate /* 12460Sstevel@tonic-gate * According to the PC+MP spec 1.1, the local ids 12470Sstevel@tonic-gate * for the default configuration has to be 0 or 1 12480Sstevel@tonic-gate */ 12490Sstevel@tonic-gate if (apic_cpus[0].aci_local_id == 1) 12500Sstevel@tonic-gate apic_cpus[1].aci_local_id = 0; 12510Sstevel@tonic-gate else if (apic_cpus[0].aci_local_id == 0) 12520Sstevel@tonic-gate apic_cpus[1].aci_local_id = 1; 12530Sstevel@tonic-gate else 12540Sstevel@tonic-gate goto apic_handle_defconf_fail; 12550Sstevel@tonic-gate 12560Sstevel@tonic-gate apic_io_id[0] = 2; 12570Sstevel@tonic-gate apic_io_max = 1; 12580Sstevel@tonic-gate if (apic_defconf >= 5) { 12590Sstevel@tonic-gate apic_cpus[0].aci_local_ver = APIC_INTEGRATED_VERS; 12600Sstevel@tonic-gate apic_cpus[1].aci_local_ver = APIC_INTEGRATED_VERS; 12610Sstevel@tonic-gate apic_io_ver[0] = APIC_INTEGRATED_VERS; 12620Sstevel@tonic-gate } else { 12630Sstevel@tonic-gate apic_cpus[0].aci_local_ver = 0; /* 82489 DX */ 12640Sstevel@tonic-gate apic_cpus[1].aci_local_ver = 0; 12650Sstevel@tonic-gate apic_io_ver[0] = 0; 12660Sstevel@tonic-gate } 12670Sstevel@tonic-gate if (apic_defconf == 2 || apic_defconf == 3 || apic_defconf == 6) 12680Sstevel@tonic-gate eisa_level_intr_mask = (inb(EISA_LEVEL_CNTL + 1) << 8) | 12690Sstevel@tonic-gate inb(EISA_LEVEL_CNTL) | ((uint_t)INT32_MAX + 1); 12700Sstevel@tonic-gate return (PSM_SUCCESS); 12710Sstevel@tonic-gate 12720Sstevel@tonic-gate apic_handle_defconf_fail: 12730Sstevel@tonic-gate if (apic_cpus) 12740Sstevel@tonic-gate kmem_free(apic_cpus, sizeof (*apic_cpus) * 2); 12750Sstevel@tonic-gate if (apicadr) 12760Sstevel@tonic-gate psm_unmap_phys((caddr_t)apicadr, APIC_LOCAL_MEMLEN); 12770Sstevel@tonic-gate if (apicioadr[0]) 12780Sstevel@tonic-gate psm_unmap_phys((caddr_t)apicioadr[0], APIC_IO_MEMLEN); 12790Sstevel@tonic-gate return (PSM_FAILURE); 12800Sstevel@tonic-gate } 12810Sstevel@tonic-gate 12820Sstevel@tonic-gate /* Parse the entries in MP configuration table and collect info that we need */ 12830Sstevel@tonic-gate static int 12840Sstevel@tonic-gate apic_parse_mpct(caddr_t mpct, int bypass_cpus_and_ioapics) 12850Sstevel@tonic-gate { 12860Sstevel@tonic-gate struct apic_procent *procp; 12870Sstevel@tonic-gate struct apic_bus *busp; 12880Sstevel@tonic-gate struct apic_io_entry *ioapicp; 12890Sstevel@tonic-gate struct apic_io_intr *intrp; 12900Sstevel@tonic-gate volatile int32_t *ioapic; 12910Sstevel@tonic-gate uint_t lid; 12920Sstevel@tonic-gate int id; 12930Sstevel@tonic-gate uchar_t hid; 12940Sstevel@tonic-gate 12950Sstevel@tonic-gate /*LINTED: pointer cast may result in improper alignment */ 12960Sstevel@tonic-gate procp = (struct apic_procent *)(mpct + sizeof (struct apic_mp_cnf_hdr)); 12970Sstevel@tonic-gate 12980Sstevel@tonic-gate /* No need to count cpu entries if we won't use them */ 12990Sstevel@tonic-gate if (!bypass_cpus_and_ioapics) { 13000Sstevel@tonic-gate 13010Sstevel@tonic-gate /* Find max # of CPUS and allocate structure accordingly */ 13020Sstevel@tonic-gate apic_nproc = 0; 1303*2006Sandrei CPUSET_ZERO(apic_cpumask); 13040Sstevel@tonic-gate while (procp->proc_entry == APIC_CPU_ENTRY) { 13050Sstevel@tonic-gate if (procp->proc_cpuflags & CPUFLAGS_EN) { 1306*2006Sandrei if (apic_nproc < NCPU) 1307*2006Sandrei CPUSET_ADD(apic_cpumask, apic_nproc); 13080Sstevel@tonic-gate apic_nproc++; 13090Sstevel@tonic-gate } 13100Sstevel@tonic-gate procp++; 13110Sstevel@tonic-gate } 13120Sstevel@tonic-gate if (apic_nproc > NCPU) 13130Sstevel@tonic-gate cmn_err(CE_WARN, "pcplusmp: exceeded " 13140Sstevel@tonic-gate "maximum no. of CPUs (= %d)", NCPU); 13150Sstevel@tonic-gate if (!apic_nproc || !(apic_cpus = (apic_cpus_info_t *) 13160Sstevel@tonic-gate kmem_zalloc(sizeof (*apic_cpus)*apic_nproc, KM_NOSLEEP))) 13170Sstevel@tonic-gate return (PSM_FAILURE); 13180Sstevel@tonic-gate } 13190Sstevel@tonic-gate 13200Sstevel@tonic-gate /*LINTED: pointer cast may result in improper alignment */ 13210Sstevel@tonic-gate procp = (struct apic_procent *)(mpct + sizeof (struct apic_mp_cnf_hdr)); 13220Sstevel@tonic-gate 13230Sstevel@tonic-gate /* 13240Sstevel@tonic-gate * start with index 1 as 0 needs to be filled in with Boot CPU, but 13250Sstevel@tonic-gate * if we're bypassing this information, it has already been filled 13260Sstevel@tonic-gate * in by acpi_probe(), so don't overwrite it. 13270Sstevel@tonic-gate */ 13280Sstevel@tonic-gate if (!bypass_cpus_and_ioapics) 13290Sstevel@tonic-gate apic_nproc = 1; 13300Sstevel@tonic-gate 13310Sstevel@tonic-gate while (procp->proc_entry == APIC_CPU_ENTRY) { 13320Sstevel@tonic-gate /* check whether the cpu exists or not */ 13330Sstevel@tonic-gate if (!bypass_cpus_and_ioapics && 13340Sstevel@tonic-gate procp->proc_cpuflags & CPUFLAGS_EN) { 13350Sstevel@tonic-gate if (procp->proc_cpuflags & CPUFLAGS_BP) { /* Boot CPU */ 13360Sstevel@tonic-gate lid = apicadr[APIC_LID_REG]; 13370Sstevel@tonic-gate apic_cpus[0].aci_local_id = procp->proc_apicid; 13380Sstevel@tonic-gate if (apic_cpus[0].aci_local_id != 13390Sstevel@tonic-gate (uchar_t)(lid >> APIC_ID_BIT_OFFSET)) { 13400Sstevel@tonic-gate return (PSM_FAILURE); 13410Sstevel@tonic-gate } 13420Sstevel@tonic-gate apic_cpus[0].aci_local_ver = 13430Sstevel@tonic-gate procp->proc_version; 13440Sstevel@tonic-gate } else { 13450Sstevel@tonic-gate 13460Sstevel@tonic-gate apic_cpus[apic_nproc].aci_local_id = 13470Sstevel@tonic-gate procp->proc_apicid; 13480Sstevel@tonic-gate apic_cpus[apic_nproc].aci_local_ver = 13490Sstevel@tonic-gate procp->proc_version; 13500Sstevel@tonic-gate apic_nproc++; 13510Sstevel@tonic-gate 13520Sstevel@tonic-gate } 13530Sstevel@tonic-gate } 13540Sstevel@tonic-gate procp++; 13550Sstevel@tonic-gate } 13560Sstevel@tonic-gate 13570Sstevel@tonic-gate /* 13580Sstevel@tonic-gate * Save start of bus entries for later use. 13590Sstevel@tonic-gate * Get EISA level cntrl if EISA bus is present. 13600Sstevel@tonic-gate * Also get the CPI bus id for single CPI bus case 13610Sstevel@tonic-gate */ 13620Sstevel@tonic-gate apic_busp = busp = (struct apic_bus *)procp; 13630Sstevel@tonic-gate while (busp->bus_entry == APIC_BUS_ENTRY) { 13640Sstevel@tonic-gate lid = apic_find_bus_type((char *)&busp->bus_str1); 13650Sstevel@tonic-gate if (lid == BUS_EISA) { 13660Sstevel@tonic-gate eisa_level_intr_mask = (inb(EISA_LEVEL_CNTL + 1) << 8) | 13670Sstevel@tonic-gate inb(EISA_LEVEL_CNTL) | ((uint_t)INT32_MAX + 1); 13680Sstevel@tonic-gate } else if (lid == BUS_PCI) { 13690Sstevel@tonic-gate /* 13700Sstevel@tonic-gate * apic_single_pci_busid will be used only if 13710Sstevel@tonic-gate * apic_pic_bus_total is equal to 1 13720Sstevel@tonic-gate */ 13730Sstevel@tonic-gate apic_pci_bus_total++; 13740Sstevel@tonic-gate apic_single_pci_busid = busp->bus_id; 13750Sstevel@tonic-gate } 13760Sstevel@tonic-gate busp++; 13770Sstevel@tonic-gate } 13780Sstevel@tonic-gate 13790Sstevel@tonic-gate ioapicp = (struct apic_io_entry *)busp; 13800Sstevel@tonic-gate 13810Sstevel@tonic-gate if (!bypass_cpus_and_ioapics) 13820Sstevel@tonic-gate apic_io_max = 0; 13830Sstevel@tonic-gate do { 13840Sstevel@tonic-gate if (!bypass_cpus_and_ioapics && apic_io_max < MAX_IO_APIC) { 13850Sstevel@tonic-gate if (ioapicp->io_flags & IOAPIC_FLAGS_EN) { 13860Sstevel@tonic-gate apic_io_id[apic_io_max] = ioapicp->io_apicid; 13870Sstevel@tonic-gate apic_io_ver[apic_io_max] = ioapicp->io_version; 13880Sstevel@tonic-gate /*LINTED: pointer cast may result in improper alignment */ 13890Sstevel@tonic-gate apicioadr[apic_io_max] = 13900Sstevel@tonic-gate (int32_t *)psm_map_phys( 13910Sstevel@tonic-gate (uint32_t)ioapicp->io_apic_addr, 13920Sstevel@tonic-gate APIC_IO_MEMLEN, PROT_READ | PROT_WRITE); 13930Sstevel@tonic-gate 13940Sstevel@tonic-gate if (!apicioadr[apic_io_max]) 13950Sstevel@tonic-gate return (PSM_FAILURE); 13960Sstevel@tonic-gate 13970Sstevel@tonic-gate ioapic = apicioadr[apic_io_max]; 13980Sstevel@tonic-gate ioapic[APIC_IO_REG] = APIC_ID_CMD; 13990Sstevel@tonic-gate id = ioapic[APIC_IO_DATA]; 14000Sstevel@tonic-gate hid = (uchar_t)(((uint_t)id) >> 24); 14010Sstevel@tonic-gate 14020Sstevel@tonic-gate if (hid != apic_io_id[apic_io_max]) { 14030Sstevel@tonic-gate if (apic_io_id[apic_io_max] == 0) 14040Sstevel@tonic-gate apic_io_id[apic_io_max] = hid; 14050Sstevel@tonic-gate else { 14060Sstevel@tonic-gate /* 14070Sstevel@tonic-gate * set ioapic id to whatever 14080Sstevel@tonic-gate * reported by MPS 14090Sstevel@tonic-gate * 14100Sstevel@tonic-gate * may not need to set index 14110Sstevel@tonic-gate * again ??? 14120Sstevel@tonic-gate * take it out and try 14130Sstevel@tonic-gate */ 14140Sstevel@tonic-gate 14150Sstevel@tonic-gate id = ((int32_t) 14160Sstevel@tonic-gate apic_io_id[apic_io_max]) << 14170Sstevel@tonic-gate 24; 14180Sstevel@tonic-gate 14190Sstevel@tonic-gate ioapic[APIC_IO_REG] = 14200Sstevel@tonic-gate APIC_ID_CMD; 14210Sstevel@tonic-gate 14220Sstevel@tonic-gate ioapic[APIC_IO_DATA] = id; 14230Sstevel@tonic-gate 14240Sstevel@tonic-gate } 14250Sstevel@tonic-gate } 14260Sstevel@tonic-gate apic_io_max++; 14270Sstevel@tonic-gate } 14280Sstevel@tonic-gate } 14290Sstevel@tonic-gate ioapicp++; 14300Sstevel@tonic-gate } while (ioapicp->io_entry == APIC_IO_ENTRY); 14310Sstevel@tonic-gate 14320Sstevel@tonic-gate apic_io_intrp = (struct apic_io_intr *)ioapicp; 14330Sstevel@tonic-gate 14340Sstevel@tonic-gate intrp = apic_io_intrp; 14350Sstevel@tonic-gate while (intrp->intr_entry == APIC_IO_INTR_ENTRY) { 14360Sstevel@tonic-gate if ((intrp->intr_irq > APIC_MAX_ISA_IRQ) || 14370Sstevel@tonic-gate (apic_find_bus(intrp->intr_busid) == BUS_PCI)) { 14380Sstevel@tonic-gate apic_irq_translate = 1; 14390Sstevel@tonic-gate break; 14400Sstevel@tonic-gate } 14410Sstevel@tonic-gate intrp++; 14420Sstevel@tonic-gate } 14430Sstevel@tonic-gate 14440Sstevel@tonic-gate return (PSM_SUCCESS); 14450Sstevel@tonic-gate } 14460Sstevel@tonic-gate 1447916Sschwartz boolean_t 1448916Sschwartz apic_cpu_in_range(int cpu) 1449916Sschwartz { 1450916Sschwartz return ((cpu & ~IRQ_USER_BOUND) < apic_nproc); 1451916Sschwartz } 1452916Sschwartz 14530Sstevel@tonic-gate static struct apic_mpfps_hdr * 14540Sstevel@tonic-gate apic_find_fps_sig(caddr_t cptr, int len) 14550Sstevel@tonic-gate { 14560Sstevel@tonic-gate int i; 14570Sstevel@tonic-gate 14580Sstevel@tonic-gate /* Look for the pattern "_MP_" */ 14590Sstevel@tonic-gate for (i = 0; i < len; i += 16) { 14600Sstevel@tonic-gate if ((*(cptr+i) == '_') && 14610Sstevel@tonic-gate (*(cptr+i+1) == 'M') && 14620Sstevel@tonic-gate (*(cptr+i+2) == 'P') && 14630Sstevel@tonic-gate (*(cptr+i+3) == '_')) 14640Sstevel@tonic-gate /*LINTED: pointer cast may result in improper alignment */ 14650Sstevel@tonic-gate return ((struct apic_mpfps_hdr *)(cptr + i)); 14660Sstevel@tonic-gate } 14670Sstevel@tonic-gate return (NULL); 14680Sstevel@tonic-gate } 14690Sstevel@tonic-gate 14700Sstevel@tonic-gate static int 14710Sstevel@tonic-gate apic_checksum(caddr_t bptr, int len) 14720Sstevel@tonic-gate { 14730Sstevel@tonic-gate int i; 14740Sstevel@tonic-gate uchar_t cksum; 14750Sstevel@tonic-gate 14760Sstevel@tonic-gate cksum = 0; 14770Sstevel@tonic-gate for (i = 0; i < len; i++) 14780Sstevel@tonic-gate cksum += *bptr++; 14790Sstevel@tonic-gate return ((int)cksum); 14800Sstevel@tonic-gate } 14810Sstevel@tonic-gate 14820Sstevel@tonic-gate 14830Sstevel@tonic-gate /* 14840Sstevel@tonic-gate * Initialise vector->ipl and ipl->pri arrays. level_intr and irqtable 14850Sstevel@tonic-gate * are also set to NULL. vector->irq is set to a value which cannot map 14860Sstevel@tonic-gate * to a real irq to show that it is free. 14870Sstevel@tonic-gate */ 14880Sstevel@tonic-gate void 14890Sstevel@tonic-gate apic_init() 14900Sstevel@tonic-gate { 14910Sstevel@tonic-gate int i; 14920Sstevel@tonic-gate int *iptr; 14930Sstevel@tonic-gate 14940Sstevel@tonic-gate int j = 1; 14950Sstevel@tonic-gate apic_ipltopri[0] = APIC_VECTOR_PER_IPL; /* leave 0 for idle */ 14960Sstevel@tonic-gate for (i = 0; i < (APIC_AVAIL_VECTOR / APIC_VECTOR_PER_IPL); i++) { 14970Sstevel@tonic-gate if ((i < ((APIC_AVAIL_VECTOR / APIC_VECTOR_PER_IPL) - 1)) && 14980Sstevel@tonic-gate (apic_vectortoipl[i + 1] == apic_vectortoipl[i])) 14990Sstevel@tonic-gate /* get to highest vector at the same ipl */ 15000Sstevel@tonic-gate continue; 15010Sstevel@tonic-gate for (; j <= apic_vectortoipl[i]; j++) { 15020Sstevel@tonic-gate apic_ipltopri[j] = (i << APIC_IPL_SHIFT) + 15030Sstevel@tonic-gate APIC_BASE_VECT; 15040Sstevel@tonic-gate } 15050Sstevel@tonic-gate } 15060Sstevel@tonic-gate for (; j < MAXIPL + 1; j++) 15070Sstevel@tonic-gate /* fill up any empty ipltopri slots */ 15080Sstevel@tonic-gate apic_ipltopri[j] = (i << APIC_IPL_SHIFT) + APIC_BASE_VECT; 15090Sstevel@tonic-gate 15100Sstevel@tonic-gate /* cpu 0 is always up */ 15110Sstevel@tonic-gate apic_cpus[0].aci_status = APIC_CPU_ONLINE | APIC_CPU_INTR_ENABLE; 15120Sstevel@tonic-gate 15130Sstevel@tonic-gate iptr = (int *)&apic_irq_table[0]; 15140Sstevel@tonic-gate for (i = 0; i <= APIC_MAX_VECTOR; i++) { 15150Sstevel@tonic-gate apic_level_intr[i] = 0; 15160Sstevel@tonic-gate *iptr++ = NULL; 15170Sstevel@tonic-gate apic_vector_to_irq[i] = APIC_RESV_IRQ; 15180Sstevel@tonic-gate apic_reprogram_info[i].valid = 0; 15190Sstevel@tonic-gate apic_reprogram_info[i].bindcpu = 0; 15200Sstevel@tonic-gate apic_reprogram_info[i].timeouts = 0; 15210Sstevel@tonic-gate } 15220Sstevel@tonic-gate 15230Sstevel@tonic-gate /* 15240Sstevel@tonic-gate * Allocate a dummy irq table entry for the reserved entry. 15250Sstevel@tonic-gate * This takes care of the race between removing an irq and 15260Sstevel@tonic-gate * clock detecting a CPU in that irq during interrupt load 15270Sstevel@tonic-gate * sampling. 15280Sstevel@tonic-gate */ 15290Sstevel@tonic-gate apic_irq_table[APIC_RESV_IRQ] = 15300Sstevel@tonic-gate kmem_zalloc(sizeof (apic_irq_t), KM_NOSLEEP); 15310Sstevel@tonic-gate 15320Sstevel@tonic-gate mutex_init(&airq_mutex, NULL, MUTEX_DEFAULT, NULL); 15330Sstevel@tonic-gate mutex_init(&apic_reprogram_timeout_mutex, NULL, MUTEX_DEFAULT, NULL); 15340Sstevel@tonic-gate #if defined(__amd64) 15350Sstevel@tonic-gate /* 15360Sstevel@tonic-gate * Make cpu-specific interrupt info point to cr8pri vector 15370Sstevel@tonic-gate */ 15380Sstevel@tonic-gate for (i = 0; i <= MAXIPL; i++) 15390Sstevel@tonic-gate apic_cr8pri[i] = apic_ipltopri[i] >> APIC_IPL_SHIFT; 15400Sstevel@tonic-gate CPU->cpu_pri_data = apic_cr8pri; 15410Sstevel@tonic-gate intpri_use_cr8 = 1; 15420Sstevel@tonic-gate #endif /* __amd64 */ 15430Sstevel@tonic-gate } 15440Sstevel@tonic-gate 15450Sstevel@tonic-gate /* 15460Sstevel@tonic-gate * handler for APIC Error interrupt. Just print a warning and continue 15470Sstevel@tonic-gate */ 15480Sstevel@tonic-gate static int 15490Sstevel@tonic-gate apic_error_intr() 15500Sstevel@tonic-gate { 15510Sstevel@tonic-gate uint_t error0, error1, error; 15520Sstevel@tonic-gate uint_t i; 15530Sstevel@tonic-gate 15540Sstevel@tonic-gate /* 15550Sstevel@tonic-gate * We need to write before read as per 7.4.17 of system prog manual. 15560Sstevel@tonic-gate * We do both and or the results to be safe 15570Sstevel@tonic-gate */ 15580Sstevel@tonic-gate error0 = apicadr[APIC_ERROR_STATUS]; 15590Sstevel@tonic-gate apicadr[APIC_ERROR_STATUS] = 0; 15600Sstevel@tonic-gate error1 = apicadr[APIC_ERROR_STATUS]; 15610Sstevel@tonic-gate error = error0 | error1; 15620Sstevel@tonic-gate 15630Sstevel@tonic-gate /* 1564846Ssethg * Clear the APIC error status (do this on all cpus that enter here) 1565846Ssethg * (two writes are required due to the semantics of accessing the 1566846Ssethg * error status register.) 1567846Ssethg */ 1568846Ssethg apicadr[APIC_ERROR_STATUS] = 0; 1569846Ssethg apicadr[APIC_ERROR_STATUS] = 0; 1570846Ssethg 1571846Ssethg /* 15720Sstevel@tonic-gate * Prevent more than 1 CPU from handling error interrupt causing 15730Sstevel@tonic-gate * double printing (interleave of characters from multiple 15740Sstevel@tonic-gate * CPU's when using prom_printf) 15750Sstevel@tonic-gate */ 15760Sstevel@tonic-gate if (lock_try(&apic_error_lock) == 0) 15770Sstevel@tonic-gate return (error ? DDI_INTR_CLAIMED : DDI_INTR_UNCLAIMED); 15780Sstevel@tonic-gate if (error) { 15790Sstevel@tonic-gate #if DEBUG 15800Sstevel@tonic-gate if (apic_debug) 15810Sstevel@tonic-gate debug_enter("pcplusmp: APIC Error interrupt received"); 15820Sstevel@tonic-gate #endif /* DEBUG */ 15830Sstevel@tonic-gate if (apic_panic_on_apic_error) 15840Sstevel@tonic-gate cmn_err(CE_PANIC, 15850Sstevel@tonic-gate "APIC Error interrupt on CPU %d. Status = %x\n", 15860Sstevel@tonic-gate psm_get_cpu_id(), error); 15870Sstevel@tonic-gate else { 15880Sstevel@tonic-gate if ((error & ~APIC_CS_ERRORS) == 0) { 15890Sstevel@tonic-gate /* cksum error only */ 15900Sstevel@tonic-gate apic_error |= APIC_ERR_APIC_ERROR; 15910Sstevel@tonic-gate apic_apic_error |= error; 15920Sstevel@tonic-gate apic_num_apic_errors++; 15930Sstevel@tonic-gate apic_num_cksum_errors++; 15940Sstevel@tonic-gate } else { 15950Sstevel@tonic-gate /* 15960Sstevel@tonic-gate * prom_printf is the best shot we have of 15970Sstevel@tonic-gate * something which is problem free from 15980Sstevel@tonic-gate * high level/NMI type of interrupts 15990Sstevel@tonic-gate */ 16000Sstevel@tonic-gate prom_printf("APIC Error interrupt on CPU %d. " 16010Sstevel@tonic-gate "Status 0 = %x, Status 1 = %x\n", 16020Sstevel@tonic-gate psm_get_cpu_id(), error0, error1); 16030Sstevel@tonic-gate apic_error |= APIC_ERR_APIC_ERROR; 16040Sstevel@tonic-gate apic_apic_error |= error; 16050Sstevel@tonic-gate apic_num_apic_errors++; 16060Sstevel@tonic-gate for (i = 0; i < apic_error_display_delay; i++) { 16070Sstevel@tonic-gate tenmicrosec(); 16080Sstevel@tonic-gate } 16090Sstevel@tonic-gate /* 16100Sstevel@tonic-gate * provide more delay next time limited to 16110Sstevel@tonic-gate * roughly 1 clock tick time 16120Sstevel@tonic-gate */ 16130Sstevel@tonic-gate if (apic_error_display_delay < 500) 16140Sstevel@tonic-gate apic_error_display_delay *= 2; 16150Sstevel@tonic-gate } 16160Sstevel@tonic-gate } 16170Sstevel@tonic-gate lock_clear(&apic_error_lock); 16180Sstevel@tonic-gate return (DDI_INTR_CLAIMED); 16190Sstevel@tonic-gate } else { 16200Sstevel@tonic-gate lock_clear(&apic_error_lock); 16210Sstevel@tonic-gate return (DDI_INTR_UNCLAIMED); 16220Sstevel@tonic-gate } 16230Sstevel@tonic-gate /* NOTREACHED */ 16240Sstevel@tonic-gate } 16250Sstevel@tonic-gate 16260Sstevel@tonic-gate /* 16270Sstevel@tonic-gate * Turn off the mask bit in the performance counter Local Vector Table entry. 16280Sstevel@tonic-gate */ 16290Sstevel@tonic-gate static void 16300Sstevel@tonic-gate apic_cpcovf_mask_clear(void) 16310Sstevel@tonic-gate { 16320Sstevel@tonic-gate apicadr[APIC_PCINT_VECT] &= ~APIC_LVT_MASK; 16330Sstevel@tonic-gate } 16340Sstevel@tonic-gate 16350Sstevel@tonic-gate static void 16360Sstevel@tonic-gate apic_init_intr() 16370Sstevel@tonic-gate { 16380Sstevel@tonic-gate processorid_t cpun = psm_get_cpu_id(); 16390Sstevel@tonic-gate 16400Sstevel@tonic-gate #if defined(__amd64) 16410Sstevel@tonic-gate setcr8((ulong_t)(APIC_MASK_ALL >> APIC_IPL_SHIFT)); 16420Sstevel@tonic-gate #else 16430Sstevel@tonic-gate apicadr[APIC_TASK_REG] = APIC_MASK_ALL; 16440Sstevel@tonic-gate #endif 16450Sstevel@tonic-gate 16460Sstevel@tonic-gate if (apic_flat_model) 16470Sstevel@tonic-gate apicadr[APIC_FORMAT_REG] = APIC_FLAT_MODEL; 16480Sstevel@tonic-gate else 16490Sstevel@tonic-gate apicadr[APIC_FORMAT_REG] = APIC_CLUSTER_MODEL; 16500Sstevel@tonic-gate apicadr[APIC_DEST_REG] = AV_HIGH_ORDER >> cpun; 16510Sstevel@tonic-gate 16520Sstevel@tonic-gate /* need to enable APIC before unmasking NMI */ 16530Sstevel@tonic-gate apicadr[APIC_SPUR_INT_REG] = AV_UNIT_ENABLE | APIC_SPUR_INTR; 16540Sstevel@tonic-gate 16550Sstevel@tonic-gate apicadr[APIC_LOCAL_TIMER] = AV_MASK; 16560Sstevel@tonic-gate apicadr[APIC_INT_VECT0] = AV_MASK; /* local intr reg 0 */ 16570Sstevel@tonic-gate apicadr[APIC_INT_VECT1] = AV_NMI; /* enable NMI */ 16580Sstevel@tonic-gate 16590Sstevel@tonic-gate if (apic_cpus[cpun].aci_local_ver < APIC_INTEGRATED_VERS) 16600Sstevel@tonic-gate return; 16610Sstevel@tonic-gate 16620Sstevel@tonic-gate /* Enable performance counter overflow interrupt */ 16630Sstevel@tonic-gate 16640Sstevel@tonic-gate if ((x86_feature & X86_MSR) != X86_MSR) 16650Sstevel@tonic-gate apic_enable_cpcovf_intr = 0; 16660Sstevel@tonic-gate if (apic_enable_cpcovf_intr) { 16670Sstevel@tonic-gate if (apic_cpcovf_vect == 0) { 16680Sstevel@tonic-gate int ipl = APIC_PCINT_IPL; 16690Sstevel@tonic-gate int irq = apic_get_ipivect(ipl, -1); 16700Sstevel@tonic-gate 16710Sstevel@tonic-gate ASSERT(irq != -1); 16720Sstevel@tonic-gate apic_cpcovf_vect = apic_irq_table[irq]->airq_vector; 16730Sstevel@tonic-gate ASSERT(apic_cpcovf_vect); 16740Sstevel@tonic-gate (void) add_avintr(NULL, ipl, 16750Sstevel@tonic-gate (avfunc)kcpc_hw_overflow_intr, 1676916Sschwartz "apic pcint", irq, NULL, NULL, NULL, NULL); 16770Sstevel@tonic-gate kcpc_hw_overflow_intr_installed = 1; 16780Sstevel@tonic-gate kcpc_hw_enable_cpc_intr = apic_cpcovf_mask_clear; 16790Sstevel@tonic-gate } 16800Sstevel@tonic-gate apicadr[APIC_PCINT_VECT] = apic_cpcovf_vect; 16810Sstevel@tonic-gate } 16820Sstevel@tonic-gate 16830Sstevel@tonic-gate /* Enable error interrupt */ 16840Sstevel@tonic-gate 16850Sstevel@tonic-gate if (apic_enable_error_intr) { 16860Sstevel@tonic-gate if (apic_errvect == 0) { 16870Sstevel@tonic-gate int ipl = 0xf; /* get highest priority intr */ 16880Sstevel@tonic-gate int irq = apic_get_ipivect(ipl, -1); 16890Sstevel@tonic-gate 16900Sstevel@tonic-gate ASSERT(irq != -1); 16910Sstevel@tonic-gate apic_errvect = apic_irq_table[irq]->airq_vector; 16920Sstevel@tonic-gate ASSERT(apic_errvect); 16930Sstevel@tonic-gate /* 16940Sstevel@tonic-gate * Not PSMI compliant, but we are going to merge 16950Sstevel@tonic-gate * with ON anyway 16960Sstevel@tonic-gate */ 16970Sstevel@tonic-gate (void) add_avintr((void *)NULL, ipl, 16980Sstevel@tonic-gate (avfunc)apic_error_intr, "apic error intr", 1699916Sschwartz irq, NULL, NULL, NULL, NULL); 17000Sstevel@tonic-gate } 17010Sstevel@tonic-gate apicadr[APIC_ERR_VECT] = apic_errvect; 17020Sstevel@tonic-gate apicadr[APIC_ERROR_STATUS] = 0; 17030Sstevel@tonic-gate apicadr[APIC_ERROR_STATUS] = 0; 17040Sstevel@tonic-gate } 17050Sstevel@tonic-gate } 17060Sstevel@tonic-gate 17070Sstevel@tonic-gate static void 17080Sstevel@tonic-gate apic_disable_local_apic() 17090Sstevel@tonic-gate { 17100Sstevel@tonic-gate apicadr[APIC_TASK_REG] = APIC_MASK_ALL; 17110Sstevel@tonic-gate apicadr[APIC_LOCAL_TIMER] = AV_MASK; 17120Sstevel@tonic-gate apicadr[APIC_INT_VECT0] = AV_MASK; /* local intr reg 0 */ 17130Sstevel@tonic-gate apicadr[APIC_INT_VECT1] = AV_MASK; /* disable NMI */ 17140Sstevel@tonic-gate apicadr[APIC_ERR_VECT] = AV_MASK; /* and error interrupt */ 17150Sstevel@tonic-gate apicadr[APIC_PCINT_VECT] = AV_MASK; /* and perf counter intr */ 17160Sstevel@tonic-gate apicadr[APIC_SPUR_INT_REG] = APIC_SPUR_INTR; 17170Sstevel@tonic-gate } 17180Sstevel@tonic-gate 17190Sstevel@tonic-gate static void 17200Sstevel@tonic-gate apic_picinit(void) 17210Sstevel@tonic-gate { 17220Sstevel@tonic-gate int i, j; 17230Sstevel@tonic-gate uint_t isr; 17240Sstevel@tonic-gate volatile int32_t *ioapic; 17250Sstevel@tonic-gate apic_irq_t *irqptr; 1726347Smyers struct intrspec ispec; 17270Sstevel@tonic-gate 17280Sstevel@tonic-gate /* 17290Sstevel@tonic-gate * On UniSys Model 6520, the BIOS leaves vector 0x20 isr 17300Sstevel@tonic-gate * bit on without clearing it with EOI. Since softint 17310Sstevel@tonic-gate * uses vector 0x20 to interrupt itself, so softint will 17320Sstevel@tonic-gate * not work on this machine. In order to fix this problem 17330Sstevel@tonic-gate * a check is made to verify all the isr bits are clear. 17340Sstevel@tonic-gate * If not, EOIs are issued to clear the bits. 17350Sstevel@tonic-gate */ 17360Sstevel@tonic-gate for (i = 7; i >= 1; i--) { 17370Sstevel@tonic-gate if ((isr = apicadr[APIC_ISR_REG + (i * 4)]) != 0) 17380Sstevel@tonic-gate for (j = 0; ((j < 32) && (isr != 0)); j++) 17390Sstevel@tonic-gate if (isr & (1 << j)) { 17400Sstevel@tonic-gate apicadr[APIC_EOI_REG] = 0; 17410Sstevel@tonic-gate isr &= ~(1 << j); 17420Sstevel@tonic-gate apic_error |= APIC_ERR_BOOT_EOI; 17430Sstevel@tonic-gate } 17440Sstevel@tonic-gate } 17450Sstevel@tonic-gate 17460Sstevel@tonic-gate /* set a flag so we know we have run apic_picinit() */ 17470Sstevel@tonic-gate apic_flag = 1; 17480Sstevel@tonic-gate LOCK_INIT_CLEAR(&apic_gethrtime_lock); 17490Sstevel@tonic-gate LOCK_INIT_CLEAR(&apic_ioapic_lock); 17500Sstevel@tonic-gate LOCK_INIT_CLEAR(&apic_revector_lock); 17510Sstevel@tonic-gate LOCK_INIT_CLEAR(&apic_ioapic_reprogram_lock); 17520Sstevel@tonic-gate LOCK_INIT_CLEAR(&apic_error_lock); 17530Sstevel@tonic-gate 17540Sstevel@tonic-gate picsetup(); /* initialise the 8259 */ 17550Sstevel@tonic-gate 17560Sstevel@tonic-gate /* add nmi handler - least priority nmi handler */ 17570Sstevel@tonic-gate LOCK_INIT_CLEAR(&apic_nmi_lock); 17580Sstevel@tonic-gate 17590Sstevel@tonic-gate if (!psm_add_nmintr(0, (avfunc) apic_nmi_intr, 17600Sstevel@tonic-gate "pcplusmp NMI handler", (caddr_t)NULL)) 17610Sstevel@tonic-gate cmn_err(CE_WARN, "pcplusmp: Unable to add nmi handler"); 17620Sstevel@tonic-gate 17630Sstevel@tonic-gate apic_init_intr(); 17640Sstevel@tonic-gate 17650Sstevel@tonic-gate /* enable apic mode if imcr present */ 17660Sstevel@tonic-gate if (apic_imcrp) { 17670Sstevel@tonic-gate outb(APIC_IMCR_P1, (uchar_t)APIC_IMCR_SELECT); 17680Sstevel@tonic-gate outb(APIC_IMCR_P2, (uchar_t)APIC_IMCR_APIC); 17690Sstevel@tonic-gate } 17700Sstevel@tonic-gate 17710Sstevel@tonic-gate /* mask interrupt vectors */ 17720Sstevel@tonic-gate for (j = 0; j < apic_io_max; j++) { 17730Sstevel@tonic-gate int intin_max; 17740Sstevel@tonic-gate ioapic = apicioadr[j]; 17750Sstevel@tonic-gate ioapic[APIC_IO_REG] = APIC_VERS_CMD; 17760Sstevel@tonic-gate /* Bits 23-16 define the maximum redirection entries */ 17770Sstevel@tonic-gate intin_max = (ioapic[APIC_IO_DATA] >> 16) & 0xff; 17780Sstevel@tonic-gate for (i = 0; i < intin_max; i++) { 17790Sstevel@tonic-gate ioapic[APIC_IO_REG] = APIC_RDT_CMD + 2 * i; 17800Sstevel@tonic-gate ioapic[APIC_IO_DATA] = AV_MASK; 17810Sstevel@tonic-gate } 17820Sstevel@tonic-gate } 17830Sstevel@tonic-gate 17840Sstevel@tonic-gate /* 17850Sstevel@tonic-gate * Hack alert: deal with ACPI SCI interrupt chicken/egg here 17860Sstevel@tonic-gate */ 1787347Smyers if (apic_sci_vect > 0) { 17880Sstevel@tonic-gate /* 17890Sstevel@tonic-gate * acpica has already done add_avintr(); we just 17900Sstevel@tonic-gate * to finish the job by mimicing translate_irq() 1791347Smyers * 1792347Smyers * Fake up an intrspec and setup the tables 17930Sstevel@tonic-gate */ 1794347Smyers ispec.intrspec_vec = apic_sci_vect; 1795347Smyers ispec.intrspec_pri = SCI_IPL; 1796347Smyers 1797347Smyers if (apic_setup_irq_table(NULL, apic_sci_vect, NULL, 1798347Smyers &ispec, &apic_sci_flags, DDI_INTR_TYPE_FIXED) < 0) { 17990Sstevel@tonic-gate cmn_err(CE_WARN, "!apic: SCI setup failed"); 18000Sstevel@tonic-gate return; 18010Sstevel@tonic-gate } 18020Sstevel@tonic-gate irqptr = apic_irq_table[apic_sci_vect]; 18030Sstevel@tonic-gate 18040Sstevel@tonic-gate /* Program I/O APIC */ 18050Sstevel@tonic-gate (void) apic_setup_io_intr(irqptr, apic_sci_vect); 1806916Sschwartz 1807916Sschwartz irqptr->airq_share++; 18080Sstevel@tonic-gate } 18090Sstevel@tonic-gate } 18100Sstevel@tonic-gate 18110Sstevel@tonic-gate 18120Sstevel@tonic-gate static void 18130Sstevel@tonic-gate apic_cpu_start(processorid_t cpun, caddr_t rm_code) 18140Sstevel@tonic-gate { 18150Sstevel@tonic-gate int loop_count; 18160Sstevel@tonic-gate uint32_t vector; 18170Sstevel@tonic-gate uint_t cpu_id, iflag; 18180Sstevel@tonic-gate 18190Sstevel@tonic-gate cpu_id = apic_cpus[cpun].aci_local_id; 18200Sstevel@tonic-gate 18210Sstevel@tonic-gate apic_cmos_ssb_set = 1; 18220Sstevel@tonic-gate 18230Sstevel@tonic-gate /* 18240Sstevel@tonic-gate * Interrupts on BSP cpu will be disabled during these startup 18250Sstevel@tonic-gate * steps in order to avoid unwanted side effects from 18260Sstevel@tonic-gate * executing interrupt handlers on a problematic BIOS. 18270Sstevel@tonic-gate */ 18280Sstevel@tonic-gate 18290Sstevel@tonic-gate iflag = intr_clear(); 18300Sstevel@tonic-gate outb(CMOS_ADDR, SSB); 18310Sstevel@tonic-gate outb(CMOS_DATA, BIOS_SHUTDOWN); 18320Sstevel@tonic-gate 18330Sstevel@tonic-gate while (get_apic_cmd1() & AV_PENDING) 18340Sstevel@tonic-gate apic_ret(); 18350Sstevel@tonic-gate 18360Sstevel@tonic-gate /* for integrated - make sure there is one INIT IPI in buffer */ 18370Sstevel@tonic-gate /* for external - it will wake up the cpu */ 18380Sstevel@tonic-gate apicadr[APIC_INT_CMD2] = cpu_id << APIC_ICR_ID_BIT_OFFSET; 18390Sstevel@tonic-gate apicadr[APIC_INT_CMD1] = AV_ASSERT | AV_RESET; 18400Sstevel@tonic-gate 18410Sstevel@tonic-gate /* If only 1 CPU is installed, PENDING bit will not go low */ 18420Sstevel@tonic-gate for (loop_count = 0x1000; loop_count; loop_count--) 18430Sstevel@tonic-gate if (get_apic_cmd1() & AV_PENDING) 18440Sstevel@tonic-gate apic_ret(); 18450Sstevel@tonic-gate else 18460Sstevel@tonic-gate break; 18470Sstevel@tonic-gate 18480Sstevel@tonic-gate apicadr[APIC_INT_CMD2] = cpu_id << APIC_ICR_ID_BIT_OFFSET; 18490Sstevel@tonic-gate apicadr[APIC_INT_CMD1] = AV_DEASSERT | AV_RESET; 18500Sstevel@tonic-gate 18510Sstevel@tonic-gate drv_usecwait(20000); /* 20 milli sec */ 18520Sstevel@tonic-gate 18530Sstevel@tonic-gate if (apic_cpus[cpun].aci_local_ver >= APIC_INTEGRATED_VERS) { 18540Sstevel@tonic-gate /* integrated apic */ 18550Sstevel@tonic-gate 18560Sstevel@tonic-gate rm_code = (caddr_t)(uintptr_t)rm_platter_pa; 18570Sstevel@tonic-gate vector = (rm_platter_pa >> MMU_PAGESHIFT) & 18580Sstevel@tonic-gate (APIC_VECTOR_MASK | APIC_IPL_MASK); 18590Sstevel@tonic-gate 18600Sstevel@tonic-gate /* to offset the INIT IPI queue up in the buffer */ 18610Sstevel@tonic-gate apicadr[APIC_INT_CMD2] = cpu_id << APIC_ICR_ID_BIT_OFFSET; 18620Sstevel@tonic-gate apicadr[APIC_INT_CMD1] = vector | AV_STARTUP; 18630Sstevel@tonic-gate 18640Sstevel@tonic-gate drv_usecwait(200); /* 20 micro sec */ 18650Sstevel@tonic-gate 18660Sstevel@tonic-gate apicadr[APIC_INT_CMD2] = cpu_id << APIC_ICR_ID_BIT_OFFSET; 18670Sstevel@tonic-gate apicadr[APIC_INT_CMD1] = vector | AV_STARTUP; 18680Sstevel@tonic-gate 18690Sstevel@tonic-gate drv_usecwait(200); /* 20 micro sec */ 18700Sstevel@tonic-gate } 18710Sstevel@tonic-gate intr_restore(iflag); 18720Sstevel@tonic-gate } 18730Sstevel@tonic-gate 18740Sstevel@tonic-gate 18750Sstevel@tonic-gate #ifdef DEBUG 18760Sstevel@tonic-gate int apic_break_on_cpu = 9; 18770Sstevel@tonic-gate int apic_stretch_interrupts = 0; 18780Sstevel@tonic-gate int apic_stretch_ISR = 1 << 3; /* IPL of 3 matches nothing now */ 18790Sstevel@tonic-gate 18800Sstevel@tonic-gate void 18810Sstevel@tonic-gate apic_break() 18820Sstevel@tonic-gate { 18830Sstevel@tonic-gate } 18840Sstevel@tonic-gate #endif /* DEBUG */ 18850Sstevel@tonic-gate 18860Sstevel@tonic-gate /* 18870Sstevel@tonic-gate * platform_intr_enter 18880Sstevel@tonic-gate * 18890Sstevel@tonic-gate * Called at the beginning of the interrupt service routine to 18900Sstevel@tonic-gate * mask all level equal to and below the interrupt priority 18910Sstevel@tonic-gate * of the interrupting vector. An EOI should be given to 18920Sstevel@tonic-gate * the interrupt controller to enable other HW interrupts. 18930Sstevel@tonic-gate * 18940Sstevel@tonic-gate * Return -1 for spurious interrupts 18950Sstevel@tonic-gate * 18960Sstevel@tonic-gate */ 18970Sstevel@tonic-gate /*ARGSUSED*/ 18980Sstevel@tonic-gate static int 18990Sstevel@tonic-gate apic_intr_enter(int ipl, int *vectorp) 19000Sstevel@tonic-gate { 19010Sstevel@tonic-gate uchar_t vector; 19020Sstevel@tonic-gate int nipl; 19030Sstevel@tonic-gate int irq, iflag; 19040Sstevel@tonic-gate apic_cpus_info_t *cpu_infop; 19050Sstevel@tonic-gate 19060Sstevel@tonic-gate /* 19070Sstevel@tonic-gate * The real vector programmed in APIC is *vectorp + 0x20 19080Sstevel@tonic-gate * But, cmnint code subtracts 0x20 before pushing it. 19090Sstevel@tonic-gate * Hence APIC_BASE_VECT is 0x20. 19100Sstevel@tonic-gate */ 19110Sstevel@tonic-gate 19120Sstevel@tonic-gate vector = (uchar_t)*vectorp; 19130Sstevel@tonic-gate 19140Sstevel@tonic-gate /* if interrupted by the clock, increment apic_nsec_since_boot */ 19150Sstevel@tonic-gate if (vector == apic_clkvect) { 19160Sstevel@tonic-gate if (!apic_oneshot) { 19170Sstevel@tonic-gate /* NOTE: this is not MT aware */ 19180Sstevel@tonic-gate apic_hrtime_stamp++; 19190Sstevel@tonic-gate apic_nsec_since_boot += apic_nsec_per_intr; 19200Sstevel@tonic-gate apic_hrtime_stamp++; 19210Sstevel@tonic-gate last_count_read = apic_hertz_count; 19220Sstevel@tonic-gate apic_redistribute_compute(); 19230Sstevel@tonic-gate } 19240Sstevel@tonic-gate 19250Sstevel@tonic-gate /* We will avoid all the book keeping overhead for clock */ 19260Sstevel@tonic-gate nipl = apic_vectortoipl[vector >> APIC_IPL_SHIFT]; 19270Sstevel@tonic-gate #if defined(__amd64) 19280Sstevel@tonic-gate setcr8((ulong_t)apic_cr8pri[nipl]); 19290Sstevel@tonic-gate #else 19300Sstevel@tonic-gate apicadr[APIC_TASK_REG] = apic_ipltopri[nipl]; 19310Sstevel@tonic-gate #endif 19320Sstevel@tonic-gate *vectorp = apic_vector_to_irq[vector + APIC_BASE_VECT]; 19330Sstevel@tonic-gate apicadr[APIC_EOI_REG] = 0; 19340Sstevel@tonic-gate return (nipl); 19350Sstevel@tonic-gate } 19360Sstevel@tonic-gate 19370Sstevel@tonic-gate cpu_infop = &apic_cpus[psm_get_cpu_id()]; 19380Sstevel@tonic-gate 19390Sstevel@tonic-gate if (vector == (APIC_SPUR_INTR - APIC_BASE_VECT)) { 19400Sstevel@tonic-gate cpu_infop->aci_spur_cnt++; 19410Sstevel@tonic-gate return (APIC_INT_SPURIOUS); 19420Sstevel@tonic-gate } 19430Sstevel@tonic-gate 19440Sstevel@tonic-gate /* Check if the vector we got is really what we need */ 19450Sstevel@tonic-gate if (apic_revector_pending) { 19460Sstevel@tonic-gate /* 19470Sstevel@tonic-gate * Disable interrupts for the duration of 19480Sstevel@tonic-gate * the vector translation to prevent a self-race for 19490Sstevel@tonic-gate * the apic_revector_lock. This cannot be done 19500Sstevel@tonic-gate * in apic_xlate_vector because it is recursive and 19510Sstevel@tonic-gate * we want the vector translation to be atomic with 19520Sstevel@tonic-gate * respect to other (higher-priority) interrupts. 19530Sstevel@tonic-gate */ 19540Sstevel@tonic-gate iflag = intr_clear(); 19550Sstevel@tonic-gate vector = apic_xlate_vector(vector + APIC_BASE_VECT) - 19560Sstevel@tonic-gate APIC_BASE_VECT; 19570Sstevel@tonic-gate intr_restore(iflag); 19580Sstevel@tonic-gate } 19590Sstevel@tonic-gate 19600Sstevel@tonic-gate nipl = apic_vectortoipl[vector >> APIC_IPL_SHIFT]; 19610Sstevel@tonic-gate *vectorp = irq = apic_vector_to_irq[vector + APIC_BASE_VECT]; 19620Sstevel@tonic-gate 19630Sstevel@tonic-gate #if defined(__amd64) 19640Sstevel@tonic-gate setcr8((ulong_t)apic_cr8pri[nipl]); 19650Sstevel@tonic-gate #else 19660Sstevel@tonic-gate apicadr[APIC_TASK_REG] = apic_ipltopri[nipl]; 19670Sstevel@tonic-gate #endif 19680Sstevel@tonic-gate 19690Sstevel@tonic-gate cpu_infop->aci_current[nipl] = (uchar_t)irq; 19700Sstevel@tonic-gate cpu_infop->aci_curipl = (uchar_t)nipl; 19710Sstevel@tonic-gate cpu_infop->aci_ISR_in_progress |= 1 << nipl; 19720Sstevel@tonic-gate 19730Sstevel@tonic-gate /* 19740Sstevel@tonic-gate * apic_level_intr could have been assimilated into the irq struct. 19750Sstevel@tonic-gate * but, having it as a character array is more efficient in terms of 19760Sstevel@tonic-gate * cache usage. So, we leave it as is. 19770Sstevel@tonic-gate */ 19780Sstevel@tonic-gate if (!apic_level_intr[irq]) 19790Sstevel@tonic-gate apicadr[APIC_EOI_REG] = 0; 19800Sstevel@tonic-gate 19810Sstevel@tonic-gate #ifdef DEBUG 19820Sstevel@tonic-gate APIC_DEBUG_BUF_PUT(vector); 19830Sstevel@tonic-gate APIC_DEBUG_BUF_PUT(irq); 19840Sstevel@tonic-gate APIC_DEBUG_BUF_PUT(nipl); 19850Sstevel@tonic-gate APIC_DEBUG_BUF_PUT(psm_get_cpu_id()); 19860Sstevel@tonic-gate if ((apic_stretch_interrupts) && (apic_stretch_ISR & (1 << nipl))) 19870Sstevel@tonic-gate drv_usecwait(apic_stretch_interrupts); 19880Sstevel@tonic-gate 19890Sstevel@tonic-gate if (apic_break_on_cpu == psm_get_cpu_id()) 19900Sstevel@tonic-gate apic_break(); 19910Sstevel@tonic-gate #endif /* DEBUG */ 19920Sstevel@tonic-gate return (nipl); 19930Sstevel@tonic-gate } 19940Sstevel@tonic-gate 19950Sstevel@tonic-gate static void 19960Sstevel@tonic-gate apic_intr_exit(int prev_ipl, int irq) 19970Sstevel@tonic-gate { 19980Sstevel@tonic-gate apic_cpus_info_t *cpu_infop; 19990Sstevel@tonic-gate 20000Sstevel@tonic-gate #if defined(__amd64) 20010Sstevel@tonic-gate setcr8((ulong_t)apic_cr8pri[prev_ipl]); 20020Sstevel@tonic-gate #else 20030Sstevel@tonic-gate apicadr[APIC_TASK_REG] = apic_ipltopri[prev_ipl]; 20040Sstevel@tonic-gate #endif 20050Sstevel@tonic-gate 20060Sstevel@tonic-gate cpu_infop = &apic_cpus[psm_get_cpu_id()]; 20070Sstevel@tonic-gate if (apic_level_intr[irq]) 20080Sstevel@tonic-gate apicadr[APIC_EOI_REG] = 0; 20090Sstevel@tonic-gate 20100Sstevel@tonic-gate cpu_infop->aci_curipl = (uchar_t)prev_ipl; 20110Sstevel@tonic-gate /* ISR above current pri could not be in progress */ 20120Sstevel@tonic-gate cpu_infop->aci_ISR_in_progress &= (2 << prev_ipl) - 1; 20130Sstevel@tonic-gate } 20140Sstevel@tonic-gate 20150Sstevel@tonic-gate /* 20160Sstevel@tonic-gate * Mask all interrupts below or equal to the given IPL 20170Sstevel@tonic-gate */ 20180Sstevel@tonic-gate static void 20190Sstevel@tonic-gate apic_setspl(int ipl) 20200Sstevel@tonic-gate { 20210Sstevel@tonic-gate 20220Sstevel@tonic-gate #if defined(__amd64) 20230Sstevel@tonic-gate setcr8((ulong_t)apic_cr8pri[ipl]); 20240Sstevel@tonic-gate #else 20250Sstevel@tonic-gate apicadr[APIC_TASK_REG] = apic_ipltopri[ipl]; 20260Sstevel@tonic-gate #endif 20270Sstevel@tonic-gate 20280Sstevel@tonic-gate /* interrupts at ipl above this cannot be in progress */ 20290Sstevel@tonic-gate apic_cpus[psm_get_cpu_id()].aci_ISR_in_progress &= (2 << ipl) - 1; 20300Sstevel@tonic-gate /* 20310Sstevel@tonic-gate * this is a patch fix for the ALR QSMP P5 machine, so that interrupts 20320Sstevel@tonic-gate * have enough time to come in before the priority is raised again 20330Sstevel@tonic-gate * during the idle() loop. 20340Sstevel@tonic-gate */ 20350Sstevel@tonic-gate if (apic_setspl_delay) 20360Sstevel@tonic-gate (void) get_apic_pri(); 20370Sstevel@tonic-gate } 20380Sstevel@tonic-gate 20390Sstevel@tonic-gate /* 20400Sstevel@tonic-gate * trigger a software interrupt at the given IPL 20410Sstevel@tonic-gate */ 20420Sstevel@tonic-gate static void 20430Sstevel@tonic-gate apic_set_softintr(int ipl) 20440Sstevel@tonic-gate { 20450Sstevel@tonic-gate int vector; 20460Sstevel@tonic-gate uint_t flag; 20470Sstevel@tonic-gate 20480Sstevel@tonic-gate vector = apic_resv_vector[ipl]; 20490Sstevel@tonic-gate 20500Sstevel@tonic-gate flag = intr_clear(); 20510Sstevel@tonic-gate 20520Sstevel@tonic-gate while (get_apic_cmd1() & AV_PENDING) 20530Sstevel@tonic-gate apic_ret(); 20540Sstevel@tonic-gate 20550Sstevel@tonic-gate /* generate interrupt at vector on itself only */ 20560Sstevel@tonic-gate apicadr[APIC_INT_CMD1] = AV_SH_SELF | vector; 20570Sstevel@tonic-gate 20580Sstevel@tonic-gate intr_restore(flag); 20590Sstevel@tonic-gate } 20600Sstevel@tonic-gate 20610Sstevel@tonic-gate /* 20620Sstevel@tonic-gate * generates an interprocessor interrupt to another CPU 20630Sstevel@tonic-gate */ 20640Sstevel@tonic-gate static void 20650Sstevel@tonic-gate apic_send_ipi(int cpun, int ipl) 20660Sstevel@tonic-gate { 20670Sstevel@tonic-gate int vector; 20680Sstevel@tonic-gate uint_t flag; 20690Sstevel@tonic-gate 20700Sstevel@tonic-gate vector = apic_resv_vector[ipl]; 20710Sstevel@tonic-gate 20720Sstevel@tonic-gate flag = intr_clear(); 20730Sstevel@tonic-gate 20740Sstevel@tonic-gate while (get_apic_cmd1() & AV_PENDING) 20750Sstevel@tonic-gate apic_ret(); 20760Sstevel@tonic-gate 20770Sstevel@tonic-gate apicadr[APIC_INT_CMD2] = 20780Sstevel@tonic-gate apic_cpus[cpun].aci_local_id << APIC_ICR_ID_BIT_OFFSET; 20790Sstevel@tonic-gate apicadr[APIC_INT_CMD1] = vector; 20800Sstevel@tonic-gate 20810Sstevel@tonic-gate intr_restore(flag); 20820Sstevel@tonic-gate } 20830Sstevel@tonic-gate 20840Sstevel@tonic-gate 20850Sstevel@tonic-gate /*ARGSUSED*/ 20860Sstevel@tonic-gate static void 20870Sstevel@tonic-gate apic_set_idlecpu(processorid_t cpun) 20880Sstevel@tonic-gate { 20890Sstevel@tonic-gate } 20900Sstevel@tonic-gate 20910Sstevel@tonic-gate /*ARGSUSED*/ 20920Sstevel@tonic-gate static void 20930Sstevel@tonic-gate apic_unset_idlecpu(processorid_t cpun) 20940Sstevel@tonic-gate { 20950Sstevel@tonic-gate } 20960Sstevel@tonic-gate 20970Sstevel@tonic-gate 20980Sstevel@tonic-gate static void 20990Sstevel@tonic-gate apic_ret() 21000Sstevel@tonic-gate { 21010Sstevel@tonic-gate } 21020Sstevel@tonic-gate 21030Sstevel@tonic-gate static int 21040Sstevel@tonic-gate get_apic_cmd1() 21050Sstevel@tonic-gate { 21060Sstevel@tonic-gate return (apicadr[APIC_INT_CMD1]); 21070Sstevel@tonic-gate } 21080Sstevel@tonic-gate 21090Sstevel@tonic-gate static int 21100Sstevel@tonic-gate get_apic_pri() 21110Sstevel@tonic-gate { 21120Sstevel@tonic-gate #if defined(__amd64) 21130Sstevel@tonic-gate return ((int)getcr8()); 21140Sstevel@tonic-gate #else 21150Sstevel@tonic-gate return (apicadr[APIC_TASK_REG]); 21160Sstevel@tonic-gate #endif 21170Sstevel@tonic-gate } 21180Sstevel@tonic-gate 21190Sstevel@tonic-gate /* 21200Sstevel@tonic-gate * If apic_coarse_time == 1, then apic_gettime() is used instead of 21210Sstevel@tonic-gate * apic_gethrtime(). This is used for performance instead of accuracy. 21220Sstevel@tonic-gate */ 21230Sstevel@tonic-gate 21240Sstevel@tonic-gate static hrtime_t 21250Sstevel@tonic-gate apic_gettime() 21260Sstevel@tonic-gate { 21270Sstevel@tonic-gate int old_hrtime_stamp; 21280Sstevel@tonic-gate hrtime_t temp; 21290Sstevel@tonic-gate 21300Sstevel@tonic-gate /* 21310Sstevel@tonic-gate * In one-shot mode, we do not keep time, so if anyone 21320Sstevel@tonic-gate * calls psm_gettime() directly, we vector over to 21330Sstevel@tonic-gate * gethrtime(). 21340Sstevel@tonic-gate * one-shot mode MUST NOT be enabled if this psm is the source of 21350Sstevel@tonic-gate * hrtime. 21360Sstevel@tonic-gate */ 21370Sstevel@tonic-gate 21380Sstevel@tonic-gate if (apic_oneshot) 21390Sstevel@tonic-gate return (gethrtime()); 21400Sstevel@tonic-gate 21410Sstevel@tonic-gate 21420Sstevel@tonic-gate gettime_again: 21430Sstevel@tonic-gate while ((old_hrtime_stamp = apic_hrtime_stamp) & 1) 21440Sstevel@tonic-gate apic_ret(); 21450Sstevel@tonic-gate 21460Sstevel@tonic-gate temp = apic_nsec_since_boot; 21470Sstevel@tonic-gate 21480Sstevel@tonic-gate if (apic_hrtime_stamp != old_hrtime_stamp) { /* got an interrupt */ 21490Sstevel@tonic-gate goto gettime_again; 21500Sstevel@tonic-gate } 21510Sstevel@tonic-gate return (temp); 21520Sstevel@tonic-gate } 21530Sstevel@tonic-gate 21540Sstevel@tonic-gate /* 21550Sstevel@tonic-gate * Here we return the number of nanoseconds since booting. Note every 21560Sstevel@tonic-gate * clock interrupt increments apic_nsec_since_boot by the appropriate 21570Sstevel@tonic-gate * amount. 21580Sstevel@tonic-gate */ 21590Sstevel@tonic-gate static hrtime_t 21600Sstevel@tonic-gate apic_gethrtime() 21610Sstevel@tonic-gate { 21620Sstevel@tonic-gate int curr_timeval, countval, elapsed_ticks, oflags; 21630Sstevel@tonic-gate int old_hrtime_stamp, status; 21640Sstevel@tonic-gate hrtime_t temp; 21650Sstevel@tonic-gate uchar_t cpun; 21660Sstevel@tonic-gate 21670Sstevel@tonic-gate 21680Sstevel@tonic-gate /* 21690Sstevel@tonic-gate * In one-shot mode, we do not keep time, so if anyone 21700Sstevel@tonic-gate * calls psm_gethrtime() directly, we vector over to 21710Sstevel@tonic-gate * gethrtime(). 21720Sstevel@tonic-gate * one-shot mode MUST NOT be enabled if this psm is the source of 21730Sstevel@tonic-gate * hrtime. 21740Sstevel@tonic-gate */ 21750Sstevel@tonic-gate 21760Sstevel@tonic-gate if (apic_oneshot) 21770Sstevel@tonic-gate return (gethrtime()); 21780Sstevel@tonic-gate 21790Sstevel@tonic-gate oflags = intr_clear(); /* prevent migration */ 21800Sstevel@tonic-gate 21810Sstevel@tonic-gate cpun = (uchar_t)((uint_t)apicadr[APIC_LID_REG] >> APIC_ID_BIT_OFFSET); 21820Sstevel@tonic-gate 21830Sstevel@tonic-gate lock_set(&apic_gethrtime_lock); 21840Sstevel@tonic-gate 21850Sstevel@tonic-gate gethrtime_again: 21860Sstevel@tonic-gate while ((old_hrtime_stamp = apic_hrtime_stamp) & 1) 21870Sstevel@tonic-gate apic_ret(); 21880Sstevel@tonic-gate 21890Sstevel@tonic-gate /* 21900Sstevel@tonic-gate * Check to see which CPU we are on. Note the time is kept on 21910Sstevel@tonic-gate * the local APIC of CPU 0. If on CPU 0, simply read the current 21920Sstevel@tonic-gate * counter. If on another CPU, issue a remote read command to CPU 0. 21930Sstevel@tonic-gate */ 21940Sstevel@tonic-gate if (cpun == apic_cpus[0].aci_local_id) { 21950Sstevel@tonic-gate countval = apicadr[APIC_CURR_COUNT]; 21960Sstevel@tonic-gate } else { 21970Sstevel@tonic-gate while (get_apic_cmd1() & AV_PENDING) 21980Sstevel@tonic-gate apic_ret(); 21990Sstevel@tonic-gate 22000Sstevel@tonic-gate apicadr[APIC_INT_CMD2] = 22010Sstevel@tonic-gate apic_cpus[0].aci_local_id << APIC_ICR_ID_BIT_OFFSET; 22020Sstevel@tonic-gate apicadr[APIC_INT_CMD1] = APIC_CURR_ADD|AV_REMOTE; 22030Sstevel@tonic-gate 22040Sstevel@tonic-gate while ((status = get_apic_cmd1()) & AV_READ_PENDING) 22050Sstevel@tonic-gate apic_ret(); 22060Sstevel@tonic-gate 22070Sstevel@tonic-gate if (status & AV_REMOTE_STATUS) /* 1 = valid */ 22080Sstevel@tonic-gate countval = apicadr[APIC_REMOTE_READ]; 22090Sstevel@tonic-gate else { /* 0 = invalid */ 22100Sstevel@tonic-gate apic_remote_hrterr++; 22110Sstevel@tonic-gate /* 22120Sstevel@tonic-gate * return last hrtime right now, will need more 22130Sstevel@tonic-gate * testing if change to retry 22140Sstevel@tonic-gate */ 22150Sstevel@tonic-gate temp = apic_last_hrtime; 22160Sstevel@tonic-gate 22170Sstevel@tonic-gate lock_clear(&apic_gethrtime_lock); 22180Sstevel@tonic-gate 22190Sstevel@tonic-gate intr_restore(oflags); 22200Sstevel@tonic-gate 22210Sstevel@tonic-gate return (temp); 22220Sstevel@tonic-gate } 22230Sstevel@tonic-gate } 22240Sstevel@tonic-gate if (countval > last_count_read) 22250Sstevel@tonic-gate countval = 0; 22260Sstevel@tonic-gate else 22270Sstevel@tonic-gate last_count_read = countval; 22280Sstevel@tonic-gate 22290Sstevel@tonic-gate elapsed_ticks = apic_hertz_count - countval; 22300Sstevel@tonic-gate 22310Sstevel@tonic-gate curr_timeval = elapsed_ticks * apic_nsec_per_tick; 22320Sstevel@tonic-gate temp = apic_nsec_since_boot + curr_timeval; 22330Sstevel@tonic-gate 22340Sstevel@tonic-gate if (apic_hrtime_stamp != old_hrtime_stamp) { /* got an interrupt */ 22350Sstevel@tonic-gate /* we might have clobbered last_count_read. Restore it */ 22360Sstevel@tonic-gate last_count_read = apic_hertz_count; 22370Sstevel@tonic-gate goto gethrtime_again; 22380Sstevel@tonic-gate } 22390Sstevel@tonic-gate 22400Sstevel@tonic-gate if (temp < apic_last_hrtime) { 22410Sstevel@tonic-gate /* return last hrtime if error occurs */ 22420Sstevel@tonic-gate apic_hrtime_error++; 22430Sstevel@tonic-gate temp = apic_last_hrtime; 22440Sstevel@tonic-gate } 22450Sstevel@tonic-gate else 22460Sstevel@tonic-gate apic_last_hrtime = temp; 22470Sstevel@tonic-gate 22480Sstevel@tonic-gate lock_clear(&apic_gethrtime_lock); 22490Sstevel@tonic-gate intr_restore(oflags); 22500Sstevel@tonic-gate 22510Sstevel@tonic-gate return (temp); 22520Sstevel@tonic-gate } 22530Sstevel@tonic-gate 22540Sstevel@tonic-gate /* apic NMI handler */ 22550Sstevel@tonic-gate /*ARGSUSED*/ 22560Sstevel@tonic-gate static void 22570Sstevel@tonic-gate apic_nmi_intr(caddr_t arg) 22580Sstevel@tonic-gate { 22590Sstevel@tonic-gate if (apic_shutdown_processors) { 22600Sstevel@tonic-gate apic_disable_local_apic(); 22610Sstevel@tonic-gate return; 22620Sstevel@tonic-gate } 22630Sstevel@tonic-gate 22640Sstevel@tonic-gate if (lock_try(&apic_nmi_lock)) { 22650Sstevel@tonic-gate if (apic_kmdb_on_nmi) { 22660Sstevel@tonic-gate if (psm_debugger() == 0) { 22670Sstevel@tonic-gate cmn_err(CE_PANIC, 22680Sstevel@tonic-gate "NMI detected, kmdb is not available."); 22690Sstevel@tonic-gate } else { 22700Sstevel@tonic-gate debug_enter("\nNMI detected, entering kmdb.\n"); 22710Sstevel@tonic-gate } 22720Sstevel@tonic-gate } else { 22730Sstevel@tonic-gate if (apic_panic_on_nmi) { 22740Sstevel@tonic-gate /* Keep panic from entering kmdb. */ 22750Sstevel@tonic-gate nopanicdebug = 1; 22760Sstevel@tonic-gate cmn_err(CE_PANIC, "pcplusmp: NMI received"); 22770Sstevel@tonic-gate } else { 22780Sstevel@tonic-gate /* 22790Sstevel@tonic-gate * prom_printf is the best shot we have 22800Sstevel@tonic-gate * of something which is problem free from 22810Sstevel@tonic-gate * high level/NMI type of interrupts 22820Sstevel@tonic-gate */ 22830Sstevel@tonic-gate prom_printf("pcplusmp: NMI received\n"); 22840Sstevel@tonic-gate apic_error |= APIC_ERR_NMI; 22850Sstevel@tonic-gate apic_num_nmis++; 22860Sstevel@tonic-gate } 22870Sstevel@tonic-gate } 22880Sstevel@tonic-gate lock_clear(&apic_nmi_lock); 22890Sstevel@tonic-gate } 22900Sstevel@tonic-gate } 22910Sstevel@tonic-gate 22920Sstevel@tonic-gate /* 22930Sstevel@tonic-gate * Add mask bits to disable interrupt vector from happening 22940Sstevel@tonic-gate * at or above IPL. In addition, it should remove mask bits 22950Sstevel@tonic-gate * to enable interrupt vectors below the given IPL. 22960Sstevel@tonic-gate * 22970Sstevel@tonic-gate * Both add and delspl are complicated by the fact that different interrupts 22980Sstevel@tonic-gate * may share IRQs. This can happen in two ways. 22990Sstevel@tonic-gate * 1. The same H/W line is shared by more than 1 device 23000Sstevel@tonic-gate * 1a. with interrupts at different IPLs 23010Sstevel@tonic-gate * 1b. with interrupts at same IPL 23020Sstevel@tonic-gate * 2. We ran out of vectors at a given IPL and started sharing vectors. 23030Sstevel@tonic-gate * 1b and 2 should be handled gracefully, except for the fact some ISRs 23040Sstevel@tonic-gate * will get called often when no interrupt is pending for the device. 23050Sstevel@tonic-gate * For 1a, we just hope that the machine blows up with the person who 23060Sstevel@tonic-gate * set it up that way!. In the meantime, we handle it at the higher IPL. 23070Sstevel@tonic-gate */ 23080Sstevel@tonic-gate /*ARGSUSED*/ 23090Sstevel@tonic-gate static int 23100Sstevel@tonic-gate apic_addspl(int irqno, int ipl, int min_ipl, int max_ipl) 23110Sstevel@tonic-gate { 23120Sstevel@tonic-gate uchar_t vector; 23130Sstevel@tonic-gate int iflag; 23140Sstevel@tonic-gate apic_irq_t *irqptr, *irqheadptr; 23150Sstevel@tonic-gate int irqindex; 23160Sstevel@tonic-gate 23170Sstevel@tonic-gate ASSERT(max_ipl <= UCHAR_MAX); 23180Sstevel@tonic-gate irqindex = IRQINDEX(irqno); 23190Sstevel@tonic-gate 23200Sstevel@tonic-gate if ((irqindex == -1) || (!apic_irq_table[irqindex])) 23210Sstevel@tonic-gate return (PSM_FAILURE); 23220Sstevel@tonic-gate 23230Sstevel@tonic-gate irqptr = irqheadptr = apic_irq_table[irqindex]; 23240Sstevel@tonic-gate 23250Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_addspl: dip=0x%p type=%d irqno=0x%x " 23260Sstevel@tonic-gate "vector=0x%x\n", (void *)irqptr->airq_dip, 23270Sstevel@tonic-gate irqptr->airq_mps_intr_index, irqno, irqptr->airq_vector)); 23280Sstevel@tonic-gate 23290Sstevel@tonic-gate while (irqptr) { 23300Sstevel@tonic-gate if (VIRTIRQ(irqindex, irqptr->airq_share_id) == irqno) 23310Sstevel@tonic-gate break; 23320Sstevel@tonic-gate irqptr = irqptr->airq_next; 23330Sstevel@tonic-gate } 23340Sstevel@tonic-gate irqptr->airq_share++; 23350Sstevel@tonic-gate 23360Sstevel@tonic-gate /* return if it is not hardware interrupt */ 23370Sstevel@tonic-gate if (irqptr->airq_mps_intr_index == RESERVE_INDEX) 23380Sstevel@tonic-gate return (PSM_SUCCESS); 23390Sstevel@tonic-gate 23400Sstevel@tonic-gate /* Or if there are more interupts at a higher IPL */ 23410Sstevel@tonic-gate if (ipl != max_ipl) 23420Sstevel@tonic-gate return (PSM_SUCCESS); 23430Sstevel@tonic-gate 23440Sstevel@tonic-gate /* 23450Sstevel@tonic-gate * if apic_picinit() has not been called yet, just return. 23460Sstevel@tonic-gate * At the end of apic_picinit(), we will call setup_io_intr(). 23470Sstevel@tonic-gate */ 23480Sstevel@tonic-gate 23490Sstevel@tonic-gate if (!apic_flag) 23500Sstevel@tonic-gate return (PSM_SUCCESS); 23510Sstevel@tonic-gate 23520Sstevel@tonic-gate iflag = intr_clear(); 23530Sstevel@tonic-gate 23540Sstevel@tonic-gate /* 23550Sstevel@tonic-gate * Upgrade vector if max_ipl is not earlier ipl. If we cannot allocate, 23560Sstevel@tonic-gate * return failure. Not very elegant, but then we hope the 23570Sstevel@tonic-gate * machine will blow up with ... 23580Sstevel@tonic-gate */ 23590Sstevel@tonic-gate if (irqptr->airq_ipl != max_ipl) { 23600Sstevel@tonic-gate vector = apic_allocate_vector(max_ipl, irqindex, 1); 23610Sstevel@tonic-gate if (vector == 0) { 23620Sstevel@tonic-gate intr_restore(iflag); 23630Sstevel@tonic-gate irqptr->airq_share--; 23640Sstevel@tonic-gate return (PSM_FAILURE); 23650Sstevel@tonic-gate } 23660Sstevel@tonic-gate irqptr = irqheadptr; 23670Sstevel@tonic-gate apic_mark_vector(irqptr->airq_vector, vector); 23680Sstevel@tonic-gate while (irqptr) { 23690Sstevel@tonic-gate irqptr->airq_vector = vector; 23700Sstevel@tonic-gate irqptr->airq_ipl = (uchar_t)max_ipl; 23710Sstevel@tonic-gate /* 23720Sstevel@tonic-gate * reprogram irq being added and every one else 23730Sstevel@tonic-gate * who is not in the UNINIT state 23740Sstevel@tonic-gate */ 23750Sstevel@tonic-gate if ((VIRTIRQ(irqindex, irqptr->airq_share_id) == 23760Sstevel@tonic-gate irqno) || (irqptr->airq_temp_cpu != IRQ_UNINIT)) { 23770Sstevel@tonic-gate apic_record_rdt_entry(irqptr, irqindex); 23780Sstevel@tonic-gate (void) apic_setup_io_intr(irqptr, irqindex); 23790Sstevel@tonic-gate } 23800Sstevel@tonic-gate irqptr = irqptr->airq_next; 23810Sstevel@tonic-gate } 23820Sstevel@tonic-gate intr_restore(iflag); 23830Sstevel@tonic-gate return (PSM_SUCCESS); 23840Sstevel@tonic-gate } 23850Sstevel@tonic-gate 23860Sstevel@tonic-gate ASSERT(irqptr); 23870Sstevel@tonic-gate (void) apic_setup_io_intr(irqptr, irqindex); 23880Sstevel@tonic-gate intr_restore(iflag); 23890Sstevel@tonic-gate return (PSM_SUCCESS); 23900Sstevel@tonic-gate } 23910Sstevel@tonic-gate 23920Sstevel@tonic-gate /* 23930Sstevel@tonic-gate * Recompute mask bits for the given interrupt vector. 23940Sstevel@tonic-gate * If there is no interrupt servicing routine for this 23950Sstevel@tonic-gate * vector, this function should disable interrupt vector 23960Sstevel@tonic-gate * from happening at all IPLs. If there are still 23970Sstevel@tonic-gate * handlers using the given vector, this function should 23980Sstevel@tonic-gate * disable the given vector from happening below the lowest 23990Sstevel@tonic-gate * IPL of the remaining hadlers. 24000Sstevel@tonic-gate */ 24010Sstevel@tonic-gate /*ARGSUSED*/ 24020Sstevel@tonic-gate static int 24030Sstevel@tonic-gate apic_delspl(int irqno, int ipl, int min_ipl, int max_ipl) 24040Sstevel@tonic-gate { 24050Sstevel@tonic-gate uchar_t vector, bind_cpu; 24060Sstevel@tonic-gate int iflag, intin, irqindex; 24070Sstevel@tonic-gate volatile int32_t *ioapic; 24080Sstevel@tonic-gate apic_irq_t *irqptr, *irqheadptr; 24090Sstevel@tonic-gate 24100Sstevel@tonic-gate irqindex = IRQINDEX(irqno); 24110Sstevel@tonic-gate irqptr = irqheadptr = apic_irq_table[irqindex]; 24120Sstevel@tonic-gate 24130Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_delspl: dip=0x%p type=%d irqno=0x%x " 24140Sstevel@tonic-gate "vector=0x%x\n", (void *)irqptr->airq_dip, 24150Sstevel@tonic-gate irqptr->airq_mps_intr_index, irqno, irqptr->airq_vector)); 24160Sstevel@tonic-gate 24170Sstevel@tonic-gate while (irqptr) { 24180Sstevel@tonic-gate if (VIRTIRQ(irqindex, irqptr->airq_share_id) == irqno) 24190Sstevel@tonic-gate break; 24200Sstevel@tonic-gate irqptr = irqptr->airq_next; 24210Sstevel@tonic-gate } 24220Sstevel@tonic-gate ASSERT(irqptr); 24230Sstevel@tonic-gate 24240Sstevel@tonic-gate irqptr->airq_share--; 24250Sstevel@tonic-gate 24260Sstevel@tonic-gate if (ipl < max_ipl) 24270Sstevel@tonic-gate return (PSM_SUCCESS); 24280Sstevel@tonic-gate 24290Sstevel@tonic-gate /* return if it is not hardware interrupt */ 24300Sstevel@tonic-gate if (irqptr->airq_mps_intr_index == RESERVE_INDEX) 24310Sstevel@tonic-gate return (PSM_SUCCESS); 24320Sstevel@tonic-gate 24330Sstevel@tonic-gate if (!apic_flag) { 24340Sstevel@tonic-gate /* 24350Sstevel@tonic-gate * Clear irq_struct. If two devices shared an intpt 24360Sstevel@tonic-gate * line & 1 unloaded before picinit, we are hosed. But, then 24370Sstevel@tonic-gate * we hope the machine will ... 24380Sstevel@tonic-gate */ 24390Sstevel@tonic-gate irqptr->airq_mps_intr_index = FREE_INDEX; 24400Sstevel@tonic-gate irqptr->airq_temp_cpu = IRQ_UNINIT; 24410Sstevel@tonic-gate apic_free_vector(irqptr->airq_vector); 24420Sstevel@tonic-gate return (PSM_SUCCESS); 24430Sstevel@tonic-gate } 24440Sstevel@tonic-gate /* 24450Sstevel@tonic-gate * Downgrade vector to new max_ipl if needed.If we cannot allocate, 24460Sstevel@tonic-gate * use old IPL. Not very elegant, but then we hope ... 24470Sstevel@tonic-gate */ 24480Sstevel@tonic-gate if ((irqptr->airq_ipl != max_ipl) && (max_ipl != PSM_INVALID_IPL)) { 24490Sstevel@tonic-gate apic_irq_t *irqp; 24500Sstevel@tonic-gate if (vector = apic_allocate_vector(max_ipl, irqno, 1)) { 24510Sstevel@tonic-gate apic_mark_vector(irqheadptr->airq_vector, vector); 24520Sstevel@tonic-gate irqp = irqheadptr; 24530Sstevel@tonic-gate while (irqp) { 24540Sstevel@tonic-gate irqp->airq_vector = vector; 24550Sstevel@tonic-gate irqp->airq_ipl = (uchar_t)max_ipl; 24560Sstevel@tonic-gate if (irqp->airq_temp_cpu != IRQ_UNINIT) { 24570Sstevel@tonic-gate apic_record_rdt_entry(irqp, irqindex); 24580Sstevel@tonic-gate (void) apic_setup_io_intr(irqp, 24590Sstevel@tonic-gate irqindex); 24600Sstevel@tonic-gate } 24610Sstevel@tonic-gate irqp = irqp->airq_next; 24620Sstevel@tonic-gate } 24630Sstevel@tonic-gate } 24640Sstevel@tonic-gate } 24650Sstevel@tonic-gate 24660Sstevel@tonic-gate if (irqptr->airq_share) 24670Sstevel@tonic-gate return (PSM_SUCCESS); 24680Sstevel@tonic-gate 24690Sstevel@tonic-gate ioapic = apicioadr[irqptr->airq_ioapicindex]; 24700Sstevel@tonic-gate intin = irqptr->airq_intin_no; 24710Sstevel@tonic-gate iflag = intr_clear(); 24720Sstevel@tonic-gate lock_set(&apic_ioapic_lock); 24730Sstevel@tonic-gate ioapic[APIC_IO_REG] = APIC_RDT_CMD + 2 * intin; 24740Sstevel@tonic-gate ioapic[APIC_IO_DATA] = AV_MASK; 24750Sstevel@tonic-gate 24760Sstevel@tonic-gate /* Disable the MSI/X vector */ 24770Sstevel@tonic-gate if (APIC_IS_MSI_OR_MSIX_INDEX(irqptr->airq_mps_intr_index)) { 24780Sstevel@tonic-gate int type = (irqptr->airq_mps_intr_index == MSI_INDEX) ? 24790Sstevel@tonic-gate DDI_INTR_TYPE_MSI : DDI_INTR_TYPE_MSIX; 24800Sstevel@tonic-gate 24810Sstevel@tonic-gate /* 24820Sstevel@tonic-gate * Make sure we only disable on the last 24830Sstevel@tonic-gate * of the multi-MSI support 24840Sstevel@tonic-gate */ 24850Sstevel@tonic-gate if (i_ddi_intr_get_current_nintrs(irqptr->airq_dip) == 1) { 24861997Sanish (void) apic_pci_msi_unconfigure(irqptr->airq_dip, 24871997Sanish type, irqptr->airq_ioapicindex); 24881997Sanish (void) apic_pci_msi_disable_mode(irqptr->airq_dip, 24891997Sanish type, 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++) { 2610*2006Sandrei if (CPU_IN_SET(apic_cpumask, 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 { 37381701Sjohnny bind_cpu = apic_next_bind_cpu++; 37390Sstevel@tonic-gate if (bind_cpu >= apic_nproc) { 37400Sstevel@tonic-gate apic_next_bind_cpu = 1; 37410Sstevel@tonic-gate bind_cpu = 0; 37420Sstevel@tonic-gate } 37430Sstevel@tonic-gate } 37440Sstevel@tonic-gate if (drv_name != NULL) 37450Sstevel@tonic-gate cmn_err(CE_CONT, "!pcplusmp: %s (%s) instance %d " 37460Sstevel@tonic-gate "vector 0x%x ioapic 0x%x intin 0x%x is bound to cpu %d\n", 37470Sstevel@tonic-gate name, drv_name, instance, 37480Sstevel@tonic-gate irq, ioapicid, intin, bind_cpu & ~IRQ_USER_BOUND); 37490Sstevel@tonic-gate else 37500Sstevel@tonic-gate cmn_err(CE_CONT, "!pcplusmp: " 37510Sstevel@tonic-gate "vector 0x%x ioapic 0x%x intin 0x%x is bound to cpu %d\n", 37520Sstevel@tonic-gate irq, ioapicid, intin, bind_cpu & ~IRQ_USER_BOUND); 37530Sstevel@tonic-gate 37540Sstevel@tonic-gate return ((uchar_t)bind_cpu); 37550Sstevel@tonic-gate } 37560Sstevel@tonic-gate 37570Sstevel@tonic-gate static struct apic_io_intr * 37580Sstevel@tonic-gate apic_find_io_intr_w_busid(int irqno, int busid) 37590Sstevel@tonic-gate { 37600Sstevel@tonic-gate struct apic_io_intr *intrp; 37610Sstevel@tonic-gate 37620Sstevel@tonic-gate /* 37630Sstevel@tonic-gate * It can have more than 1 entry with same source bus IRQ, 37640Sstevel@tonic-gate * but unique with the source bus id 37650Sstevel@tonic-gate */ 37660Sstevel@tonic-gate intrp = apic_io_intrp; 37670Sstevel@tonic-gate if (intrp != NULL) { 37680Sstevel@tonic-gate while (intrp->intr_entry == APIC_IO_INTR_ENTRY) { 37690Sstevel@tonic-gate if (intrp->intr_irq == irqno && 37700Sstevel@tonic-gate intrp->intr_busid == busid && 37710Sstevel@tonic-gate intrp->intr_type == IO_INTR_INT) 37720Sstevel@tonic-gate return (intrp); 37730Sstevel@tonic-gate intrp++; 37740Sstevel@tonic-gate } 37750Sstevel@tonic-gate } 37760Sstevel@tonic-gate APIC_VERBOSE_IOAPIC((CE_NOTE, "Did not find io intr for irqno:" 37770Sstevel@tonic-gate "busid %x:%x\n", irqno, busid)); 37780Sstevel@tonic-gate return ((struct apic_io_intr *)NULL); 37790Sstevel@tonic-gate } 37800Sstevel@tonic-gate 37810Sstevel@tonic-gate 37820Sstevel@tonic-gate struct mps_bus_info { 37830Sstevel@tonic-gate char *bus_name; 37840Sstevel@tonic-gate int bus_id; 37850Sstevel@tonic-gate } bus_info_array[] = { 37860Sstevel@tonic-gate "ISA ", BUS_ISA, 37870Sstevel@tonic-gate "PCI ", BUS_PCI, 37880Sstevel@tonic-gate "EISA ", BUS_EISA, 37890Sstevel@tonic-gate "XPRESS", BUS_XPRESS, 37900Sstevel@tonic-gate "PCMCIA", BUS_PCMCIA, 37910Sstevel@tonic-gate "VL ", BUS_VL, 37920Sstevel@tonic-gate "CBUS ", BUS_CBUS, 37930Sstevel@tonic-gate "CBUSII", BUS_CBUSII, 37940Sstevel@tonic-gate "FUTURE", BUS_FUTURE, 37950Sstevel@tonic-gate "INTERN", BUS_INTERN, 37960Sstevel@tonic-gate "MBI ", BUS_MBI, 37970Sstevel@tonic-gate "MBII ", BUS_MBII, 37980Sstevel@tonic-gate "MPI ", BUS_MPI, 37990Sstevel@tonic-gate "MPSA ", BUS_MPSA, 38000Sstevel@tonic-gate "NUBUS ", BUS_NUBUS, 38010Sstevel@tonic-gate "TC ", BUS_TC, 38020Sstevel@tonic-gate "VME ", BUS_VME 38030Sstevel@tonic-gate }; 38040Sstevel@tonic-gate 38050Sstevel@tonic-gate static int 38060Sstevel@tonic-gate apic_find_bus_type(char *bus) 38070Sstevel@tonic-gate { 38080Sstevel@tonic-gate int i = 0; 38090Sstevel@tonic-gate 38100Sstevel@tonic-gate for (; i < sizeof (bus_info_array)/sizeof (struct mps_bus_info); i++) 38110Sstevel@tonic-gate if (strncmp(bus, bus_info_array[i].bus_name, 38120Sstevel@tonic-gate strlen(bus_info_array[i].bus_name)) == 0) 38130Sstevel@tonic-gate return (bus_info_array[i].bus_id); 38140Sstevel@tonic-gate APIC_VERBOSE_IOAPIC((CE_WARN, "Did not find bus type for bus %s", bus)); 38150Sstevel@tonic-gate return (0); 38160Sstevel@tonic-gate } 38170Sstevel@tonic-gate 38180Sstevel@tonic-gate static int 38190Sstevel@tonic-gate apic_find_bus(int busid) 38200Sstevel@tonic-gate { 38210Sstevel@tonic-gate struct apic_bus *busp; 38220Sstevel@tonic-gate 38230Sstevel@tonic-gate busp = apic_busp; 38240Sstevel@tonic-gate while (busp->bus_entry == APIC_BUS_ENTRY) { 38250Sstevel@tonic-gate if (busp->bus_id == busid) 38260Sstevel@tonic-gate return (apic_find_bus_type((char *)&busp->bus_str1)); 38270Sstevel@tonic-gate busp++; 38280Sstevel@tonic-gate } 38290Sstevel@tonic-gate APIC_VERBOSE_IOAPIC((CE_WARN, "Did not find bus for bus id %x", busid)); 38300Sstevel@tonic-gate return (0); 38310Sstevel@tonic-gate } 38320Sstevel@tonic-gate 38330Sstevel@tonic-gate static int 38340Sstevel@tonic-gate apic_find_bus_id(int bustype) 38350Sstevel@tonic-gate { 38360Sstevel@tonic-gate struct apic_bus *busp; 38370Sstevel@tonic-gate 38380Sstevel@tonic-gate busp = apic_busp; 38390Sstevel@tonic-gate while (busp->bus_entry == APIC_BUS_ENTRY) { 38400Sstevel@tonic-gate if (apic_find_bus_type((char *)&busp->bus_str1) == bustype) 38410Sstevel@tonic-gate return (busp->bus_id); 38420Sstevel@tonic-gate busp++; 38430Sstevel@tonic-gate } 38440Sstevel@tonic-gate APIC_VERBOSE_IOAPIC((CE_WARN, "Did not find bus id for bustype %x", 38450Sstevel@tonic-gate bustype)); 38460Sstevel@tonic-gate return (-1); 38470Sstevel@tonic-gate } 38480Sstevel@tonic-gate 38490Sstevel@tonic-gate /* 38500Sstevel@tonic-gate * Check if a particular irq need to be reserved for any io_intr 38510Sstevel@tonic-gate */ 38520Sstevel@tonic-gate static struct apic_io_intr * 38530Sstevel@tonic-gate apic_find_io_intr(int irqno) 38540Sstevel@tonic-gate { 38550Sstevel@tonic-gate struct apic_io_intr *intrp; 38560Sstevel@tonic-gate 38570Sstevel@tonic-gate intrp = apic_io_intrp; 38580Sstevel@tonic-gate if (intrp != NULL) { 38590Sstevel@tonic-gate while (intrp->intr_entry == APIC_IO_INTR_ENTRY) { 38600Sstevel@tonic-gate if (intrp->intr_irq == irqno && 38610Sstevel@tonic-gate intrp->intr_type == IO_INTR_INT) 38620Sstevel@tonic-gate return (intrp); 38630Sstevel@tonic-gate intrp++; 38640Sstevel@tonic-gate } 38650Sstevel@tonic-gate } 38660Sstevel@tonic-gate return ((struct apic_io_intr *)NULL); 38670Sstevel@tonic-gate } 38680Sstevel@tonic-gate 38690Sstevel@tonic-gate /* 38700Sstevel@tonic-gate * Check if the given ioapicindex intin combination has already been assigned 38710Sstevel@tonic-gate * an irq. If so return irqno. Else -1 38720Sstevel@tonic-gate */ 38730Sstevel@tonic-gate static int 38740Sstevel@tonic-gate apic_find_intin(uchar_t ioapic, uchar_t intin) 38750Sstevel@tonic-gate { 38760Sstevel@tonic-gate apic_irq_t *irqptr; 38770Sstevel@tonic-gate int i; 38780Sstevel@tonic-gate 38790Sstevel@tonic-gate /* find ioapic and intin in the apic_irq_table[] and return the index */ 38800Sstevel@tonic-gate for (i = apic_min_device_irq; i <= apic_max_device_irq; i++) { 38810Sstevel@tonic-gate irqptr = apic_irq_table[i]; 38820Sstevel@tonic-gate while (irqptr) { 38830Sstevel@tonic-gate if ((irqptr->airq_mps_intr_index >= 0) && 38840Sstevel@tonic-gate (irqptr->airq_intin_no == intin) && 38850Sstevel@tonic-gate (irqptr->airq_ioapicindex == ioapic)) { 38860Sstevel@tonic-gate APIC_VERBOSE_IOAPIC((CE_NOTE, "!Found irq " 38870Sstevel@tonic-gate "entry for ioapic:intin %x:%x " 38880Sstevel@tonic-gate "shared interrupts ?", ioapic, intin)); 38890Sstevel@tonic-gate return (i); 38900Sstevel@tonic-gate } 38910Sstevel@tonic-gate irqptr = irqptr->airq_next; 38920Sstevel@tonic-gate } 38930Sstevel@tonic-gate } 38940Sstevel@tonic-gate return (-1); 38950Sstevel@tonic-gate } 38960Sstevel@tonic-gate 38970Sstevel@tonic-gate int 38980Sstevel@tonic-gate apic_allocate_irq(int irq) 38990Sstevel@tonic-gate { 39000Sstevel@tonic-gate int freeirq, i; 39010Sstevel@tonic-gate 39020Sstevel@tonic-gate if ((freeirq = apic_find_free_irq(irq, (APIC_RESV_IRQ - 1))) == -1) 39030Sstevel@tonic-gate if ((freeirq = apic_find_free_irq(APIC_FIRST_FREE_IRQ, 39040Sstevel@tonic-gate (irq - 1))) == -1) { 39050Sstevel@tonic-gate /* 39060Sstevel@tonic-gate * if BIOS really defines every single irq in the mps 39070Sstevel@tonic-gate * table, then don't worry about conflicting with 39080Sstevel@tonic-gate * them, just use any free slot in apic_irq_table 39090Sstevel@tonic-gate */ 39100Sstevel@tonic-gate for (i = APIC_FIRST_FREE_IRQ; i < APIC_RESV_IRQ; i++) { 39110Sstevel@tonic-gate if ((apic_irq_table[i] == NULL) || 39120Sstevel@tonic-gate apic_irq_table[i]->airq_mps_intr_index == 39130Sstevel@tonic-gate FREE_INDEX) { 39140Sstevel@tonic-gate freeirq = i; 39150Sstevel@tonic-gate break; 39160Sstevel@tonic-gate } 39170Sstevel@tonic-gate } 39180Sstevel@tonic-gate if (freeirq == -1) { 39190Sstevel@tonic-gate /* This shouldn't happen, but just in case */ 39200Sstevel@tonic-gate cmn_err(CE_WARN, "pcplusmp: NO available IRQ"); 39210Sstevel@tonic-gate return (-1); 39220Sstevel@tonic-gate } 39230Sstevel@tonic-gate } 39240Sstevel@tonic-gate if (apic_irq_table[freeirq] == NULL) { 39250Sstevel@tonic-gate apic_irq_table[freeirq] = 39260Sstevel@tonic-gate kmem_zalloc(sizeof (apic_irq_t), KM_NOSLEEP); 39270Sstevel@tonic-gate if (apic_irq_table[freeirq] == NULL) { 39280Sstevel@tonic-gate cmn_err(CE_WARN, "pcplusmp: NO memory to allocate IRQ"); 39290Sstevel@tonic-gate return (-1); 39300Sstevel@tonic-gate } 39310Sstevel@tonic-gate apic_irq_table[freeirq]->airq_mps_intr_index = FREE_INDEX; 39320Sstevel@tonic-gate } 39330Sstevel@tonic-gate return (freeirq); 39340Sstevel@tonic-gate } 39350Sstevel@tonic-gate 39360Sstevel@tonic-gate static int 39370Sstevel@tonic-gate apic_find_free_irq(int start, int end) 39380Sstevel@tonic-gate { 39390Sstevel@tonic-gate int i; 39400Sstevel@tonic-gate 39410Sstevel@tonic-gate for (i = start; i <= end; i++) 39420Sstevel@tonic-gate /* Check if any I/O entry needs this IRQ */ 39430Sstevel@tonic-gate if (apic_find_io_intr(i) == NULL) { 39440Sstevel@tonic-gate /* Then see if it is free */ 39450Sstevel@tonic-gate if ((apic_irq_table[i] == NULL) || 39460Sstevel@tonic-gate (apic_irq_table[i]->airq_mps_intr_index == 39470Sstevel@tonic-gate FREE_INDEX)) { 39480Sstevel@tonic-gate return (i); 39490Sstevel@tonic-gate } 39500Sstevel@tonic-gate } 39510Sstevel@tonic-gate return (-1); 39520Sstevel@tonic-gate } 39530Sstevel@tonic-gate 39540Sstevel@tonic-gate /* 39550Sstevel@tonic-gate * Allocate a free vector for irq at ipl. Takes care of merging of multiple 39560Sstevel@tonic-gate * IPLs into a single APIC level as well as stretching some IPLs onto multiple 39570Sstevel@tonic-gate * levels. APIC_HI_PRI_VECTS interrupts are reserved for high priority 39580Sstevel@tonic-gate * requests and allocated only when pri is set. 39590Sstevel@tonic-gate */ 39600Sstevel@tonic-gate static uchar_t 39610Sstevel@tonic-gate apic_allocate_vector(int ipl, int irq, int pri) 39620Sstevel@tonic-gate { 39630Sstevel@tonic-gate int lowest, highest, i; 39640Sstevel@tonic-gate 39650Sstevel@tonic-gate highest = apic_ipltopri[ipl] + APIC_VECTOR_MASK; 39660Sstevel@tonic-gate lowest = apic_ipltopri[ipl - 1] + APIC_VECTOR_PER_IPL; 39670Sstevel@tonic-gate 39680Sstevel@tonic-gate if (highest < lowest) /* Both ipl and ipl - 1 map to same pri */ 39690Sstevel@tonic-gate lowest -= APIC_VECTOR_PER_IPL; 39700Sstevel@tonic-gate 39710Sstevel@tonic-gate #ifdef DEBUG 39720Sstevel@tonic-gate if (apic_restrict_vector) /* for testing shared interrupt logic */ 39730Sstevel@tonic-gate highest = lowest + apic_restrict_vector + APIC_HI_PRI_VECTS; 39740Sstevel@tonic-gate #endif /* DEBUG */ 39750Sstevel@tonic-gate if (pri == 0) 39760Sstevel@tonic-gate highest -= APIC_HI_PRI_VECTS; 39770Sstevel@tonic-gate 39780Sstevel@tonic-gate for (i = lowest; i < highest; i++) { 39790Sstevel@tonic-gate if ((i == T_FASTTRAP) || (i == APIC_SPUR_INTR) || 39800Sstevel@tonic-gate (i == T_SYSCALLINT) || (i == T_DTRACE_PROBE) || 39810Sstevel@tonic-gate (i == T_DTRACE_RET)) 39820Sstevel@tonic-gate continue; 39830Sstevel@tonic-gate if (apic_vector_to_irq[i] == APIC_RESV_IRQ) { 39840Sstevel@tonic-gate apic_vector_to_irq[i] = (uchar_t)irq; 39850Sstevel@tonic-gate return (i); 39860Sstevel@tonic-gate } 39870Sstevel@tonic-gate } 39880Sstevel@tonic-gate 39890Sstevel@tonic-gate return (0); 39900Sstevel@tonic-gate } 39910Sstevel@tonic-gate 39920Sstevel@tonic-gate static void 39930Sstevel@tonic-gate apic_modify_vector(uchar_t vector, int irq) 39940Sstevel@tonic-gate { 39950Sstevel@tonic-gate apic_vector_to_irq[vector] = (uchar_t)irq; 39960Sstevel@tonic-gate } 39970Sstevel@tonic-gate 39980Sstevel@tonic-gate /* 39990Sstevel@tonic-gate * Mark vector as being in the process of being deleted. Interrupts 40000Sstevel@tonic-gate * may still come in on some CPU. The moment an interrupt comes with 40010Sstevel@tonic-gate * the new vector, we know we can free the old one. Called only from 40020Sstevel@tonic-gate * addspl and delspl with interrupts disabled. Because an interrupt 40030Sstevel@tonic-gate * can be shared, but no interrupt from either device may come in, 40040Sstevel@tonic-gate * we also use a timeout mechanism, which we arbitrarily set to 40050Sstevel@tonic-gate * apic_revector_timeout microseconds. 40060Sstevel@tonic-gate */ 40070Sstevel@tonic-gate static void 40080Sstevel@tonic-gate apic_mark_vector(uchar_t oldvector, uchar_t newvector) 40090Sstevel@tonic-gate { 40100Sstevel@tonic-gate int iflag = intr_clear(); 40110Sstevel@tonic-gate lock_set(&apic_revector_lock); 40120Sstevel@tonic-gate if (!apic_oldvec_to_newvec) { 40130Sstevel@tonic-gate apic_oldvec_to_newvec = 40140Sstevel@tonic-gate kmem_zalloc(sizeof (newvector) * APIC_MAX_VECTOR * 2, 40150Sstevel@tonic-gate KM_NOSLEEP); 40160Sstevel@tonic-gate 40170Sstevel@tonic-gate if (!apic_oldvec_to_newvec) { 40180Sstevel@tonic-gate /* 40190Sstevel@tonic-gate * This failure is not catastrophic. 40200Sstevel@tonic-gate * But, the oldvec will never be freed. 40210Sstevel@tonic-gate */ 40220Sstevel@tonic-gate apic_error |= APIC_ERR_MARK_VECTOR_FAIL; 40230Sstevel@tonic-gate lock_clear(&apic_revector_lock); 40240Sstevel@tonic-gate intr_restore(iflag); 40250Sstevel@tonic-gate return; 40260Sstevel@tonic-gate } 40270Sstevel@tonic-gate apic_newvec_to_oldvec = &apic_oldvec_to_newvec[APIC_MAX_VECTOR]; 40280Sstevel@tonic-gate } 40290Sstevel@tonic-gate 40300Sstevel@tonic-gate /* See if we already did this for drivers which do double addintrs */ 40310Sstevel@tonic-gate if (apic_oldvec_to_newvec[oldvector] != newvector) { 40320Sstevel@tonic-gate apic_oldvec_to_newvec[oldvector] = newvector; 40330Sstevel@tonic-gate apic_newvec_to_oldvec[newvector] = oldvector; 40340Sstevel@tonic-gate apic_revector_pending++; 40350Sstevel@tonic-gate } 40360Sstevel@tonic-gate lock_clear(&apic_revector_lock); 40370Sstevel@tonic-gate intr_restore(iflag); 40380Sstevel@tonic-gate (void) timeout(apic_xlate_vector_free_timeout_handler, 40390Sstevel@tonic-gate (void *)(uintptr_t)oldvector, drv_usectohz(apic_revector_timeout)); 40400Sstevel@tonic-gate } 40410Sstevel@tonic-gate 40420Sstevel@tonic-gate /* 40430Sstevel@tonic-gate * xlate_vector is called from intr_enter if revector_pending is set. 40440Sstevel@tonic-gate * It will xlate it if needed and mark the old vector as free. 40450Sstevel@tonic-gate */ 40460Sstevel@tonic-gate static uchar_t 40470Sstevel@tonic-gate apic_xlate_vector(uchar_t vector) 40480Sstevel@tonic-gate { 40490Sstevel@tonic-gate uchar_t newvector, oldvector = 0; 40500Sstevel@tonic-gate 40510Sstevel@tonic-gate lock_set(&apic_revector_lock); 40520Sstevel@tonic-gate /* Do we really need to do this ? */ 40530Sstevel@tonic-gate if (!apic_revector_pending) { 40540Sstevel@tonic-gate lock_clear(&apic_revector_lock); 40550Sstevel@tonic-gate return (vector); 40560Sstevel@tonic-gate } 40570Sstevel@tonic-gate if ((newvector = apic_oldvec_to_newvec[vector]) != 0) 40580Sstevel@tonic-gate oldvector = vector; 40590Sstevel@tonic-gate else { 40600Sstevel@tonic-gate /* 40610Sstevel@tonic-gate * The incoming vector is new . See if a stale entry is 40620Sstevel@tonic-gate * remaining 40630Sstevel@tonic-gate */ 40640Sstevel@tonic-gate if ((oldvector = apic_newvec_to_oldvec[vector]) != 0) 40650Sstevel@tonic-gate newvector = vector; 40660Sstevel@tonic-gate } 40670Sstevel@tonic-gate 40680Sstevel@tonic-gate if (oldvector) { 40690Sstevel@tonic-gate apic_revector_pending--; 40700Sstevel@tonic-gate apic_oldvec_to_newvec[oldvector] = 0; 40710Sstevel@tonic-gate apic_newvec_to_oldvec[newvector] = 0; 40720Sstevel@tonic-gate apic_free_vector(oldvector); 40730Sstevel@tonic-gate lock_clear(&apic_revector_lock); 40740Sstevel@tonic-gate /* There could have been more than one reprogramming! */ 40750Sstevel@tonic-gate return (apic_xlate_vector(newvector)); 40760Sstevel@tonic-gate } 40770Sstevel@tonic-gate lock_clear(&apic_revector_lock); 40780Sstevel@tonic-gate return (vector); 40790Sstevel@tonic-gate } 40800Sstevel@tonic-gate 40810Sstevel@tonic-gate void 40820Sstevel@tonic-gate apic_xlate_vector_free_timeout_handler(void *arg) 40830Sstevel@tonic-gate { 40840Sstevel@tonic-gate int iflag; 40850Sstevel@tonic-gate uchar_t oldvector, newvector; 40860Sstevel@tonic-gate 40870Sstevel@tonic-gate oldvector = (uchar_t)(uintptr_t)arg; 40880Sstevel@tonic-gate iflag = intr_clear(); 40890Sstevel@tonic-gate lock_set(&apic_revector_lock); 40900Sstevel@tonic-gate if ((newvector = apic_oldvec_to_newvec[oldvector]) != 0) { 40910Sstevel@tonic-gate apic_free_vector(oldvector); 40920Sstevel@tonic-gate apic_oldvec_to_newvec[oldvector] = 0; 40930Sstevel@tonic-gate apic_newvec_to_oldvec[newvector] = 0; 40940Sstevel@tonic-gate apic_revector_pending--; 40950Sstevel@tonic-gate } 40960Sstevel@tonic-gate 40970Sstevel@tonic-gate lock_clear(&apic_revector_lock); 40980Sstevel@tonic-gate intr_restore(iflag); 40990Sstevel@tonic-gate } 41000Sstevel@tonic-gate 41010Sstevel@tonic-gate 41020Sstevel@tonic-gate /* Mark vector as not being used by any irq */ 41030Sstevel@tonic-gate static void 41040Sstevel@tonic-gate apic_free_vector(uchar_t vector) 41050Sstevel@tonic-gate { 41060Sstevel@tonic-gate apic_vector_to_irq[vector] = APIC_RESV_IRQ; 41070Sstevel@tonic-gate } 41080Sstevel@tonic-gate 41090Sstevel@tonic-gate /* 41100Sstevel@tonic-gate * compute the polarity, trigger mode and vector for programming into 41110Sstevel@tonic-gate * the I/O apic and record in airq_rdt_entry. 41120Sstevel@tonic-gate */ 41130Sstevel@tonic-gate static void 41140Sstevel@tonic-gate apic_record_rdt_entry(apic_irq_t *irqptr, int irq) 41150Sstevel@tonic-gate { 41160Sstevel@tonic-gate int ioapicindex, bus_type, vector; 41170Sstevel@tonic-gate short intr_index; 41180Sstevel@tonic-gate uint_t level, po, io_po; 41190Sstevel@tonic-gate struct apic_io_intr *iointrp; 41200Sstevel@tonic-gate 41210Sstevel@tonic-gate intr_index = irqptr->airq_mps_intr_index; 41220Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_record_rdt_entry: intr_index=%d " 41230Sstevel@tonic-gate "irq = 0x%x dip = 0x%p vector = 0x%x\n", intr_index, irq, 41240Sstevel@tonic-gate (void *)irqptr->airq_dip, irqptr->airq_vector)); 41250Sstevel@tonic-gate 41260Sstevel@tonic-gate if (intr_index == RESERVE_INDEX) { 41270Sstevel@tonic-gate apic_error |= APIC_ERR_INVALID_INDEX; 41280Sstevel@tonic-gate return; 41290Sstevel@tonic-gate } else if (APIC_IS_MSI_OR_MSIX_INDEX(intr_index)) { 41300Sstevel@tonic-gate return; 41310Sstevel@tonic-gate } 41320Sstevel@tonic-gate 41330Sstevel@tonic-gate vector = irqptr->airq_vector; 41340Sstevel@tonic-gate ioapicindex = irqptr->airq_ioapicindex; 41350Sstevel@tonic-gate /* Assume edge triggered by default */ 41360Sstevel@tonic-gate level = 0; 41370Sstevel@tonic-gate /* Assume active high by default */ 41380Sstevel@tonic-gate po = 0; 41390Sstevel@tonic-gate 41400Sstevel@tonic-gate if (intr_index == DEFAULT_INDEX || intr_index == FREE_INDEX) { 41410Sstevel@tonic-gate ASSERT(irq < 16); 41420Sstevel@tonic-gate if (eisa_level_intr_mask & (1 << irq)) 41430Sstevel@tonic-gate level = AV_LEVEL; 41440Sstevel@tonic-gate if (intr_index == FREE_INDEX && apic_defconf == 0) 41450Sstevel@tonic-gate apic_error |= APIC_ERR_INVALID_INDEX; 4146347Smyers } else if (intr_index == ACPI_INDEX) { 41470Sstevel@tonic-gate bus_type = irqptr->airq_iflag.bustype; 41480Sstevel@tonic-gate if (irqptr->airq_iflag.intr_el == INTR_EL_CONFORM) { 41490Sstevel@tonic-gate if (bus_type == BUS_PCI) 41500Sstevel@tonic-gate level = AV_LEVEL; 41510Sstevel@tonic-gate } else 41520Sstevel@tonic-gate level = (irqptr->airq_iflag.intr_el == INTR_EL_LEVEL) ? 41530Sstevel@tonic-gate AV_LEVEL : 0; 41540Sstevel@tonic-gate if (level && 41550Sstevel@tonic-gate ((irqptr->airq_iflag.intr_po == INTR_PO_ACTIVE_LOW) || 41560Sstevel@tonic-gate (irqptr->airq_iflag.intr_po == INTR_PO_CONFORM && 41570Sstevel@tonic-gate bus_type == BUS_PCI))) 41580Sstevel@tonic-gate po = AV_ACTIVE_LOW; 41590Sstevel@tonic-gate } else { 41600Sstevel@tonic-gate iointrp = apic_io_intrp + intr_index; 41610Sstevel@tonic-gate bus_type = apic_find_bus(iointrp->intr_busid); 41620Sstevel@tonic-gate if (iointrp->intr_el == INTR_EL_CONFORM) { 41630Sstevel@tonic-gate if ((irq < 16) && (eisa_level_intr_mask & (1 << irq))) 41640Sstevel@tonic-gate level = AV_LEVEL; 41650Sstevel@tonic-gate else if (bus_type == BUS_PCI) 41660Sstevel@tonic-gate level = AV_LEVEL; 41670Sstevel@tonic-gate } else 41680Sstevel@tonic-gate level = (iointrp->intr_el == INTR_EL_LEVEL) ? 41690Sstevel@tonic-gate AV_LEVEL : 0; 41700Sstevel@tonic-gate if (level && ((iointrp->intr_po == INTR_PO_ACTIVE_LOW) || 41710Sstevel@tonic-gate (iointrp->intr_po == INTR_PO_CONFORM && 41720Sstevel@tonic-gate bus_type == BUS_PCI))) 41730Sstevel@tonic-gate po = AV_ACTIVE_LOW; 41740Sstevel@tonic-gate } 41750Sstevel@tonic-gate if (level) 41760Sstevel@tonic-gate apic_level_intr[irq] = 1; 41770Sstevel@tonic-gate /* 41780Sstevel@tonic-gate * The 82489DX External APIC cannot do active low polarity interrupts. 41790Sstevel@tonic-gate */ 41800Sstevel@tonic-gate if (po && (apic_io_ver[ioapicindex] != IOAPIC_VER_82489DX)) 41810Sstevel@tonic-gate io_po = po; 41820Sstevel@tonic-gate else 41830Sstevel@tonic-gate io_po = 0; 41840Sstevel@tonic-gate 41850Sstevel@tonic-gate if (apic_verbose & APIC_VERBOSE_IOAPIC_FLAG) 41860Sstevel@tonic-gate printf("setio: ioapic=%x intin=%x level=%x po=%x vector=%x\n", 41870Sstevel@tonic-gate ioapicindex, irqptr->airq_intin_no, level, io_po, vector); 41880Sstevel@tonic-gate 41890Sstevel@tonic-gate irqptr->airq_rdt_entry = level|io_po|vector; 41900Sstevel@tonic-gate } 41910Sstevel@tonic-gate 41920Sstevel@tonic-gate /* 41930Sstevel@tonic-gate * Call rebind to do the actual programming. 41940Sstevel@tonic-gate */ 41950Sstevel@tonic-gate static int 41960Sstevel@tonic-gate apic_setup_io_intr(apic_irq_t *irqptr, int irq) 41970Sstevel@tonic-gate { 41980Sstevel@tonic-gate int rv; 41990Sstevel@tonic-gate 42000Sstevel@tonic-gate if (rv = apic_rebind(irqptr, apic_irq_table[irq]->airq_cpu, 1, 42010Sstevel@tonic-gate IMMEDIATE)) 42020Sstevel@tonic-gate /* CPU is not up or interrupt is disabled. Fall back to 0 */ 42030Sstevel@tonic-gate rv = apic_rebind(irqptr, 0, 1, IMMEDIATE); 42040Sstevel@tonic-gate 42050Sstevel@tonic-gate return (rv); 42060Sstevel@tonic-gate } 42070Sstevel@tonic-gate 42080Sstevel@tonic-gate /* 42090Sstevel@tonic-gate * Deferred reprogramming: Call apic_rebind to do the real work. 42100Sstevel@tonic-gate */ 42110Sstevel@tonic-gate static int 42120Sstevel@tonic-gate apic_setup_io_intr_deferred(apic_irq_t *irqptr, int irq) 42130Sstevel@tonic-gate { 42140Sstevel@tonic-gate int rv; 42150Sstevel@tonic-gate 42160Sstevel@tonic-gate if (rv = apic_rebind(irqptr, apic_irq_table[irq]->airq_cpu, 1, 42170Sstevel@tonic-gate DEFERRED)) 42180Sstevel@tonic-gate /* CPU is not up or interrupt is disabled. Fall back to 0 */ 42190Sstevel@tonic-gate rv = apic_rebind(irqptr, 0, 1, DEFERRED); 42200Sstevel@tonic-gate 42210Sstevel@tonic-gate return (rv); 42220Sstevel@tonic-gate } 42230Sstevel@tonic-gate 42240Sstevel@tonic-gate /* 42250Sstevel@tonic-gate * Bind interrupt corresponding to irq_ptr to bind_cpu. acquire_lock 42260Sstevel@tonic-gate * if false (0) means lock is already held (e.g: in rebind_all). 42270Sstevel@tonic-gate */ 42280Sstevel@tonic-gate static int 42290Sstevel@tonic-gate apic_rebind(apic_irq_t *irq_ptr, int bind_cpu, int acquire_lock, int when) 42300Sstevel@tonic-gate { 42310Sstevel@tonic-gate int intin_no; 42320Sstevel@tonic-gate volatile int32_t *ioapic; 42330Sstevel@tonic-gate uchar_t airq_temp_cpu; 42340Sstevel@tonic-gate apic_cpus_info_t *cpu_infop; 42350Sstevel@tonic-gate int iflag; 42360Sstevel@tonic-gate int which_irq = apic_vector_to_irq[irq_ptr->airq_vector]; 42370Sstevel@tonic-gate 42380Sstevel@tonic-gate intin_no = irq_ptr->airq_intin_no; 42390Sstevel@tonic-gate ioapic = apicioadr[irq_ptr->airq_ioapicindex]; 42400Sstevel@tonic-gate airq_temp_cpu = irq_ptr->airq_temp_cpu; 42410Sstevel@tonic-gate if (airq_temp_cpu != IRQ_UNINIT && airq_temp_cpu != IRQ_UNBOUND) { 42420Sstevel@tonic-gate if (airq_temp_cpu & IRQ_USER_BOUND) 42430Sstevel@tonic-gate /* Mask off high bit so it can be used as array index */ 42440Sstevel@tonic-gate airq_temp_cpu &= ~IRQ_USER_BOUND; 42450Sstevel@tonic-gate 42460Sstevel@tonic-gate ASSERT(airq_temp_cpu < apic_nproc); 42470Sstevel@tonic-gate } 42480Sstevel@tonic-gate 42490Sstevel@tonic-gate iflag = intr_clear(); 42500Sstevel@tonic-gate 42510Sstevel@tonic-gate if (acquire_lock) 42520Sstevel@tonic-gate lock_set(&apic_ioapic_lock); 42530Sstevel@tonic-gate 42540Sstevel@tonic-gate /* 42550Sstevel@tonic-gate * Can't bind to a CPU that's not online: 42560Sstevel@tonic-gate */ 42570Sstevel@tonic-gate cpu_infop = &apic_cpus[bind_cpu & ~IRQ_USER_BOUND]; 42580Sstevel@tonic-gate if (!(cpu_infop->aci_status & APIC_CPU_INTR_ENABLE)) { 42590Sstevel@tonic-gate 42600Sstevel@tonic-gate if (acquire_lock) 42610Sstevel@tonic-gate lock_clear(&apic_ioapic_lock); 42620Sstevel@tonic-gate 42630Sstevel@tonic-gate intr_restore(iflag); 42640Sstevel@tonic-gate return (1); 42650Sstevel@tonic-gate } 42660Sstevel@tonic-gate 42670Sstevel@tonic-gate /* 42680Sstevel@tonic-gate * If this is a deferred reprogramming attempt, ensure we have 42690Sstevel@tonic-gate * not been passed stale data: 42700Sstevel@tonic-gate */ 42710Sstevel@tonic-gate if ((when == DEFERRED) && 42720Sstevel@tonic-gate (apic_reprogram_info[which_irq].valid == 0)) { 42730Sstevel@tonic-gate /* stale info, so just return */ 42740Sstevel@tonic-gate if (acquire_lock) 42750Sstevel@tonic-gate lock_clear(&apic_ioapic_lock); 42760Sstevel@tonic-gate 42770Sstevel@tonic-gate intr_restore(iflag); 42780Sstevel@tonic-gate return (0); 42790Sstevel@tonic-gate } 42800Sstevel@tonic-gate 42810Sstevel@tonic-gate /* 42820Sstevel@tonic-gate * If this interrupt has been delivered to a CPU and that CPU 42830Sstevel@tonic-gate * has not handled it yet, we cannot reprogram the IOAPIC now: 42840Sstevel@tonic-gate */ 42850Sstevel@tonic-gate if (!APIC_IS_MSI_OR_MSIX_INDEX(irq_ptr->airq_mps_intr_index) && 42860Sstevel@tonic-gate apic_check_stuck_interrupt(irq_ptr, airq_temp_cpu, bind_cpu, 42870Sstevel@tonic-gate ioapic, intin_no, which_irq) != 0) { 42880Sstevel@tonic-gate 42890Sstevel@tonic-gate if (acquire_lock) 42900Sstevel@tonic-gate lock_clear(&apic_ioapic_lock); 42910Sstevel@tonic-gate 42920Sstevel@tonic-gate intr_restore(iflag); 42930Sstevel@tonic-gate return (0); 42940Sstevel@tonic-gate } 42950Sstevel@tonic-gate 42960Sstevel@tonic-gate /* 42970Sstevel@tonic-gate * NOTE: We do not unmask the RDT here, as an interrupt MAY still 42980Sstevel@tonic-gate * come in before we have a chance to reprogram it below. The 42990Sstevel@tonic-gate * reprogramming below will simultaneously change and unmask the 43000Sstevel@tonic-gate * RDT entry. 43010Sstevel@tonic-gate */ 43020Sstevel@tonic-gate 43030Sstevel@tonic-gate if ((uchar_t)bind_cpu == IRQ_UNBOUND) { 43040Sstevel@tonic-gate /* Write the RDT entry -- no specific CPU binding */ 43050Sstevel@tonic-gate WRITE_IOAPIC_RDT_ENTRY_HIGH_DWORD(ioapic, intin_no, AV_TOALL); 43060Sstevel@tonic-gate 43070Sstevel@tonic-gate if (airq_temp_cpu != IRQ_UNINIT && airq_temp_cpu != IRQ_UNBOUND) 43080Sstevel@tonic-gate apic_cpus[airq_temp_cpu].aci_temp_bound--; 43090Sstevel@tonic-gate 43100Sstevel@tonic-gate /* Write the vector, trigger, and polarity portion of the RDT */ 43110Sstevel@tonic-gate WRITE_IOAPIC_RDT_ENTRY_LOW_DWORD(ioapic, intin_no, 43120Sstevel@tonic-gate AV_LDEST | AV_LOPRI | irq_ptr->airq_rdt_entry); 43130Sstevel@tonic-gate if (acquire_lock) 43140Sstevel@tonic-gate lock_clear(&apic_ioapic_lock); 43150Sstevel@tonic-gate irq_ptr->airq_temp_cpu = IRQ_UNBOUND; 43160Sstevel@tonic-gate intr_restore(iflag); 43170Sstevel@tonic-gate return (0); 43180Sstevel@tonic-gate } 43190Sstevel@tonic-gate 43200Sstevel@tonic-gate if (bind_cpu & IRQ_USER_BOUND) { 43210Sstevel@tonic-gate cpu_infop->aci_bound++; 43220Sstevel@tonic-gate } else { 43230Sstevel@tonic-gate cpu_infop->aci_temp_bound++; 43240Sstevel@tonic-gate } 43250Sstevel@tonic-gate ASSERT((bind_cpu & ~IRQ_USER_BOUND) < apic_nproc); 43260Sstevel@tonic-gate if (!APIC_IS_MSI_OR_MSIX_INDEX(irq_ptr->airq_mps_intr_index)) { 43270Sstevel@tonic-gate /* Write the RDT entry -- bind to a specific CPU: */ 43280Sstevel@tonic-gate WRITE_IOAPIC_RDT_ENTRY_HIGH_DWORD(ioapic, intin_no, 43290Sstevel@tonic-gate cpu_infop->aci_local_id << APIC_ID_BIT_OFFSET); 43300Sstevel@tonic-gate } 43310Sstevel@tonic-gate if ((airq_temp_cpu != IRQ_UNBOUND) && (airq_temp_cpu != IRQ_UNINIT)) { 43320Sstevel@tonic-gate apic_cpus[airq_temp_cpu].aci_temp_bound--; 43330Sstevel@tonic-gate } 43340Sstevel@tonic-gate if (!APIC_IS_MSI_OR_MSIX_INDEX(irq_ptr->airq_mps_intr_index)) { 43350Sstevel@tonic-gate /* Write the vector, trigger, and polarity portion of the RDT */ 43360Sstevel@tonic-gate WRITE_IOAPIC_RDT_ENTRY_LOW_DWORD(ioapic, intin_no, 43370Sstevel@tonic-gate AV_PDEST | AV_FIXED | irq_ptr->airq_rdt_entry); 43380Sstevel@tonic-gate } else { 4339916Sschwartz int type = (irq_ptr->airq_mps_intr_index == MSI_INDEX) ? 4340916Sschwartz DDI_INTR_TYPE_MSI : DDI_INTR_TYPE_MSIX; 43411997Sanish (void) apic_pci_msi_disable_mode(irq_ptr->airq_dip, type, 4342916Sschwartz irq_ptr->airq_ioapicindex); 43430Sstevel@tonic-gate if (irq_ptr->airq_ioapicindex == irq_ptr->airq_origirq) { 43440Sstevel@tonic-gate /* first one */ 43450Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_rebind: call " 43460Sstevel@tonic-gate "apic_pci_msi_enable_vector\n")); 4347916Sschwartz if (apic_pci_msi_enable_vector(irq_ptr->airq_dip, type, 4348916Sschwartz which_irq, irq_ptr->airq_vector, 4349916Sschwartz irq_ptr->airq_intin_no, 43500Sstevel@tonic-gate cpu_infop->aci_local_id) != PSM_SUCCESS) { 43510Sstevel@tonic-gate cmn_err(CE_WARN, "pcplusmp: " 43520Sstevel@tonic-gate "apic_pci_msi_enable_vector " 43530Sstevel@tonic-gate "returned PSM_FAILURE"); 43540Sstevel@tonic-gate } 43550Sstevel@tonic-gate } 43560Sstevel@tonic-gate if ((irq_ptr->airq_ioapicindex + irq_ptr->airq_intin_no - 1) == 43570Sstevel@tonic-gate irq_ptr->airq_origirq) { /* last one */ 43580Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "apic_rebind: call " 43590Sstevel@tonic-gate "pci_msi_enable_mode\n")); 43601997Sanish if (apic_pci_msi_enable_mode(irq_ptr->airq_dip, 43611997Sanish type, which_irq) != PSM_SUCCESS) { 43620Sstevel@tonic-gate DDI_INTR_IMPLDBG((CE_CONT, "pcplusmp: " 43630Sstevel@tonic-gate "pci_msi_enable failed\n")); 43641997Sanish (void) apic_pci_msi_unconfigure( 43651997Sanish irq_ptr->airq_dip, type, which_irq); 43660Sstevel@tonic-gate } 43670Sstevel@tonic-gate } 43680Sstevel@tonic-gate } 43690Sstevel@tonic-gate if (acquire_lock) 43700Sstevel@tonic-gate lock_clear(&apic_ioapic_lock); 43710Sstevel@tonic-gate irq_ptr->airq_temp_cpu = (uchar_t)bind_cpu; 43720Sstevel@tonic-gate apic_redist_cpu_skip &= ~(1 << (bind_cpu & ~IRQ_USER_BOUND)); 43730Sstevel@tonic-gate intr_restore(iflag); 43740Sstevel@tonic-gate return (0); 43750Sstevel@tonic-gate } 43760Sstevel@tonic-gate 43770Sstevel@tonic-gate /* 43780Sstevel@tonic-gate * Checks to see if the IOAPIC interrupt entry specified has its Remote IRR 43790Sstevel@tonic-gate * bit set. Sets up a timeout to perform the reprogramming at a later time 43800Sstevel@tonic-gate * if it cannot wait for the Remote IRR bit to clear (or if waiting did not 43810Sstevel@tonic-gate * result in the bit's clearing). 43820Sstevel@tonic-gate * 43830Sstevel@tonic-gate * This function will mask the RDT entry if the Remote IRR bit is set. 43840Sstevel@tonic-gate * 43850Sstevel@tonic-gate * Returns non-zero if the caller should defer IOAPIC reprogramming. 43860Sstevel@tonic-gate */ 43870Sstevel@tonic-gate static int 43880Sstevel@tonic-gate apic_check_stuck_interrupt(apic_irq_t *irq_ptr, int old_bind_cpu, 43890Sstevel@tonic-gate int new_bind_cpu, volatile int32_t *ioapic, int intin_no, int which_irq) 43900Sstevel@tonic-gate { 43910Sstevel@tonic-gate int32_t rdt_entry; 43920Sstevel@tonic-gate int waited; 43930Sstevel@tonic-gate 43940Sstevel@tonic-gate /* Mask the RDT entry, but only if it's a level-triggered interrupt */ 43950Sstevel@tonic-gate rdt_entry = READ_IOAPIC_RDT_ENTRY_LOW_DWORD(ioapic, intin_no); 43960Sstevel@tonic-gate if ((rdt_entry & (AV_LEVEL|AV_MASK)) == AV_LEVEL) { 43970Sstevel@tonic-gate 43980Sstevel@tonic-gate /* Mask it */ 43990Sstevel@tonic-gate WRITE_IOAPIC_RDT_ENTRY_LOW_DWORD(ioapic, intin_no, 44000Sstevel@tonic-gate AV_MASK | rdt_entry); 44010Sstevel@tonic-gate } 44020Sstevel@tonic-gate 44030Sstevel@tonic-gate /* 44040Sstevel@tonic-gate * Wait for the delivery pending bit to clear. 44050Sstevel@tonic-gate */ 44060Sstevel@tonic-gate if ((READ_IOAPIC_RDT_ENTRY_LOW_DWORD(ioapic, intin_no) & 44070Sstevel@tonic-gate (AV_LEVEL|AV_PENDING)) == (AV_LEVEL|AV_PENDING)) { 44080Sstevel@tonic-gate 44090Sstevel@tonic-gate /* 44100Sstevel@tonic-gate * If we're still waiting on the delivery of this interrupt, 44110Sstevel@tonic-gate * continue to wait here until it is delivered (this should be 44120Sstevel@tonic-gate * a very small amount of time, but include a timeout just in 44130Sstevel@tonic-gate * case). 44140Sstevel@tonic-gate */ 44150Sstevel@tonic-gate for (waited = 0; waited < apic_max_usecs_clear_pending; 44160Sstevel@tonic-gate waited += APIC_USECS_PER_WAIT_INTERVAL) { 44170Sstevel@tonic-gate if ((READ_IOAPIC_RDT_ENTRY_LOW_DWORD(ioapic, intin_no) 44180Sstevel@tonic-gate & AV_PENDING) == 0) { 44190Sstevel@tonic-gate break; 44200Sstevel@tonic-gate } 44210Sstevel@tonic-gate drv_usecwait(APIC_USECS_PER_WAIT_INTERVAL); 44220Sstevel@tonic-gate } 44230Sstevel@tonic-gate 44240Sstevel@tonic-gate if ((READ_IOAPIC_RDT_ENTRY_LOW_DWORD(ioapic, intin_no) & 44250Sstevel@tonic-gate AV_PENDING) != 0) { 44260Sstevel@tonic-gate cmn_err(CE_WARN, "!IOAPIC %d intin %d: Could not " 44270Sstevel@tonic-gate "deliver interrupt to local APIC within " 44280Sstevel@tonic-gate "%d usecs.", irq_ptr->airq_ioapicindex, 44290Sstevel@tonic-gate irq_ptr->airq_intin_no, 44300Sstevel@tonic-gate apic_max_usecs_clear_pending); 44310Sstevel@tonic-gate } 44320Sstevel@tonic-gate } 44330Sstevel@tonic-gate 44340Sstevel@tonic-gate /* 44350Sstevel@tonic-gate * If the remote IRR bit is set, then the interrupt has been sent 44360Sstevel@tonic-gate * to a CPU for processing. We have no choice but to wait for 44370Sstevel@tonic-gate * that CPU to process the interrupt, at which point the remote IRR 44380Sstevel@tonic-gate * bit will be cleared. 44390Sstevel@tonic-gate */ 44400Sstevel@tonic-gate if ((READ_IOAPIC_RDT_ENTRY_LOW_DWORD(ioapic, intin_no) & 44410Sstevel@tonic-gate (AV_LEVEL|AV_REMOTE_IRR)) == (AV_LEVEL|AV_REMOTE_IRR)) { 44420Sstevel@tonic-gate 44430Sstevel@tonic-gate /* 44440Sstevel@tonic-gate * If the CPU that this RDT is bound to is NOT the current 44450Sstevel@tonic-gate * CPU, wait until that CPU handles the interrupt and ACKs 44460Sstevel@tonic-gate * it. If this interrupt is not bound to any CPU (that is, 44470Sstevel@tonic-gate * if it's bound to the logical destination of "anyone"), it 44480Sstevel@tonic-gate * may have been delivered to the current CPU so handle that 44490Sstevel@tonic-gate * case by deferring the reprogramming (below). 44500Sstevel@tonic-gate */ 44510Sstevel@tonic-gate kpreempt_disable(); 44520Sstevel@tonic-gate if ((old_bind_cpu != IRQ_UNBOUND) && 44530Sstevel@tonic-gate (old_bind_cpu != IRQ_UNINIT) && 44540Sstevel@tonic-gate (old_bind_cpu != psm_get_cpu_id())) { 44550Sstevel@tonic-gate for (waited = 0; waited < apic_max_usecs_clear_pending; 44560Sstevel@tonic-gate waited += APIC_USECS_PER_WAIT_INTERVAL) { 44570Sstevel@tonic-gate if ((READ_IOAPIC_RDT_ENTRY_LOW_DWORD(ioapic, 44580Sstevel@tonic-gate intin_no) & AV_REMOTE_IRR) == 0) { 44590Sstevel@tonic-gate 44600Sstevel@tonic-gate /* Clear the reprogramming state: */ 44610Sstevel@tonic-gate lock_set(&apic_ioapic_reprogram_lock); 44620Sstevel@tonic-gate 44630Sstevel@tonic-gate apic_reprogram_info[which_irq].valid 44640Sstevel@tonic-gate = 0; 44650Sstevel@tonic-gate apic_reprogram_info[which_irq].bindcpu 44660Sstevel@tonic-gate = 0; 44670Sstevel@tonic-gate apic_reprogram_info[which_irq].timeouts 44680Sstevel@tonic-gate = 0; 44690Sstevel@tonic-gate 44700Sstevel@tonic-gate lock_clear(&apic_ioapic_reprogram_lock); 44710Sstevel@tonic-gate 44720Sstevel@tonic-gate /* Remote IRR has cleared! */ 44730Sstevel@tonic-gate kpreempt_enable(); 44740Sstevel@tonic-gate return (0); 44750Sstevel@tonic-gate } 44760Sstevel@tonic-gate drv_usecwait(APIC_USECS_PER_WAIT_INTERVAL); 44770Sstevel@tonic-gate } 44780Sstevel@tonic-gate } 44790Sstevel@tonic-gate kpreempt_enable(); 44800Sstevel@tonic-gate 44810Sstevel@tonic-gate /* 44820Sstevel@tonic-gate * If we waited and the Remote IRR bit is still not cleared, 44830Sstevel@tonic-gate * AND if we've invoked the timeout APIC_REPROGRAM_MAX_TIMEOUTS 44840Sstevel@tonic-gate * times for this interrupt, try the last-ditch workarounds: 44850Sstevel@tonic-gate */ 44860Sstevel@tonic-gate if (apic_reprogram_info[which_irq].timeouts >= 44870Sstevel@tonic-gate APIC_REPROGRAM_MAX_TIMEOUTS) { 44880Sstevel@tonic-gate 44890Sstevel@tonic-gate if ((READ_IOAPIC_RDT_ENTRY_LOW_DWORD(ioapic, intin_no) 44900Sstevel@tonic-gate & AV_REMOTE_IRR) != 0) { 44910Sstevel@tonic-gate /* 44920Sstevel@tonic-gate * Trying to clear the bit through normal 44930Sstevel@tonic-gate * channels has failed. So as a last-ditch 44940Sstevel@tonic-gate * effort, try to set the trigger mode to 44950Sstevel@tonic-gate * edge, then to level. This has been 44960Sstevel@tonic-gate * observed to work on many systems. 44970Sstevel@tonic-gate */ 44980Sstevel@tonic-gate WRITE_IOAPIC_RDT_ENTRY_LOW_DWORD(ioapic, 44990Sstevel@tonic-gate intin_no, 45000Sstevel@tonic-gate READ_IOAPIC_RDT_ENTRY_LOW_DWORD(ioapic, 45010Sstevel@tonic-gate intin_no) & ~AV_LEVEL); 45020Sstevel@tonic-gate 45030Sstevel@tonic-gate WRITE_IOAPIC_RDT_ENTRY_LOW_DWORD(ioapic, 45040Sstevel@tonic-gate intin_no, 45050Sstevel@tonic-gate READ_IOAPIC_RDT_ENTRY_LOW_DWORD(ioapic, 45060Sstevel@tonic-gate intin_no) | AV_LEVEL); 45070Sstevel@tonic-gate 45080Sstevel@tonic-gate /* 45090Sstevel@tonic-gate * If the bit's STILL set, declare total and 45100Sstevel@tonic-gate * utter failure 45110Sstevel@tonic-gate */ 45120Sstevel@tonic-gate if ((READ_IOAPIC_RDT_ENTRY_LOW_DWORD(ioapic, 45130Sstevel@tonic-gate intin_no) & AV_REMOTE_IRR) != 0) { 45140Sstevel@tonic-gate cmn_err(CE_WARN, "!IOAPIC %d intin %d: " 45150Sstevel@tonic-gate "Remote IRR failed to reset " 45160Sstevel@tonic-gate "within %d usecs. Interrupts to " 45170Sstevel@tonic-gate "this pin may cease to function.", 45180Sstevel@tonic-gate irq_ptr->airq_ioapicindex, 45190Sstevel@tonic-gate irq_ptr->airq_intin_no, 45200Sstevel@tonic-gate apic_max_usecs_clear_pending); 45210Sstevel@tonic-gate } 45220Sstevel@tonic-gate } 45230Sstevel@tonic-gate /* Clear the reprogramming state: */ 45240Sstevel@tonic-gate lock_set(&apic_ioapic_reprogram_lock); 45250Sstevel@tonic-gate 45260Sstevel@tonic-gate apic_reprogram_info[which_irq].valid = 0; 45270Sstevel@tonic-gate apic_reprogram_info[which_irq].bindcpu = 0; 45280Sstevel@tonic-gate apic_reprogram_info[which_irq].timeouts = 0; 45290Sstevel@tonic-gate 45300Sstevel@tonic-gate lock_clear(&apic_ioapic_reprogram_lock); 45310Sstevel@tonic-gate } else { 45320Sstevel@tonic-gate #ifdef DEBUG 45330Sstevel@tonic-gate cmn_err(CE_WARN, "Deferring reprogramming of irq %d", 45340Sstevel@tonic-gate which_irq); 45350Sstevel@tonic-gate #endif /* DEBUG */ 45360Sstevel@tonic-gate /* 45370Sstevel@tonic-gate * If waiting for the Remote IRR bit (above) didn't 45380Sstevel@tonic-gate * allow it to clear, defer the reprogramming: 45390Sstevel@tonic-gate */ 45400Sstevel@tonic-gate lock_set(&apic_ioapic_reprogram_lock); 45410Sstevel@tonic-gate 45420Sstevel@tonic-gate apic_reprogram_info[which_irq].valid = 1; 45430Sstevel@tonic-gate apic_reprogram_info[which_irq].bindcpu = new_bind_cpu; 45440Sstevel@tonic-gate apic_reprogram_info[which_irq].timeouts++; 45450Sstevel@tonic-gate 45460Sstevel@tonic-gate lock_clear(&apic_ioapic_reprogram_lock); 45470Sstevel@tonic-gate 45480Sstevel@tonic-gate /* Fire up a timeout to handle this later */ 45490Sstevel@tonic-gate (void) timeout(apic_reprogram_timeout_handler, 45500Sstevel@tonic-gate (void *) 0, 45510Sstevel@tonic-gate drv_usectohz(APIC_REPROGRAM_TIMEOUT_DELAY)); 45520Sstevel@tonic-gate 45530Sstevel@tonic-gate /* Inform caller to defer IOAPIC programming: */ 45540Sstevel@tonic-gate return (1); 45550Sstevel@tonic-gate } 45560Sstevel@tonic-gate } 45570Sstevel@tonic-gate return (0); 45580Sstevel@tonic-gate } 45590Sstevel@tonic-gate 45600Sstevel@tonic-gate /* 45610Sstevel@tonic-gate * Timeout handler that performs the APIC reprogramming 45620Sstevel@tonic-gate */ 45630Sstevel@tonic-gate /*ARGSUSED*/ 45640Sstevel@tonic-gate static void 45650Sstevel@tonic-gate apic_reprogram_timeout_handler(void *arg) 45660Sstevel@tonic-gate { 45670Sstevel@tonic-gate /*LINTED: set but not used in function*/ 45680Sstevel@tonic-gate int i, result; 45690Sstevel@tonic-gate 45700Sstevel@tonic-gate /* Serialize access to this function */ 45710Sstevel@tonic-gate mutex_enter(&apic_reprogram_timeout_mutex); 45720Sstevel@tonic-gate 45730Sstevel@tonic-gate /* 45740Sstevel@tonic-gate * For each entry in the reprogramming state that's valid, 45750Sstevel@tonic-gate * try the reprogramming again: 45760Sstevel@tonic-gate */ 45770Sstevel@tonic-gate for (i = 0; i < APIC_MAX_VECTOR; i++) { 45780Sstevel@tonic-gate if (apic_reprogram_info[i].valid == 0) 45790Sstevel@tonic-gate continue; 45800Sstevel@tonic-gate /* 45810Sstevel@tonic-gate * Though we can't really do anything about errors 45820Sstevel@tonic-gate * at this point, keep track of them for reporting. 45830Sstevel@tonic-gate * Note that it is very possible for apic_setup_io_intr 45840Sstevel@tonic-gate * to re-register this very timeout if the Remote IRR bit 45850Sstevel@tonic-gate * has not yet cleared. 45860Sstevel@tonic-gate */ 45870Sstevel@tonic-gate result = apic_setup_io_intr_deferred(apic_irq_table[i], i); 45880Sstevel@tonic-gate 45890Sstevel@tonic-gate #ifdef DEBUG 45900Sstevel@tonic-gate if (result) 45910Sstevel@tonic-gate cmn_err(CE_WARN, "apic_reprogram_timeout: " 45920Sstevel@tonic-gate "apic_setup_io_intr returned nonzero for " 45930Sstevel@tonic-gate "irq=%d!", i); 45940Sstevel@tonic-gate #endif /* DEBUG */ 45950Sstevel@tonic-gate } 45960Sstevel@tonic-gate 45970Sstevel@tonic-gate mutex_exit(&apic_reprogram_timeout_mutex); 45980Sstevel@tonic-gate } 45990Sstevel@tonic-gate 46000Sstevel@tonic-gate 46010Sstevel@tonic-gate /* 46020Sstevel@tonic-gate * Called to migrate all interrupts at an irq to another cpu. safe 46030Sstevel@tonic-gate * if true means we are not being called from an interrupt 46040Sstevel@tonic-gate * context and hence it is safe to do a lock_set. If false 46050Sstevel@tonic-gate * do only a lock_try and return failure ( non 0 ) if we cannot get it 46060Sstevel@tonic-gate */ 4607916Sschwartz int 46080Sstevel@tonic-gate apic_rebind_all(apic_irq_t *irq_ptr, int bind_cpu, int safe) 46090Sstevel@tonic-gate { 46100Sstevel@tonic-gate apic_irq_t *irqptr = irq_ptr; 46110Sstevel@tonic-gate int retval = 0; 46120Sstevel@tonic-gate int iflag; 46130Sstevel@tonic-gate 46140Sstevel@tonic-gate iflag = intr_clear(); 46150Sstevel@tonic-gate if (!safe) { 46160Sstevel@tonic-gate if (lock_try(&apic_ioapic_lock) == 0) { 46170Sstevel@tonic-gate intr_restore(iflag); 46180Sstevel@tonic-gate return (1); 46190Sstevel@tonic-gate } 46200Sstevel@tonic-gate } else 46210Sstevel@tonic-gate lock_set(&apic_ioapic_lock); 46220Sstevel@tonic-gate 46230Sstevel@tonic-gate while (irqptr) { 46240Sstevel@tonic-gate if (irqptr->airq_temp_cpu != IRQ_UNINIT) 46250Sstevel@tonic-gate retval |= apic_rebind(irqptr, bind_cpu, 0, IMMEDIATE); 46260Sstevel@tonic-gate irqptr = irqptr->airq_next; 46270Sstevel@tonic-gate } 46280Sstevel@tonic-gate lock_clear(&apic_ioapic_lock); 46290Sstevel@tonic-gate intr_restore(iflag); 46300Sstevel@tonic-gate return (retval); 46310Sstevel@tonic-gate } 46320Sstevel@tonic-gate 46330Sstevel@tonic-gate /* 46340Sstevel@tonic-gate * apic_intr_redistribute does all the messy computations for identifying 46350Sstevel@tonic-gate * which interrupt to move to which CPU. Currently we do just one interrupt 46360Sstevel@tonic-gate * at a time. This reduces the time we spent doing all this within clock 46370Sstevel@tonic-gate * interrupt. When it is done in idle, we could do more than 1. 46380Sstevel@tonic-gate * First we find the most busy and the most free CPU (time in ISR only) 46390Sstevel@tonic-gate * skipping those CPUs that has been identified as being ineligible (cpu_skip) 46400Sstevel@tonic-gate * Then we look for IRQs which are closest to the difference between the 46410Sstevel@tonic-gate * most busy CPU and the average ISR load. We try to find one whose load 46420Sstevel@tonic-gate * is less than difference.If none exists, then we chose one larger than the 46430Sstevel@tonic-gate * difference, provided it does not make the most idle CPU worse than the 46440Sstevel@tonic-gate * most busy one. In the end, we clear all the busy fields for CPUs. For 46450Sstevel@tonic-gate * IRQs, they are cleared as they are scanned. 46460Sstevel@tonic-gate */ 46470Sstevel@tonic-gate static void 46480Sstevel@tonic-gate apic_intr_redistribute() 46490Sstevel@tonic-gate { 46500Sstevel@tonic-gate int busiest_cpu, most_free_cpu; 46510Sstevel@tonic-gate int cpu_free, cpu_busy, max_busy, min_busy; 46520Sstevel@tonic-gate int min_free, diff; 46530Sstevel@tonic-gate int average_busy, cpus_online; 46540Sstevel@tonic-gate int i, busy; 46550Sstevel@tonic-gate apic_cpus_info_t *cpu_infop; 46560Sstevel@tonic-gate apic_irq_t *min_busy_irq = NULL; 46570Sstevel@tonic-gate apic_irq_t *max_busy_irq = NULL; 46580Sstevel@tonic-gate 46590Sstevel@tonic-gate busiest_cpu = most_free_cpu = -1; 46600Sstevel@tonic-gate cpu_free = cpu_busy = max_busy = average_busy = 0; 46610Sstevel@tonic-gate min_free = apic_sample_factor_redistribution; 46620Sstevel@tonic-gate cpus_online = 0; 46630Sstevel@tonic-gate /* 46640Sstevel@tonic-gate * Below we will check for CPU_INTR_ENABLE, bound, temp_bound, temp_cpu 46650Sstevel@tonic-gate * without ioapic_lock. That is OK as we are just doing statistical 46660Sstevel@tonic-gate * sampling anyway and any inaccuracy now will get corrected next time 46670Sstevel@tonic-gate * The call to rebind which actually changes things will make sure 46680Sstevel@tonic-gate * we are consistent. 46690Sstevel@tonic-gate */ 46700Sstevel@tonic-gate for (i = 0; i < apic_nproc; i++) { 46710Sstevel@tonic-gate if (!(apic_redist_cpu_skip & (1 << i)) && 46720Sstevel@tonic-gate (apic_cpus[i].aci_status & APIC_CPU_INTR_ENABLE)) { 46730Sstevel@tonic-gate 46740Sstevel@tonic-gate cpu_infop = &apic_cpus[i]; 46750Sstevel@tonic-gate /* 46760Sstevel@tonic-gate * If no unbound interrupts or only 1 total on this 46770Sstevel@tonic-gate * CPU, skip 46780Sstevel@tonic-gate */ 46790Sstevel@tonic-gate if (!cpu_infop->aci_temp_bound || 46800Sstevel@tonic-gate (cpu_infop->aci_bound + cpu_infop->aci_temp_bound) 46810Sstevel@tonic-gate == 1) { 46820Sstevel@tonic-gate apic_redist_cpu_skip |= 1 << i; 46830Sstevel@tonic-gate continue; 46840Sstevel@tonic-gate } 46850Sstevel@tonic-gate 46860Sstevel@tonic-gate busy = cpu_infop->aci_busy; 46870Sstevel@tonic-gate average_busy += busy; 46880Sstevel@tonic-gate cpus_online++; 46890Sstevel@tonic-gate if (max_busy < busy) { 46900Sstevel@tonic-gate max_busy = busy; 46910Sstevel@tonic-gate busiest_cpu = i; 46920Sstevel@tonic-gate } 46930Sstevel@tonic-gate if (min_free > busy) { 46940Sstevel@tonic-gate min_free = busy; 46950Sstevel@tonic-gate most_free_cpu = i; 46960Sstevel@tonic-gate } 46970Sstevel@tonic-gate if (busy > apic_int_busy_mark) { 46980Sstevel@tonic-gate cpu_busy |= 1 << i; 46990Sstevel@tonic-gate } else { 47000Sstevel@tonic-gate if (busy < apic_int_free_mark) 47010Sstevel@tonic-gate cpu_free |= 1 << i; 47020Sstevel@tonic-gate } 47030Sstevel@tonic-gate } 47040Sstevel@tonic-gate } 47050Sstevel@tonic-gate if ((cpu_busy && cpu_free) || 47060Sstevel@tonic-gate (max_busy >= (min_free + apic_diff_for_redistribution))) { 47070Sstevel@tonic-gate 47080Sstevel@tonic-gate apic_num_imbalance++; 47090Sstevel@tonic-gate #ifdef DEBUG 47100Sstevel@tonic-gate if (apic_verbose & APIC_VERBOSE_IOAPIC_FLAG) { 47110Sstevel@tonic-gate prom_printf( 47120Sstevel@tonic-gate "redistribute busy=%x free=%x max=%x min=%x", 47130Sstevel@tonic-gate cpu_busy, cpu_free, max_busy, min_free); 47140Sstevel@tonic-gate } 47150Sstevel@tonic-gate #endif /* DEBUG */ 47160Sstevel@tonic-gate 47170Sstevel@tonic-gate 47180Sstevel@tonic-gate average_busy /= cpus_online; 47190Sstevel@tonic-gate 47200Sstevel@tonic-gate diff = max_busy - average_busy; 47210Sstevel@tonic-gate min_busy = max_busy; /* start with the max possible value */ 47220Sstevel@tonic-gate max_busy = 0; 47230Sstevel@tonic-gate min_busy_irq = max_busy_irq = NULL; 47240Sstevel@tonic-gate i = apic_min_device_irq; 47250Sstevel@tonic-gate for (; i < apic_max_device_irq; i++) { 47260Sstevel@tonic-gate apic_irq_t *irq_ptr; 47270Sstevel@tonic-gate /* Change to linked list per CPU ? */ 47280Sstevel@tonic-gate if ((irq_ptr = apic_irq_table[i]) == NULL) 47290Sstevel@tonic-gate continue; 47300Sstevel@tonic-gate /* Check for irq_busy & decide which one to move */ 47310Sstevel@tonic-gate /* Also zero them for next round */ 47320Sstevel@tonic-gate if ((irq_ptr->airq_temp_cpu == busiest_cpu) && 47330Sstevel@tonic-gate irq_ptr->airq_busy) { 47340Sstevel@tonic-gate if (irq_ptr->airq_busy < diff) { 47350Sstevel@tonic-gate /* 47360Sstevel@tonic-gate * Check for least busy CPU, 47370Sstevel@tonic-gate * best fit or what ? 47380Sstevel@tonic-gate */ 47390Sstevel@tonic-gate if (max_busy < irq_ptr->airq_busy) { 47400Sstevel@tonic-gate /* 47410Sstevel@tonic-gate * Most busy within the 47420Sstevel@tonic-gate * required differential 47430Sstevel@tonic-gate */ 47440Sstevel@tonic-gate max_busy = irq_ptr->airq_busy; 47450Sstevel@tonic-gate max_busy_irq = irq_ptr; 47460Sstevel@tonic-gate } 47470Sstevel@tonic-gate } else { 47480Sstevel@tonic-gate if (min_busy > irq_ptr->airq_busy) { 47490Sstevel@tonic-gate /* 47500Sstevel@tonic-gate * least busy, but more than 47510Sstevel@tonic-gate * the reqd diff 47520Sstevel@tonic-gate */ 47530Sstevel@tonic-gate if (min_busy < 47540Sstevel@tonic-gate (diff + average_busy - 47550Sstevel@tonic-gate min_free)) { 47560Sstevel@tonic-gate /* 47570Sstevel@tonic-gate * Making sure new cpu 47580Sstevel@tonic-gate * will not end up 47590Sstevel@tonic-gate * worse 47600Sstevel@tonic-gate */ 47610Sstevel@tonic-gate min_busy = 47620Sstevel@tonic-gate irq_ptr->airq_busy; 47630Sstevel@tonic-gate 47640Sstevel@tonic-gate min_busy_irq = irq_ptr; 47650Sstevel@tonic-gate } 47660Sstevel@tonic-gate } 47670Sstevel@tonic-gate } 47680Sstevel@tonic-gate } 47690Sstevel@tonic-gate irq_ptr->airq_busy = 0; 47700Sstevel@tonic-gate } 47710Sstevel@tonic-gate 47720Sstevel@tonic-gate if (max_busy_irq != NULL) { 47730Sstevel@tonic-gate #ifdef DEBUG 47740Sstevel@tonic-gate if (apic_verbose & APIC_VERBOSE_IOAPIC_FLAG) { 47750Sstevel@tonic-gate prom_printf("rebinding %x to %x", 47760Sstevel@tonic-gate max_busy_irq->airq_vector, most_free_cpu); 47770Sstevel@tonic-gate } 47780Sstevel@tonic-gate #endif /* DEBUG */ 47790Sstevel@tonic-gate if (apic_rebind_all(max_busy_irq, most_free_cpu, 0) 47800Sstevel@tonic-gate == 0) 47810Sstevel@tonic-gate /* Make change permenant */ 47820Sstevel@tonic-gate max_busy_irq->airq_cpu = (uchar_t)most_free_cpu; 47830Sstevel@tonic-gate } else if (min_busy_irq != NULL) { 47840Sstevel@tonic-gate #ifdef DEBUG 47850Sstevel@tonic-gate if (apic_verbose & APIC_VERBOSE_IOAPIC_FLAG) { 47860Sstevel@tonic-gate prom_printf("rebinding %x to %x", 47870Sstevel@tonic-gate min_busy_irq->airq_vector, most_free_cpu); 47880Sstevel@tonic-gate } 47890Sstevel@tonic-gate #endif /* DEBUG */ 47900Sstevel@tonic-gate 47910Sstevel@tonic-gate if (apic_rebind_all(min_busy_irq, most_free_cpu, 0) == 47920Sstevel@tonic-gate 0) 47930Sstevel@tonic-gate /* Make change permenant */ 47940Sstevel@tonic-gate min_busy_irq->airq_cpu = (uchar_t)most_free_cpu; 47950Sstevel@tonic-gate } else { 47960Sstevel@tonic-gate if (cpu_busy != (1 << busiest_cpu)) { 47970Sstevel@tonic-gate apic_redist_cpu_skip |= 1 << busiest_cpu; 47980Sstevel@tonic-gate /* 47990Sstevel@tonic-gate * We leave cpu_skip set so that next time we 48000Sstevel@tonic-gate * can choose another cpu 48010Sstevel@tonic-gate */ 48020Sstevel@tonic-gate } 48030Sstevel@tonic-gate } 48040Sstevel@tonic-gate apic_num_rebind++; 48050Sstevel@tonic-gate } else { 48060Sstevel@tonic-gate /* 48070Sstevel@tonic-gate * found nothing. Could be that we skipped over valid CPUs 48080Sstevel@tonic-gate * or we have balanced everything. If we had a variable 48090Sstevel@tonic-gate * ticks_for_redistribution, it could be increased here. 48100Sstevel@tonic-gate * apic_int_busy, int_free etc would also need to be 48110Sstevel@tonic-gate * changed. 48120Sstevel@tonic-gate */ 48130Sstevel@tonic-gate if (apic_redist_cpu_skip) 48140Sstevel@tonic-gate apic_redist_cpu_skip = 0; 48150Sstevel@tonic-gate } 48160Sstevel@tonic-gate for (i = 0; i < apic_nproc; i++) { 48170Sstevel@tonic-gate apic_cpus[i].aci_busy = 0; 48180Sstevel@tonic-gate } 48190Sstevel@tonic-gate } 48200Sstevel@tonic-gate 48210Sstevel@tonic-gate static void 48220Sstevel@tonic-gate apic_cleanup_busy() 48230Sstevel@tonic-gate { 48240Sstevel@tonic-gate int i; 48250Sstevel@tonic-gate apic_irq_t *irq_ptr; 48260Sstevel@tonic-gate 48270Sstevel@tonic-gate for (i = 0; i < apic_nproc; i++) { 48280Sstevel@tonic-gate apic_cpus[i].aci_busy = 0; 48290Sstevel@tonic-gate } 48300Sstevel@tonic-gate 48310Sstevel@tonic-gate for (i = apic_min_device_irq; i < apic_max_device_irq; i++) { 48320Sstevel@tonic-gate if ((irq_ptr = apic_irq_table[i]) != NULL) 48330Sstevel@tonic-gate irq_ptr->airq_busy = 0; 48340Sstevel@tonic-gate } 48350Sstevel@tonic-gate apic_skipped_redistribute = 0; 48360Sstevel@tonic-gate } 48370Sstevel@tonic-gate 48380Sstevel@tonic-gate 48390Sstevel@tonic-gate /* 48400Sstevel@tonic-gate * This function will reprogram the timer. 48410Sstevel@tonic-gate * 48420Sstevel@tonic-gate * When in oneshot mode the argument is the absolute time in future to 48430Sstevel@tonic-gate * generate the interrupt at. 48440Sstevel@tonic-gate * 48450Sstevel@tonic-gate * When in periodic mode, the argument is the interval at which the 48460Sstevel@tonic-gate * interrupts should be generated. There is no need to support the periodic 48470Sstevel@tonic-gate * mode timer change at this time. 48480Sstevel@tonic-gate */ 48490Sstevel@tonic-gate static void 48500Sstevel@tonic-gate apic_timer_reprogram(hrtime_t time) 48510Sstevel@tonic-gate { 48520Sstevel@tonic-gate hrtime_t now; 48530Sstevel@tonic-gate uint_t ticks; 48540Sstevel@tonic-gate 48550Sstevel@tonic-gate /* 48560Sstevel@tonic-gate * We should be called from high PIL context (CBE_HIGH_PIL), 48570Sstevel@tonic-gate * so kpreempt is disabled. 48580Sstevel@tonic-gate */ 48590Sstevel@tonic-gate 48600Sstevel@tonic-gate if (!apic_oneshot) { 48610Sstevel@tonic-gate /* time is the interval for periodic mode */ 48620Sstevel@tonic-gate ticks = (uint_t)((time) / apic_nsec_per_tick); 48630Sstevel@tonic-gate } else { 48640Sstevel@tonic-gate /* one shot mode */ 48650Sstevel@tonic-gate 48660Sstevel@tonic-gate now = gethrtime(); 48670Sstevel@tonic-gate 48680Sstevel@tonic-gate if (time <= now) { 48690Sstevel@tonic-gate /* 48700Sstevel@tonic-gate * requested to generate an interrupt in the past 48710Sstevel@tonic-gate * generate an interrupt as soon as possible 48720Sstevel@tonic-gate */ 48730Sstevel@tonic-gate ticks = apic_min_timer_ticks; 48740Sstevel@tonic-gate } else if ((time - now) > apic_nsec_max) { 48750Sstevel@tonic-gate /* 48760Sstevel@tonic-gate * requested to generate an interrupt at a time 48770Sstevel@tonic-gate * further than what we are capable of. Set to max 48780Sstevel@tonic-gate * the hardware can handle 48790Sstevel@tonic-gate */ 48800Sstevel@tonic-gate 48810Sstevel@tonic-gate ticks = APIC_MAXVAL; 48820Sstevel@tonic-gate #ifdef DEBUG 48830Sstevel@tonic-gate cmn_err(CE_CONT, "apic_timer_reprogram, request at" 48840Sstevel@tonic-gate " %lld too far in future, current time" 48850Sstevel@tonic-gate " %lld \n", time, now); 48860Sstevel@tonic-gate #endif /* DEBUG */ 48870Sstevel@tonic-gate } else 48880Sstevel@tonic-gate ticks = (uint_t)((time - now) / apic_nsec_per_tick); 48890Sstevel@tonic-gate } 48900Sstevel@tonic-gate 48910Sstevel@tonic-gate if (ticks < apic_min_timer_ticks) 48920Sstevel@tonic-gate ticks = apic_min_timer_ticks; 48930Sstevel@tonic-gate 48940Sstevel@tonic-gate apicadr[APIC_INIT_COUNT] = ticks; 48950Sstevel@tonic-gate 48960Sstevel@tonic-gate } 48970Sstevel@tonic-gate 48980Sstevel@tonic-gate /* 48990Sstevel@tonic-gate * This function will enable timer interrupts. 49000Sstevel@tonic-gate */ 49010Sstevel@tonic-gate static void 49020Sstevel@tonic-gate apic_timer_enable(void) 49030Sstevel@tonic-gate { 49040Sstevel@tonic-gate /* 49050Sstevel@tonic-gate * We should be Called from high PIL context (CBE_HIGH_PIL), 49060Sstevel@tonic-gate * so kpreempt is disabled. 49070Sstevel@tonic-gate */ 49080Sstevel@tonic-gate 49090Sstevel@tonic-gate if (!apic_oneshot) 49100Sstevel@tonic-gate apicadr[APIC_LOCAL_TIMER] = 49110Sstevel@tonic-gate (apic_clkvect + APIC_BASE_VECT) | AV_TIME; 49120Sstevel@tonic-gate else { 49130Sstevel@tonic-gate /* one shot */ 49140Sstevel@tonic-gate apicadr[APIC_LOCAL_TIMER] = (apic_clkvect + APIC_BASE_VECT); 49150Sstevel@tonic-gate } 49160Sstevel@tonic-gate } 49170Sstevel@tonic-gate 49180Sstevel@tonic-gate /* 49190Sstevel@tonic-gate * This function will disable timer interrupts. 49200Sstevel@tonic-gate */ 49210Sstevel@tonic-gate static void 49220Sstevel@tonic-gate apic_timer_disable(void) 49230Sstevel@tonic-gate { 49240Sstevel@tonic-gate /* 49250Sstevel@tonic-gate * We should be Called from high PIL context (CBE_HIGH_PIL), 49260Sstevel@tonic-gate * so kpreempt is disabled. 49270Sstevel@tonic-gate */ 49280Sstevel@tonic-gate 49290Sstevel@tonic-gate apicadr[APIC_LOCAL_TIMER] = (apic_clkvect + APIC_BASE_VECT) | AV_MASK; 49300Sstevel@tonic-gate } 49310Sstevel@tonic-gate 49320Sstevel@tonic-gate 49330Sstevel@tonic-gate cyclic_id_t apic_cyclic_id; 49340Sstevel@tonic-gate 49350Sstevel@tonic-gate /* 49360Sstevel@tonic-gate * If this module needs to be a consumer of cyclic subsystem, they 49370Sstevel@tonic-gate * can be added here, since at this time kernel cyclic subsystem is initialized 49380Sstevel@tonic-gate * argument is not currently used, and is reserved for future. 49390Sstevel@tonic-gate */ 49400Sstevel@tonic-gate static void 49410Sstevel@tonic-gate apic_post_cyclic_setup(void *arg) 49420Sstevel@tonic-gate { 49430Sstevel@tonic-gate _NOTE(ARGUNUSED(arg)) 49440Sstevel@tonic-gate cyc_handler_t hdlr; 49450Sstevel@tonic-gate cyc_time_t when; 49460Sstevel@tonic-gate 49470Sstevel@tonic-gate /* cpu_lock is held */ 49480Sstevel@tonic-gate 49490Sstevel@tonic-gate /* set up cyclics for intr redistribution */ 49500Sstevel@tonic-gate 49510Sstevel@tonic-gate /* 49520Sstevel@tonic-gate * In peridoc mode intr redistribution processing is done in 49530Sstevel@tonic-gate * apic_intr_enter during clk intr processing 49540Sstevel@tonic-gate */ 49550Sstevel@tonic-gate if (!apic_oneshot) 49560Sstevel@tonic-gate return; 49570Sstevel@tonic-gate 49580Sstevel@tonic-gate hdlr.cyh_level = CY_LOW_LEVEL; 49590Sstevel@tonic-gate hdlr.cyh_func = (cyc_func_t)apic_redistribute_compute; 49600Sstevel@tonic-gate hdlr.cyh_arg = NULL; 49610Sstevel@tonic-gate 49620Sstevel@tonic-gate when.cyt_when = 0; 49630Sstevel@tonic-gate when.cyt_interval = apic_redistribute_sample_interval; 49640Sstevel@tonic-gate apic_cyclic_id = cyclic_add(&hdlr, &when); 49650Sstevel@tonic-gate 49660Sstevel@tonic-gate 49670Sstevel@tonic-gate } 49680Sstevel@tonic-gate 49690Sstevel@tonic-gate static void 49700Sstevel@tonic-gate apic_redistribute_compute(void) 49710Sstevel@tonic-gate { 49720Sstevel@tonic-gate int i, j, max_busy; 49730Sstevel@tonic-gate 49740Sstevel@tonic-gate if (apic_enable_dynamic_migration) { 49750Sstevel@tonic-gate if (++apic_nticks == apic_sample_factor_redistribution) { 49760Sstevel@tonic-gate /* 49770Sstevel@tonic-gate * Time to call apic_intr_redistribute(). 49780Sstevel@tonic-gate * reset apic_nticks. This will cause max_busy 49790Sstevel@tonic-gate * to be calculated below and if it is more than 49800Sstevel@tonic-gate * apic_int_busy, we will do the whole thing 49810Sstevel@tonic-gate */ 49820Sstevel@tonic-gate apic_nticks = 0; 49830Sstevel@tonic-gate } 49840Sstevel@tonic-gate max_busy = 0; 49850Sstevel@tonic-gate for (i = 0; i < apic_nproc; i++) { 49860Sstevel@tonic-gate 49870Sstevel@tonic-gate /* 49880Sstevel@tonic-gate * Check if curipl is non zero & if ISR is in 49890Sstevel@tonic-gate * progress 49900Sstevel@tonic-gate */ 49910Sstevel@tonic-gate if (((j = apic_cpus[i].aci_curipl) != 0) && 49920Sstevel@tonic-gate (apic_cpus[i].aci_ISR_in_progress & (1 << j))) { 49930Sstevel@tonic-gate 49940Sstevel@tonic-gate int irq; 49950Sstevel@tonic-gate apic_cpus[i].aci_busy++; 49960Sstevel@tonic-gate irq = apic_cpus[i].aci_current[j]; 49970Sstevel@tonic-gate apic_irq_table[irq]->airq_busy++; 49980Sstevel@tonic-gate } 49990Sstevel@tonic-gate 50000Sstevel@tonic-gate if (!apic_nticks && 50010Sstevel@tonic-gate (apic_cpus[i].aci_busy > max_busy)) 50020Sstevel@tonic-gate max_busy = apic_cpus[i].aci_busy; 50030Sstevel@tonic-gate } 50040Sstevel@tonic-gate if (!apic_nticks) { 50050Sstevel@tonic-gate if (max_busy > apic_int_busy_mark) { 50060Sstevel@tonic-gate /* 50070Sstevel@tonic-gate * We could make the following check be 50080Sstevel@tonic-gate * skipped > 1 in which case, we get a 50090Sstevel@tonic-gate * redistribution at half the busy mark (due to 50100Sstevel@tonic-gate * double interval). Need to be able to collect 50110Sstevel@tonic-gate * more empirical data to decide if that is a 50120Sstevel@tonic-gate * good strategy. Punt for now. 50130Sstevel@tonic-gate */ 50140Sstevel@tonic-gate if (apic_skipped_redistribute) 50150Sstevel@tonic-gate apic_cleanup_busy(); 50160Sstevel@tonic-gate else 50170Sstevel@tonic-gate apic_intr_redistribute(); 50180Sstevel@tonic-gate } else 50190Sstevel@tonic-gate apic_skipped_redistribute++; 50200Sstevel@tonic-gate } 50210Sstevel@tonic-gate } 50220Sstevel@tonic-gate } 50230Sstevel@tonic-gate 50240Sstevel@tonic-gate 50250Sstevel@tonic-gate static int 50260Sstevel@tonic-gate apic_acpi_translate_pci_irq(dev_info_t *dip, int busid, int devid, 50270Sstevel@tonic-gate int ipin, int *pci_irqp, iflag_t *intr_flagp) 50280Sstevel@tonic-gate { 50290Sstevel@tonic-gate 50300Sstevel@tonic-gate int status; 50310Sstevel@tonic-gate acpi_psm_lnk_t acpipsmlnk; 50320Sstevel@tonic-gate 50330Sstevel@tonic-gate if ((status = acpi_get_irq_cache_ent(busid, devid, ipin, pci_irqp, 50340Sstevel@tonic-gate intr_flagp)) == ACPI_PSM_SUCCESS) { 50350Sstevel@tonic-gate APIC_VERBOSE_IRQ((CE_CONT, "!pcplusmp: Found irqno %d " 50360Sstevel@tonic-gate "from cache for device %s, instance #%d\n", *pci_irqp, 50370Sstevel@tonic-gate ddi_get_name(dip), ddi_get_instance(dip))); 50380Sstevel@tonic-gate return (status); 50390Sstevel@tonic-gate } 50400Sstevel@tonic-gate 50410Sstevel@tonic-gate bzero(&acpipsmlnk, sizeof (acpi_psm_lnk_t)); 50420Sstevel@tonic-gate 50430Sstevel@tonic-gate if ((status = acpi_translate_pci_irq(dip, ipin, pci_irqp, intr_flagp, 50440Sstevel@tonic-gate &acpipsmlnk)) == ACPI_PSM_FAILURE) { 50450Sstevel@tonic-gate APIC_VERBOSE_IRQ((CE_WARN, "pcplusmp: " 50460Sstevel@tonic-gate " acpi_translate_pci_irq failed for device %s, instance" 50470Sstevel@tonic-gate " #%d", ddi_get_name(dip), ddi_get_instance(dip))); 50480Sstevel@tonic-gate return (status); 50490Sstevel@tonic-gate } 50500Sstevel@tonic-gate 50510Sstevel@tonic-gate if (status == ACPI_PSM_PARTIAL && acpipsmlnk.lnkobj != NULL) { 50520Sstevel@tonic-gate status = apic_acpi_irq_configure(&acpipsmlnk, dip, pci_irqp, 50530Sstevel@tonic-gate intr_flagp); 50540Sstevel@tonic-gate if (status != ACPI_PSM_SUCCESS) { 50550Sstevel@tonic-gate status = acpi_get_current_irq_resource(&acpipsmlnk, 50560Sstevel@tonic-gate pci_irqp, intr_flagp); 50570Sstevel@tonic-gate } 50580Sstevel@tonic-gate } 50590Sstevel@tonic-gate 50600Sstevel@tonic-gate if (status == ACPI_PSM_SUCCESS) { 50610Sstevel@tonic-gate acpi_new_irq_cache_ent(busid, devid, ipin, *pci_irqp, 50620Sstevel@tonic-gate intr_flagp, &acpipsmlnk); 50630Sstevel@tonic-gate 50640Sstevel@tonic-gate APIC_VERBOSE_IRQ((CE_CONT, "pcplusmp: [ACPI] " 50650Sstevel@tonic-gate "new irq %d for device %s, instance #%d\n", 50660Sstevel@tonic-gate *pci_irqp, ddi_get_name(dip), ddi_get_instance(dip))); 50670Sstevel@tonic-gate } 50680Sstevel@tonic-gate 50690Sstevel@tonic-gate return (status); 50700Sstevel@tonic-gate } 50710Sstevel@tonic-gate 50720Sstevel@tonic-gate /* 50730Sstevel@tonic-gate * Configures the irq for the interrupt link device identified by 50740Sstevel@tonic-gate * acpipsmlnkp. 50750Sstevel@tonic-gate * 50760Sstevel@tonic-gate * Gets the current and the list of possible irq settings for the 50770Sstevel@tonic-gate * device. If apic_unconditional_srs is not set, and the current 50780Sstevel@tonic-gate * resource setting is in the list of possible irq settings, 50790Sstevel@tonic-gate * current irq resource setting is passed to the caller. 50800Sstevel@tonic-gate * 50810Sstevel@tonic-gate * Otherwise, picks an irq number from the list of possible irq 50820Sstevel@tonic-gate * settings, and sets the irq of the device to this value. 50830Sstevel@tonic-gate * If prefer_crs is set, among a set of irq numbers in the list that have 50840Sstevel@tonic-gate * the least number of devices sharing the interrupt, we pick current irq 50850Sstevel@tonic-gate * resource setting if it is a member of this set. 50860Sstevel@tonic-gate * 50870Sstevel@tonic-gate * Passes the irq number in the value pointed to by pci_irqp, and 50880Sstevel@tonic-gate * polarity and sensitivity in the structure pointed to by dipintrflagp 50890Sstevel@tonic-gate * to the caller. 50900Sstevel@tonic-gate * 50910Sstevel@tonic-gate * Note that if setting the irq resource failed, but successfuly obtained 50920Sstevel@tonic-gate * the current irq resource settings, passes the current irq resources 50930Sstevel@tonic-gate * and considers it a success. 50940Sstevel@tonic-gate * 50950Sstevel@tonic-gate * Returns: 50960Sstevel@tonic-gate * ACPI_PSM_SUCCESS on success. 50970Sstevel@tonic-gate * 50980Sstevel@tonic-gate * ACPI_PSM_FAILURE if an error occured during the configuration or 50990Sstevel@tonic-gate * if a suitable irq was not found for this device, or if setting the 51000Sstevel@tonic-gate * irq resource and obtaining the current resource fails. 51010Sstevel@tonic-gate * 51020Sstevel@tonic-gate */ 51030Sstevel@tonic-gate static int 51040Sstevel@tonic-gate apic_acpi_irq_configure(acpi_psm_lnk_t *acpipsmlnkp, dev_info_t *dip, 51050Sstevel@tonic-gate int *pci_irqp, iflag_t *dipintr_flagp) 51060Sstevel@tonic-gate { 51070Sstevel@tonic-gate 51080Sstevel@tonic-gate int i, min_share, foundnow, done = 0; 51090Sstevel@tonic-gate int32_t irq; 51100Sstevel@tonic-gate int32_t share_irq = -1; 51110Sstevel@tonic-gate int32_t chosen_irq = -1; 51120Sstevel@tonic-gate int cur_irq = -1; 51130Sstevel@tonic-gate acpi_irqlist_t *irqlistp; 51140Sstevel@tonic-gate acpi_irqlist_t *irqlistent; 51150Sstevel@tonic-gate 51160Sstevel@tonic-gate if ((acpi_get_possible_irq_resources(acpipsmlnkp, &irqlistp)) 51170Sstevel@tonic-gate == ACPI_PSM_FAILURE) { 51180Sstevel@tonic-gate APIC_VERBOSE_IRQ((CE_WARN, "!pcplusmp: Unable to determine " 51190Sstevel@tonic-gate "or assign IRQ for device %s, instance #%d: The system was " 51200Sstevel@tonic-gate "unable to get the list of potential IRQs from ACPI.", 51210Sstevel@tonic-gate ddi_get_name(dip), ddi_get_instance(dip))); 51220Sstevel@tonic-gate 51230Sstevel@tonic-gate return (ACPI_PSM_FAILURE); 51240Sstevel@tonic-gate } 51250Sstevel@tonic-gate 51260Sstevel@tonic-gate if ((acpi_get_current_irq_resource(acpipsmlnkp, &cur_irq, 51270Sstevel@tonic-gate dipintr_flagp) == ACPI_PSM_SUCCESS) && (!apic_unconditional_srs) && 51280Sstevel@tonic-gate (cur_irq > 0)) { 51290Sstevel@tonic-gate /* 51300Sstevel@tonic-gate * If an IRQ is set in CRS and that IRQ exists in the set 51310Sstevel@tonic-gate * returned from _PRS, return that IRQ, otherwise print 51320Sstevel@tonic-gate * a warning 51330Sstevel@tonic-gate */ 51340Sstevel@tonic-gate 51350Sstevel@tonic-gate if (acpi_irqlist_find_irq(irqlistp, cur_irq, NULL) 51360Sstevel@tonic-gate == ACPI_PSM_SUCCESS) { 51370Sstevel@tonic-gate 51380Sstevel@tonic-gate acpi_free_irqlist(irqlistp); 51390Sstevel@tonic-gate ASSERT(pci_irqp != NULL); 51400Sstevel@tonic-gate *pci_irqp = cur_irq; 51410Sstevel@tonic-gate return (ACPI_PSM_SUCCESS); 51420Sstevel@tonic-gate } 51430Sstevel@tonic-gate 51440Sstevel@tonic-gate APIC_VERBOSE_IRQ((CE_WARN, "!pcplusmp: Could not find the " 51450Sstevel@tonic-gate "current irq %d for device %s, instance #%d in ACPI's " 51460Sstevel@tonic-gate "list of possible irqs for this device. Picking one from " 51470Sstevel@tonic-gate " the latter list.", cur_irq, ddi_get_name(dip), 51480Sstevel@tonic-gate ddi_get_instance(dip))); 51490Sstevel@tonic-gate } 51500Sstevel@tonic-gate 51510Sstevel@tonic-gate irqlistent = irqlistp; 51520Sstevel@tonic-gate min_share = 255; 51530Sstevel@tonic-gate 51540Sstevel@tonic-gate while (irqlistent != NULL) { 51550Sstevel@tonic-gate irqlistent->intr_flags.bustype = BUS_PCI; 51560Sstevel@tonic-gate 51570Sstevel@tonic-gate for (foundnow = 0, i = 0; i < irqlistent->num_irqs; i++) { 51580Sstevel@tonic-gate 51590Sstevel@tonic-gate irq = irqlistent->irqs[i]; 51600Sstevel@tonic-gate 51610Sstevel@tonic-gate if ((irq < 16) && (apic_reserved_irqlist[irq])) 51620Sstevel@tonic-gate continue; 51630Sstevel@tonic-gate 51640Sstevel@tonic-gate if (irq == 0) { 51650Sstevel@tonic-gate /* invalid irq number */ 51660Sstevel@tonic-gate continue; 51670Sstevel@tonic-gate } 51680Sstevel@tonic-gate 51690Sstevel@tonic-gate if ((apic_irq_table[irq] == NULL) || 51700Sstevel@tonic-gate (apic_irq_table[irq]->airq_dip == dip)) { 51710Sstevel@tonic-gate chosen_irq = irq; 51720Sstevel@tonic-gate foundnow = 1; 51730Sstevel@tonic-gate /* 51740Sstevel@tonic-gate * If we do not prefer current irq from crs 51750Sstevel@tonic-gate * or if we do and this irq is the same as 51760Sstevel@tonic-gate * current irq from crs, this is the one 51770Sstevel@tonic-gate * to pick. 51780Sstevel@tonic-gate */ 51790Sstevel@tonic-gate if (!(apic_prefer_crs) || (irq == cur_irq)) { 51800Sstevel@tonic-gate done = 1; 51810Sstevel@tonic-gate break; 51820Sstevel@tonic-gate } 51830Sstevel@tonic-gate continue; 51840Sstevel@tonic-gate } 51850Sstevel@tonic-gate 51860Sstevel@tonic-gate if (irqlistent->intr_flags.intr_el == INTR_EL_EDGE) 51870Sstevel@tonic-gate continue; 51880Sstevel@tonic-gate 51890Sstevel@tonic-gate if (!acpi_intr_compatible(irqlistent->intr_flags, 51900Sstevel@tonic-gate apic_irq_table[irq]->airq_iflag)) 51910Sstevel@tonic-gate continue; 51920Sstevel@tonic-gate 51930Sstevel@tonic-gate if ((apic_irq_table[irq]->airq_share < min_share) || 51940Sstevel@tonic-gate ((apic_irq_table[irq]->airq_share == min_share) && 51950Sstevel@tonic-gate (cur_irq == irq) && (apic_prefer_crs))) { 51960Sstevel@tonic-gate min_share = apic_irq_table[irq]->airq_share; 51970Sstevel@tonic-gate share_irq = irq; 51980Sstevel@tonic-gate foundnow = 1; 51990Sstevel@tonic-gate } 52000Sstevel@tonic-gate } 52010Sstevel@tonic-gate 52020Sstevel@tonic-gate /* 52030Sstevel@tonic-gate * If we found an IRQ in the inner loop this time, save the 52040Sstevel@tonic-gate * details from the irqlist for later use. 52050Sstevel@tonic-gate */ 52060Sstevel@tonic-gate if (foundnow && ((chosen_irq != -1) || (share_irq != -1))) { 52070Sstevel@tonic-gate /* 52080Sstevel@tonic-gate * Copy the acpi_prs_private_t and flags from this 52090Sstevel@tonic-gate * irq list entry, since we found an irq from this 52100Sstevel@tonic-gate * entry. 52110Sstevel@tonic-gate */ 52120Sstevel@tonic-gate acpipsmlnkp->acpi_prs_prv = irqlistent->acpi_prs_prv; 52130Sstevel@tonic-gate *dipintr_flagp = irqlistent->intr_flags; 52140Sstevel@tonic-gate } 52150Sstevel@tonic-gate 52160Sstevel@tonic-gate if (done) 52170Sstevel@tonic-gate break; 52180Sstevel@tonic-gate 52190Sstevel@tonic-gate /* Go to the next irqlist entry */ 52200Sstevel@tonic-gate irqlistent = irqlistent->next; 52210Sstevel@tonic-gate } 52220Sstevel@tonic-gate 52230Sstevel@tonic-gate 52240Sstevel@tonic-gate acpi_free_irqlist(irqlistp); 52250Sstevel@tonic-gate if (chosen_irq != -1) 52260Sstevel@tonic-gate irq = chosen_irq; 52270Sstevel@tonic-gate else if (share_irq != -1) 52280Sstevel@tonic-gate irq = share_irq; 52290Sstevel@tonic-gate else { 52300Sstevel@tonic-gate APIC_VERBOSE_IRQ((CE_WARN, "!pcplusmp: Could not find a " 52310Sstevel@tonic-gate "suitable irq from the list of possible irqs for device " 52320Sstevel@tonic-gate "%s, instance #%d in ACPI's list of possible irqs", 52330Sstevel@tonic-gate ddi_get_name(dip), ddi_get_instance(dip))); 52340Sstevel@tonic-gate return (ACPI_PSM_FAILURE); 52350Sstevel@tonic-gate } 52360Sstevel@tonic-gate 52370Sstevel@tonic-gate APIC_VERBOSE_IRQ((CE_CONT, "!pcplusmp: Setting irq %d for device %s " 52380Sstevel@tonic-gate "instance #%d\n", irq, ddi_get_name(dip), ddi_get_instance(dip))); 52390Sstevel@tonic-gate 52400Sstevel@tonic-gate if ((acpi_set_irq_resource(acpipsmlnkp, irq)) == ACPI_PSM_SUCCESS) { 52410Sstevel@tonic-gate /* 52420Sstevel@tonic-gate * setting irq was successful, check to make sure CRS 52430Sstevel@tonic-gate * reflects that. If CRS does not agree with what we 52440Sstevel@tonic-gate * set, return the irq that was set. 52450Sstevel@tonic-gate */ 52460Sstevel@tonic-gate 52470Sstevel@tonic-gate if (acpi_get_current_irq_resource(acpipsmlnkp, &cur_irq, 52480Sstevel@tonic-gate dipintr_flagp) == ACPI_PSM_SUCCESS) { 52490Sstevel@tonic-gate 52500Sstevel@tonic-gate if (cur_irq != irq) 52510Sstevel@tonic-gate APIC_VERBOSE_IRQ((CE_WARN, "!pcplusmp: " 52520Sstevel@tonic-gate "IRQ resource set (irqno %d) for device %s " 52530Sstevel@tonic-gate "instance #%d, differs from current " 52540Sstevel@tonic-gate "setting irqno %d", 52550Sstevel@tonic-gate irq, ddi_get_name(dip), 52560Sstevel@tonic-gate ddi_get_instance(dip), cur_irq)); 52570Sstevel@tonic-gate } 52580Sstevel@tonic-gate 52590Sstevel@tonic-gate /* 52600Sstevel@tonic-gate * return the irq that was set, and not what CRS reports, 52610Sstevel@tonic-gate * since CRS has been seen to be bogus on some systems 52620Sstevel@tonic-gate */ 52630Sstevel@tonic-gate cur_irq = irq; 52640Sstevel@tonic-gate } else { 52650Sstevel@tonic-gate APIC_VERBOSE_IRQ((CE_WARN, "!pcplusmp: set resource irq %d " 52660Sstevel@tonic-gate "failed for device %s instance #%d", 52670Sstevel@tonic-gate irq, ddi_get_name(dip), ddi_get_instance(dip))); 52680Sstevel@tonic-gate 52690Sstevel@tonic-gate if (cur_irq == -1) 52700Sstevel@tonic-gate return (ACPI_PSM_FAILURE); 52710Sstevel@tonic-gate } 52720Sstevel@tonic-gate 52730Sstevel@tonic-gate ASSERT(pci_irqp != NULL); 52740Sstevel@tonic-gate *pci_irqp = cur_irq; 52750Sstevel@tonic-gate return (ACPI_PSM_SUCCESS); 52760Sstevel@tonic-gate } 5277