DragonFly BSD
DragonFly kernel List (threaded) for 2006-02
[Date Prev][Date Next]  [Thread Prev][Thread Next]  [Date Index][Thread Index]

MSI prototype


From: Chuck Tuffli <ctuffli@xxxxxxxxx>
Date: Tue, 21 Feb 2006 00:32:06 -0800

Below is an overview of the implementation followed by the problems
the current API has with regard to this new kind of interrupt. What
I'm looking for are suggestions on ways to fix the API. It is possible
to "force" the current API to do what is necessary, but then again, it
may be better to start off with totally new interfaces. I'll also
apologize in advance for the length of this mail :)

Attached are two files. The first is a patch against the 1.4.x branch
which adds support for Message Signaled Interrupts (MSI). The second
is a driver which uses the new functionality. The driver should work
on any system with 1) a local APIC that implements a timer and 2) an
unclaimed PCI devices that supports MSI (bridge ports seem to be good
candidates). MSI requires that SMP be enabled to activate the Local
APIC.

The gist of this implementation is:

- during PCI enumeration, any device with a MSI capability structure
gets a MSI resource added to its resource list
- drivers can allocate a MSI vector using bus_alloc_resource() in the
usual way with 2 modifications: use SYS_RES_MSI and specify a range of
allowable vectors (including the entire range of 0 to MAX).
- drivers can activate the resource using bus_setup_intr().
Internally, this programs both the MSI address and data fields of the
device.

What the allocation step does is search the irq_rman structure looking
for an unshared resource (i.e. an unused IRQ) and uses that as the
basis for a MSI vector. This is why the bus_alloc_resource() step uses
a range (start/end) as parameters instead of the start==end==irq
method used for SYS_RES_IRQ. You might wonder why we don't use the
same technique as for IRQs and pre-assign vectors. Lazy allocation is
a better strategy for MSI and MSI-X ("MSI extended") as the capability
structure can only indicate the max supported number of vectors (up to
1K vectors in some cases) which may be much larger than the actual
number used. Therefore, it is best left to the drivers to decide how
many vectors to allocate AND what to do if they can't get as many as
they want/need.

The problems

I basically like the bus_alloc_resource and bus_setup_intr philosophy,
but they currently pose separate problems for MSI and MSI-X.

1) MSI allows multiple vectors per function but requires that the
vectors are contiguous (e.g. vectors 1,2,3 or 2,3,4 are OK but 2,4,5
are not). So while the current implementation finds the first unused
vector, there isn't a way to specify that this must be the first of 3
free vectors (for example). Additionally, when allocating the
subsequent vectors, there isn't a way to correlate the current
allocation with a previous one.

2) MSI-X avoids the above problem because it has separate address/data
registers for each supported interrupt (i.e. MSI only has a single
address/data register for ALL interrupts). The problem here is with
setting up the interrupt. Once an interrupt is allocated, there isn't
a way too say "use this address/data for the 3rd interrupt" (which
translates into "write this to the 3rd addr/data registers").

3) Another minor problem is in the resource implementation which
currently panics (IIRC ...) if the system tries to add more than one
resource of the same type to the resource list (i.e. can't have
multiple SYS_RES_MSI). This one I'm less worried about than the
previous 2.

So, what to do? If it were up to me, I'd be tempted to modify the
current interfaces to support the additional information needed
because I like the alloc/setup metaphors. The downside to this is
modifying "a couple" ;) of drivers to support the new API. Thoughts?

---chuck



