16356Smrj /*
26356Smrj * CDDL HEADER START
36356Smrj *
46356Smrj * The contents of this file are subject to the terms of the
56356Smrj * Common Development and Distribution License (the "License").
66356Smrj * You may not use this file except in compliance with the License.
76356Smrj *
86356Smrj * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
96356Smrj * or http://www.opensolaris.org/os/licensing.
106356Smrj * See the License for the specific language governing permissions
116356Smrj * and limitations under the License.
126356Smrj *
136356Smrj * When distributing Covered Code, include this CDDL HEADER in each
146356Smrj * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
156356Smrj * If applicable, add the following below this CDDL HEADER, with the
166356Smrj * fields enclosed by brackets "[]" replaced with your own identifying
176356Smrj * information: Portions Copyright [yyyy] [name of copyright owner]
186356Smrj *
196356Smrj * CDDL HEADER END
206356Smrj */
216356Smrj
226356Smrj /*
236356Smrj * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
246356Smrj * Use is subject to license terms.
256356Smrj */
266356Smrj
27*12004Sjiang.liu@intel.com #define PSMI_1_7
286356Smrj
296356Smrj #include <sys/mutex.h>
306356Smrj #include <sys/types.h>
316356Smrj #include <sys/time.h>
326356Smrj #include <sys/clock.h>
336356Smrj #include <sys/machlock.h>
346356Smrj #include <sys/smp_impldefs.h>
356356Smrj #include <sys/uadmin.h>
366356Smrj #include <sys/promif.h>
376356Smrj #include <sys/psm.h>
386356Smrj #include <sys/psm_common.h>
396356Smrj #include <sys/atomic.h>
406356Smrj #include <sys/archsystm.h>
416356Smrj #include <sys/mach_intr.h>
426356Smrj #include <sys/hypervisor.h>
436356Smrj #include <sys/evtchn_impl.h>
446356Smrj #include <sys/modctl.h>
456356Smrj #include <sys/trap.h>
466356Smrj #include <sys/panic.h>
476356Smrj
486356Smrj #include <xen/public/vcpu.h>
496356Smrj #include <xen/public/physdev.h>
506356Smrj
516356Smrj
526356Smrj /*
536356Smrj * Global Data
546356Smrj */
556356Smrj int xen_uppc_use_acpi = 1; /* Use ACPI by default */
566356Smrj int xen_uppc_enable_acpi = 0;
576356Smrj
586356Smrj static int xen_clock_irq = -1;
596356Smrj
606356Smrj /*
616356Smrj * For interrupt link devices, if xen_uppc_unconditional_srs is set, an irq
626356Smrj * resource will be assigned (via _SRS). If it is not set, use the current
636356Smrj * irq setting (via _CRS), but only if that irq is in the set of possible
646356Smrj * irqs (returned by _PRS) for the device.
656356Smrj */
666356Smrj int xen_uppc_unconditional_srs = 1;
676356Smrj
686356Smrj /*
696356Smrj * For interrupt link devices, if xen_uppc_prefer_crs is set when we are
706356Smrj * assigning an IRQ resource to a device, prefer the current IRQ setting
716356Smrj * over other possible irq settings under same conditions.
726356Smrj */
736356Smrj int xen_uppc_prefer_crs = 1;
746356Smrj
756356Smrj int xen_uppc_verbose = 0;
766356Smrj
776356Smrj /* flag definitions for xen_uppc_verbose */
786356Smrj #define XEN_UPPC_VERBOSE_IRQ_FLAG 0x00000001
796356Smrj #define XEN_UPPC_VERBOSE_POWEROFF_FLAG 0x00000002
806356Smrj #define XEN_UPPC_VERBOSE_POWEROFF_PAUSE_FLAG 0x00000004
816356Smrj
826356Smrj #define XEN_UPPC_VERBOSE_IRQ(fmt) \
836356Smrj if (xen_uppc_verbose & XEN_UPPC_VERBOSE_IRQ_FLAG) \
846356Smrj cmn_err fmt;
856356Smrj
866356Smrj #define XEN_UPPC_VERBOSE_POWEROFF(fmt) \
876356Smrj if (xen_uppc_verbose & XEN_UPPC_VERBOSE_POWEROFF_FLAG) \
886356Smrj prom_printf fmt;
896356Smrj
906356Smrj uchar_t xen_uppc_reserved_irqlist[MAX_ISA_IRQ + 1];
916356Smrj
926356Smrj static uint16_t xen_uppc_irq_shared_table[MAX_ISA_IRQ + 1];
936356Smrj
946356Smrj /*
956356Smrj * Contains SCI irqno from FADT after initialization
966356Smrj */
976356Smrj static int xen_uppc_sci = -1;
986356Smrj
996356Smrj static struct psm_info xen_uppc_info;
1006356Smrj
1016356Smrj /*
1026356Smrj * Local support routines
1036356Smrj */
1046356Smrj
1056356Smrj static int
xen_uppc_init_acpi(void)1066356Smrj xen_uppc_init_acpi(void)
1076356Smrj {
1086356Smrj int verboseflags = 0;
1096356Smrj int sci;
1106356Smrj iflag_t sci_flags;
1116356Smrj
1126356Smrj /*
1136356Smrj * Process SCI configuration here; this may return
1146356Smrj * an error if acpi-user-options has specified
1156356Smrj * legacy mode (use ACPI without ACPI mode or SCI)
1166356Smrj */
1176356Smrj if (acpica_get_sci(&sci, &sci_flags) != AE_OK)
1186356Smrj sci = -1;
1196356Smrj
1206356Smrj /*
1216356Smrj * Initialize sub-system - if error is returns, ACPI is not
1226356Smrj * used.
1236356Smrj */
1246356Smrj if (acpica_init() != AE_OK)
1256356Smrj return (0);
1266356Smrj
1276356Smrj /*
1286356Smrj * uppc implies system is in PIC mode; set edge/level
1296356Smrj * via ELCR based on return value from get_sci; this
1306356Smrj * will default to level/low if no override present,
1316356Smrj * as recommended by Intel ACPI CA team.
1326356Smrj */
1336356Smrj if (sci >= 0) {
1346356Smrj ASSERT((sci_flags.intr_el == INTR_EL_LEVEL) ||
1356356Smrj (sci_flags.intr_el == INTR_EL_EDGE));
1366356Smrj
1376356Smrj psm_set_elcr(sci, sci_flags.intr_el == INTR_EL_LEVEL);
1386356Smrj }
1396356Smrj
1406356Smrj /*
1416356Smrj * Remember SCI for later use
1426356Smrj */
1436356Smrj xen_uppc_sci = sci;
1446356Smrj
1456356Smrj if (xen_uppc_verbose & XEN_UPPC_VERBOSE_IRQ_FLAG)
1466356Smrj verboseflags |= PSM_VERBOSE_IRQ_FLAG;
1476356Smrj
1486356Smrj if (xen_uppc_verbose & XEN_UPPC_VERBOSE_POWEROFF_FLAG)
1496356Smrj verboseflags |= PSM_VERBOSE_POWEROFF_FLAG;
1506356Smrj
1516356Smrj if (xen_uppc_verbose & XEN_UPPC_VERBOSE_POWEROFF_PAUSE_FLAG)
1526356Smrj verboseflags |= PSM_VERBOSE_POWEROFF_PAUSE_FLAG;
1536356Smrj
1546356Smrj if (acpi_psm_init(xen_uppc_info.p_mach_idstring, verboseflags) ==
1556356Smrj ACPI_PSM_FAILURE) {
1566356Smrj return (0);
1576356Smrj }
1586356Smrj
1596356Smrj return (1);
1606356Smrj }
1616356Smrj
1626356Smrj /*
1636356Smrj * Autoconfiguration Routines
1646356Smrj */
1656356Smrj
1666356Smrj static int
xen_uppc_probe(void)1676356Smrj xen_uppc_probe(void)
1686356Smrj {
1696356Smrj
1706356Smrj return (PSM_SUCCESS);
1716356Smrj }
1726356Smrj
1736356Smrj static void
xen_uppc_softinit(void)1746356Smrj xen_uppc_softinit(void)
1756356Smrj {
1766356Smrj int i;
1776356Smrj
1786356Smrj /* LINTED logical expression always true: op "||" */
1796356Smrj ASSERT((1 << EVTCHN_SHIFT) == NBBY * sizeof (ulong_t));
1806356Smrj if (DOMAIN_IS_INITDOMAIN(xen_info)) {
1816356Smrj if (xen_uppc_use_acpi && xen_uppc_init_acpi()) {
1826356Smrj build_reserved_irqlist((uchar_t *)
1836356Smrj xen_uppc_reserved_irqlist);
1846356Smrj for (i = 0; i <= MAX_ISA_IRQ; i++)
1856356Smrj xen_uppc_irq_shared_table[i] = 0;
1866356Smrj xen_uppc_enable_acpi = 1;
1876356Smrj }
1886356Smrj }
1896356Smrj }
1906356Smrj
1916356Smrj
1926356Smrj #define XEN_NSEC_PER_TICK 10 /* XXX - assume we have a 100 Mhz clock */
1936356Smrj
1946356Smrj /*ARGSUSED*/
1956356Smrj static int
xen_uppc_clkinit(int hertz)1966356Smrj xen_uppc_clkinit(int hertz)
1976356Smrj {
1986356Smrj extern enum tod_fault_type tod_fault(enum tod_fault_type, int);
1996356Smrj extern int dosynctodr;
2006356Smrj
2016356Smrj /*
2026356Smrj * domU cannot set the TOD hardware, fault the TOD clock now to
2036356Smrj * indicate that and turn off attempts to sync TOD hardware
2046356Smrj * with the hires timer.
2056356Smrj */
2066356Smrj if (!DOMAIN_IS_INITDOMAIN(xen_info)) {
2076356Smrj mutex_enter(&tod_lock);
2086356Smrj (void) tod_fault(TOD_RDONLY, 0);
2096356Smrj dosynctodr = 0;
2106356Smrj mutex_exit(&tod_lock);
2116356Smrj }
2126356Smrj /*
2136356Smrj * The hypervisor provides a timer based on the local APIC timer.
2146356Smrj * The interface supports requests of nanosecond resolution.
2156356Smrj * A common frequency of the apic clock is 100 Mhz which
2166356Smrj * gives a resolution of 10 nsec per tick. What we would really like
2176356Smrj * is a way to get the ns per tick value from xen.
2186356Smrj * XXPV - This is an assumption that needs checking and may change
2196356Smrj */
2206356Smrj return (XEN_NSEC_PER_TICK);
2216356Smrj }
2226356Smrj
2236356Smrj static void
xen_uppc_picinit()2246356Smrj xen_uppc_picinit()
2256356Smrj {
2266356Smrj int irqno;
2276356Smrj
2286356Smrj if (DOMAIN_IS_INITDOMAIN(xen_info)) {
2296356Smrj #if 0
2306356Smrj /* hypervisor initializes the 8259, don't mess with it */
2316356Smrj picsetup(); /* initialise the 8259 */
2326356Smrj #endif
2336356Smrj /*
2346356Smrj * We never called xen_uppc_addspl() when the SCI
2356356Smrj * interrupt was added because that happened before the
2366356Smrj * PSM module was loaded. Fix that up here by doing
2376356Smrj * any missed operations (e.g. bind to CPU)
2386356Smrj */
2396356Smrj if ((irqno = xen_uppc_sci) >= 0) {
2406356Smrj ec_enable_irq(irqno);
2416356Smrj }
2426356Smrj }
2436356Smrj }
2446356Smrj
2456356Smrj
2466356Smrj /*ARGSUSED*/
2476356Smrj static int
xen_uppc_addspl(int irqno,int ipl,int min_ipl,int max_ipl)2486356Smrj xen_uppc_addspl(int irqno, int ipl, int min_ipl, int max_ipl)
2496356Smrj {
2506356Smrj int ret = PSM_SUCCESS;
2516356Smrj cpuset_t cpus;
2526356Smrj
2536356Smrj if (irqno >= 0 && irqno <= MAX_ISA_IRQ)
2546356Smrj atomic_add_16(&xen_uppc_irq_shared_table[irqno], 1);
2556356Smrj
2566356Smrj /*
2576356Smrj * We are called at splhi() so we can't call anything that might end
2586356Smrj * up trying to context switch.
2596356Smrj */
2606356Smrj if (irqno >= PIRQ_BASE && irqno < NR_PIRQS &&
2616356Smrj DOMAIN_IS_INITDOMAIN(xen_info)) {
2626356Smrj CPUSET_ZERO(cpus);
2636356Smrj CPUSET_ADD(cpus, 0);
2646356Smrj ec_setup_pirq(irqno, ipl, &cpus);
2656356Smrj } else {
2666356Smrj /*
2676356Smrj * Set priority/affinity/enable for non PIRQs
2686356Smrj */
2696356Smrj ret = ec_set_irq_priority(irqno, ipl);
2706356Smrj ASSERT(ret == 0);
2716356Smrj CPUSET_ZERO(cpus);
2726356Smrj CPUSET_ADD(cpus, 0);
2736356Smrj ec_set_irq_affinity(irqno, cpus);
2746356Smrj ec_enable_irq(irqno);
2756356Smrj }
2766356Smrj
2776356Smrj return (ret);
2786356Smrj }
2796356Smrj
2806356Smrj /*ARGSUSED*/
2816356Smrj static int
xen_uppc_delspl(int irqno,int ipl,int min_ipl,int max_ipl)2826356Smrj xen_uppc_delspl(int irqno, int ipl, int min_ipl, int max_ipl)
2836356Smrj {
2846356Smrj int err = PSM_SUCCESS;
2856356Smrj
2866356Smrj if (irqno >= 0 && irqno <= MAX_ISA_IRQ)
2876356Smrj atomic_add_16(&xen_uppc_irq_shared_table[irqno], -1);
2886356Smrj
2896356Smrj if (irqno >= PIRQ_BASE && irqno < NR_PIRQS &&
2906356Smrj DOMAIN_IS_INITDOMAIN(xen_info)) {
2916356Smrj if (max_ipl == PSM_INVALID_IPL) {
2926356Smrj /*
2936356Smrj * unbind if no more sharers of this irq/evtchn
2946356Smrj */
2956356Smrj (void) ec_block_irq(irqno);
2966356Smrj ec_unbind_irq(irqno);
2976356Smrj } else {
2986356Smrj /*
2996356Smrj * If still in use reset priority
3006356Smrj */
3016356Smrj err = ec_set_irq_priority(irqno, max_ipl);
3026356Smrj }
3036356Smrj } else {
3046356Smrj (void) ec_block_irq(irqno);
3056356Smrj ec_unbind_irq(irqno);
3066356Smrj }
3076356Smrj return (err);
3086356Smrj }
3096356Smrj
3106356Smrj static processorid_t
xen_uppc_get_next_processorid(processorid_t id)3116356Smrj xen_uppc_get_next_processorid(processorid_t id)
3126356Smrj {
3136356Smrj if (id == -1)
3146356Smrj return (0);
3156356Smrj return (-1);
3166356Smrj }
3176356Smrj
3186356Smrj /*ARGSUSED*/
3196356Smrj static int
xen_uppc_get_clockirq(int ipl)3206356Smrj xen_uppc_get_clockirq(int ipl)
3216356Smrj {
3226356Smrj if (xen_clock_irq != -1)
3236356Smrj return (xen_clock_irq);
3246356Smrj
3256356Smrj xen_clock_irq = ec_bind_virq_to_irq(VIRQ_TIMER, 0);
3266356Smrj return (xen_clock_irq);
3276356Smrj }
3286356Smrj
3296356Smrj /*ARGSUSED*/
3306356Smrj static void
xen_uppc_shutdown(int cmd,int fcn)3316356Smrj xen_uppc_shutdown(int cmd, int fcn)
3326356Smrj {
3336356Smrj XEN_UPPC_VERBOSE_POWEROFF(("xen_uppc_shutdown(%d,%d);\n", cmd, fcn));
3346356Smrj
3356356Smrj switch (cmd) {
3366356Smrj case A_SHUTDOWN:
3376356Smrj switch (fcn) {
3386356Smrj case AD_BOOT:
3396356Smrj case AD_IBOOT:
3406356Smrj (void) HYPERVISOR_shutdown(SHUTDOWN_reboot);
3416356Smrj break;
3426356Smrj case AD_POWEROFF:
3436356Smrj /* fall through if domU or if poweroff fails */
3446356Smrj if (DOMAIN_IS_INITDOMAIN(xen_info))
3456356Smrj if (xen_uppc_enable_acpi)
3466356Smrj (void) acpi_poweroff();
3476356Smrj /* FALLTHRU */
3486356Smrj case AD_HALT:
3496356Smrj default:
3506356Smrj (void) HYPERVISOR_shutdown(SHUTDOWN_poweroff);
3516356Smrj break;
3526356Smrj }
3536356Smrj break;
3546356Smrj case A_REBOOT:
3556356Smrj (void) HYPERVISOR_shutdown(SHUTDOWN_reboot);
3566356Smrj break;
3576356Smrj default:
3586356Smrj return;
3596356Smrj }
3606356Smrj }
3616356Smrj
3626356Smrj
3636356Smrj /*
3646356Smrj * This function will reprogram the timer.
3656356Smrj *
3666356Smrj * When in oneshot mode the argument is the absolute time in future at which to
3676356Smrj * generate the interrupt.
3686356Smrj *
3696356Smrj * When in periodic mode, the argument is the interval at which the
3706356Smrj * interrupts should be generated. There is no need to support the periodic
3716356Smrj * mode timer change at this time.
3726356Smrj *
3736356Smrj * Note that we must be careful to convert from hrtime to Xen system time (see
3746356Smrj * xpv_timestamp.c).
3756356Smrj */
3766356Smrj static void
xen_uppc_timer_reprogram(hrtime_t timer_req)3776356Smrj xen_uppc_timer_reprogram(hrtime_t timer_req)
3786356Smrj {
3796356Smrj hrtime_t now, timer_new, time_delta, xen_time;
3806356Smrj ulong_t flags;
3816356Smrj
3826356Smrj flags = intr_clear();
3836356Smrj /*
3846356Smrj * We should be called from high PIL context (CBE_HIGH_PIL),
3856356Smrj * so kpreempt is disabled.
3866356Smrj */
3876356Smrj
3886356Smrj now = xpv_gethrtime();
3896356Smrj xen_time = xpv_getsystime();
3906356Smrj if (timer_req <= now) {
3916356Smrj /*
3926356Smrj * requested to generate an interrupt in the past
3936356Smrj * generate an interrupt as soon as possible
3946356Smrj */
3956356Smrj time_delta = XEN_NSEC_PER_TICK;
3966356Smrj } else
3976356Smrj time_delta = timer_req - now;
3986356Smrj
3996356Smrj timer_new = xen_time + time_delta;
4006356Smrj if (HYPERVISOR_set_timer_op(timer_new) != 0)
4016356Smrj panic("can't set hypervisor timer?");
4026356Smrj intr_restore(flags);
4036356Smrj }
4046356Smrj
4056356Smrj /*
4066356Smrj * This function will enable timer interrupts.
4076356Smrj */
4086356Smrj static void
xen_uppc_timer_enable(void)4096356Smrj xen_uppc_timer_enable(void)
4106356Smrj {
4116356Smrj ec_unmask_irq(xen_clock_irq);
4126356Smrj }
4136356Smrj
4146356Smrj /*
4156356Smrj * This function will disable timer interrupts on the current cpu.
4166356Smrj */
4176356Smrj static void
xen_uppc_timer_disable(void)4186356Smrj xen_uppc_timer_disable(void)
4196356Smrj {
4206356Smrj (void) ec_block_irq(xen_clock_irq);
4216356Smrj /*
4226356Smrj * If the clock irq is pending on this cpu then we need to
4236356Smrj * clear the pending interrupt.
4246356Smrj */
4256356Smrj ec_unpend_irq(xen_clock_irq);
4266356Smrj }
4276356Smrj
4286356Smrj
4296356Smrj /*
4306356Smrj * Configures the irq for the interrupt link device identified by
4316356Smrj * acpipsmlnkp.
4326356Smrj *
4336356Smrj * Gets the current and the list of possible irq settings for the
4346356Smrj * device. If xen_uppc_unconditional_srs is not set, and the current
4356356Smrj * resource setting is in the list of possible irq settings,
4366356Smrj * current irq resource setting is passed to the caller.
4376356Smrj *
4386356Smrj * Otherwise, picks an irq number from the list of possible irq
4396356Smrj * settings, and sets the irq of the device to this value.
4406356Smrj * If prefer_crs is set, among a set of irq numbers in the list that have
4416356Smrj * the least number of devices sharing the interrupt, we pick current irq
4426356Smrj * resource setting if it is a member of this set.
4436356Smrj *
4446356Smrj * Passes the irq number in the value pointed to by pci_irqp, and
4456356Smrj * polarity and sensitivity in the structure pointed to by dipintrflagp
4466356Smrj * to the caller.
4476356Smrj *
4486356Smrj * Note that if setting the irq resource failed, but successfuly obtained
4496356Smrj * the current irq resource settings, passes the current irq resources
4506356Smrj * and considers it a success.
4516356Smrj *
4526356Smrj * Returns:
4536356Smrj * ACPI_PSM_SUCCESS on success.
4546356Smrj *
4556356Smrj * ACPI_PSM_FAILURE if an error occured during the configuration or
4566356Smrj * if a suitable irq was not found for this device, or if setting the
4576356Smrj * irq resource and obtaining the current resource fails.
4586356Smrj *
4596356Smrj */
4606356Smrj static int
xen_uppc_acpi_irq_configure(acpi_psm_lnk_t * acpipsmlnkp,dev_info_t * dip,int * pci_irqp,iflag_t * dipintr_flagp)4616356Smrj xen_uppc_acpi_irq_configure(acpi_psm_lnk_t *acpipsmlnkp, dev_info_t *dip,
4626356Smrj int *pci_irqp, iflag_t *dipintr_flagp)
4636356Smrj {
4646356Smrj int i, min_share, foundnow, done = 0;
4656356Smrj int32_t irq;
4666356Smrj int32_t share_irq = -1;
4676356Smrj int32_t chosen_irq = -1;
4686356Smrj int cur_irq = -1;
4696356Smrj acpi_irqlist_t *irqlistp;
4706356Smrj acpi_irqlist_t *irqlistent;
4716356Smrj
4726356Smrj if ((acpi_get_possible_irq_resources(acpipsmlnkp, &irqlistp))
4736356Smrj == ACPI_PSM_FAILURE) {
4746356Smrj XEN_UPPC_VERBOSE_IRQ((CE_WARN, "!xVM_uppc: Unable to determine "
4756356Smrj "or assign IRQ for device %s, instance #%d: The system was "
4766356Smrj "unable to get the list of potential IRQs from ACPI.",
4776356Smrj ddi_get_name(dip), ddi_get_instance(dip)));
4786356Smrj
4796356Smrj return (ACPI_PSM_FAILURE);
4806356Smrj }
4816356Smrj
4826356Smrj if ((acpi_get_current_irq_resource(acpipsmlnkp, &cur_irq,
4836356Smrj dipintr_flagp) == ACPI_PSM_SUCCESS) &&
4846356Smrj (!xen_uppc_unconditional_srs) &&
4856356Smrj (cur_irq > 0)) {
4866356Smrj
4876356Smrj if (acpi_irqlist_find_irq(irqlistp, cur_irq, NULL)
4886356Smrj == ACPI_PSM_SUCCESS) {
4896356Smrj
4906356Smrj acpi_free_irqlist(irqlistp);
4916356Smrj ASSERT(pci_irqp != NULL);
4926356Smrj *pci_irqp = cur_irq;
4936356Smrj return (ACPI_PSM_SUCCESS);
4946356Smrj }
4956356Smrj XEN_UPPC_VERBOSE_IRQ((CE_WARN, "!xVM_uppc: Could not find the "
4966356Smrj "current irq %d for device %s, instance #%d in ACPI's "
4976356Smrj "list of possible irqs for this device. Picking one from "
4986356Smrj " the latter list.", cur_irq, ddi_get_name(dip),
4996356Smrj ddi_get_instance(dip)));
5006356Smrj
5016356Smrj }
5026356Smrj
5036356Smrj irqlistent = irqlistp;
5046356Smrj min_share = 255;
5056356Smrj
5066356Smrj while (irqlistent != NULL) {
5076356Smrj
5086356Smrj for (foundnow = 0, i = 0; i < irqlistent->num_irqs; i++) {
5096356Smrj
5106356Smrj irq = irqlistp->irqs[i];
5116356Smrj
5126356Smrj if ((irq > MAX_ISA_IRQ) ||
5136356Smrj (irqlistent->intr_flags.intr_el == INTR_EL_EDGE) ||
5146356Smrj (irq == 0))
5156356Smrj continue;
5166356Smrj
5176356Smrj if (xen_uppc_reserved_irqlist[irq])
5186356Smrj continue;
5196356Smrj
5206356Smrj if (xen_uppc_irq_shared_table[irq] == 0) {
5216356Smrj chosen_irq = irq;
5226356Smrj foundnow = 1;
5236356Smrj if (!(xen_uppc_prefer_crs) ||
5246356Smrj (irq == cur_irq)) {
5256356Smrj done = 1;
5266356Smrj break;
5276356Smrj }
5286356Smrj }
5296356Smrj
5306356Smrj if ((xen_uppc_irq_shared_table[irq] < min_share) ||
5316356Smrj ((xen_uppc_irq_shared_table[irq] == min_share) &&
5326356Smrj (cur_irq == irq) && (xen_uppc_prefer_crs))) {
5336356Smrj min_share = xen_uppc_irq_shared_table[irq];
5346356Smrj share_irq = irq;
5356356Smrj foundnow = 1;
5366356Smrj }
5376356Smrj }
5386356Smrj
5396356Smrj /* If we found an IRQ in the inner loop, save the details */
5406356Smrj if (foundnow && ((chosen_irq != -1) || (share_irq != -1))) {
5416356Smrj /*
5426356Smrj * Copy the acpi_prs_private_t and flags from this
5436356Smrj * irq list entry, since we found an irq from this
5446356Smrj * entry.
5456356Smrj */
5466356Smrj acpipsmlnkp->acpi_prs_prv = irqlistent->acpi_prs_prv;
5476356Smrj *dipintr_flagp = irqlistent->intr_flags;
5486356Smrj }
5496356Smrj
5506356Smrj if (done)
5516356Smrj break;
5526356Smrj
5536356Smrj /* Load the next entry in the irqlist */
5546356Smrj irqlistent = irqlistent->next;
5556356Smrj }
5566356Smrj
5576356Smrj acpi_free_irqlist(irqlistp);
5586356Smrj
5596356Smrj if (chosen_irq != -1)
5606356Smrj irq = chosen_irq;
5616356Smrj else if (share_irq != -1)
5626356Smrj irq = share_irq;
5636356Smrj else {
5646356Smrj XEN_UPPC_VERBOSE_IRQ((CE_CONT, "!xVM_uppc: Could not find a "
5656356Smrj "suitable irq from the list of possible irqs for device "
5666356Smrj "%s, instance #%d in ACPI's list of possible\n",
5676356Smrj ddi_get_name(dip), ddi_get_instance(dip)));
5686356Smrj
5696356Smrj return (ACPI_PSM_FAILURE);
5706356Smrj }
5716356Smrj
5726356Smrj
5736356Smrj XEN_UPPC_VERBOSE_IRQ((CE_CONT, "!xVM_uppc: Setting irq %d "
5746356Smrj "for device %s instance #%d\n", irq, ddi_get_name(dip),
5756356Smrj ddi_get_instance(dip)));
5766356Smrj
5776356Smrj if ((acpi_set_irq_resource(acpipsmlnkp, irq)) == ACPI_PSM_SUCCESS) {
5786356Smrj /*
5796356Smrj * setting irq was successful, check to make sure CRS
5806356Smrj * reflects that. If CRS does not agree with what we
5816356Smrj * set, return the irq that was set.
5826356Smrj */
5836356Smrj
5846356Smrj if (acpi_get_current_irq_resource(acpipsmlnkp, &cur_irq,
5856356Smrj dipintr_flagp) == ACPI_PSM_SUCCESS) {
5866356Smrj
5876356Smrj if (cur_irq != irq)
5886356Smrj XEN_UPPC_VERBOSE_IRQ((CE_WARN, "!xVM_uppc: "
5896356Smrj "IRQ resource set (irqno %d) for device %s "
5906356Smrj "instance #%d, differs from current "
5916356Smrj "setting irqno %d",
5926356Smrj irq, ddi_get_name(dip),
5936356Smrj ddi_get_instance(dip), cur_irq));
5946356Smrj }
5956356Smrj /*
5966356Smrj * return the irq that was set, and not what CRS reports,
5976356Smrj * since CRS has been seen to be bogus on some systems
5986356Smrj */
5996356Smrj cur_irq = irq;
6006356Smrj } else {
6016356Smrj XEN_UPPC_VERBOSE_IRQ((CE_WARN, "!xVM_uppc: set resource irq %d "
6026356Smrj "failed for device %s instance #%d",
6036356Smrj irq, ddi_get_name(dip), ddi_get_instance(dip)));
6046356Smrj if (cur_irq == -1)
6056356Smrj return (ACPI_PSM_FAILURE);
6066356Smrj }
6076356Smrj
6086356Smrj ASSERT(pci_irqp != NULL);
6096356Smrj *pci_irqp = cur_irq;
6106356Smrj return (ACPI_PSM_SUCCESS);
6116356Smrj }
6126356Smrj
6136356Smrj
6146356Smrj static int
xen_uppc_acpi_translate_pci_irq(dev_info_t * dip,int busid,int devid,int ipin,int * pci_irqp,iflag_t * intr_flagp)6156356Smrj xen_uppc_acpi_translate_pci_irq(dev_info_t *dip, int busid, int devid,
6166356Smrj int ipin, int *pci_irqp, iflag_t *intr_flagp)
6176356Smrj {
6186356Smrj int status;
6196356Smrj acpi_psm_lnk_t acpipsmlnk;
6206356Smrj
6216356Smrj if ((status = acpi_get_irq_cache_ent(busid, devid, ipin, pci_irqp,
6226356Smrj intr_flagp)) == ACPI_PSM_SUCCESS) {
6236356Smrj XEN_UPPC_VERBOSE_IRQ((CE_CONT, "!xVM_uppc: Found irqno %d "
6246356Smrj "from cache for device %s, instance #%d\n", *pci_irqp,
6256356Smrj ddi_get_name(dip), ddi_get_instance(dip)));
6266356Smrj return (status);
6276356Smrj }
6286356Smrj
6296356Smrj bzero(&acpipsmlnk, sizeof (acpi_psm_lnk_t));
6306356Smrj
6316356Smrj if ((status = acpi_translate_pci_irq(dip, ipin, pci_irqp,
6326356Smrj intr_flagp, &acpipsmlnk)) == ACPI_PSM_FAILURE) {
6336356Smrj XEN_UPPC_VERBOSE_IRQ((CE_CONT, "!xVM_uppc: "
6346356Smrj " acpi_translate_pci_irq failed for device %s, instance"
6356356Smrj " #%d\n", ddi_get_name(dip), ddi_get_instance(dip)));
6366356Smrj
6376356Smrj return (status);
6386356Smrj }
6396356Smrj
6406356Smrj if (status == ACPI_PSM_PARTIAL && acpipsmlnk.lnkobj != NULL) {
6416356Smrj status = xen_uppc_acpi_irq_configure(&acpipsmlnk, dip, pci_irqp,
6426356Smrj intr_flagp);
6436356Smrj if (status != ACPI_PSM_SUCCESS) {
6446356Smrj status = acpi_get_current_irq_resource(&acpipsmlnk,
6456356Smrj pci_irqp, intr_flagp);
6466356Smrj }
6476356Smrj }
6486356Smrj
6496356Smrj if (status == ACPI_PSM_SUCCESS) {
6506356Smrj acpi_new_irq_cache_ent(busid, devid, ipin, *pci_irqp,
6516356Smrj intr_flagp, &acpipsmlnk);
6526356Smrj psm_set_elcr(*pci_irqp, 1); /* set IRQ to PCI mode */
6536356Smrj
6546356Smrj XEN_UPPC_VERBOSE_IRQ((CE_CONT, "!xVM_uppc: [ACPI] "
6556356Smrj "new irq %d for device %s, instance #%d\n",
6566356Smrj *pci_irqp, ddi_get_name(dip), ddi_get_instance(dip)));
6576356Smrj }
6586356Smrj
6596356Smrj return (status);
6606356Smrj }
6616356Smrj
6626356Smrj
6636356Smrj /*ARGSUSED*/
6646356Smrj static int
xen_uppc_translate_irq(dev_info_t * dip,int irqno)6656356Smrj xen_uppc_translate_irq(dev_info_t *dip, int irqno)
6666356Smrj {
6676356Smrj char dev_type[16];
6686356Smrj int dev_len, pci_irq, devid, busid;
6696356Smrj ddi_acc_handle_t cfg_handle;
6706356Smrj uchar_t ipin, iline;
6716356Smrj iflag_t intr_flag;
6726356Smrj
6736356Smrj if (dip == NULL) {
6746356Smrj XEN_UPPC_VERBOSE_IRQ((CE_CONT, "!xVM_uppc: irqno = %d"
6756356Smrj " dip = NULL\n", irqno));
6766356Smrj return (irqno);
6776356Smrj }
6786356Smrj
6796356Smrj if (!xen_uppc_enable_acpi) {
6806356Smrj return (irqno);
6816356Smrj }
6826356Smrj
6836356Smrj dev_len = sizeof (dev_type);
6846356Smrj if (ddi_getlongprop_buf(DDI_DEV_T_ANY, ddi_get_parent(dip),
6856356Smrj DDI_PROP_DONTPASS, "device_type", (caddr_t)dev_type,
6866356Smrj &dev_len) != DDI_PROP_SUCCESS) {
6876356Smrj XEN_UPPC_VERBOSE_IRQ((CE_CONT, "!xVM_uppc: irqno %d"
6886356Smrj " device %s instance %d no device_type\n", irqno,
6896356Smrj ddi_get_name(dip), ddi_get_instance(dip)));
6906356Smrj return (irqno);
6916356Smrj }
6926356Smrj
6936356Smrj if ((strcmp(dev_type, "pci") == 0) ||
6946356Smrj (strcmp(dev_type, "pciex") == 0)) {
6956356Smrj
6966356Smrj /* pci device */
6976356Smrj if (acpica_get_bdf(dip, &busid, &devid, NULL) != 0)
6986356Smrj return (irqno);
6996356Smrj
7006356Smrj if (pci_config_setup(dip, &cfg_handle) != DDI_SUCCESS)
7016356Smrj return (irqno);
7026356Smrj
7036356Smrj ipin = pci_config_get8(cfg_handle, PCI_CONF_IPIN) - PCI_INTA;
7046356Smrj iline = pci_config_get8(cfg_handle, PCI_CONF_ILINE);
7056356Smrj if (xen_uppc_acpi_translate_pci_irq(dip, busid, devid,
7066356Smrj ipin, &pci_irq, &intr_flag) == ACPI_PSM_SUCCESS) {
7076356Smrj
7086356Smrj XEN_UPPC_VERBOSE_IRQ((CE_CONT, "!xVM_uppc: [ACPI] "
7096356Smrj "new irq %d old irq %d device %s, instance %d\n",
7106356Smrj pci_irq, irqno, ddi_get_name(dip),
7116356Smrj ddi_get_instance(dip)));
7126356Smrj
7136356Smrj /*
7146356Smrj * Make sure pci_irq is within range.
7156356Smrj * Otherwise, fall through and return irqno.
7166356Smrj */
7176356Smrj if (pci_irq <= MAX_ISA_IRQ) {
7186356Smrj if (iline != pci_irq) {
7196356Smrj /*
7206356Smrj * Update the device's ILINE byte,
7216356Smrj * in case uppc_acpi_translate_pci_irq
7226356Smrj * has choosen a different pci_irq
7236356Smrj * than the BIOS has configured.
7246356Smrj * Some chipsets use the value in
7256356Smrj * ILINE to control interrupt routing,
7266356Smrj * in conflict with the PCI spec.
7276356Smrj */
7286356Smrj pci_config_put8(cfg_handle,
7296356Smrj PCI_CONF_ILINE, pci_irq);
7306356Smrj }
7316356Smrj pci_config_teardown(&cfg_handle);
7326356Smrj return (pci_irq);
7336356Smrj }
7346356Smrj }
7356356Smrj pci_config_teardown(&cfg_handle);
7366356Smrj
7376356Smrj /* FALLTHRU to common case - returning irqno */
7386356Smrj } else {
7396356Smrj /* non-PCI; assumes ISA-style edge-triggered */
7406356Smrj psm_set_elcr(irqno, 0); /* set IRQ to ISA mode */
7416356Smrj
7426356Smrj XEN_UPPC_VERBOSE_IRQ((CE_CONT, "!xVM_uppc: non-pci,"
7436356Smrj "irqno %d device %s instance %d\n", irqno,
7446356Smrj ddi_get_name(dip), ddi_get_instance(dip)));
7456356Smrj }
7466356Smrj
7476356Smrj return (irqno);
7486356Smrj }
7496356Smrj
7506356Smrj /*
7516356Smrj * xen_uppc_intr_enter() acks the event that triggered the interrupt and
7526356Smrj * returns the new priority level,
7536356Smrj */
7546356Smrj /*ARGSUSED*/
7556356Smrj static int
xen_uppc_intr_enter(int ipl,int * vector)7566356Smrj xen_uppc_intr_enter(int ipl, int *vector)
7576356Smrj {
7586356Smrj int newipl;
7596356Smrj uint_t intno;
7606356Smrj cpu_t *cpu = CPU;
7616356Smrj
7626356Smrj intno = (*vector);
7636356Smrj
7646356Smrj ASSERT(intno < NR_IRQS);
7656356Smrj ASSERT(cpu->cpu_m.mcpu_vcpu_info->evtchn_upcall_mask != 0);
7666356Smrj
7676356Smrj ec_clear_irq(intno);
7686356Smrj
7696356Smrj newipl = autovect[intno].avh_hi_pri;
7706356Smrj if (newipl == 0) {
7716356Smrj /*
7726356Smrj * (newipl == 0) means we have no service routines for this
7736356Smrj * vector. We will treat this as a spurious interrupt.
7746356Smrj * We have cleared the pending bit already, clear the event
7756356Smrj * mask and return a spurious interrupt. This case can happen
7766356Smrj * when an interrupt delivery is racing with the removal of
7776356Smrj * of the service routine for that interrupt.
7786356Smrj */
7796356Smrj ec_unmask_irq(intno);
7806356Smrj newipl = -1; /* flag spurious interrupt */
7816356Smrj } else if (newipl <= cpu->cpu_pri) {
7826356Smrj /*
7836356Smrj * (newipl <= cpu->cpu_pri) means that we must be trying to
7846356Smrj * service a vector that was shared with a higher priority
7856356Smrj * isr. The higher priority handler has been removed and
7866356Smrj * we need to service this int. We can't return a lower
7876356Smrj * priority than current cpu priority. Just synthesize a
7886356Smrj * priority to return that should be acceptable.
7896356Smrj */
7906356Smrj newipl = cpu->cpu_pri + 1; /* synthetic priority */
7916356Smrj }
7926356Smrj return (newipl);
7936356Smrj }
7946356Smrj
7956356Smrj
7966356Smrj static void xen_uppc_setspl(int);
7976356Smrj
7986356Smrj /*
7996356Smrj * xen_uppc_intr_exit() restores the old interrupt
8006356Smrj * priority level after processing an interrupt.
8016356Smrj * It is called with interrupts disabled, and does not enable interrupts.
8026356Smrj */
8036356Smrj /* ARGSUSED */
8046356Smrj static void
xen_uppc_intr_exit(int ipl,int vector)8056356Smrj xen_uppc_intr_exit(int ipl, int vector)
8066356Smrj {
8076356Smrj ec_try_unmask_irq(vector);
8086356Smrj xen_uppc_setspl(ipl);
8096356Smrj }
8106356Smrj
8116356Smrj intr_exit_fn_t
psm_intr_exit_fn(void)8126356Smrj psm_intr_exit_fn(void)
8136356Smrj {
8146356Smrj return (xen_uppc_intr_exit);
8156356Smrj }
8166356Smrj
8176356Smrj /*
8186356Smrj * Check if new ipl level allows delivery of previously unserviced events
8196356Smrj */
8206356Smrj static void
xen_uppc_setspl(int ipl)8216356Smrj xen_uppc_setspl(int ipl)
8226356Smrj {
8236356Smrj struct cpu *cpu = CPU;
8246356Smrj volatile vcpu_info_t *vci = cpu->cpu_m.mcpu_vcpu_info;
8256356Smrj uint16_t pending;
8266356Smrj
8276356Smrj ASSERT(vci->evtchn_upcall_mask != 0);
8286356Smrj
8296356Smrj /*
8306356Smrj * If new ipl level will enable any pending interrupts, setup so the
8316356Smrj * upcoming sti will cause us to get an upcall.
8326356Smrj */
8336356Smrj pending = cpu->cpu_m.mcpu_intr_pending & ~((1 << (ipl + 1)) - 1);
8346356Smrj if (pending) {
8356356Smrj int i;
8366356Smrj ulong_t pending_sels = 0;
8376356Smrj volatile ulong_t *selp;
8386356Smrj struct xen_evt_data *cpe = cpu->cpu_m.mcpu_evt_pend;
8396356Smrj
8406356Smrj for (i = bsrw_insn(pending); i > ipl; i--)
8416356Smrj pending_sels |= cpe->pending_sel[i];
8426356Smrj ASSERT(pending_sels);
8436356Smrj selp = (volatile ulong_t *)&vci->evtchn_pending_sel;
8446356Smrj atomic_or_ulong(selp, pending_sels);
8456356Smrj vci->evtchn_upcall_pending = 1;
8466356Smrj }
8476356Smrj }
8486356Smrj
8496356Smrj /*
8506356Smrj * The rest of the file is just generic psm module boilerplate
8516356Smrj */
8526356Smrj
8536356Smrj static struct psm_ops xen_uppc_ops = {
8546356Smrj xen_uppc_probe, /* psm_probe */
8556356Smrj
8566356Smrj xen_uppc_softinit, /* psm_init */
8576356Smrj xen_uppc_picinit, /* psm_picinit */
8586356Smrj xen_uppc_intr_enter, /* psm_intr_enter */
8596356Smrj xen_uppc_intr_exit, /* psm_intr_exit */
8606356Smrj xen_uppc_setspl, /* psm_setspl */
8616356Smrj xen_uppc_addspl, /* psm_addspl */
8626356Smrj xen_uppc_delspl, /* psm_delspl */
8636356Smrj (int (*)(processorid_t))NULL, /* psm_disable_intr */
8646356Smrj (void (*)(processorid_t))NULL, /* psm_enable_intr */
8656356Smrj (int (*)(int))NULL, /* psm_softlvl_to_irq */
8666356Smrj (void (*)(int))NULL, /* psm_set_softintr */
8676356Smrj (void (*)(processorid_t))NULL, /* psm_set_idlecpu */
8686356Smrj (void (*)(processorid_t))NULL, /* psm_unset_idlecpu */
8696356Smrj
8706356Smrj xen_uppc_clkinit, /* psm_clkinit */
8716356Smrj xen_uppc_get_clockirq, /* psm_get_clockirq */
8726356Smrj (void (*)(void))NULL, /* psm_hrtimeinit */
8736356Smrj xpv_gethrtime, /* psm_gethrtime */
8746356Smrj
8756356Smrj xen_uppc_get_next_processorid, /* psm_get_next_processorid */
8766356Smrj (int (*)(processorid_t, caddr_t))NULL, /* psm_cpu_start */
8776356Smrj (int (*)(void))NULL, /* psm_post_cpu_start */
8786356Smrj xen_uppc_shutdown, /* psm_shutdown */
8796356Smrj (int (*)(int, int))NULL, /* psm_get_ipivect */
8806356Smrj (void (*)(processorid_t, int))NULL, /* psm_send_ipi */
8816356Smrj
8826356Smrj xen_uppc_translate_irq, /* psm_translate_irq */
8836356Smrj
8846356Smrj (void (*)(int, char *))NULL, /* psm_notify_error */
8856356Smrj (void (*)(int msg))NULL, /* psm_notify_func */
8866356Smrj xen_uppc_timer_reprogram, /* psm_timer_reprogram */
8876356Smrj xen_uppc_timer_enable, /* psm_timer_enable */
8886356Smrj xen_uppc_timer_disable, /* psm_timer_disable */
8896356Smrj (void (*)(void *arg))NULL, /* psm_post_cyclic_setup */
8906356Smrj (void (*)(int, int))NULL, /* psm_preshutdown */
8916356Smrj
8926356Smrj (int (*)(dev_info_t *, ddi_intr_handle_impl_t *,
8936356Smrj psm_intr_op_t, int *))NULL, /* psm_intr_ops */
894*12004Sjiang.liu@intel.com (int (*)(psm_state_request_t *))NULL, /* psm_state */
895*12004Sjiang.liu@intel.com (int (*)(psm_cpu_request_t *))NULL /* psm_cpu_ops */
8966356Smrj };
8976356Smrj
8986356Smrj static struct psm_info xen_uppc_info = {
8996356Smrj PSM_INFO_VER01_5, /* version */
9006356Smrj PSM_OWN_SYS_DEFAULT, /* ownership */
9016356Smrj &xen_uppc_ops, /* operation */
9026356Smrj "xVM_uppc", /* machine name */
9036356Smrj "UniProcessor PC" /* machine descriptions */
9046356Smrj };
9056356Smrj
9066356Smrj static void *xen_uppc_hdlp;
9076356Smrj
9086356Smrj int
_init(void)9096356Smrj _init(void)
9106356Smrj {
9116356Smrj return (psm_mod_init(&xen_uppc_hdlp, &xen_uppc_info));
9126356Smrj }
9136356Smrj
9146356Smrj int
_fini(void)9156356Smrj _fini(void)
9166356Smrj {
9176356Smrj return (psm_mod_fini(&xen_uppc_hdlp, &xen_uppc_info));
9186356Smrj }
9196356Smrj
9206356Smrj int
_info(struct modinfo * modinfop)9216356Smrj _info(struct modinfo *modinfop)
9226356Smrj {
9236356Smrj return (psm_mod_info(&xen_uppc_hdlp, &xen_uppc_info, modinfop));
9246356Smrj }
925