summaryrefslogtreecommitdiff
path: root/drivers/edac
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/edac')
-rw-r--r--drivers/edac/Kconfig23
-rw-r--r--drivers/edac/Makefile3
-rw-r--r--drivers/edac/cell_edac.c258
-rw-r--r--drivers/edac/edac_core.h2
-rw-r--r--drivers/edac/edac_device.c8
-rw-r--r--drivers/edac/edac_mc_sysfs.c3
-rw-r--r--drivers/edac/edac_pci.c2
-rw-r--r--drivers/edac/edac_pci_sysfs.c12
-rw-r--r--drivers/edac/i3000_edac.c267
-rw-r--r--drivers/edac/mpc85xx_edac.c1043
-rw-r--r--drivers/edac/mpc85xx_edac.h162
-rw-r--r--drivers/edac/mv64x60_edac.c855
-rw-r--r--drivers/edac/mv64x60_edac.h114
13 files changed, 2636 insertions, 116 deletions
diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig
index 98b6b4fb4257..2b382990fe58 100644
--- a/drivers/edac/Kconfig
+++ b/drivers/edac/Kconfig
@@ -97,7 +97,7 @@ config EDAC_I82975X
config EDAC_I3000
tristate "Intel 3000/3010"
- depends on EDAC_MM_EDAC && PCI && X86_32
+ depends on EDAC_MM_EDAC && PCI && X86
help
Support for error detection and correction on the Intel
3000 and 3010 server chipsets.
@@ -123,6 +123,20 @@ config EDAC_I5000
Support for error detection and correction the Intel
Greekcreek/Blackford chipsets.
+config EDAC_MPC85XX
+ tristate "Freescale MPC85xx"
+ depends on EDAC_MM_EDAC && FSL_SOC && MPC85xx
+ help
+ Support for error detection and correction on the Freescale
+ MPC8560, MPC8540, MPC8548
+
+config EDAC_MV64X60
+ tristate "Marvell MV64x60"
+ depends on EDAC_MM_EDAC && MV64X60
+ help
+ Support for error detection and correction on the Marvell
+ MV64360 and MV64460 chipsets.
+
config EDAC_PASEMI
tristate "PA Semi PWRficient"
depends on EDAC_MM_EDAC && PCI
@@ -131,5 +145,12 @@ config EDAC_PASEMI
Support for error detection and correction on PA Semi
PWRficient.
+config EDAC_CELL
+ tristate "Cell Broadband Engine memory controller"
+ depends on EDAC_MM_EDAC && PPC_CELL_NATIVE
+ help
+ Support for error detection and correction on the
+ Cell Broadband Engine internal memory controller
+ on platform without a hypervisor
endif # EDAC
diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile
index 02c09f0ff157..83807731d4a9 100644
--- a/drivers/edac/Makefile
+++ b/drivers/edac/Makefile
@@ -28,4 +28,7 @@ obj-$(CONFIG_EDAC_I3000) += i3000_edac.o
obj-$(CONFIG_EDAC_I82860) += i82860_edac.o
obj-$(CONFIG_EDAC_R82600) += r82600_edac.o
obj-$(CONFIG_EDAC_PASEMI) += pasemi_edac.o
+obj-$(CONFIG_EDAC_MPC85XX) += mpc85xx_edac.o
+obj-$(CONFIG_EDAC_MV64X60) += mv64x60_edac.o
+obj-$(CONFIG_EDAC_CELL) += cell_edac.o
diff --git a/drivers/edac/cell_edac.c b/drivers/edac/cell_edac.c
new file mode 100644
index 000000000000..b54112ffd282
--- /dev/null
+++ b/drivers/edac/cell_edac.c
@@ -0,0 +1,258 @@
+/*
+ * Cell MIC driver for ECC counting
+ *
+ * Copyright 2007 Benjamin Herrenschmidt, IBM Corp.
+ * <benh@kernel.crashing.org>
+ *
+ * This file may be distributed under the terms of the
+ * GNU General Public License.
+ */
+#undef DEBUG
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/stop_machine.h>
+#include <linux/io.h>
+#include <asm/machdep.h>
+#include <asm/cell-regs.h>
+
+#include "edac_core.h"
+
+struct cell_edac_priv
+{
+ struct cbe_mic_tm_regs __iomem *regs;
+ int node;
+ int chanmask;
+#ifdef DEBUG
+ u64 prev_fir;
+#endif
+};
+
+static void cell_edac_count_ce(struct mem_ctl_info *mci, int chan, u64 ar)
+{
+ struct cell_edac_priv *priv = mci->pvt_info;
+ struct csrow_info *csrow = &mci->csrows[0];
+ unsigned long address, pfn, offset;
+
+ dev_dbg(mci->dev, "ECC CE err on node %d, channel %d, ar = 0x%016lx\n",
+ priv->node, chan, ar);
+
+ /* Address decoding is likely a bit bogus, to dbl check */
+ address = (ar & 0xffffffffe0000000ul) >> 29;
+ if (priv->chanmask == 0x3)
+ address = (address << 1) | chan;
+ pfn = address >> PAGE_SHIFT;
+ offset = address & ~PAGE_MASK;
+
+ /* TODO: Decoding of the error addresss */
+ edac_mc_handle_ce(mci, csrow->first_page + pfn, offset,
+ 0, 0, chan, "");
+}
+
+static void cell_edac_count_ue(struct mem_ctl_info *mci, int chan, u64 ar)
+{
+ struct cell_edac_priv *priv = mci->pvt_info;
+ struct csrow_info *csrow = &mci->csrows[0];
+ unsigned long address, pfn, offset;
+
+ dev_dbg(mci->dev, "ECC UE err on node %d, channel %d, ar = 0x%016lx\n",
+ priv->node, chan, ar);
+
+ /* Address decoding is likely a bit bogus, to dbl check */
+ address = (ar & 0xffffffffe0000000ul) >> 29;
+ if (priv->chanmask == 0x3)
+ address = (address << 1) | chan;
+ pfn = address >> PAGE_SHIFT;
+ offset = address & ~PAGE_MASK;
+
+ /* TODO: Decoding of the error addresss */
+ edac_mc_handle_ue(mci, csrow->first_page + pfn, offset, 0, "");
+}
+
+static void cell_edac_check(struct mem_ctl_info *mci)
+{
+ struct cell_edac_priv *priv = mci->pvt_info;
+ u64 fir, addreg, clear = 0;
+
+ fir = in_be64(&priv->regs->mic_fir);
+#ifdef DEBUG
+ if (fir != priv->prev_fir) {
+ dev_dbg(mci->dev, "fir change : 0x%016lx\n", fir);
+ priv->prev_fir = fir;
+ }
+#endif
+ if ((priv->chanmask & 0x1) && (fir & CBE_MIC_FIR_ECC_SINGLE_0_ERR)) {
+ addreg = in_be64(&priv->regs->mic_df_ecc_address_0);
+ clear |= CBE_MIC_FIR_ECC_SINGLE_0_RESET;
+ cell_edac_count_ce(mci, 0, addreg);
+ }
+ if ((priv->chanmask & 0x2) && (fir & CBE_MIC_FIR_ECC_SINGLE_1_ERR)) {
+ addreg = in_be64(&priv->regs->mic_df_ecc_address_1);
+ clear |= CBE_MIC_FIR_ECC_SINGLE_1_RESET;
+ cell_edac_count_ce(mci, 1, addreg);
+ }
+ if ((priv->chanmask & 0x1) && (fir & CBE_MIC_FIR_ECC_MULTI_0_ERR)) {
+ addreg = in_be64(&priv->regs->mic_df_ecc_address_0);
+ clear |= CBE_MIC_FIR_ECC_MULTI_0_RESET;
+ cell_edac_count_ue(mci, 0, addreg);
+ }
+ if ((priv->chanmask & 0x2) && (fir & CBE_MIC_FIR_ECC_MULTI_1_ERR)) {
+ addreg = in_be64(&priv->regs->mic_df_ecc_address_1);
+ clear |= CBE_MIC_FIR_ECC_MULTI_1_RESET;
+ cell_edac_count_ue(mci, 1, addreg);
+ }
+
+ /* The procedure for clearing FIR bits is a bit ... weird */
+ if (clear) {
+ fir &= ~(CBE_MIC_FIR_ECC_ERR_MASK | CBE_MIC_FIR_ECC_SET_MASK);
+ fir |= CBE_MIC_FIR_ECC_RESET_MASK;
+ fir &= ~clear;
+ out_be64(&priv->regs->mic_fir, fir);
+ (void)in_be64(&priv->regs->mic_fir);
+
+ mb(); /* sync up */
+#ifdef DEBUG
+ fir = in_be64(&priv->regs->mic_fir);
+ dev_dbg(mci->dev, "fir clear : 0x%016lx\n", fir);
+#endif
+ }
+}
+
+static void __devinit cell_edac_init_csrows(struct mem_ctl_info *mci)
+{
+ struct csrow_info *csrow = &mci->csrows[0];
+ struct cell_edac_priv *priv = mci->pvt_info;
+ struct device_node *np;
+
+ for (np = NULL;
+ (np = of_find_node_by_name(np, "memory")) != NULL;) {
+ struct resource r;
+
+ /* We "know" that the Cell firmware only creates one entry
+ * in the "memory" nodes. If that changes, this code will
+ * need to be adapted.
+ */
+ if (of_address_to_resource(np, 0, &r))
+ continue;
+ if (of_node_to_nid(np) != priv->node)
+ continue;
+ csrow->first_page = r.start >> PAGE_SHIFT;
+ csrow->nr_pages = (r.end - r.start + 1) >> PAGE_SHIFT;
+ csrow->last_page = csrow->first_page + csrow->nr_pages - 1;
+ csrow->mtype = MEM_XDR;
+ csrow->edac_mode = EDAC_FLAG_EC | EDAC_FLAG_SECDED;
+ dev_dbg(mci->dev,
+ "Initialized on node %d, chanmask=0x%x,"
+ " first_page=0x%lx, nr_pages=0x%x\n",
+ priv->node, priv->chanmask,
+ csrow->first_page, csrow->nr_pages);
+ break;
+ }
+}
+
+static int __devinit cell_edac_probe(struct platform_device *pdev)
+{
+ struct cbe_mic_tm_regs __iomem *regs;
+ struct mem_ctl_info *mci;
+ struct cell_edac_priv *priv;
+ u64 reg;
+ int rc, chanmask;
+
+ regs = cbe_get_cpu_mic_tm_regs(cbe_node_to_cpu(pdev->id));
+ if (regs == NULL)
+ return -ENODEV;
+
+ /* Get channel population */
+ reg = in_be64(&regs->mic_mnt_cfg);
+ dev_dbg(&pdev->dev, "MIC_MNT_CFG = 0x%016lx\n", reg);
+ chanmask = 0;
+ if (reg & CBE_MIC_MNT_CFG_CHAN_0_POP)
+ chanmask |= 0x1;
+ if (reg & CBE_MIC_MNT_CFG_CHAN_1_POP)
+ chanmask |= 0x2;
+ if (chanmask == 0) {
+ dev_warn(&pdev->dev,
+ "Yuck ! No channel populated ? Aborting !\n");
+ return -ENODEV;
+ }
+ dev_dbg(&pdev->dev, "Initial FIR = 0x%016lx\n",
+ in_be64(&regs->mic_fir));
+
+ /* Allocate & init EDAC MC data structure */
+ mci = edac_mc_alloc(sizeof(struct cell_edac_priv), 1,
+ chanmask == 3 ? 2 : 1, pdev->id);
+ if (mci == NULL)
+ return -ENOMEM;
+ priv = mci->pvt_info;
+ priv->regs = regs;
+ priv->node = pdev->id;
+ priv->chanmask = chanmask;
+ mci->dev = &pdev->dev;
+ mci->mtype_cap = MEM_FLAG_XDR;
+ mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_EC | EDAC_FLAG_SECDED;
+ mci->edac_cap = EDAC_FLAG_EC | EDAC_FLAG_SECDED;
+ mci->mod_name = "cell_edac";
+ mci->ctl_name = "MIC";
+ mci->dev_name = pdev->dev.bus_id;
+ mci->edac_check = cell_edac_check;
+ cell_edac_init_csrows(mci);
+
+ /* Register with EDAC core */
+ rc = edac_mc_add_mc(mci);
+ if (rc) {
+ dev_err(&pdev->dev, "failed to register with EDAC core\n");
+ edac_mc_free(mci);
+ return rc;
+ }
+
+ return 0;
+}
+
+static int __devexit cell_edac_remove(struct platform_device *pdev)
+{
+ struct mem_ctl_info *mci = edac_mc_del_mc(&pdev->dev);
+ if (mci)
+ edac_mc_free(mci);
+ return 0;
+}
+
+static struct platform_driver cell_edac_driver = {
+ .driver = {
+ .name = "cbe-mic",
+ .owner = THIS_MODULE,
+ },
+ .probe = cell_edac_probe,
+ .remove = cell_edac_remove,
+};
+
+static int __init cell_edac_init(void)
+{
+ /* Sanity check registers data structure */
+ BUILD_BUG_ON(offsetof(struct cbe_mic_tm_regs,
+ mic_df_ecc_address_0) != 0xf8);
+ BUILD_BUG_ON(offsetof(struct cbe_mic_tm_regs,
+ mic_df_ecc_address_1) != 0x1b8);
+ BUILD_BUG_ON(offsetof(struct cbe_mic_tm_regs,
+ mic_df_config) != 0x218);
+ BUILD_BUG_ON(offsetof(struct cbe_mic_tm_regs,
+ mic_fir) != 0x230);
+ BUILD_BUG_ON(offsetof(struct cbe_mic_tm_regs,
+ mic_mnt_cfg) != 0x210);
+ BUILD_BUG_ON(offsetof(struct cbe_mic_tm_regs,
+ mic_exc) != 0x208);
+
+ return platform_driver_register(&cell_edac_driver);
+}
+
+static void __exit cell_edac_exit(void)
+{
+ platform_driver_unregister(&cell_edac_driver);
+}
+
+module_init(cell_edac_init);
+module_exit(cell_edac_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
+MODULE_DESCRIPTION("ECC counting for Cell MIC");
diff --git a/drivers/edac/edac_core.h b/drivers/edac/edac_core.h
index 2d23e304f5ec..a9aa845dbe74 100644
--- a/drivers/edac/edac_core.h
+++ b/drivers/edac/edac_core.h
@@ -136,6 +136,7 @@ enum mem_type {
MEM_DDR2, /* DDR2 RAM */
MEM_FB_DDR2, /* fully buffered DDR2 */
MEM_RDDR2, /* Registered DDR2 RAM */
+ MEM_XDR, /* Rambus XDR */
};
#define MEM_FLAG_EMPTY BIT(MEM_EMPTY)
@@ -152,6 +153,7 @@ enum mem_type {
#define MEM_FLAG_DDR2 BIT(MEM_DDR2)
#define MEM_FLAG_FB_DDR2 BIT(MEM_FB_DDR2)
#define MEM_FLAG_RDDR2 BIT(MEM_RDDR2)
+#define MEM_FLAG_XDR BIT(MEM_XDR)
/* chipset Error Detection and Correction capabilities and mode */
enum edac_type {
diff --git a/drivers/edac/edac_device.c b/drivers/edac/edac_device.c
index f3690a697cf9..b9552bc03dea 100644
--- a/drivers/edac/edac_device.c
+++ b/drivers/edac/edac_device.c
@@ -155,6 +155,10 @@ struct edac_device_ctl_info *edac_device_alloc_ctl_info(
dev_ctl->instances = dev_inst;
dev_ctl->pvt_info = pvt;
+ /* Default logging of CEs and UEs */
+ dev_ctl->log_ce = 1;
+ dev_ctl->log_ue = 1;
+
/* Name of this edac device */
snprintf(dev_ctl->name,sizeof(dev_ctl->name),"%s",edac_device_name);
@@ -436,7 +440,7 @@ static void edac_device_workq_function(struct work_struct *work_req)
*/
if (edac_dev->poll_msec == 1000)
queue_delayed_work(edac_workqueue, &edac_dev->work,
- round_jiffies(edac_dev->delay));
+ round_jiffies_relative(edac_dev->delay));
else
queue_delayed_work(edac_workqueue, &edac_dev->work,
edac_dev->delay);
@@ -468,7 +472,7 @@ void edac_device_workq_setup(struct edac_device_ctl_info *edac_dev,
*/
if (edac_dev->poll_msec == 1000)
queue_delayed_work(edac_workqueue, &edac_dev->work,
- round_jiffies(edac_dev->delay));
+ round_jiffies_relative(edac_dev->delay));
else
queue_delayed_work(edac_workqueue, &edac_dev->work,
edac_dev->delay);
diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c
index 9aac88027fb3..021d18795145 100644
--- a/drivers/edac/edac_mc_sysfs.c
+++ b/drivers/edac/edac_mc_sysfs.c
@@ -73,7 +73,8 @@ static const char *mem_types[] = {
[MEM_RMBS] = "RMBS",
[MEM_DDR2] = "Unbuffered-DDR2",
[MEM_FB_DDR2] = "FullyBuffered-DDR2",
- [MEM_RDDR2] = "Registered-DDR2"
+ [MEM_RDDR2] = "Registered-DDR2",
+ [MEM_XDR] = "XDR"
};
static const char *dev_types[] = {
diff --git a/drivers/edac/edac_pci.c b/drivers/edac/edac_pci.c
index e0b47b74ec45..32be43576a8e 100644
--- a/drivers/edac/edac_pci.c
+++ b/drivers/edac/edac_pci.c
@@ -246,7 +246,7 @@ static void edac_pci_workq_function(struct work_struct *work_req)
/* if we are on a one second period, then use round */
msec = edac_pci_get_poll_msec();
if (msec == 1000)
- delay = round_jiffies(msecs_to_jiffies(msec));
+ delay = round_jiffies_relative(msecs_to_jiffies(msec));
else
delay = msecs_to_jiffies(msec);
diff --git a/drivers/edac/edac_pci_sysfs.c b/drivers/edac/edac_pci_sysfs.c
index 5b075da99145..71c3195d3704 100644
--- a/drivers/edac/edac_pci_sysfs.c
+++ b/drivers/edac/edac_pci_sysfs.c
@@ -558,8 +558,10 @@ static void edac_pci_dev_parity_test(struct pci_dev *dev)
debugf4("PCI STATUS= 0x%04x %s\n", status, dev->dev.bus_id);
- /* check the status reg for errors */
- if (status) {
+ /* check the status reg for errors on boards NOT marked as broken
+ * if broken, we cannot trust any of the status bits
+ */
+ if (status && !dev->broken_parity_status) {
if (status & (PCI_STATUS_SIG_SYSTEM_ERROR)) {
edac_printk(KERN_CRIT, EDAC_PCI,
"Signaled System Error on %s\n",
@@ -593,8 +595,10 @@ static void edac_pci_dev_parity_test(struct pci_dev *dev)
debugf4("PCI SEC_STATUS= 0x%04x %s\n", status, dev->dev.bus_id);
- /* check the secondary status reg for errors */
- if (status) {
+ /* check the secondary status reg for errors,
+ * on NOT broken boards
+ */
+ if (status && !dev->broken_parity_status) {
if (status & (PCI_STATUS_SIG_SYSTEM_ERROR)) {
edac_printk(KERN_CRIT, EDAC_PCI, "Bridge "
"Signaled System Error on %s\n",
diff --git a/drivers/edac/i3000_edac.c b/drivers/edac/i3000_edac.c
index e895f9f887ab..5d4292811c14 100644
--- a/drivers/edac/i3000_edac.c
+++ b/drivers/edac/i3000_edac.c
@@ -14,6 +14,7 @@
#include <linux/pci.h>
#include <linux/pci_ids.h>
#include <linux/slab.h>
+#include <linux/edac.h>
#include "edac_core.h"
#define I3000_REVISION "1.1"
@@ -30,105 +31,139 @@
#define I3000_MCHBAR_MASK 0xffffc000
#define I3000_MMR_WINDOW_SIZE 16384
-#define I3000_EDEAP 0x70 /* Extended DRAM Error Address Pointer (8b)
- *
- * 7:1 reserved
- * 0 bit 32 of address
- */
-#define I3000_DEAP 0x58 /* DRAM Error Address Pointer (32b)
- *
- * 31:7 address
- * 6:1 reserved
- * 0 Error channel 0/1
- */
-#define I3000_DEAP_GRAIN (1 << 7)
-#define I3000_DEAP_PFN(edeap, deap) ((((edeap) & 1) << (32 - PAGE_SHIFT)) | \
- ((deap) >> PAGE_SHIFT))
-#define I3000_DEAP_OFFSET(deap) ((deap) & ~(I3000_DEAP_GRAIN-1) & ~PAGE_MASK)
-#define I3000_DEAP_CHANNEL(deap) ((deap) & 1)
-
-#define I3000_DERRSYN 0x5c /* DRAM Error Syndrome (8b)
- *
- * 7:0 DRAM ECC Syndrome
- */
-
-#define I3000_ERRSTS 0xc8 /* Error Status Register (16b)
- *
- * 15:12 reserved
- * 11 MCH Thermal Sensor Event for SMI/SCI/SERR
- * 10 reserved
- * 9 LOCK to non-DRAM Memory Flag (LCKF)
- * 8 Received Refresh Timeout Flag (RRTOF)
- * 7:2 reserved
- * 1 Multiple-bit DRAM ECC Error Flag (DMERR)
- * 0 Single-bit DRAM ECC Error Flag (DSERR)
- */
+#define I3000_EDEAP 0x70 /* Extended DRAM Error Address Pointer (8b)
+ *
+ * 7:1 reserved
+ * 0 bit 32 of address
+ */
+#define I3000_DEAP 0x58 /* DRAM Error Address Pointer (32b)
+ *
+ * 31:7 address
+ * 6:1 reserved
+ * 0 Error channel 0/1
+ */
+#define I3000_DEAP_GRAIN (1 << 7)
+
+/*
+ * Helper functions to decode the DEAP/EDEAP hardware registers.
+ *
+ * The type promotion here is deliberate; we're deriving an
+ * unsigned long pfn and offset from hardware regs which are u8/u32.
+ */
+
+static inline unsigned long deap_pfn(u8 edeap, u32 deap)
+{
+ deap >>= PAGE_SHIFT;
+ deap |= (edeap & 1) << (32 - PAGE_SHIFT);
+ return deap;
+}
+
+static inline unsigned long deap_offset(u32 deap)
+{
+ return deap & ~(I3000_DEAP_GRAIN - 1) & ~PAGE_MASK;
+}
+
+static inline int deap_channel(u32 deap)
+{
+ return deap & 1;
+}
+
+#define I3000_DERRSYN 0x5c /* DRAM Error Syndrome (8b)
+ *
+ * 7:0 DRAM ECC Syndrome
+ */
+
+#define I3000_ERRSTS 0xc8 /* Error Status Register (16b)
+ *
+ * 15:12 reserved
+ * 11 MCH Thermal Sensor Event
+ * for SMI/SCI/SERR
+ * 10 reserved
+ * 9 LOCK to non-DRAM Memory Flag (LCKF)
+ * 8 Received Refresh Timeout Flag (RRTOF)
+ * 7:2 reserved
+ * 1 Multi-bit DRAM ECC Error Flag (DMERR)
+ * 0 Single-bit DRAM ECC Error Flag (DSERR)
+ */
#define I3000_ERRSTS_BITS 0x0b03 /* bits which indicate errors */
#define I3000_ERRSTS_UE 0x0002
#define I3000_ERRSTS_CE 0x0001
-#define I3000_ERRCMD 0xca /* Error Command (16b)
- *
- * 15:12 reserved
- * 11 SERR on MCH Thermal Sensor Event (TSESERR)
- * 10 reserved
- * 9 SERR on LOCK to non-DRAM Memory (LCKERR)
- * 8 SERR on DRAM Refresh Timeout (DRTOERR)
- * 7:2 reserved
- * 1 SERR Multiple-Bit DRAM ECC Error (DMERR)
- * 0 SERR on Single-Bit ECC Error (DSERR)
- */
+#define I3000_ERRCMD 0xca /* Error Command (16b)
+ *
+ * 15:12 reserved
+ * 11 SERR on MCH Thermal Sensor Event
+ * (TSESERR)
+ * 10 reserved
+ * 9 SERR on LOCK to non-DRAM Memory
+ * (LCKERR)
+ * 8 SERR on DRAM Refresh Timeout
+ * (DRTOERR)
+ * 7:2 reserved
+ * 1 SERR Multi-Bit DRAM ECC Error
+ * (DMERR)
+ * 0 SERR on Single-Bit ECC Error
+ * (DSERR)
+ */
/* Intel MMIO register space - device 0 function 0 - MMR space */
#define I3000_DRB_SHIFT 25 /* 32MiB grain */
-#define I3000_C0DRB 0x100 /* Channel 0 DRAM Rank Boundary (8b x 4)
- *
- * 7:0 Channel 0 DRAM Rank Boundary Address
- */
-#define I3000_C1DRB 0x180 /* Channel 1 DRAM Rank Boundary (8b x 4)
- *
- * 7:0 Channel 1 DRAM Rank Boundary Address
- */
-
-#define I3000_C0DRA 0x108 /* Channel 0 DRAM Rank Attribute (8b x 2)
- *
- * 7 reserved
- * 6:4 DRAM odd Rank Attribute
- * 3 reserved
- * 2:0 DRAM even Rank Attribute
- *
- * Each attribute defines the page
- * size of the corresponding rank:
- * 000: unpopulated
- * 001: reserved
- * 010: 4 KB
- * 011: 8 KB
- * 100: 16 KB
- * Others: reserved
- */
-#define I3000_C1DRA 0x188 /* Channel 1 DRAM Rank Attribute (8b x 2) */
-#define ODD_RANK_ATTRIB(dra) (((dra) & 0x70) >> 4)
-#define EVEN_RANK_ATTRIB(dra) ((dra) & 0x07)
-
-#define I3000_C0DRC0 0x120 /* DRAM Controller Mode 0 (32b)
- *
- * 31:30 reserved
- * 29 Initialization Complete (IC)
- * 28:11 reserved
- * 10:8 Refresh Mode Select (RMS)
- * 7 reserved
- * 6:4 Mode Select (SMS)
- * 3:2 reserved
- * 1:0 DRAM Type (DT)
- */
-
-#define I3000_C0DRC1 0x124 /* DRAM Controller Mode 1 (32b)
- *
- * 31 Enhanced Addressing Enable (ENHADE)
- * 30:0 reserved
- */
+#define I3000_C0DRB 0x100 /* Channel 0 DRAM Rank Boundary (8b x 4)
+ *
+ * 7:0 Channel 0 DRAM Rank Boundary Address
+ */
+#define I3000_C1DRB 0x180 /* Channel 1 DRAM Rank Boundary (8b x 4)
+ *
+ * 7:0 Channel 1 DRAM Rank Boundary Address
+ */
+
+#define I3000_C0DRA 0x108 /* Channel 0 DRAM Rank Attribute (8b x 2)
+ *
+ * 7 reserved
+ * 6:4 DRAM odd Rank Attribute
+ * 3 reserved
+ * 2:0 DRAM even Rank Attribute
+ *
+ * Each attribute defines the page
+ * size of the corresponding rank:
+ * 000: unpopulated
+ * 001: reserved
+ * 010: 4 KB
+ * 011: 8 KB
+ * 100: 16 KB
+ * Others: reserved
+ */
+#define I3000_C1DRA 0x188 /* Channel 1 DRAM Rank Attribute (8b x 2) */
+
+static inline unsigned char odd_rank_attrib(unsigned char dra)
+{
+ return (dra & 0x70) >> 4;
+}
+
+static inline unsigned char even_rank_attrib(unsigned char dra)
+{
+ return dra & 0x07;
+}
+
+#define I3000_C0DRC0 0x120 /* DRAM Controller Mode 0 (32b)
+ *
+ * 31:30 reserved
+ * 29 Initialization Complete (IC)
+ * 28:11 reserved
+ * 10:8 Refresh Mode Select (RMS)
+ * 7 reserved
+ * 6:4 Mode Select (SMS)
+ * 3:2 reserved
+ * 1:0 DRAM Type (DT)
+ */
+
+#define I3000_C0DRC1 0x124 /* DRAM Controller Mode 1 (32b)
+ *
+ * 31 Enhanced Addressing Enable (ENHADE)
+ * 30:0 reserved
+ */
enum i3000p_chips {
I3000 = 0,
@@ -187,7 +222,8 @@ static void i3000_get_error_info(struct mem_ctl_info *mci,
pci_read_config_byte(pdev, I3000_DERRSYN, &info->derrsyn);
}
- /* Clear any error bits.
+ /*
+ * Clear any error bits.
* (Yes, we really clear bits by writing 1 to them.)
*/
pci_write_bits16(pdev, I3000_ERRSTS, I3000_ERRSTS_BITS,
@@ -198,8 +234,8 @@ static int i3000_process_error_info(struct mem_ctl_info *mci,
struct i3000_error_info *info,
int handle_errors)
{
- int row, multi_chan;
- int pfn, offset, channel;
+ int row, multi_chan, channel;
+ unsigned long pfn, offset;
multi_chan = mci->csrows[0].nr_channels - 1;
@@ -214,9 +250,9 @@ static int i3000_process_error_info(struct mem_ctl_info *mci,
info->errsts = info->errsts2;
}
- pfn = I3000_DEAP_PFN(info->edeap, info->deap);
- offset = I3000_DEAP_OFFSET(info->deap);
- channel = I3000_DEAP_CHANNEL(info->deap);
+ pfn = deap_pfn(info->edeap, info->deap);
+ offset = deap_offset(info->deap);
+ channel = deap_channel(info->deap);
row = edac_mc_find_csrow_by_page(mci, pfn);
@@ -245,16 +281,18 @@ static int i3000_is_interleaved(const unsigned char *c0dra,
{
int i;
- /* If the channels aren't populated identically then
+ /*
+ * If the channels aren't populated identically then
* we're not interleaved.
*/
for (i = 0; i < I3000_RANKS_PER_CHANNEL / 2; i++)
- if (ODD_RANK_ATTRIB(c0dra[i]) != ODD_RANK_ATTRIB(c1dra[i]) ||
- EVEN_RANK_ATTRIB(c0dra[i]) !=
- EVEN_RANK_ATTRIB(c1dra[i]))
+ if (odd_rank_attrib(c0dra[i]) != odd_rank_attrib(c1dra[i]) ||
+ even_rank_attrib(c0dra[i]) !=
+ even_rank_attrib(c1dra[i]))
return 0;
- /* If the rank boundaries for the two channels are different
+ /*
+ * If the rank boundaries for the two channels are different
* then we're not interleaved.
*/
for (i = 0; i < I3000_RANKS_PER_CHANNEL; i++)
@@ -288,6 +326,15 @@ static int i3000_probe1(struct pci_dev *pdev, int dev_idx)
return -ENODEV;
}
+ switch (edac_op_state) {
+ case EDAC_OPSTATE_POLL:
+ case EDAC_OPSTATE_NMI:
+ break;
+ default:
+ edac_op_state = EDAC_OPSTATE_POLL;
+ break;
+ }
+
c0dra[0] = readb(window + I3000_C0DRA + 0); /* ranks 0,1 */
c0dra[1] = readb(window + I3000_C0DRA + 1); /* ranks 2,3 */
c1dra[0] = readb(window + I3000_C1DRA + 0); /* ranks 0,1 */
@@ -300,7 +347,8 @@ static int i3000_probe1(struct pci_dev *pdev, int dev_idx)
iounmap(window);
- /* Figure out how many channels we have.
+ /*
+ * Figure out how many channels we have.
*
* If we have what the datasheet calls "asymmetric channels"
* (essentially the same as what was called "virtual single
@@ -363,7 +411,8 @@ static int i3000_probe1(struct pci_dev *pdev, int dev_idx)
csrow->edac_mode = EDAC_UNKNOWN;
}
- /* Clear any error bits.
+ /*
+ * Clear any error bits.
* (Yes, we really clear bits by writing 1 to them.)
*/
pci_write_bits16(pdev, I3000_ERRSTS, I3000_ERRSTS_BITS,
@@ -390,7 +439,7 @@ static int i3000_probe1(struct pci_dev *pdev, int dev_idx)
debugf3("MC: %s(): success\n", __func__);
return 0;
- fail:
+fail:
if (mci)
edac_mc_free(mci);
@@ -409,7 +458,7 @@ static int __devinit i3000_init_one(struct pci_dev *pdev,
return -EIO;
rc = i3000_probe1(pdev, ent->driver_data);
- if (mci_pdev == NULL)
+ if (!mci_pdev)
mci_pdev = pci_dev_get(pdev);
return rc;
@@ -424,7 +473,8 @@ static void __devexit i3000_remove_one(struct pci_dev *pdev)
if (i3000_pci)
edac_pci_release_generic_ctl(i3000_pci);
- if ((mci = edac_mc_del_mc(&pdev->dev)) == NULL)
+ mci = edac_mc_del_mc(&pdev->dev);
+ if (!mci)
return;
edac_mc_free(mci);
@@ -457,7 +507,7 @@ static int __init i3000_init(void)
if (pci_rc < 0)
goto fail0;
- if (mci_pdev == NULL) {
+ if (!mci_pdev) {
i3000_registered = 0;
mci_pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
PCI_DEVICE_ID_INTEL_3000_HB, NULL);
@@ -504,3 +554,6 @@ module_exit(i3000_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Akamai Technologies Arthur Ulfeldt/Jason Uhlenkott");
MODULE_DESCRIPTION("MC support for Intel 3000 memory hub controllers");
+
+module_param(edac_op_state, int, 0444);
+MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI");
diff --git a/drivers/edac/mpc85xx_edac.c b/drivers/edac/mpc85xx_edac.c
new file mode 100644
index 000000000000..065732ddf40c
--- /dev/null
+++ b/drivers/edac/mpc85xx_edac.c
@@ -0,0 +1,1043 @@
+/*
+ * Freescale MPC85xx Memory Controller kenel module
+ *
+ * Author: Dave Jiang <djiang@mvista.com>
+ *
+ * 2006-2007 (c) MontaVista Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ *
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/ctype.h>
+#include <linux/io.h>
+#include <linux/mod_devicetable.h>
+#include <linux/edac.h>
+
+#include <linux/of_platform.h>
+#include <linux/of_device.h>
+#include <asm/mpc85xx.h>
+#include "edac_module.h"
+#include "edac_core.h"
+#include "mpc85xx_edac.h"
+
+static int edac_dev_idx;
+static int edac_pci_idx;
+static int edac_mc_idx;
+
+static u32 orig_ddr_err_disable;
+static u32 orig_ddr_err_sbe;
+
+/*
+ * PCI Err defines
+ */
+#ifdef CONFIG_PCI
+static u32 orig_pci_err_cap_dr;
+static u32 orig_pci_err_en;
+#endif
+
+static u32 orig_l2_err_disable;
+static u32 orig_hid1;
+
+static const char *mpc85xx_ctl_name = "MPC85xx";
+
+/************************ MC SYSFS parts ***********************************/
+
+static ssize_t mpc85xx_mc_inject_data_hi_show(struct mem_ctl_info *mci,
+ char *data)
+{
+ struct mpc85xx_mc_pdata *pdata = mci->pvt_info;
+ return sprintf(data, "0x%08x",
+ in_be32(pdata->mc_vbase +
+ MPC85XX_MC_DATA_ERR_INJECT_HI));
+}
+
+static ssize_t mpc85xx_mc_inject_data_lo_show(struct mem_ctl_info *mci,
+ char *data)
+{
+ struct mpc85xx_mc_pdata *pdata = mci->pvt_info;
+ return sprintf(data, "0x%08x",
+ in_be32(pdata->mc_vbase +
+ MPC85XX_MC_DATA_ERR_INJECT_LO));
+}
+
+static ssize_t mpc85xx_mc_inject_ctrl_show(struct mem_ctl_info *mci, char *data)
+{
+ struct mpc85xx_mc_pdata *pdata = mci->pvt_info;
+ return sprintf(data, "0x%08x",
+ in_be32(pdata->mc_vbase + MPC85XX_MC_ECC_ERR_INJECT));
+}
+
+static ssize_t mpc85xx_mc_inject_data_hi_store(struct mem_ctl_info *mci,
+ const char *data, size_t count)
+{
+ struct mpc85xx_mc_pdata *pdata = mci->pvt_info;
+ if (isdigit(*data)) {
+ out_be32(pdata->mc_vbase + MPC85XX_MC_DATA_ERR_INJECT_HI,
+ simple_strtoul(data, NULL, 0));
+ return count;
+ }
+ return 0;
+}
+
+static ssize_t mpc85xx_mc_inject_data_lo_store(struct mem_ctl_info *mci,
+ const char *data, size_t count)
+{
+ struct mpc85xx_mc_pdata *pdata = mci->pvt_info;
+ if (isdigit(*data)) {
+ out_be32(pdata->mc_vbase + MPC85XX_MC_DATA_ERR_INJECT_LO,
+ simple_strtoul(data, NULL, 0));
+ return count;
+ }
+ return 0;
+}
+
+static ssize_t mpc85xx_mc_inject_ctrl_store(struct mem_ctl_info *mci,
+ const char *data, size_t count)
+{
+ struct mpc85xx_mc_pdata *pdata = mci->pvt_info;
+ if (isdigit(*data)) {
+ out_be32(pdata->mc_vbase + MPC85XX_MC_ECC_ERR_INJECT,
+ simple_strtoul(data, NULL, 0));
+ return count;
+ }
+ return 0;
+}
+
+static struct mcidev_sysfs_attribute mpc85xx_mc_sysfs_attributes[] = {
+ {
+ .attr = {
+ .name = "inject_data_hi",
+ .mode = (S_IRUGO | S_IWUSR)
+ },
+ .show = mpc85xx_mc_inject_data_hi_show,
+ .store = mpc85xx_mc_inject_data_hi_store},
+ {
+ .attr = {
+ .name = "inject_data_lo",
+ .mode = (S_IRUGO | S_IWUSR)
+ },
+ .show = mpc85xx_mc_inject_data_lo_show,
+ .store = mpc85xx_mc_inject_data_lo_store},
+ {
+ .attr = {
+ .name = "inject_ctrl",
+ .mode = (S_IRUGO | S_IWUSR)
+ },
+ .show = mpc85xx_mc_inject_ctrl_show,
+ .store = mpc85xx_mc_inject_ctrl_store},
+
+ /* End of list */
+ {
+ .attr = {.name = NULL}
+ }
+};
+
+static void mpc85xx_set_mc_sysfs_attributes(struct mem_ctl_info *mci)
+{
+ mci->mc_driver_sysfs_attributes = mpc85xx_mc_sysfs_attributes;
+}
+
+/**************************** PCI Err device ***************************/
+#ifdef CONFIG_PCI
+
+static void mpc85xx_pci_check(struct edac_pci_ctl_info *pci)
+{
+ struct mpc85xx_pci_pdata *pdata = pci->pvt_info;
+ u32 err_detect;
+
+ err_detect = in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_DR);
+
+ /* master aborts can happen during PCI config cycles */
+ if (!(err_detect & ~(PCI_EDE_MULTI_ERR | PCI_EDE_MST_ABRT))) {
+ out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_DR, err_detect);
+ return;
+ }
+
+ printk(KERN_ERR "PCI error(s) detected\n");
+ printk(KERN_ERR "PCI/X ERR_DR register: %#08x\n", err_detect);
+
+ printk(KERN_ERR "PCI/X ERR_ATTRIB register: %#08x\n",
+ in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_ATTRIB));
+ printk(KERN_ERR "PCI/X ERR_ADDR register: %#08x\n",
+ in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_ADDR));
+ printk(KERN_ERR "PCI/X ERR_EXT_ADDR register: %#08x\n",
+ in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_EXT_ADDR));
+ printk(KERN_ERR "PCI/X ERR_DL register: %#08x\n",
+ in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_DL));
+ printk(KERN_ERR "PCI/X ERR_DH register: %#08x\n",
+ in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_DH));
+
+ /* clear error bits */
+ out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_DR, err_detect);
+
+ if (err_detect & PCI_EDE_PERR_MASK)
+ edac_pci_handle_pe(pci, pci->ctl_name);
+
+ if ((err_detect & ~PCI_EDE_MULTI_ERR) & ~PCI_EDE_PERR_MASK)
+ edac_pci_handle_npe(pci, pci->ctl_name);
+}
+
+static irqreturn_t mpc85xx_pci_isr(int irq, void *dev_id)
+{
+ struct edac_pci_ctl_info *pci = dev_id;
+ struct mpc85xx_pci_pdata *pdata = pci->pvt_info;
+ u32 err_detect;
+
+ err_detect = in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_DR);
+
+ if (!err_detect)
+ return IRQ_NONE;
+
+ mpc85xx_pci_check(pci);
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit mpc85xx_pci_err_probe(struct platform_device *pdev)
+{
+ struct edac_pci_ctl_info *pci;
+ struct mpc85xx_pci_pdata *pdata;
+ struct resource *r;
+ int res = 0;
+
+ if (!devres_open_group(&pdev->dev, mpc85xx_pci_err_probe, GFP_KERNEL))
+ return -ENOMEM;
+
+ pci = edac_pci_alloc_ctl_info(sizeof(*pdata), "mpc85xx_pci_err");
+ if (!pci)
+ return -ENOMEM;
+
+ pdata = pci->pvt_info;
+ pdata->name = "mpc85xx_pci_err";
+ pdata->irq = NO_IRQ;
+ platform_set_drvdata(pdev, pci);
+ pci->dev = &pdev->dev;
+ pci->mod_name = EDAC_MOD_STR;
+ pci->ctl_name = pdata->name;
+ pci->dev_name = pdev->dev.bus_id;
+
+ if (edac_op_state == EDAC_OPSTATE_POLL)
+ pci->edac_check = mpc85xx_pci_check;
+
+ pdata->edac_idx = edac_pci_idx++;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r) {
+ printk(KERN_ERR "%s: Unable to get resource for "
+ "PCI err regs\n", __func__);
+ goto err;
+ }
+
+ if (!devm_request_mem_region(&pdev->dev, r->start,
+ r->end - r->start + 1, pdata->name)) {
+ printk(KERN_ERR "%s: Error while requesting mem region\n",
+ __func__);
+ res = -EBUSY;
+ goto err;
+ }
+
+ pdata->pci_vbase = devm_ioremap(&pdev->dev, r->start,
+ r->end - r->start + 1);
+ if (!pdata->pci_vbase) {
+ printk(KERN_ERR "%s: Unable to setup PCI err regs\n", __func__);
+ res = -ENOMEM;
+ goto err;
+ }
+
+ orig_pci_err_cap_dr =
+ in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_CAP_DR);
+
+ /* PCI master abort is expected during config cycles */
+ out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_CAP_DR, 0x40);
+
+ orig_pci_err_en = in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_EN);
+
+ /* disable master abort reporting */
+ out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_EN, ~0x40);
+
+ /* clear error bits */
+ out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_DR, ~0);
+
+ if (edac_pci_add_device(pci, pdata->edac_idx) > 0) {
+ debugf3("%s(): failed edac_pci_add_device()\n", __func__);
+ goto err;
+ }
+
+ if (edac_op_state == EDAC_OPSTATE_INT) {
+ pdata->irq = platform_get_irq(pdev, 0);
+ res = devm_request_irq(&pdev->dev, pdata->irq,
+ mpc85xx_pci_isr, IRQF_DISABLED,
+ "[EDAC] PCI err", pci);
+ if (res < 0) {
+ printk(KERN_ERR
+ "%s: Unable to requiest irq %d for "
+ "MPC85xx PCI err\n", __func__, pdata->irq);
+ res = -ENODEV;
+ goto err2;
+ }
+
+ printk(KERN_INFO EDAC_MOD_STR " acquired irq %d for PCI Err\n",
+ pdata->irq);
+ }
+
+ devres_remove_group(&pdev->dev, mpc85xx_pci_err_probe);
+ debugf3("%s(): success\n", __func__);
+ printk(KERN_INFO EDAC_MOD_STR " PCI err registered\n");
+
+ return 0;
+
+err2:
+ edac_pci_del_device(&pdev->dev);
+err:
+ edac_pci_free_ctl_info(pci);
+ devres_release_group(&pdev->dev, mpc85xx_pci_err_probe);
+ return res;
+}
+
+static int mpc85xx_pci_err_remove(struct platform_device *pdev)
+{
+ struct edac_pci_ctl_info *pci = platform_get_drvdata(pdev);
+ struct mpc85xx_pci_pdata *pdata = pci->pvt_info;
+
+ debugf0("%s()\n", __func__);
+
+ out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_CAP_DR,
+ orig_pci_err_cap_dr);
+
+ out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_EN, orig_pci_err_en);
+
+ edac_pci_del_device(pci->dev);
+
+ if (edac_op_state == EDAC_OPSTATE_INT)
+ irq_dispose_mapping(pdata->irq);
+
+ edac_pci_free_ctl_info(pci);
+
+ return 0;
+}
+
+static struct platform_driver mpc85xx_pci_err_driver = {
+ .probe = mpc85xx_pci_err_probe,
+ .remove = __devexit_p(mpc85xx_pci_err_remove),
+ .driver = {
+ .name = "mpc85xx_pci_err",
+ }
+};
+
+#endif /* CONFIG_PCI */
+
+/**************************** L2 Err device ***************************/
+
+/************************ L2 SYSFS parts ***********************************/
+
+static ssize_t mpc85xx_l2_inject_data_hi_show(struct edac_device_ctl_info
+ *edac_dev, char *data)
+{
+ struct mpc85xx_l2_pdata *pdata = edac_dev->pvt_info;
+ return sprintf(data, "0x%08x",
+ in_be32(pdata->l2_vbase + MPC85XX_L2_ERRINJHI));
+}
+
+static ssize_t mpc85xx_l2_inject_data_lo_show(struct edac_device_ctl_info
+ *edac_dev, char *data)
+{
+ struct mpc85xx_l2_pdata *pdata = edac_dev->pvt_info;
+ return sprintf(data, "0x%08x",
+ in_be32(pdata->l2_vbase + MPC85XX_L2_ERRINJLO));
+}
+
+static ssize_t mpc85xx_l2_inject_ctrl_show(struct edac_device_ctl_info
+ *edac_dev, char *data)
+{
+ struct mpc85xx_l2_pdata *pdata = edac_dev->pvt_info;
+ return sprintf(data, "0x%08x",
+ in_be32(pdata->l2_vbase + MPC85XX_L2_ERRINJCTL));
+}
+
+static ssize_t mpc85xx_l2_inject_data_hi_store(struct edac_device_ctl_info
+ *edac_dev, const char *data,
+ size_t count)
+{
+ struct mpc85xx_l2_pdata *pdata = edac_dev->pvt_info;
+ if (isdigit(*data)) {
+ out_be32(pdata->l2_vbase + MPC85XX_L2_ERRINJHI,
+ simple_strtoul(data, NULL, 0));
+ return count;
+ }
+ return 0;
+}
+
+static ssize_t mpc85xx_l2_inject_data_lo_store(struct edac_device_ctl_info
+ *edac_dev, const char *data,
+ size_t count)
+{
+ struct mpc85xx_l2_pdata *pdata = edac_dev->pvt_info;
+ if (isdigit(*data)) {
+ out_be32(pdata->l2_vbase + MPC85XX_L2_ERRINJLO,
+ simple_strtoul(data, NULL, 0));
+ return count;
+ }
+ return 0;
+}
+
+static ssize_t mpc85xx_l2_inject_ctrl_store(struct edac_device_ctl_info
+ *edac_dev, const char *data,
+ size_t count)
+{
+ struct mpc85xx_l2_pdata *pdata = edac_dev->pvt_info;
+ if (isdigit(*data)) {
+ out_be32(pdata->l2_vbase + MPC85XX_L2_ERRINJCTL,
+ simple_strtoul(data, NULL, 0));
+ return count;
+ }
+ return 0;
+}
+
+static struct edac_dev_sysfs_attribute mpc85xx_l2_sysfs_attributes[] = {
+ {
+ .attr = {
+ .name = "inject_data_hi",
+ .mode = (S_IRUGO | S_IWUSR)
+ },
+ .show = mpc85xx_l2_inject_data_hi_show,
+ .store = mpc85xx_l2_inject_data_hi_store},
+ {
+ .attr = {
+ .name = "inject_data_lo",
+ .mode = (S_IRUGO | S_IWUSR)
+ },
+ .show = mpc85xx_l2_inject_data_lo_show,
+ .store = mpc85xx_l2_inject_data_lo_store},
+ {
+ .attr = {
+ .name = "inject_ctrl",
+ .mode = (S_IRUGO | S_IWUSR)
+ },
+ .show = mpc85xx_l2_inject_ctrl_show,
+ .store = mpc85xx_l2_inject_ctrl_store},
+
+ /* End of list */
+ {
+ .attr = {.name = NULL}
+ }
+};
+
+static void mpc85xx_set_l2_sysfs_attributes(struct edac_device_ctl_info
+ *edac_dev)
+{
+ edac_dev->sysfs_attributes = mpc85xx_l2_sysfs_attributes;
+}
+
+/***************************** L2 ops ***********************************/
+
+static void mpc85xx_l2_check(struct edac_device_ctl_info *edac_dev)
+{
+ struct mpc85xx_l2_pdata *pdata = edac_dev->pvt_info;
+ u32 err_detect;
+
+ err_detect = in_be32(pdata->l2_vbase + MPC85XX_L2_ERRDET);
+
+ if (!(err_detect & L2_EDE_MASK))
+ return;
+
+ printk(KERN_ERR "ECC Error in CPU L2 cache\n");
+ printk(KERN_ERR "L2 Error Detect Register: 0x%08x\n", err_detect);
+ printk(KERN_ERR "L2 Error Capture Data High Register: 0x%08x\n",
+ in_be32(pdata->l2_vbase + MPC85XX_L2_CAPTDATAHI));
+ printk(KERN_ERR "L2 Error Capture Data Lo Register: 0x%08x\n",
+ in_be32(pdata->l2_vbase + MPC85XX_L2_CAPTDATALO));
+ printk(KERN_ERR "L2 Error Syndrome Register: 0x%08x\n",
+ in_be32(pdata->l2_vbase + MPC85XX_L2_CAPTECC));
+ printk(KERN_ERR "L2 Error Attributes Capture Register: 0x%08x\n",
+ in_be32(pdata->l2_vbase + MPC85XX_L2_ERRATTR));
+ printk(KERN_ERR "L2 Error Address Capture Register: 0x%08x\n",
+ in_be32(pdata->l2_vbase + MPC85XX_L2_ERRADDR));
+
+ /* clear error detect register */
+ out_be32(pdata->l2_vbase + MPC85XX_L2_ERRDET, err_detect);
+
+ if (err_detect & L2_EDE_CE_MASK)
+ edac_device_handle_ce(edac_dev, 0, 0, edac_dev->ctl_name);
+
+ if (err_detect & L2_EDE_UE_MASK)
+ edac_device_handle_ue(edac_dev, 0, 0, edac_dev->ctl_name);
+}
+
+static irqreturn_t mpc85xx_l2_isr(int irq, void *dev_id)
+{
+ struct edac_device_ctl_info *edac_dev = dev_id;
+ struct mpc85xx_l2_pdata *pdata = edac_dev->pvt_info;
+ u32 err_detect;
+
+ err_detect = in_be32(pdata->l2_vbase + MPC85XX_L2_ERRDET);
+
+ if (!(err_detect & L2_EDE_MASK))
+ return IRQ_NONE;
+
+ mpc85xx_l2_check(edac_dev);
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit mpc85xx_l2_err_probe(struct of_device *op,
+ const struct of_device_id *match)
+{
+ struct edac_device_ctl_info *edac_dev;
+ struct mpc85xx_l2_pdata *pdata;
+ struct resource r;
+ int res;
+
+ if (!devres_open_group(&op->dev, mpc85xx_l2_err_probe, GFP_KERNEL))
+ return -ENOMEM;
+
+ edac_dev = edac_device_alloc_ctl_info(sizeof(*pdata),
+ "cpu", 1, "L", 1, 2, NULL, 0,
+ edac_dev_idx);
+ if (!edac_dev) {
+ devres_release_group(&op->dev, mpc85xx_l2_err_probe);
+ return -ENOMEM;
+ }
+
+ pdata = edac_dev->pvt_info;
+ pdata->name = "mpc85xx_l2_err";
+ pdata->irq = NO_IRQ;
+ edac_dev->dev = &op->dev;
+ dev_set_drvdata(edac_dev->dev, edac_dev);
+ edac_dev->ctl_name = pdata->name;
+ edac_dev->dev_name = pdata->name;
+
+ res = of_address_to_resource(op->node, 0, &r);
+ if (res) {
+ printk(KERN_ERR "%s: Unable to get resource for "
+ "L2 err regs\n", __func__);
+ goto err;
+ }
+
+ /* we only need the error registers */
+ r.start += 0xe00;
+
+ if (!devm_request_mem_region(&op->dev, r.start,
+ r.end - r.start + 1, pdata->name)) {
+ printk(KERN_ERR "%s: Error while requesting mem region\n",
+ __func__);
+ res = -EBUSY;
+ goto err;
+ }
+
+ pdata->l2_vbase = devm_ioremap(&op->dev, r.start, r.end - r.start + 1);
+ if (!pdata->l2_vbase) {
+ printk(KERN_ERR "%s: Unable to setup L2 err regs\n", __func__);
+ res = -ENOMEM;
+ goto err;
+ }
+
+ out_be32(pdata->l2_vbase + MPC85XX_L2_ERRDET, ~0);
+
+ orig_l2_err_disable = in_be32(pdata->l2_vbase + MPC85XX_L2_ERRDIS);
+
+ /* clear the err_dis */
+ out_be32(pdata->l2_vbase + MPC85XX_L2_ERRDIS, 0);
+
+ edac_dev->mod_name = EDAC_MOD_STR;
+
+ if (edac_op_state == EDAC_OPSTATE_POLL)
+ edac_dev->edac_check = mpc85xx_l2_check;
+
+ mpc85xx_set_l2_sysfs_attributes(edac_dev);
+
+ pdata->edac_idx = edac_dev_idx++;
+
+ if (edac_device_add_device(edac_dev) > 0) {
+ debugf3("%s(): failed edac_device_add_device()\n", __func__);
+ goto err;
+ }
+
+ if (edac_op_state == EDAC_OPSTATE_INT) {
+ pdata->irq = irq_of_parse_and_map(op->node, 0);
+ res = devm_request_irq(&op->dev, pdata->irq,
+ mpc85xx_l2_isr, IRQF_DISABLED,
+ "[EDAC] L2 err", edac_dev);
+ if (res < 0) {
+ printk(KERN_ERR
+ "%s: Unable to requiest irq %d for "
+ "MPC85xx L2 err\n", __func__, pdata->irq);
+ irq_dispose_mapping(pdata->irq);
+ res = -ENODEV;
+ goto err2;
+ }
+
+ printk(KERN_INFO EDAC_MOD_STR " acquired irq %d for L2 Err\n",
+ pdata->irq);
+
+ edac_dev->op_state = OP_RUNNING_INTERRUPT;
+
+ out_be32(pdata->l2_vbase + MPC85XX_L2_ERRINTEN, L2_EIE_MASK);
+ }
+
+ devres_remove_group(&op->dev, mpc85xx_l2_err_probe);
+
+ debugf3("%s(): success\n", __func__);
+ printk(KERN_INFO EDAC_MOD_STR " L2 err registered\n");
+
+ return 0;
+
+err2:
+ edac_device_del_device(&op->dev);
+err:
+ devres_release_group(&op->dev, mpc85xx_l2_err_probe);
+ edac_device_free_ctl_info(edac_dev);
+ return res;
+}
+
+static int mpc85xx_l2_err_remove(struct of_device *op)
+{
+ struct edac_device_ctl_info *edac_dev = dev_get_drvdata(&op->dev);
+ struct mpc85xx_l2_pdata *pdata = edac_dev->pvt_info;
+
+ debugf0("%s()\n", __func__);
+
+ if (edac_op_state == EDAC_OPSTATE_INT) {
+ out_be32(pdata->l2_vbase + MPC85XX_L2_ERRINTEN, 0);
+ irq_dispose_mapping(pdata->irq);
+ }
+
+ out_be32(pdata->l2_vbase + MPC85XX_L2_ERRDIS, orig_l2_err_disable);
+ edac_device_del_device(&op->dev);
+ edac_device_free_ctl_info(edac_dev);
+ return 0;
+}
+
+static struct of_device_id mpc85xx_l2_err_of_match[] = {
+ {
+ .compatible = "fsl,8540-l2-cache-controller",
+ },
+ {
+ .compatible = "fsl,8541-l2-cache-controller",
+ },
+ {
+ .compatible = "fsl,8544-l2-cache-controller",
+ },
+ {
+ .compatible = "fsl,8548-l2-cache-controller",
+ },
+ {
+ .compatible = "fsl,8555-l2-cache-controller",
+ },
+ {
+ .compatible = "fsl,8568-l2-cache-controller",
+ },
+ {},
+};
+
+static struct of_platform_driver mpc85xx_l2_err_driver = {
+ .owner = THIS_MODULE,
+ .name = "mpc85xx_l2_err",
+ .match_table = mpc85xx_l2_err_of_match,
+ .probe = mpc85xx_l2_err_probe,
+ .remove = mpc85xx_l2_err_remove,
+ .driver = {
+ .name = "mpc85xx_l2_err",
+ .owner = THIS_MODULE,
+ },
+};
+
+/**************************** MC Err device ***************************/
+
+static void mpc85xx_mc_check(struct mem_ctl_info *mci)
+{
+ struct mpc85xx_mc_pdata *pdata = mci->pvt_info;
+ struct csrow_info *csrow;
+ u32 err_detect;
+ u32 syndrome;
+ u32 err_addr;
+ u32 pfn;
+ int row_index;
+
+ err_detect = in_be32(pdata->mc_vbase + MPC85XX_MC_ERR_DETECT);
+ if (err_detect)
+ return;
+
+ mpc85xx_mc_printk(mci, KERN_ERR, "Err Detect Register: %#8.8x\n",
+ err_detect);
+
+ /* no more processing if not ECC bit errors */
+ if (!(err_detect & (DDR_EDE_SBE | DDR_EDE_MBE))) {
+ out_be32(pdata->mc_vbase + MPC85XX_MC_ERR_DETECT, err_detect);
+ return;
+ }
+
+ syndrome = in_be32(pdata->mc_vbase + MPC85XX_MC_CAPTURE_ECC);
+ err_addr = in_be32(pdata->mc_vbase + MPC85XX_MC_CAPTURE_ADDRESS);
+ pfn = err_addr >> PAGE_SHIFT;
+
+ for (row_index = 0; row_index < mci->nr_csrows; row_index++) {
+ csrow = &mci->csrows[row_index];
+ if ((pfn >= csrow->first_page) && (pfn <= csrow->last_page))
+ break;
+ }
+
+ mpc85xx_mc_printk(mci, KERN_ERR, "Capture Data High: %#8.8x\n",
+ in_be32(pdata->mc_vbase +
+ MPC85XX_MC_CAPTURE_DATA_HI));
+ mpc85xx_mc_printk(mci, KERN_ERR, "Capture Data Low: %#8.8x\n",
+ in_be32(pdata->mc_vbase +
+ MPC85XX_MC_CAPTURE_DATA_LO));
+ mpc85xx_mc_printk(mci, KERN_ERR, "syndrome: %#8.8x\n", syndrome);
+ mpc85xx_mc_printk(mci, KERN_ERR, "err addr: %#8.8x\n", err_addr);
+ mpc85xx_mc_printk(mci, KERN_ERR, "PFN: %#8.8x\n", pfn);
+
+ /* we are out of range */
+ if (row_index == mci->nr_csrows)
+ mpc85xx_mc_printk(mci, KERN_ERR, "PFN out of range!\n");
+
+ if (err_detect & DDR_EDE_SBE)
+ edac_mc_handle_ce(mci, pfn, err_addr & PAGE_MASK,
+ syndrome, row_index, 0, mci->ctl_name);
+
+ if (err_detect & DDR_EDE_MBE)
+ edac_mc_handle_ue(mci, pfn, err_addr & PAGE_MASK,
+ row_index, mci->ctl_name);
+
+ out_be32(pdata->mc_vbase + MPC85XX_MC_ERR_DETECT, err_detect);
+}
+
+static irqreturn_t mpc85xx_mc_isr(int irq, void *dev_id)
+{
+ struct mem_ctl_info *mci = dev_id;
+ struct mpc85xx_mc_pdata *pdata = mci->pvt_info;
+ u32 err_detect;
+
+ err_detect = in_be32(pdata->mc_vbase + MPC85XX_MC_ERR_DETECT);
+ if (!err_detect)
+ return IRQ_NONE;
+
+ mpc85xx_mc_check(mci);
+
+ return IRQ_HANDLED;
+}
+
+static void __devinit mpc85xx_init_csrows(struct mem_ctl_info *mci)
+{
+ struct mpc85xx_mc_pdata *pdata = mci->pvt_info;
+ struct csrow_info *csrow;
+ u32 sdram_ctl;
+ u32 sdtype;
+ enum mem_type mtype;
+ u32 cs_bnds;
+ int index;
+
+ sdram_ctl = in_be32(pdata->mc_vbase + MPC85XX_MC_DDR_SDRAM_CFG);
+
+ sdtype = sdram_ctl & DSC_SDTYPE_MASK;
+ if (sdram_ctl & DSC_RD_EN) {
+ switch (sdtype) {
+ case DSC_SDTYPE_DDR:
+ mtype = MEM_RDDR;
+ break;
+ case DSC_SDTYPE_DDR2:
+ mtype = MEM_RDDR2;
+ break;
+ default:
+ mtype = MEM_UNKNOWN;
+ break;
+ }
+ } else {
+ switch (sdtype) {
+ case DSC_SDTYPE_DDR:
+ mtype = MEM_DDR;
+ break;
+ case DSC_SDTYPE_DDR2:
+ mtype = MEM_DDR2;
+ break;
+ default:
+ mtype = MEM_UNKNOWN;
+ break;
+ }
+ }
+
+ for (index = 0; index < mci->nr_csrows; index++) {
+ u32 start;
+ u32 end;
+
+ csrow = &mci->csrows[index];
+ cs_bnds = in_be32(pdata->mc_vbase + MPC85XX_MC_CS_BNDS_0 +
+ (index * MPC85XX_MC_CS_BNDS_OFS));
+ start = (cs_bnds & 0xfff0000) << 4;
+ end = ((cs_bnds & 0xfff) << 20);
+ if (start)
+ start |= 0xfffff;
+ if (end)
+ end |= 0xfffff;
+
+ if (start == end)
+ continue; /* not populated */
+
+ csrow->first_page = start >> PAGE_SHIFT;
+ csrow->last_page = end >> PAGE_SHIFT;
+ csrow->nr_pages = csrow->last_page + 1 - csrow->first_page;
+ csrow->grain = 8;
+ csrow->mtype = mtype;
+ csrow->dtype = DEV_UNKNOWN;
+ if (sdram_ctl & DSC_X32_EN)
+ csrow->dtype = DEV_X32;
+ csrow->edac_mode = EDAC_SECDED;
+ }
+}
+
+static int __devinit mpc85xx_mc_err_probe(struct of_device *op,
+ const struct of_device_id *match)
+{
+ struct mem_ctl_info *mci;
+ struct mpc85xx_mc_pdata *pdata;
+ struct resource r;
+ u32 sdram_ctl;
+ int res;
+
+ if (!devres_open_group(&op->dev, mpc85xx_mc_err_probe, GFP_KERNEL))
+ return -ENOMEM;
+
+ mci = edac_mc_alloc(sizeof(*pdata), 4, 1, edac_mc_idx);
+ if (!mci) {
+ devres_release_group(&op->dev, mpc85xx_mc_err_probe);
+ return -ENOMEM;
+ }
+
+ pdata = mci->pvt_info;
+ pdata->name = "mpc85xx_mc_err";
+ pdata->irq = NO_IRQ;
+ mci->dev = &op->dev;
+ pdata->edac_idx = edac_mc_idx++;
+ dev_set_drvdata(mci->dev, mci);
+ mci->ctl_name = pdata->name;
+ mci->dev_name = pdata->name;
+
+ res = of_address_to_resource(op->node, 0, &r);
+ if (res) {
+ printk(KERN_ERR "%s: Unable to get resource for MC err regs\n",
+ __func__);
+ goto err;
+ }
+
+ if (!devm_request_mem_region(&op->dev, r.start,
+ r.end - r.start + 1, pdata->name)) {
+ printk(KERN_ERR "%s: Error while requesting mem region\n",
+ __func__);
+ res = -EBUSY;
+ goto err;
+ }
+
+ pdata->mc_vbase = devm_ioremap(&op->dev, r.start, r.end - r.start + 1);
+ if (!pdata->mc_vbase) {
+ printk(KERN_ERR "%s: Unable to setup MC err regs\n", __func__);
+ res = -ENOMEM;
+ goto err;
+ }
+
+ sdram_ctl = in_be32(pdata->mc_vbase + MPC85XX_MC_DDR_SDRAM_CFG);
+ if (!(sdram_ctl & DSC_ECC_EN)) {
+ /* no ECC */
+ printk(KERN_WARNING "%s: No ECC DIMMs discovered\n", __func__);
+ res = -ENODEV;
+ goto err;
+ }
+
+ debugf3("%s(): init mci\n", __func__);
+ mci->mtype_cap = MEM_FLAG_RDDR | MEM_FLAG_RDDR2 |
+ MEM_FLAG_DDR | MEM_FLAG_DDR2;
+ mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED;
+ mci->edac_cap = EDAC_FLAG_SECDED;
+ mci->mod_name = EDAC_MOD_STR;
+ mci->mod_ver = MPC85XX_REVISION;
+
+ if (edac_op_state == EDAC_OPSTATE_POLL)
+ mci->edac_check = mpc85xx_mc_check;
+
+ mci->ctl_page_to_phys = NULL;
+
+ mci->scrub_mode = SCRUB_SW_SRC;
+
+ mpc85xx_set_mc_sysfs_attributes(mci);
+
+ mpc85xx_init_csrows(mci);
+
+#ifdef CONFIG_EDAC_DEBUG
+ edac_mc_register_mcidev_debug((struct attribute **)debug_attr);
+#endif
+
+ /* store the original error disable bits */
+ orig_ddr_err_disable =
+ in_be32(pdata->mc_vbase + MPC85XX_MC_ERR_DISABLE);
+ out_be32(pdata->mc_vbase + MPC85XX_MC_ERR_DISABLE, 0);
+
+ /* clear all error bits */
+ out_be32(pdata->mc_vbase + MPC85XX_MC_ERR_DETECT, ~0);
+
+ if (edac_mc_add_mc(mci)) {
+ debugf3("%s(): failed edac_mc_add_mc()\n", __func__);
+ goto err;
+ }
+
+ if (edac_op_state == EDAC_OPSTATE_INT) {
+ out_be32(pdata->mc_vbase + MPC85XX_MC_ERR_INT_EN,
+ DDR_EIE_MBEE | DDR_EIE_SBEE);
+
+ /* store the original error management threshold */
+ orig_ddr_err_sbe = in_be32(pdata->mc_vbase +
+ MPC85XX_MC_ERR_SBE) & 0xff0000;
+
+ /* set threshold to 1 error per interrupt */
+ out_be32(pdata->mc_vbase + MPC85XX_MC_ERR_SBE, 0x10000);
+
+ /* register interrupts */
+ pdata->irq = irq_of_parse_and_map(op->node, 0);
+ res = devm_request_irq(&op->dev, pdata->irq,
+ mpc85xx_mc_isr, IRQF_DISABLED,
+ "[EDAC] MC err", mci);
+ if (res < 0) {
+ printk(KERN_ERR "%s: Unable to request irq %d for "
+ "MPC85xx DRAM ERR\n", __func__, pdata->irq);
+ irq_dispose_mapping(pdata->irq);
+ res = -ENODEV;
+ goto err2;
+ }
+
+ printk(KERN_INFO EDAC_MOD_STR " acquired irq %d for MC\n",
+ pdata->irq);
+ }
+
+ devres_remove_group(&op->dev, mpc85xx_mc_err_probe);
+ debugf3("%s(): success\n", __func__);
+ printk(KERN_INFO EDAC_MOD_STR " MC err registered\n");
+
+ return 0;
+
+err2:
+ edac_mc_del_mc(&op->dev);
+err:
+ devres_release_group(&op->dev, mpc85xx_mc_err_probe);
+ edac_mc_free(mci);
+ return res;
+}
+
+static int mpc85xx_mc_err_remove(struct of_device *op)
+{
+ struct mem_ctl_info *mci = dev_get_drvdata(&op->dev);
+ struct mpc85xx_mc_pdata *pdata = mci->pvt_info;
+
+ debugf0("%s()\n", __func__);
+
+ if (edac_op_state == EDAC_OPSTATE_INT) {
+ out_be32(pdata->mc_vbase + MPC85XX_MC_ERR_INT_EN, 0);
+ irq_dispose_mapping(pdata->irq);
+ }
+
+ out_be32(pdata->mc_vbase + MPC85XX_MC_ERR_DISABLE,
+ orig_ddr_err_disable);
+ out_be32(pdata->mc_vbase + MPC85XX_MC_ERR_SBE, orig_ddr_err_sbe);
+
+ edac_mc_del_mc(&op->dev);
+ edac_mc_free(mci);
+ return 0;
+}
+
+static struct of_device_id mpc85xx_mc_err_of_match[] = {
+ {
+ .compatible = "fsl,8540-memory-controller",
+ },
+ {
+ .compatible = "fsl,8541-memory-controller",
+ },
+ {
+ .compatible = "fsl,8544-memory-controller",
+ },
+ {
+ .compatible = "fsl,8548-memory-controller",
+ },
+ {
+ .compatible = "fsl,8555-memory-controller",
+ },
+ {
+ .compatible = "fsl,8568-memory-controller",
+ },
+ {},
+};
+
+static struct of_platform_driver mpc85xx_mc_err_driver = {
+ .owner = THIS_MODULE,
+ .name = "mpc85xx_mc_err",
+ .match_table = mpc85xx_mc_err_of_match,
+ .probe = mpc85xx_mc_err_probe,
+ .remove = mpc85xx_mc_err_remove,
+ .driver = {
+ .name = "mpc85xx_mc_err",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init mpc85xx_mc_init(void)
+{
+ int res = 0;
+
+ printk(KERN_INFO "Freescale(R) MPC85xx EDAC driver, "
+ "(C) 2006 Montavista Software\n");
+
+ /* make sure error reporting method is sane */
+ switch (edac_op_state) {
+ case EDAC_OPSTATE_POLL:
+ case EDAC_OPSTATE_INT:
+ break;
+ default:
+ edac_op_state = EDAC_OPSTATE_INT;
+ break;
+ }
+
+ res = of_register_platform_driver(&mpc85xx_mc_err_driver);
+ if (res)
+ printk(KERN_WARNING EDAC_MOD_STR "MC fails to register\n");
+
+ res = of_register_platform_driver(&mpc85xx_l2_err_driver);
+ if (res)
+ printk(KERN_WARNING EDAC_MOD_STR "L2 fails to register\n");
+
+#ifdef CONFIG_PCI
+ res = platform_driver_register(&mpc85xx_pci_err_driver);
+ if (res)
+ printk(KERN_WARNING EDAC_MOD_STR "PCI fails to register\n");
+#endif
+
+ /*
+ * need to clear HID1[RFXE] to disable machine check int
+ * so we can catch it
+ */
+ if (edac_op_state == EDAC_OPSTATE_INT) {
+ orig_hid1 = mfspr(SPRN_HID1);
+ mtspr(SPRN_HID1, (orig_hid1 & ~0x20000));
+ }
+
+ return 0;
+}
+
+module_init(mpc85xx_mc_init);
+
+static void __exit mpc85xx_mc_exit(void)
+{
+ mtspr(SPRN_HID1, orig_hid1);
+#ifdef CONFIG_PCI
+ platform_driver_unregister(&mpc85xx_pci_err_driver);
+#endif
+ of_unregister_platform_driver(&mpc85xx_l2_err_driver);
+ of_unregister_platform_driver(&mpc85xx_mc_err_driver);
+}
+
+module_exit(mpc85xx_mc_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Montavista Software, Inc.");
+module_param(edac_op_state, int, 0444);
+MODULE_PARM_DESC(edac_op_state,
+ "EDAC Error Reporting state: 0=Poll, 2=Interrupt");
diff --git a/drivers/edac/mpc85xx_edac.h b/drivers/edac/mpc85xx_edac.h
new file mode 100644
index 000000000000..135b3539a030
--- /dev/null
+++ b/drivers/edac/mpc85xx_edac.h
@@ -0,0 +1,162 @@
+/*
+ * Freescale MPC85xx Memory Controller kenel module
+ * Author: Dave Jiang <djiang@mvista.com>
+ *
+ * 2006-2007 (c) MontaVista Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ *
+ */
+#ifndef _MPC85XX_EDAC_H_
+#define _MPC85XX_EDAC_H_
+
+#define MPC85XX_REVISION " Ver: 2.0.0 " __DATE__
+#define EDAC_MOD_STR "MPC85xx_edac"
+
+#define mpc85xx_printk(level, fmt, arg...) \
+ edac_printk(level, "MPC85xx", fmt, ##arg)
+
+#define mpc85xx_mc_printk(mci, level, fmt, arg...) \
+ edac_mc_chipset_printk(mci, level, "MPC85xx", fmt, ##arg)
+
+/*
+ * DRAM error defines
+ */
+
+/* DDR_SDRAM_CFG */
+#define MPC85XX_MC_DDR_SDRAM_CFG 0x0110
+#define MPC85XX_MC_CS_BNDS_0 0x0000
+#define MPC85XX_MC_CS_BNDS_1 0x0008
+#define MPC85XX_MC_CS_BNDS_2 0x0010
+#define MPC85XX_MC_CS_BNDS_3 0x0018
+#define MPC85XX_MC_CS_BNDS_OFS 0x0008
+
+#define MPC85XX_MC_DATA_ERR_INJECT_HI 0x0e00
+#define MPC85XX_MC_DATA_ERR_INJECT_LO 0x0e04
+#define MPC85XX_MC_ECC_ERR_INJECT 0x0e08
+#define MPC85XX_MC_CAPTURE_DATA_HI 0x0e20
+#define MPC85XX_MC_CAPTURE_DATA_LO 0x0e24
+#define MPC85XX_MC_CAPTURE_ECC 0x0e28
+#define MPC85XX_MC_ERR_DETECT 0x0e40
+#define MPC85XX_MC_ERR_DISABLE 0x0e44
+#define MPC85XX_MC_ERR_INT_EN 0x0e48
+#define MPC85XX_MC_CAPTURE_ATRIBUTES 0x0e4c
+#define MPC85XX_MC_CAPTURE_ADDRESS 0x0e50
+#define MPC85XX_MC_ERR_SBE 0x0e58
+
+#define DSC_MEM_EN 0x80000000
+#define DSC_ECC_EN 0x20000000
+#define DSC_RD_EN 0x10000000
+
+#define DSC_SDTYPE_MASK 0x07000000
+
+#define DSC_SDTYPE_DDR 0x02000000
+#define DSC_SDTYPE_DDR2 0x03000000
+#define DSC_X32_EN 0x00000020
+
+/* Err_Int_En */
+#define DDR_EIE_MSEE 0x1 /* memory select */
+#define DDR_EIE_SBEE 0x4 /* single-bit ECC error */
+#define DDR_EIE_MBEE 0x8 /* multi-bit ECC error */
+
+/* Err_Detect */
+#define DDR_EDE_MSE 0x1 /* memory select */
+#define DDR_EDE_SBE 0x4 /* single-bit ECC error */
+#define DDR_EDE_MBE 0x8 /* multi-bit ECC error */
+#define DDR_EDE_MME 0x80000000 /* multiple memory errors */
+
+/* Err_Disable */
+#define DDR_EDI_MSED 0x1 /* memory select disable */
+#define DDR_EDI_SBED 0x4 /* single-bit ECC error disable */
+#define DDR_EDI_MBED 0x8 /* multi-bit ECC error disable */
+
+/*
+ * L2 Err defines
+ */
+#define MPC85XX_L2_ERRINJHI 0x0000
+#define MPC85XX_L2_ERRINJLO 0x0004
+#define MPC85XX_L2_ERRINJCTL 0x0008
+#define MPC85XX_L2_CAPTDATAHI 0x0020
+#define MPC85XX_L2_CAPTDATALO 0x0024
+#define MPC85XX_L2_CAPTECC 0x0028
+#define MPC85XX_L2_ERRDET 0x0040
+#define MPC85XX_L2_ERRDIS 0x0044
+#define MPC85XX_L2_ERRINTEN 0x0048
+#define MPC85XX_L2_ERRATTR 0x004c
+#define MPC85XX_L2_ERRADDR 0x0050
+#define MPC85XX_L2_ERRCTL 0x0058
+
+/* Error Interrupt Enable */
+#define L2_EIE_L2CFGINTEN 0x1
+#define L2_EIE_SBECCINTEN 0x4
+#define L2_EIE_MBECCINTEN 0x8
+#define L2_EIE_TPARINTEN 0x10
+#define L2_EIE_MASK (L2_EIE_L2CFGINTEN | L2_EIE_SBECCINTEN | \
+ L2_EIE_MBECCINTEN | L2_EIE_TPARINTEN)
+
+/* Error Detect */
+#define L2_EDE_L2CFGERR 0x1
+#define L2_EDE_SBECCERR 0x4
+#define L2_EDE_MBECCERR 0x8
+#define L2_EDE_TPARERR 0x10
+#define L2_EDE_MULL2ERR 0x80000000
+
+#define L2_EDE_CE_MASK L2_EDE_SBECCERR
+#define L2_EDE_UE_MASK (L2_EDE_L2CFGERR | L2_EDE_MBECCERR | \
+ L2_EDE_TPARERR)
+#define L2_EDE_MASK (L2_EDE_L2CFGERR | L2_EDE_SBECCERR | \
+ L2_EDE_MBECCERR | L2_EDE_TPARERR | L2_EDE_MULL2ERR)
+
+/*
+ * PCI Err defines
+ */
+#define PCI_EDE_TOE 0x00000001
+#define PCI_EDE_SCM 0x00000002
+#define PCI_EDE_IRMSV 0x00000004
+#define PCI_EDE_ORMSV 0x00000008
+#define PCI_EDE_OWMSV 0x00000010
+#define PCI_EDE_TGT_ABRT 0x00000020
+#define PCI_EDE_MST_ABRT 0x00000040
+#define PCI_EDE_TGT_PERR 0x00000080
+#define PCI_EDE_MST_PERR 0x00000100
+#define PCI_EDE_RCVD_SERR 0x00000200
+#define PCI_EDE_ADDR_PERR 0x00000400
+#define PCI_EDE_MULTI_ERR 0x80000000
+
+#define PCI_EDE_PERR_MASK (PCI_EDE_TGT_PERR | PCI_EDE_MST_PERR | \
+ PCI_EDE_ADDR_PERR)
+
+#define MPC85XX_PCI_ERR_DR 0x0000
+#define MPC85XX_PCI_ERR_CAP_DR 0x0004
+#define MPC85XX_PCI_ERR_EN 0x0008
+#define MPC85XX_PCI_ERR_ATTRIB 0x000c
+#define MPC85XX_PCI_ERR_ADDR 0x0010
+#define MPC85XX_PCI_ERR_EXT_ADDR 0x0014
+#define MPC85XX_PCI_ERR_DL 0x0018
+#define MPC85XX_PCI_ERR_DH 0x001c
+#define MPC85XX_PCI_GAS_TIMR 0x0020
+#define MPC85XX_PCI_PCIX_TIMR 0x0024
+
+struct mpc85xx_mc_pdata {
+ char *name;
+ int edac_idx;
+ void __iomem *mc_vbase;
+ int irq;
+};
+
+struct mpc85xx_l2_pdata {
+ char *name;
+ int edac_idx;
+ void __iomem *l2_vbase;
+ int irq;
+};
+
+struct mpc85xx_pci_pdata {
+ char *name;
+ int edac_idx;
+ void __iomem *pci_vbase;
+ int irq;
+};
+
+#endif
diff --git a/drivers/edac/mv64x60_edac.c b/drivers/edac/mv64x60_edac.c
new file mode 100644
index 000000000000..bf071f140a05
--- /dev/null
+++ b/drivers/edac/mv64x60_edac.c
@@ -0,0 +1,855 @@
+/*
+ * Marvell MV64x60 Memory Controller kernel module for PPC platforms
+ *
+ * Author: Dave Jiang <djiang@mvista.com>
+ *
+ * 2006-2007 (c) MontaVista Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/edac.h>
+
+#include "edac_core.h"
+#include "edac_module.h"
+#include "mv64x60_edac.h"
+
+static const char *mv64x60_ctl_name = "MV64x60";
+static int edac_dev_idx;
+static int edac_pci_idx;
+static int edac_mc_idx;
+
+/*********************** PCI err device **********************************/
+#ifdef CONFIG_PCI
+static void mv64x60_pci_check(struct edac_pci_ctl_info *pci)
+{
+ struct mv64x60_pci_pdata *pdata = pci->pvt_info;
+ u32 cause;
+
+ cause = in_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_CAUSE);
+ if (!cause)
+ return;
+
+ printk(KERN_ERR "Error in PCI %d Interface\n", pdata->pci_hose);
+ printk(KERN_ERR "Cause register: 0x%08x\n", cause);
+ printk(KERN_ERR "Address Low: 0x%08x\n",
+ in_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_ADDR_LO));
+ printk(KERN_ERR "Address High: 0x%08x\n",
+ in_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_ADDR_HI));
+ printk(KERN_ERR "Attribute: 0x%08x\n",
+ in_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_ATTR));
+ printk(KERN_ERR "Command: 0x%08x\n",
+ in_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_CMD));
+ out_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_CAUSE, ~cause);
+
+ if (cause & MV64X60_PCI_PE_MASK)
+ edac_pci_handle_pe(pci, pci->ctl_name);
+
+ if (!(cause & MV64X60_PCI_PE_MASK))
+ edac_pci_handle_npe(pci, pci->ctl_name);
+}
+
+static irqreturn_t mv64x60_pci_isr(int irq, void *dev_id)
+{
+ struct edac_pci_ctl_info *pci = dev_id;
+ struct mv64x60_pci_pdata *pdata = pci->pvt_info;
+ u32 val;
+
+ val = in_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_CAUSE);
+ if (!val)
+ return IRQ_NONE;
+
+ mv64x60_pci_check(pci);
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit mv64x60_pci_err_probe(struct platform_device *pdev)
+{
+ struct edac_pci_ctl_info *pci;
+ struct mv64x60_pci_pdata *pdata;
+ struct resource *r;
+ int res = 0;
+
+ if (!devres_open_group(&pdev->dev, mv64x60_pci_err_probe, GFP_KERNEL))
+ return -ENOMEM;
+
+ pci = edac_pci_alloc_ctl_info(sizeof(*pdata), "mv64x60_pci_err");
+ if (!pci)
+ return -ENOMEM;
+
+ pdata = pci->pvt_info;
+
+ pdata->pci_hose = pdev->id;
+ pdata->name = "mpc85xx_pci_err";
+ pdata->irq = NO_IRQ;
+ platform_set_drvdata(pdev, pci);
+ pci->dev = &pdev->dev;
+ pci->dev_name = pdev->dev.bus_id;
+ pci->mod_name = EDAC_MOD_STR;
+ pci->ctl_name = pdata->name;
+
+ if (edac_op_state == EDAC_OPSTATE_POLL)
+ pci->edac_check = mv64x60_pci_check;
+
+ pdata->edac_idx = edac_pci_idx++;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r) {
+ printk(KERN_ERR "%s: Unable to get resource for "
+ "PCI err regs\n", __func__);
+ res = -ENOENT;
+ goto err;
+ }
+
+ if (!devm_request_mem_region(&pdev->dev,
+ r->start,
+ r->end - r->start + 1,
+ pdata->name)) {
+ printk(KERN_ERR "%s: Error while requesting mem region\n",
+ __func__);
+ res = -EBUSY;
+ goto err;
+ }
+
+ pdata->pci_vbase = devm_ioremap(&pdev->dev,
+ r->start,
+ r->end - r->start + 1);
+ if (!pdata->pci_vbase) {
+ printk(KERN_ERR "%s: Unable to setup PCI err regs\n", __func__);
+ res = -ENOMEM;
+ goto err;
+ }
+
+ out_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_CAUSE, 0);
+ out_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_MASK, 0);
+ out_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_MASK,
+ MV64X60_PCIx_ERR_MASK_VAL);
+
+ if (edac_pci_add_device(pci, pdata->edac_idx) > 0) {
+ debugf3("%s(): failed edac_pci_add_device()\n", __func__);
+ goto err;
+ }
+
+ if (edac_op_state == EDAC_OPSTATE_INT) {
+ pdata->irq = platform_get_irq(pdev, 0);
+ res = devm_request_irq(&pdev->dev,
+ pdata->irq,
+ mv64x60_pci_isr,
+ IRQF_DISABLED,
+ "[EDAC] PCI err",
+ pci);
+ if (res < 0) {
+ printk(KERN_ERR "%s: Unable to request irq %d for "
+ "MV64x60 PCI ERR\n", __func__, pdata->irq);
+ res = -ENODEV;
+ goto err2;
+ }
+ printk(KERN_INFO EDAC_MOD_STR " acquired irq %d for PCI Err\n",
+ pdata->irq);
+ }
+
+ devres_remove_group(&pdev->dev, mv64x60_pci_err_probe);
+
+ /* get this far and it's successful */
+ debugf3("%s(): success\n", __func__);
+
+ return 0;
+
+err2:
+ edac_pci_del_device(&pdev->dev);
+err:
+ edac_pci_free_ctl_info(pci);
+ devres_release_group(&pdev->dev, mv64x60_pci_err_probe);
+ return res;
+}
+
+static int mv64x60_pci_err_remove(struct platform_device *pdev)
+{
+ struct edac_pci_ctl_info *pci = platform_get_drvdata(pdev);
+
+ debugf0("%s()\n", __func__);
+
+ edac_pci_del_device(&pdev->dev);
+
+ edac_pci_free_ctl_info(pci);
+
+ return 0;
+}
+
+static struct platform_driver mv64x60_pci_err_driver = {
+ .probe = mv64x60_pci_err_probe,
+ .remove = __devexit_p(mv64x60_pci_err_remove),
+ .driver = {
+ .name = "mv64x60_pci_err",
+ }
+};
+
+#endif /* CONFIG_PCI */
+
+/*********************** SRAM err device **********************************/
+static void mv64x60_sram_check(struct edac_device_ctl_info *edac_dev)
+{
+ struct mv64x60_sram_pdata *pdata = edac_dev->pvt_info;
+ u32 cause;
+
+ cause = in_le32(pdata->sram_vbase + MV64X60_SRAM_ERR_CAUSE);
+ if (!cause)
+ return;
+
+ printk(KERN_ERR "Error in internal SRAM\n");
+ printk(KERN_ERR "Cause register: 0x%08x\n", cause);
+ printk(KERN_ERR "Address Low: 0x%08x\n",
+ in_le32(pdata->sram_vbase + MV64X60_SRAM_ERR_ADDR_LO));
+ printk(KERN_ERR "Address High: 0x%08x\n",
+ in_le32(pdata->sram_vbase + MV64X60_SRAM_ERR_ADDR_HI));
+ printk(KERN_ERR "Data Low: 0x%08x\n",
+ in_le32(pdata->sram_vbase + MV64X60_SRAM_ERR_DATA_LO));
+ printk(KERN_ERR "Data High: 0x%08x\n",
+ in_le32(pdata->sram_vbase + MV64X60_SRAM_ERR_DATA_HI));
+ printk(KERN_ERR "Parity: 0x%08x\n",
+ in_le32(pdata->sram_vbase + MV64X60_SRAM_ERR_PARITY));
+ out_le32(pdata->sram_vbase + MV64X60_SRAM_ERR_CAUSE, 0);
+
+ edac_device_handle_ue(edac_dev, 0, 0, edac_dev->ctl_name);
+}
+
+static irqreturn_t mv64x60_sram_isr(int irq, void *dev_id)
+{
+ struct edac_device_ctl_info *edac_dev = dev_id;
+ struct mv64x60_sram_pdata *pdata = edac_dev->pvt_info;
+ u32 cause;
+
+ cause = in_le32(pdata->sram_vbase + MV64X60_SRAM_ERR_CAUSE);
+ if (!cause)
+ return IRQ_NONE;
+
+ mv64x60_sram_check(edac_dev);
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit mv64x60_sram_err_probe(struct platform_device *pdev)
+{
+ struct edac_device_ctl_info *edac_dev;
+ struct mv64x60_sram_pdata *pdata;
+ struct resource *r;
+ int res = 0;
+
+ if (!devres_open_group(&pdev->dev, mv64x60_sram_err_probe, GFP_KERNEL))
+ return -ENOMEM;
+
+ edac_dev = edac_device_alloc_ctl_info(sizeof(*pdata),
+ "sram", 1, NULL, 0, 0, NULL, 0,
+ edac_dev_idx);
+ if (!edac_dev) {
+ devres_release_group(&pdev->dev, mv64x60_sram_err_probe);
+ return -ENOMEM;
+ }
+
+ pdata = edac_dev->pvt_info;
+ pdata->name = "mv64x60_sram_err";
+ pdata->irq = NO_IRQ;
+ edac_dev->dev = &pdev->dev;
+ platform_set_drvdata(pdev, edac_dev);
+ edac_dev->dev_name = pdev->dev.bus_id;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r) {
+ printk(KERN_ERR "%s: Unable to get resource for "
+ "SRAM err regs\n", __func__);
+ res = -ENOENT;
+ goto err;
+ }
+
+ if (!devm_request_mem_region(&pdev->dev,
+ r->start,
+ r->end - r->start + 1,
+ pdata->name)) {
+ printk(KERN_ERR "%s: Error while request mem region\n",
+ __func__);
+ res = -EBUSY;
+ goto err;
+ }
+
+ pdata->sram_vbase = devm_ioremap(&pdev->dev,
+ r->start,
+ r->end - r->start + 1);
+ if (!pdata->sram_vbase) {
+ printk(KERN_ERR "%s: Unable to setup SRAM err regs\n",
+ __func__);
+ res = -ENOMEM;
+ goto err;
+ }
+
+ /* setup SRAM err registers */
+ out_le32(pdata->sram_vbase + MV64X60_SRAM_ERR_CAUSE, 0);
+
+ edac_dev->mod_name = EDAC_MOD_STR;
+ edac_dev->ctl_name = pdata->name;
+
+ if (edac_op_state == EDAC_OPSTATE_POLL)
+ edac_dev->edac_check = mv64x60_sram_check;
+
+ pdata->edac_idx = edac_dev_idx++;
+
+ if (edac_device_add_device(edac_dev) > 0) {
+ debugf3("%s(): failed edac_device_add_device()\n", __func__);
+ goto err;
+ }
+
+ if (edac_op_state == EDAC_OPSTATE_INT) {
+ pdata->irq = platform_get_irq(pdev, 0);
+ res = devm_request_irq(&pdev->dev,
+ pdata->irq,
+ mv64x60_sram_isr,
+ IRQF_DISABLED,
+ "[EDAC] SRAM err",
+ edac_dev);
+ if (res < 0) {
+ printk(KERN_ERR
+ "%s: Unable to request irq %d for "
+ "MV64x60 SRAM ERR\n", __func__, pdata->irq);
+ res = -ENODEV;
+ goto err2;
+ }
+
+ printk(KERN_INFO EDAC_MOD_STR " acquired irq %d for SRAM Err\n",
+ pdata->irq);
+ }
+
+ devres_remove_group(&pdev->dev, mv64x60_sram_err_probe);
+
+ /* get this far and it's successful */
+ debugf3("%s(): success\n", __func__);
+
+ return 0;
+
+err2:
+ edac_device_del_device(&pdev->dev);
+err:
+ devres_release_group(&pdev->dev, mv64x60_sram_err_probe);
+ edac_device_free_ctl_info(edac_dev);
+ return res;
+}
+
+static int mv64x60_sram_err_remove(struct platform_device *pdev)
+{
+ struct edac_device_ctl_info *edac_dev = platform_get_drvdata(pdev);
+
+ debugf0("%s()\n", __func__);
+
+ edac_device_del_device(&pdev->dev);
+ edac_device_free_ctl_info(edac_dev);
+
+ return 0;
+}
+
+static struct platform_driver mv64x60_sram_err_driver = {
+ .probe = mv64x60_sram_err_probe,
+ .remove = mv64x60_sram_err_remove,
+ .driver = {
+ .name = "mv64x60_sram_err",
+ }
+};
+
+/*********************** CPU err device **********************************/
+static void mv64x60_cpu_check(struct edac_device_ctl_info *edac_dev)
+{
+ struct mv64x60_cpu_pdata *pdata = edac_dev->pvt_info;
+ u32 cause;
+
+ cause = in_le32(pdata->cpu_vbase[1] + MV64x60_CPU_ERR_CAUSE) &
+ MV64x60_CPU_CAUSE_MASK;
+ if (!cause)
+ return;
+
+ printk(KERN_ERR "Error on CPU interface\n");
+ printk(KERN_ERR "Cause register: 0x%08x\n", cause);
+ printk(KERN_ERR "Address Low: 0x%08x\n",
+ in_le32(pdata->cpu_vbase[0] + MV64x60_CPU_ERR_ADDR_LO));
+ printk(KERN_ERR "Address High: 0x%08x\n",
+ in_le32(pdata->cpu_vbase[0] + MV64x60_CPU_ERR_ADDR_HI));
+ printk(KERN_ERR "Data Low: 0x%08x\n",
+ in_le32(pdata->cpu_vbase[1] + MV64x60_CPU_ERR_DATA_LO));
+ printk(KERN_ERR "Data High: 0x%08x\n",
+ in_le32(pdata->cpu_vbase[1] + MV64x60_CPU_ERR_DATA_HI));
+ printk(KERN_ERR "Parity: 0x%08x\n",
+ in_le32(pdata->cpu_vbase[1] + MV64x60_CPU_ERR_PARITY));
+ out_le32(pdata->cpu_vbase[1] + MV64x60_CPU_ERR_CAUSE, 0);
+
+ edac_device_handle_ue(edac_dev, 0, 0, edac_dev->ctl_name);
+}
+
+static irqreturn_t mv64x60_cpu_isr(int irq, void *dev_id)
+{
+ struct edac_device_ctl_info *edac_dev = dev_id;
+ struct mv64x60_cpu_pdata *pdata = edac_dev->pvt_info;
+ u32 cause;
+
+ cause = in_le32(pdata->cpu_vbase[1] + MV64x60_CPU_ERR_CAUSE) &
+ MV64x60_CPU_CAUSE_MASK;
+ if (!cause)
+ return IRQ_NONE;
+
+ mv64x60_cpu_check(edac_dev);
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit mv64x60_cpu_err_probe(struct platform_device *pdev)
+{
+ struct edac_device_ctl_info *edac_dev;
+ struct resource *r;
+ struct mv64x60_cpu_pdata *pdata;
+ int res = 0;
+
+ if (!devres_open_group(&pdev->dev, mv64x60_cpu_err_probe, GFP_KERNEL))
+ return -ENOMEM;
+
+ edac_dev = edac_device_alloc_ctl_info(sizeof(*pdata),
+ "cpu", 1, NULL, 0, 0, NULL, 0,
+ edac_dev_idx);
+ if (!edac_dev) {
+ devres_release_group(&pdev->dev, mv64x60_cpu_err_probe);
+ return -ENOMEM;
+ }
+
+ pdata = edac_dev->pvt_info;
+ pdata->name = "mv64x60_cpu_err";
+ pdata->irq = NO_IRQ;
+ edac_dev->dev = &pdev->dev;
+ platform_set_drvdata(pdev, edac_dev);
+ edac_dev->dev_name = pdev->dev.bus_id;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r) {
+ printk(KERN_ERR "%s: Unable to get resource for "
+ "CPU err regs\n", __func__);
+ res = -ENOENT;
+ goto err;
+ }
+
+ if (!devm_request_mem_region(&pdev->dev,
+ r->start,
+ r->end - r->start + 1,
+ pdata->name)) {
+ printk(KERN_ERR "%s: Error while requesting mem region\n",
+ __func__);
+ res = -EBUSY;
+ goto err;
+ }
+
+ pdata->cpu_vbase[0] = devm_ioremap(&pdev->dev,
+ r->start,
+ r->end - r->start + 1);
+ if (!pdata->cpu_vbase[0]) {
+ printk(KERN_ERR "%s: Unable to setup CPU err regs\n", __func__);
+ res = -ENOMEM;
+ goto err;
+ }
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!r) {
+ printk(KERN_ERR "%s: Unable to get resource for "
+ "CPU err regs\n", __func__);
+ res = -ENOENT;
+ goto err;
+ }
+
+ if (!devm_request_mem_region(&pdev->dev,
+ r->start,
+ r->end - r->start + 1,
+ pdata->name)) {
+ printk(KERN_ERR "%s: Error while requesting mem region\n",
+ __func__);
+ res = -EBUSY;
+ goto err;
+ }
+
+ pdata->cpu_vbase[1] = devm_ioremap(&pdev->dev,
+ r->start,
+ r->end - r->start + 1);
+ if (!pdata->cpu_vbase[1]) {
+ printk(KERN_ERR "%s: Unable to setup CPU err regs\n", __func__);
+ res = -ENOMEM;
+ goto err;
+ }
+
+ /* setup CPU err registers */
+ out_le32(pdata->cpu_vbase[1] + MV64x60_CPU_ERR_CAUSE, 0);
+ out_le32(pdata->cpu_vbase[1] + MV64x60_CPU_ERR_MASK, 0);
+ out_le32(pdata->cpu_vbase[1] + MV64x60_CPU_ERR_MASK, 0x000000ff);
+
+ edac_dev->mod_name = EDAC_MOD_STR;
+ edac_dev->ctl_name = pdata->name;
+ if (edac_op_state == EDAC_OPSTATE_POLL)
+ edac_dev->edac_check = mv64x60_cpu_check;
+
+ pdata->edac_idx = edac_dev_idx++;
+
+ if (edac_device_add_device(edac_dev) > 0) {
+ debugf3("%s(): failed edac_device_add_device()\n", __func__);
+ goto err;
+ }
+
+ if (edac_op_state == EDAC_OPSTATE_INT) {
+ pdata->irq = platform_get_irq(pdev, 0);
+ res = devm_request_irq(&pdev->dev,
+ pdata->irq,
+ mv64x60_cpu_isr,
+ IRQF_DISABLED,
+ "[EDAC] CPU err",
+ edac_dev);
+ if (res < 0) {
+ printk(KERN_ERR
+ "%s: Unable to request irq %d for MV64x60 "
+ "CPU ERR\n", __func__, pdata->irq);
+ res = -ENODEV;
+ goto err2;
+ }
+
+ printk(KERN_INFO EDAC_MOD_STR
+ " acquired irq %d for CPU Err\n", pdata->irq);
+ }
+
+ devres_remove_group(&pdev->dev, mv64x60_cpu_err_probe);
+
+ /* get this far and it's successful */
+ debugf3("%s(): success\n", __func__);
+
+ return 0;
+
+err2:
+ edac_device_del_device(&pdev->dev);
+err:
+ devres_release_group(&pdev->dev, mv64x60_cpu_err_probe);
+ edac_device_free_ctl_info(edac_dev);
+ return res;
+}
+
+static int mv64x60_cpu_err_remove(struct platform_device *pdev)
+{
+ struct edac_device_ctl_info *edac_dev = platform_get_drvdata(pdev);
+
+ debugf0("%s()\n", __func__);
+
+ edac_device_del_device(&pdev->dev);
+ edac_device_free_ctl_info(edac_dev);
+ return 0;
+}
+
+static struct platform_driver mv64x60_cpu_err_driver = {
+ .probe = mv64x60_cpu_err_probe,
+ .remove = mv64x60_cpu_err_remove,
+ .driver = {
+ .name = "mv64x60_cpu_err",
+ }
+};
+
+/*********************** DRAM err device **********************************/
+
+static void mv64x60_mc_check(struct mem_ctl_info *mci)
+{
+ struct mv64x60_mc_pdata *pdata = mci->pvt_info;
+ u32 reg;
+ u32 err_addr;
+ u32 sdram_ecc;
+ u32 comp_ecc;
+ u32 syndrome;
+
+ reg = in_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ADDR);
+ if (!reg)
+ return;
+
+ err_addr = reg & ~0x3;
+ sdram_ecc = in_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ECC_RCVD);
+ comp_ecc = in_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ECC_CALC);
+ syndrome = sdram_ecc ^ comp_ecc;
+
+ /* first bit clear in ECC Err Reg, 1 bit error, correctable by HW */
+ if (!(reg & 0x1))
+ edac_mc_handle_ce(mci, err_addr >> PAGE_SHIFT,
+ err_addr & PAGE_MASK, syndrome, 0, 0,
+ mci->ctl_name);
+ else /* 2 bit error, UE */
+ edac_mc_handle_ue(mci, err_addr >> PAGE_SHIFT,
+ err_addr & PAGE_MASK, 0, mci->ctl_name);
+
+ /* clear the error */
+ out_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ADDR, 0);
+}
+
+static irqreturn_t mv64x60_mc_isr(int irq, void *dev_id)
+{
+ struct mem_ctl_info *mci = dev_id;
+ struct mv64x60_mc_pdata *pdata = mci->pvt_info;
+ u32 reg;
+
+ reg = in_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ADDR);
+ if (!reg)
+ return IRQ_NONE;
+
+ /* writing 0's to the ECC err addr in check function clears irq */
+ mv64x60_mc_check(mci);
+
+ return IRQ_HANDLED;
+}
+
+static void get_total_mem(struct mv64x60_mc_pdata *pdata)
+{
+ struct device_node *np = NULL;
+ const unsigned int *reg;
+
+ np = of_find_node_by_type(NULL, "memory");
+ if (!np)
+ return;
+
+ reg = get_property(np, "reg", NULL);
+
+ pdata->total_mem = reg[1];
+}
+
+static void mv64x60_init_csrows(struct mem_ctl_info *mci,
+ struct mv64x60_mc_pdata *pdata)
+{
+ struct csrow_info *csrow;
+ u32 devtype;
+ u32 ctl;
+
+ get_total_mem(pdata);
+
+ ctl = in_le32(pdata->mc_vbase + MV64X60_SDRAM_CONFIG);
+
+ csrow = &mci->csrows[0];
+ csrow->first_page = 0;
+ csrow->nr_pages = pdata->total_mem >> PAGE_SHIFT;
+ csrow->last_page = csrow->first_page + csrow->nr_pages - 1;
+ csrow->grain = 8;
+
+ csrow->mtype = (ctl & MV64X60_SDRAM_REGISTERED) ? MEM_RDDR : MEM_DDR;
+
+ devtype = (ctl >> 20) & 0x3;
+ switch (devtype) {
+ case 0x0:
+ csrow->dtype = DEV_X32;
+ break;
+ case 0x2: /* could be X8 too, but no way to tell */
+ csrow->dtype = DEV_X16;
+ break;
+ case 0x3:
+ csrow->dtype = DEV_X4;
+ break;
+ default:
+ csrow->dtype = DEV_UNKNOWN;
+ break;
+ }
+
+ csrow->edac_mode = EDAC_SECDED;
+}
+
+static int __devinit mv64x60_mc_err_probe(struct platform_device *pdev)
+{
+ struct mem_ctl_info *mci;
+ struct mv64x60_mc_pdata *pdata;
+ struct resource *r;
+ u32 ctl;
+ int res = 0;
+
+ if (!devres_open_group(&pdev->dev, mv64x60_mc_err_probe, GFP_KERNEL))
+ return -ENOMEM;
+
+ mci = edac_mc_alloc(sizeof(struct mv64x60_mc_pdata), 1, 1, edac_mc_idx);
+ if (!mci) {
+ printk(KERN_ERR "%s: No memory for CPU err\n", __func__);
+ devres_release_group(&pdev->dev, mv64x60_mc_err_probe);
+ return -ENOMEM;
+ }
+
+ pdata = mci->pvt_info;
+ mci->dev = &pdev->dev;
+ platform_set_drvdata(pdev, mci);
+ pdata->name = "mv64x60_mc_err";
+ pdata->irq = NO_IRQ;
+ mci->dev_name = pdev->dev.bus_id;
+ pdata->edac_idx = edac_mc_idx++;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r) {
+ printk(KERN_ERR "%s: Unable to get resource for "
+ "MC err regs\n", __func__);
+ res = -ENOENT;
+ goto err;
+ }
+
+ if (!devm_request_mem_region(&pdev->dev,
+ r->start,
+ r->end - r->start + 1,
+ pdata->name)) {
+ printk(KERN_ERR "%s: Error while requesting mem region\n",
+ __func__);
+ res = -EBUSY;
+ goto err;
+ }
+
+ pdata->mc_vbase = devm_ioremap(&pdev->dev,
+ r->start,
+ r->end - r->start + 1);
+ if (!pdata->mc_vbase) {
+ printk(KERN_ERR "%s: Unable to setup MC err regs\n", __func__);
+ res = -ENOMEM;
+ goto err;
+ }
+
+ ctl = in_le32(pdata->mc_vbase + MV64X60_SDRAM_CONFIG);
+ if (!(ctl & MV64X60_SDRAM_ECC)) {
+ /* Non-ECC RAM? */
+ printk(KERN_WARNING "%s: No ECC DIMMs discovered\n", __func__);
+ res = -ENODEV;
+ goto err2;
+ }
+
+ debugf3("%s(): init mci\n", __func__);
+ mci->mtype_cap = MEM_FLAG_RDDR | MEM_FLAG_DDR;
+ mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED;
+ mci->edac_cap = EDAC_FLAG_SECDED;
+ mci->mod_name = EDAC_MOD_STR;
+ mci->mod_ver = MV64x60_REVISION;
+ mci->ctl_name = mv64x60_ctl_name;
+
+ if (edac_op_state == EDAC_OPSTATE_POLL)
+ mci->edac_check = mv64x60_mc_check;
+
+ mci->ctl_page_to_phys = NULL;
+
+ mci->scrub_mode = SCRUB_SW_SRC;
+
+ mv64x60_init_csrows(mci, pdata);
+
+ /* setup MC registers */
+ out_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ADDR, 0);
+ ctl = in_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ECC_CNTL);
+ ctl = (ctl & 0xff00ffff) | 0x10000;
+ out_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ECC_CNTL, ctl);
+
+ if (edac_mc_add_mc(mci)) {
+ debugf3("%s(): failed edac_mc_add_mc()\n", __func__);
+ goto err;
+ }
+
+ if (edac_op_state == EDAC_OPSTATE_INT) {
+ /* acquire interrupt that reports errors */
+ pdata->irq = platform_get_irq(pdev, 0);
+ res = devm_request_irq(&pdev->dev,
+ pdata->irq,
+ mv64x60_mc_isr,
+ IRQF_DISABLED,
+ "[EDAC] MC err",
+ mci);
+ if (res < 0) {
+ printk(KERN_ERR "%s: Unable to request irq %d for "
+ "MV64x60 DRAM ERR\n", __func__, pdata->irq);
+ res = -ENODEV;
+ goto err2;
+ }
+
+ printk(KERN_INFO EDAC_MOD_STR " acquired irq %d for MC Err\n",
+ pdata->irq);
+ }
+
+ /* get this far and it's successful */
+ debugf3("%s(): success\n", __func__);
+
+ return 0;
+
+err2:
+ edac_mc_del_mc(&pdev->dev);
+err:
+ devres_release_group(&pdev->dev, mv64x60_mc_err_probe);
+ edac_mc_free(mci);
+ return res;
+}
+
+static int mv64x60_mc_err_remove(struct platform_device *pdev)
+{
+ struct mem_ctl_info *mci = platform_get_drvdata(pdev);
+
+ debugf0("%s()\n", __func__);
+
+ edac_mc_del_mc(&pdev->dev);
+ edac_mc_free(mci);
+ return 0;
+}
+
+static struct platform_driver mv64x60_mc_err_driver = {
+ .probe = mv64x60_mc_err_probe,
+ .remove = mv64x60_mc_err_remove,
+ .driver = {
+ .name = "mv64x60_mc_err",
+ }
+};
+
+static int __init mv64x60_edac_init(void)
+{
+ int ret = 0;
+
+ printk(KERN_INFO "Marvell MV64x60 EDAC driver " MV64x60_REVISION "\n");
+ printk(KERN_INFO "\t(C) 2006-2007 MontaVista Software\n");
+ /* make sure error reporting method is sane */
+ switch (edac_op_state) {
+ case EDAC_OPSTATE_POLL:
+ case EDAC_OPSTATE_INT:
+ break;
+ default:
+ edac_op_state = EDAC_OPSTATE_INT;
+ break;
+ }
+
+ ret = platform_driver_register(&mv64x60_mc_err_driver);
+ if (ret)
+ printk(KERN_WARNING EDAC_MOD_STR "MC err failed to register\n");
+
+ ret = platform_driver_register(&mv64x60_cpu_err_driver);
+ if (ret)
+ printk(KERN_WARNING EDAC_MOD_STR
+ "CPU err failed to register\n");
+
+ ret = platform_driver_register(&mv64x60_sram_err_driver);
+ if (ret)
+ printk(KERN_WARNING EDAC_MOD_STR
+ "SRAM err failed to register\n");
+
+#ifdef CONFIG_PCI
+ ret = platform_driver_register(&mv64x60_pci_err_driver);
+ if (ret)
+ printk(KERN_WARNING EDAC_MOD_STR
+ "PCI err failed to register\n");
+#endif
+
+ return ret;
+}
+module_init(mv64x60_edac_init);
+
+static void __exit mv64x60_edac_exit(void)
+{
+#ifdef CONFIG_PCI
+ platform_driver_unregister(&mv64x60_pci_err_driver);
+#endif
+ platform_driver_unregister(&mv64x60_sram_err_driver);
+ platform_driver_unregister(&mv64x60_cpu_err_driver);
+ platform_driver_unregister(&mv64x60_mc_err_driver);
+}
+module_exit(mv64x60_edac_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Montavista Software, Inc.");
+module_param(edac_op_state, int, 0444);
+MODULE_PARM_DESC(edac_op_state,
+ "EDAC Error Reporting state: 0=Poll, 2=Interrupt");
diff --git a/drivers/edac/mv64x60_edac.h b/drivers/edac/mv64x60_edac.h
new file mode 100644
index 000000000000..e042e2daa8f4
--- /dev/null
+++ b/drivers/edac/mv64x60_edac.h
@@ -0,0 +1,114 @@
+/*
+ * EDAC defs for Marvell MV64x60 bridge chip
+ *
+ * Author: Dave Jiang <djiang@mvista.com>
+ *
+ * 2007 (c) MontaVista Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ *
+ */
+#ifndef _MV64X60_EDAC_H_
+#define _MV64X60_EDAC_H_
+
+#define MV64x60_REVISION " Ver: 2.0.0 " __DATE__
+#define EDAC_MOD_STR "MV64x60_edac"
+
+#define mv64x60_printk(level, fmt, arg...) \
+ edac_printk(level, "MV64x60", fmt, ##arg)
+
+#define mv64x60_mc_printk(mci, level, fmt, arg...) \
+ edac_mc_chipset_printk(mci, level, "MV64x60", fmt, ##arg)
+
+/* CPU Error Report Registers */
+#define MV64x60_CPU_ERR_ADDR_LO 0x00 /* 0x0070 */
+#define MV64x60_CPU_ERR_ADDR_HI 0x08 /* 0x0078 */
+#define MV64x60_CPU_ERR_DATA_LO 0x00 /* 0x0128 */
+#define MV64x60_CPU_ERR_DATA_HI 0x08 /* 0x0130 */
+#define MV64x60_CPU_ERR_PARITY 0x10 /* 0x0138 */
+#define MV64x60_CPU_ERR_CAUSE 0x18 /* 0x0140 */
+#define MV64x60_CPU_ERR_MASK 0x20 /* 0x0148 */
+
+#define MV64x60_CPU_CAUSE_MASK 0x07ffffff
+
+/* SRAM Error Report Registers */
+#define MV64X60_SRAM_ERR_CAUSE 0x08 /* 0x0388 */
+#define MV64X60_SRAM_ERR_ADDR_LO 0x10 /* 0x0390 */
+#define MV64X60_SRAM_ERR_ADDR_HI 0x78 /* 0x03f8 */
+#define MV64X60_SRAM_ERR_DATA_LO 0x18 /* 0x0398 */
+#define MV64X60_SRAM_ERR_DATA_HI 0x20 /* 0x03a0 */
+#define MV64X60_SRAM_ERR_PARITY 0x28 /* 0x03a8 */
+
+/* SDRAM Controller Registers */
+#define MV64X60_SDRAM_CONFIG 0x00 /* 0x1400 */
+#define MV64X60_SDRAM_ERR_DATA_HI 0x40 /* 0x1440 */
+#define MV64X60_SDRAM_ERR_DATA_LO 0x44 /* 0x1444 */
+#define MV64X60_SDRAM_ERR_ECC_RCVD 0x48 /* 0x1448 */
+#define MV64X60_SDRAM_ERR_ECC_CALC 0x4c /* 0x144c */
+#define MV64X60_SDRAM_ERR_ADDR 0x50 /* 0x1450 */
+#define MV64X60_SDRAM_ERR_ECC_CNTL 0x54 /* 0x1454 */
+#define MV64X60_SDRAM_ERR_ECC_ERR_CNT 0x58 /* 0x1458 */
+
+#define MV64X60_SDRAM_REGISTERED 0x20000
+#define MV64X60_SDRAM_ECC 0x40000
+
+#ifdef CONFIG_PCI
+/*
+ * Bit 0 of MV64x60_PCIx_ERR_MASK does not exist on the 64360 and because of
+ * errata FEr-#11 and FEr-##16 for the 64460, it should be 0 on that chip as
+ * well. IOW, don't set bit 0.
+ */
+#define MV64X60_PCIx_ERR_MASK_VAL 0x00a50c24
+
+/* Register offsets from PCIx error address low register */
+#define MV64X60_PCI_ERROR_ADDR_LO 0x00
+#define MV64X60_PCI_ERROR_ADDR_HI 0x04
+#define MV64X60_PCI_ERROR_ATTR 0x08
+#define MV64X60_PCI_ERROR_CMD 0x10
+#define MV64X60_PCI_ERROR_CAUSE 0x18
+#define MV64X60_PCI_ERROR_MASK 0x1c
+
+#define MV64X60_PCI_ERR_SWrPerr 0x0002
+#define MV64X60_PCI_ERR_SRdPerr 0x0004
+#define MV64X60_PCI_ERR_MWrPerr 0x0020
+#define MV64X60_PCI_ERR_MRdPerr 0x0040
+
+#define MV64X60_PCI_PE_MASK (MV64X60_PCI_ERR_SWrPerr | \
+ MV64X60_PCI_ERR_SRdPerr | \
+ MV64X60_PCI_ERR_MWrPerr | \
+ MV64X60_PCI_ERR_MRdPerr)
+
+struct mv64x60_pci_pdata {
+ int pci_hose;
+ void __iomem *pci_vbase;
+ char *name;
+ int irq;
+ int edac_idx;
+};
+
+#endif /* CONFIG_PCI */
+
+struct mv64x60_mc_pdata {
+ void __iomem *mc_vbase;
+ int total_mem;
+ char *name;
+ int irq;
+ int edac_idx;
+};
+
+struct mv64x60_cpu_pdata {
+ void __iomem *cpu_vbase[2];
+ char *name;
+ int irq;
+ int edac_idx;
+};
+
+struct mv64x60_sram_pdata {
+ void __iomem *sram_vbase;
+ char *name;
+ int irq;
+ int edac_idx;
+};
+
+#endif