/*
 * Copyright (c) 2006 The DragonFly Project.  All rights reserved.
 * 
 * This code is derived from software contributed to The DragonFly Project
 * by Chuck Tuffli <ctuffli@xxxxxxxxx>
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name of The DragonFly Project nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific, prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * 
 * $DragonFly: Exp $
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/bus.h>

#include <bus/pci/pcireg.h>
#include <bus/pci/pcivar.h>

#include <machine/resource.h>
#include <sys/rman.h>

#include <machine/apicreg.h>
#include <machine/smp.h>

#define MSID_DESCR		"MSI test driver"

extern volatile lapic_t		lapic;

typedef struct msid_softc_s {
	device_t dev;
	struct resource *intr_res;
	uint	intr_rid;
	void	*intr_h;
} msid_t;

static int msid_probe(device_t);
static int msid_attach(device_t);
static int msid_detach(device_t);
static void msid_intr(void *);

static device_method_t msid_methods[] = {
	DEVMETHOD(device_probe,		msid_probe),
	DEVMETHOD(device_attach,	msid_attach),
	DEVMETHOD(device_detach,	msid_detach),
	{ 0, 0 }
};

static driver_t msid_driver = {
	"msid", msid_methods, sizeof(msid_t)
};

static devclass_t msid_devclass;
DRIVER_MODULE(msid, pci, msid_driver, msid_devclass, 0, 0);
MODULE_VERSION(msid, 1);

void
msid_intr(void *arg)
{
	printf("msid_intr\n");
}

int
msid_probe(device_t dev)
{
	/*
	 * If the device is capable of supporting MSI and noone else
	 * has claimed it, call dibs on it.
	 */
	if (0 == pci_get_msi(dev)) {
		return ENXIO;
	}

	/* only load once */
	if (device_get_unit(dev) == 0) {
		device_printf(dev, MSID_DESCR"\n");
		device_set_desc(dev, MSID_DESCR);
		return 0;
	} else {
		return ENXIO;
	}
}

#define MAX_VEC 24

int
msid_attach(device_t dev)
{
	msid_t	*msid;
	uint32_t	val;

	msid = (msid_t *)device_get_softc(dev);
	if (NULL == msid) {
		device_printf(dev, "can't alloc softc?!?!\n");
		return ENOMEM;
	}
	bzero(msid, sizeof(msid_t));
	msid->dev = dev;

	/* enable bus mastering for MSI */
	val = pci_read_config(dev, PCIR_COMMAND, 2);
	val |= PCIM_CMD_BUSMASTEREN;
	pci_write_config(dev, PCIR_COMMAND, val, 2);

	/*
	 * setup interrupts. note that flags doesn't include RF_SHAREABLE
	 * and that the range allows the resource manager to find any
	 * available interrupt vector.
	 */
	msid->intr_res = bus_alloc_resource(dev, SYS_RES_MSI, &msid->intr_rid,
			1, MAX_VEC, 1, RF_ACTIVE);
	if (NULL == msid->intr_res) {
		device_printf(dev, "couldn't alloc interrupt vector\n");
		return ENXIO;
	}

	if (bus_setup_intr(dev, msid->intr_res, 0, msid_intr, msid,
				&msid->intr_h, NULL)) {
		device_printf(dev, "couldn't setup interrupt\n");
	} else {
		/*
		 * Program LVT Timer (Local Vector Table) on the local APIC
		 * to generate a MSI using the vector allocated above. Normally,
		 * a device specific external event (e.g. receiving a packet)
		 * would trigger the interrupt, but this technique is much easier
		 * to debug ;)
		 *
		 * Note that this only generates a single interrupt. Unload and
		 * reload the driver to trigger another interrupt.
		 *
		 * To convert an IRQ to a vector, add 32 to offset into the
		 * user-defined vector range. The assignment to lvt_timer sets up
		 * a one shot, unmasked timer using the same vector as our
		 * adopted MSI device.
		 */
		device_printf(dev, "irq %#lx\n", rman_get_start(msid->intr_res));
		lapic.lvt_timer = 32 + rman_get_start(msid->intr_res);
		lapic.icr_timer = 1;
	}

	return 0;
}

int
msid_detach(device_t dev)
{
	msid_t	*msid;

	msid = (msid_t *)device_get_softc(dev);
	if (NULL == msid) {
		device_printf(dev, "can't get softc?!?!\n");
		return ENOMEM;
	}

	printf("lvt_timer=%#x\n", lapic.lvt_timer);

	lapic.lvt_timer = APIC_LVTT_M;

	if (NULL != msid->intr_h) {
		bus_teardown_intr(dev, msid->intr_res, msid->intr_h);
		msid->intr_h = NULL;
	}

	if (NULL != msid->intr_res) {
		bus_release_resource(dev, SYS_RES_MSI, 0, msid->intr_res);
		msid->intr_res = 0;
	}

	return 0;
}

diff -ruN sys.orig/bus/pci/i386/pcibus.c sys/bus/pci/i386/pcibus.c
--- sys.orig/bus/pci/i386/pcibus.c	2005-10-29 21:41:12.000000000 -0700
+++ sys/bus/pci/i386/pcibus.c	2006-02-21 05:47:49.000000000 -0800
@@ -41,6 +41,7 @@
 #include <bus/pci/i386/pci_cfgreg.h>
 #include <machine/md_var.h>
 #include <machine/nexusvar.h>
