xenpci.c revision 255040
11638Srgrimes/*
21638Srgrimes * Copyright (c) 2008 Citrix Systems, Inc.
31638Srgrimes * All rights reserved.
41638Srgrimes *
51638Srgrimes * Redistribution and use in source and binary forms, with or without
61638Srgrimes * modification, are permitted provided that the following conditions
71638Srgrimes * are met:
81638Srgrimes * 1. Redistributions of source code must retain the above copyright
91638Srgrimes *    notice, this list of conditions and the following disclaimer.
101638Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111638Srgrimes *    notice, this list of conditions and the following disclaimer in the
121638Srgrimes *    documentation and/or other materials provided with the distribution.
131638Srgrimes *
141638Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS AS IS'' AND
151638Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
161638Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
171638Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
181638Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
191638Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
201638Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
211638Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
221638Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
231638Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
241638Srgrimes * SUCH DAMAGE.
251638Srgrimes */
261638Srgrimes
271638Srgrimes#include <sys/cdefs.h>
281638Srgrimes__FBSDID("$FreeBSD: head/sys/dev/xen/xenpci/xenpci.c 255040 2013-08-29 19:52:18Z gibbs $");
291638Srgrimes
301638Srgrimes#include <sys/param.h>
311638Srgrimes#include <sys/bus.h>
321638Srgrimes#include <sys/kernel.h>
331638Srgrimes#include <sys/malloc.h>
341638Srgrimes#include <sys/module.h>
351638Srgrimes
361638Srgrimes#include <machine/bus.h>
371638Srgrimes#include <machine/resource.h>
381638Srgrimes#include <sys/rman.h>
391638Srgrimes
401638Srgrimes#include <machine/stdarg.h>
411638Srgrimes
421638Srgrimes#include <xen/xen-os.h>
431638Srgrimes#include <xen/features.h>
441638Srgrimes#include <xen/hypervisor.h>
451638Srgrimes#include <xen/hvm.h>
461638Srgrimes
471638Srgrimes#include <dev/pci/pcireg.h>
481638Srgrimes#include <dev/pci/pcivar.h>
491638Srgrimes
501638Srgrimes#include <dev/xen/xenpci/xenpcivar.h>
511638Srgrimes
521638Srgrimesextern void xen_intr_handle_upcall(struct trapframe *trap_frame);
531638Srgrimes
541638Srgrimesstatic device_t nexus;
551638Srgrimes
561638Srgrimes/*
571638Srgrimes * This is used to find our platform device instance.
581638Srgrimes */
591638Srgrimesstatic devclass_t xenpci_devclass;
601638Srgrimes
611638Srgrimesstatic int
621638Srgrimesxenpci_intr_filter(void *trap_frame)
631638Srgrimes{
641638Srgrimes	xen_intr_handle_upcall(trap_frame);
651638Srgrimes	return (FILTER_HANDLED);
661638Srgrimes}
671638Srgrimes
681638Srgrimesstatic int
691638Srgrimesxenpci_irq_init(device_t device, struct xenpci_softc *scp)
701638Srgrimes{
711638Srgrimes	int error;
721638Srgrimes
731638Srgrimes	error = BUS_SETUP_INTR(device_get_parent(device), device,
74101089Smarkm			       scp->res_irq, INTR_MPSAFE|INTR_TYPE_MISC,
751638Srgrimes			       xenpci_intr_filter, NULL, /*trap_frame*/NULL,
761638Srgrimes			       &scp->intr_cookie);
771638Srgrimes	if (error)
781638Srgrimes		return error;
791638Srgrimes
801638Srgrimes	/*
811638Srgrimes	 * When using the PCI event delivery callback we cannot assign
821638Srgrimes	 * events to specific vCPUs, so all events are delivered to vCPU#0 by
831638Srgrimes	 * Xen. Since the PCI interrupt can fire on any CPU by default, we
841638Srgrimes	 * need to bind it to vCPU#0 in order to ensure that
851638Srgrimes	 * xen_intr_handle_upcall always gets called on vCPU#0.
861638Srgrimes	 */
871638Srgrimes	error = BUS_BIND_INTR(device_get_parent(device), device,
881638Srgrimes	                      scp->res_irq, 0);
89101089Smarkm	if (error)
901638Srgrimes		return error;
911638Srgrimes
921638Srgrimes	xen_hvm_set_callback(device);
931638Srgrimes	return (0);
941638Srgrimes}
951638Srgrimes
961638Srgrimes/*
971638Srgrimes * Deallocate anything allocated by xenpci_allocate_resources.
981638Srgrimes */
991638Srgrimesstatic int
1001638Srgrimesxenpci_deallocate_resources(device_t dev)
1011638Srgrimes{
1021638Srgrimes	struct xenpci_softc *scp = device_get_softc(dev);
1031638Srgrimes
1041638Srgrimes	if (scp->res_irq != 0) {
1051638Srgrimes		bus_deactivate_resource(dev, SYS_RES_IRQ,
1061638Srgrimes			scp->rid_irq, scp->res_irq);
1071638Srgrimes		bus_release_resource(dev, SYS_RES_IRQ,
1081638Srgrimes			scp->rid_irq, scp->res_irq);
1091638Srgrimes		scp->res_irq = 0;
1101638Srgrimes	}
1111638Srgrimes	if (scp->res_memory != 0) {
1121638Srgrimes		bus_deactivate_resource(dev, SYS_RES_MEMORY,
1131638Srgrimes			scp->rid_memory, scp->res_memory);
1141638Srgrimes		bus_release_resource(dev, SYS_RES_MEMORY,
1151638Srgrimes			scp->rid_memory, scp->res_memory);
1161638Srgrimes		scp->res_memory = 0;
1171638Srgrimes	}
1181638Srgrimes
1191638Srgrimes	return (0);
1201638Srgrimes}
1211638Srgrimes
1221638Srgrimes/*
1231638Srgrimes * Allocate irq and memory resources.
1241638Srgrimes */
1251638Srgrimesstatic int
1261638Srgrimesxenpci_allocate_resources(device_t dev)
1271638Srgrimes{
1281638Srgrimes	struct xenpci_softc *scp = device_get_softc(dev);
1291638Srgrimes
1301638Srgrimes	scp->res_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ,
1311638Srgrimes			&scp->rid_irq, RF_SHAREABLE|RF_ACTIVE);
1321638Srgrimes	if (scp->res_irq == NULL) {
1331638Srgrimes		printf("xenpci Could not allocate irq.\n");
1341638Srgrimes		goto errexit;
1351638Srgrimes	}
1361638Srgrimes
1371638Srgrimes	scp->rid_memory = PCIR_BAR(1);
1381638Srgrimes	scp->res_memory = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
1391638Srgrimes			&scp->rid_memory, RF_ACTIVE);
1401638Srgrimes	if (scp->res_memory == NULL) {
1411638Srgrimes		printf("xenpci Could not allocate memory bar.\n");
1421638Srgrimes		goto errexit;
1431638Srgrimes	}
1441638Srgrimes
145101089Smarkm	scp->phys_next = rman_get_start(scp->res_memory);
1461638Srgrimes
1471638Srgrimes	return (0);
1481638Srgrimes
1491638Srgrimeserrexit:
1501638Srgrimes	/* Cleanup anything we may have assigned. */
1511638Srgrimes	xenpci_deallocate_resources(dev);
1521638Srgrimes	return (ENXIO); /* For want of a better idea. */
1531638Srgrimes}
1541638Srgrimes
1551638Srgrimes/*
1561638Srgrimes * Allocate a physical address range from our mmio region.
1571638Srgrimes */
1581638Srgrimesstatic int
1591638Srgrimesxenpci_alloc_space_int(struct xenpci_softc *scp, size_t sz,
1601638Srgrimes    vm_paddr_t *pa)
1611638Srgrimes{
1621638Srgrimes
1631638Srgrimes	if (scp->phys_next + sz > rman_get_end(scp->res_memory)) {
1641638Srgrimes		return (ENOMEM);
1651638Srgrimes	}
166101089Smarkm
1671638Srgrimes	*pa = scp->phys_next;
1681638Srgrimes	scp->phys_next += sz;
1691638Srgrimes
1701638Srgrimes	return (0);
1711638Srgrimes}
1721638Srgrimes
1731638Srgrimes/*
1741638Srgrimes * Allocate a physical address range from our mmio region.
1751638Srgrimes */
1761638Srgrimesint
177101089Smarkmxenpci_alloc_space(size_t sz, vm_paddr_t *pa)
1781638Srgrimes{
1791638Srgrimes	device_t dev = devclass_get_device(xenpci_devclass, 0);
1801638Srgrimes
1811638Srgrimes	if (dev) {
1821638Srgrimes		return (xenpci_alloc_space_int(device_get_softc(dev),
1831638Srgrimes			sz, pa));
1841638Srgrimes	} else {
1851638Srgrimes		return (ENOMEM);
1861638Srgrimes	}
1871638Srgrimes}
1881638Srgrimes
1891638Srgrimesstatic struct resource *
1901638Srgrimesxenpci_alloc_resource(device_t dev, device_t child, int type, int *rid,
1911638Srgrimes    u_long start, u_long end, u_long count, u_int flags)
192101089Smarkm{
1931638Srgrimes	return (BUS_ALLOC_RESOURCE(nexus, child, type, rid, start,
1941638Srgrimes	    end, count, flags));
1951638Srgrimes}
1961638Srgrimes
1971638Srgrimes
1981638Srgrimesstatic int
1991638Srgrimesxenpci_release_resource(device_t dev, device_t child, int type, int rid,
2001638Srgrimes    struct resource *r)
2011638Srgrimes{
2021638Srgrimes	return (BUS_RELEASE_RESOURCE(nexus, child, type, rid, r));
2031638Srgrimes}
2041638Srgrimes
2051638Srgrimesstatic int
2061638Srgrimesxenpci_activate_resource(device_t dev, device_t child, int type, int rid,
2071638Srgrimes    struct resource *r)
2081638Srgrimes{
209101089Smarkm	return (BUS_ACTIVATE_RESOURCE(nexus, child, type, rid, r));
2101638Srgrimes}
2111638Srgrimes
2121638Srgrimesstatic int
2131638Srgrimesxenpci_deactivate_resource(device_t dev, device_t child, int type,
2141638Srgrimes    int rid, struct resource *r)
2151638Srgrimes{
2161638Srgrimes	return (BUS_DEACTIVATE_RESOURCE(nexus, child, type, rid, r));
2171638Srgrimes}
2181638Srgrimes
2191638Srgrimes/*
2201638Srgrimes * Probe - just check device ID.
2211638Srgrimes */
2221638Srgrimesstatic int
2231638Srgrimesxenpci_probe(device_t dev)
2241638Srgrimes{
2251638Srgrimes
2261638Srgrimes	if (pci_get_devid(dev) != 0x00015853)
2271638Srgrimes		return (ENXIO);
2281638Srgrimes
2291638Srgrimes	device_set_desc(dev, "Xen Platform Device");
2301638Srgrimes	return (bus_generic_probe(dev));
2311638Srgrimes}
2321638Srgrimes
2331638Srgrimes/*
2341638Srgrimes * Attach - find resources and talk to Xen.
2351638Srgrimes */
2361638Srgrimesstatic int
2371638Srgrimesxenpci_attach(device_t dev)
2381638Srgrimes{
239101089Smarkm	struct xenpci_softc *scp = device_get_softc(dev);
2401638Srgrimes	devclass_t dc;
2411638Srgrimes	int error;
2421638Srgrimes
2431638Srgrimes	/*
2441638Srgrimes	 * Find and record nexus0.  Since we are not really on the
2451638Srgrimes	 * PCI bus, all resource operations are directed to nexus
2461638Srgrimes	 * instead of through our parent.
2471638Srgrimes	 */
2481638Srgrimes	if ((dc = devclass_find("nexus"))  == 0
249101089Smarkm	 || (nexus = devclass_get_device(dc, 0)) == 0) {
2501638Srgrimes		device_printf(dev, "unable to find nexus.");
2511638Srgrimes		return (ENOENT);
2521638Srgrimes	}
2531638Srgrimes
2541638Srgrimes	error = xenpci_allocate_resources(dev);
2551638Srgrimes	if (error) {
256101089Smarkm		device_printf(dev, "xenpci_allocate_resources failed(%d).\n",
2571638Srgrimes		    error);
2581638Srgrimes		goto errexit;
2591638Srgrimes	}
2601638Srgrimes
2611638Srgrimes	/*
2621638Srgrimes	 * Hook the irq up to evtchn
2631638Srgrimes	 */
2641638Srgrimes	error = xenpci_irq_init(dev, scp);
2651638Srgrimes	if (error) {
2661638Srgrimes		device_printf(dev, "xenpci_irq_init failed(%d).\n",
2671638Srgrimes			error);
2681638Srgrimes		goto errexit;
2691638Srgrimes	}
2701638Srgrimes
2711638Srgrimes	return (bus_generic_attach(dev));
272101089Smarkm
2731638Srgrimeserrexit:
2741638Srgrimes	/*
2751638Srgrimes	 * Undo anything we may have done.
2761638Srgrimes	 */
2771638Srgrimes	xenpci_deallocate_resources(dev);
2781638Srgrimes	return (error);
2791638Srgrimes}
2801638Srgrimes
2811638Srgrimes/*
2821638Srgrimes * Detach - reverse anything done by attach.
2831638Srgrimes */
2841638Srgrimesstatic int
2851638Srgrimesxenpci_detach(device_t dev)
2861638Srgrimes{
2871638Srgrimes	struct xenpci_softc *scp = device_get_softc(dev);
2881638Srgrimes	device_t parent = device_get_parent(dev);
2891638Srgrimes
2901638Srgrimes	/*
2911638Srgrimes	 * Take our interrupt handler out of the list of handlers
2921638Srgrimes	 * that can handle this irq.
2931638Srgrimes	 */
2941638Srgrimes	if (scp->intr_cookie != NULL) {
295101089Smarkm		if (BUS_TEARDOWN_INTR(parent, dev,
2961638Srgrimes		    scp->res_irq, scp->intr_cookie) != 0)
2971638Srgrimes			device_printf(dev,
2981638Srgrimes			    "intr teardown failed.. continuing\n");
2991638Srgrimes		scp->intr_cookie = NULL;
3001638Srgrimes	}
301101089Smarkm
3021638Srgrimes	/*
3031638Srgrimes	 * Deallocate any system resources we may have
3041638Srgrimes	 * allocated on behalf of this driver.
3051638Srgrimes	 */
3061638Srgrimes	return (xenpci_deallocate_resources(dev));
3071638Srgrimes}
3081638Srgrimes
309101089Smarkmstatic int
3101638Srgrimesxenpci_suspend(device_t dev)
3111638Srgrimes{
3121638Srgrimes	struct xenpci_softc *scp = device_get_softc(dev);
3131638Srgrimes	device_t parent = device_get_parent(dev);
3141638Srgrimes
3151638Srgrimes	if (scp->intr_cookie != NULL) {
3161638Srgrimes		if (BUS_TEARDOWN_INTR(parent, dev, scp->res_irq,
3171638Srgrimes		    scp->intr_cookie) != 0)
3181638Srgrimes			printf("intr teardown failed.. continuing\n");
3191638Srgrimes		scp->intr_cookie = NULL;
3201638Srgrimes	}
3211638Srgrimes
3221638Srgrimes	return (bus_generic_suspend(dev));
3231638Srgrimes}
3241638Srgrimes
3251638Srgrimesstatic int
3261638Srgrimesxenpci_resume(device_t dev)
3271638Srgrimes{
3281638Srgrimes	struct xenpci_softc *scp = device_get_softc(dev);
3291638Srgrimes	device_t parent = device_get_parent(dev);
3301638Srgrimes
3311638Srgrimes	BUS_SETUP_INTR(parent, dev, scp->res_irq,
3321638Srgrimes	    INTR_MPSAFE|INTR_TYPE_MISC, xenpci_intr_filter, NULL,
3331638Srgrimes	    /*trap_frame*/NULL, &scp->intr_cookie);
3341638Srgrimes	xen_hvm_set_callback(dev);
3351638Srgrimes	return (bus_generic_resume(dev));
3361638Srgrimes}
3371638Srgrimes
3381638Srgrimesstatic device_method_t xenpci_methods[] = {
3391638Srgrimes	/* Device interface */
3401638Srgrimes	DEVMETHOD(device_probe,		xenpci_probe),
3411638Srgrimes	DEVMETHOD(device_attach,	xenpci_attach),
3421638Srgrimes	DEVMETHOD(device_detach,	xenpci_detach),
3431638Srgrimes	DEVMETHOD(device_suspend,	xenpci_suspend),
344101089Smarkm	DEVMETHOD(device_resume,	xenpci_resume),
3451638Srgrimes
3461638Srgrimes	/* Bus interface */
3471638Srgrimes	DEVMETHOD(bus_add_child,	bus_generic_add_child),
3481638Srgrimes	DEVMETHOD(bus_alloc_resource,   xenpci_alloc_resource),
3491638Srgrimes	DEVMETHOD(bus_release_resource, xenpci_release_resource),
3501638Srgrimes	DEVMETHOD(bus_activate_resource, xenpci_activate_resource),
3511638Srgrimes	DEVMETHOD(bus_deactivate_resource, xenpci_deactivate_resource),
3521638Srgrimes
3531638Srgrimes	{ 0, 0 }
3541638Srgrimes};
3551638Srgrimes
3561638Srgrimesstatic driver_t xenpci_driver = {
3571638Srgrimes	"xenpci",
3581638Srgrimes	xenpci_methods,
3591638Srgrimes	sizeof(struct xenpci_softc),
3601638Srgrimes};
3611638Srgrimes
3621638SrgrimesDRIVER_MODULE(xenpci, pci, xenpci_driver, xenpci_devclass, 0, 0);
3631638Srgrimes