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 */ 213446Smrj 220Sstevel@tonic-gate /* 233446Smrj * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 240Sstevel@tonic-gate * Use is subject to license terms. 250Sstevel@tonic-gate */ 260Sstevel@tonic-gate 270Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 280Sstevel@tonic-gate 290Sstevel@tonic-gate /* 300Sstevel@tonic-gate * PSMI 1.1 extensions are supported only in 2.6 and later versions. 310Sstevel@tonic-gate * PSMI 1.2 extensions are supported only in 2.7 and later versions. 320Sstevel@tonic-gate * PSMI 1.3 and 1.4 extensions are supported in Solaris 10. 330Sstevel@tonic-gate * PSMI 1.5 extensions are supported in Solaris Nevada. 340Sstevel@tonic-gate */ 350Sstevel@tonic-gate #define PSMI_1_5 360Sstevel@tonic-gate 370Sstevel@tonic-gate #include <sys/processor.h> 380Sstevel@tonic-gate #include <sys/time.h> 390Sstevel@tonic-gate #include <sys/psm.h> 400Sstevel@tonic-gate #include <sys/smp_impldefs.h> 410Sstevel@tonic-gate #include <sys/cram.h> 420Sstevel@tonic-gate #include <sys/acpi/acpi.h> 430Sstevel@tonic-gate #include <sys/acpica.h> 440Sstevel@tonic-gate #include <sys/psm_common.h> 453446Smrj #include <sys/apic.h> 460Sstevel@tonic-gate #include <sys/pit.h> 470Sstevel@tonic-gate #include <sys/ddi.h> 480Sstevel@tonic-gate #include <sys/sunddi.h> 490Sstevel@tonic-gate #include <sys/ddi_impldefs.h> 500Sstevel@tonic-gate #include <sys/pci.h> 510Sstevel@tonic-gate #include <sys/promif.h> 520Sstevel@tonic-gate #include <sys/x86_archext.h> 530Sstevel@tonic-gate #include <sys/cpc_impl.h> 540Sstevel@tonic-gate #include <sys/uadmin.h> 550Sstevel@tonic-gate #include <sys/panic.h> 560Sstevel@tonic-gate #include <sys/debug.h> 570Sstevel@tonic-gate #include <sys/archsystm.h> 580Sstevel@tonic-gate #include <sys/trap.h> 590Sstevel@tonic-gate #include <sys/machsystm.h> 603446Smrj #include <sys/sysmacros.h> 610Sstevel@tonic-gate #include <sys/cpuvar.h> 620Sstevel@tonic-gate #include <sys/rm_platter.h> 630Sstevel@tonic-gate #include <sys/privregs.h> 640Sstevel@tonic-gate #include <sys/cyclic.h> 650Sstevel@tonic-gate #include <sys/note.h> 660Sstevel@tonic-gate #include <sys/pci_intr_lib.h> 673446Smrj #include <sys/spl.h> 680Sstevel@tonic-gate 690Sstevel@tonic-gate /* 700Sstevel@tonic-gate * Local Function Prototypes 710Sstevel@tonic-gate */ 720Sstevel@tonic-gate static void apic_init_intr(); 730Sstevel@tonic-gate static void apic_ret(); 740Sstevel@tonic-gate static int get_apic_cmd1(); 750Sstevel@tonic-gate static int get_apic_pri(); 760Sstevel@tonic-gate static void apic_nmi_intr(caddr_t arg); 770Sstevel@tonic-gate 780Sstevel@tonic-gate /* 790Sstevel@tonic-gate * standard MP entries 800Sstevel@tonic-gate */ 810Sstevel@tonic-gate static int apic_probe(); 820Sstevel@tonic-gate static int apic_clkinit(); 830Sstevel@tonic-gate static int apic_getclkirq(int ipl); 840Sstevel@tonic-gate static uint_t apic_calibrate(volatile uint32_t *addr, 850Sstevel@tonic-gate uint16_t *pit_ticks_adj); 860Sstevel@tonic-gate static hrtime_t apic_gettime(); 870Sstevel@tonic-gate static hrtime_t apic_gethrtime(); 880Sstevel@tonic-gate static void apic_init(); 890Sstevel@tonic-gate static void apic_picinit(void); 903446Smrj static int apic_cpu_start(processorid_t, caddr_t); 910Sstevel@tonic-gate static int apic_post_cpu_start(void); 920Sstevel@tonic-gate static void apic_send_ipi(int cpun, int ipl); 930Sstevel@tonic-gate static void apic_set_idlecpu(processorid_t cpun); 940Sstevel@tonic-gate static void apic_unset_idlecpu(processorid_t cpun); 950Sstevel@tonic-gate static int apic_intr_enter(int ipl, int *vect); 960Sstevel@tonic-gate static void apic_setspl(int ipl); 970Sstevel@tonic-gate static int apic_addspl(int ipl, int vector, int min_ipl, int max_ipl); 980Sstevel@tonic-gate static int apic_delspl(int ipl, int vector, int min_ipl, int max_ipl); 990Sstevel@tonic-gate static void apic_shutdown(int cmd, int fcn); 1000Sstevel@tonic-gate static void apic_preshutdown(int cmd, int fcn); 1010Sstevel@tonic-gate static int apic_disable_intr(processorid_t cpun); 1020Sstevel@tonic-gate static void apic_enable_intr(processorid_t cpun); 1030Sstevel@tonic-gate static processorid_t apic_get_next_processorid(processorid_t cpun); 1040Sstevel@tonic-gate static int apic_get_ipivect(int ipl, int type); 1050Sstevel@tonic-gate static void apic_timer_reprogram(hrtime_t time); 1060Sstevel@tonic-gate static void apic_timer_enable(void); 1070Sstevel@tonic-gate static void apic_timer_disable(void); 1080Sstevel@tonic-gate static void apic_post_cyclic_setup(void *arg); 1090Sstevel@tonic-gate 1100Sstevel@tonic-gate static int apic_oneshot = 0; 1110Sstevel@tonic-gate int apic_oneshot_enable = 1; /* to allow disabling one-shot capability */ 1120Sstevel@tonic-gate 1133446Smrj /* Now the ones for Dynamic Interrupt distribution */ 1143446Smrj int apic_enable_dynamic_migration = 0; 1153446Smrj 1163446Smrj 1170Sstevel@tonic-gate /* 1180Sstevel@tonic-gate * These variables are frequently accessed in apic_intr_enter(), 1190Sstevel@tonic-gate * apic_intr_exit and apic_setspl, so group them together 1200Sstevel@tonic-gate */ 1210Sstevel@tonic-gate volatile uint32_t *apicadr = NULL; /* virtual addr of local APIC */ 1220Sstevel@tonic-gate int apic_setspl_delay = 1; /* apic_setspl - delay enable */ 1230Sstevel@tonic-gate int apic_clkvect; 1240Sstevel@tonic-gate 1250Sstevel@tonic-gate /* vector at which error interrupts come in */ 1260Sstevel@tonic-gate int apic_errvect; 1270Sstevel@tonic-gate int apic_enable_error_intr = 1; 1280Sstevel@tonic-gate int apic_error_display_delay = 100; 1290Sstevel@tonic-gate 1300Sstevel@tonic-gate /* vector at which performance counter overflow interrupts come in */ 1310Sstevel@tonic-gate int apic_cpcovf_vect; 1320Sstevel@tonic-gate int apic_enable_cpcovf_intr = 1; 1330Sstevel@tonic-gate 1340Sstevel@tonic-gate /* 1350Sstevel@tonic-gate * The following vector assignments influence the value of ipltopri and 1360Sstevel@tonic-gate * vectortoipl. Note that vectors 0 - 0x1f are not used. We can program 1373745Ssethg * idle to 0 and IPL 0 to 0xf to differentiate idle in case 1380Sstevel@tonic-gate * we care to do so in future. Note some IPLs which are rarely used 1390Sstevel@tonic-gate * will share the vector ranges and heavily used IPLs (5 and 6) have 1400Sstevel@tonic-gate * a wide range. 1413745Ssethg * 1423745Ssethg * This array is used to initialize apic_ipls[] (in apic_init()). 1433745Ssethg * 1440Sstevel@tonic-gate * IPL Vector range. as passed to intr_enter 1450Sstevel@tonic-gate * 0 none. 1460Sstevel@tonic-gate * 1,2,3 0x20-0x2f 0x0-0xf 1470Sstevel@tonic-gate * 4 0x30-0x3f 0x10-0x1f 1480Sstevel@tonic-gate * 5 0x40-0x5f 0x20-0x3f 1490Sstevel@tonic-gate * 6 0x60-0x7f 0x40-0x5f 1500Sstevel@tonic-gate * 7,8,9 0x80-0x8f 0x60-0x6f 1510Sstevel@tonic-gate * 10 0x90-0x9f 0x70-0x7f 1520Sstevel@tonic-gate * 11 0xa0-0xaf 0x80-0x8f 1530Sstevel@tonic-gate * ... ... 1543745Ssethg * 15 0xe0-0xef 0xc0-0xcf 1553745Ssethg * 15 0xf0-0xff 0xd0-0xdf 1560Sstevel@tonic-gate */ 1570Sstevel@tonic-gate uchar_t apic_vectortoipl[APIC_AVAIL_VECTOR / APIC_VECTOR_PER_IPL] = { 1583745Ssethg 3, 4, 5, 5, 6, 6, 9, 10, 11, 12, 13, 14, 15, 15 1590Sstevel@tonic-gate }; 1600Sstevel@tonic-gate /* 1613745Ssethg * The ipl of an ISR at vector X is apic_vectortoipl[X>>4] 1620Sstevel@tonic-gate * NOTE that this is vector as passed into intr_enter which is 1630Sstevel@tonic-gate * programmed vector - 0x20 (APIC_BASE_VECT) 1640Sstevel@tonic-gate */ 1650Sstevel@tonic-gate 1660Sstevel@tonic-gate uchar_t apic_ipltopri[MAXIPL + 1]; /* unix ipl to apic pri */ 1670Sstevel@tonic-gate /* The taskpri to be programmed into apic to mask given ipl */ 1680Sstevel@tonic-gate 1690Sstevel@tonic-gate #if defined(__amd64) 1700Sstevel@tonic-gate uchar_t apic_cr8pri[MAXIPL + 1]; /* unix ipl to cr8 pri */ 1710Sstevel@tonic-gate #endif 1720Sstevel@tonic-gate 1730Sstevel@tonic-gate /* 1743745Ssethg * Correlation of the hardware vector to the IPL in use, initialized 1753745Ssethg * from apic_vectortoipl[] in apic_init(). The final IPLs may not correlate 1763745Ssethg * to the IPLs in apic_vectortoipl on some systems that share interrupt lines 1773745Ssethg * connected to errata-stricken IOAPICs 1783745Ssethg */ 1793745Ssethg uchar_t apic_ipls[APIC_AVAIL_VECTOR]; 1803745Ssethg 1813745Ssethg /* 1820Sstevel@tonic-gate * Patchable global variables. 1830Sstevel@tonic-gate */ 1840Sstevel@tonic-gate int apic_forceload = 0; 1850Sstevel@tonic-gate 1860Sstevel@tonic-gate int apic_coarse_hrtime = 1; /* 0 - use accurate slow gethrtime() */ 1870Sstevel@tonic-gate /* 1 - use gettime() for performance */ 1880Sstevel@tonic-gate int apic_flat_model = 0; /* 0 - clustered. 1 - flat */ 1890Sstevel@tonic-gate int apic_enable_hwsoftint = 0; /* 0 - disable, 1 - enable */ 1900Sstevel@tonic-gate int apic_enable_bind_log = 1; /* 1 - display interrupt binding log */ 1910Sstevel@tonic-gate int apic_panic_on_nmi = 0; 1920Sstevel@tonic-gate int apic_panic_on_apic_error = 0; 1930Sstevel@tonic-gate 1940Sstevel@tonic-gate int apic_verbose = 0; 1950Sstevel@tonic-gate 1960Sstevel@tonic-gate /* minimum number of timer ticks to program to */ 1970Sstevel@tonic-gate int apic_min_timer_ticks = 1; 1980Sstevel@tonic-gate /* 1990Sstevel@tonic-gate * Local static data 2000Sstevel@tonic-gate */ 2010Sstevel@tonic-gate static struct psm_ops apic_ops = { 2020Sstevel@tonic-gate apic_probe, 2030Sstevel@tonic-gate 2040Sstevel@tonic-gate apic_init, 2050Sstevel@tonic-gate apic_picinit, 2060Sstevel@tonic-gate apic_intr_enter, 2070Sstevel@tonic-gate apic_intr_exit, 2080Sstevel@tonic-gate apic_setspl, 2090Sstevel@tonic-gate apic_addspl, 2100Sstevel@tonic-gate apic_delspl, 2110Sstevel@tonic-gate apic_disable_intr, 2120Sstevel@tonic-gate apic_enable_intr, 2134652Scwb (int (*)(int))NULL, /* psm_softlvl_to_irq */ 2144652Scwb (void (*)(int))NULL, /* psm_set_softintr */ 2150Sstevel@tonic-gate 2160Sstevel@tonic-gate apic_set_idlecpu, 2170Sstevel@tonic-gate apic_unset_idlecpu, 2180Sstevel@tonic-gate 2190Sstevel@tonic-gate apic_clkinit, 2200Sstevel@tonic-gate apic_getclkirq, 2210Sstevel@tonic-gate (void (*)(void))NULL, /* psm_hrtimeinit */ 2220Sstevel@tonic-gate apic_gethrtime, 2230Sstevel@tonic-gate 2240Sstevel@tonic-gate apic_get_next_processorid, 2250Sstevel@tonic-gate apic_cpu_start, 2260Sstevel@tonic-gate apic_post_cpu_start, 2270Sstevel@tonic-gate apic_shutdown, 2280Sstevel@tonic-gate apic_get_ipivect, 2290Sstevel@tonic-gate apic_send_ipi, 2300Sstevel@tonic-gate 2310Sstevel@tonic-gate (int (*)(dev_info_t *, int))NULL, /* psm_translate_irq */ 2320Sstevel@tonic-gate (void (*)(int, char *))NULL, /* psm_notify_error */ 2330Sstevel@tonic-gate (void (*)(int))NULL, /* psm_notify_func */ 2340Sstevel@tonic-gate apic_timer_reprogram, 2350Sstevel@tonic-gate apic_timer_enable, 2360Sstevel@tonic-gate apic_timer_disable, 2370Sstevel@tonic-gate apic_post_cyclic_setup, 2380Sstevel@tonic-gate apic_preshutdown, 2390Sstevel@tonic-gate apic_intr_ops /* Advanced DDI Interrupt framework */ 2400Sstevel@tonic-gate }; 2410Sstevel@tonic-gate 2420Sstevel@tonic-gate 2430Sstevel@tonic-gate static struct psm_info apic_psm_info = { 2440Sstevel@tonic-gate PSM_INFO_VER01_5, /* version */ 2450Sstevel@tonic-gate PSM_OWN_EXCLUSIVE, /* ownership */ 2460Sstevel@tonic-gate (struct psm_ops *)&apic_ops, /* operation */ 2474397Sschwartz APIC_PCPLUSMP_NAME, /* machine name */ 2480Sstevel@tonic-gate "pcplusmp v1.4 compatible %I%", 2490Sstevel@tonic-gate }; 2500Sstevel@tonic-gate 2510Sstevel@tonic-gate static void *apic_hdlp; 2520Sstevel@tonic-gate 2530Sstevel@tonic-gate #ifdef DEBUG 2540Sstevel@tonic-gate int apic_debug = 0; 2550Sstevel@tonic-gate int apic_restrict_vector = 0; 2560Sstevel@tonic-gate 2570Sstevel@tonic-gate int apic_debug_msgbuf[APIC_DEBUG_MSGBUFSIZE]; 2580Sstevel@tonic-gate int apic_debug_msgbufindex = 0; 2590Sstevel@tonic-gate 2600Sstevel@tonic-gate #endif /* DEBUG */ 2610Sstevel@tonic-gate 2620Sstevel@tonic-gate apic_cpus_info_t *apic_cpus; 2630Sstevel@tonic-gate 2643446Smrj cpuset_t apic_cpumask; 2653446Smrj uint_t apic_flag; 2660Sstevel@tonic-gate 2670Sstevel@tonic-gate /* Flag to indicate that we need to shut down all processors */ 2680Sstevel@tonic-gate static uint_t apic_shutdown_processors; 2690Sstevel@tonic-gate 2700Sstevel@tonic-gate uint_t apic_nsec_per_intr = 0; 2710Sstevel@tonic-gate 2720Sstevel@tonic-gate /* 2730Sstevel@tonic-gate * apic_let_idle_redistribute can have the following values: 2740Sstevel@tonic-gate * 0 - If clock decremented it from 1 to 0, clock has to call redistribute. 2750Sstevel@tonic-gate * apic_redistribute_lock prevents multiple idle cpus from redistributing 2760Sstevel@tonic-gate */ 2770Sstevel@tonic-gate int apic_num_idle_redistributions = 0; 2780Sstevel@tonic-gate static int apic_let_idle_redistribute = 0; 2790Sstevel@tonic-gate static uint_t apic_nticks = 0; 2800Sstevel@tonic-gate static uint_t apic_skipped_redistribute = 0; 2810Sstevel@tonic-gate 2820Sstevel@tonic-gate /* to gather intr data and redistribute */ 2830Sstevel@tonic-gate static void apic_redistribute_compute(void); 2840Sstevel@tonic-gate 2850Sstevel@tonic-gate static uint_t last_count_read = 0; 2860Sstevel@tonic-gate static lock_t apic_gethrtime_lock; 2870Sstevel@tonic-gate volatile int apic_hrtime_stamp = 0; 2880Sstevel@tonic-gate volatile hrtime_t apic_nsec_since_boot = 0; 2892992Sdmick static uint_t apic_hertz_count; 2902992Sdmick 2912992Sdmick uint64_t apic_ticks_per_SFnsecs; /* # of ticks in SF nsecs */ 2922992Sdmick 2930Sstevel@tonic-gate static hrtime_t apic_nsec_max; 2940Sstevel@tonic-gate 2950Sstevel@tonic-gate static hrtime_t apic_last_hrtime = 0; 2960Sstevel@tonic-gate int apic_hrtime_error = 0; 2970Sstevel@tonic-gate int apic_remote_hrterr = 0; 2980Sstevel@tonic-gate int apic_num_nmis = 0; 2990Sstevel@tonic-gate int apic_apic_error = 0; 3000Sstevel@tonic-gate int apic_num_apic_errors = 0; 3010Sstevel@tonic-gate int apic_num_cksum_errors = 0; 3020Sstevel@tonic-gate 3033446Smrj int apic_error = 0; 3040Sstevel@tonic-gate static int apic_cmos_ssb_set = 0; 3050Sstevel@tonic-gate 3060Sstevel@tonic-gate /* use to make sure only one cpu handles the nmi */ 3070Sstevel@tonic-gate static lock_t apic_nmi_lock; 3080Sstevel@tonic-gate /* use to make sure only one cpu handles the error interrupt */ 3090Sstevel@tonic-gate static lock_t apic_error_lock; 3100Sstevel@tonic-gate 3110Sstevel@tonic-gate static struct { 3120Sstevel@tonic-gate uchar_t cntl; 3130Sstevel@tonic-gate uchar_t data; 3140Sstevel@tonic-gate } aspen_bmc[] = { 3150Sstevel@tonic-gate { CC_SMS_WR_START, 0x18 }, /* NetFn/LUN */ 3160Sstevel@tonic-gate { CC_SMS_WR_NEXT, 0x24 }, /* Cmd SET_WATCHDOG_TIMER */ 3170Sstevel@tonic-gate { CC_SMS_WR_NEXT, 0x84 }, /* DataByte 1: SMS/OS no log */ 3180Sstevel@tonic-gate { CC_SMS_WR_NEXT, 0x2 }, /* DataByte 2: Power Down */ 3190Sstevel@tonic-gate { CC_SMS_WR_NEXT, 0x0 }, /* DataByte 3: no pre-timeout */ 3200Sstevel@tonic-gate { CC_SMS_WR_NEXT, 0x0 }, /* DataByte 4: timer expir. */ 3210Sstevel@tonic-gate { CC_SMS_WR_NEXT, 0xa }, /* DataByte 5: init countdown */ 3220Sstevel@tonic-gate { CC_SMS_WR_END, 0x0 }, /* DataByte 6: init countdown */ 3230Sstevel@tonic-gate 3240Sstevel@tonic-gate { CC_SMS_WR_START, 0x18 }, /* NetFn/LUN */ 3250Sstevel@tonic-gate { CC_SMS_WR_END, 0x22 } /* Cmd RESET_WATCHDOG_TIMER */ 3260Sstevel@tonic-gate }; 3270Sstevel@tonic-gate 3280Sstevel@tonic-gate static struct { 3290Sstevel@tonic-gate int port; 3300Sstevel@tonic-gate uchar_t data; 3310Sstevel@tonic-gate } sitka_bmc[] = { 3320Sstevel@tonic-gate { SMS_COMMAND_REGISTER, SMS_WRITE_START }, 3330Sstevel@tonic-gate { SMS_DATA_REGISTER, 0x18 }, /* NetFn/LUN */ 3340Sstevel@tonic-gate { SMS_DATA_REGISTER, 0x24 }, /* Cmd SET_WATCHDOG_TIMER */ 3350Sstevel@tonic-gate { SMS_DATA_REGISTER, 0x84 }, /* DataByte 1: SMS/OS no log */ 3360Sstevel@tonic-gate { SMS_DATA_REGISTER, 0x2 }, /* DataByte 2: Power Down */ 3370Sstevel@tonic-gate { SMS_DATA_REGISTER, 0x0 }, /* DataByte 3: no pre-timeout */ 3380Sstevel@tonic-gate { SMS_DATA_REGISTER, 0x0 }, /* DataByte 4: timer expir. */ 3390Sstevel@tonic-gate { SMS_DATA_REGISTER, 0xa }, /* DataByte 5: init countdown */ 3400Sstevel@tonic-gate { SMS_COMMAND_REGISTER, SMS_WRITE_END }, 3410Sstevel@tonic-gate { SMS_DATA_REGISTER, 0x0 }, /* DataByte 6: init countdown */ 3420Sstevel@tonic-gate 3430Sstevel@tonic-gate { SMS_COMMAND_REGISTER, SMS_WRITE_START }, 3440Sstevel@tonic-gate { SMS_DATA_REGISTER, 0x18 }, /* NetFn/LUN */ 3450Sstevel@tonic-gate { SMS_COMMAND_REGISTER, SMS_WRITE_END }, 3460Sstevel@tonic-gate { SMS_DATA_REGISTER, 0x22 } /* Cmd RESET_WATCHDOG_TIMER */ 3470Sstevel@tonic-gate }; 3480Sstevel@tonic-gate 3490Sstevel@tonic-gate /* Patchable global variables. */ 3500Sstevel@tonic-gate int apic_kmdb_on_nmi = 0; /* 0 - no, 1 - yes enter kmdb */ 3512992Sdmick uint32_t apic_divide_reg_init = 0; /* 0 - divide by 2 */ 3520Sstevel@tonic-gate 3530Sstevel@tonic-gate /* 3540Sstevel@tonic-gate * This is the loadable module wrapper 3550Sstevel@tonic-gate */ 3560Sstevel@tonic-gate 3570Sstevel@tonic-gate int 3580Sstevel@tonic-gate _init(void) 3590Sstevel@tonic-gate { 3600Sstevel@tonic-gate if (apic_coarse_hrtime) 3610Sstevel@tonic-gate apic_ops.psm_gethrtime = &apic_gettime; 3620Sstevel@tonic-gate return (psm_mod_init(&apic_hdlp, &apic_psm_info)); 3630Sstevel@tonic-gate } 3640Sstevel@tonic-gate 3650Sstevel@tonic-gate int 3660Sstevel@tonic-gate _fini(void) 3670Sstevel@tonic-gate { 3680Sstevel@tonic-gate return (psm_mod_fini(&apic_hdlp, &apic_psm_info)); 3690Sstevel@tonic-gate } 3700Sstevel@tonic-gate 3710Sstevel@tonic-gate int 3720Sstevel@tonic-gate _info(struct modinfo *modinfop) 3730Sstevel@tonic-gate { 3740Sstevel@tonic-gate return (psm_mod_info(&apic_hdlp, &apic_psm_info, modinfop)); 3750Sstevel@tonic-gate } 3760Sstevel@tonic-gate 3770Sstevel@tonic-gate 3780Sstevel@tonic-gate static int 3790Sstevel@tonic-gate apic_probe() 3800Sstevel@tonic-gate { 3813446Smrj return (apic_probe_common(apic_psm_info.p_mach_idstring)); 3820Sstevel@tonic-gate } 3830Sstevel@tonic-gate 3840Sstevel@tonic-gate void 3850Sstevel@tonic-gate apic_init() 3860Sstevel@tonic-gate { 3873446Smrj int i; 3883446Smrj int j = 1; 3890Sstevel@tonic-gate 3900Sstevel@tonic-gate apic_ipltopri[0] = APIC_VECTOR_PER_IPL; /* leave 0 for idle */ 3910Sstevel@tonic-gate for (i = 0; i < (APIC_AVAIL_VECTOR / APIC_VECTOR_PER_IPL); i++) { 3920Sstevel@tonic-gate if ((i < ((APIC_AVAIL_VECTOR / APIC_VECTOR_PER_IPL) - 1)) && 3930Sstevel@tonic-gate (apic_vectortoipl[i + 1] == apic_vectortoipl[i])) 3940Sstevel@tonic-gate /* get to highest vector at the same ipl */ 3950Sstevel@tonic-gate continue; 3960Sstevel@tonic-gate for (; j <= apic_vectortoipl[i]; j++) { 3970Sstevel@tonic-gate apic_ipltopri[j] = (i << APIC_IPL_SHIFT) + 3980Sstevel@tonic-gate APIC_BASE_VECT; 3990Sstevel@tonic-gate } 4000Sstevel@tonic-gate } 4010Sstevel@tonic-gate for (; j < MAXIPL + 1; j++) 4020Sstevel@tonic-gate /* fill up any empty ipltopri slots */ 4030Sstevel@tonic-gate apic_ipltopri[j] = (i << APIC_IPL_SHIFT) + APIC_BASE_VECT; 4043446Smrj apic_init_common(); 4050Sstevel@tonic-gate #if defined(__amd64) 4060Sstevel@tonic-gate /* 4070Sstevel@tonic-gate * Make cpu-specific interrupt info point to cr8pri vector 4080Sstevel@tonic-gate */ 4090Sstevel@tonic-gate for (i = 0; i <= MAXIPL; i++) 4100Sstevel@tonic-gate apic_cr8pri[i] = apic_ipltopri[i] >> APIC_IPL_SHIFT; 4110Sstevel@tonic-gate CPU->cpu_pri_data = apic_cr8pri; 4120Sstevel@tonic-gate #endif /* __amd64 */ 4130Sstevel@tonic-gate } 4140Sstevel@tonic-gate 4150Sstevel@tonic-gate /* 4160Sstevel@tonic-gate * handler for APIC Error interrupt. Just print a warning and continue 4170Sstevel@tonic-gate */ 4180Sstevel@tonic-gate static int 4190Sstevel@tonic-gate apic_error_intr() 4200Sstevel@tonic-gate { 4210Sstevel@tonic-gate uint_t error0, error1, error; 4220Sstevel@tonic-gate uint_t i; 4230Sstevel@tonic-gate 4240Sstevel@tonic-gate /* 4250Sstevel@tonic-gate * We need to write before read as per 7.4.17 of system prog manual. 4260Sstevel@tonic-gate * We do both and or the results to be safe 4270Sstevel@tonic-gate */ 4280Sstevel@tonic-gate error0 = apicadr[APIC_ERROR_STATUS]; 4290Sstevel@tonic-gate apicadr[APIC_ERROR_STATUS] = 0; 4300Sstevel@tonic-gate error1 = apicadr[APIC_ERROR_STATUS]; 4310Sstevel@tonic-gate error = error0 | error1; 4320Sstevel@tonic-gate 4330Sstevel@tonic-gate /* 434846Ssethg * Clear the APIC error status (do this on all cpus that enter here) 435846Ssethg * (two writes are required due to the semantics of accessing the 436846Ssethg * error status register.) 437846Ssethg */ 438846Ssethg apicadr[APIC_ERROR_STATUS] = 0; 439846Ssethg apicadr[APIC_ERROR_STATUS] = 0; 440846Ssethg 441846Ssethg /* 4420Sstevel@tonic-gate * Prevent more than 1 CPU from handling error interrupt causing 4430Sstevel@tonic-gate * double printing (interleave of characters from multiple 4440Sstevel@tonic-gate * CPU's when using prom_printf) 4450Sstevel@tonic-gate */ 4460Sstevel@tonic-gate if (lock_try(&apic_error_lock) == 0) 4470Sstevel@tonic-gate return (error ? DDI_INTR_CLAIMED : DDI_INTR_UNCLAIMED); 4480Sstevel@tonic-gate if (error) { 4490Sstevel@tonic-gate #if DEBUG 4500Sstevel@tonic-gate if (apic_debug) 4510Sstevel@tonic-gate debug_enter("pcplusmp: APIC Error interrupt received"); 4520Sstevel@tonic-gate #endif /* DEBUG */ 4530Sstevel@tonic-gate if (apic_panic_on_apic_error) 4540Sstevel@tonic-gate cmn_err(CE_PANIC, 4550Sstevel@tonic-gate "APIC Error interrupt on CPU %d. Status = %x\n", 4560Sstevel@tonic-gate psm_get_cpu_id(), error); 4570Sstevel@tonic-gate else { 4580Sstevel@tonic-gate if ((error & ~APIC_CS_ERRORS) == 0) { 4590Sstevel@tonic-gate /* cksum error only */ 4600Sstevel@tonic-gate apic_error |= APIC_ERR_APIC_ERROR; 4610Sstevel@tonic-gate apic_apic_error |= error; 4620Sstevel@tonic-gate apic_num_apic_errors++; 4630Sstevel@tonic-gate apic_num_cksum_errors++; 4640Sstevel@tonic-gate } else { 4650Sstevel@tonic-gate /* 4660Sstevel@tonic-gate * prom_printf is the best shot we have of 4670Sstevel@tonic-gate * something which is problem free from 4680Sstevel@tonic-gate * high level/NMI type of interrupts 4690Sstevel@tonic-gate */ 4700Sstevel@tonic-gate prom_printf("APIC Error interrupt on CPU %d. " 4710Sstevel@tonic-gate "Status 0 = %x, Status 1 = %x\n", 4720Sstevel@tonic-gate psm_get_cpu_id(), error0, error1); 4730Sstevel@tonic-gate apic_error |= APIC_ERR_APIC_ERROR; 4740Sstevel@tonic-gate apic_apic_error |= error; 4750Sstevel@tonic-gate apic_num_apic_errors++; 4760Sstevel@tonic-gate for (i = 0; i < apic_error_display_delay; i++) { 4770Sstevel@tonic-gate tenmicrosec(); 4780Sstevel@tonic-gate } 4790Sstevel@tonic-gate /* 4800Sstevel@tonic-gate * provide more delay next time limited to 4810Sstevel@tonic-gate * roughly 1 clock tick time 4820Sstevel@tonic-gate */ 4830Sstevel@tonic-gate if (apic_error_display_delay < 500) 4840Sstevel@tonic-gate apic_error_display_delay *= 2; 4850Sstevel@tonic-gate } 4860Sstevel@tonic-gate } 4870Sstevel@tonic-gate lock_clear(&apic_error_lock); 4880Sstevel@tonic-gate return (DDI_INTR_CLAIMED); 4890Sstevel@tonic-gate } else { 4900Sstevel@tonic-gate lock_clear(&apic_error_lock); 4910Sstevel@tonic-gate return (DDI_INTR_UNCLAIMED); 4920Sstevel@tonic-gate } 4930Sstevel@tonic-gate /* NOTREACHED */ 4940Sstevel@tonic-gate } 4950Sstevel@tonic-gate 4960Sstevel@tonic-gate /* 4970Sstevel@tonic-gate * Turn off the mask bit in the performance counter Local Vector Table entry. 4980Sstevel@tonic-gate */ 4990Sstevel@tonic-gate static void 5000Sstevel@tonic-gate apic_cpcovf_mask_clear(void) 5010Sstevel@tonic-gate { 5020Sstevel@tonic-gate apicadr[APIC_PCINT_VECT] &= ~APIC_LVT_MASK; 5030Sstevel@tonic-gate } 5040Sstevel@tonic-gate 5050Sstevel@tonic-gate static void 5060Sstevel@tonic-gate apic_init_intr() 5070Sstevel@tonic-gate { 5080Sstevel@tonic-gate processorid_t cpun = psm_get_cpu_id(); 5090Sstevel@tonic-gate 5100Sstevel@tonic-gate #if defined(__amd64) 5110Sstevel@tonic-gate setcr8((ulong_t)(APIC_MASK_ALL >> APIC_IPL_SHIFT)); 5120Sstevel@tonic-gate #else 5130Sstevel@tonic-gate apicadr[APIC_TASK_REG] = APIC_MASK_ALL; 5140Sstevel@tonic-gate #endif 5150Sstevel@tonic-gate 5160Sstevel@tonic-gate if (apic_flat_model) 5170Sstevel@tonic-gate apicadr[APIC_FORMAT_REG] = APIC_FLAT_MODEL; 5180Sstevel@tonic-gate else 5190Sstevel@tonic-gate apicadr[APIC_FORMAT_REG] = APIC_CLUSTER_MODEL; 5200Sstevel@tonic-gate apicadr[APIC_DEST_REG] = AV_HIGH_ORDER >> cpun; 5210Sstevel@tonic-gate 5220Sstevel@tonic-gate /* need to enable APIC before unmasking NMI */ 5230Sstevel@tonic-gate apicadr[APIC_SPUR_INT_REG] = AV_UNIT_ENABLE | APIC_SPUR_INTR; 5240Sstevel@tonic-gate 5250Sstevel@tonic-gate apicadr[APIC_LOCAL_TIMER] = AV_MASK; 5260Sstevel@tonic-gate apicadr[APIC_INT_VECT0] = AV_MASK; /* local intr reg 0 */ 5270Sstevel@tonic-gate apicadr[APIC_INT_VECT1] = AV_NMI; /* enable NMI */ 5280Sstevel@tonic-gate 5290Sstevel@tonic-gate if (apic_cpus[cpun].aci_local_ver < APIC_INTEGRATED_VERS) 5300Sstevel@tonic-gate return; 5310Sstevel@tonic-gate 5320Sstevel@tonic-gate /* Enable performance counter overflow interrupt */ 5330Sstevel@tonic-gate 5340Sstevel@tonic-gate if ((x86_feature & X86_MSR) != X86_MSR) 5350Sstevel@tonic-gate apic_enable_cpcovf_intr = 0; 5360Sstevel@tonic-gate if (apic_enable_cpcovf_intr) { 5370Sstevel@tonic-gate if (apic_cpcovf_vect == 0) { 5380Sstevel@tonic-gate int ipl = APIC_PCINT_IPL; 5390Sstevel@tonic-gate int irq = apic_get_ipivect(ipl, -1); 5400Sstevel@tonic-gate 5410Sstevel@tonic-gate ASSERT(irq != -1); 5420Sstevel@tonic-gate apic_cpcovf_vect = apic_irq_table[irq]->airq_vector; 5430Sstevel@tonic-gate ASSERT(apic_cpcovf_vect); 5440Sstevel@tonic-gate (void) add_avintr(NULL, ipl, 5450Sstevel@tonic-gate (avfunc)kcpc_hw_overflow_intr, 546916Sschwartz "apic pcint", irq, NULL, NULL, NULL, NULL); 5470Sstevel@tonic-gate kcpc_hw_overflow_intr_installed = 1; 5480Sstevel@tonic-gate kcpc_hw_enable_cpc_intr = apic_cpcovf_mask_clear; 5490Sstevel@tonic-gate } 5500Sstevel@tonic-gate apicadr[APIC_PCINT_VECT] = apic_cpcovf_vect; 5510Sstevel@tonic-gate } 5520Sstevel@tonic-gate 5530Sstevel@tonic-gate /* Enable error interrupt */ 5540Sstevel@tonic-gate 5550Sstevel@tonic-gate if (apic_enable_error_intr) { 5560Sstevel@tonic-gate if (apic_errvect == 0) { 5570Sstevel@tonic-gate int ipl = 0xf; /* get highest priority intr */ 5580Sstevel@tonic-gate int irq = apic_get_ipivect(ipl, -1); 5590Sstevel@tonic-gate 5600Sstevel@tonic-gate ASSERT(irq != -1); 5610Sstevel@tonic-gate apic_errvect = apic_irq_table[irq]->airq_vector; 5620Sstevel@tonic-gate ASSERT(apic_errvect); 5630Sstevel@tonic-gate /* 5640Sstevel@tonic-gate * Not PSMI compliant, but we are going to merge 5650Sstevel@tonic-gate * with ON anyway 5660Sstevel@tonic-gate */ 5670Sstevel@tonic-gate (void) add_avintr((void *)NULL, ipl, 5680Sstevel@tonic-gate (avfunc)apic_error_intr, "apic error intr", 569916Sschwartz irq, NULL, NULL, NULL, NULL); 5700Sstevel@tonic-gate } 5710Sstevel@tonic-gate apicadr[APIC_ERR_VECT] = apic_errvect; 5720Sstevel@tonic-gate apicadr[APIC_ERROR_STATUS] = 0; 5730Sstevel@tonic-gate apicadr[APIC_ERROR_STATUS] = 0; 5740Sstevel@tonic-gate } 5750Sstevel@tonic-gate } 5760Sstevel@tonic-gate 5770Sstevel@tonic-gate static void 5780Sstevel@tonic-gate apic_disable_local_apic() 5790Sstevel@tonic-gate { 5800Sstevel@tonic-gate apicadr[APIC_TASK_REG] = APIC_MASK_ALL; 5810Sstevel@tonic-gate apicadr[APIC_LOCAL_TIMER] = AV_MASK; 5820Sstevel@tonic-gate apicadr[APIC_INT_VECT0] = AV_MASK; /* local intr reg 0 */ 5830Sstevel@tonic-gate apicadr[APIC_INT_VECT1] = AV_MASK; /* disable NMI */ 5840Sstevel@tonic-gate apicadr[APIC_ERR_VECT] = AV_MASK; /* and error interrupt */ 5850Sstevel@tonic-gate apicadr[APIC_PCINT_VECT] = AV_MASK; /* and perf counter intr */ 5860Sstevel@tonic-gate apicadr[APIC_SPUR_INT_REG] = APIC_SPUR_INTR; 5870Sstevel@tonic-gate } 5880Sstevel@tonic-gate 5890Sstevel@tonic-gate static void 5900Sstevel@tonic-gate apic_picinit(void) 5910Sstevel@tonic-gate { 5923446Smrj int i, j; 5930Sstevel@tonic-gate uint_t isr; 5940Sstevel@tonic-gate 5950Sstevel@tonic-gate /* 5960Sstevel@tonic-gate * On UniSys Model 6520, the BIOS leaves vector 0x20 isr 5970Sstevel@tonic-gate * bit on without clearing it with EOI. Since softint 5980Sstevel@tonic-gate * uses vector 0x20 to interrupt itself, so softint will 5990Sstevel@tonic-gate * not work on this machine. In order to fix this problem 6000Sstevel@tonic-gate * a check is made to verify all the isr bits are clear. 6010Sstevel@tonic-gate * If not, EOIs are issued to clear the bits. 6020Sstevel@tonic-gate */ 6030Sstevel@tonic-gate for (i = 7; i >= 1; i--) { 6040Sstevel@tonic-gate if ((isr = apicadr[APIC_ISR_REG + (i * 4)]) != 0) 6050Sstevel@tonic-gate for (j = 0; ((j < 32) && (isr != 0)); j++) 6060Sstevel@tonic-gate if (isr & (1 << j)) { 6070Sstevel@tonic-gate apicadr[APIC_EOI_REG] = 0; 6080Sstevel@tonic-gate isr &= ~(1 << j); 6090Sstevel@tonic-gate apic_error |= APIC_ERR_BOOT_EOI; 6100Sstevel@tonic-gate } 6110Sstevel@tonic-gate } 6120Sstevel@tonic-gate 6130Sstevel@tonic-gate /* set a flag so we know we have run apic_picinit() */ 6140Sstevel@tonic-gate apic_flag = 1; 6150Sstevel@tonic-gate LOCK_INIT_CLEAR(&apic_gethrtime_lock); 6160Sstevel@tonic-gate LOCK_INIT_CLEAR(&apic_ioapic_lock); 6170Sstevel@tonic-gate LOCK_INIT_CLEAR(&apic_error_lock); 6180Sstevel@tonic-gate 6190Sstevel@tonic-gate picsetup(); /* initialise the 8259 */ 6200Sstevel@tonic-gate 6210Sstevel@tonic-gate /* add nmi handler - least priority nmi handler */ 6220Sstevel@tonic-gate LOCK_INIT_CLEAR(&apic_nmi_lock); 6230Sstevel@tonic-gate 6240Sstevel@tonic-gate if (!psm_add_nmintr(0, (avfunc) apic_nmi_intr, 6250Sstevel@tonic-gate "pcplusmp NMI handler", (caddr_t)NULL)) 6260Sstevel@tonic-gate cmn_err(CE_WARN, "pcplusmp: Unable to add nmi handler"); 6270Sstevel@tonic-gate 6280Sstevel@tonic-gate apic_init_intr(); 6290Sstevel@tonic-gate 6300Sstevel@tonic-gate /* enable apic mode if imcr present */ 6310Sstevel@tonic-gate if (apic_imcrp) { 6320Sstevel@tonic-gate outb(APIC_IMCR_P1, (uchar_t)APIC_IMCR_SELECT); 6330Sstevel@tonic-gate outb(APIC_IMCR_P2, (uchar_t)APIC_IMCR_APIC); 6340Sstevel@tonic-gate } 6350Sstevel@tonic-gate 6363446Smrj ioapic_init_intr(IOAPIC_MASK); 6370Sstevel@tonic-gate } 6380Sstevel@tonic-gate 6390Sstevel@tonic-gate 6403446Smrj /*ARGSUSED1*/ 6413446Smrj static int 6423446Smrj apic_cpu_start(processorid_t cpun, caddr_t arg) 6430Sstevel@tonic-gate { 6440Sstevel@tonic-gate int loop_count; 6450Sstevel@tonic-gate uint32_t vector; 6463446Smrj uint_t cpu_id; 6473446Smrj ulong_t iflag; 6480Sstevel@tonic-gate 6490Sstevel@tonic-gate cpu_id = apic_cpus[cpun].aci_local_id; 6500Sstevel@tonic-gate 6510Sstevel@tonic-gate apic_cmos_ssb_set = 1; 6520Sstevel@tonic-gate 6530Sstevel@tonic-gate /* 6540Sstevel@tonic-gate * Interrupts on BSP cpu will be disabled during these startup 6550Sstevel@tonic-gate * steps in order to avoid unwanted side effects from 6560Sstevel@tonic-gate * executing interrupt handlers on a problematic BIOS. 6570Sstevel@tonic-gate */ 6580Sstevel@tonic-gate 6590Sstevel@tonic-gate iflag = intr_clear(); 6600Sstevel@tonic-gate outb(CMOS_ADDR, SSB); 6610Sstevel@tonic-gate outb(CMOS_DATA, BIOS_SHUTDOWN); 6620Sstevel@tonic-gate 6630Sstevel@tonic-gate while (get_apic_cmd1() & AV_PENDING) 6640Sstevel@tonic-gate apic_ret(); 6650Sstevel@tonic-gate 6660Sstevel@tonic-gate /* for integrated - make sure there is one INIT IPI in buffer */ 6670Sstevel@tonic-gate /* for external - it will wake up the cpu */ 6680Sstevel@tonic-gate apicadr[APIC_INT_CMD2] = cpu_id << APIC_ICR_ID_BIT_OFFSET; 6690Sstevel@tonic-gate apicadr[APIC_INT_CMD1] = AV_ASSERT | AV_RESET; 6700Sstevel@tonic-gate 6710Sstevel@tonic-gate /* If only 1 CPU is installed, PENDING bit will not go low */ 6720Sstevel@tonic-gate for (loop_count = 0x1000; loop_count; loop_count--) 6730Sstevel@tonic-gate if (get_apic_cmd1() & AV_PENDING) 6740Sstevel@tonic-gate apic_ret(); 6750Sstevel@tonic-gate else 6760Sstevel@tonic-gate break; 6770Sstevel@tonic-gate 6780Sstevel@tonic-gate apicadr[APIC_INT_CMD2] = cpu_id << APIC_ICR_ID_BIT_OFFSET; 6790Sstevel@tonic-gate apicadr[APIC_INT_CMD1] = AV_DEASSERT | AV_RESET; 6800Sstevel@tonic-gate 6810Sstevel@tonic-gate drv_usecwait(20000); /* 20 milli sec */ 6820Sstevel@tonic-gate 6830Sstevel@tonic-gate if (apic_cpus[cpun].aci_local_ver >= APIC_INTEGRATED_VERS) { 6840Sstevel@tonic-gate /* integrated apic */ 6850Sstevel@tonic-gate 6860Sstevel@tonic-gate vector = (rm_platter_pa >> MMU_PAGESHIFT) & 6870Sstevel@tonic-gate (APIC_VECTOR_MASK | APIC_IPL_MASK); 6880Sstevel@tonic-gate 6890Sstevel@tonic-gate /* to offset the INIT IPI queue up in the buffer */ 6900Sstevel@tonic-gate apicadr[APIC_INT_CMD2] = cpu_id << APIC_ICR_ID_BIT_OFFSET; 6910Sstevel@tonic-gate apicadr[APIC_INT_CMD1] = vector | AV_STARTUP; 6920Sstevel@tonic-gate 6930Sstevel@tonic-gate drv_usecwait(200); /* 20 micro sec */ 6940Sstevel@tonic-gate 6950Sstevel@tonic-gate apicadr[APIC_INT_CMD2] = cpu_id << APIC_ICR_ID_BIT_OFFSET; 6960Sstevel@tonic-gate apicadr[APIC_INT_CMD1] = vector | AV_STARTUP; 6970Sstevel@tonic-gate 6980Sstevel@tonic-gate drv_usecwait(200); /* 20 micro sec */ 6990Sstevel@tonic-gate } 7000Sstevel@tonic-gate intr_restore(iflag); 7013446Smrj return (0); 7020Sstevel@tonic-gate } 7030Sstevel@tonic-gate 7040Sstevel@tonic-gate 7050Sstevel@tonic-gate #ifdef DEBUG 7060Sstevel@tonic-gate int apic_break_on_cpu = 9; 7070Sstevel@tonic-gate int apic_stretch_interrupts = 0; 7080Sstevel@tonic-gate int apic_stretch_ISR = 1 << 3; /* IPL of 3 matches nothing now */ 7090Sstevel@tonic-gate 7100Sstevel@tonic-gate void 7110Sstevel@tonic-gate apic_break() 7120Sstevel@tonic-gate { 7130Sstevel@tonic-gate } 7140Sstevel@tonic-gate #endif /* DEBUG */ 7150Sstevel@tonic-gate 7160Sstevel@tonic-gate /* 7170Sstevel@tonic-gate * platform_intr_enter 7180Sstevel@tonic-gate * 7190Sstevel@tonic-gate * Called at the beginning of the interrupt service routine to 7200Sstevel@tonic-gate * mask all level equal to and below the interrupt priority 7210Sstevel@tonic-gate * of the interrupting vector. An EOI should be given to 7220Sstevel@tonic-gate * the interrupt controller to enable other HW interrupts. 7230Sstevel@tonic-gate * 7240Sstevel@tonic-gate * Return -1 for spurious interrupts 7250Sstevel@tonic-gate * 7260Sstevel@tonic-gate */ 7270Sstevel@tonic-gate /*ARGSUSED*/ 7280Sstevel@tonic-gate static int 7290Sstevel@tonic-gate apic_intr_enter(int ipl, int *vectorp) 7300Sstevel@tonic-gate { 7310Sstevel@tonic-gate uchar_t vector; 7320Sstevel@tonic-gate int nipl; 7333446Smrj int irq; 7343446Smrj ulong_t iflag; 7350Sstevel@tonic-gate apic_cpus_info_t *cpu_infop; 7360Sstevel@tonic-gate 7370Sstevel@tonic-gate /* 7383745Ssethg * The real vector delivered is (*vectorp + 0x20), but our caller 7393745Ssethg * subtracts 0x20 from the vector before passing it to us. 7403745Ssethg * (That's why APIC_BASE_VECT is 0x20.) 7410Sstevel@tonic-gate */ 7420Sstevel@tonic-gate vector = (uchar_t)*vectorp; 7430Sstevel@tonic-gate 7440Sstevel@tonic-gate /* if interrupted by the clock, increment apic_nsec_since_boot */ 7450Sstevel@tonic-gate if (vector == apic_clkvect) { 7460Sstevel@tonic-gate if (!apic_oneshot) { 7470Sstevel@tonic-gate /* NOTE: this is not MT aware */ 7480Sstevel@tonic-gate apic_hrtime_stamp++; 7490Sstevel@tonic-gate apic_nsec_since_boot += apic_nsec_per_intr; 7500Sstevel@tonic-gate apic_hrtime_stamp++; 7510Sstevel@tonic-gate last_count_read = apic_hertz_count; 7520Sstevel@tonic-gate apic_redistribute_compute(); 7530Sstevel@tonic-gate } 7540Sstevel@tonic-gate 7550Sstevel@tonic-gate /* We will avoid all the book keeping overhead for clock */ 7563745Ssethg nipl = apic_ipls[vector]; 7573745Ssethg 7580Sstevel@tonic-gate #if defined(__amd64) 7590Sstevel@tonic-gate setcr8((ulong_t)apic_cr8pri[nipl]); 7600Sstevel@tonic-gate #else 7610Sstevel@tonic-gate apicadr[APIC_TASK_REG] = apic_ipltopri[nipl]; 7620Sstevel@tonic-gate #endif 7630Sstevel@tonic-gate *vectorp = apic_vector_to_irq[vector + APIC_BASE_VECT]; 7640Sstevel@tonic-gate apicadr[APIC_EOI_REG] = 0; 7650Sstevel@tonic-gate return (nipl); 7660Sstevel@tonic-gate } 7670Sstevel@tonic-gate 7680Sstevel@tonic-gate cpu_infop = &apic_cpus[psm_get_cpu_id()]; 7690Sstevel@tonic-gate 7700Sstevel@tonic-gate if (vector == (APIC_SPUR_INTR - APIC_BASE_VECT)) { 7710Sstevel@tonic-gate cpu_infop->aci_spur_cnt++; 7720Sstevel@tonic-gate return (APIC_INT_SPURIOUS); 7730Sstevel@tonic-gate } 7740Sstevel@tonic-gate 7750Sstevel@tonic-gate /* Check if the vector we got is really what we need */ 7760Sstevel@tonic-gate if (apic_revector_pending) { 7770Sstevel@tonic-gate /* 7780Sstevel@tonic-gate * Disable interrupts for the duration of 7790Sstevel@tonic-gate * the vector translation to prevent a self-race for 7800Sstevel@tonic-gate * the apic_revector_lock. This cannot be done 7810Sstevel@tonic-gate * in apic_xlate_vector because it is recursive and 7820Sstevel@tonic-gate * we want the vector translation to be atomic with 7830Sstevel@tonic-gate * respect to other (higher-priority) interrupts. 7840Sstevel@tonic-gate */ 7850Sstevel@tonic-gate iflag = intr_clear(); 7860Sstevel@tonic-gate vector = apic_xlate_vector(vector + APIC_BASE_VECT) - 7870Sstevel@tonic-gate APIC_BASE_VECT; 7880Sstevel@tonic-gate intr_restore(iflag); 7890Sstevel@tonic-gate } 7900Sstevel@tonic-gate 7913745Ssethg nipl = apic_ipls[vector]; 7920Sstevel@tonic-gate *vectorp = irq = apic_vector_to_irq[vector + APIC_BASE_VECT]; 7930Sstevel@tonic-gate 7940Sstevel@tonic-gate #if defined(__amd64) 7950Sstevel@tonic-gate setcr8((ulong_t)apic_cr8pri[nipl]); 7960Sstevel@tonic-gate #else 7970Sstevel@tonic-gate apicadr[APIC_TASK_REG] = apic_ipltopri[nipl]; 7980Sstevel@tonic-gate #endif 7990Sstevel@tonic-gate 8000Sstevel@tonic-gate cpu_infop->aci_current[nipl] = (uchar_t)irq; 8010Sstevel@tonic-gate cpu_infop->aci_curipl = (uchar_t)nipl; 8020Sstevel@tonic-gate cpu_infop->aci_ISR_in_progress |= 1 << nipl; 8030Sstevel@tonic-gate 8040Sstevel@tonic-gate /* 8050Sstevel@tonic-gate * apic_level_intr could have been assimilated into the irq struct. 8060Sstevel@tonic-gate * but, having it as a character array is more efficient in terms of 8070Sstevel@tonic-gate * cache usage. So, we leave it as is. 8080Sstevel@tonic-gate */ 8090Sstevel@tonic-gate if (!apic_level_intr[irq]) 8100Sstevel@tonic-gate apicadr[APIC_EOI_REG] = 0; 8110Sstevel@tonic-gate 8120Sstevel@tonic-gate #ifdef DEBUG 8130Sstevel@tonic-gate APIC_DEBUG_BUF_PUT(vector); 8140Sstevel@tonic-gate APIC_DEBUG_BUF_PUT(irq); 8150Sstevel@tonic-gate APIC_DEBUG_BUF_PUT(nipl); 8160Sstevel@tonic-gate APIC_DEBUG_BUF_PUT(psm_get_cpu_id()); 8170Sstevel@tonic-gate if ((apic_stretch_interrupts) && (apic_stretch_ISR & (1 << nipl))) 8180Sstevel@tonic-gate drv_usecwait(apic_stretch_interrupts); 8190Sstevel@tonic-gate 8200Sstevel@tonic-gate if (apic_break_on_cpu == psm_get_cpu_id()) 8210Sstevel@tonic-gate apic_break(); 8220Sstevel@tonic-gate #endif /* DEBUG */ 8230Sstevel@tonic-gate return (nipl); 8240Sstevel@tonic-gate } 8250Sstevel@tonic-gate 8263446Smrj void 8270Sstevel@tonic-gate apic_intr_exit(int prev_ipl, int irq) 8280Sstevel@tonic-gate { 8290Sstevel@tonic-gate apic_cpus_info_t *cpu_infop; 8300Sstevel@tonic-gate 8310Sstevel@tonic-gate #if defined(__amd64) 8320Sstevel@tonic-gate setcr8((ulong_t)apic_cr8pri[prev_ipl]); 8330Sstevel@tonic-gate #else 8340Sstevel@tonic-gate apicadr[APIC_TASK_REG] = apic_ipltopri[prev_ipl]; 8350Sstevel@tonic-gate #endif 8360Sstevel@tonic-gate 8370Sstevel@tonic-gate cpu_infop = &apic_cpus[psm_get_cpu_id()]; 8380Sstevel@tonic-gate if (apic_level_intr[irq]) 8390Sstevel@tonic-gate apicadr[APIC_EOI_REG] = 0; 8400Sstevel@tonic-gate 8410Sstevel@tonic-gate cpu_infop->aci_curipl = (uchar_t)prev_ipl; 8420Sstevel@tonic-gate /* ISR above current pri could not be in progress */ 8430Sstevel@tonic-gate cpu_infop->aci_ISR_in_progress &= (2 << prev_ipl) - 1; 8440Sstevel@tonic-gate } 8450Sstevel@tonic-gate 8460Sstevel@tonic-gate /* 8470Sstevel@tonic-gate * Mask all interrupts below or equal to the given IPL 8480Sstevel@tonic-gate */ 8490Sstevel@tonic-gate static void 8500Sstevel@tonic-gate apic_setspl(int ipl) 8510Sstevel@tonic-gate { 8520Sstevel@tonic-gate 8530Sstevel@tonic-gate #if defined(__amd64) 8540Sstevel@tonic-gate setcr8((ulong_t)apic_cr8pri[ipl]); 8550Sstevel@tonic-gate #else 8560Sstevel@tonic-gate apicadr[APIC_TASK_REG] = apic_ipltopri[ipl]; 8570Sstevel@tonic-gate #endif 8580Sstevel@tonic-gate 8590Sstevel@tonic-gate /* interrupts at ipl above this cannot be in progress */ 8600Sstevel@tonic-gate apic_cpus[psm_get_cpu_id()].aci_ISR_in_progress &= (2 << ipl) - 1; 8610Sstevel@tonic-gate /* 8620Sstevel@tonic-gate * this is a patch fix for the ALR QSMP P5 machine, so that interrupts 8630Sstevel@tonic-gate * have enough time to come in before the priority is raised again 8640Sstevel@tonic-gate * during the idle() loop. 8650Sstevel@tonic-gate */ 8660Sstevel@tonic-gate if (apic_setspl_delay) 8670Sstevel@tonic-gate (void) get_apic_pri(); 8680Sstevel@tonic-gate } 8690Sstevel@tonic-gate 8700Sstevel@tonic-gate /* 8710Sstevel@tonic-gate * generates an interprocessor interrupt to another CPU 8720Sstevel@tonic-gate */ 8730Sstevel@tonic-gate static void 8740Sstevel@tonic-gate apic_send_ipi(int cpun, int ipl) 8750Sstevel@tonic-gate { 8760Sstevel@tonic-gate int vector; 8773446Smrj ulong_t flag; 8780Sstevel@tonic-gate 8790Sstevel@tonic-gate vector = apic_resv_vector[ipl]; 8800Sstevel@tonic-gate 8810Sstevel@tonic-gate flag = intr_clear(); 8820Sstevel@tonic-gate 8830Sstevel@tonic-gate while (get_apic_cmd1() & AV_PENDING) 8840Sstevel@tonic-gate apic_ret(); 8850Sstevel@tonic-gate 8860Sstevel@tonic-gate apicadr[APIC_INT_CMD2] = 8870Sstevel@tonic-gate apic_cpus[cpun].aci_local_id << APIC_ICR_ID_BIT_OFFSET; 8880Sstevel@tonic-gate apicadr[APIC_INT_CMD1] = vector; 8890Sstevel@tonic-gate 8900Sstevel@tonic-gate intr_restore(flag); 8910Sstevel@tonic-gate } 8920Sstevel@tonic-gate 8930Sstevel@tonic-gate 8940Sstevel@tonic-gate /*ARGSUSED*/ 8950Sstevel@tonic-gate static void 8960Sstevel@tonic-gate apic_set_idlecpu(processorid_t cpun) 8970Sstevel@tonic-gate { 8980Sstevel@tonic-gate } 8990Sstevel@tonic-gate 9000Sstevel@tonic-gate /*ARGSUSED*/ 9010Sstevel@tonic-gate static void 9020Sstevel@tonic-gate apic_unset_idlecpu(processorid_t cpun) 9030Sstevel@tonic-gate { 9040Sstevel@tonic-gate } 9050Sstevel@tonic-gate 9060Sstevel@tonic-gate 9070Sstevel@tonic-gate static void 9080Sstevel@tonic-gate apic_ret() 9090Sstevel@tonic-gate { 9100Sstevel@tonic-gate } 9110Sstevel@tonic-gate 9120Sstevel@tonic-gate static int 9130Sstevel@tonic-gate get_apic_cmd1() 9140Sstevel@tonic-gate { 9150Sstevel@tonic-gate return (apicadr[APIC_INT_CMD1]); 9160Sstevel@tonic-gate } 9170Sstevel@tonic-gate 9180Sstevel@tonic-gate static int 9190Sstevel@tonic-gate get_apic_pri() 9200Sstevel@tonic-gate { 9210Sstevel@tonic-gate #if defined(__amd64) 9220Sstevel@tonic-gate return ((int)getcr8()); 9230Sstevel@tonic-gate #else 9240Sstevel@tonic-gate return (apicadr[APIC_TASK_REG]); 9250Sstevel@tonic-gate #endif 9260Sstevel@tonic-gate } 9270Sstevel@tonic-gate 9280Sstevel@tonic-gate /* 9290Sstevel@tonic-gate * If apic_coarse_time == 1, then apic_gettime() is used instead of 9300Sstevel@tonic-gate * apic_gethrtime(). This is used for performance instead of accuracy. 9310Sstevel@tonic-gate */ 9320Sstevel@tonic-gate 9330Sstevel@tonic-gate static hrtime_t 9340Sstevel@tonic-gate apic_gettime() 9350Sstevel@tonic-gate { 9360Sstevel@tonic-gate int old_hrtime_stamp; 9370Sstevel@tonic-gate hrtime_t temp; 9380Sstevel@tonic-gate 9390Sstevel@tonic-gate /* 9400Sstevel@tonic-gate * In one-shot mode, we do not keep time, so if anyone 9410Sstevel@tonic-gate * calls psm_gettime() directly, we vector over to 9420Sstevel@tonic-gate * gethrtime(). 9430Sstevel@tonic-gate * one-shot mode MUST NOT be enabled if this psm is the source of 9440Sstevel@tonic-gate * hrtime. 9450Sstevel@tonic-gate */ 9460Sstevel@tonic-gate 9470Sstevel@tonic-gate if (apic_oneshot) 9480Sstevel@tonic-gate return (gethrtime()); 9490Sstevel@tonic-gate 9500Sstevel@tonic-gate 9510Sstevel@tonic-gate gettime_again: 9520Sstevel@tonic-gate while ((old_hrtime_stamp = apic_hrtime_stamp) & 1) 9530Sstevel@tonic-gate apic_ret(); 9540Sstevel@tonic-gate 9550Sstevel@tonic-gate temp = apic_nsec_since_boot; 9560Sstevel@tonic-gate 9570Sstevel@tonic-gate if (apic_hrtime_stamp != old_hrtime_stamp) { /* got an interrupt */ 9580Sstevel@tonic-gate goto gettime_again; 9590Sstevel@tonic-gate } 9600Sstevel@tonic-gate return (temp); 9610Sstevel@tonic-gate } 9620Sstevel@tonic-gate 9630Sstevel@tonic-gate /* 9640Sstevel@tonic-gate * Here we return the number of nanoseconds since booting. Note every 9650Sstevel@tonic-gate * clock interrupt increments apic_nsec_since_boot by the appropriate 9660Sstevel@tonic-gate * amount. 9670Sstevel@tonic-gate */ 9680Sstevel@tonic-gate static hrtime_t 9690Sstevel@tonic-gate apic_gethrtime() 9700Sstevel@tonic-gate { 9713446Smrj int curr_timeval, countval, elapsed_ticks; 9720Sstevel@tonic-gate int old_hrtime_stamp, status; 9730Sstevel@tonic-gate hrtime_t temp; 9740Sstevel@tonic-gate uchar_t cpun; 9753446Smrj ulong_t oflags; 9760Sstevel@tonic-gate 9770Sstevel@tonic-gate /* 9780Sstevel@tonic-gate * In one-shot mode, we do not keep time, so if anyone 9790Sstevel@tonic-gate * calls psm_gethrtime() directly, we vector over to 9800Sstevel@tonic-gate * gethrtime(). 9810Sstevel@tonic-gate * one-shot mode MUST NOT be enabled if this psm is the source of 9820Sstevel@tonic-gate * hrtime. 9830Sstevel@tonic-gate */ 9840Sstevel@tonic-gate 9850Sstevel@tonic-gate if (apic_oneshot) 9860Sstevel@tonic-gate return (gethrtime()); 9870Sstevel@tonic-gate 9880Sstevel@tonic-gate oflags = intr_clear(); /* prevent migration */ 9890Sstevel@tonic-gate 9900Sstevel@tonic-gate cpun = (uchar_t)((uint_t)apicadr[APIC_LID_REG] >> APIC_ID_BIT_OFFSET); 9910Sstevel@tonic-gate 9920Sstevel@tonic-gate lock_set(&apic_gethrtime_lock); 9930Sstevel@tonic-gate 9940Sstevel@tonic-gate gethrtime_again: 9950Sstevel@tonic-gate while ((old_hrtime_stamp = apic_hrtime_stamp) & 1) 9960Sstevel@tonic-gate apic_ret(); 9970Sstevel@tonic-gate 9980Sstevel@tonic-gate /* 9990Sstevel@tonic-gate * Check to see which CPU we are on. Note the time is kept on 10000Sstevel@tonic-gate * the local APIC of CPU 0. If on CPU 0, simply read the current 10010Sstevel@tonic-gate * counter. If on another CPU, issue a remote read command to CPU 0. 10020Sstevel@tonic-gate */ 10030Sstevel@tonic-gate if (cpun == apic_cpus[0].aci_local_id) { 10040Sstevel@tonic-gate countval = apicadr[APIC_CURR_COUNT]; 10050Sstevel@tonic-gate } else { 10060Sstevel@tonic-gate while (get_apic_cmd1() & AV_PENDING) 10070Sstevel@tonic-gate apic_ret(); 10080Sstevel@tonic-gate 10090Sstevel@tonic-gate apicadr[APIC_INT_CMD2] = 10100Sstevel@tonic-gate apic_cpus[0].aci_local_id << APIC_ICR_ID_BIT_OFFSET; 10110Sstevel@tonic-gate apicadr[APIC_INT_CMD1] = APIC_CURR_ADD|AV_REMOTE; 10120Sstevel@tonic-gate 10130Sstevel@tonic-gate while ((status = get_apic_cmd1()) & AV_READ_PENDING) 10140Sstevel@tonic-gate apic_ret(); 10150Sstevel@tonic-gate 10160Sstevel@tonic-gate if (status & AV_REMOTE_STATUS) /* 1 = valid */ 10170Sstevel@tonic-gate countval = apicadr[APIC_REMOTE_READ]; 10180Sstevel@tonic-gate else { /* 0 = invalid */ 10190Sstevel@tonic-gate apic_remote_hrterr++; 10200Sstevel@tonic-gate /* 10210Sstevel@tonic-gate * return last hrtime right now, will need more 10220Sstevel@tonic-gate * testing if change to retry 10230Sstevel@tonic-gate */ 10240Sstevel@tonic-gate temp = apic_last_hrtime; 10250Sstevel@tonic-gate 10260Sstevel@tonic-gate lock_clear(&apic_gethrtime_lock); 10270Sstevel@tonic-gate 10280Sstevel@tonic-gate intr_restore(oflags); 10290Sstevel@tonic-gate 10300Sstevel@tonic-gate return (temp); 10310Sstevel@tonic-gate } 10320Sstevel@tonic-gate } 10330Sstevel@tonic-gate if (countval > last_count_read) 10340Sstevel@tonic-gate countval = 0; 10350Sstevel@tonic-gate else 10360Sstevel@tonic-gate last_count_read = countval; 10370Sstevel@tonic-gate 10380Sstevel@tonic-gate elapsed_ticks = apic_hertz_count - countval; 10390Sstevel@tonic-gate 10402992Sdmick curr_timeval = APIC_TICKS_TO_NSECS(elapsed_ticks); 10410Sstevel@tonic-gate temp = apic_nsec_since_boot + curr_timeval; 10420Sstevel@tonic-gate 10430Sstevel@tonic-gate if (apic_hrtime_stamp != old_hrtime_stamp) { /* got an interrupt */ 10440Sstevel@tonic-gate /* we might have clobbered last_count_read. Restore it */ 10450Sstevel@tonic-gate last_count_read = apic_hertz_count; 10460Sstevel@tonic-gate goto gethrtime_again; 10470Sstevel@tonic-gate } 10480Sstevel@tonic-gate 10490Sstevel@tonic-gate if (temp < apic_last_hrtime) { 10500Sstevel@tonic-gate /* return last hrtime if error occurs */ 10510Sstevel@tonic-gate apic_hrtime_error++; 10520Sstevel@tonic-gate temp = apic_last_hrtime; 10530Sstevel@tonic-gate } 10540Sstevel@tonic-gate else 10550Sstevel@tonic-gate apic_last_hrtime = temp; 10560Sstevel@tonic-gate 10570Sstevel@tonic-gate lock_clear(&apic_gethrtime_lock); 10580Sstevel@tonic-gate intr_restore(oflags); 10590Sstevel@tonic-gate 10600Sstevel@tonic-gate return (temp); 10610Sstevel@tonic-gate } 10620Sstevel@tonic-gate 10630Sstevel@tonic-gate /* apic NMI handler */ 10640Sstevel@tonic-gate /*ARGSUSED*/ 10650Sstevel@tonic-gate static void 10660Sstevel@tonic-gate apic_nmi_intr(caddr_t arg) 10670Sstevel@tonic-gate { 10680Sstevel@tonic-gate if (apic_shutdown_processors) { 10690Sstevel@tonic-gate apic_disable_local_apic(); 10700Sstevel@tonic-gate return; 10710Sstevel@tonic-gate } 10720Sstevel@tonic-gate 10730Sstevel@tonic-gate if (lock_try(&apic_nmi_lock)) { 10740Sstevel@tonic-gate if (apic_kmdb_on_nmi) { 10750Sstevel@tonic-gate if (psm_debugger() == 0) { 10760Sstevel@tonic-gate cmn_err(CE_PANIC, 10770Sstevel@tonic-gate "NMI detected, kmdb is not available."); 10780Sstevel@tonic-gate } else { 10790Sstevel@tonic-gate debug_enter("\nNMI detected, entering kmdb.\n"); 10800Sstevel@tonic-gate } 10810Sstevel@tonic-gate } else { 10820Sstevel@tonic-gate if (apic_panic_on_nmi) { 10830Sstevel@tonic-gate /* Keep panic from entering kmdb. */ 10840Sstevel@tonic-gate nopanicdebug = 1; 10850Sstevel@tonic-gate cmn_err(CE_PANIC, "pcplusmp: NMI received"); 10860Sstevel@tonic-gate } else { 10870Sstevel@tonic-gate /* 10880Sstevel@tonic-gate * prom_printf is the best shot we have 10890Sstevel@tonic-gate * of something which is problem free from 10900Sstevel@tonic-gate * high level/NMI type of interrupts 10910Sstevel@tonic-gate */ 10920Sstevel@tonic-gate prom_printf("pcplusmp: NMI received\n"); 10930Sstevel@tonic-gate apic_error |= APIC_ERR_NMI; 10940Sstevel@tonic-gate apic_num_nmis++; 10950Sstevel@tonic-gate } 10960Sstevel@tonic-gate } 10970Sstevel@tonic-gate lock_clear(&apic_nmi_lock); 10980Sstevel@tonic-gate } 10990Sstevel@tonic-gate } 11000Sstevel@tonic-gate 11010Sstevel@tonic-gate /*ARGSUSED*/ 11020Sstevel@tonic-gate static int 11030Sstevel@tonic-gate apic_addspl(int irqno, int ipl, int min_ipl, int max_ipl) 11040Sstevel@tonic-gate { 11053446Smrj return (apic_addspl_common(irqno, ipl, min_ipl, max_ipl)); 11060Sstevel@tonic-gate } 11070Sstevel@tonic-gate 11080Sstevel@tonic-gate static int 11090Sstevel@tonic-gate apic_delspl(int irqno, int ipl, int min_ipl, int max_ipl) 11100Sstevel@tonic-gate { 11113446Smrj return (apic_delspl_common(irqno, ipl, min_ipl, max_ipl)); 11120Sstevel@tonic-gate } 11130Sstevel@tonic-gate 11140Sstevel@tonic-gate static int 11150Sstevel@tonic-gate apic_post_cpu_start() 11160Sstevel@tonic-gate { 11173446Smrj int i, cpun; 11183446Smrj ulong_t iflag; 11190Sstevel@tonic-gate apic_irq_t *irq_ptr; 11200Sstevel@tonic-gate 11213446Smrj splx(ipltospl(LOCK_LEVEL)); 11220Sstevel@tonic-gate apic_init_intr(); 11230Sstevel@tonic-gate 11240Sstevel@tonic-gate /* 11250Sstevel@tonic-gate * since some systems don't enable the internal cache on the non-boot 11260Sstevel@tonic-gate * cpus, so we have to enable them here 11270Sstevel@tonic-gate */ 11283446Smrj setcr0(getcr0() & ~(CR0_CD | CR0_NW)); 11290Sstevel@tonic-gate 11300Sstevel@tonic-gate while (get_apic_cmd1() & AV_PENDING) 11310Sstevel@tonic-gate apic_ret(); 11320Sstevel@tonic-gate 11330Sstevel@tonic-gate cpun = psm_get_cpu_id(); 11340Sstevel@tonic-gate apic_cpus[cpun].aci_status = APIC_CPU_ONLINE | APIC_CPU_INTR_ENABLE; 11350Sstevel@tonic-gate 11360Sstevel@tonic-gate for (i = apic_min_device_irq; i <= apic_max_device_irq; i++) { 11370Sstevel@tonic-gate irq_ptr = apic_irq_table[i]; 11380Sstevel@tonic-gate if ((irq_ptr == NULL) || 11390Sstevel@tonic-gate ((irq_ptr->airq_cpu & ~IRQ_USER_BOUND) != cpun)) 11400Sstevel@tonic-gate continue; 11410Sstevel@tonic-gate 11420Sstevel@tonic-gate while (irq_ptr) { 11433139Ssethg if (irq_ptr->airq_temp_cpu != IRQ_UNINIT) { 11443139Ssethg iflag = intr_clear(); 11453139Ssethg lock_set(&apic_ioapic_lock); 11463139Ssethg 11473139Ssethg (void) apic_rebind(irq_ptr, cpun, NULL); 11483139Ssethg 11493139Ssethg lock_clear(&apic_ioapic_lock); 11503139Ssethg intr_restore(iflag); 11513139Ssethg } 11520Sstevel@tonic-gate irq_ptr = irq_ptr->airq_next; 11530Sstevel@tonic-gate } 11540Sstevel@tonic-gate } 11550Sstevel@tonic-gate 11562992Sdmick apicadr[APIC_DIVIDE_REG] = apic_divide_reg_init; 11570Sstevel@tonic-gate return (PSM_SUCCESS); 11580Sstevel@tonic-gate } 11590Sstevel@tonic-gate 11600Sstevel@tonic-gate processorid_t 11610Sstevel@tonic-gate apic_get_next_processorid(processorid_t cpu_id) 11620Sstevel@tonic-gate { 11630Sstevel@tonic-gate 11640Sstevel@tonic-gate int i; 11650Sstevel@tonic-gate 11660Sstevel@tonic-gate if (cpu_id == -1) 11670Sstevel@tonic-gate return ((processorid_t)0); 11680Sstevel@tonic-gate 11690Sstevel@tonic-gate for (i = cpu_id + 1; i < NCPU; i++) { 11702006Sandrei if (CPU_IN_SET(apic_cpumask, i)) 11710Sstevel@tonic-gate return (i); 11720Sstevel@tonic-gate } 11730Sstevel@tonic-gate 11740Sstevel@tonic-gate return ((processorid_t)-1); 11750Sstevel@tonic-gate } 11760Sstevel@tonic-gate 11770Sstevel@tonic-gate 11780Sstevel@tonic-gate /* 11790Sstevel@tonic-gate * type == -1 indicates it is an internal request. Do not change 11800Sstevel@tonic-gate * resv_vector for these requests 11810Sstevel@tonic-gate */ 11820Sstevel@tonic-gate static int 11830Sstevel@tonic-gate apic_get_ipivect(int ipl, int type) 11840Sstevel@tonic-gate { 11850Sstevel@tonic-gate uchar_t vector; 11860Sstevel@tonic-gate int irq; 11870Sstevel@tonic-gate 11880Sstevel@tonic-gate if (irq = apic_allocate_irq(APIC_VECTOR(ipl))) { 11890Sstevel@tonic-gate if (vector = apic_allocate_vector(ipl, irq, 1)) { 11900Sstevel@tonic-gate apic_irq_table[irq]->airq_mps_intr_index = 11910Sstevel@tonic-gate RESERVE_INDEX; 11920Sstevel@tonic-gate apic_irq_table[irq]->airq_vector = vector; 11930Sstevel@tonic-gate if (type != -1) { 11940Sstevel@tonic-gate apic_resv_vector[ipl] = vector; 11950Sstevel@tonic-gate } 11960Sstevel@tonic-gate return (irq); 11970Sstevel@tonic-gate } 11980Sstevel@tonic-gate } 11990Sstevel@tonic-gate apic_error |= APIC_ERR_GET_IPIVECT_FAIL; 12000Sstevel@tonic-gate return (-1); /* shouldn't happen */ 12010Sstevel@tonic-gate } 12020Sstevel@tonic-gate 12030Sstevel@tonic-gate static int 12040Sstevel@tonic-gate apic_getclkirq(int ipl) 12050Sstevel@tonic-gate { 12060Sstevel@tonic-gate int irq; 12070Sstevel@tonic-gate 12080Sstevel@tonic-gate if ((irq = apic_get_ipivect(ipl, -1)) == -1) 12090Sstevel@tonic-gate return (-1); 12100Sstevel@tonic-gate /* 12110Sstevel@tonic-gate * Note the vector in apic_clkvect for per clock handling. 12120Sstevel@tonic-gate */ 12130Sstevel@tonic-gate apic_clkvect = apic_irq_table[irq]->airq_vector - APIC_BASE_VECT; 12140Sstevel@tonic-gate APIC_VERBOSE_IOAPIC((CE_NOTE, "get_clkirq: vector = %x\n", 12150Sstevel@tonic-gate apic_clkvect)); 12160Sstevel@tonic-gate return (irq); 12170Sstevel@tonic-gate } 12180Sstevel@tonic-gate 12192992Sdmick 12200Sstevel@tonic-gate /* 12210Sstevel@tonic-gate * Return the number of APIC clock ticks elapsed for 8245 to decrement 12220Sstevel@tonic-gate * (APIC_TIME_COUNT + pit_ticks_adj) ticks. 12230Sstevel@tonic-gate */ 12240Sstevel@tonic-gate static uint_t 12250Sstevel@tonic-gate apic_calibrate(volatile uint32_t *addr, uint16_t *pit_ticks_adj) 12260Sstevel@tonic-gate { 12270Sstevel@tonic-gate uint8_t pit_tick_lo; 12280Sstevel@tonic-gate uint16_t pit_tick, target_pit_tick; 12290Sstevel@tonic-gate uint32_t start_apic_tick, end_apic_tick; 12303446Smrj ulong_t iflag; 12310Sstevel@tonic-gate 12320Sstevel@tonic-gate addr += APIC_CURR_COUNT; 12330Sstevel@tonic-gate 12340Sstevel@tonic-gate iflag = intr_clear(); 12350Sstevel@tonic-gate 12360Sstevel@tonic-gate do { 12370Sstevel@tonic-gate pit_tick_lo = inb(PITCTR0_PORT); 12380Sstevel@tonic-gate pit_tick = (inb(PITCTR0_PORT) << 8) | pit_tick_lo; 12390Sstevel@tonic-gate } while (pit_tick < APIC_TIME_MIN || 12400Sstevel@tonic-gate pit_tick_lo <= APIC_LB_MIN || pit_tick_lo >= APIC_LB_MAX); 12410Sstevel@tonic-gate 12420Sstevel@tonic-gate /* 12430Sstevel@tonic-gate * Wait for the 8254 to decrement by 5 ticks to ensure 12440Sstevel@tonic-gate * we didn't start in the middle of a tick. 12450Sstevel@tonic-gate * Compare with 0x10 for the wrap around case. 12460Sstevel@tonic-gate */ 12470Sstevel@tonic-gate target_pit_tick = pit_tick - 5; 12480Sstevel@tonic-gate do { 12490Sstevel@tonic-gate pit_tick_lo = inb(PITCTR0_PORT); 12500Sstevel@tonic-gate pit_tick = (inb(PITCTR0_PORT) << 8) | pit_tick_lo; 12510Sstevel@tonic-gate } while (pit_tick > target_pit_tick || pit_tick_lo < 0x10); 12520Sstevel@tonic-gate 12530Sstevel@tonic-gate start_apic_tick = *addr; 12540Sstevel@tonic-gate 12550Sstevel@tonic-gate /* 12560Sstevel@tonic-gate * Wait for the 8254 to decrement by 12570Sstevel@tonic-gate * (APIC_TIME_COUNT + pit_ticks_adj) ticks 12580Sstevel@tonic-gate */ 12590Sstevel@tonic-gate target_pit_tick = pit_tick - APIC_TIME_COUNT; 12600Sstevel@tonic-gate do { 12610Sstevel@tonic-gate pit_tick_lo = inb(PITCTR0_PORT); 12620Sstevel@tonic-gate pit_tick = (inb(PITCTR0_PORT) << 8) | pit_tick_lo; 12630Sstevel@tonic-gate } while (pit_tick > target_pit_tick || pit_tick_lo < 0x10); 12640Sstevel@tonic-gate 12650Sstevel@tonic-gate end_apic_tick = *addr; 12660Sstevel@tonic-gate 12670Sstevel@tonic-gate *pit_ticks_adj = target_pit_tick - pit_tick; 12680Sstevel@tonic-gate 12690Sstevel@tonic-gate intr_restore(iflag); 12700Sstevel@tonic-gate 12710Sstevel@tonic-gate return (start_apic_tick - end_apic_tick); 12720Sstevel@tonic-gate } 12730Sstevel@tonic-gate 12740Sstevel@tonic-gate /* 12750Sstevel@tonic-gate * Initialise the APIC timer on the local APIC of CPU 0 to the desired 12760Sstevel@tonic-gate * frequency. Note at this stage in the boot sequence, the boot processor 12770Sstevel@tonic-gate * is the only active processor. 12780Sstevel@tonic-gate * hertz value of 0 indicates a one-shot mode request. In this case 12790Sstevel@tonic-gate * the function returns the resolution (in nanoseconds) for the hardware 12800Sstevel@tonic-gate * timer interrupt. If one-shot mode capability is not available, 12810Sstevel@tonic-gate * the return value will be 0. apic_enable_oneshot is a global switch 12820Sstevel@tonic-gate * for disabling the functionality. 12830Sstevel@tonic-gate * A non-zero positive value for hertz indicates a periodic mode request. 12840Sstevel@tonic-gate * In this case the hardware will be programmed to generate clock interrupts 12850Sstevel@tonic-gate * at hertz frequency and returns the resolution of interrupts in 12860Sstevel@tonic-gate * nanosecond. 12870Sstevel@tonic-gate */ 12880Sstevel@tonic-gate 12890Sstevel@tonic-gate static int 12900Sstevel@tonic-gate apic_clkinit(int hertz) 12910Sstevel@tonic-gate { 12920Sstevel@tonic-gate 12930Sstevel@tonic-gate uint_t apic_ticks = 0; 12942992Sdmick uint_t pit_ticks; 12950Sstevel@tonic-gate int ret; 12960Sstevel@tonic-gate uint16_t pit_ticks_adj; 12970Sstevel@tonic-gate static int firsttime = 1; 12980Sstevel@tonic-gate 12990Sstevel@tonic-gate if (firsttime) { 13002992Sdmick /* first time calibrate on CPU0 only */ 13012992Sdmick 13022992Sdmick apicadr[APIC_DIVIDE_REG] = apic_divide_reg_init; 13033446Smrj apicadr[APIC_INIT_COUNT] = APIC_MAXVAL; 13040Sstevel@tonic-gate apic_ticks = apic_calibrate(apicadr, &pit_ticks_adj); 13050Sstevel@tonic-gate 13062992Sdmick /* total number of PIT ticks corresponding to apic_ticks */ 13072992Sdmick pit_ticks = APIC_TIME_COUNT + pit_ticks_adj; 13080Sstevel@tonic-gate 13090Sstevel@tonic-gate /* 13100Sstevel@tonic-gate * Determine the number of nanoseconds per APIC clock tick 13110Sstevel@tonic-gate * and then determine how many APIC ticks to interrupt at the 13120Sstevel@tonic-gate * desired frequency 13132992Sdmick * apic_ticks / (pitticks / PIT_HZ) = apic_ticks_per_s 13142992Sdmick * (apic_ticks * PIT_HZ) / pitticks = apic_ticks_per_s 13152992Sdmick * apic_ticks_per_ns = (apic_ticks * PIT_HZ) / (pitticks * 10^9) 13163446Smrj * pic_ticks_per_SFns = 13172992Sdmick * (SF * apic_ticks * PIT_HZ) / (pitticks * 10^9) 13180Sstevel@tonic-gate */ 13192992Sdmick apic_ticks_per_SFnsecs = 13202992Sdmick ((SF * apic_ticks * PIT_HZ) / 13212992Sdmick ((uint64_t)pit_ticks * NANOSEC)); 13220Sstevel@tonic-gate 13230Sstevel@tonic-gate /* the interval timer initial count is 32 bit max */ 13242992Sdmick apic_nsec_max = APIC_TICKS_TO_NSECS(APIC_MAXVAL); 13250Sstevel@tonic-gate firsttime = 0; 13260Sstevel@tonic-gate } 13270Sstevel@tonic-gate 13280Sstevel@tonic-gate if (hertz != 0) { 13290Sstevel@tonic-gate /* periodic */ 13300Sstevel@tonic-gate apic_nsec_per_intr = NANOSEC / hertz; 13312992Sdmick apic_hertz_count = APIC_NSECS_TO_TICKS(apic_nsec_per_intr); 13320Sstevel@tonic-gate } 13330Sstevel@tonic-gate 13340Sstevel@tonic-gate apic_int_busy_mark = (apic_int_busy_mark * 13350Sstevel@tonic-gate apic_sample_factor_redistribution) / 100; 13360Sstevel@tonic-gate apic_int_free_mark = (apic_int_free_mark * 13370Sstevel@tonic-gate apic_sample_factor_redistribution) / 100; 13380Sstevel@tonic-gate apic_diff_for_redistribution = (apic_diff_for_redistribution * 13390Sstevel@tonic-gate apic_sample_factor_redistribution) / 100; 13400Sstevel@tonic-gate 13410Sstevel@tonic-gate if (hertz == 0) { 13420Sstevel@tonic-gate /* requested one_shot */ 13430Sstevel@tonic-gate if (!apic_oneshot_enable) 13440Sstevel@tonic-gate return (0); 13450Sstevel@tonic-gate apic_oneshot = 1; 13462992Sdmick ret = (int)APIC_TICKS_TO_NSECS(1); 13470Sstevel@tonic-gate } else { 13480Sstevel@tonic-gate /* program the local APIC to interrupt at the given frequency */ 13490Sstevel@tonic-gate apicadr[APIC_INIT_COUNT] = apic_hertz_count; 13500Sstevel@tonic-gate apicadr[APIC_LOCAL_TIMER] = 13510Sstevel@tonic-gate (apic_clkvect + APIC_BASE_VECT) | AV_TIME; 13520Sstevel@tonic-gate apic_oneshot = 0; 13530Sstevel@tonic-gate ret = NANOSEC / hertz; 13540Sstevel@tonic-gate } 13550Sstevel@tonic-gate 13560Sstevel@tonic-gate return (ret); 13570Sstevel@tonic-gate 13580Sstevel@tonic-gate } 13590Sstevel@tonic-gate 13600Sstevel@tonic-gate /* 13610Sstevel@tonic-gate * apic_preshutdown: 13620Sstevel@tonic-gate * Called early in shutdown whilst we can still access filesystems to do 13630Sstevel@tonic-gate * things like loading modules which will be required to complete shutdown 13640Sstevel@tonic-gate * after filesystems are all unmounted. 13650Sstevel@tonic-gate */ 13660Sstevel@tonic-gate static void 13670Sstevel@tonic-gate apic_preshutdown(int cmd, int fcn) 13680Sstevel@tonic-gate { 13690Sstevel@tonic-gate APIC_VERBOSE_POWEROFF(("apic_preshutdown(%d,%d); m=%d a=%d\n", 13700Sstevel@tonic-gate cmd, fcn, apic_poweroff_method, apic_enable_acpi)); 13710Sstevel@tonic-gate 13720Sstevel@tonic-gate } 13730Sstevel@tonic-gate 13740Sstevel@tonic-gate static void 13750Sstevel@tonic-gate apic_shutdown(int cmd, int fcn) 13760Sstevel@tonic-gate { 13773446Smrj int restarts, attempts; 13783446Smrj int i; 13790Sstevel@tonic-gate uchar_t byte; 13803446Smrj ulong_t iflag; 13810Sstevel@tonic-gate 13820Sstevel@tonic-gate /* Send NMI to all CPUs except self to do per processor shutdown */ 13830Sstevel@tonic-gate iflag = intr_clear(); 13840Sstevel@tonic-gate while (get_apic_cmd1() & AV_PENDING) 13850Sstevel@tonic-gate apic_ret(); 13860Sstevel@tonic-gate apic_shutdown_processors = 1; 13870Sstevel@tonic-gate apicadr[APIC_INT_CMD1] = AV_NMI | AV_LEVEL | AV_SH_ALL_EXCSELF; 13880Sstevel@tonic-gate 13890Sstevel@tonic-gate /* restore cmos shutdown byte before reboot */ 13900Sstevel@tonic-gate if (apic_cmos_ssb_set) { 13910Sstevel@tonic-gate outb(CMOS_ADDR, SSB); 13920Sstevel@tonic-gate outb(CMOS_DATA, 0); 13930Sstevel@tonic-gate } 13943446Smrj 13953446Smrj ioapic_disable_redirection(); 13960Sstevel@tonic-gate 13970Sstevel@tonic-gate /* disable apic mode if imcr present */ 13980Sstevel@tonic-gate if (apic_imcrp) { 13990Sstevel@tonic-gate outb(APIC_IMCR_P1, (uchar_t)APIC_IMCR_SELECT); 14000Sstevel@tonic-gate outb(APIC_IMCR_P2, (uchar_t)APIC_IMCR_PIC); 14010Sstevel@tonic-gate } 14020Sstevel@tonic-gate 14030Sstevel@tonic-gate apic_disable_local_apic(); 14040Sstevel@tonic-gate 14050Sstevel@tonic-gate intr_restore(iflag); 14060Sstevel@tonic-gate 14073472Smyers /* remainder of function is for shutdown cases only */ 14083472Smyers if (cmd != A_SHUTDOWN) 14090Sstevel@tonic-gate return; 14103472Smyers 14114189Smyers /* 14124189Smyers * Switch system back into Legacy-Mode if using ACPI and 14134189Smyers * not powering-off. Some BIOSes need to remain in ACPI-mode 14144189Smyers * for power-off to succeed (Dell Dimension 4600) 14154189Smyers */ 14164189Smyers if (apic_enable_acpi && (fcn != AD_POWEROFF)) 14173472Smyers (void) AcpiDisable(); 14183472Smyers 14193472Smyers /* remainder of function is for shutdown+poweroff case only */ 14203472Smyers if (fcn != AD_POWEROFF) 14213472Smyers return; 14220Sstevel@tonic-gate 14230Sstevel@tonic-gate switch (apic_poweroff_method) { 14240Sstevel@tonic-gate case APIC_POWEROFF_VIA_RTC: 14250Sstevel@tonic-gate 14260Sstevel@tonic-gate /* select the extended NVRAM bank in the RTC */ 14270Sstevel@tonic-gate outb(CMOS_ADDR, RTC_REGA); 14280Sstevel@tonic-gate byte = inb(CMOS_DATA); 14290Sstevel@tonic-gate outb(CMOS_DATA, (byte | EXT_BANK)); 14300Sstevel@tonic-gate 14310Sstevel@tonic-gate outb(CMOS_ADDR, PFR_REG); 14320Sstevel@tonic-gate 14330Sstevel@tonic-gate /* for Predator must toggle the PAB bit */ 14340Sstevel@tonic-gate byte = inb(CMOS_DATA); 14350Sstevel@tonic-gate 14360Sstevel@tonic-gate /* 14370Sstevel@tonic-gate * clear power active bar, wakeup alarm and 14380Sstevel@tonic-gate * kickstart 14390Sstevel@tonic-gate */ 14400Sstevel@tonic-gate byte &= ~(PAB_CBIT | WF_FLAG | KS_FLAG); 14410Sstevel@tonic-gate outb(CMOS_DATA, byte); 14420Sstevel@tonic-gate 14430Sstevel@tonic-gate /* delay before next write */ 14440Sstevel@tonic-gate drv_usecwait(1000); 14450Sstevel@tonic-gate 14460Sstevel@tonic-gate /* for S40 the following would suffice */ 14470Sstevel@tonic-gate byte = inb(CMOS_DATA); 14480Sstevel@tonic-gate 14490Sstevel@tonic-gate /* power active bar control bit */ 14500Sstevel@tonic-gate byte |= PAB_CBIT; 14510Sstevel@tonic-gate outb(CMOS_DATA, byte); 14520Sstevel@tonic-gate 14530Sstevel@tonic-gate break; 14540Sstevel@tonic-gate 14550Sstevel@tonic-gate case APIC_POWEROFF_VIA_ASPEN_BMC: 14560Sstevel@tonic-gate restarts = 0; 14570Sstevel@tonic-gate restart_aspen_bmc: 14580Sstevel@tonic-gate if (++restarts == 3) 14590Sstevel@tonic-gate break; 14600Sstevel@tonic-gate attempts = 0; 14610Sstevel@tonic-gate do { 14620Sstevel@tonic-gate byte = inb(MISMIC_FLAG_REGISTER); 14630Sstevel@tonic-gate byte &= MISMIC_BUSY_MASK; 14640Sstevel@tonic-gate if (byte != 0) { 14650Sstevel@tonic-gate drv_usecwait(1000); 14660Sstevel@tonic-gate if (attempts >= 3) 14670Sstevel@tonic-gate goto restart_aspen_bmc; 14680Sstevel@tonic-gate ++attempts; 14690Sstevel@tonic-gate } 14700Sstevel@tonic-gate } while (byte != 0); 14710Sstevel@tonic-gate outb(MISMIC_CNTL_REGISTER, CC_SMS_GET_STATUS); 14720Sstevel@tonic-gate byte = inb(MISMIC_FLAG_REGISTER); 14730Sstevel@tonic-gate byte |= 0x1; 14740Sstevel@tonic-gate outb(MISMIC_FLAG_REGISTER, byte); 14750Sstevel@tonic-gate i = 0; 14760Sstevel@tonic-gate for (; i < (sizeof (aspen_bmc)/sizeof (aspen_bmc[0])); 14770Sstevel@tonic-gate i++) { 14780Sstevel@tonic-gate attempts = 0; 14790Sstevel@tonic-gate do { 14800Sstevel@tonic-gate byte = inb(MISMIC_FLAG_REGISTER); 14810Sstevel@tonic-gate byte &= MISMIC_BUSY_MASK; 14820Sstevel@tonic-gate if (byte != 0) { 14830Sstevel@tonic-gate drv_usecwait(1000); 14840Sstevel@tonic-gate if (attempts >= 3) 14850Sstevel@tonic-gate goto restart_aspen_bmc; 14860Sstevel@tonic-gate ++attempts; 14870Sstevel@tonic-gate } 14880Sstevel@tonic-gate } while (byte != 0); 14890Sstevel@tonic-gate outb(MISMIC_CNTL_REGISTER, aspen_bmc[i].cntl); 14900Sstevel@tonic-gate outb(MISMIC_DATA_REGISTER, aspen_bmc[i].data); 14910Sstevel@tonic-gate byte = inb(MISMIC_FLAG_REGISTER); 14920Sstevel@tonic-gate byte |= 0x1; 14930Sstevel@tonic-gate outb(MISMIC_FLAG_REGISTER, byte); 14940Sstevel@tonic-gate } 14950Sstevel@tonic-gate break; 14960Sstevel@tonic-gate 14970Sstevel@tonic-gate case APIC_POWEROFF_VIA_SITKA_BMC: 14980Sstevel@tonic-gate restarts = 0; 14990Sstevel@tonic-gate restart_sitka_bmc: 15000Sstevel@tonic-gate if (++restarts == 3) 15010Sstevel@tonic-gate break; 15020Sstevel@tonic-gate attempts = 0; 15030Sstevel@tonic-gate do { 15040Sstevel@tonic-gate byte = inb(SMS_STATUS_REGISTER); 15050Sstevel@tonic-gate byte &= SMS_STATE_MASK; 15060Sstevel@tonic-gate if ((byte == SMS_READ_STATE) || 15070Sstevel@tonic-gate (byte == SMS_WRITE_STATE)) { 15080Sstevel@tonic-gate drv_usecwait(1000); 15090Sstevel@tonic-gate if (attempts >= 3) 15100Sstevel@tonic-gate goto restart_sitka_bmc; 15110Sstevel@tonic-gate ++attempts; 15120Sstevel@tonic-gate } 15130Sstevel@tonic-gate } while ((byte == SMS_READ_STATE) || 15140Sstevel@tonic-gate (byte == SMS_WRITE_STATE)); 15150Sstevel@tonic-gate outb(SMS_COMMAND_REGISTER, SMS_GET_STATUS); 15160Sstevel@tonic-gate i = 0; 15170Sstevel@tonic-gate for (; i < (sizeof (sitka_bmc)/sizeof (sitka_bmc[0])); 15180Sstevel@tonic-gate i++) { 15190Sstevel@tonic-gate attempts = 0; 15200Sstevel@tonic-gate do { 15210Sstevel@tonic-gate byte = inb(SMS_STATUS_REGISTER); 15220Sstevel@tonic-gate byte &= SMS_IBF_MASK; 15230Sstevel@tonic-gate if (byte != 0) { 15240Sstevel@tonic-gate drv_usecwait(1000); 15250Sstevel@tonic-gate if (attempts >= 3) 15260Sstevel@tonic-gate goto restart_sitka_bmc; 15270Sstevel@tonic-gate ++attempts; 15280Sstevel@tonic-gate } 15290Sstevel@tonic-gate } while (byte != 0); 15300Sstevel@tonic-gate outb(sitka_bmc[i].port, sitka_bmc[i].data); 15310Sstevel@tonic-gate } 15320Sstevel@tonic-gate break; 15330Sstevel@tonic-gate 15340Sstevel@tonic-gate case APIC_POWEROFF_NONE: 15350Sstevel@tonic-gate 15360Sstevel@tonic-gate /* If no APIC direct method, we will try using ACPI */ 15370Sstevel@tonic-gate if (apic_enable_acpi) { 15380Sstevel@tonic-gate if (acpi_poweroff() == 1) 15390Sstevel@tonic-gate return; 15400Sstevel@tonic-gate } else 15410Sstevel@tonic-gate return; 15420Sstevel@tonic-gate 15430Sstevel@tonic-gate break; 15440Sstevel@tonic-gate } 15450Sstevel@tonic-gate /* 15460Sstevel@tonic-gate * Wait a limited time here for power to go off. 15470Sstevel@tonic-gate * If the power does not go off, then there was a 15480Sstevel@tonic-gate * problem and we should continue to the halt which 15490Sstevel@tonic-gate * prints a message for the user to press a key to 15500Sstevel@tonic-gate * reboot. 15510Sstevel@tonic-gate */ 15520Sstevel@tonic-gate drv_usecwait(7000000); /* wait seven seconds */ 15530Sstevel@tonic-gate 15540Sstevel@tonic-gate } 15550Sstevel@tonic-gate 15560Sstevel@tonic-gate /* 15570Sstevel@tonic-gate * Try and disable all interrupts. We just assign interrupts to other 15580Sstevel@tonic-gate * processors based on policy. If any were bound by user request, we 15590Sstevel@tonic-gate * let them continue and return failure. We do not bother to check 15600Sstevel@tonic-gate * for cache affinity while rebinding. 15610Sstevel@tonic-gate */ 15620Sstevel@tonic-gate 15630Sstevel@tonic-gate static int 15640Sstevel@tonic-gate apic_disable_intr(processorid_t cpun) 15650Sstevel@tonic-gate { 15663446Smrj int bind_cpu = 0, i, hardbound = 0; 15670Sstevel@tonic-gate apic_irq_t *irq_ptr; 15683446Smrj ulong_t iflag; 15690Sstevel@tonic-gate 15700Sstevel@tonic-gate iflag = intr_clear(); 15710Sstevel@tonic-gate lock_set(&apic_ioapic_lock); 15723139Ssethg 15733139Ssethg for (i = 0; i <= APIC_MAX_VECTOR; i++) { 15743139Ssethg if (apic_reprogram_info[i].done == B_FALSE) { 15753139Ssethg if (apic_reprogram_info[i].bindcpu == cpun) { 15763139Ssethg /* 15773139Ssethg * CPU is busy -- it's the target of 15783139Ssethg * a pending reprogramming attempt 15793139Ssethg */ 15803139Ssethg lock_clear(&apic_ioapic_lock); 15813139Ssethg intr_restore(iflag); 15823139Ssethg return (PSM_FAILURE); 15833139Ssethg } 15843139Ssethg } 15853139Ssethg } 15863139Ssethg 15870Sstevel@tonic-gate apic_cpus[cpun].aci_status &= ~APIC_CPU_INTR_ENABLE; 15883139Ssethg 15890Sstevel@tonic-gate apic_cpus[cpun].aci_curipl = 0; 15903139Ssethg 15910Sstevel@tonic-gate i = apic_min_device_irq; 15920Sstevel@tonic-gate for (; i <= apic_max_device_irq; i++) { 15930Sstevel@tonic-gate /* 15940Sstevel@tonic-gate * If there are bound interrupts on this cpu, then 15950Sstevel@tonic-gate * rebind them to other processors. 15960Sstevel@tonic-gate */ 15970Sstevel@tonic-gate if ((irq_ptr = apic_irq_table[i]) != NULL) { 15980Sstevel@tonic-gate ASSERT((irq_ptr->airq_temp_cpu == IRQ_UNBOUND) || 15990Sstevel@tonic-gate (irq_ptr->airq_temp_cpu == IRQ_UNINIT) || 16000Sstevel@tonic-gate ((irq_ptr->airq_temp_cpu & ~IRQ_USER_BOUND) < 16010Sstevel@tonic-gate apic_nproc)); 16020Sstevel@tonic-gate 16030Sstevel@tonic-gate if (irq_ptr->airq_temp_cpu == (cpun | IRQ_USER_BOUND)) { 16040Sstevel@tonic-gate hardbound = 1; 16050Sstevel@tonic-gate continue; 16060Sstevel@tonic-gate } 16070Sstevel@tonic-gate 16080Sstevel@tonic-gate if (irq_ptr->airq_temp_cpu == cpun) { 16090Sstevel@tonic-gate do { 16103446Smrj bind_cpu = apic_next_bind_cpu++; 16110Sstevel@tonic-gate if (bind_cpu >= apic_nproc) { 16120Sstevel@tonic-gate apic_next_bind_cpu = 1; 16130Sstevel@tonic-gate bind_cpu = 0; 16140Sstevel@tonic-gate 16150Sstevel@tonic-gate } 16163139Ssethg } while (apic_rebind_all(irq_ptr, bind_cpu)); 16170Sstevel@tonic-gate } 16180Sstevel@tonic-gate } 16190Sstevel@tonic-gate } 16203139Ssethg 16213139Ssethg lock_clear(&apic_ioapic_lock); 16223139Ssethg intr_restore(iflag); 16233139Ssethg 16240Sstevel@tonic-gate if (hardbound) { 16250Sstevel@tonic-gate cmn_err(CE_WARN, "Could not disable interrupts on %d" 16260Sstevel@tonic-gate "due to user bound interrupts", cpun); 16270Sstevel@tonic-gate return (PSM_FAILURE); 16280Sstevel@tonic-gate } 16290Sstevel@tonic-gate else 16300Sstevel@tonic-gate return (PSM_SUCCESS); 16310Sstevel@tonic-gate } 16320Sstevel@tonic-gate 16330Sstevel@tonic-gate static void 16340Sstevel@tonic-gate apic_enable_intr(processorid_t cpun) 16350Sstevel@tonic-gate { 16363446Smrj int i; 16370Sstevel@tonic-gate apic_irq_t *irq_ptr; 16383446Smrj ulong_t iflag; 16390Sstevel@tonic-gate 16400Sstevel@tonic-gate iflag = intr_clear(); 16410Sstevel@tonic-gate lock_set(&apic_ioapic_lock); 16423139Ssethg 16430Sstevel@tonic-gate apic_cpus[cpun].aci_status |= APIC_CPU_INTR_ENABLE; 16440Sstevel@tonic-gate 16450Sstevel@tonic-gate i = apic_min_device_irq; 16460Sstevel@tonic-gate for (i = apic_min_device_irq; i <= apic_max_device_irq; i++) { 16470Sstevel@tonic-gate if ((irq_ptr = apic_irq_table[i]) != NULL) { 16480Sstevel@tonic-gate if ((irq_ptr->airq_cpu & ~IRQ_USER_BOUND) == cpun) { 16490Sstevel@tonic-gate (void) apic_rebind_all(irq_ptr, 16503139Ssethg irq_ptr->airq_cpu); 16510Sstevel@tonic-gate } 16520Sstevel@tonic-gate } 16530Sstevel@tonic-gate } 16543139Ssethg 16553139Ssethg lock_clear(&apic_ioapic_lock); 16563139Ssethg intr_restore(iflag); 16570Sstevel@tonic-gate } 16580Sstevel@tonic-gate 16590Sstevel@tonic-gate 16600Sstevel@tonic-gate /* 16610Sstevel@tonic-gate * This function will reprogram the timer. 16620Sstevel@tonic-gate * 16630Sstevel@tonic-gate * When in oneshot mode the argument is the absolute time in future to 16640Sstevel@tonic-gate * generate the interrupt at. 16650Sstevel@tonic-gate * 16660Sstevel@tonic-gate * When in periodic mode, the argument is the interval at which the 16670Sstevel@tonic-gate * interrupts should be generated. There is no need to support the periodic 16680Sstevel@tonic-gate * mode timer change at this time. 16690Sstevel@tonic-gate */ 16700Sstevel@tonic-gate static void 16710Sstevel@tonic-gate apic_timer_reprogram(hrtime_t time) 16720Sstevel@tonic-gate { 16730Sstevel@tonic-gate hrtime_t now; 16740Sstevel@tonic-gate uint_t ticks; 16753446Smrj int64_t delta; 16760Sstevel@tonic-gate 16770Sstevel@tonic-gate /* 16780Sstevel@tonic-gate * We should be called from high PIL context (CBE_HIGH_PIL), 16790Sstevel@tonic-gate * so kpreempt is disabled. 16800Sstevel@tonic-gate */ 16810Sstevel@tonic-gate 16820Sstevel@tonic-gate if (!apic_oneshot) { 16830Sstevel@tonic-gate /* time is the interval for periodic mode */ 16842992Sdmick ticks = APIC_NSECS_TO_TICKS(time); 16850Sstevel@tonic-gate } else { 16860Sstevel@tonic-gate /* one shot mode */ 16870Sstevel@tonic-gate 16880Sstevel@tonic-gate now = gethrtime(); 16892992Sdmick delta = time - now; 16902992Sdmick 16912992Sdmick if (delta <= 0) { 16920Sstevel@tonic-gate /* 16930Sstevel@tonic-gate * requested to generate an interrupt in the past 16940Sstevel@tonic-gate * generate an interrupt as soon as possible 16950Sstevel@tonic-gate */ 16960Sstevel@tonic-gate ticks = apic_min_timer_ticks; 16972992Sdmick } else if (delta > apic_nsec_max) { 16980Sstevel@tonic-gate /* 16990Sstevel@tonic-gate * requested to generate an interrupt at a time 17000Sstevel@tonic-gate * further than what we are capable of. Set to max 17010Sstevel@tonic-gate * the hardware can handle 17020Sstevel@tonic-gate */ 17030Sstevel@tonic-gate 17040Sstevel@tonic-gate ticks = APIC_MAXVAL; 17050Sstevel@tonic-gate #ifdef DEBUG 17060Sstevel@tonic-gate cmn_err(CE_CONT, "apic_timer_reprogram, request at" 17070Sstevel@tonic-gate " %lld too far in future, current time" 17080Sstevel@tonic-gate " %lld \n", time, now); 17092992Sdmick #endif 17100Sstevel@tonic-gate } else 17112992Sdmick ticks = APIC_NSECS_TO_TICKS(delta); 17120Sstevel@tonic-gate } 17130Sstevel@tonic-gate 17140Sstevel@tonic-gate if (ticks < apic_min_timer_ticks) 17150Sstevel@tonic-gate ticks = apic_min_timer_ticks; 17160Sstevel@tonic-gate 17170Sstevel@tonic-gate apicadr[APIC_INIT_COUNT] = ticks; 17180Sstevel@tonic-gate 17190Sstevel@tonic-gate } 17200Sstevel@tonic-gate 17210Sstevel@tonic-gate /* 17220Sstevel@tonic-gate * This function will enable timer interrupts. 17230Sstevel@tonic-gate */ 17240Sstevel@tonic-gate static void 17250Sstevel@tonic-gate apic_timer_enable(void) 17260Sstevel@tonic-gate { 17270Sstevel@tonic-gate /* 17280Sstevel@tonic-gate * We should be Called from high PIL context (CBE_HIGH_PIL), 17290Sstevel@tonic-gate * so kpreempt is disabled. 17300Sstevel@tonic-gate */ 17310Sstevel@tonic-gate 17320Sstevel@tonic-gate if (!apic_oneshot) 17330Sstevel@tonic-gate apicadr[APIC_LOCAL_TIMER] = 17340Sstevel@tonic-gate (apic_clkvect + APIC_BASE_VECT) | AV_TIME; 17350Sstevel@tonic-gate else { 17360Sstevel@tonic-gate /* one shot */ 17370Sstevel@tonic-gate apicadr[APIC_LOCAL_TIMER] = (apic_clkvect + APIC_BASE_VECT); 17380Sstevel@tonic-gate } 17390Sstevel@tonic-gate } 17400Sstevel@tonic-gate 17410Sstevel@tonic-gate /* 17420Sstevel@tonic-gate * This function will disable timer interrupts. 17430Sstevel@tonic-gate */ 17440Sstevel@tonic-gate static void 17450Sstevel@tonic-gate apic_timer_disable(void) 17460Sstevel@tonic-gate { 17470Sstevel@tonic-gate /* 17480Sstevel@tonic-gate * We should be Called from high PIL context (CBE_HIGH_PIL), 17490Sstevel@tonic-gate * so kpreempt is disabled. 17500Sstevel@tonic-gate */ 17510Sstevel@tonic-gate 17520Sstevel@tonic-gate apicadr[APIC_LOCAL_TIMER] = (apic_clkvect + APIC_BASE_VECT) | AV_MASK; 17530Sstevel@tonic-gate } 17540Sstevel@tonic-gate 17550Sstevel@tonic-gate 17560Sstevel@tonic-gate cyclic_id_t apic_cyclic_id; 17570Sstevel@tonic-gate 17580Sstevel@tonic-gate /* 17590Sstevel@tonic-gate * If this module needs to be a consumer of cyclic subsystem, they 17600Sstevel@tonic-gate * can be added here, since at this time kernel cyclic subsystem is initialized 17610Sstevel@tonic-gate * argument is not currently used, and is reserved for future. 17620Sstevel@tonic-gate */ 17630Sstevel@tonic-gate static void 17640Sstevel@tonic-gate apic_post_cyclic_setup(void *arg) 17650Sstevel@tonic-gate { 17660Sstevel@tonic-gate _NOTE(ARGUNUSED(arg)) 17670Sstevel@tonic-gate cyc_handler_t hdlr; 17680Sstevel@tonic-gate cyc_time_t when; 17690Sstevel@tonic-gate 17700Sstevel@tonic-gate /* cpu_lock is held */ 17710Sstevel@tonic-gate 17720Sstevel@tonic-gate /* set up cyclics for intr redistribution */ 17730Sstevel@tonic-gate 17740Sstevel@tonic-gate /* 17750Sstevel@tonic-gate * In peridoc mode intr redistribution processing is done in 17760Sstevel@tonic-gate * apic_intr_enter during clk intr processing 17770Sstevel@tonic-gate */ 17780Sstevel@tonic-gate if (!apic_oneshot) 17790Sstevel@tonic-gate return; 17800Sstevel@tonic-gate 17810Sstevel@tonic-gate hdlr.cyh_level = CY_LOW_LEVEL; 17820Sstevel@tonic-gate hdlr.cyh_func = (cyc_func_t)apic_redistribute_compute; 17830Sstevel@tonic-gate hdlr.cyh_arg = NULL; 17840Sstevel@tonic-gate 17850Sstevel@tonic-gate when.cyt_when = 0; 17860Sstevel@tonic-gate when.cyt_interval = apic_redistribute_sample_interval; 17870Sstevel@tonic-gate apic_cyclic_id = cyclic_add(&hdlr, &when); 17880Sstevel@tonic-gate 17890Sstevel@tonic-gate 17900Sstevel@tonic-gate } 17910Sstevel@tonic-gate 17920Sstevel@tonic-gate static void 17930Sstevel@tonic-gate apic_redistribute_compute(void) 17940Sstevel@tonic-gate { 17950Sstevel@tonic-gate int i, j, max_busy; 17960Sstevel@tonic-gate 17970Sstevel@tonic-gate if (apic_enable_dynamic_migration) { 17980Sstevel@tonic-gate if (++apic_nticks == apic_sample_factor_redistribution) { 17990Sstevel@tonic-gate /* 18000Sstevel@tonic-gate * Time to call apic_intr_redistribute(). 18010Sstevel@tonic-gate * reset apic_nticks. This will cause max_busy 18020Sstevel@tonic-gate * to be calculated below and if it is more than 18030Sstevel@tonic-gate * apic_int_busy, we will do the whole thing 18040Sstevel@tonic-gate */ 18050Sstevel@tonic-gate apic_nticks = 0; 18060Sstevel@tonic-gate } 18070Sstevel@tonic-gate max_busy = 0; 18080Sstevel@tonic-gate for (i = 0; i < apic_nproc; i++) { 18090Sstevel@tonic-gate 18100Sstevel@tonic-gate /* 18110Sstevel@tonic-gate * Check if curipl is non zero & if ISR is in 18120Sstevel@tonic-gate * progress 18130Sstevel@tonic-gate */ 18140Sstevel@tonic-gate if (((j = apic_cpus[i].aci_curipl) != 0) && 18150Sstevel@tonic-gate (apic_cpus[i].aci_ISR_in_progress & (1 << j))) { 18160Sstevel@tonic-gate 18170Sstevel@tonic-gate int irq; 18180Sstevel@tonic-gate apic_cpus[i].aci_busy++; 18190Sstevel@tonic-gate irq = apic_cpus[i].aci_current[j]; 18200Sstevel@tonic-gate apic_irq_table[irq]->airq_busy++; 18210Sstevel@tonic-gate } 18220Sstevel@tonic-gate 18230Sstevel@tonic-gate if (!apic_nticks && 18240Sstevel@tonic-gate (apic_cpus[i].aci_busy > max_busy)) 18250Sstevel@tonic-gate max_busy = apic_cpus[i].aci_busy; 18260Sstevel@tonic-gate } 18270Sstevel@tonic-gate if (!apic_nticks) { 18280Sstevel@tonic-gate if (max_busy > apic_int_busy_mark) { 18290Sstevel@tonic-gate /* 18300Sstevel@tonic-gate * We could make the following check be 18310Sstevel@tonic-gate * skipped > 1 in which case, we get a 18320Sstevel@tonic-gate * redistribution at half the busy mark (due to 18330Sstevel@tonic-gate * double interval). Need to be able to collect 18340Sstevel@tonic-gate * more empirical data to decide if that is a 18350Sstevel@tonic-gate * good strategy. Punt for now. 18360Sstevel@tonic-gate */ 18373446Smrj if (apic_skipped_redistribute) { 18380Sstevel@tonic-gate apic_cleanup_busy(); 18393446Smrj apic_skipped_redistribute = 0; 18403446Smrj } else { 18410Sstevel@tonic-gate apic_intr_redistribute(); 18423446Smrj } 18430Sstevel@tonic-gate } else 18440Sstevel@tonic-gate apic_skipped_redistribute++; 18450Sstevel@tonic-gate } 18460Sstevel@tonic-gate } 18470Sstevel@tonic-gate } 18480Sstevel@tonic-gate 18490Sstevel@tonic-gate 18503446Smrj /* 18513446Smrj * The following functions are in the platform specific file so that they 18523446Smrj * can be different functions depending on whether we are running on 18533446Smrj * bare metal or a hypervisor. 18543446Smrj */ 18550Sstevel@tonic-gate 18563446Smrj /* 18573446Smrj * map an apic for memory-mapped access 18583446Smrj */ 18593446Smrj uint32_t * 18603446Smrj mapin_apic(uint32_t addr, size_t len, int flags) 18613446Smrj { 18623446Smrj /*LINTED: pointer cast may result in improper alignment */ 18633446Smrj return ((uint32_t *)psm_map_phys(addr, len, flags)); 18643446Smrj } 18650Sstevel@tonic-gate 18663446Smrj uint32_t * 18673446Smrj mapin_ioapic(uint32_t addr, size_t len, int flags) 18683446Smrj { 18693446Smrj return (mapin_apic(addr, len, flags)); 18700Sstevel@tonic-gate } 18710Sstevel@tonic-gate 18720Sstevel@tonic-gate /* 18733446Smrj * unmap an apic 18743139Ssethg */ 18753446Smrj void 18763446Smrj mapout_apic(caddr_t addr, size_t len) 18773139Ssethg { 18783446Smrj psm_unmap_phys(addr, len); 18793139Ssethg } 18803139Ssethg 18813446Smrj void 18823446Smrj mapout_ioapic(caddr_t addr, size_t len) 18833139Ssethg { 18843446Smrj mapout_apic(addr, len); 18853139Ssethg } 18863139Ssethg 18873139Ssethg /* 1888*4937Sjohnny * Check to make sure there are enough irq slots 18893139Ssethg */ 18903446Smrj int 1891*4937Sjohnny apic_check_free_irqs(int count) 1892*4937Sjohnny { 1893*4937Sjohnny int i, avail; 1894*4937Sjohnny 1895*4937Sjohnny avail = 0; 1896*4937Sjohnny for (i = APIC_FIRST_FREE_IRQ; i < APIC_RESV_IRQ; i++) { 1897*4937Sjohnny if ((apic_irq_table[i] == NULL) || 1898*4937Sjohnny apic_irq_table[i]->airq_mps_intr_index == FREE_INDEX) { 1899*4937Sjohnny if (++avail >= count) 1900*4937Sjohnny return (PSM_SUCCESS); 1901*4937Sjohnny } 1902*4937Sjohnny } 1903*4937Sjohnny return (PSM_FAILURE); 1904*4937Sjohnny } 1905*4937Sjohnny 1906*4937Sjohnny /* 1907*4937Sjohnny * This function allocates "count" MSI vector(s) for the given "dip/pri/type" 1908*4937Sjohnny */ 1909*4937Sjohnny int 1910*4937Sjohnny apic_alloc_msi_vectors(dev_info_t *dip, int inum, int count, int pri, 19113446Smrj int behavior) 19123139Ssethg { 19133446Smrj int rcount, i; 19143446Smrj uchar_t start, irqno, cpu; 19153446Smrj major_t major; 19163446Smrj apic_irq_t *irqptr; 19173139Ssethg 1918*4937Sjohnny DDI_INTR_IMPLDBG((CE_CONT, "apic_alloc_msi_vectors: dip=0x%p " 19193446Smrj "inum=0x%x pri=0x%x count=0x%x behavior=%d\n", 1920*4937Sjohnny (void *)dip, inum, pri, count, behavior)); 19213139Ssethg 19223446Smrj if (count > 1) { 19233446Smrj if (behavior == DDI_INTR_ALLOC_STRICT && 19243446Smrj (apic_multi_msi_enable == 0 || count > apic_multi_msi_max)) 19253446Smrj return (0); 19263139Ssethg 19273446Smrj if (apic_multi_msi_enable == 0) 19283446Smrj count = 1; 19293446Smrj else if (count > apic_multi_msi_max) 19303446Smrj count = apic_multi_msi_max; 19313446Smrj } 19323139Ssethg 19333446Smrj if ((rcount = apic_navail_vector(dip, pri)) > count) 19343446Smrj rcount = count; 19353446Smrj else if (rcount == 0 || (rcount < count && 19363446Smrj behavior == DDI_INTR_ALLOC_STRICT)) 19373446Smrj return (0); 19383139Ssethg 19393446Smrj /* if not ISP2, then round it down */ 19403446Smrj if (!ISP2(rcount)) 19413446Smrj rcount = 1 << (highbit(rcount) - 1); 19423139Ssethg 19433446Smrj mutex_enter(&airq_mutex); 19443446Smrj 19453446Smrj for (start = 0; rcount > 0; rcount >>= 1) { 19463446Smrj if ((start = apic_find_multi_vectors(pri, rcount)) != 0 || 19473446Smrj behavior == DDI_INTR_ALLOC_STRICT) 19483446Smrj break; 19493139Ssethg } 19503139Ssethg 19513446Smrj if (start == 0) { 19523446Smrj /* no vector available */ 19533446Smrj mutex_exit(&airq_mutex); 19543446Smrj return (0); 19553446Smrj } 19563446Smrj 1957*4937Sjohnny if (apic_check_free_irqs(rcount) == PSM_FAILURE) { 1958*4937Sjohnny /* not enough free irq slots available */ 1959*4937Sjohnny mutex_exit(&airq_mutex); 1960*4937Sjohnny return (0); 1961*4937Sjohnny } 1962*4937Sjohnny 19633446Smrj major = (dip != NULL) ? ddi_name_to_major(ddi_get_name(dip)) : 0; 19643446Smrj for (i = 0; i < rcount; i++) { 19653446Smrj if ((irqno = apic_allocate_irq(apic_first_avail_irq)) == 19663446Smrj (uchar_t)-1) { 1967*4937Sjohnny /* 1968*4937Sjohnny * shouldn't happen because of the 1969*4937Sjohnny * apic_check_free_irqs() check earlier 1970*4937Sjohnny */ 19713446Smrj mutex_exit(&airq_mutex); 1972*4937Sjohnny DDI_INTR_IMPLDBG((CE_CONT, "apic_alloc_msi_vectors: " 19733446Smrj "apic_allocate_irq failed\n")); 19743446Smrj return (i); 19753446Smrj } 19763446Smrj apic_max_device_irq = max(irqno, apic_max_device_irq); 19773446Smrj apic_min_device_irq = min(irqno, apic_min_device_irq); 19783446Smrj irqptr = apic_irq_table[irqno]; 19793446Smrj #ifdef DEBUG 19803446Smrj if (apic_vector_to_irq[start + i] != APIC_RESV_IRQ) 1981*4937Sjohnny DDI_INTR_IMPLDBG((CE_CONT, "apic_alloc_msi_vectors: " 19823446Smrj "apic_vector_to_irq is not APIC_RESV_IRQ\n")); 19833446Smrj #endif 19843446Smrj apic_vector_to_irq[start + i] = (uchar_t)irqno; 19853446Smrj 19863446Smrj irqptr->airq_vector = (uchar_t)(start + i); 19873446Smrj irqptr->airq_ioapicindex = (uchar_t)inum; /* start */ 19883446Smrj irqptr->airq_intin_no = (uchar_t)rcount; 19893446Smrj irqptr->airq_ipl = pri; 19903446Smrj irqptr->airq_vector = start + i; 19913446Smrj irqptr->airq_origirq = (uchar_t)(inum + i); 19923446Smrj irqptr->airq_share_id = 0; 19933446Smrj irqptr->airq_mps_intr_index = MSI_INDEX; 19943446Smrj irqptr->airq_dip = dip; 19953446Smrj irqptr->airq_major = major; 19963446Smrj if (i == 0) /* they all bound to the same cpu */ 19973446Smrj cpu = irqptr->airq_cpu = apic_bind_intr(dip, irqno, 19984397Sschwartz 0xff, 0xff); 19993446Smrj else 20003446Smrj irqptr->airq_cpu = cpu; 2001*4937Sjohnny DDI_INTR_IMPLDBG((CE_CONT, "apic_alloc_msi_vectors: irq=0x%x " 20023446Smrj "dip=0x%p vector=0x%x origirq=0x%x pri=0x%x\n", irqno, 20033446Smrj (void *)irqptr->airq_dip, irqptr->airq_vector, 20043446Smrj irqptr->airq_origirq, pri)); 20053446Smrj } 20063446Smrj mutex_exit(&airq_mutex); 20073446Smrj return (rcount); 20083139Ssethg } 20093139Ssethg 20103139Ssethg /* 2011*4937Sjohnny * This function allocates "count" MSI-X vector(s) for the given "dip/pri/type" 2012*4937Sjohnny */ 2013*4937Sjohnny int 2014*4937Sjohnny apic_alloc_msix_vectors(dev_info_t *dip, int inum, int count, int pri, 2015*4937Sjohnny int behavior) 2016*4937Sjohnny { 2017*4937Sjohnny int rcount, i; 2018*4937Sjohnny major_t major; 2019*4937Sjohnny 2020*4937Sjohnny if (count > 1) { 2021*4937Sjohnny if (behavior == DDI_INTR_ALLOC_STRICT) { 2022*4937Sjohnny if (count > apic_msix_max) 2023*4937Sjohnny return (0); 2024*4937Sjohnny } else if (count > apic_msix_max) 2025*4937Sjohnny count = apic_msix_max; 2026*4937Sjohnny } 2027*4937Sjohnny 2028*4937Sjohnny mutex_enter(&airq_mutex); 2029*4937Sjohnny 2030*4937Sjohnny if ((rcount = apic_navail_vector(dip, pri)) > count) 2031*4937Sjohnny rcount = count; 2032*4937Sjohnny else if (rcount == 0 || (rcount < count && 2033*4937Sjohnny behavior == DDI_INTR_ALLOC_STRICT)) { 2034*4937Sjohnny rcount = 0; 2035*4937Sjohnny goto out; 2036*4937Sjohnny } 2037*4937Sjohnny 2038*4937Sjohnny if (apic_check_free_irqs(rcount) == PSM_FAILURE) { 2039*4937Sjohnny /* not enough free irq slots available */ 2040*4937Sjohnny rcount = 0; 2041*4937Sjohnny goto out; 2042*4937Sjohnny } 2043*4937Sjohnny 2044*4937Sjohnny major = (dip != NULL) ? ddi_name_to_major(ddi_get_name(dip)) : 0; 2045*4937Sjohnny for (i = 0; i < rcount; i++) { 2046*4937Sjohnny uchar_t vector, irqno; 2047*4937Sjohnny apic_irq_t *irqptr; 2048*4937Sjohnny 2049*4937Sjohnny if ((irqno = apic_allocate_irq(apic_first_avail_irq)) == 2050*4937Sjohnny (uchar_t)-1) { 2051*4937Sjohnny /* 2052*4937Sjohnny * shouldn't happen because of the 2053*4937Sjohnny * apic_check_free_irqs() check earlier 2054*4937Sjohnny */ 2055*4937Sjohnny DDI_INTR_IMPLDBG((CE_CONT, "apic_alloc_msix_vectors: " 2056*4937Sjohnny "apic_allocate_irq failed\n")); 2057*4937Sjohnny rcount = i; 2058*4937Sjohnny goto out; 2059*4937Sjohnny } 2060*4937Sjohnny if ((vector = apic_allocate_vector(pri, irqno, 1)) == 0) { 2061*4937Sjohnny /* 2062*4937Sjohnny * shouldn't happen because of the 2063*4937Sjohnny * apic_navail_vector() call earlier 2064*4937Sjohnny */ 2065*4937Sjohnny DDI_INTR_IMPLDBG((CE_CONT, "apic_alloc_msix_vectors: " 2066*4937Sjohnny "apic_allocate_vector failed\n")); 2067*4937Sjohnny rcount = i; 2068*4937Sjohnny goto out; 2069*4937Sjohnny } 2070*4937Sjohnny apic_max_device_irq = max(irqno, apic_max_device_irq); 2071*4937Sjohnny apic_min_device_irq = min(irqno, apic_min_device_irq); 2072*4937Sjohnny irqptr = apic_irq_table[irqno]; 2073*4937Sjohnny irqptr->airq_vector = (uchar_t)vector; 2074*4937Sjohnny irqptr->airq_ipl = pri; 2075*4937Sjohnny irqptr->airq_origirq = (uchar_t)(inum + i); 2076*4937Sjohnny irqptr->airq_share_id = 0; 2077*4937Sjohnny irqptr->airq_mps_intr_index = MSIX_INDEX; 2078*4937Sjohnny irqptr->airq_dip = dip; 2079*4937Sjohnny irqptr->airq_major = major; 2080*4937Sjohnny irqptr->airq_cpu = apic_bind_intr(dip, irqno, 0xff, 0xff); 2081*4937Sjohnny } 2082*4937Sjohnny out: 2083*4937Sjohnny mutex_exit(&airq_mutex); 2084*4937Sjohnny return (rcount); 2085*4937Sjohnny } 2086*4937Sjohnny 2087*4937Sjohnny /* 20883446Smrj * Allocate a free vector for irq at ipl. Takes care of merging of multiple 20893446Smrj * IPLs into a single APIC level as well as stretching some IPLs onto multiple 20903446Smrj * levels. APIC_HI_PRI_VECTS interrupts are reserved for high priority 20913446Smrj * requests and allocated only when pri is set. 20920Sstevel@tonic-gate */ 20933446Smrj uchar_t 20943446Smrj apic_allocate_vector(int ipl, int irq, int pri) 20950Sstevel@tonic-gate { 20963446Smrj int lowest, highest, i; 20970Sstevel@tonic-gate 20983446Smrj highest = apic_ipltopri[ipl] + APIC_VECTOR_MASK; 20993446Smrj lowest = apic_ipltopri[ipl - 1] + APIC_VECTOR_PER_IPL; 21000Sstevel@tonic-gate 21013446Smrj if (highest < lowest) /* Both ipl and ipl - 1 map to same pri */ 21023446Smrj lowest -= APIC_VECTOR_PER_IPL; 21033139Ssethg 21043446Smrj #ifdef DEBUG 21053446Smrj if (apic_restrict_vector) /* for testing shared interrupt logic */ 21063446Smrj highest = lowest + apic_restrict_vector + APIC_HI_PRI_VECTS; 21073446Smrj #endif /* DEBUG */ 21083446Smrj if (pri == 0) 21093446Smrj highest -= APIC_HI_PRI_VECTS; 21103139Ssethg 21113446Smrj for (i = lowest; i < highest; i++) { 21123446Smrj if (APIC_CHECK_RESERVE_VECTORS(i)) 21133446Smrj continue; 21143446Smrj if (apic_vector_to_irq[i] == APIC_RESV_IRQ) { 21153446Smrj apic_vector_to_irq[i] = (uchar_t)irq; 21163446Smrj return (i); 21170Sstevel@tonic-gate } 21180Sstevel@tonic-gate } 21190Sstevel@tonic-gate 21203446Smrj return (0); 21213446Smrj } 21223446Smrj 21233446Smrj /* Mark vector as not being used by any irq */ 21243446Smrj void 21253446Smrj apic_free_vector(uchar_t vector) 21263446Smrj { 21273446Smrj apic_vector_to_irq[vector] = APIC_RESV_IRQ; 21283446Smrj } 21293446Smrj 21303446Smrj uint32_t 21313446Smrj ioapic_read(int ioapic_ix, uint32_t reg) 21323446Smrj { 21333446Smrj volatile uint32_t *ioapic; 21343446Smrj 21353446Smrj ioapic = apicioadr[ioapic_ix]; 21363446Smrj ioapic[APIC_IO_REG] = reg; 21373446Smrj return (ioapic[APIC_IO_DATA]); 21383446Smrj } 21393139Ssethg 21403446Smrj void 21413446Smrj ioapic_write(int ioapic_ix, uint32_t reg, uint32_t value) 21423446Smrj { 21433446Smrj volatile uint32_t *ioapic; 21443446Smrj 21453446Smrj ioapic = apicioadr[ioapic_ix]; 21463446Smrj ioapic[APIC_IO_REG] = reg; 21473446Smrj ioapic[APIC_IO_DATA] = value; 21483446Smrj } 21493446Smrj 21503446Smrj static processorid_t 21513446Smrj apic_find_cpu(int flag) 21523446Smrj { 21533446Smrj processorid_t acid = 0; 21543446Smrj int i; 21553446Smrj 21563446Smrj /* Find the first CPU with the passed-in flag set */ 21573446Smrj for (i = 0; i < apic_nproc; i++) { 21583446Smrj if (apic_cpus[i].aci_status & flag) { 21593446Smrj acid = i; 21603446Smrj break; 21613446Smrj } 21623446Smrj } 21633446Smrj 21643446Smrj ASSERT((apic_cpus[acid].aci_status & flag) != 0); 21653446Smrj return (acid); 21663446Smrj } 21673139Ssethg 21683446Smrj /* 21693446Smrj * Call rebind to do the actual programming. 21703446Smrj * Must be called with interrupts disabled and apic_ioapic_lock held 21713446Smrj * 'p' is polymorphic -- if this function is called to process a deferred 21723446Smrj * reprogramming, p is of type 'struct ioapic_reprogram_data *', from which 21733446Smrj * the irq pointer is retrieved. If not doing deferred reprogramming, 21743446Smrj * p is of the type 'apic_irq_t *'. 21753446Smrj * 21763446Smrj * apic_ioapic_lock must be held across this call, as it protects apic_rebind 21773446Smrj * and it protects apic_find_cpu() from a race in which a CPU can be taken 21783446Smrj * offline after a cpu is selected, but before apic_rebind is called to 21793446Smrj * bind interrupts to it. 21803446Smrj */ 21813446Smrj int 21823446Smrj apic_setup_io_intr(void *p, int irq, boolean_t deferred) 21833446Smrj { 21843446Smrj apic_irq_t *irqptr; 21853446Smrj struct ioapic_reprogram_data *drep = NULL; 21863446Smrj int rv; 21873446Smrj 21883446Smrj if (deferred) { 21893446Smrj drep = (struct ioapic_reprogram_data *)p; 21903446Smrj ASSERT(drep != NULL); 21913446Smrj irqptr = drep->irqp; 21923446Smrj } else 21933446Smrj irqptr = (apic_irq_t *)p; 21943446Smrj 21953446Smrj ASSERT(irqptr != NULL); 21963446Smrj 21973446Smrj rv = apic_rebind(irqptr, apic_irq_table[irq]->airq_cpu, drep); 21983446Smrj if (rv) { 21993446Smrj /* 22003446Smrj * CPU is not up or interrupts are disabled. Fall back to 22013446Smrj * the first available CPU 22023446Smrj */ 22033446Smrj rv = apic_rebind(irqptr, apic_find_cpu(APIC_CPU_INTR_ENABLE), 22043446Smrj drep); 22053446Smrj } 22063446Smrj 22073446Smrj return (rv); 22080Sstevel@tonic-gate } 22093446Smrj 22103446Smrj 22113446Smrj uchar_t 22123446Smrj apic_modify_vector(uchar_t vector, int irq) 22133446Smrj { 22143446Smrj apic_vector_to_irq[vector] = (uchar_t)irq; 22153446Smrj return (vector); 22163446Smrj } 22174397Sschwartz 22184397Sschwartz char * 22194397Sschwartz apic_get_apic_type() 22204397Sschwartz { 22214397Sschwartz return (apic_psm_info.p_mach_idstring); 22224397Sschwartz } 2223