+#include <machine/resource.h>
 
 #include "pcib_if.h"
 
@@ -317,6 +318,9 @@
 	if (pci_cfgregopen() == 0)
 		return (ENXIO);
 
+	/* initialize the MSI resource */
+	BUS_SET_RESOURCE(dev, dev, SYS_RES_MSI, 0, 0, 256 /* XXX */);
+
 	/*
 	 * Scan away!
 	 */
diff -ruN sys.orig/bus/pci/pci.c sys/bus/pci/pci.c
--- sys.orig/bus/pci/pci.c	2005-11-04 00:57:22.000000000 -0800
+++ sys/bus/pci/pci.c	2006-02-20 23:05:42.000000000 -0800
@@ -66,6 +66,8 @@
 
 #include "pcib_if.h"
 
+#include <sys/msi.h>
+
 devclass_t	pci_devclass;
 const char	*pcib_owner;
 
@@ -437,7 +439,7 @@
 
 	switch (cfg->hdrtype) {
 	case 0:
-		ptrptr = 0x34;
+		ptrptr = PCIR_CAP_PTR;
 		break;
 	case 2:
 		ptrptr = 0x14;
@@ -463,7 +465,7 @@
 
 		/* Process this entry */
 		switch (REG(ptr, 1)) {
-		case 0x01:		/* PCI power management */
+		case PCIY_PMG:		/* PCI power management */
 			if (cfg->pp_cap == 0) {
 				cfg->pp_cap = REG(ptr + PCIR_POWER_CAP, 2);
 				cfg->pp_status = ptr + PCIR_POWER_STATUS;
@@ -472,6 +474,13 @@
 					cfg->pp_data = ptr + PCIR_POWER_DATA;
 			}
 			break;
+		case PCIY_MSI:
+			if (cfg->msi_cap == 0) {
+				/* save the offset for later use */
+				cfg->msi_cap = ptr;
+				cfg->msi_ctrl = REG(ptr + PCIR_MSI_CTRL, 2);
+				/* Address(es) and data assigned when driver allocates */
+			}
 		default:
 			break;
 		}
@@ -1393,6 +1402,10 @@
 	if (cfg->intpin > 0 && cfg->intline != 255)
 		resource_list_add(rl, SYS_RES_IRQ, 0,
 				  cfg->intline, cfg->intline, 1);
+
+	if (cfg->msi_cap != 0) {
+		resource_list_add(rl, SYS_RES_MSI, 0, 0, 0, 1);
+	}
 }
 
 void
@@ -1641,6 +1654,9 @@
 		 */
 		*result = NULL;
 		return (EINVAL);
+	case PCI_IVAR_MSI:
+		*result = cfg->msi_cap;
+		break;
 	default:
 		return ENOENT;
 	}
@@ -1907,6 +1923,86 @@
         return (bus_generic_resume(dev));
 }
 
