Skip to contentSkip to conversation

msi: set 'En' bit of MSI Mapping Capability on HT platform

PATCH In review peerchen 4 participants 5 messages Nov 25, 2007 – Nov 27, 2007 PCI

lore
Share & tools
lore

peerchen

author
#0 lore raw

PATCH 1/2 msi: set 'En' bit of MSI Mapping Capability on HT platform

According to the HyperTransport spec, 'En' indicate if the MSI Mapping is active. So it should be set when enable the MSI.

The patch base on kernel 2.6.24-rc3

---

drivers/pci/msi.c renamed from drivers/pci/msi.c.orig +103 -0
@@ -20,6 +20,8 @@
 #include <asm/errno.h>
 #include <asm/io.h>
 
+#include <asm/k8.h>
+
 #include "pci.h"
 #include "msi.h"
 
@@ -290,6 +292,99 @@ void pci_restore_msi_state(struct pci_de
 }
 #endif	/* CONFIG_PM */
 
+/*
+ * pci_enable_msi_ht_cap - Set the HT MSI mapping capability En bit of
+ * a device.
+ *
+ * @dev: pointer to the pci_dev data structure of MSI device function
+ */
+
+static int pci_enable_msi_ht_cap(struct pci_dev *dev)
+{
+	int pos;
+	u8 flags;
+
+	if ((pos = pci_find_ht_capability(dev, HT_CAPTYPE_MSI_MAPPING)) != 0)
+	{	
+		pci_read_config_byte(dev, pos + HT_MSI_FLAGS, &flags);
+		pci_write_config_byte(dev, pos + HT_MSI_FLAGS,
+						  flags | HT_MSI_FLAGS_ENABLE);
+
+		printk(KERN_INFO "PCI: %s: enabled HT MSI mapping\n", pci_name(dev));
+	}
+
+	return pos;
+}
+
+/**
+ * pci_check_msi_ht_cap - check for and enable the MSI mapping capability En bit
+ * of devices or upstream bridge on HT-base system.
+ * @dev: pointer to the pci_dev data structure of MSI device function
+ *
+ * Search if device support ht MSI mapping capability on HT-base 
+ * platform, if yes, enable the En bit. If device can't support MSI mapping,
+ * search the the upstream bridge for that capability, enable En bit find it, 
+ * otherwise disable the MSI function if device and upstream bridge can't 
+ * support MSI mapping capability.
+ **/
+
+static int pci_check_msi_ht_cap(struct pci_dev *dev)
+{
+	struct pci_dev *bridge_dev;
+	
+	if (num_k8_northbridges != 0) {	/* If the system is the HT-base */
+
+		/* Check for upstream NVIDIA host bridges */
+
+		if (((bridge_dev = pci_find_slot(0, 0)) != NULL) &&
+				 (bridge_dev->vendor == PCI_VENDOR_ID_NVIDIA)) {
+			switch (bridge_dev->device) {
+			case PCI_DEVICE_ID_NVIDIA_NFORCE_C51_MEMC0:
+			case PCI_DEVICE_ID_NVIDIA_NFORCE_C51_MEMC1:
+			case PCI_DEVICE_ID_NVIDIA_NFORCE_C51_MEMC2:
+			case PCI_DEVICE_ID_NVIDIA_NFORCE_C51_MEMC3:
+			case PCI_DEVICE_ID_NVIDIA_NFORCE_C51_MEMC4:
+			case PCI_DEVICE_ID_NVIDIA_NFORCE_C51_MEMC5:
+			case PCI_DEVICE_ID_NVIDIA_NFORCE_C51_MEMC6:
+			case PCI_DEVICE_ID_NVIDIA_NFORCE_C51_MEMC7:
+			case PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_MEMC:
+
+				pci_enable_msi_ht_cap(bridge_dev);
+
+				bridge_dev = NULL;
+				while ((bridge_dev = pci_get_device(PCI_VENDOR_ID_NVIDIA,
+					PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_MEMC, bridge_dev))
+					!= NULL) {
+					pci_enable_msi_ht_cap(bridge_dev);
+			 	}
+
+				break;
+
+			default:
+				break;
+			}
+		}
+
+
+		if (pci_enable_msi_ht_cap(dev) != 0) {
+			return 0;
+		} else {
+			/* Get upstream bridge device handle */
+
+			bridge_dev = dev->bus->self;
+			while(bridge_dev != 0) {
+				if (pci_enable_msi_ht_cap(bridge_dev) != 0) {
+					return 0;
+				} else
+					bridge_dev = bridge_dev->bus->self;
+			}
+
+			return 1;
+		}
+	}
+	return 0;
+}
+
 /**
  * msi_capability_init - configure device's MSI capability structure
  * @dev: pointer to the pci_dev data structure of MSI device function
@@ -510,6 +605,10 @@ int pci_enable_msi(struct pci_dev* dev)
 	status = pci_msi_check_device(dev, 1, PCI_CAP_ID_MSI);
 	if (status)
 		return status;
+	
+	status = pci_check_msi_ht_cap(dev);
+	if(status)
+		return status;
 
 	WARN_ON(!!dev->msi_enabled);
 
@@ -606,6 +705,10 @@ int pci_enable_msix(struct pci_dev* dev,
 	if (status)
 		return status;
 
+	status = pci_check_msi_ht_cap(dev);
+	if(status)
+		return status;
+
 	pos = pci_find_capability(dev, PCI_CAP_ID_MSIX);
 	pci_read_config_word(dev, msi_control_reg(pos), &control);
 	nr_entries = multi_msix_capable(control);

-

#1 lore raw

Hi,

Quoted 2 lines

According to the HyperTransport spec, 'En' indicate if the MSI Mapping is active. So it should be set when enable the MSI.

Great! This is what I needed to get MSI going on my MCP51 board. I added some ids, but I think there were not necessary, as I only got

PCI: 0000:00:00.0: enabled HT MSI mapping
PCI: 0000:00:10.1: enabled HT MSI mapping

were those devices are:
00:00.0 RAM memory: nVidia Corporation C51 Host Bridge (rev a2)
Subsystem: ASUSTeK Computer Inc. Unknown device 81c0
Flags: bus master, 66MHz, fast devsel, latency 0
Capabilities: [44] HyperTransport: Slave or Primary Interface
Capabilities: [e0] HyperTransport: MSI Mapping Enable+ Fixed-
00: de 10 f0 02 06 00 b0 00 a2 00 00 05 00 00 80 00
10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
20: 00 00 00 00 00 00 00 00 00 00 00 00 43 10 c0 81
30: 00 00 00 00 44 00 00 00 00 00 00 00 ff 00 00 00

According to your patch, you named this device
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_C51_MEMC0 0x02F0
So is the lspci database not correct or your naming? lspci lists some memcontrollers for me, but they don't seem to have MSI specific items.

00:10.1 Audio device: nVidia Corporation MCP51 High Definition Audio (rev a2)
Subsystem: ASUSTeK Computer Inc. Unknown device 81cb
Flags: bus master, 66MHz, fast devsel, latency 0, IRQ 319 Memory at fe024000 (32-bit, non-prefetchable) [size=16K]
Capabilities: [44] Power Management version 2
Capabilities: [50] Message Signalled Interrupts: Mask+ 64bit+
Queue=0/0 Enable+
Capabilities: [6c] HyperTransport: MSI Mapping Enable+ Fixed+
00: de 10 6c 02 06 04 b0 00 a2 00 03 04 00 00 80 00
10: 00 40 02 fe 00 00 00 00 00 00 00 00 00 00 00 00
20: 00 00 00 00 00 00 00 00 00 00 00 00 43 10 cb 81
30: 00 00 00 00 44 00 00 00 00 00 00 00 05 02 02 05

Yay:
cat /proc/interrupts
319: 26 9357 PCI-MSI-edge HDA Intel

So, now sata_nv and nvidia binary graphics driver could activate MSI, as well. :-)

One thing I noticed in the patch:
+ if (((bridge_dev = pci_find_slot(0, 0)) != NULL) &&

You rather want to use pci_get_bus_and_slot instead, as pci_find_slot is deprecated.

Thanks!

Signature · 3 lines
(°=                 =°)
//\ Prakash Punnoor /\\
V_/                 \_V
#2 lore raw
Quoted 1 line · On Sun, 25 Nov 2007 11:21:45 +0100 Prakash Punnoor <[email protected]> wrote:

Yay:

Sounds good.

Quoted 5 lines

One thing I noticed in the patch:

drivers/pci/msi.c renamed from drivers/pci/msi.c.orig +1 -0
+		if (((bridge_dev = pci_find_slot(0, 0)) != NULL) &&

You rather want to use pci_get_bus_and_slot instead, as pci_find_slot is deprecated.

yup. I'll queue this to get it a bit of testing but won't send it to Greg, in the expectation that an updated version will be prepared.

Please do feed the diff through scripts/checkpatch.pl too - there are a large number of trivial coding-style glitches in there.

Also, your [patch 1/2] will break the build unless your [patch 2/2] is applied. This will break git bisection so please avoid it. It is appropriate to fold both these patches into a single one.

And please avoid sending multiple patches under the same title, as these two were.

Thanks.

#3 lore raw
In reply to peerchen
Quoted 3 lines · On Sun, 25 Nov 2007 11:21:48 +0800 "peerchen" <[email protected]> wrote:

According to the HyperTransport spec, 'En' indicate if the MSI Mapping is active. So it should be set when enable the MSI.

Cool, I had a patch that added a quirk to enable MSI Mapping on Broadcom's
HT1000 so that the builtin BCM5706S Gigabit Ethernet would use MSI. This one looks even cleaner.

As a side note, the bnx2 driver tries to use MSI by default (in the kernel I'm
using - 2.6.21.5-rt10) and falls back to INTx if no interrupt is received within a reasonable amount of time. If the HT MSI Mapping is not enabled beforehand, the first MSI message (before reverting to INTx) is not trapped by the bridge and lands in memory thus corrupting what is there at that time. In my case it's the mapcount of a struct page in the freelist which is overwritten with the MSI message.

Sébastien.

#4 lore raw
In reply to peerchen
Quoted 11 lines · On Sun, 25 Nov 2007 11:21:48 +0800 "peerchen" <[email protected]> wrote:

According to the HyperTransport spec, 'En' indicate if the MSI Mapping is active. So it should be set when enable the MSI.

The patch base on kernel 2.6.24-rc3

---

drivers/pci/msi.c line 23

Quoted 4 lines
drivers/pci/msi.c renamed from drivers/pci/msi.c.orig +1 -0
@@ -20,6 +20,8 @@
 #include <asm/errno.h>
 #include <asm/io.h>
 
+#include <asm/k8.h>

This inclusion makes msi.c x86-specific whereas it previously was not. I assume that this change breaks arm, ia64, sparc64 and powerpc.

I'll queue the below ugliness to avoid this, but it would be nicer to move these functions out to an arch-specific file and then require that all MSI-using architectures implement them.

drivers/pci/msi.c renamed from drivers/pci/msi.c~msi-set-en-bit-of-msi-mapping-capability-on-ht-platform-fix +9 -0
diff -puN drivers/pci/msi.c~msi-set-en-bit-of-msi-mapping-capability-on-ht-platform-fix drivers/pci/msi.c
@@ -20,7 +20,9 @@
 #include <asm/errno.h>
 #include <asm/io.h>
 
+#ifdef CONFIG_X86
 #include <asm/k8.h>
+#endif
 
 #include "pci.h"
 #include "msi.h"
@@ -292,6 +294,7 @@ void pci_restore_msi_state(struct pci_de
 }
 #endif	/* CONFIG_PM */
 
+#ifdef CONFIG_X86
 /*
  * pci_enable_msi_ht_cap - Set the HT MSI mapping capability En bit of
  * a device.
@@ -384,6 +387,12 @@ static int pci_check_msi_ht_cap(struct p
 	}
 	return 0;
 }
+#else
+static inline int pci_check_msi_ht_cap(struct pci_dev *dev)
+{
+	return 0;
+}
+#endif
 
 /**
  * msi_capability_init - configure device's MSI capability structure

_