2 * Copyright (c) 2008 Citrix Systems, Inc.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
30 #include <sys/param.h>
32 #include <sys/kernel.h>
33 #include <sys/malloc.h>
34 #include <sys/module.h>
36 #include <machine/bus.h>
37 #include <machine/resource.h>
40 #include <machine/stdarg.h>
42 #include <xen/xen-os.h>
43 #include <xen/features.h>
44 #include <xen/hypervisor.h>
47 #include <dev/pci/pcireg.h>
48 #include <dev/pci/pcivar.h>
50 #include <dev/xen/xenpci/xenpcivar.h>
52 extern void xen_intr_handle_upcall(struct trapframe *trap_frame);
54 static device_t nexus;
57 * This is used to find our platform device instance.
59 static devclass_t xenpci_devclass;
62 xenpci_intr_filter(void *trap_frame)
64 xen_intr_handle_upcall(trap_frame);
65 return (FILTER_HANDLED);
69 xenpci_irq_init(device_t device, struct xenpci_softc *scp)
73 error = BUS_SETUP_INTR(device_get_parent(device), device,
74 scp->res_irq, INTR_MPSAFE|INTR_TYPE_MISC,
75 xenpci_intr_filter, NULL, /*trap_frame*/NULL,
82 * When using the PCI event delivery callback we cannot assign
83 * events to specific vCPUs, so all events are delivered to vCPU#0 by
84 * Xen. Since the PCI interrupt can fire on any CPU by default, we
85 * need to bind it to vCPU#0 in order to ensure that
86 * xen_intr_handle_upcall always gets called on vCPU#0.
88 error = BUS_BIND_INTR(device_get_parent(device), device,
94 xen_hvm_set_callback(device);
99 * Deallocate anything allocated by xenpci_allocate_resources.
102 xenpci_deallocate_resources(device_t dev)
104 struct xenpci_softc *scp = device_get_softc(dev);
106 if (scp->res_irq != 0) {
107 bus_deactivate_resource(dev, SYS_RES_IRQ,
108 scp->rid_irq, scp->res_irq);
109 bus_release_resource(dev, SYS_RES_IRQ,
110 scp->rid_irq, scp->res_irq);
113 if (scp->res_memory != 0) {
114 bus_deactivate_resource(dev, SYS_RES_MEMORY,
115 scp->rid_memory, scp->res_memory);
116 bus_release_resource(dev, SYS_RES_MEMORY,
117 scp->rid_memory, scp->res_memory);
125 * Allocate irq and memory resources.
128 xenpci_allocate_resources(device_t dev)
130 struct xenpci_softc *scp = device_get_softc(dev);
132 scp->res_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ,
133 &scp->rid_irq, RF_SHAREABLE|RF_ACTIVE);
134 if (scp->res_irq == NULL) {
135 printf("xenpci Could not allocate irq.\n");
139 scp->rid_memory = PCIR_BAR(1);
140 scp->res_memory = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
141 &scp->rid_memory, RF_ACTIVE);
142 if (scp->res_memory == NULL) {
143 printf("xenpci Could not allocate memory bar.\n");
147 scp->phys_next = rman_get_start(scp->res_memory);
152 /* Cleanup anything we may have assigned. */
153 xenpci_deallocate_resources(dev);
154 return (ENXIO); /* For want of a better idea. */
158 * Allocate a physical address range from our mmio region.
161 xenpci_alloc_space_int(struct xenpci_softc *scp, size_t sz,
165 if (scp->phys_next + sz > rman_get_end(scp->res_memory)) {
169 *pa = scp->phys_next;
170 scp->phys_next += sz;
176 * Allocate a physical address range from our mmio region.
179 xenpci_alloc_space(size_t sz, vm_paddr_t *pa)
181 device_t dev = devclass_get_device(xenpci_devclass, 0);
184 return (xenpci_alloc_space_int(device_get_softc(dev),
191 static struct resource *
192 xenpci_alloc_resource(device_t dev, device_t child, int type, int *rid,
193 u_long start, u_long end, u_long count, u_int flags)
195 return (BUS_ALLOC_RESOURCE(nexus, child, type, rid, start,
201 xenpci_release_resource(device_t dev, device_t child, int type, int rid,
204 return (BUS_RELEASE_RESOURCE(nexus, child, type, rid, r));
208 xenpci_activate_resource(device_t dev, device_t child, int type, int rid,
211 return (BUS_ACTIVATE_RESOURCE(nexus, child, type, rid, r));
215 xenpci_deactivate_resource(device_t dev, device_t child, int type,
216 int rid, struct resource *r)
218 return (BUS_DEACTIVATE_RESOURCE(nexus, child, type, rid, r));
222 * Probe - just check device ID.
225 xenpci_probe(device_t dev)
228 if (pci_get_devid(dev) != 0x00015853)
231 device_set_desc(dev, "Xen Platform Device");
232 return (bus_generic_probe(dev));
236 * Attach - find resources and talk to Xen.
239 xenpci_attach(device_t dev)
241 struct xenpci_softc *scp = device_get_softc(dev);
246 * Find and record nexus0. Since we are not really on the
247 * PCI bus, all resource operations are directed to nexus
248 * instead of through our parent.
250 if ((dc = devclass_find("nexus")) == 0
251 || (nexus = devclass_get_device(dc, 0)) == 0) {
252 device_printf(dev, "unable to find nexus.");
256 error = xenpci_allocate_resources(dev);
258 device_printf(dev, "xenpci_allocate_resources failed(%d).\n",
264 * Hook the irq up to evtchn
266 error = xenpci_irq_init(dev, scp);
268 device_printf(dev, "xenpci_irq_init failed(%d).\n",
273 return (bus_generic_attach(dev));
277 * Undo anything we may have done.
279 xenpci_deallocate_resources(dev);
284 * Detach - reverse anything done by attach.
287 xenpci_detach(device_t dev)
289 struct xenpci_softc *scp = device_get_softc(dev);
290 device_t parent = device_get_parent(dev);
293 * Take our interrupt handler out of the list of handlers
294 * that can handle this irq.
296 if (scp->intr_cookie != NULL) {
297 if (BUS_TEARDOWN_INTR(parent, dev,
298 scp->res_irq, scp->intr_cookie) != 0)
300 "intr teardown failed.. continuing\n");
301 scp->intr_cookie = NULL;
305 * Deallocate any system resources we may have
306 * allocated on behalf of this driver.
308 return (xenpci_deallocate_resources(dev));
312 xenpci_suspend(device_t dev)
314 return (bus_generic_suspend(dev));
318 xenpci_resume(device_t dev)
320 xen_hvm_set_callback(dev);
321 return (bus_generic_resume(dev));
324 static device_method_t xenpci_methods[] = {
325 /* Device interface */
326 DEVMETHOD(device_probe, xenpci_probe),
327 DEVMETHOD(device_attach, xenpci_attach),
328 DEVMETHOD(device_detach, xenpci_detach),
329 DEVMETHOD(device_suspend, xenpci_suspend),
330 DEVMETHOD(device_resume, xenpci_resume),
333 DEVMETHOD(bus_add_child, bus_generic_add_child),
334 DEVMETHOD(bus_alloc_resource, xenpci_alloc_resource),
335 DEVMETHOD(bus_release_resource, xenpci_release_resource),
336 DEVMETHOD(bus_activate_resource, xenpci_activate_resource),
337 DEVMETHOD(bus_deactivate_resource, xenpci_deactivate_resource),
342 static driver_t xenpci_driver = {
345 sizeof(struct xenpci_softc),
348 DRIVER_MODULE(xenpci, pci, xenpci_driver, xenpci_devclass, 0, 0);