xref: /titanic_51/usr/src/uts/i86xpv/io/psm/xpv_uppc.c (revision 1a5e258f5471356ca102c7176637cdce45bac147)
1cc7a88b5Smrj /*
2cc7a88b5Smrj  * CDDL HEADER START
3cc7a88b5Smrj  *
4cc7a88b5Smrj  * The contents of this file are subject to the terms of the
5cc7a88b5Smrj  * Common Development and Distribution License (the "License").
6cc7a88b5Smrj  * You may not use this file except in compliance with the License.
7cc7a88b5Smrj  *
8cc7a88b5Smrj  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9cc7a88b5Smrj  * or http://www.opensolaris.org/os/licensing.
10cc7a88b5Smrj  * See the License for the specific language governing permissions
11cc7a88b5Smrj  * and limitations under the License.
12cc7a88b5Smrj  *
13cc7a88b5Smrj  * When distributing Covered Code, include this CDDL HEADER in each
14cc7a88b5Smrj  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15cc7a88b5Smrj  * If applicable, add the following below this CDDL HEADER, with the
16cc7a88b5Smrj  * fields enclosed by brackets "[]" replaced with your own identifying
17cc7a88b5Smrj  * information: Portions Copyright [yyyy] [name of copyright owner]
18cc7a88b5Smrj  *
19cc7a88b5Smrj  * CDDL HEADER END
20cc7a88b5Smrj  */
21cc7a88b5Smrj 
22cc7a88b5Smrj /*
23cc7a88b5Smrj  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24cc7a88b5Smrj  * Use is subject to license terms.
25cc7a88b5Smrj  */
26cc7a88b5Smrj 
27a3114836SGerry Liu #define	PSMI_1_7
28cc7a88b5Smrj 
29cc7a88b5Smrj #include <sys/mutex.h>
30cc7a88b5Smrj #include <sys/types.h>
31cc7a88b5Smrj #include <sys/time.h>
32cc7a88b5Smrj #include <sys/clock.h>
33cc7a88b5Smrj #include <sys/machlock.h>
34cc7a88b5Smrj #include <sys/smp_impldefs.h>
35cc7a88b5Smrj #include <sys/uadmin.h>
36cc7a88b5Smrj #include <sys/promif.h>
37cc7a88b5Smrj #include <sys/psm.h>
38cc7a88b5Smrj #include <sys/psm_common.h>
39cc7a88b5Smrj #include <sys/atomic.h>
40cc7a88b5Smrj #include <sys/archsystm.h>
41cc7a88b5Smrj #include <sys/mach_intr.h>
42cc7a88b5Smrj #include <sys/hypervisor.h>
43cc7a88b5Smrj #include <sys/evtchn_impl.h>
44cc7a88b5Smrj #include <sys/modctl.h>
45cc7a88b5Smrj #include <sys/trap.h>
46cc7a88b5Smrj #include <sys/panic.h>
47cc7a88b5Smrj 
48cc7a88b5Smrj #include <xen/public/vcpu.h>
49cc7a88b5Smrj #include <xen/public/physdev.h>
50cc7a88b5Smrj 
51cc7a88b5Smrj 
52cc7a88b5Smrj /*
53cc7a88b5Smrj  * Global Data
54cc7a88b5Smrj  */
55cc7a88b5Smrj int xen_uppc_use_acpi = 1;	/* Use ACPI by default */
56cc7a88b5Smrj int xen_uppc_enable_acpi = 0;
57cc7a88b5Smrj 
58cc7a88b5Smrj static int xen_clock_irq = -1;
59cc7a88b5Smrj 
60cc7a88b5Smrj /*
61cc7a88b5Smrj  * For interrupt link devices, if xen_uppc_unconditional_srs is set, an irq
62cc7a88b5Smrj  * resource will be assigned (via _SRS). If it is not set, use the current
63cc7a88b5Smrj  * irq setting (via _CRS), but only if that irq is in the set of possible
64cc7a88b5Smrj  * irqs (returned by _PRS) for the device.
65cc7a88b5Smrj  */
66cc7a88b5Smrj int xen_uppc_unconditional_srs = 1;
67cc7a88b5Smrj 
68cc7a88b5Smrj /*
69cc7a88b5Smrj  * For interrupt link devices, if xen_uppc_prefer_crs is set when we are
70cc7a88b5Smrj  * assigning an IRQ resource to a device, prefer the current IRQ setting
71cc7a88b5Smrj  * over other possible irq settings under same conditions.
72cc7a88b5Smrj  */
73cc7a88b5Smrj int xen_uppc_prefer_crs = 1;
74cc7a88b5Smrj 
75cc7a88b5Smrj int xen_uppc_verbose = 0;
76cc7a88b5Smrj 
77cc7a88b5Smrj /* flag definitions for xen_uppc_verbose */
78cc7a88b5Smrj #define	XEN_UPPC_VERBOSE_IRQ_FLAG		0x00000001
79cc7a88b5Smrj #define	XEN_UPPC_VERBOSE_POWEROFF_FLAG		0x00000002
80cc7a88b5Smrj #define	XEN_UPPC_VERBOSE_POWEROFF_PAUSE_FLAG	0x00000004
81cc7a88b5Smrj 
82cc7a88b5Smrj #define	XEN_UPPC_VERBOSE_IRQ(fmt) \
83cc7a88b5Smrj 	if (xen_uppc_verbose & XEN_UPPC_VERBOSE_IRQ_FLAG) \
84cc7a88b5Smrj 		cmn_err fmt;
85cc7a88b5Smrj 
86cc7a88b5Smrj #define	XEN_UPPC_VERBOSE_POWEROFF(fmt) \
87cc7a88b5Smrj 	if (xen_uppc_verbose & XEN_UPPC_VERBOSE_POWEROFF_FLAG) \
88cc7a88b5Smrj 		prom_printf fmt;
89cc7a88b5Smrj 
90cc7a88b5Smrj uchar_t xen_uppc_reserved_irqlist[MAX_ISA_IRQ + 1];
91cc7a88b5Smrj 
92cc7a88b5Smrj static uint16_t xen_uppc_irq_shared_table[MAX_ISA_IRQ + 1];
93cc7a88b5Smrj 
94cc7a88b5Smrj /*
95cc7a88b5Smrj  * Contains SCI irqno from FADT after initialization
96cc7a88b5Smrj  */
97cc7a88b5Smrj static int xen_uppc_sci = -1;
98cc7a88b5Smrj 
99cc7a88b5Smrj static struct psm_info xen_uppc_info;
100cc7a88b5Smrj 
101cc7a88b5Smrj /*
102cc7a88b5Smrj  * Local support routines
103cc7a88b5Smrj  */
104cc7a88b5Smrj 
105cc7a88b5Smrj static int
106cc7a88b5Smrj xen_uppc_init_acpi(void)
107cc7a88b5Smrj {
108cc7a88b5Smrj 	int verboseflags = 0;
109cc7a88b5Smrj 	int	sci;
110cc7a88b5Smrj 	iflag_t sci_flags;
111cc7a88b5Smrj 
112cc7a88b5Smrj 	/*
113cc7a88b5Smrj 	 * Process SCI configuration here; this may return
114cc7a88b5Smrj 	 * an error if acpi-user-options has specified
115cc7a88b5Smrj 	 * legacy mode (use ACPI without ACPI mode or SCI)
116cc7a88b5Smrj 	 */
117cc7a88b5Smrj 	if (acpica_get_sci(&sci, &sci_flags) != AE_OK)
118cc7a88b5Smrj 		sci = -1;
119cc7a88b5Smrj 
120cc7a88b5Smrj 	/*
121cc7a88b5Smrj 	 * Initialize sub-system - if error is returns, ACPI is not
122cc7a88b5Smrj 	 * used.
123cc7a88b5Smrj 	 */
124cc7a88b5Smrj 	if (acpica_init() != AE_OK)
125cc7a88b5Smrj 		return (0);
126cc7a88b5Smrj 
127cc7a88b5Smrj 	/*
128cc7a88b5Smrj 	 * uppc implies system is in PIC mode; set edge/level
129cc7a88b5Smrj 	 * via ELCR based on return value from get_sci; this
130cc7a88b5Smrj 	 * will default to level/low if no override present,
131cc7a88b5Smrj 	 * as recommended by Intel ACPI CA team.
132cc7a88b5Smrj 	 */
133cc7a88b5Smrj 	if (sci >= 0) {
134cc7a88b5Smrj 		ASSERT((sci_flags.intr_el == INTR_EL_LEVEL) ||
135cc7a88b5Smrj 		    (sci_flags.intr_el == INTR_EL_EDGE));
136cc7a88b5Smrj 
137cc7a88b5Smrj 		psm_set_elcr(sci, sci_flags.intr_el == INTR_EL_LEVEL);
138cc7a88b5Smrj 	}
139cc7a88b5Smrj 
140cc7a88b5Smrj 	/*
141cc7a88b5Smrj 	 * Remember SCI for later use
142cc7a88b5Smrj 	 */
143cc7a88b5Smrj 	xen_uppc_sci = sci;
144cc7a88b5Smrj 
145cc7a88b5Smrj 	if (xen_uppc_verbose & XEN_UPPC_VERBOSE_IRQ_FLAG)
146cc7a88b5Smrj 		verboseflags |= PSM_VERBOSE_IRQ_FLAG;
147cc7a88b5Smrj 
148cc7a88b5Smrj 	if (xen_uppc_verbose & XEN_UPPC_VERBOSE_POWEROFF_FLAG)
149cc7a88b5Smrj 		verboseflags |= PSM_VERBOSE_POWEROFF_FLAG;
150cc7a88b5Smrj 
151cc7a88b5Smrj 	if (xen_uppc_verbose & XEN_UPPC_VERBOSE_POWEROFF_PAUSE_FLAG)
152cc7a88b5Smrj 		verboseflags |= PSM_VERBOSE_POWEROFF_PAUSE_FLAG;
153cc7a88b5Smrj 
154cc7a88b5Smrj 	if (acpi_psm_init(xen_uppc_info.p_mach_idstring, verboseflags) ==
155cc7a88b5Smrj 	    ACPI_PSM_FAILURE) {
156cc7a88b5Smrj 		return (0);
157cc7a88b5Smrj 	}
158cc7a88b5Smrj 
159cc7a88b5Smrj 	return (1);
160cc7a88b5Smrj }
161cc7a88b5Smrj 
162cc7a88b5Smrj /*
163cc7a88b5Smrj  * Autoconfiguration Routines
164cc7a88b5Smrj  */
165cc7a88b5Smrj 
166cc7a88b5Smrj static int
167cc7a88b5Smrj xen_uppc_probe(void)
168cc7a88b5Smrj {
169cc7a88b5Smrj 
170cc7a88b5Smrj 	return (PSM_SUCCESS);
171cc7a88b5Smrj }
172cc7a88b5Smrj 
173cc7a88b5Smrj static void
174cc7a88b5Smrj xen_uppc_softinit(void)
175cc7a88b5Smrj {
176cc7a88b5Smrj 	int i;
177cc7a88b5Smrj 
178cc7a88b5Smrj 	/* LINTED logical expression always true: op "||" */
179cc7a88b5Smrj 	ASSERT((1 << EVTCHN_SHIFT) == NBBY * sizeof (ulong_t));
180cc7a88b5Smrj 	if (DOMAIN_IS_INITDOMAIN(xen_info)) {
181cc7a88b5Smrj 		if (xen_uppc_use_acpi && xen_uppc_init_acpi()) {
182cc7a88b5Smrj 			build_reserved_irqlist((uchar_t *)
183cc7a88b5Smrj 			    xen_uppc_reserved_irqlist);
184cc7a88b5Smrj 			for (i = 0; i <= MAX_ISA_IRQ; i++)
185cc7a88b5Smrj 				xen_uppc_irq_shared_table[i] = 0;
186cc7a88b5Smrj 			xen_uppc_enable_acpi = 1;
187cc7a88b5Smrj 		}
188cc7a88b5Smrj 	}
189cc7a88b5Smrj }
190cc7a88b5Smrj 
191cc7a88b5Smrj 
192cc7a88b5Smrj #define	XEN_NSEC_PER_TICK	10 /* XXX - assume we have a 100 Mhz clock */
193cc7a88b5Smrj 
194cc7a88b5Smrj /*ARGSUSED*/
195cc7a88b5Smrj static int
196cc7a88b5Smrj xen_uppc_clkinit(int hertz)
197cc7a88b5Smrj {
198cc7a88b5Smrj 	extern enum tod_fault_type tod_fault(enum tod_fault_type, int);
199cc7a88b5Smrj 	extern int dosynctodr;
200cc7a88b5Smrj 
201cc7a88b5Smrj 	/*
202cc7a88b5Smrj 	 * domU cannot set the TOD hardware, fault the TOD clock now to
203cc7a88b5Smrj 	 * indicate that and turn off attempts to sync TOD hardware
204cc7a88b5Smrj 	 * with the hires timer.
205cc7a88b5Smrj 	 */
206cc7a88b5Smrj 	if (!DOMAIN_IS_INITDOMAIN(xen_info)) {
207cc7a88b5Smrj 		mutex_enter(&tod_lock);
208cc7a88b5Smrj 		(void) tod_fault(TOD_RDONLY, 0);
209cc7a88b5Smrj 		dosynctodr = 0;
210cc7a88b5Smrj 		mutex_exit(&tod_lock);
211cc7a88b5Smrj 	}
212cc7a88b5Smrj 	/*
213cc7a88b5Smrj 	 * The hypervisor provides a timer based on the local APIC timer.
214cc7a88b5Smrj 	 * The interface supports requests of nanosecond resolution.
215cc7a88b5Smrj 	 * A common frequency of the apic clock is 100 Mhz which
216cc7a88b5Smrj 	 * gives a resolution of 10 nsec per tick.  What we would really like
217cc7a88b5Smrj 	 * is a way to get the ns per tick value from xen.
218cc7a88b5Smrj 	 * XXPV - This is an assumption that needs checking and may change
219cc7a88b5Smrj 	 */
220cc7a88b5Smrj 	return (XEN_NSEC_PER_TICK);
221cc7a88b5Smrj }
222cc7a88b5Smrj 
223cc7a88b5Smrj static void
224cc7a88b5Smrj xen_uppc_picinit()
225cc7a88b5Smrj {
226cc7a88b5Smrj 	int irqno;
227cc7a88b5Smrj 
228cc7a88b5Smrj 	if (DOMAIN_IS_INITDOMAIN(xen_info)) {
229cc7a88b5Smrj #if 0
230cc7a88b5Smrj 		/* hypervisor initializes the 8259, don't mess with it */
231cc7a88b5Smrj 		picsetup();	 /* initialise the 8259 */
232cc7a88b5Smrj #endif
233cc7a88b5Smrj 		/*
234cc7a88b5Smrj 		 * We never called xen_uppc_addspl() when the SCI
235cc7a88b5Smrj 		 * interrupt was added because that happened before the
236cc7a88b5Smrj 		 * PSM module was loaded.  Fix that up here by doing
237cc7a88b5Smrj 		 * any missed operations (e.g. bind to CPU)
238cc7a88b5Smrj 		 */
239cc7a88b5Smrj 		if ((irqno = xen_uppc_sci) >= 0) {
240cc7a88b5Smrj 			ec_enable_irq(irqno);
241cc7a88b5Smrj 		}
242cc7a88b5Smrj 	}
243cc7a88b5Smrj }
244cc7a88b5Smrj 
245cc7a88b5Smrj 
246cc7a88b5Smrj /*ARGSUSED*/
247cc7a88b5Smrj static int
248cc7a88b5Smrj xen_uppc_addspl(int irqno, int ipl, int min_ipl, int max_ipl)
249cc7a88b5Smrj {
250cc7a88b5Smrj 	int ret = PSM_SUCCESS;
251cc7a88b5Smrj 	cpuset_t cpus;
252cc7a88b5Smrj 
253cc7a88b5Smrj 	if (irqno >= 0 && irqno <= MAX_ISA_IRQ)
254*1a5e258fSJosef 'Jeff' Sipek 		atomic_inc_16(&xen_uppc_irq_shared_table[irqno]);
255cc7a88b5Smrj 
256cc7a88b5Smrj 	/*
257cc7a88b5Smrj 	 * We are called at splhi() so we can't call anything that might end
258cc7a88b5Smrj 	 * up trying to context switch.
259cc7a88b5Smrj 	 */
260cc7a88b5Smrj 	if (irqno >= PIRQ_BASE && irqno < NR_PIRQS &&
261cc7a88b5Smrj 	    DOMAIN_IS_INITDOMAIN(xen_info)) {
262cc7a88b5Smrj 		CPUSET_ZERO(cpus);
263cc7a88b5Smrj 		CPUSET_ADD(cpus, 0);
264cc7a88b5Smrj 		ec_setup_pirq(irqno, ipl, &cpus);
265cc7a88b5Smrj 	} else {
266cc7a88b5Smrj 		/*
267cc7a88b5Smrj 		 * Set priority/affinity/enable for non PIRQs
268cc7a88b5Smrj 		 */
269cc7a88b5Smrj 		ret = ec_set_irq_priority(irqno, ipl);
270cc7a88b5Smrj 		ASSERT(ret == 0);
271cc7a88b5Smrj 		CPUSET_ZERO(cpus);
272cc7a88b5Smrj 		CPUSET_ADD(cpus, 0);
273cc7a88b5Smrj 		ec_set_irq_affinity(irqno, cpus);
274cc7a88b5Smrj 		ec_enable_irq(irqno);
275cc7a88b5Smrj 	}
276cc7a88b5Smrj 
277cc7a88b5Smrj 	return (ret);
278cc7a88b5Smrj }
279cc7a88b5Smrj 
280cc7a88b5Smrj /*ARGSUSED*/
281cc7a88b5Smrj static int
282cc7a88b5Smrj xen_uppc_delspl(int irqno, int ipl, int min_ipl, int max_ipl)
283cc7a88b5Smrj {
284cc7a88b5Smrj 	int err = PSM_SUCCESS;
285cc7a88b5Smrj 
286cc7a88b5Smrj 	if (irqno >= 0 && irqno <= MAX_ISA_IRQ)
287*1a5e258fSJosef 'Jeff' Sipek 		atomic_dec_16(&xen_uppc_irq_shared_table[irqno]);
288cc7a88b5Smrj 
289cc7a88b5Smrj 	if (irqno >= PIRQ_BASE && irqno < NR_PIRQS &&
290cc7a88b5Smrj 	    DOMAIN_IS_INITDOMAIN(xen_info)) {
291cc7a88b5Smrj 		if (max_ipl == PSM_INVALID_IPL) {
292cc7a88b5Smrj 			/*
293cc7a88b5Smrj 			 * unbind if no more sharers of this irq/evtchn
294cc7a88b5Smrj 			 */
295cc7a88b5Smrj 			(void) ec_block_irq(irqno);
296cc7a88b5Smrj 			ec_unbind_irq(irqno);
297cc7a88b5Smrj 		} else {
298cc7a88b5Smrj 			/*
299cc7a88b5Smrj 			 * If still in use reset priority
300cc7a88b5Smrj 			 */
301cc7a88b5Smrj 			err = ec_set_irq_priority(irqno, max_ipl);
302cc7a88b5Smrj 		}
303cc7a88b5Smrj 	} else {
304cc7a88b5Smrj 		(void) ec_block_irq(irqno);
305cc7a88b5Smrj 		ec_unbind_irq(irqno);
306cc7a88b5Smrj 	}
307cc7a88b5Smrj 	return (err);
308cc7a88b5Smrj }
309cc7a88b5Smrj 
310cc7a88b5Smrj static processorid_t
311cc7a88b5Smrj xen_uppc_get_next_processorid(processorid_t id)
312cc7a88b5Smrj {
313cc7a88b5Smrj 	if (id == -1)
314cc7a88b5Smrj 		return (0);
315cc7a88b5Smrj 	return (-1);
316cc7a88b5Smrj }
317cc7a88b5Smrj 
318cc7a88b5Smrj /*ARGSUSED*/
319cc7a88b5Smrj static int
320cc7a88b5Smrj xen_uppc_get_clockirq(int ipl)
321cc7a88b5Smrj {
322cc7a88b5Smrj 	if (xen_clock_irq != -1)
323cc7a88b5Smrj 		return (xen_clock_irq);
324cc7a88b5Smrj 
325cc7a88b5Smrj 	xen_clock_irq = ec_bind_virq_to_irq(VIRQ_TIMER, 0);
326cc7a88b5Smrj 	return (xen_clock_irq);
327cc7a88b5Smrj }
328cc7a88b5Smrj 
329cc7a88b5Smrj /*ARGSUSED*/
330cc7a88b5Smrj static void
331cc7a88b5Smrj xen_uppc_shutdown(int cmd, int fcn)
332cc7a88b5Smrj {
333cc7a88b5Smrj 	XEN_UPPC_VERBOSE_POWEROFF(("xen_uppc_shutdown(%d,%d);\n", cmd, fcn));
334cc7a88b5Smrj 
335cc7a88b5Smrj 	switch (cmd) {
336cc7a88b5Smrj 	case A_SHUTDOWN:
337cc7a88b5Smrj 		switch (fcn) {
338cc7a88b5Smrj 		case AD_BOOT:
339cc7a88b5Smrj 		case AD_IBOOT:
340cc7a88b5Smrj 			(void) HYPERVISOR_shutdown(SHUTDOWN_reboot);
341cc7a88b5Smrj 			break;
342cc7a88b5Smrj 		case AD_POWEROFF:
343cc7a88b5Smrj 			/* fall through if domU or if poweroff fails */
344cc7a88b5Smrj 			if (DOMAIN_IS_INITDOMAIN(xen_info))
345cc7a88b5Smrj 				if (xen_uppc_enable_acpi)
346cc7a88b5Smrj 					(void) acpi_poweroff();
347cc7a88b5Smrj 			/* FALLTHRU */
348cc7a88b5Smrj 		case AD_HALT:
349cc7a88b5Smrj 		default:
350cc7a88b5Smrj 			(void) HYPERVISOR_shutdown(SHUTDOWN_poweroff);
351cc7a88b5Smrj 			break;
352cc7a88b5Smrj 		}
353cc7a88b5Smrj 		break;
354cc7a88b5Smrj 	case A_REBOOT:
355cc7a88b5Smrj 		(void) HYPERVISOR_shutdown(SHUTDOWN_reboot);
356cc7a88b5Smrj 		break;
357cc7a88b5Smrj 	default:
358cc7a88b5Smrj 		return;
359cc7a88b5Smrj 	}
360cc7a88b5Smrj }
361cc7a88b5Smrj 
362cc7a88b5Smrj 
363cc7a88b5Smrj /*
364cc7a88b5Smrj  * This function will reprogram the timer.
365cc7a88b5Smrj  *
366cc7a88b5Smrj  * When in oneshot mode the argument is the absolute time in future at which to
367cc7a88b5Smrj  * generate the interrupt.
368cc7a88b5Smrj  *
369cc7a88b5Smrj  * When in periodic mode, the argument is the interval at which the
370cc7a88b5Smrj  * interrupts should be generated. There is no need to support the periodic
371cc7a88b5Smrj  * mode timer change at this time.
372cc7a88b5Smrj  *
373cc7a88b5Smrj  * Note that we must be careful to convert from hrtime to Xen system time (see
374cc7a88b5Smrj  * xpv_timestamp.c).
375cc7a88b5Smrj  */
376cc7a88b5Smrj static void
377cc7a88b5Smrj xen_uppc_timer_reprogram(hrtime_t timer_req)
378cc7a88b5Smrj {
379cc7a88b5Smrj 	hrtime_t now, timer_new, time_delta, xen_time;
380cc7a88b5Smrj 	ulong_t flags;
381cc7a88b5Smrj 
382cc7a88b5Smrj 	flags = intr_clear();
383cc7a88b5Smrj 	/*
384cc7a88b5Smrj 	 * We should be called from high PIL context (CBE_HIGH_PIL),
385cc7a88b5Smrj 	 * so kpreempt is disabled.
386cc7a88b5Smrj 	 */
387cc7a88b5Smrj 
388cc7a88b5Smrj 	now = xpv_gethrtime();
389cc7a88b5Smrj 	xen_time = xpv_getsystime();
390cc7a88b5Smrj 	if (timer_req <= now) {
391cc7a88b5Smrj 		/*
392cc7a88b5Smrj 		 * requested to generate an interrupt in the past
393cc7a88b5Smrj 		 * generate an interrupt as soon as possible
394cc7a88b5Smrj 		 */
395cc7a88b5Smrj 		time_delta = XEN_NSEC_PER_TICK;
396cc7a88b5Smrj 	} else
397cc7a88b5Smrj 		time_delta = timer_req - now;
398cc7a88b5Smrj 
399cc7a88b5Smrj 	timer_new = xen_time + time_delta;
400cc7a88b5Smrj 	if (HYPERVISOR_set_timer_op(timer_new) != 0)
401cc7a88b5Smrj 		panic("can't set hypervisor timer?");
402cc7a88b5Smrj 	intr_restore(flags);
403cc7a88b5Smrj }
404cc7a88b5Smrj 
405cc7a88b5Smrj /*
406cc7a88b5Smrj  * This function will enable timer interrupts.
407cc7a88b5Smrj  */
408cc7a88b5Smrj static void
409cc7a88b5Smrj xen_uppc_timer_enable(void)
410cc7a88b5Smrj {
411cc7a88b5Smrj 	ec_unmask_irq(xen_clock_irq);
412cc7a88b5Smrj }
413cc7a88b5Smrj 
414cc7a88b5Smrj /*
415cc7a88b5Smrj  * This function will disable timer interrupts on the current cpu.
416cc7a88b5Smrj  */
417cc7a88b5Smrj static void
418cc7a88b5Smrj xen_uppc_timer_disable(void)
419cc7a88b5Smrj {
420cc7a88b5Smrj 	(void) ec_block_irq(xen_clock_irq);
421cc7a88b5Smrj 	/*
422cc7a88b5Smrj 	 * If the clock irq is pending on this cpu then we need to
423cc7a88b5Smrj 	 * clear the pending interrupt.
424cc7a88b5Smrj 	 */
425cc7a88b5Smrj 	ec_unpend_irq(xen_clock_irq);
426cc7a88b5Smrj }
427cc7a88b5Smrj 
428cc7a88b5Smrj 
429cc7a88b5Smrj /*
430cc7a88b5Smrj  * Configures the irq for the interrupt link device identified by
431cc7a88b5Smrj  * acpipsmlnkp.
432cc7a88b5Smrj  *
433cc7a88b5Smrj  * Gets the current and the list of possible irq settings for the
434cc7a88b5Smrj  * device. If xen_uppc_unconditional_srs is not set, and the current
435cc7a88b5Smrj  * resource setting is in the list of possible irq settings,
436cc7a88b5Smrj  * current irq resource setting is passed to the caller.
437cc7a88b5Smrj  *
438cc7a88b5Smrj  * Otherwise, picks an irq number from the list of possible irq
439cc7a88b5Smrj  * settings, and sets the irq of the device to this value.
440cc7a88b5Smrj  * If prefer_crs is set, among a set of irq numbers in the list that have
441cc7a88b5Smrj  * the least number of devices sharing the interrupt, we pick current irq
442cc7a88b5Smrj  * resource setting if it is a member of this set.
443cc7a88b5Smrj  *
444cc7a88b5Smrj  * Passes the irq number in the value pointed to by pci_irqp, and
445cc7a88b5Smrj  * polarity and sensitivity in the structure pointed to by dipintrflagp
446cc7a88b5Smrj  * to the caller.
447cc7a88b5Smrj  *
448cc7a88b5Smrj  * Note that if setting the irq resource failed, but successfuly obtained
449cc7a88b5Smrj  * the current irq resource settings, passes the current irq resources
450cc7a88b5Smrj  * and considers it a success.
451cc7a88b5Smrj  *
452cc7a88b5Smrj  * Returns:
453cc7a88b5Smrj  * ACPI_PSM_SUCCESS on success.
454cc7a88b5Smrj  *
455cc7a88b5Smrj  * ACPI_PSM_FAILURE if an error occured during the configuration or
456cc7a88b5Smrj  * if a suitable irq was not found for this device, or if setting the
457cc7a88b5Smrj  * irq resource and obtaining the current resource fails.
458cc7a88b5Smrj  *
459cc7a88b5Smrj  */
460cc7a88b5Smrj static int
461cc7a88b5Smrj xen_uppc_acpi_irq_configure(acpi_psm_lnk_t *acpipsmlnkp, dev_info_t *dip,
462cc7a88b5Smrj     int *pci_irqp, iflag_t *dipintr_flagp)
463cc7a88b5Smrj {
464cc7a88b5Smrj 	int i, min_share, foundnow, done = 0;
465cc7a88b5Smrj 	int32_t irq;
466cc7a88b5Smrj 	int32_t share_irq = -1;
467cc7a88b5Smrj 	int32_t chosen_irq = -1;
468cc7a88b5Smrj 	int cur_irq = -1;
469cc7a88b5Smrj 	acpi_irqlist_t *irqlistp;
470cc7a88b5Smrj 	acpi_irqlist_t *irqlistent;
471cc7a88b5Smrj 
472cc7a88b5Smrj 	if ((acpi_get_possible_irq_resources(acpipsmlnkp, &irqlistp))
473cc7a88b5Smrj 	    == ACPI_PSM_FAILURE) {
474cc7a88b5Smrj 		XEN_UPPC_VERBOSE_IRQ((CE_WARN, "!xVM_uppc: Unable to determine "
475cc7a88b5Smrj 		    "or assign IRQ for device %s, instance #%d: The system was "
476cc7a88b5Smrj 		    "unable to get the list of potential IRQs from ACPI.",
477cc7a88b5Smrj 		    ddi_get_name(dip), ddi_get_instance(dip)));
478cc7a88b5Smrj 
479cc7a88b5Smrj 		return (ACPI_PSM_FAILURE);
480cc7a88b5Smrj 	}
481cc7a88b5Smrj 
482cc7a88b5Smrj 	if ((acpi_get_current_irq_resource(acpipsmlnkp, &cur_irq,
483cc7a88b5Smrj 	    dipintr_flagp) == ACPI_PSM_SUCCESS) &&
484cc7a88b5Smrj 	    (!xen_uppc_unconditional_srs) &&
485cc7a88b5Smrj 	    (cur_irq > 0)) {
486cc7a88b5Smrj 
487cc7a88b5Smrj 		if (acpi_irqlist_find_irq(irqlistp, cur_irq, NULL)
488cc7a88b5Smrj 		    == ACPI_PSM_SUCCESS) {
489cc7a88b5Smrj 
490cc7a88b5Smrj 			acpi_free_irqlist(irqlistp);
491cc7a88b5Smrj 			ASSERT(pci_irqp != NULL);
492cc7a88b5Smrj 			*pci_irqp = cur_irq;
493cc7a88b5Smrj 			return (ACPI_PSM_SUCCESS);
494cc7a88b5Smrj 		}
495cc7a88b5Smrj 		XEN_UPPC_VERBOSE_IRQ((CE_WARN, "!xVM_uppc: Could not find the "
496cc7a88b5Smrj 		    "current irq %d for device %s, instance #%d in ACPI's "
497cc7a88b5Smrj 		    "list of possible irqs for this device. Picking one from "
498cc7a88b5Smrj 		    " the latter list.", cur_irq, ddi_get_name(dip),
499cc7a88b5Smrj 		    ddi_get_instance(dip)));
500cc7a88b5Smrj 
501cc7a88b5Smrj 	}
502cc7a88b5Smrj 
503cc7a88b5Smrj 	irqlistent = irqlistp;
504cc7a88b5Smrj 	min_share = 255;
505cc7a88b5Smrj 
506cc7a88b5Smrj 	while (irqlistent != NULL) {
507cc7a88b5Smrj 
508cc7a88b5Smrj 		for (foundnow = 0, i = 0; i < irqlistent->num_irqs; i++) {
509cc7a88b5Smrj 
510cc7a88b5Smrj 			irq = irqlistp->irqs[i];
511cc7a88b5Smrj 
512cc7a88b5Smrj 			if ((irq > MAX_ISA_IRQ) ||
513cc7a88b5Smrj 			    (irqlistent->intr_flags.intr_el == INTR_EL_EDGE) ||
514cc7a88b5Smrj 			    (irq == 0))
515cc7a88b5Smrj 				continue;
516cc7a88b5Smrj 
517cc7a88b5Smrj 			if (xen_uppc_reserved_irqlist[irq])
518cc7a88b5Smrj 				continue;
519cc7a88b5Smrj 
520cc7a88b5Smrj 			if (xen_uppc_irq_shared_table[irq] == 0) {
521cc7a88b5Smrj 				chosen_irq = irq;
522cc7a88b5Smrj 				foundnow = 1;
523cc7a88b5Smrj 				if (!(xen_uppc_prefer_crs) ||
524cc7a88b5Smrj 				    (irq == cur_irq)) {
525cc7a88b5Smrj 					done = 1;
526cc7a88b5Smrj 					break;
527cc7a88b5Smrj 				}
528cc7a88b5Smrj 			}
529cc7a88b5Smrj 
530cc7a88b5Smrj 			if ((xen_uppc_irq_shared_table[irq] < min_share) ||
531cc7a88b5Smrj 			    ((xen_uppc_irq_shared_table[irq] == min_share) &&
532cc7a88b5Smrj 			    (cur_irq == irq) && (xen_uppc_prefer_crs))) {
533cc7a88b5Smrj 				min_share = xen_uppc_irq_shared_table[irq];
534cc7a88b5Smrj 				share_irq = irq;
535cc7a88b5Smrj 				foundnow = 1;
536cc7a88b5Smrj 			}
537cc7a88b5Smrj 		}
538cc7a88b5Smrj 
539cc7a88b5Smrj 		/* If we found an IRQ in the inner loop, save the details */
540cc7a88b5Smrj 		if (foundnow && ((chosen_irq != -1) || (share_irq != -1))) {
541cc7a88b5Smrj 			/*
542cc7a88b5Smrj 			 * Copy the acpi_prs_private_t and flags from this
543cc7a88b5Smrj 			 * irq list entry, since we found an irq from this
544cc7a88b5Smrj 			 * entry.
545cc7a88b5Smrj 			 */
546cc7a88b5Smrj 			acpipsmlnkp->acpi_prs_prv = irqlistent->acpi_prs_prv;
547cc7a88b5Smrj 			*dipintr_flagp = irqlistent->intr_flags;
548cc7a88b5Smrj 		}
549cc7a88b5Smrj 
550cc7a88b5Smrj 		if (done)
551cc7a88b5Smrj 			break;
552cc7a88b5Smrj 
553cc7a88b5Smrj 		/* Load the next entry in the irqlist */
554cc7a88b5Smrj 		irqlistent = irqlistent->next;
555cc7a88b5Smrj 	}
556cc7a88b5Smrj 
557cc7a88b5Smrj 	acpi_free_irqlist(irqlistp);
558cc7a88b5Smrj 
559cc7a88b5Smrj 	if (chosen_irq != -1)
560cc7a88b5Smrj 		irq = chosen_irq;
561cc7a88b5Smrj 	else if (share_irq != -1)
562cc7a88b5Smrj 		irq = share_irq;
563cc7a88b5Smrj 	else {
564cc7a88b5Smrj 		XEN_UPPC_VERBOSE_IRQ((CE_CONT, "!xVM_uppc: Could not find a "
565cc7a88b5Smrj 		    "suitable irq from the list of possible irqs for device "
566cc7a88b5Smrj 		    "%s, instance #%d in ACPI's list of possible\n",
567cc7a88b5Smrj 		    ddi_get_name(dip), ddi_get_instance(dip)));
568cc7a88b5Smrj 
569cc7a88b5Smrj 		return (ACPI_PSM_FAILURE);
570cc7a88b5Smrj 	}
571cc7a88b5Smrj 
572cc7a88b5Smrj 
573cc7a88b5Smrj 	XEN_UPPC_VERBOSE_IRQ((CE_CONT, "!xVM_uppc: Setting irq %d "
574cc7a88b5Smrj 	    "for device %s instance #%d\n", irq, ddi_get_name(dip),
575cc7a88b5Smrj 	    ddi_get_instance(dip)));
576cc7a88b5Smrj 
577cc7a88b5Smrj 	if ((acpi_set_irq_resource(acpipsmlnkp, irq)) == ACPI_PSM_SUCCESS) {
578cc7a88b5Smrj 		/*
579cc7a88b5Smrj 		 * setting irq was successful, check to make sure CRS
580cc7a88b5Smrj 		 * reflects that. If CRS does not agree with what we
581cc7a88b5Smrj 		 * set, return the irq that was set.
582cc7a88b5Smrj 		 */
583cc7a88b5Smrj 
584cc7a88b5Smrj 		if (acpi_get_current_irq_resource(acpipsmlnkp, &cur_irq,
585cc7a88b5Smrj 		    dipintr_flagp) == ACPI_PSM_SUCCESS) {
586cc7a88b5Smrj 
587cc7a88b5Smrj 			if (cur_irq != irq)
588cc7a88b5Smrj 				XEN_UPPC_VERBOSE_IRQ((CE_WARN, "!xVM_uppc: "
589cc7a88b5Smrj 				    "IRQ resource set (irqno %d) for device %s "
590cc7a88b5Smrj 				    "instance #%d, differs from current "
591cc7a88b5Smrj 				    "setting irqno %d",
592cc7a88b5Smrj 				    irq, ddi_get_name(dip),
593cc7a88b5Smrj 				    ddi_get_instance(dip), cur_irq));
594cc7a88b5Smrj 		}
595cc7a88b5Smrj 		/*
596cc7a88b5Smrj 		 * return the irq that was set, and not what CRS reports,
597cc7a88b5Smrj 		 * since CRS has been seen to be bogus on some systems
598cc7a88b5Smrj 		 */
599cc7a88b5Smrj 		cur_irq = irq;
600cc7a88b5Smrj 	} else {
601cc7a88b5Smrj 		XEN_UPPC_VERBOSE_IRQ((CE_WARN, "!xVM_uppc: set resource irq %d "
602cc7a88b5Smrj 		    "failed for device %s instance #%d",
603cc7a88b5Smrj 		    irq, ddi_get_name(dip), ddi_get_instance(dip)));
604cc7a88b5Smrj 		if (cur_irq == -1)
605cc7a88b5Smrj 			return (ACPI_PSM_FAILURE);
606cc7a88b5Smrj 	}
607cc7a88b5Smrj 
608cc7a88b5Smrj 	ASSERT(pci_irqp != NULL);
609cc7a88b5Smrj 	*pci_irqp = cur_irq;
610cc7a88b5Smrj 	return (ACPI_PSM_SUCCESS);
611cc7a88b5Smrj }
612cc7a88b5Smrj 
613cc7a88b5Smrj 
614cc7a88b5Smrj static int
615cc7a88b5Smrj xen_uppc_acpi_translate_pci_irq(dev_info_t *dip, int busid, int devid,
616cc7a88b5Smrj     int ipin, int *pci_irqp, iflag_t *intr_flagp)
617cc7a88b5Smrj {
618cc7a88b5Smrj 	int status;
619cc7a88b5Smrj 	acpi_psm_lnk_t acpipsmlnk;
620cc7a88b5Smrj 
621cc7a88b5Smrj 	if ((status = acpi_get_irq_cache_ent(busid, devid, ipin, pci_irqp,
622cc7a88b5Smrj 	    intr_flagp)) == ACPI_PSM_SUCCESS) {
623cc7a88b5Smrj 		XEN_UPPC_VERBOSE_IRQ((CE_CONT, "!xVM_uppc: Found irqno %d "
624cc7a88b5Smrj 		    "from cache for device %s, instance #%d\n", *pci_irqp,
625cc7a88b5Smrj 		    ddi_get_name(dip), ddi_get_instance(dip)));
626cc7a88b5Smrj 		return (status);
627cc7a88b5Smrj 	}
628cc7a88b5Smrj 
629cc7a88b5Smrj 	bzero(&acpipsmlnk, sizeof (acpi_psm_lnk_t));
630cc7a88b5Smrj 
631cc7a88b5Smrj 	if ((status = acpi_translate_pci_irq(dip, ipin, pci_irqp,
632cc7a88b5Smrj 	    intr_flagp, &acpipsmlnk)) == ACPI_PSM_FAILURE) {
633cc7a88b5Smrj 		XEN_UPPC_VERBOSE_IRQ((CE_CONT, "!xVM_uppc: "
634cc7a88b5Smrj 		    " acpi_translate_pci_irq failed for device %s, instance"
635cc7a88b5Smrj 		    " #%d\n", ddi_get_name(dip), ddi_get_instance(dip)));
636cc7a88b5Smrj 
637cc7a88b5Smrj 		return (status);
638cc7a88b5Smrj 	}
639cc7a88b5Smrj 
640cc7a88b5Smrj 	if (status == ACPI_PSM_PARTIAL && acpipsmlnk.lnkobj != NULL) {
641cc7a88b5Smrj 		status = xen_uppc_acpi_irq_configure(&acpipsmlnk, dip, pci_irqp,
642cc7a88b5Smrj 		    intr_flagp);
643cc7a88b5Smrj 		if (status != ACPI_PSM_SUCCESS) {
644cc7a88b5Smrj 			status = acpi_get_current_irq_resource(&acpipsmlnk,
645cc7a88b5Smrj 			    pci_irqp, intr_flagp);
646cc7a88b5Smrj 		}
647cc7a88b5Smrj 	}
648cc7a88b5Smrj 
649cc7a88b5Smrj 	if (status == ACPI_PSM_SUCCESS) {
650cc7a88b5Smrj 		acpi_new_irq_cache_ent(busid, devid, ipin, *pci_irqp,
651cc7a88b5Smrj 		    intr_flagp, &acpipsmlnk);
652cc7a88b5Smrj 		psm_set_elcr(*pci_irqp, 1); 	/* set IRQ to PCI mode */
653cc7a88b5Smrj 
654cc7a88b5Smrj 		XEN_UPPC_VERBOSE_IRQ((CE_CONT, "!xVM_uppc: [ACPI] "
655cc7a88b5Smrj 		    "new irq %d for device %s, instance #%d\n",
656cc7a88b5Smrj 		    *pci_irqp, ddi_get_name(dip), ddi_get_instance(dip)));
657cc7a88b5Smrj 	}
658cc7a88b5Smrj 
659cc7a88b5Smrj 	return (status);
660cc7a88b5Smrj }
661cc7a88b5Smrj 
662cc7a88b5Smrj 
663cc7a88b5Smrj /*ARGSUSED*/
664cc7a88b5Smrj static int
665cc7a88b5Smrj xen_uppc_translate_irq(dev_info_t *dip, int irqno)
666cc7a88b5Smrj {
667cc7a88b5Smrj 	char dev_type[16];
668cc7a88b5Smrj 	int dev_len, pci_irq, devid, busid;
669cc7a88b5Smrj 	ddi_acc_handle_t cfg_handle;
670cc7a88b5Smrj 	uchar_t ipin, iline;
671cc7a88b5Smrj 	iflag_t intr_flag;
672cc7a88b5Smrj 
673cc7a88b5Smrj 	if (dip == NULL) {
674cc7a88b5Smrj 		XEN_UPPC_VERBOSE_IRQ((CE_CONT, "!xVM_uppc: irqno = %d"
675cc7a88b5Smrj 		    " dip = NULL\n", irqno));
676cc7a88b5Smrj 		return (irqno);
677cc7a88b5Smrj 	}
678cc7a88b5Smrj 
679cc7a88b5Smrj 	if (!xen_uppc_enable_acpi) {
680cc7a88b5Smrj 		return (irqno);
681cc7a88b5Smrj 	}
682cc7a88b5Smrj 
683cc7a88b5Smrj 	dev_len = sizeof (dev_type);
684cc7a88b5Smrj 	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, ddi_get_parent(dip),
685cc7a88b5Smrj 	    DDI_PROP_DONTPASS, "device_type", (caddr_t)dev_type,
686cc7a88b5Smrj 	    &dev_len) != DDI_PROP_SUCCESS) {
687cc7a88b5Smrj 		XEN_UPPC_VERBOSE_IRQ((CE_CONT, "!xVM_uppc: irqno %d"
688cc7a88b5Smrj 		    " device %s instance %d no device_type\n", irqno,
689cc7a88b5Smrj 		    ddi_get_name(dip), ddi_get_instance(dip)));
690cc7a88b5Smrj 		return (irqno);
691cc7a88b5Smrj 	}
692cc7a88b5Smrj 
693cc7a88b5Smrj 	if ((strcmp(dev_type, "pci") == 0) ||
694cc7a88b5Smrj 	    (strcmp(dev_type, "pciex") == 0)) {
695cc7a88b5Smrj 
696cc7a88b5Smrj 		/* pci device */
697cc7a88b5Smrj 		if (acpica_get_bdf(dip, &busid, &devid, NULL) != 0)
698cc7a88b5Smrj 			return (irqno);
699cc7a88b5Smrj 
700cc7a88b5Smrj 		if (pci_config_setup(dip, &cfg_handle) != DDI_SUCCESS)
701cc7a88b5Smrj 			return (irqno);
702cc7a88b5Smrj 
703cc7a88b5Smrj 		ipin = pci_config_get8(cfg_handle, PCI_CONF_IPIN) - PCI_INTA;
704cc7a88b5Smrj 		iline = pci_config_get8(cfg_handle, PCI_CONF_ILINE);
705cc7a88b5Smrj 		if (xen_uppc_acpi_translate_pci_irq(dip, busid, devid,
706cc7a88b5Smrj 		    ipin, &pci_irq, &intr_flag) == ACPI_PSM_SUCCESS) {
707cc7a88b5Smrj 
708cc7a88b5Smrj 			XEN_UPPC_VERBOSE_IRQ((CE_CONT, "!xVM_uppc: [ACPI] "
709cc7a88b5Smrj 			    "new irq %d old irq %d device %s, instance %d\n",
710cc7a88b5Smrj 			    pci_irq, irqno, ddi_get_name(dip),
711cc7a88b5Smrj 			    ddi_get_instance(dip)));
712cc7a88b5Smrj 
713cc7a88b5Smrj 			/*
714cc7a88b5Smrj 			 * Make sure pci_irq is within range.
715cc7a88b5Smrj 			 * Otherwise, fall through and return irqno.
716cc7a88b5Smrj 			 */
717cc7a88b5Smrj 			if (pci_irq <= MAX_ISA_IRQ) {
718cc7a88b5Smrj 				if (iline != pci_irq) {
719cc7a88b5Smrj 					/*
720cc7a88b5Smrj 					 * Update the device's ILINE byte,
721cc7a88b5Smrj 					 * in case uppc_acpi_translate_pci_irq
722cc7a88b5Smrj 					 * has choosen a different pci_irq
723cc7a88b5Smrj 					 * than the BIOS has configured.
724cc7a88b5Smrj 					 * Some chipsets use the value in
725cc7a88b5Smrj 					 * ILINE to control interrupt routing,
726cc7a88b5Smrj 					 * in conflict with the PCI spec.
727cc7a88b5Smrj 					 */
728cc7a88b5Smrj 					pci_config_put8(cfg_handle,
729cc7a88b5Smrj 					    PCI_CONF_ILINE, pci_irq);
730cc7a88b5Smrj 				}
731cc7a88b5Smrj 				pci_config_teardown(&cfg_handle);
732cc7a88b5Smrj 				return (pci_irq);
733cc7a88b5Smrj 			}
734cc7a88b5Smrj 		}
735cc7a88b5Smrj 		pci_config_teardown(&cfg_handle);
736cc7a88b5Smrj 
737cc7a88b5Smrj 		/* FALLTHRU to common case - returning irqno */
738cc7a88b5Smrj 	} else {
739cc7a88b5Smrj 		/* non-PCI; assumes ISA-style edge-triggered */
740cc7a88b5Smrj 		psm_set_elcr(irqno, 0); 	/* set IRQ to ISA mode */
741cc7a88b5Smrj 
742cc7a88b5Smrj 		XEN_UPPC_VERBOSE_IRQ((CE_CONT, "!xVM_uppc: non-pci,"
743cc7a88b5Smrj 		    "irqno %d device %s instance %d\n", irqno,
744cc7a88b5Smrj 		    ddi_get_name(dip), ddi_get_instance(dip)));
745cc7a88b5Smrj 	}
746cc7a88b5Smrj 
747cc7a88b5Smrj 	return (irqno);
748cc7a88b5Smrj }
749cc7a88b5Smrj 
750cc7a88b5Smrj /*
751cc7a88b5Smrj  * xen_uppc_intr_enter() acks the event that triggered the interrupt and
752cc7a88b5Smrj  * returns the new priority level,
753cc7a88b5Smrj  */
754cc7a88b5Smrj /*ARGSUSED*/
755cc7a88b5Smrj static int
756cc7a88b5Smrj xen_uppc_intr_enter(int ipl, int *vector)
757cc7a88b5Smrj {
758cc7a88b5Smrj 	int newipl;
759cc7a88b5Smrj 	uint_t intno;
760cc7a88b5Smrj 	cpu_t *cpu = CPU;
761cc7a88b5Smrj 
762cc7a88b5Smrj 	intno = (*vector);
763cc7a88b5Smrj 
764cc7a88b5Smrj 	ASSERT(intno < NR_IRQS);
765cc7a88b5Smrj 	ASSERT(cpu->cpu_m.mcpu_vcpu_info->evtchn_upcall_mask != 0);
766cc7a88b5Smrj 
767cc7a88b5Smrj 	ec_clear_irq(intno);
768cc7a88b5Smrj 
769cc7a88b5Smrj 	newipl = autovect[intno].avh_hi_pri;
770cc7a88b5Smrj 	if (newipl == 0) {
771cc7a88b5Smrj 		/*
772cc7a88b5Smrj 		 * (newipl == 0) means we have no service routines for this
773cc7a88b5Smrj 		 * vector.  We will treat this as a spurious interrupt.
774cc7a88b5Smrj 		 * We have cleared the pending bit already, clear the event
775cc7a88b5Smrj 		 * mask and return a spurious interrupt.  This case can happen
776cc7a88b5Smrj 		 * when an interrupt delivery is racing with the removal of
777cc7a88b5Smrj 		 * of the service routine for that interrupt.
778cc7a88b5Smrj 		 */
779cc7a88b5Smrj 		ec_unmask_irq(intno);
780cc7a88b5Smrj 		newipl = -1;	/* flag spurious interrupt */
781cc7a88b5Smrj 	} else if (newipl <= cpu->cpu_pri) {
782cc7a88b5Smrj 		/*
783cc7a88b5Smrj 		 * (newipl <= cpu->cpu_pri) means that we must be trying to
784cc7a88b5Smrj 		 * service a vector that was shared with a higher priority
785cc7a88b5Smrj 		 * isr.  The higher priority handler has been removed and
786cc7a88b5Smrj 		 * we need to service this int.  We can't return a lower
787cc7a88b5Smrj 		 * priority than current cpu priority.  Just synthesize a
788cc7a88b5Smrj 		 * priority to return that should be acceptable.
789cc7a88b5Smrj 		 */
790cc7a88b5Smrj 		newipl = cpu->cpu_pri + 1;	/* synthetic priority */
791cc7a88b5Smrj 	}
792cc7a88b5Smrj 	return (newipl);
793cc7a88b5Smrj }
794cc7a88b5Smrj 
795cc7a88b5Smrj 
796cc7a88b5Smrj static void xen_uppc_setspl(int);
797cc7a88b5Smrj 
798cc7a88b5Smrj /*
799cc7a88b5Smrj  * xen_uppc_intr_exit() restores the old interrupt
800cc7a88b5Smrj  * priority level after processing an interrupt.
801cc7a88b5Smrj  * It is called with interrupts disabled, and does not enable interrupts.
802cc7a88b5Smrj  */
803cc7a88b5Smrj /* ARGSUSED */
804cc7a88b5Smrj static void
805cc7a88b5Smrj xen_uppc_intr_exit(int ipl, int vector)
806cc7a88b5Smrj {
807cc7a88b5Smrj 	ec_try_unmask_irq(vector);
808cc7a88b5Smrj 	xen_uppc_setspl(ipl);
809cc7a88b5Smrj }
810cc7a88b5Smrj 
811cc7a88b5Smrj intr_exit_fn_t
812cc7a88b5Smrj psm_intr_exit_fn(void)
813cc7a88b5Smrj {
814cc7a88b5Smrj 	return (xen_uppc_intr_exit);
815cc7a88b5Smrj }
816cc7a88b5Smrj 
817cc7a88b5Smrj /*
818cc7a88b5Smrj  * Check if new ipl level allows delivery of previously unserviced events
819cc7a88b5Smrj  */
820cc7a88b5Smrj static void
821cc7a88b5Smrj xen_uppc_setspl(int ipl)
822cc7a88b5Smrj {
823cc7a88b5Smrj 	struct cpu *cpu = CPU;
824cc7a88b5Smrj 	volatile vcpu_info_t *vci = cpu->cpu_m.mcpu_vcpu_info;
825cc7a88b5Smrj 	uint16_t pending;
826cc7a88b5Smrj 
827cc7a88b5Smrj 	ASSERT(vci->evtchn_upcall_mask != 0);
828cc7a88b5Smrj 
829cc7a88b5Smrj 	/*
830cc7a88b5Smrj 	 * If new ipl level will enable any pending interrupts, setup so the
831cc7a88b5Smrj 	 * upcoming sti will cause us to get an upcall.
832cc7a88b5Smrj 	 */
833cc7a88b5Smrj 	pending = cpu->cpu_m.mcpu_intr_pending & ~((1 << (ipl + 1)) - 1);
834cc7a88b5Smrj 	if (pending) {
835cc7a88b5Smrj 		int i;
836cc7a88b5Smrj 		ulong_t pending_sels = 0;
837cc7a88b5Smrj 		volatile ulong_t *selp;
838cc7a88b5Smrj 		struct xen_evt_data *cpe = cpu->cpu_m.mcpu_evt_pend;
839cc7a88b5Smrj 
840cc7a88b5Smrj 		for (i = bsrw_insn(pending); i > ipl; i--)
841cc7a88b5Smrj 			pending_sels |= cpe->pending_sel[i];
842cc7a88b5Smrj 		ASSERT(pending_sels);
843cc7a88b5Smrj 		selp = (volatile ulong_t *)&vci->evtchn_pending_sel;
844cc7a88b5Smrj 		atomic_or_ulong(selp, pending_sels);
845cc7a88b5Smrj 		vci->evtchn_upcall_pending = 1;
846cc7a88b5Smrj 	}
847cc7a88b5Smrj }
848cc7a88b5Smrj 
849cc7a88b5Smrj /*
850cc7a88b5Smrj  * The rest of the file is just generic psm module boilerplate
851cc7a88b5Smrj  */
852cc7a88b5Smrj 
853cc7a88b5Smrj static struct psm_ops xen_uppc_ops = {
854cc7a88b5Smrj 	xen_uppc_probe,				/* psm_probe		*/
855cc7a88b5Smrj 
856cc7a88b5Smrj 	xen_uppc_softinit,			/* psm_init		*/
857cc7a88b5Smrj 	xen_uppc_picinit,			/* psm_picinit		*/
858cc7a88b5Smrj 	xen_uppc_intr_enter,			/* psm_intr_enter	*/
859cc7a88b5Smrj 	xen_uppc_intr_exit,			/* psm_intr_exit	*/
860cc7a88b5Smrj 	xen_uppc_setspl,			/* psm_setspl		*/
861cc7a88b5Smrj 	xen_uppc_addspl,			/* psm_addspl		*/
862cc7a88b5Smrj 	xen_uppc_delspl,			/* psm_delspl		*/
863cc7a88b5Smrj 	(int (*)(processorid_t))NULL,		/* psm_disable_intr	*/
864cc7a88b5Smrj 	(void (*)(processorid_t))NULL,		/* psm_enable_intr	*/
865cc7a88b5Smrj 	(int (*)(int))NULL,			/* psm_softlvl_to_irq	*/
866cc7a88b5Smrj 	(void (*)(int))NULL,			/* psm_set_softintr	*/
867cc7a88b5Smrj 	(void (*)(processorid_t))NULL,		/* psm_set_idlecpu	*/
868cc7a88b5Smrj 	(void (*)(processorid_t))NULL,		/* psm_unset_idlecpu	*/
869cc7a88b5Smrj 
870cc7a88b5Smrj 	xen_uppc_clkinit,			/* psm_clkinit		*/
871cc7a88b5Smrj 	xen_uppc_get_clockirq,			/* psm_get_clockirq	*/
872cc7a88b5Smrj 	(void (*)(void))NULL,			/* psm_hrtimeinit	*/
873cc7a88b5Smrj 	xpv_gethrtime,				/* psm_gethrtime	*/
874cc7a88b5Smrj 
875cc7a88b5Smrj 	xen_uppc_get_next_processorid,		/* psm_get_next_processorid */
876cc7a88b5Smrj 	(int (*)(processorid_t, caddr_t))NULL,	/* psm_cpu_start	*/
877cc7a88b5Smrj 	(int (*)(void))NULL,			/* psm_post_cpu_start	*/
878cc7a88b5Smrj 	xen_uppc_shutdown,			/* psm_shutdown		*/
879cc7a88b5Smrj 	(int (*)(int, int))NULL,		/* psm_get_ipivect	*/
880cc7a88b5Smrj 	(void (*)(processorid_t, int))NULL,	/* psm_send_ipi		*/
881cc7a88b5Smrj 
882cc7a88b5Smrj 	xen_uppc_translate_irq,			/* psm_translate_irq	*/
883cc7a88b5Smrj 
884cc7a88b5Smrj 	(void (*)(int, char *))NULL,		/* psm_notify_error	*/
885cc7a88b5Smrj 	(void (*)(int msg))NULL,		/* psm_notify_func	*/
886cc7a88b5Smrj 	xen_uppc_timer_reprogram,		/* psm_timer_reprogram	*/
887cc7a88b5Smrj 	xen_uppc_timer_enable,			/* psm_timer_enable	*/
888cc7a88b5Smrj 	xen_uppc_timer_disable,			/* psm_timer_disable	*/
889cc7a88b5Smrj 	(void (*)(void *arg))NULL,		/* psm_post_cyclic_setup */
890cc7a88b5Smrj 	(void (*)(int, int))NULL,		/* psm_preshutdown	*/
891cc7a88b5Smrj 
892cc7a88b5Smrj 	(int (*)(dev_info_t *, ddi_intr_handle_impl_t *,
893cc7a88b5Smrj 	    psm_intr_op_t, int *))NULL,		/* psm_intr_ops		*/
894a3114836SGerry Liu 	(int (*)(psm_state_request_t *))NULL,	/* psm_state		*/
895a3114836SGerry Liu 	(int (*)(psm_cpu_request_t *))NULL	/* psm_cpu_ops		*/
896cc7a88b5Smrj };
897cc7a88b5Smrj 
898cc7a88b5Smrj static struct psm_info xen_uppc_info = {
899cc7a88b5Smrj 	PSM_INFO_VER01_5,	/* version				*/
900cc7a88b5Smrj 	PSM_OWN_SYS_DEFAULT,	/* ownership				*/
901cc7a88b5Smrj 	&xen_uppc_ops,		/* operation				*/
902cc7a88b5Smrj 	"xVM_uppc",		/* machine name				*/
903cc7a88b5Smrj 	"UniProcessor PC"	/* machine descriptions			*/
904cc7a88b5Smrj };
905cc7a88b5Smrj 
906cc7a88b5Smrj static void *xen_uppc_hdlp;
907cc7a88b5Smrj 
908cc7a88b5Smrj int
909cc7a88b5Smrj _init(void)
910cc7a88b5Smrj {
911cc7a88b5Smrj 	return (psm_mod_init(&xen_uppc_hdlp, &xen_uppc_info));
912cc7a88b5Smrj }
913cc7a88b5Smrj 
914cc7a88b5Smrj int
915cc7a88b5Smrj _fini(void)
916cc7a88b5Smrj {
917cc7a88b5Smrj 	return (psm_mod_fini(&xen_uppc_hdlp, &xen_uppc_info));
918cc7a88b5Smrj }
919cc7a88b5Smrj 
920cc7a88b5Smrj int
921cc7a88b5Smrj _info(struct modinfo *modinfop)
922cc7a88b5Smrj {
923cc7a88b5Smrj 	return (psm_mod_info(&xen_uppc_hdlp, &xen_uppc_info, modinfop));
924cc7a88b5Smrj }
925