[linux-yocto] [PATCH 06/38] arch/arm/drivers/edac: Changes to support axxia BSP
Cristian Bercaru
cristian.bercaru at windriver.com
Wed May 20 12:23:49 PDT 2015
From: Charlie Paul <cpaul.windriver at gmail.com>
These files were changed to add the support of edac
to the LSI axxia 5500 board.
Signed-off-by: Charlie Paul <cpaul.windriver at gmail.com>
---
drivers/edac/Kconfig | 24 ++
drivers/edac/Makefile | 3 +
drivers/edac/axxia_edac-l2_cpu.c | 333 +++++++++++++++++++++++++++
drivers/edac/axxia_edac-l3.c | 185 +++++++++++++++
drivers/edac/axxia_edac-mc.c | 351 +++++++++++++++++++++++++++++
drivers/edac/axxia_edac.c | 461 ++++++++++++++++++++++++++++++++++++++
6 files changed, 1357 insertions(+)
create mode 100644 drivers/edac/axxia_edac-l2_cpu.c
create mode 100644 drivers/edac/axxia_edac-l3.c
create mode 100644 drivers/edac/axxia_edac-mc.c
create mode 100644 drivers/edac/axxia_edac.c
diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig
index 49c2652..3aacf72 100644
--- a/drivers/edac/Kconfig
+++ b/drivers/edac/Kconfig
@@ -376,6 +376,30 @@ config EDAC_OCTEON_PCI
Support for error detection and correction on the
Cavium Octeon family of SOCs.
+config EDAC_AXXIA_SYSMEM
+ tristate "AXXIA EDAC SysMem Controller"
+ help
+ Support for System Memory Denali controller error
+ detection on the AXXIA AXM55xx devices. This enables
+ the System Memory error detection. System Memory error
+ detection is interrupt driven.
+
+config EDAC_AXXIA_L3
+ tristate "AXXIA EDAC L3 Controller"
+ help
+ Support for the eight L3 caches error detection
+ on the AXXIA AXM55xx devices. This enables the
+ L3 cache error detection. L3 cache error detection
+ uses polling mechanism.
+
+config EDAC_AXXIA_L2_CPU
+ tristate "AXXIA EDAC L2/CPU Controller"
+ help
+ Support for L2 cache and A15 CPU error detection
+ on AXXIA AXM55xx devices. This enables the L2
+ cache and A15 CPU error detction. L2 cache and A15
+ CPU error detection uses polling mechanism.
+
config EDAC_ALTERA_MC
tristate "Altera SDRAM Memory Controller EDAC"
depends on EDAC_MM_EDAC && ARCH_SOCFPGA
diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile
index d40c69a..b39abb4 100644
--- a/drivers/edac/Makefile
+++ b/drivers/edac/Makefile
@@ -16,6 +16,9 @@ ifdef CONFIG_PCI
edac_core-y += edac_pci.o edac_pci_sysfs.o
endif
+obj-$(CONFIG_EDAC_AXXIA_SYSMEM) += axxia_edac-mc.o
+obj-$(CONFIG_EDAC_AXXIA_L3) += axxia_edac-l3.o
+obj-$(CONFIG_EDAC_AXXIA_L2_CPU) += axxia_edac-l2_cpu.o
obj-$(CONFIG_EDAC_GHES) += ghes_edac.o
obj-$(CONFIG_EDAC_MCE_INJ) += mce_amd_inj.o
diff --git a/drivers/edac/axxia_edac-l2_cpu.c b/drivers/edac/axxia_edac-l2_cpu.c
new file mode 100644
index 0000000..534562d
--- /dev/null
+++ b/drivers/edac/axxia_edac-l2_cpu.c
@@ -0,0 +1,333 @@
+ /*
+ * drivers/edac/axxia_edac-l2_cpu.c
+ *
+ * EDAC Driver for Avago's Axxia 5500 A15 CPUs and L2 caches
+ *
+ * Copyright (C) 2010 LSI Inc.
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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/slab.h>
+#include <linux/io.h>
+#include <linux/edac.h>
+#include <mach/ncr.h>
+#include <linux/of_platform.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include "edac_core.h"
+#include "edac_module.h"
+
+#define LSI_EDAC_MOD_STR "lsi_edac"
+#define CORES_PER_CLUSTER 4
+
+#define APB2_PERSIST_SCRATCH 0xdc
+#define L2_PERSIST_SCRATCH_BIT (0x1 << 5)
+#define CPU_PERSIST_SCRATCH_BIT (0x1 << 6)
+
+/* Private structure for common edac device */
+struct lsi_edac_dev_info {
+ struct platform_device *pdev;
+ char *ctl_name;
+ char *blk_name;
+ int edac_idx;
+ struct regmap *syscon;
+ struct edac_device_ctl_info *edac_dev;
+ void (*check)(struct edac_device_ctl_info *edac_dev);
+};
+
+static inline u64 read_cpumerrsr(void)
+{
+ u64 val;
+
+ asm volatile("mrrc\tp15, 0, %Q0, %R0, c15" : "=r" (val));
+ return val;
+}
+
+static inline u64 read_l2merrsr(void)
+{
+ u64 val;
+
+ asm volatile("mrrc\tp15, 1, %Q0, %R0, c15" : "=r"(val));
+ return val;
+}
+
+inline void write_cpumerrsr(u64 val)
+{
+ asm volatile("mcrr\tp15, 0, %Q0, %R0, c15" : : "r" (val));
+}
+
+inline void write_l2merrsr(u64 val)
+{
+ asm volatile("mcrr\tp15, 1, %Q0, %R0, c15" : : "r"(val));
+}
+
+void log_cpumerrsr(void *edac)
+{
+ struct edac_device_ctl_info *edac_dev = edac;
+ u64 val, clear_val;
+ u32 count0, count1;
+ int i;
+ struct lsi_edac_dev_info *dev_info;
+
+ dev_info = edac_dev->pvt_info;
+
+ /* Read cp15 for CPUMERRSR counts */
+ val = read_cpumerrsr();
+ if (val & 0x80000000) {
+ int cpu = get_cpu();
+
+ count0 = ((val) & 0x000000ff00000000) >> 20;
+ count1 = ((val) & 0x0000ff0000000000) >> 28;
+
+ /* increment correctable error counts */
+ for (i = 0; i < count0+count1; i++) {
+ edac_device_handle_ce(edac_dev, 0,
+ cpu, edac_dev->ctl_name);
+ }
+
+ /* Clear the valid bit */
+ clear_val = 0x80000000;
+ write_cpumerrsr(clear_val);
+ put_cpu();
+ }
+ if (val & 0x8000000000000000) {
+ regmap_update_bits(dev_info->syscon,
+ APB2_PERSIST_SCRATCH,
+ CPU_PERSIST_SCRATCH_BIT,
+ CPU_PERSIST_SCRATCH_BIT);
+ pr_emerg("CPU uncorrectable error\n");
+ machine_restart(NULL);
+ }
+}
+
+
+/* Check for CPU Errors */
+static void lsi_cpu_error_check(struct edac_device_ctl_info *edac_dev)
+{
+ /* execute on current cpu */
+ log_cpumerrsr(edac_dev);
+
+ /* send ipis to execute on other cpus */
+ smp_call_function(log_cpumerrsr, edac_dev, 1);
+
+}
+
+void log_l2merrsr(void *edac)
+{
+ struct edac_device_ctl_info *edac_dev = edac;
+ u64 val, clear_val;
+ u32 count0, count1;
+ int i;
+ struct lsi_edac_dev_info *dev_info;
+
+ dev_info = edac_dev->pvt_info;
+
+ val = read_l2merrsr();
+ if (val & 0x80000000) {
+ int cpu = get_cpu();
+
+ count0 = ((val) & 0x000000ff00000000) >> 20;
+ count1 = ((val) & 0x0000ff0000000000) >> 28;
+
+ /* increment correctable error counts */
+ for (i = 0; i < count0+count1; i++) {
+ edac_device_handle_ce(edac_dev, 0,
+ cpu/CORES_PER_CLUSTER,
+ edac_dev->ctl_name);
+ }
+
+ /* Clear the valid bit */
+ clear_val = 0x80000000;
+ write_l2merrsr(clear_val);
+ put_cpu();
+ }
+ if (val & 0x8000000000000000) {
+ regmap_update_bits(dev_info->syscon,
+ APB2_PERSIST_SCRATCH,
+ L2_PERSIST_SCRATCH_BIT,
+ L2_PERSIST_SCRATCH_BIT);
+ pr_emerg("L2 uncorrectable error\n");
+ machine_restart(NULL);
+ }
+}
+
+/* Check for L2 Errors */
+static void lsi_l2_error_check(struct edac_device_ctl_info *edac_dev)
+{
+ /* 4 cores per cluster */
+ int nr_cluster_ids = ((nr_cpu_ids - 1) / CORES_PER_CLUSTER) + 1;
+ int i, j, cpu;
+
+ /* execute on current cpu */
+ log_l2merrsr(edac_dev);
+
+ for (i = 0; i < nr_cluster_ids; i++) {
+ /* No need to run on local cluster. */
+ if (i == (get_cpu() / CORES_PER_CLUSTER)) {
+ put_cpu();
+ continue;
+ }
+ /*
+ * Have some core in each cluster execute this,
+ * Start with the first core on that cluster.
+ */
+ cpu = i * CORES_PER_CLUSTER;
+ for (j = cpu; j < cpu + CORES_PER_CLUSTER; j++) {
+ if (cpu_online(j)) {
+ smp_call_function_single(j, log_l2merrsr,
+ edac_dev, 1);
+ break;
+ }
+ }
+ put_cpu();
+ }
+}
+
+static void lsi_add_edac_devices(struct platform_device *pdev,
+ int num)
+{
+ struct lsi_edac_dev_info *dev_info = NULL;
+ /* 4 cores per cluster */
+ int nr_cluster_ids = ((nr_cpu_ids - 1) / CORES_PER_CLUSTER) + 1;
+ struct device_node *np = pdev->dev.of_node;
+
+ dev_info = devm_kzalloc(&pdev->dev, sizeof(*dev_info), GFP_KERNEL);
+ if (!dev_info)
+ return;
+
+ dev_info->ctl_name = kstrdup(np->name, GFP_KERNEL);
+ if (num == 0) {
+ dev_info->blk_name = "cpumerrsr";
+ dev_info->check = lsi_cpu_error_check;
+ } else {
+ dev_info->blk_name = "l2merrsr";
+ dev_info->check = lsi_l2_error_check;
+ }
+ dev_info->pdev = pdev;
+ dev_info->edac_idx = edac_device_alloc_index();
+ dev_info->syscon =
+ syscon_regmap_lookup_by_phandle(np, "syscon");
+ if (IS_ERR(dev_info->syscon)) {
+ pr_info("%s: syscon lookup failed\n", np->name);
+ goto err1;
+ }
+
+ if (strcmp(dev_info->ctl_name, "edac_cpu") == 0) {
+ dev_info->edac_dev =
+ edac_device_alloc_ctl_info(0, dev_info->ctl_name,
+ 1, dev_info->blk_name, num_possible_cpus(), 0, NULL,
+ 0, dev_info->edac_idx);
+ } else if (strcmp(dev_info->ctl_name, "edac_l2") == 0) {
+ dev_info->edac_dev =
+ edac_device_alloc_ctl_info(0, dev_info->ctl_name,
+ 1, dev_info->blk_name, nr_cluster_ids, 0, NULL,
+ 0, dev_info->edac_idx);
+ }
+ if (!dev_info->edac_dev) {
+ pr_info("No memory for edac device\n");
+ goto err1;
+ }
+
+ dev_info->edac_dev->pvt_info = dev_info;
+ dev_info->edac_dev->dev = &dev_info->pdev->dev;
+ dev_info->edac_dev->ctl_name = dev_info->ctl_name;
+ dev_info->edac_dev->mod_name = LSI_EDAC_MOD_STR;
+ dev_info->edac_dev->dev_name = dev_name(&dev_info->pdev->dev);
+
+ dev_info->edac_dev->edac_check = dev_info->check;
+
+ if (edac_device_add_device(dev_info->edac_dev) != 0) {
+ pr_info("Unable to add edac device for %s\n",
+ dev_info->ctl_name);
+ goto err2;
+ }
+
+ return;
+err2:
+ edac_device_free_ctl_info(dev_info->edac_dev);
+err1:
+ platform_device_unregister(dev_info->pdev);
+}
+
+static int lsi_edac_cpu_probe(struct platform_device *pdev)
+{
+ edac_op_state = EDAC_OPSTATE_POLL;
+ lsi_add_edac_devices(pdev, 0);
+ return 0;
+}
+
+static int lsi_edac_cpu_remove(struct platform_device *pdev)
+{
+ platform_device_unregister(pdev);
+ return 0;
+}
+
+static int lsi_edac_l2_probe(struct platform_device *pdev)
+{
+ edac_op_state = EDAC_OPSTATE_POLL;
+ lsi_add_edac_devices(pdev, 1);
+ return 0;
+}
+
+static int lsi_edac_l2_remove(struct platform_device *pdev)
+{
+ platform_device_unregister(pdev);
+ return 0;
+}
+
+static struct of_device_id lsi_edac_l2_match[] = {
+ {
+ .compatible = "lsi,cortex-a15-l2-cache",
+ },
+ {},
+};
+
+static struct platform_driver lsi_edac_l2_driver = {
+ .probe = lsi_edac_l2_probe,
+ .remove = lsi_edac_l2_remove,
+ .driver = {
+ .name = "lsi_edac_l2",
+ .of_match_table = lsi_edac_l2_match,
+ }
+};
+static struct of_device_id lsi_edac_cpu_match[] = {
+ {
+ .compatible = "lsi,cortex-a15-cpu",
+ },
+ {},
+};
+
+static struct platform_driver lsi_edac_cpu_driver = {
+ .probe = lsi_edac_cpu_probe,
+ .remove = lsi_edac_cpu_remove,
+ .driver = {
+ .name = "lsi_edac_cpu",
+ .of_match_table = lsi_edac_cpu_match,
+ }
+};
+
+module_platform_driver(lsi_edac_cpu_driver);
+module_platform_driver(lsi_edac_l2_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Sangeetha Rao <sangeetha.rao at avagotech.com>");
diff --git a/drivers/edac/axxia_edac-l3.c b/drivers/edac/axxia_edac-l3.c
new file mode 100644
index 0000000..2732490
--- /dev/null
+++ b/drivers/edac/axxia_edac-l3.c
@@ -0,0 +1,185 @@
+ /*
+ * drivers/edac/axxia_edac-l3.c
+ *
+ * EDAC Driver for Avago's Axxia 5500 for L3 cache
+ *
+ * Copyright (C) 2010 LSI Inc.
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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/slab.h>
+#include <linux/io.h>
+#include <linux/edac.h>
+#include <mach/ncr.h>
+#include <linux/of_platform.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include "edac_core.h"
+#include "edac_module.h"
+
+#define LSI_EDAC_MOD_STR "lsi_edac"
+
+#define APB2_PERSIST_SCRATCH 0xdc
+#define L3_PERSIST_SCRATCH_BIT (0x1 << 4)
+
+/* Private structure for common edac device */
+struct lsi_edac_dev_info {
+ struct platform_device *pdev;
+ char *ctl_name;
+ char *blk_name;
+ int edac_idx;
+ struct regmap *syscon;
+ void __iomem *dickens_L3;
+ struct edac_device_ctl_info *edac_dev;
+ void (*check)(struct edac_device_ctl_info *edac_dev);
+};
+
+/* Check for L3 Errors */
+static void lsi_l3_error_check(struct edac_device_ctl_info *edac_dev)
+{
+ unsigned long regVal1, regVal2;
+ unsigned count = 0;
+ int i, instance;
+ struct lsi_edac_dev_info *dev_info;
+
+ dev_info = (struct lsi_edac_dev_info *) edac_dev->pvt_info;
+
+ for (instance = 0; instance < 8; instance++) {
+ regVal1 = readl(dev_info->dickens_L3 + (instance * 0x10000));
+ regVal2 = readl(dev_info->dickens_L3 +
+ (instance * 0x10000) + 4);
+ /* First error valid */
+ if (regVal2 & 0x40000000) {
+ if (regVal2 & 0x30000000) {
+ regmap_update_bits(dev_info->syscon,
+ APB2_PERSIST_SCRATCH,
+ L3_PERSIST_SCRATCH_BIT,
+ L3_PERSIST_SCRATCH_BIT);
+ /* Fatal error */
+ pr_emerg("L3 uncorrectable error\n");
+ machine_restart(NULL);
+ }
+ count = (regVal2 & 0x07fff800) >> 11;
+ for (i = 0; i < count; i++)
+ edac_device_handle_ce(edac_dev, 0,
+ instance, edac_dev->ctl_name);
+ /* clear the valid bit */
+ writel(0x48000000, dev_info->dickens_L3 +
+ (instance * 0x10000) + 84);
+ }
+ }
+}
+
+static int lsi_edac_l3_probe(struct platform_device *pdev)
+{
+ struct lsi_edac_dev_info *dev_info = NULL;
+ struct device_node *np = pdev->dev.of_node;
+ struct resource *r;
+
+ dev_info = devm_kzalloc(&pdev->dev, sizeof(*dev_info), GFP_KERNEL);
+ if (!dev_info)
+ return -ENOMEM;
+
+ dev_info->ctl_name = kstrdup(np->name, GFP_KERNEL);
+ dev_info->blk_name = "l3merrsr";
+ dev_info->pdev = pdev;
+ dev_info->edac_idx = edac_device_alloc_index();
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r) {
+ pr_err("Unable to get mem resource\n");
+ goto err1;
+ }
+
+ dev_info->dickens_L3 = devm_ioremap(&pdev->dev, r->start,
+ resource_size(r));
+ if (!dev_info->dickens_L3) {
+ pr_err("LSI_L3 devm_ioremap error\n");
+ goto err1;
+ }
+
+ dev_info->syscon =
+ syscon_regmap_lookup_by_phandle(np, "syscon");
+ if (IS_ERR(dev_info->syscon)) {
+ pr_info("%s: syscon lookup failed\n",
+ np->name);
+ goto err1;
+ }
+ dev_info->edac_dev =
+ edac_device_alloc_ctl_info(0, dev_info->ctl_name,
+ 1, dev_info->blk_name,
+ 8, 0, NULL, 0,
+ dev_info->edac_idx);
+ if (!dev_info->edac_dev) {
+ pr_info("No memory for edac device\n");
+ goto err1;
+ }
+
+ dev_info->edac_dev->pvt_info = dev_info;
+ dev_info->edac_dev->dev = &dev_info->pdev->dev;
+ dev_info->edac_dev->ctl_name = dev_info->ctl_name;
+ dev_info->edac_dev->mod_name = LSI_EDAC_MOD_STR;
+ dev_info->edac_dev->dev_name = dev_name(&dev_info->pdev->dev);
+ edac_op_state = EDAC_OPSTATE_POLL;
+ dev_info->edac_dev->edac_check = lsi_l3_error_check;
+
+ if (edac_device_add_device(dev_info->edac_dev) != 0) {
+ pr_info("Unable to add edac device for %s\n",
+ dev_info->ctl_name);
+ goto err2;
+ }
+
+ return 0;
+err2:
+ edac_device_free_ctl_info(dev_info->edac_dev);
+err1:
+ platform_device_unregister(dev_info->pdev);
+ return 1;
+}
+
+static int lsi_edac_l3_remove(struct platform_device *pdev)
+{
+ platform_device_unregister(pdev);
+ return 0;
+}
+
+static struct of_device_id lsi_edac_l3_match[] = {
+ {
+ .compatible = "lsi,ccn504-l3-cache",
+ },
+ {},
+};
+
+static struct platform_driver lsi_edac_l3_driver = {
+ .probe = lsi_edac_l3_probe,
+ .remove = lsi_edac_l3_remove,
+ .driver = {
+ .name = "lsi_edac_l3",
+ .of_match_table = lsi_edac_l3_match,
+ }
+};
+
+module_platform_driver(lsi_edac_l3_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Sangeetha Rao <sangeetha.rao at avagotech.com>");
diff --git a/drivers/edac/axxia_edac-mc.c b/drivers/edac/axxia_edac-mc.c
new file mode 100644
index 0000000..643ed38
--- /dev/null
+++ b/drivers/edac/axxia_edac-mc.c
@@ -0,0 +1,351 @@
+ /*
+ * drivers/edac/axxia_edac-mc.c
+ *
+ * EDAC Driver for Avago's Axxia 5500 System Memory Controller
+ *
+ * Copyright (C) 2010 LSI Inc.
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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/slab.h>
+#include <linux/io.h>
+#include <linux/edac.h>
+#include <mach/ncr.h>
+#include <linux/of_platform.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/irq.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <linux/interrupt.h>
+#include "edac_core.h"
+#include "edac_module.h"
+
+#define LSI_EDAC_MOD_STR "lsi_edac"
+
+#define APB2_SER3_PHY_ADDR 0x002010030000ULL
+#define APB2_SER3_PHY_SIZE 0x1000
+
+#define SM_INT_STATUS_REG 0x410
+#define SM_INT_STATUS_CLEAR_REG 0x548
+#define SM_INT_STATUS_MASK_REG 0x414
+
+#define APB2_PERSIST_SCRATCH 0xdc
+#define SMEM_PERSIST_SCRATCH_BIT (0x1 << 3)
+
+static int log = 1;
+module_param(log, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(log, "Log each error to kernel log.");
+
+static int machine_restrt = 1;
+module_param(machine_restrt, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(machine_restrt, "Machine restart on fatal error.");
+
+/*
+ AXM55xx memory controller interrupt status bits:
+
+ Bit [24] = The software-initiated control word write has completed.
+ Bit [23] = The user-initiated DLL resync has completed.
+ Bit [22] = A state change has been detected on the dfi_init_complete signal
+ after initialization.
+ Bit [21] = The assertion of the INHIBIT_DRAM_CMD parameter has successfully
+ inhibited the command queue.
+ Bit [20] = The register interface-initiated mode register write has completed
+ and another mode register write may be issued.
+ Bit [19] = A parity error has been detected on the address/control bus on a
+ registered DIMM.
+ Bit [18] = The leveling operation has completed.
+ Bit [17] = A leveling operation has been requested.
+ Bit [16] = A DFI update error has occurred. Error information can be found in
+ the UPDATE_ERROR_STATUS parameter.
+ Bit [15] = A write leveling error has occurred. Error information can be found
+ in the WRLVL_ERROR_STATUS parameter.
+ Bit [14] = A read leveling gate training error has occurred. Error information
+ can be found in the RDLVL_ERROR_STATUS parameter.
+ Bit [13] = A read leveling error has occurred. Error information can be found
+ in the RDLVL_ERROR_STATUS parameter.
+ Bit [12] = The user has programmed an invalid setting associated with user
+ words per burst. Examples: Setting param_reduc when burst
+ length = 2. A 1:2 MC:PHY clock ratio with burst length = 2.
+ Bit [11] = A wrap cycle crossing a DRAM page has been detected. This is
+ unsupported & may result in memory data corruption.
+ Bit [10] = The BIST operation has been completed.
+ Bit [09] = The low power operation has been completed.
+ Bit [08] = The MC initialization has been completed.
+ Bit [07] = An error occurred on the port command channel.
+ Bit [06] = Multiple uncorrectable ECC events have been detected.
+ Bit [05] = An uncorrectable ECC event has been detected.
+ Bit [04] = Multiple correctable ECC events have been detected.
+ Bit [03] = A correctable ECC event has been detected.
+ Bit [02] = Multiple accesses outside the defined PHYSICAL memory space have
+ occurred.
+ Bit [01] = A memory access outside the defined PHYSICAL memory space has
+ occurred.
+ Bit [00] = The memory reset is valid on the DFI bus.
+
+ Of these, 1, 2, 3, 4, 5, 6, 7, 11, and 19 are of interest.
+*/
+#define SM_INT_MASK (0x1f7f719)
+
+enum events {
+ EV_ILLEGAL = 0,
+ EV_MULT_ILLEGAL,
+ EV_CORR_ECC,
+ EV_MULT_CORR_ECC,
+ EV_UNCORR_ECC,
+ EV_MULT_UNCORR_ECC,
+ EV_PORT_ERROR,
+ EV_WRAP_ERROR,
+ EV_PARITY_ERROR,
+ NR_EVENTS
+};
+
+static const u32 event_mask[NR_EVENTS] = {
+ [EV_ILLEGAL] = 0x00000002,
+ [EV_MULT_ILLEGAL] = 0x00000004,
+ [EV_CORR_ECC] = 0x00000008,
+ [EV_MULT_CORR_ECC] = 0x00000010,
+ [EV_UNCORR_ECC] = 0x00000020,
+ [EV_MULT_UNCORR_ECC] = 0x00000040,
+ [EV_PORT_ERROR] = 0x00000080,
+ [EV_WRAP_ERROR] = 0x00000800,
+ [EV_PARITY_ERROR] = 0x00080000,
+};
+
+static const struct event_logging {
+ int fatal;
+ const char *level;
+ const char *name;
+} event_logging[NR_EVENTS] = {
+ [EV_ILLEGAL] = {0, KERN_ERR, "Illegal access"},
+ [EV_MULT_ILLEGAL] = {0, KERN_ERR, "Illegal access"},
+ [EV_CORR_ECC] = {0, KERN_NOTICE, "Correctable ECC error"},
+ [EV_MULT_CORR_ECC] = {0, KERN_NOTICE, "Correctable ECC error"},
+ [EV_UNCORR_ECC] = {1, KERN_CRIT, "Uncorrectable ECC error"},
+ [EV_MULT_UNCORR_ECC] = {1, KERN_CRIT, "Uncorrectable ECC error"},
+ [EV_PORT_ERROR] = {0, KERN_CRIT, "Port error"},
+ [EV_WRAP_ERROR] = {0, KERN_CRIT, "Wrap error"},
+ [EV_PARITY_ERROR] = {0, KERN_CRIT, "Parity error"},
+};
+
+/* Private structure for common edac device */
+struct lsi_edac_dev_info {
+ struct platform_device *pdev;
+ char *ctl_name;
+ char *blk_name;
+ int edac_idx;
+ u32 sm_region;
+ struct regmap *syscon;
+ void __iomem *apb2ser3_region;
+ struct edac_device_ctl_info *edac_dev;
+ void (*check)(struct edac_device_ctl_info *edac_dev);
+};
+
+static irqreturn_t
+smmon_isr(int interrupt, void *device)
+{
+ struct lsi_edac_dev_info *edac_dev = device;
+ u32 status;
+ unsigned long set_val;
+ int i;
+
+ if (ncr_read(edac_dev->sm_region, SM_INT_STATUS_REG,
+ 4, &status)) {
+ pr_err("%s: Error reading interrupt status\n",
+ dev_name(&edac_dev->pdev->dev));
+ return IRQ_NONE;
+ }
+
+ for (i = 0; i < NR_EVENTS; ++i) {
+ if ((status & event_mask[i]) != 0) {
+ if (machine_restrt && event_logging[i].fatal) {
+ if (IS_ERR(edac_dev->syscon)) {
+ set_val = readl(
+ edac_dev->apb2ser3_region
+ + APB2_PERSIST_SCRATCH);
+ /* set bit 3 in pscratch reg */
+ set_val = set_val
+ | SMEM_PERSIST_SCRATCH_BIT;
+ writel(set_val,
+ edac_dev->apb2ser3_region +
+ APB2_PERSIST_SCRATCH);
+ } else
+ regmap_update_bits(edac_dev->syscon,
+ APB2_PERSIST_SCRATCH,
+ SMEM_PERSIST_SCRATCH_BIT,
+ SMEM_PERSIST_SCRATCH_BIT);
+ pr_emerg("%s uncorrectable error\n",
+ edac_dev->ctl_name);
+ machine_restart(NULL);
+ }
+ }
+ }
+
+ /* Clear interrupt */
+ ncr_write(edac_dev->sm_region, SM_INT_STATUS_CLEAR_REG,
+ 4, &status);
+
+ return IRQ_HANDLED;
+}
+
+static void lsi_sm_error_check(struct edac_device_ctl_info *edac_dev)
+{
+ unsigned long sm_reg_val, clear_val;
+ struct lsi_edac_dev_info *dev_info;
+
+ dev_info = (struct lsi_edac_dev_info *) edac_dev->pvt_info;
+
+ /* SM0 is instance 0 */
+ ncr_read(dev_info->sm_region, SM_INT_STATUS_REG, 4, &sm_reg_val);
+ if (sm_reg_val & 0x18) {
+ /* single bit and multiple bit correctable errors */
+ edac_device_handle_ce(edac_dev, 0, 0, edac_dev->ctl_name);
+ /* Clear bits */
+ clear_val = 0x18;
+ ncr_write(dev_info->sm_region, SM_INT_STATUS_CLEAR_REG,
+ 4, &clear_val);
+ }
+}
+
+
+static int lsi_edac_mc_probe(struct platform_device *pdev)
+{
+ static int count;
+ struct lsi_edac_dev_info *dev_info = NULL;
+ /* 4 cores per cluster */
+ struct resource *io;
+ struct device_node *np = pdev->dev.of_node;
+ int irq = -1, rc = 0;
+ u32 mask;
+
+ dev_info = devm_kzalloc(&pdev->dev, sizeof(*dev_info), GFP_KERNEL);
+ if (!dev_info)
+ return -ENOMEM;
+
+ dev_info->ctl_name = kstrdup(np->name, GFP_KERNEL);
+ dev_info->blk_name = "ECC";
+ edac_op_state = EDAC_OPSTATE_POLL;
+
+ dev_info->pdev = pdev;
+ dev_info->edac_idx = edac_device_alloc_index();
+
+ io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!io) {
+ dev_err(&pdev->dev, "Unable to get mem resource\n");
+ goto err1;
+ }
+ dev_info->sm_region = io->start;
+ dev_info->syscon =
+ syscon_regmap_lookup_by_phandle(np, "syscon");
+ if (IS_ERR(dev_info->syscon)) {
+ pr_info("%s: syscon lookup failed hence using hardcoded register address\n",
+ np->name);
+ dev_info->apb2ser3_region = ioremap(APB2_SER3_PHY_ADDR,
+ APB2_SER3_PHY_SIZE);
+ if (!dev_info->apb2ser3_region) {
+ pr_err("ioremap of apb2ser3 region failed\n");
+ goto err1;
+ }
+
+ }
+ /* Disable all memory controller interrupts */
+ mask = 0xffffffff;
+ ncr_write(dev_info->sm_region, SM_INT_STATUS_MASK_REG,
+ 4, &mask);
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ goto err1;
+ if (count == 0) {
+ rc = devm_request_irq(&pdev->dev, irq,
+ smmon_isr, IRQF_ONESHOT,
+ "smmon0", dev_info);
+ } else {
+ rc = devm_request_irq(&pdev->dev, irq,
+ smmon_isr, IRQF_ONESHOT,
+ "smmon1", dev_info);
+ }
+ if (rc)
+ goto err1;
+ dev_info->edac_dev =
+ edac_device_alloc_ctl_info(0, dev_info->ctl_name,
+ 1, dev_info->blk_name, 1, 0,
+ NULL, 0, dev_info->edac_idx);
+ if (!dev_info->edac_dev) {
+ pr_info("No memory for edac device\n");
+ goto err1;
+ }
+ /* Enable memory controller interrupts. We need to disable
+ * the interrupt while unmasking it, since otherwise there
+ * will be a locking conflict in ncr_write/ncr_read when the
+ * ISR tries to read interrupt status.
+ */
+ disable_irq(irq);
+ mask = SM_INT_MASK;
+ ncr_write(dev_info->sm_region, SM_INT_STATUS_MASK_REG,
+ 4, &mask);
+
+ dev_info->edac_dev->pvt_info = dev_info;
+ dev_info->edac_dev->dev = &dev_info->pdev->dev;
+ dev_info->edac_dev->ctl_name = dev_info->ctl_name;
+ dev_info->edac_dev->mod_name = LSI_EDAC_MOD_STR;
+ dev_info->edac_dev->dev_name = dev_name(&dev_info->pdev->dev);
+ dev_info->edac_dev->edac_check = lsi_sm_error_check;
+
+
+ if (edac_device_add_device(dev_info->edac_dev) != 0) {
+ pr_info("Unable to add edac device for %s\n",
+ dev_info->ctl_name);
+ goto err2;
+ }
+ enable_irq(irq);
+ return 0;
+err2:
+ edac_device_free_ctl_info(dev_info->edac_dev);
+err1:
+ platform_device_unregister(dev_info->pdev);
+ return 1;
+}
+
+static int lsi_edac_mc_remove(struct platform_device *pdev)
+{
+ platform_device_unregister(pdev);
+ return 0;
+}
+
+static const struct of_device_id lsi_edac_smmon_match[] = {
+ { .compatible = "lsi,smmon" },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, lsi_edac_smmon_match);
+
+static struct platform_driver lsi_edac_mc_driver = {
+ .probe = lsi_edac_mc_probe,
+ .remove = lsi_edac_mc_remove,
+ .driver = {
+ .name = "lsi_edac_smmon",
+ .of_match_table = lsi_edac_smmon_match,
+ }
+};
+module_platform_driver(lsi_edac_mc_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Sangeetha Rao <sangeetha.rao at avagotech.com>");
diff --git a/drivers/edac/axxia_edac.c b/drivers/edac/axxia_edac.c
new file mode 100644
index 0000000..d638141
--- /dev/null
+++ b/drivers/edac/axxia_edac.c
@@ -0,0 +1,461 @@
+ /*
+ * drivers/edac/axxia_edac.c
+ *
+ * EDAC Driver for Avago's Axxia 5500
+ *
+ * Copyright (C) 2010 LSI Inc.
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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/slab.h>
+#include <linux/io.h>
+#include <linux/edac.h>
+#include <mach/ncr.h>
+#include <linux/of_platform.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/irq.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+
+#include "edac_core.h"
+#include "edac_module.h"
+
+#define LSI_EDAC_MOD_STR "lsi_edac"
+#define CORES_PER_CLUSTER 4
+
+/* Private structure for common edac device */
+struct lsi_edac_dev_info {
+ void __iomem *vbase;
+ struct platform_device *pdev;
+ char *ctl_name;
+ char *blk_name;
+ int edac_idx;
+ u32 sm0_region;
+ u32 sm1_region;
+ void __iomem *apb2ser3_region;
+ void __iomem *dickens_L3[8];
+ struct edac_device_ctl_info *edac_dev;
+ void (*init)(struct lsi_edac_dev_info *dev_info);
+ void (*exit)(struct lsi_edac_dev_info *dev_info);
+ void (*check)(struct edac_device_ctl_info *edac_dev);
+};
+
+static void lsi_error_init(struct lsi_edac_dev_info *dev_info)
+{
+}
+
+static void lsi_error_exit(struct lsi_edac_dev_info *dev_info)
+{
+}
+
+void log_cpumerrsr(void *edac)
+{
+ struct edac_device_ctl_info *edac_dev =
+ (struct edac_device_ctl_info *)edac;
+ u32 tmp1, tmp2, count0, count1;
+ unsigned long setVal;
+ int i;
+ struct lsi_edac_dev_info *dev_info;
+
+ dev_info = (struct lsi_edac_dev_info *) edac_dev->pvt_info;
+
+ /* Read cp15 for CPUMERRSR counts */
+ asm volatile("mrrc\tp15, 0, %0, %1, c15" : "=r"(tmp1),
+ "=r"(tmp2));
+ if (tmp1 & 0x80000000) {
+ count0 = (tmp2) & 0x000000ff;
+ count1 = ((tmp2) & 0x0000ff00) >> 8;
+
+ /* increment correctable error counts */
+ for (i = 0; i < count0+count1; i++) {
+ edac_device_handle_ce(edac_dev, 0,
+ raw_smp_processor_id(), edac_dev->ctl_name);
+ }
+
+ /* Clear the valid bit */
+ tmp1 = 0x80000000;
+ tmp2 = 0;
+ asm volatile("mcrr\tp15, 0, %0, %1, c15" : : "r"(tmp1),
+ "r"(tmp2));
+ }
+ if (tmp2 & 0x80000000) {
+ setVal = readl(dev_info->apb2ser3_region + 0xdc);
+ /* set bit 3 in pscratch reg */
+ setVal = (setVal) | (0x1 << 3);
+ writel(setVal, dev_info->apb2ser3_region + 0xdc);
+ pr_info("CPU uncorrectable error\n");
+ machine_restart(NULL);
+ }
+}
+
+
+/* Check for CPU Errors */
+static void lsi_cpu_error_check(struct edac_device_ctl_info *edac_dev)
+{
+ /* execute on current cpu */
+ log_cpumerrsr(edac_dev);
+
+ /* send ipis to execute on other cpus */
+ smp_call_function(log_cpumerrsr, edac_dev, 1);
+
+}
+
+void log_l2merrsr(void *edac)
+{
+ struct edac_device_ctl_info *edac_dev =
+ (struct edac_device_ctl_info *)edac;
+ u32 tmp1, tmp2, count0, count1;
+ unsigned long setVal;
+ int i;
+ struct lsi_edac_dev_info *dev_info;
+
+ dev_info = (struct lsi_edac_dev_info *) edac_dev->pvt_info;
+
+ /* Read cp15 for L2MERRSR counts */
+ asm volatile("mrrc\tp15, 1, %0, %1, c15" : "=r"(tmp1),
+ "=r"(tmp2));
+ if (tmp1 & 0x80000000) {
+ count0 = (tmp2) & 0x000000ff;
+ count1 = ((tmp2) & 0x0000ff00) >> 8;
+
+ /* increment correctable error counts */
+ for (i = 0; i < count0+count1; i++) {
+ edac_device_handle_ce(edac_dev, 0,
+ raw_smp_processor_id()/CORES_PER_CLUSTER,
+ edac_dev->ctl_name);
+ }
+
+ /* Clear the valid bit */
+ tmp1 = 0x80000000;
+ tmp2 = 0;
+ asm volatile("mcrr\tp15, 1, %0, %1, c15" : : "r"(tmp1),
+ "r"(tmp2));
+ }
+ if (tmp2 & 0x80000000) {
+ setVal = readl(dev_info->apb2ser3_region + 0xdc);
+ /* set bit 3 in pscratch reg */
+ setVal = (setVal) | (0x1 << 3);
+ writel(setVal, dev_info->apb2ser3_region + 0xdc);
+ pr_info("L2 uncorrectable error\n");
+ machine_restart(NULL);
+ }
+}
+
+/* Check for L2 Errors */
+static void lsi_l2_error_check(struct edac_device_ctl_info *edac_dev)
+{
+ /* 4 cores per cluster */
+ int nr_cluster_ids = ((nr_cpu_ids - 1) / CORES_PER_CLUSTER) + 1;
+ int i, j, cpu;
+
+ /* execute on current cpu */
+ log_l2merrsr(edac_dev);
+
+ for (i = 0; i < nr_cluster_ids; i++) {
+ /* No need to run on local cluster. */
+ if (i == (raw_smp_processor_id() / CORES_PER_CLUSTER))
+ continue;
+ /*
+ * Have some core in each cluster execute this,
+ * Start with the first core on that cluster.
+ */
+ cpu = i * CORES_PER_CLUSTER;
+ for (j = cpu; j < cpu + CORES_PER_CLUSTER; j++) {
+ if (cpu_online(j)) {
+ smp_call_function_single(j, log_l2merrsr,
+ edac_dev, 1);
+ break;
+ }
+ }
+ }
+}
+
+/* Check for L3 Errors */
+static void lsi_l3_error_check(struct edac_device_ctl_info *edac_dev)
+{
+ unsigned long regVal1, regVal2, setVal;
+ unsigned count = 0;
+ int i, instance;
+ struct lsi_edac_dev_info *dev_info;
+
+ dev_info = (struct lsi_edac_dev_info *) edac_dev->pvt_info;
+
+ for (instance = 0; instance < 8; instance++) {
+ regVal1 = readl(dev_info->dickens_L3[instance]);
+ regVal2 = readl(dev_info->dickens_L3[instance] + 4);
+ /* First error valid */
+ if (regVal2 & 0x40000000) {
+ if (regVal2 & 0x30000000) {
+ setVal = readl(dev_info->apb2ser3_region +
+ 0xdc);
+ /* set bit 3 in pscratch reg */
+ setVal = (setVal) | (0x1 << 3);
+ writel(setVal, dev_info->apb2ser3_region +
+ 0xdc);
+ /* Fatal error */
+ pr_info("L3 uncorrectable error\n");
+ machine_restart(NULL);
+ }
+ count = (regVal2 & 0x07fff800) >> 11;
+ for (i = 0; i < count; i++)
+ edac_device_handle_ce(edac_dev, 0,
+ instance, edac_dev->ctl_name);
+ /* clear the valid bit */
+ writel(0x48000000, dev_info->dickens_L3[instance] + 84);
+ }
+ }
+}
+
+/* Check for SysMem Errors */
+static void lsi_sm_error_check(struct edac_device_ctl_info *edac_dev)
+{
+ unsigned long sm0_regVal, sm1_regVal, clearVal, setVal;
+ struct lsi_edac_dev_info *dev_info;
+
+ dev_info = (struct lsi_edac_dev_info *) edac_dev->pvt_info;
+
+ /* SM0 is instance 0 */
+ ncr_read(dev_info->sm0_region, 0x410, 4, &sm0_regVal);
+ if (sm0_regVal & 0x8) {
+ /* single bit and multiple bit correctable errors */
+ edac_device_handle_ce(edac_dev, 0, 0, edac_dev->ctl_name);
+ /* Clear bits */
+ clearVal = 0x8;
+ ncr_write(dev_info->sm0_region, 0x548, 4, &clearVal);
+ }
+ if (sm0_regVal & 0x40) {
+ setVal = readl(dev_info->apb2ser3_region + 0xdc);
+ /* set bit 3 in pscratch reg */
+ setVal = (setVal) | (0x1 << 3);
+ writel(setVal, dev_info->apb2ser3_region + 0xdc);
+ /* single bit and multiple bit uncorrectable errors */
+ pr_info("SM0 uncorrectable error\n");
+ machine_restart(NULL);
+ }
+
+ /* SM1 is instance 1 */
+ ncr_read(dev_info->sm1_region, 0x410, 4, &sm1_regVal);
+ if (sm1_regVal & 0x8) {
+ /* single bit and multiple bit correctable errors */
+ edac_device_handle_ce(edac_dev, 0, 1, edac_dev->ctl_name);
+ /* Clear bits */
+ clearVal = 0x8;
+ ncr_write(dev_info->sm1_region, 0x548, 4, &clearVal);
+ }
+ if (sm1_regVal & 0x40) {
+ setVal = readl(dev_info->apb2ser3_region + 0xdc);
+ /* set bit 3 in pscratch reg */
+ setVal = (setVal) | (0x1 << 3);
+ writel(setVal, dev_info->apb2ser3_region + 0xdc);
+ /* single bit and multiple bit uncorrectable errors */
+ pr_info("SM1 uncorrectable error\n");
+ machine_restart(NULL);
+ }
+}
+
+
+static struct lsi_edac_dev_info lsi_edac_devs[] = {
+ {
+ .ctl_name = "LSI_CPU",
+ .blk_name = "cpumerrsr",
+ .init = lsi_error_init,
+ .exit = lsi_error_exit,
+ .check = lsi_cpu_error_check
+ },
+ {
+ .ctl_name = "LSI_L2",
+ .blk_name = "l2merrsr",
+ .init = lsi_error_init,
+ .exit = lsi_error_exit,
+ .check = lsi_l2_error_check
+ },
+ {
+ .ctl_name = "LSI_L3",
+ .blk_name = "l3merrsr",
+ .init = lsi_error_init,
+ .exit = lsi_error_exit,
+ .check = lsi_l3_error_check
+ },
+ {
+ .ctl_name = "LSI_SM",
+ .blk_name = "ECC",
+ .init = lsi_error_init,
+ .exit = lsi_error_exit,
+ .check = lsi_sm_error_check
+ },
+ {0} /* Terminated by NULL */
+};
+
+
+
+/* static void lsi_add_edac_devices(void __iomem *vbase) */
+static void lsi_add_edac_devices(struct platform_device *pdev)
+{
+ struct lsi_edac_dev_info *dev_info = NULL;
+ /* 4 cores per cluster */
+ int nr_cluster_ids = ((nr_cpu_ids - 1) / CORES_PER_CLUSTER) + 1;
+ struct resource *io0, *io1;
+ struct device_node *np = pdev->dev.of_node;
+ void __iomem *apb2ser3_region;
+ int i;
+
+ apb2ser3_region = of_iomap(np, 2);
+ if (!apb2ser3_region) {
+ dev_err(&pdev->dev, "LSI_apb2ser3_region iomap error\n");
+ goto err2;
+ }
+
+ for (dev_info = &lsi_edac_devs[0]; dev_info->init; dev_info++) {
+ dev_info->pdev = platform_device_register_simple(
+ dev_info->ctl_name, 0, NULL, 0);
+ if (IS_ERR(dev_info->pdev)) {
+ pr_info("Can't register platform device for %s\n",
+ dev_info->ctl_name);
+ continue;
+ }
+ /*
+ * Don't have to allocate private structure but
+ * make use of cpc925_devs[] instead.
+ */
+ dev_info->edac_idx = edac_device_alloc_index();
+
+ if (strcmp(dev_info->ctl_name, "LSI_SM") == 0) {
+ io0 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!io0) {
+ dev_err(&pdev->dev, "Unable to get mem resource\n");
+ goto err2;
+ }
+ io1 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!io1) {
+ dev_err(&pdev->dev, "Unable to get mem resource\n");
+ goto err2;
+ }
+
+ dev_info->sm0_region = io0->start;
+ dev_info->sm1_region = io1->start;
+ dev_info->apb2ser3_region = apb2ser3_region;
+ dev_info->edac_dev =
+ edac_device_alloc_ctl_info(0, dev_info->ctl_name,
+ 1, dev_info->blk_name, 2, 0,
+ NULL, 0, dev_info->edac_idx);
+ } else if (strcmp(dev_info->ctl_name, "LSI_L3") == 0) {
+ /* 8 L3 caches */
+ for (i = 0; i < 8; i++) {
+ dev_info->dickens_L3[i] = of_iomap(np, i+3);
+ if (!dev_info->dickens_L3[i]) {
+ dev_err(&pdev->dev,
+ "LSI_L3 iomap error\n");
+ goto err2;
+ }
+ }
+ dev_info->apb2ser3_region = apb2ser3_region;
+ dev_info->edac_dev =
+ edac_device_alloc_ctl_info(0, dev_info->ctl_name,
+ 1, dev_info->blk_name, 8, 0, NULL, 0,
+ dev_info->edac_idx);
+ } else if (strcmp(dev_info->ctl_name, "LSI_CPU") == 0) {
+ dev_info->apb2ser3_region = apb2ser3_region;
+ dev_info->edac_dev =
+ edac_device_alloc_ctl_info(0, dev_info->ctl_name,
+ 1, dev_info->blk_name, num_possible_cpus(), 0, NULL,
+ 0, dev_info->edac_idx);
+ } else if (strcmp(dev_info->ctl_name, "LSI_L2") == 0) {
+ dev_info->apb2ser3_region = apb2ser3_region;
+ dev_info->edac_dev =
+ edac_device_alloc_ctl_info(0, dev_info->ctl_name,
+ 1, dev_info->blk_name, nr_cluster_ids, 0, NULL,
+ 0, dev_info->edac_idx);
+ } else {
+ dev_info->edac_dev =
+ edac_device_alloc_ctl_info(0, dev_info->ctl_name,
+ 1, dev_info->blk_name, 1, 0,
+ NULL, 0, dev_info->edac_idx);
+ }
+ if (!dev_info->edac_dev) {
+ pr_info("No memory for edac device\n");
+ goto err1;
+ }
+
+ dev_info->edac_dev->pvt_info = dev_info;
+ dev_info->edac_dev->dev = &dev_info->pdev->dev;
+ dev_info->edac_dev->ctl_name = dev_info->ctl_name;
+ dev_info->edac_dev->mod_name = LSI_EDAC_MOD_STR;
+ dev_info->edac_dev->dev_name = dev_name(&dev_info->pdev->dev);
+
+ 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) {
+ pr_info("Unable to add edac device for %s\n",
+ dev_info->ctl_name);
+ goto err2;
+ }
+ pr_info("Successfully added edac device for %s\n",
+ dev_info->ctl_name);
+
+ continue;
+err2:
+ if (dev_info->exit)
+ dev_info->exit(dev_info);
+ edac_device_free_ctl_info(dev_info->edac_dev);
+err1:
+ platform_device_unregister(dev_info->pdev);
+ }
+}
+
+
+static int lsi_edac_probe(struct platform_device *pdev)
+{
+ edac_op_state = EDAC_OPSTATE_POLL;
+ lsi_add_edac_devices(pdev);
+ return 0;
+}
+
+static int lsi_edac_remove(struct platform_device *pdev)
+{
+ platform_device_unregister(pdev);
+ return 0;
+}
+
+static struct of_device_id lsi_edac_match[] = {
+ {
+ .type = "edac",
+ .compatible = "lsi,edac",
+ },
+ {},
+};
+
+static struct platform_driver lsi_edac_driver = {
+ .probe = lsi_edac_probe,
+ .remove = lsi_edac_remove,
+ .driver = {
+ .name = "lsi_edac",
+ .of_match_table = lsi_edac_match,
+ }
+};
+
+module_platform_driver(lsi_edac_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Sangeetha Rao <sangeetha.rao at avagotech.com>");
--
1.7.9.5
More information about the linux-yocto
mailing list