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