@@ -28,6 +28,7 @@
#include <linux/bitops.h>
#include <linux/edac.h>
#include <linux/pci_ids.h>
+#include <linux/interrupt.h>
#include "edac_core.h"
#include "edac_module.h"
@@ -36,6 +37,11 @@
#define AMD8131_EDAC_REVISION " Ver: 1.0.0 " __DATE__
#define AMD8131_EDAC_MOD_STR "amd8131_edac"
+static int amd8131_op_state = EDAC_OPSTATE_POLL;
+module_param(amd8131_op_state, int, 0444);
+MODULE_PARM_DESC(amd8131_op_state, "EDAC Error Reporting state: 0=Poll, 1=NMI");
+static int amd8131_nmi_irq;
+
/* Wrapper functions for accessing PCI configuration space */
static void edac_pci_read_dword(struct pci_dev *dev, int reg, u32 *val32)
{
@@ -139,6 +145,17 @@ static void amd8131_pcix_init(struct amd8131_dev_info *dev_info)
edac_pci_read_dword(dev, REG_LNK_CTRL_B, &val32);
val32 |= LNK_CTRL_CRCFEN;
edac_pci_write_dword(dev, REG_LNK_CTRL_B, val32);
+
+ /* enable HT NMI messages generation on errors */
+ if (amd8131_op_state == EDAC_OPSTATE_NMI) {
+ edac_pci_read_dword(dev, REG_MISC_I, &val32);
+ val32 &= ~MISC_I_NIOAMODE;
+ edac_pci_write_dword(dev, REG_MISC_I, val32);
+
+ edac_pci_read_dword(dev, REG_MISC_II, &val32);
+ val32 |= MISC_II_NMIEN;
+ edac_pci_write_dword(dev, REG_MISC_II, val32);
+ }
}
static void amd8131_pcix_exit(struct amd8131_dev_info *dev_info)
@@ -165,6 +182,17 @@ static void amd8131_pcix_exit(struct amd8131_dev_info *dev_info)
edac_pci_read_dword(dev, REG_LNK_CTRL_B, &val32);
val32 &= ~LNK_CTRL_CRCFEN;
edac_pci_write_dword(dev, REG_LNK_CTRL_B, val32);
+
+ /* Disable HT NMI messages on errors*/
+ if (amd8131_op_state == EDAC_OPSTATE_NMI) {
+ edac_pci_read_dword(dev, REG_MISC_II, &val32);
+ val32 &= ~MISC_II_NMIEN;
+ edac_pci_write_dword(dev, REG_MISC_II, val32);
+
+ edac_pci_read_dword(dev, REG_MISC_I, &val32);
+ val32 |= MISC_I_NIOAMODE;
+ edac_pci_write_dword(dev, REG_MISC_I, val32);
+ }
}
static void amd8131_pcix_check(struct edac_pci_ctl_info *edac_dev)
@@ -233,12 +261,33 @@ static void amd8131_pcix_check(struct edac_pci_ctl_info *edac_dev)
}
}
+static irqreturn_t amd8131_pcix_isr(int irq, void *dev_id)
+{
+ struct edac_pci_ctl_info *edac_pci = dev_id;
+ struct amd8131_dev_info *dev_info = edac_pci->pvt_info;
+ struct pci_dev *dev = dev_info->dev;
+ u32 val32;
+
+ /*
+ * Only a handful of errors in PCI-X Bridge Memory Base-Limit
+ * Register could trigger NMI interrupt request message.
+ */
+ edac_pci_read_dword(dev, REG_MEM_LIM, &val32);
+ if (!(val32 & MEM_LIMIT_NMI_MASK))
+ return IRQ_NONE;
+
+ amd8131_pcix_check(edac_pci);
+
+ return IRQ_HANDLED;
+}
+
static struct amd8131_info amd8131_chipset = {
.err_dev = PCI_DEVICE_ID_AMD_8131_APIC,
.devices = amd8131_devices,
.init = amd8131_pcix_init,
.exit = amd8131_pcix_exit,
.check = amd8131_pcix_check,
+ .isr = amd8131_pcix_isr,
};
/*
@@ -249,6 +298,7 @@ static struct amd8131_info amd8131_chipset = {
static int amd8131_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
struct amd8131_dev_info *dev_info;
+ int ret = -ENODEV;
for (dev_info = amd8131_chipset.devices; dev_info->inst != NO_BRIDGE;
dev_info++)
@@ -256,7 +306,7 @@ static int amd8131_probe(struct pci_dev *dev, const struct pci_device_id *id)
break;
if (dev_info->inst == NO_BRIDGE) /* should never happen */
- return -ENODEV;
+ goto out;
/*
* We can't call pci_get_device() as we are used to do because
@@ -265,12 +315,11 @@ static int amd8131_probe(struct pci_dev *dev, const struct pci_device_id *id)
dev_info->dev = pci_dev_get(dev);
if (pci_enable_device(dev_info->dev)) {
- pci_dev_put(dev_info->dev);
printk(KERN_ERR "failed to enable:"
"vendor %x, device %x, devfn %x, name %s\n",
PCI_VENDOR_ID_AMD, amd8131_chipset.err_dev,
dev_info->devfn, dev_info->ctl_name);
- return -ENODEV;
+ goto err1;
}
/*
@@ -280,8 +329,10 @@ static int amd8131_probe(struct pci_dev *dev, const struct pci_device_id *id)
*/
dev_info->edac_idx = edac_pci_alloc_index();
dev_info->edac_dev = edac_pci_alloc_ctl_info(0, dev_info->ctl_name);
- if (!dev_info->edac_dev)
- return -ENOMEM;
+ if (!dev_info->edac_dev) {
+ ret = -ENOMEM;
+ goto err1;
+ }
dev_info->edac_dev->pvt_info = dev_info;
dev_info->edac_dev->dev = &dev_info->dev->dev;
@@ -289,7 +340,7 @@ static int amd8131_probe(struct pci_dev *dev, const struct pci_device_id *id)
dev_info->edac_dev->ctl_name = dev_info->ctl_name;
dev_info->edac_dev->dev_name = dev_name(&dev_info->dev->dev);
- if (edac_op_state == EDAC_OPSTATE_POLL)
+ if (amd8131_op_state == EDAC_OPSTATE_POLL)
dev_info->edac_dev->edac_check = amd8131_chipset.check;
if (amd8131_chipset.init)
@@ -298,8 +349,8 @@ static int amd8131_probe(struct pci_dev *dev, const struct pci_device_id *id)
if (edac_pci_add_device(dev_info->edac_dev, dev_info->edac_idx) > 0) {
printk(KERN_ERR "failed edac_pci_add_device() for %s\n",
dev_info->ctl_name);
- edac_pci_free_ctl_info(dev_info->edac_dev);
- return -ENODEV;
+ ret = -ENOMEM;
+ goto err2;
}
printk(KERN_INFO "added one device on AMD8131 "
@@ -307,7 +358,18 @@ static int amd8131_probe(struct pci_dev *dev, const struct pci_device_id *id)
PCI_VENDOR_ID_AMD, amd8131_chipset.err_dev,
dev_info->devfn, dev_info->ctl_name);
- return 0;
+ ret = 0;
+ goto out;
+
+err2:
+ if (amd8131_chipset.exit)
+ amd8131_chipset.exit(dev_info);
+
+ edac_pci_free_ctl_info(dev_info->edac_dev);
+err1:
+ pci_dev_put(dev_info->dev);
+out:
+ return ret;
}
static void amd8131_remove(struct pci_dev *dev)
@@ -322,14 +384,14 @@ static void amd8131_remove(struct pci_dev *dev)
if (dev_info->inst == NO_BRIDGE) /* should never happen */
return;
+ if (amd8131_chipset.exit)
+ amd8131_chipset.exit(dev_info);
+
if (dev_info->edac_dev) {
edac_pci_del_device(dev_info->edac_dev->dev);
edac_pci_free_ctl_info(dev_info->edac_dev);
}
- if (amd8131_chipset.exit)
- amd8131_chipset.exit(dev_info);
-
pci_dev_put(dev_info->dev);
}
@@ -342,9 +404,7 @@ static const struct pci_device_id amd8131_edac_pci_tbl[] = {
.class_mask = 0,
.driver_data = 0,
},
- {
- 0,
- } /* table is NULL-terminated */
+ {0} /* table is NULL-terminated */
};
MODULE_DEVICE_TABLE(pci, amd8131_edac_pci_tbl);
@@ -355,20 +415,93 @@ static struct pci_driver amd8131_edac_driver = {
.id_table = amd8131_edac_pci_tbl,
};
+/*
+ * AMD8131 NMI handler - check PCI-X Bridges to claim any
+ * possible NMI instance.
+ * Southbridge NMI Request messages posted through Hypertransport
+ * Channel will be transferred to a MPIC interrupt instance.
+ *
+ * NOTE: According to AMD8131 data sheet 4.5.7 section,
+ * only a partial of error detections could generate NMI
+ * Upstream Hypertransport Interrupt request messages, so
+ * use NMI mode at sacrifice that not all error detections
+ * could be made use of.
+ */
+static irqreturn_t amd8131_nmi_handler(int irq, void *dev_id)
+{
+ struct amd8131_info *info = dev_id;
+ struct amd8131_dev_info *dev_info;
+ irqreturn_t ret = IRQ_NONE;
+
+ if (!info->isr)
+ return IRQ_NONE;
+
+ for (dev_info = info->devices; dev_info->inst != NO_BRIDGE; dev_info++)
+ ret |= info->isr(irq, dev_info->edac_dev);
+
+ return ret;
+}
+
+static void __init amd8131_nmi_handler_setup(void)
+{
+ int ret;
+
+ if (amd8131_op_state != EDAC_OPSTATE_NMI)
+ return;
+
+ amd8131_nmi_irq = NO_IRQ;
+
+#ifdef CONFIG_MPIC
+ amd8131_nmi_irq = edac_get_mpic_irq(MPIC_HWIRQ_HT_NMI);
+#endif
+
+ if (amd8131_nmi_irq == NO_IRQ) {
+ printk(KERN_ERR "%s: failed to get virq "
+ "for AMD8131 NMI requests\n", __func__);
+ return;
+ }
+
+ ret = request_irq(amd8131_nmi_irq, amd8131_nmi_handler,
+ IRQF_SHARED, "[EDAC] AMD8131", &amd8131_chipset);
+ if (ret < 0) {
+ printk(KERN_INFO "%s: failed to request irq %d for "
+ "AMD8131 NMI requests\n", __func__, amd8131_nmi_irq);
+ return;
+ }
+
+ debugf0("%s: Successfully requested irq %d for AMD8131 NMI requests\n",
+ __func__, amd8131_nmi_irq);
+}
+
+static void __exit amd8131_nmi_handler_exit(void)
+{
+ if (amd8131_op_state != EDAC_OPSTATE_NMI)
+ return;
+
+ if (amd8131_nmi_irq != NO_IRQ)
+ free_irq(amd8131_nmi_irq, &amd8131_chipset);
+}
+
static int __init amd8131_edac_init(void)
{
+ int ret;
+
printk(KERN_INFO "AMD8131 EDAC driver " AMD8131_EDAC_REVISION "\n");
printk(KERN_INFO "\t(c) 2008 Wind River Systems, Inc.\n");
- /* Only POLL mode supported so far */
- edac_op_state = EDAC_OPSTATE_POLL;
+ ret = pci_register_driver(&amd8131_edac_driver);
- return pci_register_driver(&amd8131_edac_driver);
+ if (ret == 0)
+ amd8131_nmi_handler_setup();
+
+ return ret;
}
static void __exit amd8131_edac_exit(void)
{
pci_unregister_driver(&amd8131_edac_driver);
+
+ amd8131_nmi_handler_exit();
}
module_init(amd8131_edac_init);
@@ -61,7 +61,8 @@ enum mem_limit_bits {
MEM_LIMIT_STA = BIT(27),
MEM_LIMIT_MDPE = BIT(24),
MEM_LIMIT_MASK = MEM_LIMIT_DPE|MEM_LIMIT_RSE|MEM_LIMIT_RMA|
- MEM_LIMIT_RTA|MEM_LIMIT_STA|MEM_LIMIT_MDPE
+ MEM_LIMIT_RTA|MEM_LIMIT_STA|MEM_LIMIT_MDPE,
+ MEM_LIMIT_NMI_MASK = MEM_LIMIT_DPE | MEM_LIMIT_RSE
};
/************************************************************
@@ -80,6 +81,22 @@ enum lnk_ctrl_bits {
LNK_CTRL_CRCFEN = BIT(1)
};
+/************************************************************
+ * PCI-X Miscellaneous Register, Dev[B,A]:0x40
+ ************************************************************/
+#define REG_MISC_I 0x40
+enum misc_i_bits {
+ MISC_I_NIOAMODE = BIT(0),
+};
+
+/************************************************************
+ * PCI-X Miscellaneous II Register, Dev[B,A]:0x44
+ ************************************************************/
+#define REG_MISC_II 0x44
+enum misc_ii_bits {
+ MISC_II_NMIEN = BIT(0),
+};
+
enum pcix_bridge_inst {
NORTH_A = 0,
NORTH_B = 1,
@@ -113,6 +130,7 @@ struct amd8131_info {
void (*init)(struct amd8131_dev_info *dev_info);
void (*exit)(struct amd8131_dev_info *dev_info);
void (*check)(struct edac_pci_ctl_info *edac_dev);
+ irqreturn_t (*isr)(int irq, void *dev_id);
};
#endif /* _AMD8131_EDAC_H_ */
Support EDAC INT mode for AMD8131 EDAC driver, which may post upstream NMI interrupt request messages that will latch MPIC INT0 pin. Following aspects for this patch have been tested: 1, module initialization and deletion for NMI mode; 2, creation and deletion for the mapping between hwirq==0 to a virq; Note, due to the difficulty and complexity to generate a real hardware EDAC Errors, below aspects have not been tested yet: 1, code that controls the generation of the NMI Request Message; 2, the mapping from the NMI Request Messages to MPIC INT0 pin; 3, if EDAC isr methods could handle errors correctly. Signed-off-by: Harry Ciao <qingtao.cao@windriver.com> --- drivers/edac/amd8131_edac.c | 169 ++++++++++++++++++++++++++++++++++++++----- drivers/edac/amd8131_edac.h | 20 +++++- 2 files changed, 170 insertions(+), 19 deletions(-)