+static int
+pci_setup_intr(device_t dev, device_t child, struct resource *intr,
+		 int flags, void (*ihand)(void *), void *arg,
+		 void **cookiep, lwkt_serialize_t serializer)
+{
+	if ((NULL != intr) && (SYS_RES_MSI == rman_get_type(intr))) {
+        struct pci_devinfo *dinfo;
+        pcicfgregs	*cfg;
+		uint32_t	data_off = 0;
+
+		dinfo = device_get_ivars(child);
+		cfg = &dinfo->cfg;
+		
+		if (msi_address_get(rman_get_start(intr), cfg)) {
+			printf("error getting MSI address/data\n");
+			return -1;
+		}
+		printf("pci_setup_intr msi: cap=%x ctl=%x adr=%x adH=%x d=%x\n",
+				cfg->msi_cap, cfg->msi_ctrl, cfg->msi_addr, cfg->msi_addr_hi,
+				cfg->msi_data);
+		
+		/* TODO disable INTx */
+
+		pci_write_config(child, cfg->msi_cap + PCIR_MSI_ADDR, cfg->msi_addr, 4);
+
+		if (cfg->msi_ctrl & PCIM_MSICTRL_64BIT) {
+			pci_write_config(child, cfg->msi_cap + PCIR_MSI_ADDR_HIGH,
+					cfg->msi_addr_hi, 4);
+			data_off = PCIR_MSI_DATA_64BIT;
+		} else {
+			data_off = PCIR_MSI_DATA;
+		}
+
+		pci_write_config(child, cfg->msi_cap + data_off, cfg->msi_data, 2);
+
+		/* TODO MSI masking */
+
+		cfg->msi_ctrl |= PCIM_MSICTRL_MSI_ENABLE;
+		pci_write_config(child, cfg->msi_cap + PCIR_MSI_CTRL, cfg->msi_ctrl, 2);
+	}
+
+	return (bus_generic_setup_intr(device_get_parent(dev), child, intr,
+				flags, ihand, arg, cookiep, serializer));
+}
+
+static int
+pci_teardown_intr(device_t dev, device_t child, struct resource *intr, void *ih)
+{
+	if ((NULL != intr) && (SYS_RES_MSI == rman_get_type(intr))) {
+        struct pci_devinfo *dinfo;
+        pcicfgregs	*cfg;
+		uint32_t	data_off = 0;
+
+		dinfo = device_get_ivars(child);
+		cfg = &dinfo->cfg;
+		
+		/* TODO re-enable INTx */
+
+		cfg->msi_addr = 0;
+		pci_write_config(child, cfg->msi_cap + PCIR_MSI_ADDR, 0, 4);
+
+		cfg->msi_addr_hi = 0;
+		if (cfg->msi_ctrl & PCIM_MSICTRL_64BIT) {
+			pci_write_config(child, cfg->msi_cap + PCIR_MSI_ADDR_HIGH, 0, 4);
+			data_off = PCIR_MSI_DATA_64BIT;
+		} else {
+			data_off = PCIR_MSI_DATA;
+		}
+
+		cfg->msi_data = 0;
+		pci_write_config(child, cfg->msi_cap + data_off, 0, 2);
+
+		/* TODO MSI masking */
+
+		cfg->msi_ctrl &= ~PCIM_MSICTRL_MSI_ENABLE;
+		pci_write_config(child, cfg->msi_cap + PCIR_MSI_CTRL, cfg->msi_ctrl, 2);	}
+		
+	return(bus_generic_teardown_intr(device_get_parent(dev), child, intr, ih));
+}
+
 static device_method_t pci_methods[] = {
 	/* Device interface */
 	DEVMETHOD(device_probe,		pci_probe),
@@ -1921,8 +2017,8 @@
 	DEVMETHOD(bus_read_ivar,	pci_read_ivar),
 	DEVMETHOD(bus_write_ivar,	pci_write_ivar),
 	DEVMETHOD(bus_driver_added,	bus_generic_driver_added),
-	DEVMETHOD(bus_setup_intr,	bus_generic_setup_intr),
-	DEVMETHOD(bus_teardown_intr,	bus_generic_teardown_intr),
+	DEVMETHOD(bus_setup_intr,	pci_setup_intr),
+	DEVMETHOD(bus_teardown_intr,	pci_teardown_intr),
 
 	DEVMETHOD(bus_get_resource_list,pci_get_resource_list),
 	DEVMETHOD(bus_set_resource,	pci_set_resource),
diff -ruN sys.orig/bus/pci/pcivar.h sys/bus/pci/pcivar.h
--- sys.orig/bus/pci/pcivar.h	2005-10-29 21:41:10.000000000 -0700
+++ sys/bus/pci/pcivar.h	2006-02-15 15:47:51.000000000 -0800
@@ -94,6 +94,12 @@
     u_int8_t	pp_status;	/* config space address of PCI power status reg */
     u_int8_t	pp_pmcsr;	/* config space address of PMCSR reg */
     u_int8_t	pp_data;	/* config space address of PCI power data reg */
+
+	u_int16_t	msi_cap;
+	u_int16_t	msi_ctrl;
+	u_int32_t	msi_addr;
+	u_int32_t	msi_addr_hi;
+	u_int16_t	msi_data;
 } pcicfgregs;
 
 /* additional type 1 device config header information (PCI to PCI bridge) */
@@ -195,6 +201,7 @@
 	PCI_IVAR_SECONDARYBUS,
 	PCI_IVAR_SUBORDINATEBUS,
 	PCI_IVAR_ETHADDR,
