1331722Seadler/*
2186767Sdfr * Copyright (c) 2008 Citrix Systems, Inc.
3185387Sdfr * All rights reserved.
4185387Sdfr *
5185387Sdfr * Redistribution and use in source and binary forms, with or without
6185387Sdfr * modification, are permitted provided that the following conditions
7185387Sdfr * are met:
8185387Sdfr * 1. Redistributions of source code must retain the above copyright
9185387Sdfr *    notice, this list of conditions and the following disclaimer.
10185387Sdfr * 2. Redistributions in binary form must reproduce the above copyright
11185387Sdfr *    notice, this list of conditions and the following disclaimer in the
12185387Sdfr *    documentation and/or other materials provided with the distribution.
13185387Sdfr *
14185387Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS AS IS'' AND
15185387Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16185387Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17185387Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18185387Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19185387Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20185387Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21185387Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22185387Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23185387Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24185387Sdfr * SUCH DAMAGE.
25185387Sdfr */
26185387Sdfr
27185387Sdfr#include <sys/cdefs.h>
28185387Sdfr__FBSDID("$FreeBSD$");
29185387Sdfr
30185387Sdfr#include <sys/param.h>
31185387Sdfr#include <sys/bus.h>
32185387Sdfr#include <sys/kernel.h>
33185387Sdfr#include <sys/malloc.h>
34185387Sdfr#include <sys/module.h>
35185387Sdfr
36185387Sdfr#include <machine/bus.h>
37185387Sdfr#include <machine/resource.h>
38185387Sdfr#include <sys/rman.h>
39185387Sdfr
40185387Sdfr#include <machine/stdarg.h>
41255040Sgibbs
42255040Sgibbs#include <xen/xen-os.h>
43185637Sdfr#include <xen/features.h>
44185637Sdfr#include <xen/hypervisor.h>
45255040Sgibbs#include <xen/hvm.h>
46289686Sroyger#include <xen/xen_intr.h>
47185387Sdfr
48185387Sdfr#include <dev/pci/pcireg.h>
49185387Sdfr#include <dev/pci/pcivar.h>
50185387Sdfr
51185387Sdfr#include <dev/xen/xenpci/xenpcivar.h>
52185387Sdfr
53185387Sdfr/*
54186767Sdfr * This is used to find our platform device instance.
55185387Sdfr */
56185387Sdfrstatic devclass_t xenpci_devclass;
57185387Sdfr
58255040Sgibbsstatic int
59255040Sgibbsxenpci_intr_filter(void *trap_frame)
60185387Sdfr{
61255040Sgibbs	xen_intr_handle_upcall(trap_frame);
62255040Sgibbs	return (FILTER_HANDLED);
63185387Sdfr}
64185387Sdfr
65185387Sdfrstatic int
66255040Sgibbsxenpci_irq_init(device_t device, struct xenpci_softc *scp)
67185387Sdfr{
68255040Sgibbs	int error;
69185387Sdfr
70255040Sgibbs	error = BUS_SETUP_INTR(device_get_parent(device), device,
71255040Sgibbs			       scp->res_irq, INTR_MPSAFE|INTR_TYPE_MISC,
72255040Sgibbs			       xenpci_intr_filter, NULL, /*trap_frame*/NULL,
73255040Sgibbs			       &scp->intr_cookie);
74255040Sgibbs	if (error)
75255040Sgibbs		return error;
76185387Sdfr
77255726Sgibbs#ifdef SMP
78185387Sdfr	/*
79255040Sgibbs	 * When using the PCI event delivery callback we cannot assign
80255040Sgibbs	 * events to specific vCPUs, so all events are delivered to vCPU#0 by
81255040Sgibbs	 * Xen. Since the PCI interrupt can fire on any CPU by default, we
82255040Sgibbs	 * need to bind it to vCPU#0 in order to ensure that
83255040Sgibbs	 * xen_intr_handle_upcall always gets called on vCPU#0.
84185387Sdfr	 */
85255040Sgibbs	error = BUS_BIND_INTR(device_get_parent(device), device,
86255040Sgibbs	                      scp->res_irq, 0);
87255040Sgibbs	if (error)
88255040Sgibbs		return error;
89255726Sgibbs#endif
90185387Sdfr
91255040Sgibbs	xen_hvm_set_callback(device);
92185387Sdfr	return (0);
93185387Sdfr}
94185387Sdfr
95186767Sdfr/*
96186767Sdfr * Deallocate anything allocated by xenpci_allocate_resources.
97186767Sdfr */
98185387Sdfrstatic int
99186767Sdfrxenpci_deallocate_resources(device_t dev)
100185387Sdfr{
101186767Sdfr	struct xenpci_softc *scp = device_get_softc(dev);
102185387Sdfr
103186767Sdfr	if (scp->res_irq != 0) {
104186767Sdfr		bus_deactivate_resource(dev, SYS_RES_IRQ,
105186767Sdfr			scp->rid_irq, scp->res_irq);
106186767Sdfr		bus_release_resource(dev, SYS_RES_IRQ,
107186767Sdfr			scp->rid_irq, scp->res_irq);
108186767Sdfr		scp->res_irq = 0;
109185387Sdfr	}
110185387Sdfr
111186767Sdfr	return (0);
112185387Sdfr}
113185387Sdfr
114186767Sdfr/*
115186767Sdfr * Allocate irq and memory resources.
116186767Sdfr */
117185387Sdfrstatic int
118186767Sdfrxenpci_allocate_resources(device_t dev)
119185387Sdfr{
120186767Sdfr	struct xenpci_softc *scp = device_get_softc(dev);
121185387Sdfr
122186767Sdfr	scp->res_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ,
123185387Sdfr			&scp->rid_irq, RF_SHAREABLE|RF_ACTIVE);
124214077Sgibbs	if (scp->res_irq == NULL) {
125214077Sgibbs		printf("xenpci Could not allocate irq.\n");
126185387Sdfr		goto errexit;
127214077Sgibbs	}
128185387Sdfr
129185387Sdfr	return (0);
130185387Sdfr
131185387Sdfrerrexit:
132185387Sdfr	/* Cleanup anything we may have assigned. */
133186767Sdfr	xenpci_deallocate_resources(dev);
134185387Sdfr	return (ENXIO); /* For want of a better idea. */
135185387Sdfr}
136185387Sdfr
137186767Sdfr/*
138186767Sdfr * Probe - just check device ID.
139186767Sdfr */
140186767Sdfrstatic int
141186767Sdfrxenpci_probe(device_t dev)
142186767Sdfr{
143186767Sdfr
144186767Sdfr	if (pci_get_devid(dev) != 0x00015853)
145186767Sdfr		return (ENXIO);
146186767Sdfr
147186767Sdfr	device_set_desc(dev, "Xen Platform Device");
148267528Sroyger	return (BUS_PROBE_DEFAULT);
149186767Sdfr}
150186767Sdfr
151186767Sdfr/*
152186767Sdfr * Attach - find resources and talk to Xen.
153186767Sdfr */
154186767Sdfrstatic int
155186767Sdfrxenpci_attach(device_t dev)
156186767Sdfr{
157186767Sdfr	struct xenpci_softc *scp = device_get_softc(dev);
158255040Sgibbs	int error;
159186767Sdfr
160186767Sdfr	error = xenpci_allocate_resources(dev);
161214077Sgibbs	if (error) {
162214077Sgibbs		device_printf(dev, "xenpci_allocate_resources failed(%d).\n",
163214077Sgibbs		    error);
164186767Sdfr		goto errexit;
165214077Sgibbs	}
166186767Sdfr
167255040Sgibbs	/*
168255040Sgibbs	 * Hook the irq up to evtchn
169255040Sgibbs	 */
170255040Sgibbs	error = xenpci_irq_init(dev, scp);
171214077Sgibbs	if (error) {
172255040Sgibbs		device_printf(dev, "xenpci_irq_init failed(%d).\n",
173255040Sgibbs			error);
174186767Sdfr		goto errexit;
175214077Sgibbs	}
176186767Sdfr
177267528Sroyger	return (0);
178186767Sdfr
179186767Sdfrerrexit:
180186767Sdfr	/*
181186767Sdfr	 * Undo anything we may have done.
182186767Sdfr	 */
183186767Sdfr	xenpci_deallocate_resources(dev);
184214077Sgibbs	return (error);
185186767Sdfr}
186186767Sdfr
187186767Sdfr/*
188186767Sdfr * Detach - reverse anything done by attach.
189186767Sdfr */
190186767Sdfrstatic int
191186767Sdfrxenpci_detach(device_t dev)
192186767Sdfr{
193186767Sdfr	struct xenpci_softc *scp = device_get_softc(dev);
194186767Sdfr	device_t parent = device_get_parent(dev);
195186767Sdfr
196186767Sdfr	/*
197186767Sdfr	 * Take our interrupt handler out of the list of handlers
198186767Sdfr	 * that can handle this irq.
199186767Sdfr	 */
200186767Sdfr	if (scp->intr_cookie != NULL) {
201186767Sdfr		if (BUS_TEARDOWN_INTR(parent, dev,
202214077Sgibbs		    scp->res_irq, scp->intr_cookie) != 0)
203214077Sgibbs			device_printf(dev,
204214077Sgibbs			    "intr teardown failed.. continuing\n");
205186767Sdfr		scp->intr_cookie = NULL;
206186767Sdfr	}
207186767Sdfr
208186767Sdfr	/*
209186767Sdfr	 * Deallocate any system resources we may have
210186767Sdfr	 * allocated on behalf of this driver.
211186767Sdfr	 */
212186767Sdfr	return (xenpci_deallocate_resources(dev));
213186767Sdfr}
214186767Sdfr
215255040Sgibbsstatic int
216255040Sgibbsxenpci_resume(device_t dev)
217255040Sgibbs{
218255040Sgibbs	xen_hvm_set_callback(dev);
219267528Sroyger	return (0);
220255040Sgibbs}
221255040Sgibbs
222186767Sdfrstatic device_method_t xenpci_methods[] = {
223186767Sdfr	/* Device interface */
224186767Sdfr	DEVMETHOD(device_probe,		xenpci_probe),
225186767Sdfr	DEVMETHOD(device_attach,	xenpci_attach),
226186767Sdfr	DEVMETHOD(device_detach,	xenpci_detach),
227255040Sgibbs	DEVMETHOD(device_resume,	xenpci_resume),
228186767Sdfr
229186767Sdfr	{ 0, 0 }
230186767Sdfr};
231186767Sdfr
232186767Sdfrstatic driver_t xenpci_driver = {
233186767Sdfr	"xenpci",
234186767Sdfr	xenpci_methods,
235186767Sdfr	sizeof(struct xenpci_softc),
236186767Sdfr};
237186767Sdfr
238186767SdfrDRIVER_MODULE(xenpci, pci, xenpci_driver, xenpci_devclass, 0, 0);
239