112678024SDoug Rabson /* 212678024SDoug Rabson * Copyright (c) 2008 Citrix Systems, Inc. 312678024SDoug Rabson * All rights reserved. 412678024SDoug Rabson * 512678024SDoug Rabson * Redistribution and use in source and binary forms, with or without 612678024SDoug Rabson * modification, are permitted provided that the following conditions 712678024SDoug Rabson * are met: 812678024SDoug Rabson * 1. Redistributions of source code must retain the above copyright 912678024SDoug Rabson * notice, this list of conditions and the following disclaimer. 1012678024SDoug Rabson * 2. Redistributions in binary form must reproduce the above copyright 1112678024SDoug Rabson * notice, this list of conditions and the following disclaimer in the 1212678024SDoug Rabson * documentation and/or other materials provided with the distribution. 1312678024SDoug Rabson * 1412678024SDoug Rabson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS AS IS'' AND 1512678024SDoug Rabson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1612678024SDoug Rabson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1712678024SDoug Rabson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1812678024SDoug Rabson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1912678024SDoug Rabson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2012678024SDoug Rabson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2112678024SDoug Rabson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2212678024SDoug Rabson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2312678024SDoug Rabson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2412678024SDoug Rabson * SUCH DAMAGE. 2512678024SDoug Rabson */ 2612678024SDoug Rabson 2712678024SDoug Rabson #include <sys/cdefs.h> 2812678024SDoug Rabson __FBSDID("$FreeBSD$"); 2912678024SDoug Rabson 3012678024SDoug Rabson #include <sys/param.h> 3112678024SDoug Rabson #include <sys/bus.h> 3212678024SDoug Rabson #include <sys/kernel.h> 3312678024SDoug Rabson #include <sys/malloc.h> 3412678024SDoug Rabson #include <sys/module.h> 3512678024SDoug Rabson 3612678024SDoug Rabson #include <machine/bus.h> 3712678024SDoug Rabson #include <machine/resource.h> 3812678024SDoug Rabson #include <sys/rman.h> 3912678024SDoug Rabson 4012678024SDoug Rabson #include <machine/stdarg.h> 4176acc41fSJustin T. Gibbs 4276acc41fSJustin T. Gibbs #include <xen/xen-os.h> 4312678024SDoug Rabson #include <xen/features.h> 4412678024SDoug Rabson #include <xen/hypervisor.h> 4576acc41fSJustin T. Gibbs #include <xen/hvm.h> 4612678024SDoug Rabson 4712678024SDoug Rabson #include <dev/pci/pcireg.h> 4812678024SDoug Rabson #include <dev/pci/pcivar.h> 4912678024SDoug Rabson 5012678024SDoug Rabson #include <dev/xen/xenpci/xenpcivar.h> 5112678024SDoug Rabson 5276acc41fSJustin T. Gibbs extern void xen_intr_handle_upcall(struct trapframe *trap_frame); 5376acc41fSJustin T. Gibbs 5412678024SDoug Rabson /* 5512678024SDoug Rabson * This is used to find our platform device instance. 5612678024SDoug Rabson */ 5712678024SDoug Rabson static devclass_t xenpci_devclass; 5812678024SDoug Rabson 5912678024SDoug Rabson static int 6076acc41fSJustin T. Gibbs xenpci_intr_filter(void *trap_frame) 6112678024SDoug Rabson { 6276acc41fSJustin T. Gibbs xen_intr_handle_upcall(trap_frame); 6376acc41fSJustin T. Gibbs return (FILTER_HANDLED); 6412678024SDoug Rabson } 6512678024SDoug Rabson 6676acc41fSJustin T. Gibbs static int 6776acc41fSJustin T. Gibbs xenpci_irq_init(device_t device, struct xenpci_softc *scp) 6876acc41fSJustin T. Gibbs { 6976acc41fSJustin T. Gibbs int error; 7076acc41fSJustin T. Gibbs 7176acc41fSJustin T. Gibbs error = BUS_SETUP_INTR(device_get_parent(device), device, 7276acc41fSJustin T. Gibbs scp->res_irq, INTR_MPSAFE|INTR_TYPE_MISC, 7376acc41fSJustin T. Gibbs xenpci_intr_filter, NULL, /*trap_frame*/NULL, 7476acc41fSJustin T. Gibbs &scp->intr_cookie); 7576acc41fSJustin T. Gibbs if (error) 7676acc41fSJustin T. Gibbs return error; 7712678024SDoug Rabson 78428b7ca2SJustin T. Gibbs #ifdef SMP 7912678024SDoug Rabson /* 8076acc41fSJustin T. Gibbs * When using the PCI event delivery callback we cannot assign 8176acc41fSJustin T. Gibbs * events to specific vCPUs, so all events are delivered to vCPU#0 by 8276acc41fSJustin T. Gibbs * Xen. Since the PCI interrupt can fire on any CPU by default, we 8376acc41fSJustin T. Gibbs * need to bind it to vCPU#0 in order to ensure that 8476acc41fSJustin T. Gibbs * xen_intr_handle_upcall always gets called on vCPU#0. 8512678024SDoug Rabson */ 8676acc41fSJustin T. Gibbs error = BUS_BIND_INTR(device_get_parent(device), device, 8776acc41fSJustin T. Gibbs scp->res_irq, 0); 8876acc41fSJustin T. Gibbs if (error) 8976acc41fSJustin T. Gibbs return error; 90428b7ca2SJustin T. Gibbs #endif 9112678024SDoug Rabson 9276acc41fSJustin T. Gibbs xen_hvm_set_callback(device); 9312678024SDoug Rabson return (0); 9412678024SDoug Rabson } 9512678024SDoug Rabson 9612678024SDoug Rabson /* 9712678024SDoug Rabson * Deallocate anything allocated by xenpci_allocate_resources. 9812678024SDoug Rabson */ 9912678024SDoug Rabson static int 10012678024SDoug Rabson xenpci_deallocate_resources(device_t dev) 10112678024SDoug Rabson { 10212678024SDoug Rabson struct xenpci_softc *scp = device_get_softc(dev); 10312678024SDoug Rabson 10412678024SDoug Rabson if (scp->res_irq != 0) { 10512678024SDoug Rabson bus_deactivate_resource(dev, SYS_RES_IRQ, 10612678024SDoug Rabson scp->rid_irq, scp->res_irq); 10712678024SDoug Rabson bus_release_resource(dev, SYS_RES_IRQ, 10812678024SDoug Rabson scp->rid_irq, scp->res_irq); 10912678024SDoug Rabson scp->res_irq = 0; 11012678024SDoug Rabson } 11112678024SDoug Rabson if (scp->res_memory != 0) { 11212678024SDoug Rabson bus_deactivate_resource(dev, SYS_RES_MEMORY, 11312678024SDoug Rabson scp->rid_memory, scp->res_memory); 11412678024SDoug Rabson bus_release_resource(dev, SYS_RES_MEMORY, 11512678024SDoug Rabson scp->rid_memory, scp->res_memory); 11612678024SDoug Rabson scp->res_memory = 0; 11712678024SDoug Rabson } 11812678024SDoug Rabson 11912678024SDoug Rabson return (0); 12012678024SDoug Rabson } 12112678024SDoug Rabson 12212678024SDoug Rabson /* 12312678024SDoug Rabson * Allocate irq and memory resources. 12412678024SDoug Rabson */ 12512678024SDoug Rabson static int 12612678024SDoug Rabson xenpci_allocate_resources(device_t dev) 12712678024SDoug Rabson { 12812678024SDoug Rabson struct xenpci_softc *scp = device_get_softc(dev); 12912678024SDoug Rabson 13012678024SDoug Rabson scp->res_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, 13112678024SDoug Rabson &scp->rid_irq, RF_SHAREABLE|RF_ACTIVE); 132ff662b5cSJustin T. Gibbs if (scp->res_irq == NULL) { 133ff662b5cSJustin T. Gibbs printf("xenpci Could not allocate irq.\n"); 13412678024SDoug Rabson goto errexit; 135ff662b5cSJustin T. Gibbs } 13612678024SDoug Rabson 13712678024SDoug Rabson scp->rid_memory = PCIR_BAR(1); 13812678024SDoug Rabson scp->res_memory = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 13912678024SDoug Rabson &scp->rid_memory, RF_ACTIVE); 140ff662b5cSJustin T. Gibbs if (scp->res_memory == NULL) { 141ff662b5cSJustin T. Gibbs printf("xenpci Could not allocate memory bar.\n"); 14212678024SDoug Rabson goto errexit; 143ff662b5cSJustin T. Gibbs } 144ff662b5cSJustin T. Gibbs 145ff662b5cSJustin T. Gibbs scp->phys_next = rman_get_start(scp->res_memory); 146ff662b5cSJustin T. Gibbs 14712678024SDoug Rabson return (0); 14812678024SDoug Rabson 14912678024SDoug Rabson errexit: 15012678024SDoug Rabson /* Cleanup anything we may have assigned. */ 15112678024SDoug Rabson xenpci_deallocate_resources(dev); 15212678024SDoug Rabson return (ENXIO); /* For want of a better idea. */ 15312678024SDoug Rabson } 15412678024SDoug Rabson 15512678024SDoug Rabson /* 15612678024SDoug Rabson * Allocate a physical address range from our mmio region. 15712678024SDoug Rabson */ 15812678024SDoug Rabson static int 15912678024SDoug Rabson xenpci_alloc_space_int(struct xenpci_softc *scp, size_t sz, 16012678024SDoug Rabson vm_paddr_t *pa) 16112678024SDoug Rabson { 16212678024SDoug Rabson 16312678024SDoug Rabson if (scp->phys_next + sz > rman_get_end(scp->res_memory)) { 16412678024SDoug Rabson return (ENOMEM); 16512678024SDoug Rabson } 16612678024SDoug Rabson 16712678024SDoug Rabson *pa = scp->phys_next; 16812678024SDoug Rabson scp->phys_next += sz; 16912678024SDoug Rabson 17012678024SDoug Rabson return (0); 17112678024SDoug Rabson } 17212678024SDoug Rabson 17312678024SDoug Rabson /* 17412678024SDoug Rabson * Allocate a physical address range from our mmio region. 17512678024SDoug Rabson */ 17612678024SDoug Rabson int 17712678024SDoug Rabson xenpci_alloc_space(size_t sz, vm_paddr_t *pa) 17812678024SDoug Rabson { 17912678024SDoug Rabson device_t dev = devclass_get_device(xenpci_devclass, 0); 18012678024SDoug Rabson 18112678024SDoug Rabson if (dev) { 18212678024SDoug Rabson return (xenpci_alloc_space_int(device_get_softc(dev), 18312678024SDoug Rabson sz, pa)); 18412678024SDoug Rabson } else { 18512678024SDoug Rabson return (ENOMEM); 18612678024SDoug Rabson } 18712678024SDoug Rabson } 18812678024SDoug Rabson 18912678024SDoug Rabson /* 19012678024SDoug Rabson * Probe - just check device ID. 19112678024SDoug Rabson */ 19212678024SDoug Rabson static int 19312678024SDoug Rabson xenpci_probe(device_t dev) 19412678024SDoug Rabson { 19512678024SDoug Rabson 19612678024SDoug Rabson if (pci_get_devid(dev) != 0x00015853) 19712678024SDoug Rabson return (ENXIO); 19812678024SDoug Rabson 19912678024SDoug Rabson device_set_desc(dev, "Xen Platform Device"); 200*aa64d12bSRoger Pau Monné return (BUS_PROBE_DEFAULT); 20112678024SDoug Rabson } 20212678024SDoug Rabson 20312678024SDoug Rabson /* 20412678024SDoug Rabson * Attach - find resources and talk to Xen. 20512678024SDoug Rabson */ 20612678024SDoug Rabson static int 20712678024SDoug Rabson xenpci_attach(device_t dev) 20812678024SDoug Rabson { 20912678024SDoug Rabson struct xenpci_softc *scp = device_get_softc(dev); 21076acc41fSJustin T. Gibbs int error; 211ff662b5cSJustin T. Gibbs 21212678024SDoug Rabson error = xenpci_allocate_resources(dev); 213ff662b5cSJustin T. Gibbs if (error) { 214ff662b5cSJustin T. Gibbs device_printf(dev, "xenpci_allocate_resources failed(%d).\n", 215ff662b5cSJustin T. Gibbs error); 21612678024SDoug Rabson goto errexit; 217ff662b5cSJustin T. Gibbs } 21812678024SDoug Rabson 21912678024SDoug Rabson /* 22012678024SDoug Rabson * Hook the irq up to evtchn 22112678024SDoug Rabson */ 22276acc41fSJustin T. Gibbs error = xenpci_irq_init(dev, scp); 22376acc41fSJustin T. Gibbs if (error) { 22476acc41fSJustin T. Gibbs device_printf(dev, "xenpci_irq_init failed(%d).\n", 22576acc41fSJustin T. Gibbs error); 22676acc41fSJustin T. Gibbs goto errexit; 22776acc41fSJustin T. Gibbs } 22812678024SDoug Rabson 229*aa64d12bSRoger Pau Monné return (0); 23012678024SDoug Rabson 23112678024SDoug Rabson errexit: 23212678024SDoug Rabson /* 23312678024SDoug Rabson * Undo anything we may have done. 23412678024SDoug Rabson */ 23512678024SDoug Rabson xenpci_deallocate_resources(dev); 23612678024SDoug Rabson return (error); 23712678024SDoug Rabson } 23812678024SDoug Rabson 23912678024SDoug Rabson /* 24012678024SDoug Rabson * Detach - reverse anything done by attach. 24112678024SDoug Rabson */ 24212678024SDoug Rabson static int 24312678024SDoug Rabson xenpci_detach(device_t dev) 24412678024SDoug Rabson { 24512678024SDoug Rabson struct xenpci_softc *scp = device_get_softc(dev); 24612678024SDoug Rabson device_t parent = device_get_parent(dev); 24712678024SDoug Rabson 24812678024SDoug Rabson /* 24912678024SDoug Rabson * Take our interrupt handler out of the list of handlers 25012678024SDoug Rabson * that can handle this irq. 25112678024SDoug Rabson */ 25212678024SDoug Rabson if (scp->intr_cookie != NULL) { 25312678024SDoug Rabson if (BUS_TEARDOWN_INTR(parent, dev, 25412678024SDoug Rabson scp->res_irq, scp->intr_cookie) != 0) 255ff662b5cSJustin T. Gibbs device_printf(dev, 256ff662b5cSJustin T. Gibbs "intr teardown failed.. continuing\n"); 25712678024SDoug Rabson scp->intr_cookie = NULL; 25812678024SDoug Rabson } 25912678024SDoug Rabson 26012678024SDoug Rabson /* 26112678024SDoug Rabson * Deallocate any system resources we may have 26212678024SDoug Rabson * allocated on behalf of this driver. 26312678024SDoug Rabson */ 26412678024SDoug Rabson return (xenpci_deallocate_resources(dev)); 26512678024SDoug Rabson } 26612678024SDoug Rabson 26776acc41fSJustin T. Gibbs static int 26876acc41fSJustin T. Gibbs xenpci_resume(device_t dev) 26976acc41fSJustin T. Gibbs { 27076acc41fSJustin T. Gibbs xen_hvm_set_callback(dev); 271*aa64d12bSRoger Pau Monné return (0); 27276acc41fSJustin T. Gibbs } 27376acc41fSJustin T. Gibbs 27412678024SDoug Rabson static device_method_t xenpci_methods[] = { 27512678024SDoug Rabson /* Device interface */ 27612678024SDoug Rabson DEVMETHOD(device_probe, xenpci_probe), 27712678024SDoug Rabson DEVMETHOD(device_attach, xenpci_attach), 27812678024SDoug Rabson DEVMETHOD(device_detach, xenpci_detach), 27976acc41fSJustin T. Gibbs DEVMETHOD(device_resume, xenpci_resume), 28012678024SDoug Rabson 28112678024SDoug Rabson { 0, 0 } 28212678024SDoug Rabson }; 28312678024SDoug Rabson 28412678024SDoug Rabson static driver_t xenpci_driver = { 28512678024SDoug Rabson "xenpci", 28612678024SDoug Rabson xenpci_methods, 28712678024SDoug Rabson sizeof(struct xenpci_softc), 28812678024SDoug Rabson }; 28912678024SDoug Rabson 29012678024SDoug Rabson DRIVER_MODULE(xenpci, pci, xenpci_driver, xenpci_devclass, 0, 0); 291