aboutsummaryrefslogtreecommitdiff
path: root/drivers/edac
diff options
context:
space:
mode:
authorChris Zankel <chris@zankel.net>2009-04-03 02:29:05 -0700
committerChris Zankel <chris@zankel.net>2009-04-03 02:29:05 -0700
commit65127d28e312bb6b38ce84a7bb71d762ef63ad4c (patch)
treed5fdf52a2d0731f7fab0ce0ed394faac50b04fbc /drivers/edac
parentb8bb76713ec50df2f11efee386e16f93d51e1076 (diff)
parent8fe74cf053de7ad2124a894996f84fa890a81093 (diff)
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6 into merge
Diffstat (limited to 'drivers/edac')
-rw-r--r--drivers/edac/Kconfig36
-rw-r--r--drivers/edac/Makefile2
-rw-r--r--drivers/edac/amd8111_edac.c595
-rw-r--r--drivers/edac/amd8111_edac.h130
-rw-r--r--drivers/edac/amd8131_edac.c379
-rw-r--r--drivers/edac/amd8131_edac.h119
-rw-r--r--drivers/edac/edac_core.h16
-rw-r--r--drivers/edac/edac_pci.c14
-rw-r--r--drivers/edac/ppc4xx_edac.c1448
-rw-r--r--drivers/edac/ppc4xx_edac.h172
10 files changed, 2906 insertions, 5 deletions
diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig
index eee47fd16d7..e5f5c5a8ba6 100644
--- a/drivers/edac/Kconfig
+++ b/drivers/edac/Kconfig
@@ -1,13 +1,12 @@
#
# EDAC Kconfig
-# Copyright (c) 2003 Linux Networx
+# Copyright (c) 2008 Doug Thompson www.softwarebitmaker.com
# Licensed and distributed under the GPL
#
menuconfig EDAC
- bool "EDAC - error detection and reporting (EXPERIMENTAL)"
+ bool "EDAC - error detection and reporting"
depends on HAS_IOMEM
- depends on EXPERIMENTAL
depends on X86 || PPC
help
EDAC is designed to report errors in the core system.
@@ -40,6 +39,14 @@ config EDAC_DEBUG
there're four debug levels (x=0,1,2,3 from low to high).
Usually you should select 'N'.
+config EDAC_DEBUG_VERBOSE
+ bool "More verbose debugging"
+ depends on EDAC_DEBUG
+ help
+ This option makes debugging information more verbose.
+ Source file name and line number where debugging message
+ printed will be added to debugging message.
+
config EDAC_MM_EDAC
tristate "Main Memory EDAC (Error Detection And Correction) reporting"
default y
@@ -174,4 +181,27 @@ config EDAC_CELL
Cell Broadband Engine internal memory controller
on platform without a hypervisor
+config EDAC_PPC4XX
+ tristate "PPC4xx IBM DDR2 Memory Controller"
+ depends on EDAC_MM_EDAC && 4xx
+ help
+ This enables support for EDAC on the ECC memory used
+ with the IBM DDR2 memory controller found in various
+ PowerPC 4xx embedded processors such as the 405EX[r],
+ 440SP, 440SPe, 460EX, 460GT and 460SX.
+
+config EDAC_AMD8131
+ tristate "AMD8131 HyperTransport PCI-X Tunnel"
+ depends on EDAC_MM_EDAC && PCI
+ help
+ Support for error detection and correction on the
+ AMD8131 HyperTransport PCI-X Tunnel chip.
+
+config EDAC_AMD8111
+ tristate "AMD8111 HyperTransport I/O Hub"
+ depends on EDAC_MM_EDAC && PCI
+ help
+ Support for error detection and correction on the
+ AMD8111 HyperTransport I/O Hub chip.
+
endif # EDAC
diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile
index b75196927de..a5fdcf02f59 100644
--- a/drivers/edac/Makefile
+++ b/drivers/edac/Makefile
@@ -34,4 +34,4 @@ 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
-
+obj-$(CONFIG_EDAC_PPC4XX) += ppc4xx_edac.o
diff --git a/drivers/edac/amd8111_edac.c b/drivers/edac/amd8111_edac.c
new file mode 100644
index 00000000000..61469218112
--- /dev/null
+++ b/drivers/edac/amd8111_edac.c
@@ -0,0 +1,595 @@
+/*
+ * amd8111_edac.c, AMD8111 Hyper Transport chip EDAC kernel module
+ *
+ * Copyright (c) 2008 Wind River Systems, Inc.
+ *
+ * Authors: Cao Qingtao <qingtao.cao@windriver.com>
+ * Benjamin Walsh <benjamin.walsh@windriver.com>
+ * Hu Yongqi <yongqi.hu@windriver.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/bitops.h>
+#include <linux/edac.h>
+#include <linux/pci_ids.h>
+#include <asm/io.h>
+
+#include "edac_core.h"
+#include "edac_module.h"
+#include "amd8111_edac.h"
+
+#define AMD8111_EDAC_REVISION " Ver: 1.0.0 " __DATE__
+#define AMD8111_EDAC_MOD_STR "amd8111_edac"
+
+#define PCI_DEVICE_ID_AMD_8111_PCI 0x7460
+static int edac_dev_idx;
+
+enum amd8111_edac_devs {
+ LPC_BRIDGE = 0,
+};
+
+enum amd8111_edac_pcis {
+ PCI_BRIDGE = 0,
+};
+
+/* Wrapper functions for accessing PCI configuration space */
+static int edac_pci_read_dword(struct pci_dev *dev, int reg, u32 *val32)
+{
+ int ret;
+
+ ret = pci_read_config_dword(dev, reg, val32);
+ if (ret != 0)
+ printk(KERN_ERR AMD8111_EDAC_MOD_STR
+ " PCI Access Read Error at 0x%x\n", reg);
+
+ return ret;
+}
+
+static void edac_pci_read_byte(struct pci_dev *dev, int reg, u8 *val8)
+{
+ int ret;
+
+ ret = pci_read_config_byte(dev, reg, val8);
+ if (ret != 0)
+ printk(KERN_ERR AMD8111_EDAC_MOD_STR
+ " PCI Access Read Error at 0x%x\n", reg);
+}
+
+static void edac_pci_write_dword(struct pci_dev *dev, int reg, u32 val32)
+{
+ int ret;
+
+ ret = pci_write_config_dword(dev, reg, val32);
+ if (ret != 0)
+ printk(KERN_ERR AMD8111_EDAC_MOD_STR
+ " PCI Access Write Error at 0x%x\n", reg);
+}
+
+static void edac_pci_write_byte(struct pci_dev *dev, int reg, u8 val8)
+{
+ int ret;
+
+ ret = pci_write_config_byte(dev, reg, val8);
+ if (ret != 0)
+ printk(KERN_ERR AMD8111_EDAC_MOD_STR
+ " PCI Access Write Error at 0x%x\n", reg);
+}
+
+/*
+ * device-specific methods for amd8111 PCI Bridge Controller
+ *
+ * Error Reporting and Handling for amd8111 chipset could be found
+ * in its datasheet 3.1.2 section, P37
+ */
+static void amd8111_pci_bridge_init(struct amd8111_pci_info *pci_info)
+{
+ u32 val32;
+ struct pci_dev *dev = pci_info->dev;
+
+ /* First clear error detection flags on the host interface */
+
+ /* Clear SSE/SMA/STA flags in the global status register*/
+ edac_pci_read_dword(dev, REG_PCI_STSCMD, &val32);
+ if (val32 & PCI_STSCMD_CLEAR_MASK)
+ edac_pci_write_dword(dev, REG_PCI_STSCMD, val32);
+
+ /* Clear CRC and Link Fail flags in HT Link Control reg */
+ edac_pci_read_dword(dev, REG_HT_LINK, &val32);
+ if (val32 & HT_LINK_CLEAR_MASK)
+ edac_pci_write_dword(dev, REG_HT_LINK, val32);
+
+ /* Second clear all fault on the secondary interface */
+
+ /* Clear error flags in the memory-base limit reg. */
+ edac_pci_read_dword(dev, REG_MEM_LIM, &val32);
+ if (val32 & MEM_LIMIT_CLEAR_MASK)
+ edac_pci_write_dword(dev, REG_MEM_LIM, val32);
+
+ /* Clear Discard Timer Expired flag in Interrupt/Bridge Control reg */
+ edac_pci_read_dword(dev, REG_PCI_INTBRG_CTRL, &val32);
+ if (val32 & PCI_INTBRG_CTRL_CLEAR_MASK)
+ edac_pci_write_dword(dev, REG_PCI_INTBRG_CTRL, val32);
+
+ /* Last enable error detections */
+ if (edac_op_state == EDAC_OPSTATE_POLL) {
+ /* Enable System Error reporting in global status register */
+ edac_pci_read_dword(dev, REG_PCI_STSCMD, &val32);
+ val32 |= PCI_STSCMD_SERREN;
+ edac_pci_write_dword(dev, REG_PCI_STSCMD, val32);
+
+ /* Enable CRC Sync flood packets to HyperTransport Link */
+ edac_pci_read_dword(dev, REG_HT_LINK, &val32);
+ val32 |= HT_LINK_CRCFEN;
+ edac_pci_write_dword(dev, REG_HT_LINK, val32);
+
+ /* Enable SSE reporting etc in Interrupt control reg */
+ edac_pci_read_dword(dev, REG_PCI_INTBRG_CTRL, &val32);
+ val32 |= PCI_INTBRG_CTRL_POLL_MASK;
+ edac_pci_write_dword(dev, REG_PCI_INTBRG_CTRL, val32);
+ }
+}
+
+static void amd8111_pci_bridge_exit(struct amd8111_pci_info *pci_info)
+{
+ u32 val32;
+ struct pci_dev *dev = pci_info->dev;
+
+ if (edac_op_state == EDAC_OPSTATE_POLL) {
+ /* Disable System Error reporting */
+ edac_pci_read_dword(dev, REG_PCI_STSCMD, &val32);
+ val32 &= ~PCI_STSCMD_SERREN;
+ edac_pci_write_dword(dev, REG_PCI_STSCMD, val32);
+
+ /* Disable CRC flood packets */
+ edac_pci_read_dword(dev, REG_HT_LINK, &val32);
+ val32 &= ~HT_LINK_CRCFEN;
+ edac_pci_write_dword(dev, REG_HT_LINK, val32);
+
+ /* Disable DTSERREN/MARSP/SERREN in Interrupt Control reg */
+ edac_pci_read_dword(dev, REG_PCI_INTBRG_CTRL, &val32);
+ val32 &= ~PCI_INTBRG_CTRL_POLL_MASK;
+ edac_pci_write_dword(dev, REG_PCI_INTBRG_CTRL, val32);
+ }
+}
+
+static void amd8111_pci_bridge_check(struct edac_pci_ctl_info *edac_dev)
+{
+ struct amd8111_pci_info *pci_info = edac_dev->pvt_info;
+ struct pci_dev *dev = pci_info->dev;
+ u32 val32;
+
+ /* Check out PCI Bridge Status and Command Register */
+ edac_pci_read_dword(dev, REG_PCI_STSCMD, &val32);
+ if (val32 & PCI_STSCMD_CLEAR_MASK) {
+ printk(KERN_INFO "Error(s) in PCI bridge status and command"
+ "register on device %s\n", pci_info->ctl_name);
+ printk(KERN_INFO "SSE: %d, RMA: %d, RTA: %d\n",
+ (val32 & PCI_STSCMD_SSE) != 0,
+ (val32 & PCI_STSCMD_RMA) != 0,
+ (val32 & PCI_STSCMD_RTA) != 0);
+
+ val32 |= PCI_STSCMD_CLEAR_MASK;
+ edac_pci_write_dword(dev, REG_PCI_STSCMD, val32);
+
+ edac_pci_handle_npe(edac_dev, edac_dev->ctl_name);
+ }
+
+ /* Check out HyperTransport Link Control Register */
+ edac_pci_read_dword(dev, REG_HT_LINK, &val32);
+ if (val32 & HT_LINK_LKFAIL) {
+ printk(KERN_INFO "Error(s) in hypertransport link control"
+ "register on device %s\n", pci_info->ctl_name);
+ printk(KERN_INFO "LKFAIL: %d\n",
+ (val32 & HT_LINK_LKFAIL) != 0);
+
+ val32 |= HT_LINK_LKFAIL;
+ edac_pci_write_dword(dev, REG_HT_LINK, val32);
+
+ edac_pci_handle_npe(edac_dev, edac_dev->ctl_name);
+ }
+
+ /* Check out PCI Interrupt and Bridge Control Register */
+ edac_pci_read_dword(dev, REG_PCI_INTBRG_CTRL, &val32);
+ if (val32 & PCI_INTBRG_CTRL_DTSTAT) {
+ printk(KERN_INFO "Error(s) in PCI interrupt and bridge control"
+ "register on device %s\n", pci_info->ctl_name);
+ printk(KERN_INFO "DTSTAT: %d\n",
+ (val32 & PCI_INTBRG_CTRL_DTSTAT) != 0);
+
+ val32 |= PCI_INTBRG_CTRL_DTSTAT;
+ edac_pci_write_dword(dev, REG_PCI_INTBRG_CTRL, val32);
+
+ edac_pci_handle_npe(edac_dev, edac_dev->ctl_name);
+ }
+
+ /* Check out PCI Bridge Memory Base-Limit Register */
+ edac_pci_read_dword(dev, REG_MEM_LIM, &val32);
+ if (val32 & MEM_LIMIT_CLEAR_MASK) {
+ printk(KERN_INFO
+ "Error(s) in mem limit register on %s device\n",
+ pci_info->ctl_name);
+ printk(KERN_INFO "DPE: %d, RSE: %d, RMA: %d\n"
+ "RTA: %d, STA: %d, MDPE: %d\n",
+ (val32 & MEM_LIMIT_DPE) != 0,
+ (val32 & MEM_LIMIT_RSE) != 0,
+ (val32 & MEM_LIMIT_RMA) != 0,
+ (val32 & MEM_LIMIT_RTA) != 0,
+ (val32 & MEM_LIMIT_STA) != 0,
+ (val32 & MEM_LIMIT_MDPE) != 0);
+
+ val32 |= MEM_LIMIT_CLEAR_MASK;
+ edac_pci_write_dword(dev, REG_MEM_LIM, val32);
+
+ edac_pci_handle_npe(edac_dev, edac_dev->ctl_name);
+ }
+}
+
+static struct resource *legacy_io_res;
+static int at_compat_reg_broken;
+#define LEGACY_NR_PORTS 1
+
+/* device-specific methods for amd8111 LPC Bridge device */
+static void amd8111_lpc_bridge_init(struct amd8111_dev_info *dev_info)
+{
+ u8 val8;
+ struct pci_dev *dev = dev_info->dev;
+
+ /* First clear REG_AT_COMPAT[SERR, IOCHK] if necessary */
+ legacy_io_res = request_region(REG_AT_COMPAT, LEGACY_NR_PORTS,
+ AMD8111_EDAC_MOD_STR);
+ if (!legacy_io_res)
+ printk(KERN_INFO "%s: failed to request legacy I/O region "
+ "start %d, len %d\n", __func__,
+ REG_AT_COMPAT, LEGACY_NR_PORTS);
+ else {
+ val8 = __do_inb(REG_AT_COMPAT);
+ if (val8 == 0xff) { /* buggy port */
+ printk(KERN_INFO "%s: port %d is buggy, not supported"
+ " by hardware?\n", __func__, REG_AT_COMPAT);
+ at_compat_reg_broken = 1;
+ release_region(REG_AT_COMPAT, LEGACY_NR_PORTS);
+ legacy_io_res = NULL;
+ } else {
+ u8 out8 = 0;
+ if (val8 & AT_COMPAT_SERR)
+ out8 = AT_COMPAT_CLRSERR;
+ if (val8 & AT_COMPAT_IOCHK)
+ out8 |= AT_COMPAT_CLRIOCHK;
+ if (out8 > 0)
+ __do_outb(out8, REG_AT_COMPAT);
+ }
+ }
+
+ /* Second clear error flags on LPC bridge */
+ edac_pci_read_byte(dev, REG_IO_CTRL_1, &val8);
+ if (val8 & IO_CTRL_1_CLEAR_MASK)
+ edac_pci_write_byte(dev, REG_IO_CTRL_1, val8);
+}
+
+static void amd8111_lpc_bridge_exit(struct amd8111_dev_info *dev_info)
+{
+ if (legacy_io_res)
+ release_region(REG_AT_COMPAT, LEGACY_NR_PORTS);
+}
+
+static void amd8111_lpc_bridge_check(struct edac_device_ctl_info *edac_dev)
+{
+ struct amd8111_dev_info *dev_info = edac_dev->pvt_info;
+ struct pci_dev *dev = dev_info->dev;
+ u8 val8;
+
+ edac_pci_read_byte(dev, REG_IO_CTRL_1, &val8);
+ if (val8 & IO_CTRL_1_CLEAR_MASK) {
+ printk(KERN_INFO
+ "Error(s) in IO control register on %s device\n",
+ dev_info->ctl_name);
+ printk(KERN_INFO "LPC ERR: %d, PW2LPC: %d\n",
+ (val8 & IO_CTRL_1_LPC_ERR) != 0,
+ (val8 & IO_CTRL_1_PW2LPC) != 0);
+
+ val8 |= IO_CTRL_1_CLEAR_MASK;
+ edac_pci_write_byte(dev, REG_IO_CTRL_1, val8);
+
+ edac_device_handle_ue(edac_dev, 0, 0, edac_dev->ctl_name);
+ }
+
+ if (at_compat_reg_broken == 0) {
+ u8 out8 = 0;
+ val8 = __do_inb(REG_AT_COMPAT);
+ if (val8 & AT_COMPAT_SERR)
+ out8 = AT_COMPAT_CLRSERR;
+ if (val8 & AT_COMPAT_IOCHK)
+ out8 |= AT_COMPAT_CLRIOCHK;
+ if (out8 > 0) {
+ __do_outb(out8, REG_AT_COMPAT);
+ edac_device_handle_ue(edac_dev, 0, 0,
+ edac_dev->ctl_name);
+ }
+ }
+}
+
+/* General devices represented by edac_device_ctl_info */
+static struct amd8111_dev_info amd8111_devices[] = {
+ [LPC_BRIDGE] = {
+ .err_dev = PCI_DEVICE_ID_AMD_8111_LPC,
+ .ctl_name = "lpc",
+ .init = amd8111_lpc_bridge_init,
+ .exit = amd8111_lpc_bridge_exit,
+ .check = amd8111_lpc_bridge_check,
+ },
+ {0},
+};
+
+/* PCI controllers represented by edac_pci_ctl_info */
+static struct amd8111_pci_info amd8111_pcis[] = {
+ [PCI_BRIDGE] = {
+ .err_dev = PCI_DEVICE_ID_AMD_8111_PCI,
+ .ctl_name = "AMD8111_PCI_Controller",
+ .init = amd8111_pci_bridge_init,
+ .exit = amd8111_pci_bridge_exit,
+ .check = amd8111_pci_bridge_check,
+ },
+ {0},
+};
+
+static int amd8111_dev_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ struct amd8111_dev_info *dev_info = &amd8111_devices[id->driver_data];
+
+ dev_info->dev = pci_get_device(PCI_VENDOR_ID_AMD,
+ dev_info->err_dev, NULL);
+
+ if (!dev_info->dev) {
+ printk(KERN_ERR "EDAC device not found:"
+ "vendor %x, device %x, name %s\n",
+ PCI_VENDOR_ID_AMD, dev_info->err_dev,
+ dev_info->ctl_name);
+ return -ENODEV;
+ }
+
+ if (pci_enable_device(dev_info->dev)) {
+ pci_dev_put(dev_info->dev);
+ printk(KERN_ERR "failed to enable:"
+ "vendor %x, device %x, name %s\n",
+ PCI_VENDOR_ID_AMD, dev_info->err_dev,
+ dev_info->ctl_name);
+ return -ENODEV;
+ }
+
+ /*
+ * we do not allocate extra private structure for
+ * edac_device_ctl_info, but make use of existing
+ * one instead.
+ */
+ dev_info->edac_idx = edac_dev_idx++;
+ dev_info->edac_dev =
+ edac_device_alloc_ctl_info(0, dev_info->ctl_name, 1,
+ NULL, 0, 0,
+ NULL, 0, dev_info->edac_idx);
+ if (!dev_info->edac_dev)
+ return -ENOMEM;
+
+ dev_info->edac_dev->pvt_info = dev_info;
+ dev_info->edac_dev->dev = &dev_info->dev->dev;
+ dev_info->edac_dev->mod_name = AMD8111_EDAC_MOD_STR;
+ dev_info->edac_dev->ctl_name = dev_info->ctl_name;
+ dev_info->edac_dev->dev_name = dev_info->dev->dev.bus_id;
+
+ if (edac_op_state == EDAC_OPSTATE_POLL)
+ dev_info->edac_dev->edac_check = dev_info->check;
+
+ if (dev_info->init)
+ dev_info->init(dev_info);
+
+ if (edac_device_add_device(dev_info->edac_dev) > 0) {
+ printk(KERN_ERR "failed to add edac_dev for %s\n",
+ dev_info->ctl_name);
+ edac_device_free_ctl_info(dev_info->edac_dev);
+ return -ENODEV;
+ }
+
+ printk(KERN_INFO "added one edac_dev on AMD8111 "
+ "vendor %x, device %x, name %s\n",
+ PCI_VENDOR_ID_AMD, dev_info->err_dev,
+ dev_info->ctl_name);
+
+ return 0;
+}
+
+static void amd8111_dev_remove(struct pci_dev *dev)
+{
+ struct amd8111_dev_info *dev_info;
+
+ for (dev_info = amd8111_devices; dev_info->err_dev; dev_info++)
+ if (dev_info->dev->device == dev->device)
+ break;
+
+ if (!dev_info->err_dev) /* should never happen */
+ return;
+
+ if (dev_info->edac_dev) {
+ edac_device_del_device(dev_info->edac_dev->dev);
+ edac_device_free_ctl_info(dev_info->edac_dev);
+ }
+
+ if (dev_info->exit)
+ dev_info->exit(dev_info);
+
+ pci_dev_put(dev_info->dev);
+}
+
+static int amd8111_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ struct amd8111_pci_info *pci_info = &amd8111_pcis[id->driver_data];
+
+ pci_info->dev = pci_get_device(PCI_VENDOR_ID_AMD,
+ pci_info->err_dev, NULL);
+
+ if (!pci_info->dev) {
+ printk(KERN_ERR "EDAC device not found:"
+ "vendor %x, device %x, name %s\n",
+ PCI_VENDOR_ID_AMD, pci_info->err_dev,
+ pci_info->ctl_name);
+ return -ENODEV;
+ }
+
+ if (pci_enable_device(pci_info->dev)) {
+ pci_dev_put(pci_info->dev);
+ printk(KERN_ERR "failed to enable:"
+ "vendor %x, device %x, name %s\n",
+ PCI_VENDOR_ID_AMD, pci_info->err_dev,
+ pci_info->ctl_name);
+ return -ENODEV;
+ }
+
+ /*
+ * we do not allocate extra private structure for
+ * edac_pci_ctl_info, but make use of existing
+ * one instead.
+ */
+ pci_info->edac_idx = edac_pci_alloc_index();
+ pci_info->edac_dev = edac_pci_alloc_ctl_info(0, pci_info->ctl_name);
+ if (!pci_info->edac_dev)
+ return -ENOMEM;
+
+ pci_info->edac_dev->pvt_info = pci_info;
+ pci_info->edac_dev->dev = &pci_info->dev->dev;
+ pci_info->edac_dev->mod_name = AMD8111_EDAC_MOD_STR;
+ pci_info->edac_dev->ctl_name = pci_info->ctl_name;
+ pci_info->edac_dev->dev_name = pci_info->dev->dev.bus_id;
+
+ if (edac_op_state == EDAC_OPSTATE_POLL)
+ pci_info->edac_dev->edac_check = pci_info->check;
+
+ if (pci_info->init)
+ pci_info->init(pci_info);
+
+ if (edac_pci_add_device(pci_info->edac_dev, pci_info->edac_idx) > 0) {
+ printk(KERN_ERR "failed to add edac_pci for %s\n",
+ pci_info->ctl_name);
+ edac_pci_free_ctl_info(pci_info->edac_dev);
+ return -ENODEV;
+ }
+
+ printk(KERN_INFO "added one edac_pci on AMD8111 "
+ "vendor %x, device %x, name %s\n",
+ PCI_VENDOR_ID_AMD, pci_info->err_dev,
+ pci_info->ctl_name);
+
+ return 0;
+}
+
+static void amd8111_pci_remove(struct pci_dev *dev)
+{
+ struct amd8111_pci_info *pci_info;
+
+ for (pci_info = amd8111_pcis; pci_info->err_dev; pci_info++)
+ if (pci_info->dev->device == dev->device)
+ break;
+
+ if (!pci_info->err_dev) /* should never happen */
+ return;
+
+ if (pci_info->edac_dev) {
+ edac_pci_del_device(pci_info->edac_dev->dev);
+ edac_pci_free_ctl_info(pci_info->edac_dev);
+ }
+
+ if (pci_info->exit)
+ pci_info->exit(pci_info);
+
+ pci_dev_put(pci_info->dev);
+}
+
+/* PCI Device ID talbe for general EDAC device */
+static const struct pci_device_id amd8111_edac_dev_tbl[] = {
+ {
+ PCI_VEND_DEV(AMD, 8111_LPC),
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .class = 0,
+ .class_mask = 0,
+ .driver_data = LPC_BRIDGE,
+ },
+ {
+ 0,
+ } /* table is NULL-terminated */
+};
+MODULE_DEVICE_TABLE(pci, amd8111_edac_dev_tbl);
+
+static struct pci_driver amd8111_edac_dev_driver = {
+ .name = "AMD8111_EDAC_DEV",
+ .probe = amd8111_dev_probe,
+ .remove = amd8111_dev_remove,
+ .id_table = amd8111_edac_dev_tbl,
+};
+
+/* PCI Device ID table for EDAC PCI controller */
+static const struct pci_device_id amd8111_edac_pci_tbl[] = {
+ {
+ PCI_VEND_DEV(AMD, 8111_PCI),
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .class = 0,
+ .class_mask = 0,
+ .driver_data = PCI_BRIDGE,
+ },
+ {
+ 0,
+ } /* table is NULL-terminated */
+};
+MODULE_DEVICE_TABLE(pci, amd8111_edac_pci_tbl);
+
+static struct pci_driver amd8111_edac_pci_driver = {
+ .name = "AMD8111_EDAC_PCI",
+ .probe = amd8111_pci_probe,
+ .remove = amd8111_pci_remove,
+ .id_table = amd8111_edac_pci_tbl,
+};
+
+static int __init amd8111_edac_init(void)
+{
+ int val;
+
+ printk(KERN_INFO "AMD8111 EDAC driver " AMD8111_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;
+
+ val = pci_register_driver(&amd8111_edac_dev_driver);
+ val |= pci_register_driver(&amd8111_edac_pci_driver);
+
+ return val;
+}
+
+static void __exit amd8111_edac_exit(void)
+{
+ pci_unregister_driver(&amd8111_edac_pci_driver);
+ pci_unregister_driver(&amd8111_edac_dev_driver);
+}
+
+
+module_init(amd8111_edac_init);
+module_exit(amd8111_edac_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Cao Qingtao <qingtao.cao@windriver.com>\n");
+MODULE_DESCRIPTION("AMD8111 HyperTransport I/O Hub EDAC kernel module");
diff --git a/drivers/edac/amd8111_edac.h b/drivers/edac/amd8111_edac.h
new file mode 100644
index 00000000000..35794331deb
--- /dev/null
+++ b/drivers/edac/amd8111_edac.h
@@ -0,0 +1,130 @@
+/*
+ * amd8111_edac.h, EDAC defs for AMD8111 hypertransport chip
+ *
+ * Copyright (c) 2008 Wind River Systems, Inc.
+ *
+ * Authors: Cao Qingtao <qingtao.cao@windriver.com>
+ * Benjamin Walsh <benjamin.walsh@windriver.com>
+ * Hu Yongqi <yongqi.hu@windriver.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _AMD8111_EDAC_H_
+#define _AMD8111_EDAC_H_
+
+/************************************************************
+ * PCI Bridge Status and Command Register, DevA:0x04
+ ************************************************************/
+#define REG_PCI_STSCMD 0x04
+enum pci_stscmd_bits {
+ PCI_STSCMD_SSE = BIT(30),
+ PCI_STSCMD_RMA = BIT(29),
+ PCI_STSCMD_RTA = BIT(28),
+ PCI_STSCMD_SERREN = BIT(8),
+ PCI_STSCMD_CLEAR_MASK = (PCI_STSCMD_SSE |
+ PCI_STSCMD_RMA |
+ PCI_STSCMD_RTA)
+};
+
+/************************************************************
+ * PCI Bridge Memory Base-Limit Register, DevA:0x1c
+ ************************************************************/
+#define REG_MEM_LIM 0x1c
+enum mem_limit_bits {
+ MEM_LIMIT_DPE = BIT(31),
+ MEM_LIMIT_RSE = BIT(30),
+ MEM_LIMIT_RMA = BIT(29),
+ MEM_LIMIT_RTA = BIT(28),
+ MEM_LIMIT_STA = BIT(27),
+ MEM_LIMIT_MDPE = BIT(24),
+ MEM_LIMIT_CLEAR_MASK = (MEM_LIMIT_DPE |
+ MEM_LIMIT_RSE |
+ MEM_LIMIT_RMA |
+ MEM_LIMIT_RTA |
+ MEM_LIMIT_STA |
+ MEM_LIMIT_MDPE)
+};
+
+/************************************************************
+ * HyperTransport Link Control Register, DevA:0xc4
+ ************************************************************/
+#define REG_HT_LINK 0xc4
+enum ht_link_bits {
+ HT_LINK_LKFAIL = BIT(4),
+ HT_LINK_CRCFEN = BIT(1),
+ HT_LINK_CLEAR_MASK = (HT_LINK_LKFAIL)
+};
+
+/************************************************************
+ * PCI Bridge Interrupt and Bridge Control, DevA:0x3c
+ ************************************************************/
+#define REG_PCI_INTBRG_CTRL 0x3c
+enum pci_intbrg_ctrl_bits {
+ PCI_INTBRG_CTRL_DTSERREN = BIT(27),
+ PCI_INTBRG_CTRL_DTSTAT = BIT(26),
+ PCI_INTBRG_CTRL_MARSP = BIT(21),
+ PCI_INTBRG_CTRL_SERREN = BIT(17),
+ PCI_INTBRG_CTRL_PEREN = BIT(16),
+ PCI_INTBRG_CTRL_CLEAR_MASK = (PCI_INTBRG_CTRL_DTSTAT),
+ PCI_INTBRG_CTRL_POLL_MASK = (PCI_INTBRG_CTRL_DTSERREN |
+ PCI_INTBRG_CTRL_MARSP |
+ PCI_INTBRG_CTRL_SERREN)
+};
+
+/************************************************************
+ * I/O Control 1 Register, DevB:0x40
+ ************************************************************/
+#define REG_IO_CTRL_1 0x40
+enum io_ctrl_1_bits {
+ IO_CTRL_1_NMIONERR = BIT(7),
+ IO_CTRL_1_LPC_ERR = BIT(6),
+ IO_CTRL_1_PW2LPC = BIT(1),
+ IO_CTRL_1_CLEAR_MASK = (IO_CTRL_1_LPC_ERR | IO_CTRL_1_PW2LPC)
+};
+
+/************************************************************
+ * Legacy I/O Space Registers
+ ************************************************************/
+#define REG_AT_COMPAT 0x61
+enum at_compat_bits {
+ AT_COMPAT_SERR = BIT(7),
+ AT_COMPAT_IOCHK = BIT(6),
+ AT_COMPAT_CLRIOCHK = BIT(3),
+ AT_COMPAT_CLRSERR = BIT(2),
+};
+
+struct amd8111_dev_info {
+ u16 err_dev; /* PCI Device ID */
+ struct pci_dev *dev;
+ int edac_idx; /* device index */
+ char *ctl_name;
+ struct edac_device_ctl_info *edac_dev;
+ void (*init)(struct amd8111_dev_info *dev_info);
+ void (*exit)(struct amd8111_dev_info *dev_info);
+ void (*check)(struct edac_device_ctl_info *edac_dev);
+};
+
+struct amd8111_pci_info {
+ u16 err_dev; /* PCI Device ID */
+ struct pci_dev *dev;
+ int edac_idx; /* pci index */
+ const char *ctl_name;
+ struct edac_pci_ctl_info *edac_dev;
+ void (*init)(struct amd8111_pci_info *dev_info);
+ void (*exit)(struct amd8111_pci_info *dev_info);
+ void (*check)(struct edac_pci_ctl_info *edac_dev);
+};
+
+#endif /* _AMD8111_EDAC_H_ */
diff --git a/drivers/edac/amd8131_edac.c b/drivers/edac/amd8131_edac.c
new file mode 100644
index 00000000000..c083b31cac5
--- /dev/null
+++ b/drivers/edac/amd8131_edac.c
@@ -0,0 +1,379 @@
+/*
+ * amd8131_edac.c, AMD8131 hypertransport chip EDAC kernel module
+ *
+ * Copyright (c) 2008 Wind River Systems, Inc.
+ *
+ * Authors: Cao Qingtao <qingtao.cao@windriver.com>
+ * Benjamin Walsh <benjamin.walsh@windriver.com>
+ * Hu Yongqi <yongqi.hu@windriver.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/bitops.h>
+#include <linux/edac.h>
+#include <linux/pci_ids.h>
+
+#include "edac_core.h"
+#include "edac_module.h"
+#include "amd8131_edac.h"
+
+#define AMD8131_EDAC_REVISION " Ver: 1.0.0 " __DATE__
+#define AMD8131_EDAC_MOD_STR "amd8131_edac"
+
+/* Wrapper functions for accessing PCI configuration space */
+static void edac_pci_read_dword(struct pci_dev *dev, int reg, u32 *val32)
+{
+ int ret;
+
+ ret = pci_read_config_dword(dev, reg, val32);
+ if (ret != 0)
+ printk(KERN_ERR AMD8131_EDAC_MOD_STR
+ " PCI Access Read Error at 0x%x\n", reg);
+}
+
+static void edac_pci_write_dword(struct pci_dev *dev, int reg, u32 val32)
+{
+ int ret;
+
+ ret = pci_write_config_dword(dev, reg, val32);
+ if (ret != 0)
+ printk(KERN_ERR AMD8131_EDAC_MOD_STR
+ " PCI Access Write Error at 0x%x\n", reg);
+}
+
+static char * const bridge_str[] = {
+ [NORTH_A] = "NORTH A",
+ [NORTH_B] = "NORTH B",
+ [SOUTH_A] = "SOUTH A",
+ [SOUTH_B] = "SOUTH B",
+ [NO_BRIDGE] = "NO BRIDGE",
+};
+
+/* Support up to two AMD8131 chipsets on a platform */
+static struct amd8131_dev_info amd8131_devices[] = {
+ {
+ .inst = NORTH_A,
+ .devfn = DEVFN_PCIX_BRIDGE_NORTH_A,
+ .ctl_name = "AMD8131_PCIX_NORTH_A",
+ },
+ {
+ .inst = NORTH_B,
+ .devfn = DEVFN_PCIX_BRIDGE_NORTH_B,
+ .ctl_name = "AMD8131_PCIX_NORTH_B",
+ },
+ {
+ .inst = SOUTH_A,
+ .devfn = DEVFN_PCIX_BRIDGE_SOUTH_A,
+ .ctl_name = "AMD8131_PCIX_SOUTH_A",
+ },
+ {
+ .inst = SOUTH_B,
+ .devfn = DEVFN_PCIX_BRIDGE_SOUTH_B,
+ .ctl_name = "AMD8131_PCIX_SOUTH_B",
+ },
+ {.inst = NO_BRIDGE,},
+};
+
+static void amd8131_pcix_init(struct amd8131_dev_info *dev_info)
+{
+ u32 val32;
+ struct pci_dev *dev = dev_info->dev;
+
+ /* First clear error detection flags */
+ edac_pci_read_dword(dev, REG_MEM_LIM, &val32);
+ if (val32 & MEM_LIMIT_MASK)
+ edac_pci_write_dword(dev, REG_MEM_LIM, val32);
+
+ /* Clear Discard Timer Timedout flag */
+ edac_pci_read_dword(dev, REG_INT_CTLR, &val32);
+ if (val32 & INT_CTLR_DTS)
+ edac_pci_write_dword(dev, REG_INT_CTLR, val32);
+
+ /* Clear CRC Error flag on link side A */
+ edac_pci_read_dword(dev, REG_LNK_CTRL_A, &val32);
+ if (val32 & LNK_CTRL_CRCERR_A)
+ edac_pci_write_dword(dev, REG_LNK_CTRL_A, val32);
+
+ /* Clear CRC Error flag on link side B */
+ edac_pci_read_dword(dev, REG_LNK_CTRL_B, &val32);
+ if (val32 & LNK_CTRL_CRCERR_B)
+ edac_pci_write_dword(dev, REG_LNK_CTRL_B, val32);
+
+ /*
+ * Then enable all error detections.
+ *
+ * Setup Discard Timer Sync Flood Enable,
+ * System Error Enable and Parity Error Enable.
+ */
+ edac_pci_read_dword(dev, REG_INT_CTLR, &val32);
+ val32 |= INT_CTLR_PERR | INT_CTLR_SERR | INT_CTLR_DTSE;
+ edac_pci_write_dword(dev, REG_INT_CTLR, val32);
+
+ /* Enable overall SERR Error detection */
+ edac_pci_read_dword(dev, REG_STS_CMD, &val32);
+ val32 |= STS_CMD_SERREN;
+ edac_pci_write_dword(dev, REG_STS_CMD, val32);
+
+ /* Setup CRC Flood Enable for link side A */
+ edac_pci_read_dword(dev, REG_LNK_CTRL_A, &val32);
+ val32 |= LNK_CTRL_CRCFEN;
+ edac_pci_write_dword(dev, REG_LNK_CTRL_A, val32);
+
+ /* Setup CRC Flood Enable for link side B */
+ edac_pci_read_dword(dev, REG_LNK_CTRL_B, &val32);
+ val32 |= LNK_CTRL_CRCFEN;
+ edac_pci_write_dword(dev, REG_LNK_CTRL_B, val32);
+}
+
+static void amd8131_pcix_exit(struct amd8131_dev_info *dev_info)
+{
+ u32 val32;
+ struct pci_dev *dev = dev_info->dev;
+
+ /* Disable SERR, PERR and DTSE Error detection */
+ edac_pci_read_dword(dev, REG_INT_CTLR, &val32);
+ val32 &= ~(INT_CTLR_PERR | INT_CTLR_SERR | INT_CTLR_DTSE);
+ edac_pci_write_dword(dev, REG_INT_CTLR, val32);
+
+ /* Disable overall System Error detection */
+ edac_pci_read_dword(dev, REG_STS_CMD, &val32);
+ val32 &= ~STS_CMD_SERREN;
+ edac_pci_write_dword(dev, REG_STS_CMD, val32);
+
+ /* Disable CRC Sync Flood on link side A */
+ edac_pci_read_dword(dev, REG_LNK_CTRL_A, &val32);
+ val32 &= ~LNK_CTRL_CRCFEN;
+ edac_pci_write_dword(dev, REG_LNK_CTRL_A, val32);
+
+ /* Disable CRC Sync Flood on link side B */
+ edac_pci_read_dword(dev, REG_LNK_CTRL_B, &val32);
+ val32 &= ~LNK_CTRL_CRCFEN;
+ edac_pci_write_dword(dev, REG_LNK_CTRL_B, val32);
+}
+
+static void amd8131_pcix_check(struct edac_pci_ctl_info *edac_dev)
+{
+ struct amd8131_dev_info *dev_info = edac_dev->pvt_info;
+ struct pci_dev *dev = dev_info->dev;
+ u32 val32;
+
+ /* Check PCI-X Bridge Memory Base-Limit Register for errors */
+ edac_pci_read_dword(dev, REG_MEM_LIM, &val32);
+ if (val32 & MEM_LIMIT_MASK) {
+ printk(KERN_INFO "Error(s) in mem limit register "
+ "on %s bridge\n", dev_info->ctl_name);
+ printk(KERN_INFO "DPE: %d, RSE: %d, RMA: %d\n"
+ "RTA: %d, STA: %d, MDPE: %d\n",
+ val32 & MEM_LIMIT_DPE,
+ val32 & MEM_LIMIT_RSE,
+ val32 & MEM_LIMIT_RMA,
+ val32 & MEM_LIMIT_RTA,
+ val32 & MEM_LIMIT_STA,
+ val32 & MEM_LIMIT_MDPE);
+
+ val32 |= MEM_LIMIT_MASK;
+ edac_pci_write_dword(dev, REG_MEM_LIM, val32);
+
+ edac_pci_handle_npe(edac_dev, edac_dev->ctl_name);
+ }
+
+ /* Check if Discard Timer timed out */
+ edac_pci_read_dword(dev, REG_INT_CTLR, &val32);
+ if (val32 & INT_CTLR_DTS) {
+ printk(KERN_INFO "Error(s) in interrupt and control register "
+ "on %s bridge\n", dev_info->ctl_name);
+ printk(KERN_INFO "DTS: %d\n", val32 & INT_CTLR_DTS);
+
+ val32 |= INT_CTLR_DTS;
+ edac_pci_write_dword(dev, REG_INT_CTLR, val32);
+
+ edac_pci_handle_npe(edac_dev, edac_dev->ctl_name);
+ }
+
+ /* Check if CRC error happens on link side A */
+ edac_pci_read_dword(dev, REG_LNK_CTRL_A, &val32);
+ if (val32 & LNK_CTRL_CRCERR_A) {
+ printk(KERN_INFO "Error(s) in link conf and control register "
+ "on %s bridge\n", dev_info->ctl_name);
+ printk(KERN_INFO "CRCERR: %d\n", val32 & LNK_CTRL_CRCERR_A);
+
+ val32 |= LNK_CTRL_CRCERR_A;
+ edac_pci_write_dword(dev, REG_LNK_CTRL_A, val32);
+
+ edac_pci_handle_npe(edac_dev, edac_dev->ctl_name);
+ }
+
+ /* Check if CRC error happens on link side B */
+ edac_pci_read_dword(dev, REG_LNK_CTRL_B, &val32);
+ if (val32 & LNK_CTRL_CRCERR_B) {
+ printk(KERN_INFO "Error(s) in link conf and control register "
+ "on %s bridge\n", dev_info->ctl_name);
+ printk(KERN_INFO "CRCERR: %d\n", val32 & LNK_CTRL_CRCERR_B);
+
+ val32 |= LNK_CTRL_CRCERR_B;
+ edac_pci_write_dword(dev, REG_LNK_CTRL_B, val32);
+
+ edac_pci_handle_npe(edac_dev, edac_dev->ctl_name);
+ }
+}
+
+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,
+};
+
+/*
+ * There are 4 PCIX Bridges on ATCA-6101 that share the same PCI Device ID,
+ * so amd8131_probe() would be called by kernel 4 times, with different
+ * address of pci_dev for each of them each time.
+ */
+static int amd8131_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+ struct amd8131_dev_info *dev_info;
+
+ for (dev_info = amd8131_chipset.devices; dev_info->inst != NO_BRIDGE;
+ dev_info++)
+ if (dev_info->devfn == dev->devfn)
+ break;
+
+ if (dev_info->inst == NO_BRIDGE) /* should never happen */
+ return -ENODEV;
+
+ /*
+ * We can't call pci_get_device() as we are used to do because
+ * there are 4 of them but pci_dev_get() instead.
+ */
+ 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;
+ }
+
+ /*
+ * we do not allocate extra private structure for
+ * edac_pci_ctl_info, but make use of existing
+ * one instead.
+ */
+ 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;
+
+ dev_info->edac_dev->pvt_info = dev_info;
+ dev_info->edac_dev->dev = &dev_info->dev->dev;
+ dev_info->edac_dev->mod_name = AMD8131_EDAC_MOD_STR;
+ dev_info->edac_dev->ctl_name = dev_info->ctl_name;
+ dev_info->edac_dev->dev_name = dev_info->dev->dev.bus_id;
+
+ if (edac_op_state == EDAC_OPSTATE_POLL)
+ dev_info->edac_dev->edac_check = amd8131_chipset.check;
+
+ if (amd8131_chipset.init)
+ amd8131_chipset.init(dev_info);
+
+ 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;
+ }
+
+ printk(KERN_INFO "added one device on AMD8131 "
+ "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 0;
+}
+
+static void amd8131_remove(struct pci_dev *dev)
+{
+ struct amd8131_dev_info *dev_info;
+
+ for (dev_info = amd8131_chipset.devices; dev_info->inst != NO_BRIDGE;
+ dev_info++)
+ if (dev_info->devfn == dev->devfn)
+ break;
+
+ if (dev_info->inst == NO_BRIDGE) /* should never happen */
+ return;
+
+ 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);
+}
+
+static const struct pci_device_id amd8131_edac_pci_tbl[] = {
+ {
+ PCI_VEND_DEV(AMD, 8131_BRIDGE),
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .class = 0,
+ .class_mask = 0,
+ .driver_data = 0,
+ },
+ {
+ 0,
+ } /* table is NULL-terminated */
+};
+MODULE_DEVICE_TABLE(pci, amd8131_edac_pci_tbl);
+
+static struct pci_driver amd8131_edac_driver = {
+ .name = AMD8131_EDAC_MOD_STR,
+ .probe = amd8131_probe,
+ .remove = amd8131_remove,
+ .id_table = amd8131_edac_pci_tbl,
+};
+
+static int __init amd8131_edac_init(void)
+{
+ 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;
+
+ return pci_register_driver(&amd8131_edac_driver);
+}
+
+static void __exit amd8131_edac_exit(void)
+{
+ pci_unregister_driver(&amd8131_edac_driver);
+}
+
+module_init(amd8131_edac_init);
+module_exit(amd8131_edac_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Cao Qingtao <qingtao.cao@windriver.com>\n");
+MODULE_DESCRIPTION("AMD8131 HyperTransport PCI-X Tunnel EDAC kernel module");
diff --git a/drivers/edac/amd8131_edac.h b/drivers/edac/amd8131_edac.h
new file mode 100644
index 00000000000..60e0d1c72de
--- /dev/null
+++ b/drivers/edac/amd8131_edac.h
@@ -0,0 +1,119 @@
+/*
+ * amd8131_edac.h, EDAC defs for AMD8131 hypertransport chip
+ *
+ * Copyright (c) 2008 Wind River Systems, Inc.
+ *
+ * Authors: Cao Qingtao <qingtao.cao@windriver.com>
+ * Benjamin Walsh <benjamin.walsh@windriver.com>
+ * Hu Yongqi <yongqi.hu@windriver.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _AMD8131_EDAC_H_
+#define _AMD8131_EDAC_H_
+
+#define DEVFN_PCIX_BRIDGE_NORTH_A 8
+#define DEVFN_PCIX_BRIDGE_NORTH_B 16
+#define DEVFN_PCIX_BRIDGE_SOUTH_A 24
+#define DEVFN_PCIX_BRIDGE_SOUTH_B 32
+
+/************************************************************
+ * PCI-X Bridge Status and Command Register, DevA:0x04
+ ************************************************************/
+#define REG_STS_CMD 0x04
+enum sts_cmd_bits {
+ STS_CMD_SSE = BIT(30),
+ STS_CMD_SERREN = BIT(8)
+};
+
+/************************************************************
+ * PCI-X Bridge Interrupt and Bridge Control Register,
+ ************************************************************/
+#define REG_INT_CTLR 0x3c
+enum int_ctlr_bits {
+ INT_CTLR_DTSE = BIT(27),
+ INT_CTLR_DTS = BIT(26),
+ INT_CTLR_SERR = BIT(17),
+ INT_CTLR_PERR = BIT(16)
+};
+
+/************************************************************
+ * PCI-X Bridge Memory Base-Limit Register, DevA:0x1C
+ ************************************************************/
+#define REG_MEM_LIM 0x1c
+enum mem_limit_bits {
+ MEM_LIMIT_DPE = BIT(31),
+ MEM_LIMIT_RSE = BIT(30),
+ MEM_LIMIT_RMA = BIT(29),
+ MEM_LIMIT_RTA = BIT(28),
+ 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
+};
+
+/************************************************************
+ * Link Configuration And Control Register, side A
+ ************************************************************/
+#define REG_LNK_CTRL_A 0xc4
+
+/************************************************************
+ * Link Configuration And Control Register, side B
+ ************************************************************/
+#define REG_LNK_CTRL_B 0xc8
+
+enum lnk_ctrl_bits {
+ LNK_CTRL_CRCERR_A = BIT(9),
+ LNK_CTRL_CRCERR_B = BIT(8),
+ LNK_CTRL_CRCFEN = BIT(1)
+};
+
+enum pcix_bridge_inst {
+ NORTH_A = 0,
+ NORTH_B = 1,
+ SOUTH_A = 2,
+ SOUTH_B = 3,
+ NO_BRIDGE = 4
+};
+
+struct amd8131_dev_info {
+ int devfn;
+ enum pcix_bridge_inst inst;
+ struct pci_dev *dev;
+ int edac_idx; /* pci device index */
+ char *ctl_name;
+ struct edac_pci_ctl_info *edac_dev;
+};
+
+/*
+ * AMD8131 chipset has two pairs of PCIX Bridge and related IOAPIC
+ * Controler, and ATCA-6101 has two AMD8131 chipsets, so there are
+ * four PCIX Bridges on ATCA-6101 altogether.
+ *
+ * These PCIX Bridges share the same PCI Device ID and are all of
+ * Function Zero, they could be discrimated by their pci_dev->devfn.
+ * They share the same set of init/check/exit methods, and their
+ * private structures are collected in the devices[] array.
+ */
+struct amd8131_info {
+ u16 err_dev; /* PCI Device ID for AMD8131 APIC*/
+ struct amd8131_dev_info *devices;
+ 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);
+};
+
+#endif /* _AMD8131_EDAC_H_ */
+
diff --git a/drivers/edac/edac_core.h b/drivers/edac/edac_core.h
index 4b55ec607a8..28f2c3f959b 100644
--- a/drivers/edac/edac_core.h
+++ b/drivers/edac/edac_core.h
@@ -49,6 +49,10 @@
#define edac_printk(level, prefix, fmt, arg...) \
printk(level "EDAC " prefix ": " fmt, ##arg)
+#define edac_printk_verbose(level, prefix, fmt, arg...) \
+ printk(level "EDAC " prefix ": " "in %s, line at %d: " fmt, \
+ __FILE__, __LINE__, ##arg)
+
#define edac_mc_printk(mci, level, fmt, arg...) \
printk(level "EDAC MC%d: " fmt, mci->mc_idx, ##arg)
@@ -71,11 +75,20 @@
#ifdef CONFIG_EDAC_DEBUG
extern int edac_debug_level;
+#ifndef CONFIG_EDAC_DEBUG_VERBOSE
#define edac_debug_printk(level, fmt, arg...) \
do { \
if (level <= edac_debug_level) \
edac_printk(KERN_DEBUG, EDAC_DEBUG, fmt, ##arg); \
- } while(0)
+ } while (0)
+#else /* CONFIG_EDAC_DEBUG_VERBOSE */
+#define edac_debug_printk(level, fmt, arg...) \
+ do { \
+ if (level <= edac_debug_level) \
+ edac_printk_verbose(KERN_DEBUG, EDAC_DEBUG, fmt, \
+ ##arg); \
+ } while (0)
+#endif
#define debugf0( ... ) edac_debug_printk(0, __VA_ARGS__ )
#define debugf1( ... ) edac_debug_printk(1, __VA_ARGS__ )
@@ -831,6 +844,7 @@ extern void edac_pci_free_ctl_info(struct edac_pci_ctl_info *pci);
extern void edac_pci_reset_delay_period(struct edac_pci_ctl_info *pci,
unsigned long value);
+extern int edac_pci_alloc_index(void);
extern int edac_pci_add_device(struct edac_pci_ctl_info *pci, int edac_idx);
extern struct edac_pci_ctl_info *edac_pci_del_device(struct device *dev);
diff --git a/drivers/edac/edac_pci.c b/drivers/edac/edac_pci.c
index 5d3c8083a40..5b150aea703 100644
--- a/drivers/edac/edac_pci.c
+++ b/drivers/edac/edac_pci.c
@@ -30,6 +30,7 @@
static DEFINE_MUTEX(edac_pci_ctls_mutex);
static LIST_HEAD(edac_pci_list);
+static atomic_t pci_indexes = ATOMIC_INIT(0);
/*
* edac_pci_alloc_ctl_info
@@ -318,6 +319,19 @@ void edac_pci_reset_delay_period(struct edac_pci_ctl_info *pci,
EXPORT_SYMBOL_GPL(edac_pci_reset_delay_period);
/*
+ * edac_pci_alloc_index: Allocate a unique PCI index number
+ *
+ * Return:
+ * allocated index number
+ *
+ */
+int edac_pci_alloc_index(void)
+{
+ return atomic_inc_return(&pci_indexes) - 1;
+}
+EXPORT_SYMBOL_GPL(edac_pci_alloc_index);
+
+/*
* edac_pci_add_device: Insert the 'edac_dev' structure into the
* edac_pci global list and create sysfs entries associated with
* edac_pci structure.
diff --git a/drivers/edac/ppc4xx_edac.c b/drivers/edac/ppc4xx_edac.c
new file mode 100644
index 00000000000..11f2172aa1e
--- /dev/null
+++ b/drivers/edac/ppc4xx_edac.c
@@ -0,0 +1,1448 @@
+/*
+ * Copyright (c) 2008 Nuovation System Designs, LLC
+ * Grant Erickson <gerickson@nuovations.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the
+ * License.
+ *
+ */
+
+#include <linux/edac.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/types.h>
+
+#include <asm/dcr.h>
+
+#include "edac_core.h"
+#include "ppc4xx_edac.h"
+
+/*
+ * This file implements a driver for monitoring and handling events
+ * associated with the IMB DDR2 ECC controller found in the AMCC/IBM
+ * 405EX[r], 440SP, 440SPe, 460EX, 460GT and 460SX.
+ *
+ * As realized in the 405EX[r], this controller features:
+ *
+ * - Support for registered- and non-registered DDR1 and DDR2 memory.
+ * - 32-bit or 16-bit memory interface with optional ECC.
+ *
+ * o ECC support includes:
+ *
+ * - 4-bit SEC/DED
+ * - Aligned-nibble error detect
+ * - Bypass mode
+ *
+ * - Two (2) memory banks/ranks.
+ * - Up to 1 GiB per bank/rank in 32-bit mode and up to 512 MiB per
+ * bank/rank in 16-bit mode.
+ *
+ * As realized in the 440SP and 440SPe, this controller changes/adds:
+ *
+ * - 64-bit or 32-bit memory interface with optional ECC.
+ *
+ * o ECC support includes:
+ *
+ * - 8-bit SEC/DED
+ * - Aligned-nibble error detect
+ * - Bypass mode
+ *
+ * - Up to 4 GiB per bank/rank in 64-bit mode and up to 2 GiB
+ * per bank/rank in 32-bit mode.
+ *
+ * As realized in the 460EX and 460GT, this controller changes/adds:
+ *
+ * - 64-bit or 32-bit memory interface with optional ECC.
+ *
+ * o ECC support includes:
+ *
+ * - 8-bit SEC/DED
+ * - Aligned-nibble error detect
+ * - Bypass mode
+ *
+ * - Four (4) memory banks/ranks.
+ * - Up to 16 GiB per bank/rank in 64-bit mode and up to 8 GiB
+ * per bank/rank in 32-bit mode.
+ *
+ * At present, this driver has ONLY been tested against the controller
+ * realization in the 405EX[r] on the AMCC Kilauea and Haleakala
+ * boards (256 MiB w/o ECC memory soldered onto the board) and a
+ * proprietary board based on those designs (128 MiB ECC memory, also
+ * soldered onto the board).
+ *
+ * Dynamic feature detection and handling needs to be added for the
+ * other realizations of this controller listed above.
+ *
+ * Eventually, this driver will likely be adapted to the above variant
+ * realizations of this controller as well as broken apart to handle
+ * the other known ECC-capable controllers prevalent in other 4xx
+ * processors:
+ *
+ * - IBM SDRAM (405GP, 405CR and 405EP) "ibm,sdram-4xx"
+ * - IBM DDR1 (440GP, 440GX, 440EP and 440GR) "ibm,sdram-4xx-ddr"
+ * - Denali DDR1/DDR2 (440EPX and 440GRX) "denali,sdram-4xx-ddr2"
+ *
+ * For this controller, unfortunately, correctable errors report
+ * nothing more than the beat/cycle and byte/lane the correction
+ * occurred on and the check bit group that covered the error.
+ *
+ * In contrast, uncorrectable errors also report the failing address,
+ * the bus master and the transaction direction (i.e. read or write)
+ *
+ * Regardless of whether the error is a CE or a UE, we report the
+ * following pieces of information in the driver-unique message to the
+ * EDAC subsystem:
+ *
+ * - Device tree path
+ * - Bank(s)
+ * - Check bit error group
+ * - Beat(s)/lane(s)
+ */
+
+/* Preprocessor Definitions */
+
+#define EDAC_OPSTATE_INT_STR "interrupt"
+#define EDAC_OPSTATE_POLL_STR "polled"
+#define EDAC_OPSTATE_UNKNOWN_STR "unknown"
+
+#define PPC4XX_EDAC_MODULE_NAME "ppc4xx_edac"
+#define PPC4XX_EDAC_MODULE_REVISION "v1.0.0 " __DATE__
+
+#define PPC4XX_EDAC_MESSAGE_SIZE 256
+
+/*
+ * Kernel logging without an EDAC instance
+ */
+#define ppc4xx_edac_printk(level, fmt, arg...) \
+ edac_printk(level, "PPC4xx MC", fmt, ##arg)
+
+/*
+ * Kernel logging with an EDAC instance
+ */
+#define ppc4xx_edac_mc_printk(level, mci, fmt, arg...) \
+ edac_mc_chipset_printk(mci, level, "PPC4xx", fmt, ##arg)
+
+/*
+ * Macros to convert bank configuration size enumerations into MiB and
+ * page values.
+ */
+#define SDRAM_MBCF_SZ_MiB_MIN 4
+#define SDRAM_MBCF_SZ_TO_MiB(n) (SDRAM_MBCF_SZ_MiB_MIN \
+ << (SDRAM_MBCF_SZ_DECODE(n)))
+#define SDRAM_MBCF_SZ_TO_PAGES(n) (SDRAM_MBCF_SZ_MiB_MIN \
+ << (20 - PAGE_SHIFT + \
+ SDRAM_MBCF_SZ_DECODE(n)))
+
+/*
+ * The ibm,sdram-4xx-ddr2 Device Control Registers (DCRs) are
+ * indirectly acccessed and have a base and length defined by the
+ * device tree. The base can be anything; however, we expect the
+ * length to be precisely two registers, the first for the address
+ * window and the second for the data window.
+ */
+#define SDRAM_DCR_RESOURCE_LEN 2
+#define SDRAM_DCR_ADDR_OFFSET 0
+#define SDRAM_DCR_DATA_OFFSET 1
+
+/*
+ * Device tree interrupt indices
+ */
+#define INTMAP_ECCDED_INDEX 0 /* Double-bit Error Detect */
+#define INTMAP_ECCSEC_INDEX 1 /* Single-bit Error Correct */
+
+/* Type Definitions */
+
+/*
+ * PPC4xx SDRAM memory controller private instance data
+ */
+struct ppc4xx_edac_pdata {
+ dcr_host_t dcr_host; /* Indirect DCR address/data window mapping */
+ struct {
+ int sec; /* Single-bit correctable error IRQ assigned */
+ int ded; /* Double-bit detectable error IRQ assigned */
+ } irqs;
+};
+
+/*
+ * Various status data gathered and manipulated when checking and
+ * reporting ECC status.
+ */
+struct ppc4xx_ecc_status {
+ u32 ecces;
+ u32 besr;
+ u32 bearh;
+ u32 bearl;
+ u32 wmirq;
+};
+
+/* Function Prototypes */
+
+static int ppc4xx_edac_probe(struct of_device *device,
+ const struct of_device_id *device_id);
+static int ppc4xx_edac_remove(struct of_device *device);
+
+/* Global Variables */
+
+/*
+ * Device tree node type and compatible tuples this driver can match
+ * on.
+ */
+static struct of_device_id ppc4xx_edac_match[] = {
+ {
+ .compatible = "ibm,sdram-4xx-ddr2"
+ },
+ { }
+};
+
+static struct of_platform_driver ppc4xx_edac_driver = {
+ .match_table = ppc4xx_edac_match,
+ .probe = ppc4xx_edac_probe,
+ .remove = ppc4xx_edac_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = PPC4XX_EDAC_MODULE_NAME
+ }
+};
+
+/*
+ * TODO: The row and channel parameters likely need to be dynamically
+ * set based on the aforementioned variant controller realizations.
+ */
+static const unsigned ppc4xx_edac_nr_csrows = 2;
+static const unsigned ppc4xx_edac_nr_chans = 1;
+
+/*
+ * Strings associated with PLB master IDs capable of being posted in
+ * SDRAM_BESR or SDRAM_WMIRQ on uncorrectable ECC errors.
+ */
+static const char * const ppc4xx_plb_masters[9] = {
+ [SDRAM_PLB_M0ID_ICU] = "ICU",
+ [SDRAM_PLB_M0ID_PCIE0] = "PCI-E 0",
+ [SDRAM_PLB_M0ID_PCIE1] = "PCI-E 1",
+ [SDRAM_PLB_M0ID_DMA] = "DMA",
+ [SDRAM_PLB_M0ID_DCU] = "DCU",
+ [SDRAM_PLB_M0ID_OPB] = "OPB",
+ [SDRAM_PLB_M0ID_MAL] = "MAL",
+ [SDRAM_PLB_M0ID_SEC] = "SEC",
+ [SDRAM_PLB_M0ID_AHB] = "AHB"
+};
+
+/**
+ * mfsdram - read and return controller register data
+ * @dcr_host: A pointer to the DCR mapping.
+ * @idcr_n: The indirect DCR register to read.
+ *
+ * This routine reads and returns the data associated with the
+ * controller's specified indirect DCR register.
+ *
+ * Returns the read data.
+ */
+static inline u32
+mfsdram(const dcr_host_t *dcr_host, unsigned int idcr_n)
+{
+ return __mfdcri(dcr_host->base + SDRAM_DCR_ADDR_OFFSET,
+ dcr_host->base + SDRAM_DCR_DATA_OFFSET,
+ idcr_n);
+}
+
+/**
+ * mtsdram - write controller register data
+ * @dcr_host: A pointer to the DCR mapping.
+ * @idcr_n: The indirect DCR register to write.
+ * @value: The data to write.
+ *
+ * This routine writes the provided data to the controller's specified
+ * indirect DCR register.
+ */
+static inline void
+mtsdram(const dcr_host_t *dcr_host, unsigned int idcr_n, u32 value)
+{
+ return __mtdcri(dcr_host->base + SDRAM_DCR_ADDR_OFFSET,
+ dcr_host->base + SDRAM_DCR_DATA_OFFSET,
+ idcr_n,
+ value);
+}
+
+/**
+ * ppc4xx_edac_check_bank_error - check a bank for an ECC bank error
+ * @status: A pointer to the ECC status structure to check for an
+ * ECC bank error.
+ * @bank: The bank to check for an ECC error.
+ *
+ * This routine determines whether the specified bank has an ECC
+ * error.
+ *
+ * Returns true if the specified bank has an ECC error; otherwise,
+ * false.
+ */
+static bool
+ppc4xx_edac_check_bank_error(const struct ppc4xx_ecc_status *status,
+ unsigned int bank)
+{
+ switch (bank) {
+ case 0:
+ return status->ecces & SDRAM_ECCES_BK0ER;
+ case 1:
+ return status->ecces & SDRAM_ECCES_BK1ER;
+ default:
+ return false;
+ }
+}
+
+/**
+ * ppc4xx_edac_generate_bank_message - generate interpretted bank status message
+ * @mci: A pointer to the EDAC memory controller instance associated
+ * with the bank message being generated.
+ * @status: A pointer to the ECC status structure to generate the
+ * message from.
+ * @buffer: A pointer to the buffer in which to generate the
+ * message.
+ * @size: The size, in bytes, of space available in buffer.
+ *
+ * This routine generates to the provided buffer the portion of the
+ * driver-unique report message associated with the ECCESS[BKNER]
+ * field of the specified ECC status.
+ *
+ * Returns the number of characters generated on success; otherwise, <
+ * 0 on error.
+ */
+static int
+ppc4xx_edac_generate_bank_message(const struct mem_ctl_info *mci,
+ const struct ppc4xx_ecc_status *status,
+ char *buffer,
+ size_t size)
+{
+ int n, total = 0;
+ unsigned int row, rows;
+
+ n = snprintf(buffer, size, "%s: Banks: ", mci->dev_name);
+
+ if (n < 0 || n >= size)
+ goto fail;
+
+ buffer += n;
+ size -= n;
+ total += n;
+
+ for (rows = 0, row = 0; row < mci->nr_csrows; row++) {
+ if (ppc4xx_edac_check_bank_error(status, row)) {
+ n = snprintf(buffer, size, "%s%u",
+ (rows++ ? ", " : ""), row);
+
+ if (n < 0 || n >= size)
+ goto fail;
+
+ buffer += n;
+ size -= n;
+ total += n;
+ }
+ }
+
+ n = snprintf(buffer, size, "%s; ", rows ? "" : "None");
+
+ if (n < 0 || n >= size)
+ goto fail;
+
+ buffer += n;
+ size -= n;
+ total += n;
+
+ fail:
+ return total;
+}
+
+/**
+ * ppc4xx_edac_generate_checkbit_message - generate interpretted checkbit message
+ * @mci: A pointer to the EDAC memory controller instance associated
+ * with the checkbit message being generated.
+ * @status: A pointer to the ECC status structure to generate the
+ * message from.
+ * @buffer: A pointer to the buffer in which to generate the
+ * message.
+ * @size: The size, in bytes, of space available in buffer.
+ *
+ * This routine generates to the provided buffer the portion of the
+ * driver-unique report message associated with the ECCESS[CKBER]
+ * field of the specified ECC status.
+ *
+ * Returns the number of characters generated on success; otherwise, <
+ * 0 on error.
+ */
+static int
+ppc4xx_edac_generate_checkbit_message(const struct mem_ctl_info *mci,
+ const struct ppc4xx_ecc_status *status,
+ char *buffer,
+ size_t size)
+{
+ const struct ppc4xx_edac_pdata *pdata = mci->pvt_info;
+ const char *ckber = NULL;
+
+ switch (status->ecces & SDRAM_ECCES_CKBER_MASK) {
+ case SDRAM_ECCES_CKBER_NONE:
+ ckber = "None";
+ break;
+ case SDRAM_ECCES_CKBER_32_ECC_0_3:
+ ckber = "ECC0:3";
+ break;
+ case SDRAM_ECCES_CKBER_32_ECC_4_8:
+ switch (mfsdram(&pdata->dcr_host, SDRAM_MCOPT1) &
+ SDRAM_MCOPT1_WDTH_MASK) {
+ case SDRAM_MCOPT1_WDTH_16:
+ ckber = "ECC0:3";
+ break;
+ case SDRAM_MCOPT1_WDTH_32:
+ ckber = "ECC4:8";
+ break;
+ default:
+ ckber = "Unknown";
+ break;
+ }
+ break;
+ case SDRAM_ECCES_CKBER_32_ECC_0_8:
+ ckber = "ECC0:8";
+ break;
+ default:
+ ckber = "Unknown";
+ break;
+ }
+
+ return snprintf(buffer, size, "Checkbit Error: %s", ckber);
+}
+
+/**
+ * ppc4xx_edac_generate_lane_message - generate interpretted byte lane message
+ * @mci: A pointer to the EDAC memory controller instance associated
+ * with the byte lane message being generated.
+ * @status: A pointer to the ECC status structure to generate the
+ * message from.
+ * @buffer: A pointer to the buffer in which to generate the
+ * message.
+ * @size: The size, in bytes, of space available in buffer.
+ *
+ * This routine generates to the provided buffer the portion of the
+ * driver-unique report message associated with the ECCESS[BNCE]
+ * field of the specified ECC status.
+ *
+ * Returns the number of characters generated on success; otherwise, <
+ * 0 on error.
+ */
+static int
+ppc4xx_edac_generate_lane_message(const struct mem_ctl_info *mci,
+ const struct ppc4xx_ecc_status *status,
+ char *buffer,
+ size_t size)
+{
+ int n, total = 0;
+ unsigned int lane, lanes;
+ const unsigned int first_lane = 0;
+ const unsigned int lane_count = 16;
+
+ n = snprintf(buffer, size, "; Byte Lane Errors: ");
+
+ if (n < 0 || n >= size)
+ goto fail;
+
+ buffer += n;
+ size -= n;
+ total += n;
+
+ for (lanes = 0, lane = first_lane; lane < lane_count; lane++) {
+ if ((status->ecces & SDRAM_ECCES_BNCE_ENCODE(lane)) != 0) {
+ n = snprintf(buffer, size,
+ "%s%u",
+ (lanes++ ? ", " : ""), lane);
+
+ if (n < 0 || n >= size)
+ goto fail;
+
+ buffer += n;
+ size -= n;
+ total += n;
+ }
+ }
+
+ n = snprintf(buffer, size, "%s; ", lanes ? "" : "None");
+
+ if (n < 0 || n >= size)
+ goto fail;
+
+ buffer += n;
+ size -= n;
+ total += n;
+
+ fail:
+ return total;
+}
+
+/**
+ * ppc4xx_edac_generate_ecc_message - generate interpretted ECC status message
+ * @mci: A pointer to the EDAC memory controller instance associated
+ * with the ECCES message being generated.
+ * @status: A pointer to the ECC status structure to generate the
+ * message from.
+ * @buffer: A pointer to the buffer in which to generate the
+ * message.
+ * @size: The size, in bytes, of space available in buffer.
+ *
+ * This routine generates to the provided buffer the portion of the
+ * driver-unique report message associated with the ECCESS register of
+ * the specified ECC status.
+ *
+ * Returns the number of characters generated on success; otherwise, <
+ * 0 on error.
+ */
+static int
+ppc4xx_edac_generate_ecc_message(const struct mem_ctl_info *mci,
+ const struct ppc4xx_ecc_status *status,
+ char *buffer,
+ size_t size)
+{
+ int n, total = 0;
+
+ n = ppc4xx_edac_generate_bank_message(mci, status, buffer, size);
+
+ if (n < 0 || n >= size)
+ goto fail;
+
+ buffer += n;
+ size -= n;
+ total += n;
+
+ n = ppc4xx_edac_generate_checkbit_message(mci, status, buffer, size);
+
+ if (n < 0 || n >= size)
+ goto fail;
+
+ buffer += n;
+ size -= n;
+ total += n;
+
+ n = ppc4xx_edac_generate_lane_message(mci, status, buffer, size);
+
+ if (n < 0 || n >= size)
+ goto fail;
+
+ buffer += n;
+ size -= n;
+ total += n;
+
+ fail:
+ return total;
+}
+
+/**
+ * ppc4xx_edac_generate_plb_message - generate interpretted PLB status message
+ * @mci: A pointer to the EDAC memory controller instance associated
+ * with the PLB message being generated.
+ * @status: A pointer to the ECC status structure to generate the
+ * message from.
+ * @buffer: A pointer to the buffer in which to generate the
+ * message.
+ * @size: The size, in bytes, of space available in buffer.
+ *
+ * This routine generates to the provided buffer the portion of the
+ * driver-unique report message associated with the PLB-related BESR
+ * and/or WMIRQ registers of the specified ECC status.
+ *
+ * Returns the number of characters generated on success; otherwise, <
+ * 0 on error.
+ */
+static int
+ppc4xx_edac_generate_plb_message(const struct mem_ctl_info *mci,
+ const struct ppc4xx_ecc_status *status,
+ char *buffer,
+ size_t size)
+{
+ unsigned int master;
+ bool read;
+
+ if ((status->besr & SDRAM_BESR_MASK) == 0)
+ return 0;
+
+ if ((status->besr & SDRAM_BESR_M0ET_MASK) == SDRAM_BESR_M0ET_NONE)
+ return 0;
+
+ read = ((status->besr & SDRAM_BESR_M0RW_MASK) == SDRAM_BESR_M0RW_READ);
+
+ master = SDRAM_BESR_M0ID_DECODE(status->besr);
+
+ return snprintf(buffer, size,
+ "%s error w/ PLB master %u \"%s\"; ",
+ (read ? "Read" : "Write"),
+ master,
+ (((master >= SDRAM_PLB_M0ID_FIRST) &&
+ (master <= SDRAM_PLB_M0ID_LAST)) ?
+ ppc4xx_plb_masters[master] : "UNKNOWN"));
+}
+
+/**
+ * ppc4xx_edac_generate_message - generate interpretted status message
+ * @mci: A pointer to the EDAC memory controller instance associated
+ * with the driver-unique message being generated.
+ * @status: A pointer to the ECC status structure to generate the
+ * message from.
+ * @buffer: A pointer to the buffer in which to generate the
+ * message.
+ * @size: The size, in bytes, of space available in buffer.
+ *
+ * This routine generates to the provided buffer the driver-unique
+ * EDAC report message from the specified ECC status.
+ */
+static void
+ppc4xx_edac_generate_message(const struct mem_ctl_info *mci,
+ const struct ppc4xx_ecc_status *status,
+ char *buffer,
+ size_t size)
+{
+ int n;
+
+ if (buffer == NULL || size == 0)
+ return;
+
+ n = ppc4xx_edac_generate_ecc_message(mci, status, buffer, size);
+
+ if (n < 0 || n >= size)
+ return;
+
+ buffer += n;
+ size -= n;
+
+ ppc4xx_edac_generate_plb_message(mci, status, buffer, size);
+}
+
+#ifdef DEBUG
+/**
+ * ppc4xx_ecc_dump_status - dump controller ECC status registers
+ * @mci: A pointer to the EDAC memory controller instance
+ * associated with the status being dumped.
+ * @status: A pointer to the ECC status structure to generate the
+ * dump from.
+ *
+ * This routine dumps to the kernel log buffer the raw and
+ * interpretted specified ECC status.
+ */
+static void
+ppc4xx_ecc_dump_status(const struct mem_ctl_info *mci,
+ const struct ppc4xx_ecc_status *status)
+{
+ char message[PPC4XX_EDAC_MESSAGE_SIZE];
+
+ ppc4xx_edac_generate_message(mci, status, message, sizeof(message));
+
+ ppc4xx_edac_mc_printk(KERN_INFO, mci,
+ "\n"
+ "\tECCES: 0x%08x\n"
+ "\tWMIRQ: 0x%08x\n"
+ "\tBESR: 0x%08x\n"
+ "\tBEAR: 0x%08x%08x\n"
+ "\t%s\n",
+ status->ecces,
+ status->wmirq,
+ status->besr,
+ status->bearh,
+ status->bearl,
+ message);
+}
+#endif /* DEBUG */
+
+/**
+ * ppc4xx_ecc_get_status - get controller ECC status
+ * @mci: A pointer to the EDAC memory controller instance
+ * associated with the status being retrieved.
+ * @status: A pointer to the ECC status structure to populate the
+ * ECC status with.
+ *
+ * This routine reads and masks, as appropriate, all the relevant
+ * status registers that deal with ibm,sdram-4xx-ddr2 ECC errors.
+ * While we read all of them, for correctable errors, we only expect
+ * to deal with ECCES. For uncorrectable errors, we expect to deal
+ * with all of them.
+ */
+static void
+ppc4xx_ecc_get_status(const struct mem_ctl_info *mci,
+ struct ppc4xx_ecc_status *status)
+{
+ const struct ppc4xx_edac_pdata *pdata = mci->pvt_info;
+ const dcr_host_t *dcr_host = &pdata->dcr_host;
+
+ status->ecces = mfsdram(dcr_host, SDRAM_ECCES) & SDRAM_ECCES_MASK;
+ status->wmirq = mfsdram(dcr_host, SDRAM_WMIRQ) & SDRAM_WMIRQ_MASK;
+ status->besr = mfsdram(dcr_host, SDRAM_BESR) & SDRAM_BESR_MASK;
+ status->bearl = mfsdram(dcr_host, SDRAM_BEARL);
+ status->bearh = mfsdram(dcr_host, SDRAM_BEARH);
+}
+
+/**
+ * ppc4xx_ecc_clear_status - clear controller ECC status
+ * @mci: A pointer to the EDAC memory controller instance
+ * associated with the status being cleared.
+ * @status: A pointer to the ECC status structure containing the
+ * values to write to clear the ECC status.
+ *
+ * This routine clears--by writing the masked (as appropriate) status
+ * values back to--the status registers that deal with
+ * ibm,sdram-4xx-ddr2 ECC errors.
+ */
+static void
+ppc4xx_ecc_clear_status(const struct mem_ctl_info *mci,
+ const struct ppc4xx_ecc_status *status)
+{
+ const struct ppc4xx_edac_pdata *pdata = mci->pvt_info;
+ const dcr_host_t *dcr_host = &pdata->dcr_host;
+
+ mtsdram(dcr_host, SDRAM_ECCES, status->ecces & SDRAM_ECCES_MASK);
+ mtsdram(dcr_host, SDRAM_WMIRQ, status->wmirq & SDRAM_WMIRQ_MASK);
+ mtsdram(dcr_host, SDRAM_BESR, status->besr & SDRAM_BESR_MASK);
+ mtsdram(dcr_host, SDRAM_BEARL, 0);
+ mtsdram(dcr_host, SDRAM_BEARH, 0);
+}
+
+/**
+ * ppc4xx_edac_handle_ce - handle controller correctable ECC error (CE)
+ * @mci: A pointer to the EDAC memory controller instance
+ * associated with the correctable error being handled and reported.
+ * @status: A pointer to the ECC status structure associated with
+ * the correctable error being handled and reported.
+ *
+ * This routine handles an ibm,sdram-4xx-ddr2 controller ECC
+ * correctable error. Per the aforementioned discussion, there's not
+ * enough status available to use the full EDAC correctable error
+ * interface, so we just pass driver-unique message to the "no info"
+ * interface.
+ */
+static void
+ppc4xx_edac_handle_ce(struct mem_ctl_info *mci,
+ const struct ppc4xx_ecc_status *status)
+{
+ int row;
+ char message[PPC4XX_EDAC_MESSAGE_SIZE];
+
+ ppc4xx_edac_generate_message(mci, status, message, sizeof(message));
+
+ for (row = 0; row < mci->nr_csrows; row++)
+ if (ppc4xx_edac_check_bank_error(status, row))
+ edac_mc_handle_ce_no_info(mci, message);
+}
+
+/**
+ * ppc4xx_edac_handle_ue - handle controller uncorrectable ECC error (UE)
+ * @mci: A pointer to the EDAC memory controller instance
+ * associated with the uncorrectable error being handled and
+ * reported.
+ * @status: A pointer to the ECC status structure associated with
+ * the uncorrectable error being handled and reported.
+ *
+ * This routine handles an ibm,sdram-4xx-ddr2 controller ECC
+ * uncorrectable error.
+ */
+static void
+ppc4xx_edac_handle_ue(struct mem_ctl_info *mci,
+ const struct ppc4xx_ecc_status *status)
+{
+ const u64 bear = ((u64)status->bearh << 32 | status->bearl);
+ const unsigned long page = bear >> PAGE_SHIFT;
+ const unsigned long offset = bear & ~PAGE_MASK;
+ int row;
+ char message[PPC4XX_EDAC_MESSAGE_SIZE];
+
+ ppc4xx_edac_generate_message(mci, status, message, sizeof(message));
+
+ for (row = 0; row < mci->nr_csrows; row++)
+ if (ppc4xx_edac_check_bank_error(status, row))
+ edac_mc_handle_ue(mci, page, offset, row, message);
+}
+
+/**
+ * ppc4xx_edac_check - check controller for ECC errors
+ * @mci: A pointer to the EDAC memory controller instance
+ * associated with the ibm,sdram-4xx-ddr2 controller being
+ * checked.
+ *
+ * This routine is used to check and post ECC errors and is called by
+ * both the EDAC polling thread and this driver's CE and UE interrupt
+ * handler.
+ */
+static void
+ppc4xx_edac_check(struct mem_ctl_info *mci)
+{
+#ifdef DEBUG
+ static unsigned int count;
+#endif
+ struct ppc4xx_ecc_status status;
+
+ ppc4xx_ecc_get_status(mci, &status);
+
+#ifdef DEBUG
+ if (count++ % 30 == 0)
+ ppc4xx_ecc_dump_status(mci, &status);
+#endif
+
+ if (status.ecces & SDRAM_ECCES_UE)
+ ppc4xx_edac_handle_ue(mci, &status);
+
+ if (status.ecces & SDRAM_ECCES_CE)
+ ppc4xx_edac_handle_ce(mci, &status);
+
+ ppc4xx_ecc_clear_status(mci, &status);
+}
+
+/**
+ * ppc4xx_edac_isr - SEC (CE) and DED (UE) interrupt service routine
+ * @irq: The virtual interrupt number being serviced.
+ * @dev_id: A pointer to the EDAC memory controller instance
+ * associated with the interrupt being handled.
+ *
+ * This routine implements the interrupt handler for both correctable
+ * (CE) and uncorrectable (UE) ECC errors for the ibm,sdram-4xx-ddr2
+ * controller. It simply calls through to the same routine used during
+ * polling to check, report and clear the ECC status.
+ *
+ * Unconditionally returns IRQ_HANDLED.
+ */
+static irqreturn_t
+ppc4xx_edac_isr(int irq, void *dev_id)
+{
+ struct mem_ctl_info *mci = dev_id;
+
+ ppc4xx_edac_check(mci);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ppc4xx_edac_get_dtype - return the controller memory width
+ * @mcopt1: The 32-bit Memory Controller Option 1 register value
+ * currently set for the controller, from which the width
+ * is derived.
+ *
+ * This routine returns the EDAC device type width appropriate for the
+ * current controller configuration.
+ *
+ * TODO: This needs to be conditioned dynamically through feature
+ * flags or some such when other controller variants are supported as
+ * the 405EX[r] is 16-/32-bit and the others are 32-/64-bit with the
+ * 16- and 64-bit field definition/value/enumeration (b1) overloaded
+ * among them.
+ *
+ * Returns a device type width enumeration.
+ */
+static enum dev_type __devinit
+ppc4xx_edac_get_dtype(u32 mcopt1)
+{
+ switch (mcopt1 & SDRAM_MCOPT1_WDTH_MASK) {
+ case SDRAM_MCOPT1_WDTH_16:
+ return DEV_X2;
+ case SDRAM_MCOPT1_WDTH_32:
+ return DEV_X4;
+ default:
+ return DEV_UNKNOWN;
+ }
+}
+
+/**
+ * ppc4xx_edac_get_mtype - return controller memory type
+ * @mcopt1: The 32-bit Memory Controller Option 1 register value
+ * currently set for the controller, from which the memory type
+ * is derived.
+ *
+ * This routine returns the EDAC memory type appropriate for the
+ * current controller configuration.
+ *
+ * Returns a memory type enumeration.
+ */
+static enum mem_type __devinit
+ppc4xx_edac_get_mtype(u32 mcopt1)
+{
+ bool rden = ((mcopt1 & SDRAM_MCOPT1_RDEN_MASK) == SDRAM_MCOPT1_RDEN);
+
+ switch (mcopt1 & SDRAM_MCOPT1_DDR_TYPE_MASK) {
+ case SDRAM_MCOPT1_DDR2_TYPE:
+ return rden ? MEM_RDDR2 : MEM_DDR2;
+ case SDRAM_MCOPT1_DDR1_TYPE:
+ return rden ? MEM_RDDR : MEM_DDR;
+ default:
+ return MEM_UNKNOWN;
+ }
+}
+
+/**
+ * ppc4xx_edac_init_csrows - intialize driver instance rows
+ * @mci: A pointer to the EDAC memory controller instance
+ * associated with the ibm,sdram-4xx-ddr2 controller for which
+ * the csrows (i.e. banks/ranks) are being initialized.
+ * @mcopt1: The 32-bit Memory Controller Option 1 register value
+ * currently set for the controller, from which bank width
+ * and memory typ information is derived.
+ *
+ * This routine intializes the virtual "chip select rows" associated
+ * with the EDAC memory controller instance. An ibm,sdram-4xx-ddr2
+ * controller bank/rank is mapped to a row.
+ *
+ * Returns 0 if OK; otherwise, -EINVAL if the memory bank size
+ * configuration cannot be determined.
+ */
+static int __devinit
+ppc4xx_edac_init_csrows(struct mem_ctl_info *mci, u32 mcopt1)
+{
+ const struct ppc4xx_edac_pdata *pdata = mci->pvt_info;
+ int status = 0;
+ enum mem_type mtype;
+ enum dev_type dtype;
+ enum edac_type edac_mode;
+ int row;
+ u32 mbxcf, size;
+ static u32 ppc4xx_last_page;
+
+ /* Establish the memory type and width */
+
+ mtype = ppc4xx_edac_get_mtype(mcopt1);
+ dtype = ppc4xx_edac_get_dtype(mcopt1);
+
+ /* Establish EDAC mode */
+
+ if (mci->edac_cap & EDAC_FLAG_SECDED)
+ edac_mode = EDAC_SECDED;
+ else if (mci->edac_cap & EDAC_FLAG_EC)
+ edac_mode = EDAC_EC;
+ else
+ edac_mode = EDAC_NONE;
+
+ /*
+ * Initialize each chip select row structure which correspond
+ * 1:1 with a controller bank/rank.
+ */
+
+ for (row = 0; row < mci->nr_csrows; row++) {
+ struct csrow_info *csi = &mci->csrows[row];
+
+ /*
+ * Get the configuration settings for this
+ * row/bank/rank and skip disabled banks.
+ */
+
+ mbxcf = mfsdram(&pdata->dcr_host, SDRAM_MBXCF(row));
+
+ if ((mbxcf & SDRAM_MBCF_BE_MASK) != SDRAM_MBCF_BE_ENABLE)
+ continue;
+
+ /* Map the bank configuration size setting to pages. */
+
+ size = mbxcf & SDRAM_MBCF_SZ_MASK;
+
+ switch (size) {
+ case SDRAM_MBCF_SZ_4MB:
+ case SDRAM_MBCF_SZ_8MB:
+ case SDRAM_MBCF_SZ_16MB:
+ case SDRAM_MBCF_SZ_32MB:
+ case SDRAM_MBCF_SZ_64MB:
+ case SDRAM_MBCF_SZ_128MB:
+ case SDRAM_MBCF_SZ_256MB:
+ case SDRAM_MBCF_SZ_512MB:
+ case SDRAM_MBCF_SZ_1GB:
+ case SDRAM_MBCF_SZ_2GB:
+ case SDRAM_MBCF_SZ_4GB:
+ case SDRAM_MBCF_SZ_8GB:
+ csi->nr_pages = SDRAM_MBCF_SZ_TO_PAGES(size);
+ break;
+ default:
+ ppc4xx_edac_mc_printk(KERN_ERR, mci,
+ "Unrecognized memory bank %d "
+ "size 0x%08x\n",
+ row, SDRAM_MBCF_SZ_DECODE(size));
+ status = -EINVAL;
+ goto done;
+ }
+
+ csi->first_page = ppc4xx_last_page;
+ csi->last_page = csi->first_page + csi->nr_pages - 1;
+ csi->page_mask = 0;
+
+ /*
+ * It's unclear exactly what grain should be set to
+ * here. The SDRAM_ECCES register allows resolution of
+ * an error down to a nibble which would potentially
+ * argue for a grain of '1' byte, even though we only
+ * know the associated address for uncorrectable
+ * errors. This value is not used at present for
+ * anything other than error reporting so getting it
+ * wrong should be of little consequence. Other
+ * possible values would be the PLB width (16), the
+ * page size (PAGE_SIZE) or the memory width (2 or 4).
+ */
+
+ csi->grain = 1;
+
+ csi->mtype = mtype;
+ csi->dtype = dtype;
+
+ csi->edac_mode = edac_mode;
+
+ ppc4xx_last_page += csi->nr_pages;
+ }
+
+ done:
+ return status;
+}
+
+/**
+ * ppc4xx_edac_mc_init - intialize driver instance
+ * @mci: A pointer to the EDAC memory controller instance being
+ * initialized.
+ * @op: A pointer to the OpenFirmware device tree node associated
+ * with the controller this EDAC instance is bound to.
+ * @match: A pointer to the OpenFirmware device tree match
+ * information associated with the controller this EDAC instance
+ * is bound to.
+ * @dcr_host: A pointer to the DCR data containing the DCR mapping
+ * for this controller instance.
+ * @mcopt1: The 32-bit Memory Controller Option 1 register value
+ * currently set for the controller, from which ECC capabilities
+ * and scrub mode are derived.
+ *
+ * This routine performs initialization of the EDAC memory controller
+ * instance and related driver-private data associated with the
+ * ibm,sdram-4xx-ddr2 memory controller the instance is bound to.
+ *
+ * Returns 0 if OK; otherwise, < 0 on error.
+ */
+static int __devinit
+ppc4xx_edac_mc_init(struct mem_ctl_info *mci,
+ struct of_device *op,
+ const struct of_device_id *match,
+ const dcr_host_t *dcr_host,
+ u32 mcopt1)
+{
+ int status = 0;
+ const u32 memcheck = (mcopt1 & SDRAM_MCOPT1_MCHK_MASK);
+ struct ppc4xx_edac_pdata *pdata = NULL;
+ const struct device_node *np = op->node;
+
+ if (match == NULL)
+ return -EINVAL;
+
+ /* Initial driver pointers and private data */
+
+ mci->dev = &op->dev;
+
+ dev_set_drvdata(mci->dev, mci);
+
+ pdata = mci->pvt_info;
+
+ pdata->dcr_host = *dcr_host;
+ pdata->irqs.sec = NO_IRQ;
+ pdata->irqs.ded = NO_IRQ;
+
+ /* Initialize controller capabilities and configuration */
+
+ mci->mtype_cap = (MEM_FLAG_DDR | MEM_FLAG_RDDR |
+ MEM_FLAG_DDR2 | MEM_FLAG_RDDR2);
+
+ mci->edac_ctl_cap = (EDAC_FLAG_NONE |
+ EDAC_FLAG_EC |
+ EDAC_FLAG_SECDED);
+
+ mci->scrub_cap = SCRUB_NONE;
+ mci->scrub_mode = SCRUB_NONE;
+
+ /*
+ * Update the actual capabilites based on the MCOPT1[MCHK]
+ * settings. Scrubbing is only useful if reporting is enabled.
+ */
+
+ switch (memcheck) {
+ case SDRAM_MCOPT1_MCHK_CHK:
+ mci->edac_cap = EDAC_FLAG_EC;
+ break;
+ case SDRAM_MCOPT1_MCHK_CHK_REP:
+ mci->edac_cap = (EDAC_FLAG_EC | EDAC_FLAG_SECDED);
+ mci->scrub_mode = SCRUB_SW_SRC;
+ break;
+ default:
+ mci->edac_cap = EDAC_FLAG_NONE;
+ break;
+ }
+
+ /* Initialize strings */
+
+ mci->mod_name = PPC4XX_EDAC_MODULE_NAME;
+ mci->mod_ver = PPC4XX_EDAC_MODULE_REVISION;
+ mci->ctl_name = match->compatible,
+ mci->dev_name = np->full_name;
+
+ /* Initialize callbacks */
+
+ mci->edac_check = ppc4xx_edac_check;
+ mci->ctl_page_to_phys = NULL;
+
+ /* Initialize chip select rows */
+
+ status = ppc4xx_edac_init_csrows(mci, mcopt1);
+
+ if (status)
+ ppc4xx_edac_mc_printk(KERN_ERR, mci,
+ "Failed to initialize rows!\n");
+
+ return status;
+}
+
+/**
+ * ppc4xx_edac_register_irq - setup and register controller interrupts
+ * @op: A pointer to the OpenFirmware device tree node associated
+ * with the controller this EDAC instance is bound to.
+ * @mci: A pointer to the EDAC memory controller instance
+ * associated with the ibm,sdram-4xx-ddr2 controller for which
+ * interrupts are being registered.
+ *
+ * This routine parses the correctable (CE) and uncorrectable error (UE)
+ * interrupts from the device tree node and maps and assigns them to
+ * the associated EDAC memory controller instance.
+ *
+ * Returns 0 if OK; otherwise, -ENODEV if the interrupts could not be
+ * mapped and assigned.
+ */
+static int __devinit
+ppc4xx_edac_register_irq(struct of_device *op, struct mem_ctl_info *mci)
+{
+ int status = 0;
+ int ded_irq, sec_irq;
+ struct ppc4xx_edac_pdata *pdata = mci->pvt_info;
+ struct device_node *np = op->node;
+
+ ded_irq = irq_of_parse_and_map(np, INTMAP_ECCDED_INDEX);
+ sec_irq = irq_of_parse_and_map(np, INTMAP_ECCSEC_INDEX);
+
+ if (ded_irq == NO_IRQ || sec_irq == NO_IRQ) {
+ ppc4xx_edac_mc_printk(KERN_ERR, mci,
+ "Unable to map interrupts.\n");
+ status = -ENODEV;
+ goto fail;
+ }
+
+ status = request_irq(ded_irq,
+ ppc4xx_edac_isr,
+ IRQF_DISABLED,
+ "[EDAC] MC ECCDED",
+ mci);
+
+ if (status < 0) {
+ ppc4xx_edac_mc_printk(KERN_ERR, mci,
+ "Unable to request irq %d for ECC DED",
+ ded_irq);
+ status = -ENODEV;
+ goto fail1;
+ }
+
+ status = request_irq(sec_irq,
+ ppc4xx_edac_isr,
+ IRQF_DISABLED,
+ "[EDAC] MC ECCSEC",
+ mci);
+
+ if (status < 0) {
+ ppc4xx_edac_mc_printk(KERN_ERR, mci,
+ "Unable to request irq %d for ECC SEC",
+ sec_irq);
+ status = -ENODEV;
+ goto fail2;
+ }
+
+ ppc4xx_edac_mc_printk(KERN_INFO, mci, "ECCDED irq is %d\n", ded_irq);
+ ppc4xx_edac_mc_printk(KERN_INFO, mci, "ECCSEC irq is %d\n", sec_irq);
+
+ pdata->irqs.ded = ded_irq;
+ pdata->irqs.sec = sec_irq;
+
+ return 0;
+
+ fail2:
+ free_irq(sec_irq, mci);
+
+ fail1:
+ free_irq(ded_irq, mci);
+
+ fail:
+ return status;
+}
+
+/**
+ * ppc4xx_edac_map_dcrs - locate and map controller registers
+ * @np: A pointer to the device tree node containing the DCR
+ * resources to map.
+ * @dcr_host: A pointer to the DCR data to populate with the
+ * DCR mapping.
+ *
+ * This routine attempts to locate in the device tree and map the DCR
+ * register resources associated with the controller's indirect DCR
+ * address and data windows.
+ *
+ * Returns 0 if the DCRs were successfully mapped; otherwise, < 0 on
+ * error.
+ */
+static int __devinit
+ppc4xx_edac_map_dcrs(const struct device_node *np, dcr_host_t *dcr_host)
+{
+ unsigned int dcr_base, dcr_len;
+
+ if (np == NULL || dcr_host == NULL)
+ return -EINVAL;
+
+ /* Get the DCR resource extent and sanity check the values. */
+
+ dcr_base = dcr_resource_start(np, 0);
+ dcr_len = dcr_resource_len(np, 0);
+
+ if (dcr_base == 0 || dcr_len == 0) {
+ ppc4xx_edac_printk(KERN_ERR,
+ "Failed to obtain DCR property.\n");
+ return -ENODEV;
+ }
+
+ if (dcr_len != SDRAM_DCR_RESOURCE_LEN) {
+ ppc4xx_edac_printk(KERN_ERR,
+ "Unexpected DCR length %d, expected %d.\n",
+ dcr_len, SDRAM_DCR_RESOURCE_LEN);
+ return -ENODEV;
+ }
+
+ /* Attempt to map the DCR extent. */
+
+ *dcr_host = dcr_map(np, dcr_base, dcr_len);
+
+ if (!DCR_MAP_OK(*dcr_host)) {
+ ppc4xx_edac_printk(KERN_INFO, "Failed to map DCRs.\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+/**
+ * ppc4xx_edac_probe - check controller and bind driver
+ * @op: A pointer to the OpenFirmware device tree node associated
+ * with the controller being probed for driver binding.
+ * @match: A pointer to the OpenFirmware device tree match
+ * information associated with the controller being probed
+ * for driver binding.
+ *
+ * This routine probes a specific ibm,sdram-4xx-ddr2 controller
+ * instance for binding with the driver.
+ *
+ * Returns 0 if the controller instance was successfully bound to the
+ * driver; otherwise, < 0 on error.
+ */
+static int __devinit
+ppc4xx_edac_probe(struct of_device *op, const struct of_device_id *match)
+{
+ int status = 0;
+ u32 mcopt1, memcheck;
+ dcr_host_t dcr_host;
+ const struct device_node *np = op->node;
+ struct mem_ctl_info *mci = NULL;
+ static int ppc4xx_edac_instance;
+
+ /*
+ * At this point, we only support the controller realized on
+ * the AMCC PPC 405EX[r]. Reject anything else.
+ */
+
+ if (!of_device_is_compatible(np, "ibm,sdram-405ex") &&
+ !of_device_is_compatible(np, "ibm,sdram-405exr")) {
+ ppc4xx_edac_printk(KERN_NOTICE,
+ "Only the PPC405EX[r] is supported.\n");
+ return -ENODEV;
+ }
+
+ /*
+ * Next, get the DCR property and attempt to map it so that we
+ * can probe the controller.
+ */
+
+ status = ppc4xx_edac_map_dcrs(np, &dcr_host);
+
+ if (status)
+ return status;
+
+ /*
+ * First determine whether ECC is enabled at all. If not,
+ * there is no useful checking or monitoring that can be done
+ * for this controller.
+ */
+
+ mcopt1 = mfsdram(&dcr_host, SDRAM_MCOPT1);
+ memcheck = (mcopt1 & SDRAM_MCOPT1_MCHK_MASK);
+
+ if (memcheck == SDRAM_MCOPT1_MCHK_NON) {
+ ppc4xx_edac_printk(KERN_INFO, "%s: No ECC memory detected or "
+ "ECC is disabled.\n", np->full_name);
+ status = -ENODEV;
+ goto done;
+ }
+
+ /*
+ * At this point, we know ECC is enabled, allocate an EDAC
+ * controller instance and perform the appropriate
+ * initialization.
+ */
+
+ mci = edac_mc_alloc(sizeof(struct ppc4xx_edac_pdata),
+ ppc4xx_edac_nr_csrows,
+ ppc4xx_edac_nr_chans,
+ ppc4xx_edac_instance);
+
+ if (mci == NULL) {
+ ppc4xx_edac_printk(KERN_ERR, "%s: "
+ "Failed to allocate EDAC MC instance!\n",
+ np->full_name);
+ status = -ENOMEM;
+ goto done;
+ }
+
+ status = ppc4xx_edac_mc_init(mci, op, match, &dcr_host, mcopt1);
+
+ if (status) {
+ ppc4xx_edac_mc_printk(KERN_ERR, mci,
+ "Failed to initialize instance!\n");
+ goto fail;
+ }
+
+ /*
+ * We have a valid, initialized EDAC instance bound to the
+ * controller. Attempt to register it with the EDAC subsystem
+ * and, if necessary, register interrupts.
+ */
+
+ if (edac_mc_add_mc(mci)) {
+ ppc4xx_edac_mc_printk(KERN_ERR, mci,
+ "Failed to add instance!\n");
+ status = -ENODEV;
+ goto fail;
+ }
+
+ if (edac_op_state == EDAC_OPSTATE_INT) {
+ status = ppc4xx_edac_register_irq(op, mci);
+
+ if (status)
+ goto fail1;
+ }
+
+ ppc4xx_edac_instance++;
+
+ return 0;
+
+ fail1:
+ edac_mc_del_mc(mci->dev);
+
+ fail:
+ edac_mc_free(mci);
+
+ done:
+ return status;
+}
+
+/**
+ * ppc4xx_edac_remove - unbind driver from controller
+ * @op: A pointer to the OpenFirmware device tree node associated
+ * with the controller this EDAC instance is to be unbound/removed
+ * from.
+ *
+ * This routine unbinds the EDAC memory controller instance associated
+ * with the specified ibm,sdram-4xx-ddr2 controller described by the
+ * OpenFirmware device tree node passed as a parameter.
+ *
+ * Unconditionally returns 0.
+ */
+static int
+ppc4xx_edac_remove(struct of_device *op)
+{
+ struct mem_ctl_info *mci = dev_get_drvdata(&op->dev);
+ struct ppc4xx_edac_pdata *pdata = mci->pvt_info;
+
+ if (edac_op_state == EDAC_OPSTATE_INT) {
+ free_irq(pdata->irqs.sec, mci);
+ free_irq(pdata->irqs.ded, mci);
+ }
+
+ dcr_unmap(pdata->dcr_host, SDRAM_DCR_RESOURCE_LEN);
+
+ edac_mc_del_mc(mci->dev);
+ edac_mc_free(mci);
+
+ return 0;
+}
+
+/**
+ * ppc4xx_edac_opstate_init - initialize EDAC reporting method
+ *
+ * This routine ensures that the EDAC memory controller reporting
+ * method is mapped to a sane value as the EDAC core defines the value
+ * to EDAC_OPSTATE_INVAL by default. We don't call the global
+ * opstate_init as that defaults to polling and we want interrupt as
+ * the default.
+ */
+static inline void __init
+ppc4xx_edac_opstate_init(void)
+{
+ switch (edac_op_state) {
+ case EDAC_OPSTATE_POLL:
+ case EDAC_OPSTATE_INT:
+ break;
+ default:
+ edac_op_state = EDAC_OPSTATE_INT;
+ break;
+ }
+
+ ppc4xx_edac_printk(KERN_INFO, "Reporting type: %s\n",
+ ((edac_op_state == EDAC_OPSTATE_POLL) ?
+ EDAC_OPSTATE_POLL_STR :
+ ((edac_op_state == EDAC_OPSTATE_INT) ?
+ EDAC_OPSTATE_INT_STR :
+ EDAC_OPSTATE_UNKNOWN_STR)));
+}
+
+/**
+ * ppc4xx_edac_init - driver/module insertion entry point
+ *
+ * This routine is the driver/module insertion entry point. It
+ * initializes the EDAC memory controller reporting state and
+ * registers the driver as an OpenFirmware device tree platform
+ * driver.
+ */
+static int __init
+ppc4xx_edac_init(void)
+{
+ ppc4xx_edac_printk(KERN_INFO, PPC4XX_EDAC_MODULE_REVISION "\n");
+
+ ppc4xx_edac_opstate_init();
+
+ return of_register_platform_driver(&ppc4xx_edac_driver);
+}
+
+/**
+ * ppc4xx_edac_exit - driver/module removal entry point
+ *
+ * This routine is the driver/module removal entry point. It
+ * unregisters the driver as an OpenFirmware device tree platform
+ * driver.
+ */
+static void __exit
+ppc4xx_edac_exit(void)
+{
+ of_unregister_platform_driver(&ppc4xx_edac_driver);
+}
+
+module_init(ppc4xx_edac_init);
+module_exit(ppc4xx_edac_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Grant Erickson <gerickson@nuovations.com>");
+MODULE_DESCRIPTION("EDAC MC Driver for the PPC4xx IBM DDR2 Memory Controller");
+module_param(edac_op_state, int, 0444);
+MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting State: "
+ "0=" EDAC_OPSTATE_POLL_STR ", 2=" EDAC_OPSTATE_INT_STR);
diff --git a/drivers/edac/ppc4xx_edac.h b/drivers/edac/ppc4xx_edac.h
new file mode 100644
index 00000000000..d3154764c44
--- /dev/null
+++ b/drivers/edac/ppc4xx_edac.h
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2008 Nuovation System Designs, LLC
+ * Grant Erickson <gerickson@nuovations.com>
+ *
+ * This file defines processor mnemonics for accessing and managing
+ * the IBM DDR1/DDR2 ECC controller found in the 405EX[r], 440SP,
+ * 440SPe, 460EX, 460GT and 460SX.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the
+ * License.
+ *
+ */
+
+#ifndef __PPC4XX_EDAC_H
+#define __PPC4XX_EDAC_H
+
+#include <linux/types.h>
+
+/*
+ * Macro for generating register field mnemonics
+ */
+#define PPC_REG_BITS 32
+#define PPC_REG_VAL(bit, val) ((val) << ((PPC_REG_BITS - 1) - (bit)))
+#define PPC_REG_DECODE(bit, val) ((val) >> ((PPC_REG_BITS - 1) - (bit)))
+
+/*
+ * IBM 4xx DDR1/DDR2 SDRAM memory controller registers (at least those
+ * relevant to ECC)
+ */
+#define SDRAM_BESR 0x00 /* Error status (read/clear) */
+#define SDRAM_BESRT 0x01 /* Error statuss (test/set) */
+#define SDRAM_BEARL 0x02 /* Error address low */
+#define SDRAM_BEARH 0x03 /* Error address high */
+#define SDRAM_WMIRQ 0x06 /* Write master (read/clear) */
+#define SDRAM_WMIRQT 0x07 /* Write master (test/set) */
+#define SDRAM_MCOPT1 0x20 /* Controller options 1 */
+#define SDRAM_MBXCF_BASE 0x40 /* Bank n configuration base */
+#define SDRAM_MBXCF(n) (SDRAM_MBXCF_BASE + (4 * (n)))
+#define SDRAM_MB0CF SDRAM_MBXCF(0)
+#define SDRAM_MB1CF SDRAM_MBXCF(1)
+#define SDRAM_MB2CF SDRAM_MBXCF(2)
+#define SDRAM_MB3CF SDRAM_MBXCF(3)
+#define SDRAM_ECCCR 0x98 /* ECC error status */
+#define SDRAM_ECCES SDRAM_ECCCR
+
+/*
+ * PLB Master IDs
+ */
+#define SDRAM_PLB_M0ID_FIRST 0
+#define SDRAM_PLB_M0ID_ICU SDRAM_PLB_M0ID_FIRST
+#define SDRAM_PLB_M0ID_PCIE0 1
+#define SDRAM_PLB_M0ID_PCIE1 2
+#define SDRAM_PLB_M0ID_DMA 3
+#define SDRAM_PLB_M0ID_DCU 4
+#define SDRAM_PLB_M0ID_OPB 5
+#define SDRAM_PLB_M0ID_MAL 6
+#define SDRAM_PLB_M0ID_SEC 7
+#define SDRAM_PLB_M0ID_AHB 8
+#define SDRAM_PLB_M0ID_LAST SDRAM_PLB_M0ID_AHB
+#define SDRAM_PLB_M0ID_COUNT (SDRAM_PLB_M0ID_LAST - \
+ SDRAM_PLB_M0ID_FIRST + 1)
+
+/*
+ * Memory Controller Bus Error Status Register
+ */
+#define SDRAM_BESR_MASK PPC_REG_VAL(7, 0xFF)
+#define SDRAM_BESR_M0ID_MASK PPC_REG_VAL(3, 0xF)
+#define SDRAM_BESR_M0ID_DECODE(n) PPC_REG_DECODE(3, n)
+#define SDRAM_BESR_M0ID_ICU PPC_REG_VAL(3, SDRAM_PLB_M0ID_ICU)
+#define SDRAM_BESR_M0ID_PCIE0 PPC_REG_VAL(3, SDRAM_PLB_M0ID_PCIE0)
+#define SDRAM_BESR_M0ID_PCIE1 PPC_REG_VAL(3, SDRAM_PLB_M0ID_PCIE1)
+#define SDRAM_BESR_M0ID_DMA PPC_REG_VAL(3, SDRAM_PLB_M0ID_DMA)
+#define SDRAM_BESR_M0ID_DCU PPC_REG_VAL(3, SDRAM_PLB_M0ID_DCU)
+#define SDRAM_BESR_M0ID_OPB PPC_REG_VAL(3, SDRAM_PLB_M0ID_OPB)
+#define SDRAM_BESR_M0ID_MAL PPC_REG_VAL(3, SDRAM_PLB_M0ID_MAL)
+#define SDRAM_BESR_M0ID_SEC PPC_REG_VAL(3, SDRAM_PLB_M0ID_SEC)
+#define SDRAM_BESR_M0ID_AHB PPC_REG_VAL(3, SDRAM_PLB_M0ID_AHB)
+#define SDRAM_BESR_M0ET_MASK PPC_REG_VAL(6, 0x7)
+#define SDRAM_BESR_M0ET_NONE PPC_REG_VAL(6, 0)
+#define SDRAM_BESR_M0ET_ECC PPC_REG_VAL(6, 1)
+#define SDRAM_BESR_M0RW_MASK PPC_REG_VAL(7, 1)
+#define SDRAM_BESR_M0RW_WRITE PPC_REG_VAL(7, 0)
+#define SDRAM_BESR_M0RW_READ PPC_REG_VAL(7, 1)
+
+/*
+ * Memory Controller PLB Write Master Interrupt Register
+ */
+#define SDRAM_WMIRQ_MASK PPC_REG_VAL(8, 0x1FF)
+#define SDRAM_WMIRQ_ENCODE(id) PPC_REG_VAL((id % \
+ SDRAM_PLB_M0ID_COUNT), 1)
+#define SDRAM_WMIRQ_ICU PPC_REG_VAL(SDRAM_PLB_M0ID_ICU, 1)
+#define SDRAM_WMIRQ_PCIE0 PPC_REG_VAL(SDRAM_PLB_M0ID_PCIE0, 1)
+#define SDRAM_WMIRQ_PCIE1 PPC_REG_VAL(SDRAM_PLB_M0ID_PCIE1, 1)
+#define SDRAM_WMIRQ_DMA PPC_REG_VAL(SDRAM_PLB_M0ID_DMA, 1)
+#define SDRAM_WMIRQ_DCU PPC_REG_VAL(SDRAM_PLB_M0ID_DCU, 1)
+#define SDRAM_WMIRQ_OPB PPC_REG_VAL(SDRAM_PLB_M0ID_OPB, 1)
+#define SDRAM_WMIRQ_MAL PPC_REG_VAL(SDRAM_PLB_M0ID_MAL, 1)
+#define SDRAM_WMIRQ_SEC PPC_REG_VAL(SDRAM_PLB_M0ID_SEC, 1)
+#define SDRAM_WMIRQ_AHB PPC_REG_VAL(SDRAM_PLB_M0ID_AHB, 1)
+
+/*
+ * Memory Controller Options 1 Register
+ */
+#define SDRAM_MCOPT1_MCHK_MASK PPC_REG_VAL(3, 0x3) /* ECC mask */
+#define SDRAM_MCOPT1_MCHK_NON PPC_REG_VAL(3, 0x0) /* No ECC gen */
+#define SDRAM_MCOPT1_MCHK_GEN PPC_REG_VAL(3, 0x2) /* ECC gen */
+#define SDRAM_MCOPT1_MCHK_CHK PPC_REG_VAL(3, 0x1) /* ECC gen and chk */
+#define SDRAM_MCOPT1_MCHK_CHK_REP PPC_REG_VAL(3, 0x3) /* ECC gen/chk/rpt */
+#define SDRAM_MCOPT1_MCHK_DECODE(n) ((((u32)(n)) >> 28) & 0x3)
+#define SDRAM_MCOPT1_RDEN_MASK PPC_REG_VAL(4, 0x1) /* Rgstrd DIMM mask */
+#define SDRAM_MCOPT1_RDEN PPC_REG_VAL(4, 0x1) /* Rgstrd DIMM enbl */
+#define SDRAM_MCOPT1_WDTH_MASK PPC_REG_VAL(7, 0x1) /* Width mask */
+#define SDRAM_MCOPT1_WDTH_32 PPC_REG_VAL(7, 0x0) /* 32 bits */
+#define SDRAM_MCOPT1_WDTH_16 PPC_REG_VAL(7, 0x1) /* 16 bits */
+#define SDRAM_MCOPT1_DDR_TYPE_MASK PPC_REG_VAL(11, 0x1) /* DDR type mask */
+#define SDRAM_MCOPT1_DDR1_TYPE PPC_REG_VAL(11, 0x0) /* DDR1 type */
+#define SDRAM_MCOPT1_DDR2_TYPE PPC_REG_VAL(11, 0x1) /* DDR2 type */
+
+/*
+ * Memory Bank 0 - n Configuration Register
+ */
+#define SDRAM_MBCF_BA_MASK PPC_REG_VAL(12, 0x1FFF)
+#define SDRAM_MBCF_SZ_MASK PPC_REG_VAL(19, 0xF)
+#define SDRAM_MBCF_SZ_DECODE(mbxcf) PPC_REG_DECODE(19, mbxcf)
+#define SDRAM_MBCF_SZ_4MB PPC_REG_VAL(19, 0x0)
+#define SDRAM_MBCF_SZ_8MB PPC_REG_VAL(19, 0x1)
+#define SDRAM_MBCF_SZ_16MB PPC_REG_VAL(19, 0x2)
+#define SDRAM_MBCF_SZ_32MB PPC_REG_VAL(19, 0x3)
+#define SDRAM_MBCF_SZ_64MB PPC_REG_VAL(19, 0x4)
+#define SDRAM_MBCF_SZ_128MB PPC_REG_VAL(19, 0x5)
+#define SDRAM_MBCF_SZ_256MB PPC_REG_VAL(19, 0x6)
+#define SDRAM_MBCF_SZ_512MB PPC_REG_VAL(19, 0x7)
+#define SDRAM_MBCF_SZ_1GB PPC_REG_VAL(19, 0x8)
+#define SDRAM_MBCF_SZ_2GB PPC_REG_VAL(19, 0x9)
+#define SDRAM_MBCF_SZ_4GB PPC_REG_VAL(19, 0xA)
+#define SDRAM_MBCF_SZ_8GB PPC_REG_VAL(19, 0xB)
+#define SDRAM_MBCF_AM_MASK PPC_REG_VAL(23, 0xF)
+#define SDRAM_MBCF_AM_MODE0 PPC_REG_VAL(23, 0x0)
+#define SDRAM_MBCF_AM_MODE1 PPC_REG_VAL(23, 0x1)
+#define SDRAM_MBCF_AM_MODE2 PPC_REG_VAL(23, 0x2)
+#define SDRAM_MBCF_AM_MODE3 PPC_REG_VAL(23, 0x3)
+#define SDRAM_MBCF_AM_MODE4 PPC_REG_VAL(23, 0x4)
+#define SDRAM_MBCF_AM_MODE5 PPC_REG_VAL(23, 0x5)
+#define SDRAM_MBCF_AM_MODE6 PPC_REG_VAL(23, 0x6)
+#define SDRAM_MBCF_AM_MODE7 PPC_REG_VAL(23, 0x7)
+#define SDRAM_MBCF_AM_MODE8 PPC_REG_VAL(23, 0x8)
+#define SDRAM_MBCF_AM_MODE9 PPC_REG_VAL(23, 0x9)
+#define SDRAM_MBCF_BE_MASK PPC_REG_VAL(31, 0x1)
+#define SDRAM_MBCF_BE_DISABLE PPC_REG_VAL(31, 0x0)
+#define SDRAM_MBCF_BE_ENABLE PPC_REG_VAL(31, 0x1)
+
+/*
+ * ECC Error Status
+ */
+#define SDRAM_ECCES_MASK PPC_REG_VAL(21, 0x3FFFFF)
+#define SDRAM_ECCES_BNCE_MASK PPC_REG_VAL(15, 0xFFFF)
+#define SDRAM_ECCES_BNCE_ENCODE(lane) PPC_REG_VAL(((lane) & 0xF), 1)
+#define SDRAM_ECCES_CKBER_MASK PPC_REG_VAL(17, 0x3)
+#define SDRAM_ECCES_CKBER_NONE PPC_REG_VAL(17, 0)
+#define SDRAM_ECCES_CKBER_16_ECC_0_3 PPC_REG_VAL(17, 2)
+#define SDRAM_ECCES_CKBER_32_ECC_0_3 PPC_REG_VAL(17, 1)
+#define SDRAM_ECCES_CKBER_32_ECC_4_8 PPC_REG_VAL(17, 2)
+#define SDRAM_ECCES_CKBER_32_ECC_0_8 PPC_REG_VAL(17, 3)
+#define SDRAM_ECCES_CE PPC_REG_VAL(18, 1)
+#define SDRAM_ECCES_UE PPC_REG_VAL(19, 1)
+#define SDRAM_ECCES_BKNER_MASK PPC_REG_VAL(21, 0x3)
+#define SDRAM_ECCES_BK0ER PPC_REG_VAL(20, 1)
+#define SDRAM_ECCES_BK1ER PPC_REG_VAL(21, 1)
+
+#endif /* __PPC4XX_EDAC_H */