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