+	PCI_IVAR_MSI,
 };
 
 /*
@@ -236,6 +243,7 @@
 PCI_ACCESSOR(secondarybus,	SECONDARYBUS,	u_int8_t)
 PCI_ACCESSOR(subordinatebus,	SUBORDINATEBUS,	u_int8_t)
 PCI_ACCESSOR(ether,		ETHADDR,	uint8_t *)
+PCI_ACCESSOR(msi,		MSI,	uint16_t)
 
 #undef PCI_ACCESSOR
 
diff -ruN sys.orig/conf/files.i386 sys/conf/files.i386
--- sys.orig/conf/files.i386	2005-11-28 09:13:25.000000000 -0800
+++ sys/conf/files.i386	2006-02-15 13:45:57.000000000 -0800
@@ -196,6 +196,7 @@
 # temporarily not in build until we get the 'apic' option working on UP
 #i386/i386/io_apic.c		optional	smp
 #i386/i386/local_apic.c		optional	smp
+i386/i386/msi.c			optional	smp
 i386/i386/mpboot.s		optional	smp
 i386/i386/mplock.s		optional	smp
 i386/i386/mp_clock.c		optional	smp
diff -ruN sys.orig/i386/apic/apicreg.h sys/i386/apic/apicreg.h
--- sys.orig/i386/apic/apicreg.h	2005-11-03 15:45:09.000000000 -0800
+++ sys/i386/apic/apicreg.h	2006-02-16 15:54:16.000000000 -0800
@@ -742,6 +742,23 @@
 #define APIC_IRQ22		22
 #define APIC_IRQ23		23
 
+/*
+ * Interrupt Message Address format
+ * 63               31     19   11  321
+ * +----------------+------+----+---+++-
+ * Upper Addr       Addr   DID  Rsv HM
+ *
+ * Upper Addr - zero even for devices supporting a 64bit address
+ * Addr       - must be FFEh
+ * DID        - Destination ID of local APIC or group of local APICs
+ * H          - redirection Hint bit: 1 = lowest-priority delivery mode
+ * M          - destination Mode bit: 0 = physical, 1 = logical
+ */
+
+#define APIC_ADDR_REDIR_LOWPRI	0x00000008
+#define APIC_ADDR_DESTMODE_PHY	0x00000000
+#define APIC_ADDR_DESTMODE_LOG	0x00000004
+
 /******************************************************************************
  * I/O APIC defines
  */
diff -ruN sys.orig/i386/i386/msi.c sys/i386/i386/msi.c
--- sys.orig/i386/i386/msi.c	1969-12-31 16:00:00.000000000 -0800
+++ sys/i386/i386/msi.c	2006-02-21 05:50:10.000000000 -0800
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2006 The DragonFly Project.  All rights reserved.
+ * 
+ * This code is derived from software contributed to The DragonFly Project
+ * by Chuck Tuffli <ctuffli@xxxxxxxxx>
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific, prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ * $DragonFly: Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+
+#include <bus/pci/pcivar.h>
+#include <sys/msi.h>
+
+#include <machine/globaldata.h>
+#include <machine/segments.h>
+
+#include <arch/apic/apicreg.h>
+#include <arch/apic/apicvar.h>
+
+#include <i386/isa/intr_machdep.h>	/* IDT_OFFSET */
+
+
+/* global data in mpapic.c */
+extern volatile lapic_t		lapic;
+
+/* global data in mp_machdep.c */
+extern vm_offset_t		cpu_apic_address;
+
+/*
+ * machine dependent functions for Message Signaled Interrupts (MSI)
+ */
+
+
+/*
+ * Construct an address/data pair suitable for MSI given a vector
+ */
+int32_t
+msi_address_get(uint32_t vec, pcicfgregs *cfg)
+{
+	if (!(vec < APIC_LOCAL_INTS)) {
+		return -1;
+	}
+
+	cfg->msi_addr_hi = 0;
+	cfg->msi_addr	 = cpu_apic_address |
+						(lapic.id << 12) |
+						APIC_ADDR_REDIR_LOWPRI |
+						APIC_ADDR_DESTMODE_PHY;
+
+	/* device vectors start at 32 in IA, but the input to this function
+	 * is zero based
+	 */
+	cfg->msi_data    = APIC_TRIGMOD_EDGE |
+						APIC_LEVEL_ASSERT |
+						APIC_DESTMODE_PHY |
+						APIC_DELMODE_LOWPRIO |
+						(vec + IDT_OFFSET);
+
+	printf("msi_address_get: h=%x a=%x d=%x\n", cfg->msi_addr_hi,
+			cfg->msi_addr, cfg->msi_data);
+
+	return 0;
+}
diff -ruN sys.orig/i386/i386/nexus.c sys/i386/i386/nexus.c
--- sys.orig/i386/i386/nexus.c	2005-11-21 10:02:40.000000000 -0800
+++ sys/i386/i386/nexus.c	2006-02-16 15:00:58.000000000 -0800
@@ -361,6 +361,9 @@
 	flags &= ~RF_ACTIVE;
 
 	switch (type) {
+	/* XXX cft - for now leach off of irq_rman */
+	case SYS_RES_MSI:
+	case SYS_RES_MSIX:
 	case SYS_RES_IRQ:
 		rm = &irq_rman;
 		break;
@@ -392,6 +395,8 @@
 		rman_set_bushandle(rv, rv->r_start);
 	}
 
+	rman_set_type(rv, type);
+
 	if (needactivate) {
 		if (bus_activate_resource(child, type, *rid, rv)) {
 			rman_release_resource(rv);
diff -ruN sys.orig/i386/include/resource.h sys/i386/include/resource.h
--- sys.orig/i386/include/resource.h	2003-06-16 21:28:36.000000000 -0700
+++ sys/i386/include/resource.h	2006-02-12 19:05:32.000000000 -0800
@@ -41,5 +41,7 @@
 #define	SYS_RES_DRQ	2	/* isa dma lines */
 #define	SYS_RES_MEMORY	3	/* i/o memory */
 #define	SYS_RES_IOPORT	4	/* i/o ports */
+#define SYS_RES_MSI		5	/* Message Signaled Interrupts */
+#define SYS_RES_MSIX	6	/* enhanced Message Signaled Interrupts */
 
 #endif /* !_MACHINE_RESOURCE_H_ */
diff -ruN sys.orig/sys/msi.h sys/sys/msi.h
--- sys.orig/sys/msi.h	1969-12-31 16:00:00.000000000 -0800
+++ sys/sys/msi.h	2006-02-21 05:49:40.000000000 -0800
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2006 The DragonFly Project.  All rights reserved.
+ * 
+ * This code is derived from software contributed to The DragonFly Project
+ * by Chuck Tuffli <ctuffli@xxxxxxxxx>
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific, prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ * $DragonFly: Exp $
+ */
+
+#ifndef _SYS_MSI_H_
+#define _SYS_MSI_H_
+
+extern int32_t msi_address_get(uint32_t vec, pcicfgregs *cfg);
+
+#endif /* !_SYS_MSI_H_ */
diff -ruN sys.orig/sys/rman.h sys/sys/rman.h
--- sys.orig/sys/rman.h	2005-08-06 17:31:31.000000000 -0700
+++ sys/sys/rman.h	2006-02-16 15:02:49.000000000 -0800
@@ -59,6 +59,15 @@
 	bus_space_handle_t r_bushandle;	/* bus_space handle */
 	struct	device *r_dev;	/* device which has allocated this resource */
 	struct	rman *r_rm;	/* resource manager from whence this came */
+
+	/* XXX resource_list_entry type. needed to differentiate IRQ and MSI.
+	 * <rant>
+	 * This structure is turning into a dumping ground of special case
+	 * values (e.g. why have a r_bustag var for an interrupt resource). Not
+	 * that adding the below is any better ...
+	 * </rant>
+	 */
+	int		rle_type;
 };
 
 #define	RF_ALLOCATED	0x0001	/* resource has been reserved */
@@ -117,6 +126,8 @@
 #define rman_get_bustag(r)	((r)->r_bustag)
 #define rman_set_bushandle(r,h)	((r)->r_bushandle = (h))
 #define rman_get_bushandle(r)	((r)->r_bushandle)
+#define rman_set_type(r,t)	((r)->rle_type = (t))
+#define rman_get_type(r)	((r)->rle_type)
 
 extern	struct rman_head rman_head;
 #endif /* _KERNEL */



[Date Prev][Date Next]  [Thread Prev][Thread Next]  [Date Index][Thread Index]