From: John Jacques <john.jacq...@lsi.com>

Handles interrupts from the system memory controller.

Signed-off-by: John Jacques <john.jacq...@lsi.com>
---
 drivers/misc/Kconfig     |    6 ++
 drivers/misc/Makefile    |    1 +
 drivers/misc/lsi-ncr.c   |  118 +++++++++-----------
 drivers/misc/lsi-ncr.h   |    2 +
 drivers/misc/lsi-smmon.c |  269 ++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 329 insertions(+), 67 deletions(-)
 create mode 100644 drivers/misc/lsi-smmon.c

diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index b15811f..d930ada 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -549,6 +549,12 @@ config LSI_NCR
        help
          Provides access to the LSI Axxia NCR bus.
 
+config LSI_SMMON
+       tristate "LSI System Memory Monitor"
+       depends on ARCH_AXXIA || ACP
+       help
+         Monitor the system memory controllers for errors.
+
 source "drivers/misc/c2port/Kconfig"
 source "drivers/misc/eeprom/Kconfig"
 source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index ce48cc8..8a86516 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -52,3 +52,4 @@ obj-$(CONFIG_MAX8997_MUIC)    += max8997-muic.o
 obj-$(CONFIG_HWLAT_DETECTOR)   += hwlat_detector.o
 obj-$(CONFIG_LSI_NCR)           += lsi-ncr.o
 obj-$(CONFIG_LSI_MTC)          += lsi-mtc.o
+obj-$(CONFIG_LSI_SMMON)         += lsi-smmon.o
diff --git a/drivers/misc/lsi-ncr.c b/drivers/misc/lsi-ncr.c
index acb8e8f..23bcbe6 100644
--- a/drivers/misc/lsi-ncr.c
+++ b/drivers/misc/lsi-ncr.c
@@ -26,7 +26,7 @@
 
 #include "lsi-ncr.h"
 
-static void __iomem *nca_address = NULL;
+static void __iomem *nca_address;
 
 #ifdef CONFIG_ARCH_AXXIA
 #define NCA_PHYS_ADDRESS 0x002020100000ULL
@@ -42,48 +42,48 @@ typedef union {
        unsigned long raw;
        struct {
 #ifdef __BIG_ENDIAN
-               unsigned long start_done              :1;
-               unsigned long unused                  :6;
-               unsigned long local_bit               :1;
-               unsigned long status                  :2;
-               unsigned long byte_swap_enable        :1;
-               unsigned long cfg_cmpl_int_enable     :1;
-               unsigned long cmd_type                :4;
-               unsigned long dbs                     :16;
+               unsigned long start_done:1;
+               unsigned long unused:6;
+               unsigned long local_bit:1;
+               unsigned long status:2;
+               unsigned long byte_swap_enable:1;
+               unsigned long cfg_cmpl_int_enable:1;
+               unsigned long cmd_type:4;
+               unsigned long dbs:16;
 #else
-               unsigned long dbs                     :16;
-               unsigned long cmd_type                :4;
-               unsigned long cfg_cmpl_int_enable     :1;
-               unsigned long byte_swap_enable        :1;
-               unsigned long status                  :2;
-               unsigned long local_bit               :1;
-               unsigned long unused                  :6;
-               unsigned long start_done              :1;
+               unsigned long dbs:16;
+               unsigned long cmd_type:4;
+               unsigned long cfg_cmpl_int_enable:1;
+               unsigned long byte_swap_enable:1;
+               unsigned long status:2;
+               unsigned long local_bit:1;
+               unsigned long unused:6;
+               unsigned long start_done:1;
 #endif
-       } __attribute__ ((packed)) bits;
-} __attribute__ ((packed)) command_data_register_0_t;
+       } __packed bits;
+} __packed command_data_register_0_t;
 
 typedef union {
        unsigned long raw;
        struct {
-               unsigned long target_address          :32;
-       } __attribute__ ((packed)) bits;
-} __attribute__ ((packed)) command_data_register_1_t;
+               unsigned long target_address:32;
+       } __packed bits;
+} __packed command_data_register_1_t;
 
 typedef union {
        unsigned long raw;
        struct {
 #ifdef __BIG_ENDIAN
-               unsigned long unused                  :16;
-               unsigned long target_node_id          :8;
-               unsigned long target_id_address_upper :8;
+               unsigned long unused:16;
+               unsigned long target_node_id:8;
+               unsigned long target_id_address_upper:8;
 #else
-               unsigned long target_id_address_upper :8;
-               unsigned long target_node_id          :8;
-               unsigned long unused                  :16;
+               unsigned long target_id_address_upper:8;
+               unsigned long target_node_id:8;
+               unsigned long unused:16;
 #endif
-       } __attribute__ ((packed)) bits;
-} __attribute__ ((packed)) command_data_register_2_t;
+       } __packed bits;
+} __packed command_data_register_2_t;
 
 #ifdef CONFIG_ARM
 
@@ -92,13 +92,12 @@ typedef union {
   ncr_register_read
 */
 
-static __inline__ unsigned long
+unsigned long
 ncr_register_read(unsigned *address)
 {
        unsigned long value;
 
        value = ioread32be(address);
-       printk("%s: value=0x%x address=0x%p\n", __FUNCTION__, value, address);
 
        return value;
 }
@@ -108,10 +107,9 @@ ncr_register_read(unsigned *address)
   ncr_register_write
 */
 
-static __inline__ void
+void
 ncr_register_write(const unsigned value, unsigned *address)
 {
-       printk("%s: value=0x%x address=0x%p\n", __FUNCTION__, value, address);
        iowrite32be(value, address);
 
        return;
@@ -124,10 +122,14 @@ ncr_register_write(const unsigned value, unsigned 
*address)
   ncr_register_read
 */
 
-static __inline__ unsigned long
+unsigned long
 ncr_register_read(unsigned *address)
 {
-       return in_be32((unsigned *)address);
+       unsigned long value;
+
+       value = in_be32((unsigned *)address);
+
+       return value;
 }
 
 /*
@@ -135,10 +137,12 @@ ncr_register_read(unsigned *address)
   ncr_register_write
 */
 
-static __inline__ void
+void
 ncr_register_write(const unsigned value, unsigned *address)
 {
        out_be32(address, value);
+
+       return;
 }
 
 #endif
@@ -155,7 +159,7 @@ ncr_lock(int domain)
        unsigned long value;
        int loops = 10000;
 
-       offset=(0xff80 + (domain * 4));
+       offset = (0xff80 + (domain * 4));
 
        do {
                value = ncr_register_read((unsigned *)(nca_address + offset));
@@ -177,7 +181,7 @@ ncr_unlock(int domain)
 {
        unsigned long offset;
 
-       offset=(0xff80 + (domain * 4));
+       offset = (0xff80 + (domain * 4));
        ncr_register_write(0, (unsigned *)(nca_address + offset));
 
        return;
@@ -336,9 +340,8 @@ ncr_write(unsigned long region, unsigned long address, int 
number,
        cdr0.raw = 0;
        cdr0.bits.start_done = 1;
 
-       if (0xff == cdr2.bits.target_id_address_upper) {
+       if (0xff == cdr2.bits.target_id_address_upper)
                cdr0.bits.local_bit = 1;
-       }
 
        cdr0.bits.cmd_type = 5;
        /* TODO: Verify number... */
@@ -365,9 +368,9 @@ ncr_write(unsigned long region, unsigned long address, int 
number,
          Check status.
        */
 
-       if( 0x3 !=
-           ( ( ncr_register_read( ( unsigned * ) ( nca_address + 0xf0 ) ) &
-               0x00c00000 ) >> 22 ) ) {
+       if (0x3 !=
+           ((ncr_register_read((unsigned *) (nca_address + 0xf0)) &
+               0x00c00000) >> 22)) {
                unsigned long status;
 
                status = ncr_register_read((unsigned *)(nca_address + 0xe4));
@@ -389,27 +392,7 @@ ncr_write(unsigned long region, unsigned long address, int 
number,
 int
 ncr_init(void)
 {
-#if 1
-       {
-               unsigned long value;
-
-#ifdef CONFIG_ARCH_AXXIA
-               if (0 != ncr_read(NCP_REGION_ID(0x22, 0), 0x694, 4, &value))
-                       printk(KERN_CRIT
-                              "%s: ncr_read() failed!\n", __FUNCTION__);
-               else
-                       printk(KERN_CRIT
-                              "%s: value is 0x%x\n", __FUNCTION__, value);
-#else
-               if (0 != ncr_read(NCP_REGION_ID(0x22, 0), 0x50, 4, &value))
-                       printk(KERN_CRIT
-                              "%s: ncr_read() failed!\n", __FUNCTION__);
-               else
-                       printk(KERN_CRIT
-                              "%s: value is 0x%x\n", __FUNCTION__, value);
-#endif
-       }
-#endif
+       nca_address = ioremap(NCA_PHYS_ADDRESS, 0x20000);
 
        return 0;
 }
@@ -425,12 +408,13 @@ void __exit
 ncr_exit(void)
 {
        /* Unmap the NCA. */
-       iounmap(nca_address);
+       if (NULL != nca_address)
+               iounmap(nca_address);
 
        return;
 }
 
-module_exit( ncr_exit );
+module_exit(ncr_exit);
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Register Ring access for LSI's ACP board");
diff --git a/drivers/misc/lsi-ncr.h b/drivers/misc/lsi-ncr.h
index f102df5..cb6bebb 100644
--- a/drivers/misc/lsi-ncr.h
+++ b/drivers/misc/lsi-ncr.h
@@ -34,6 +34,8 @@
 #define NCP_TARGET_ID(region) ((region) & 0xffff)
 #endif
 
+unsigned long ncr_register_read(unsigned *);
+void ncr_register_write(const unsigned, unsigned *);
 int ncr_read(unsigned long, unsigned long, int, void *);
 int ncr_write(unsigned long, unsigned long, int, void *);
 
diff --git a/drivers/misc/lsi-smmon.c b/drivers/misc/lsi-smmon.c
new file mode 100644
index 0000000..6b57ca9
--- /dev/null
+++ b/drivers/misc/lsi-smmon.c
@@ -0,0 +1,269 @@
+/*
+ *  Copyright (C) 2013 LSI Corporation
+ *
+ *  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.
+ *
+ *  Error monitor for system memory.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/proc_fs.h>
+
+#include "lsi-ncr.h"
+
+#ifndef CONFIG_ARCH_AXXIA
+#error "Only AXM55xx is Supported At Present!"
+#endif
+
+/*
+  AXM55xx 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.
+*/
+
+struct smmon_counts {
+       unsigned long illegal_access[2];
+       unsigned long multiple_illegal_access[2];
+       unsigned long correctable_ecc[2];
+       unsigned long multiple_correctable_ecc[2];
+       unsigned long uncorrectable_ecc[2];
+       unsigned long multiple_uncorrectable_ecc[2];
+       unsigned long port_error[2];
+       unsigned long wrap_error[2];
+       unsigned long parity_error[2];
+};
+
+static struct smmon_counts counts;
+
+DEFINE_SPINLOCK(counts_lock);
+
+/*
+  
------------------------------------------------------------------------------
+  smmon_isr
+*/
+
+static irqreturn_t smmon_isr(int interrupt, void *device)
+{
+       unsigned long status;
+       unsigned long region;
+       int rc;
+       int sm;
+
+       if ((32 + 161) == interrupt) {
+               region = NCP_REGION_ID(0x22, 0);
+               sm = 1;
+       } else if ((32 + 160) == interrupt) {
+               region = NCP_REGION_ID(0xf, 0);
+               sm = 0;
+       } else {
+               return IRQ_NONE;
+       }
+
+       rc = ncr_read(region, 0x410, 4, &status);
+
+       if (0 != rc) {
+               printk(KERN_ERR
+                      "smmon(%d): Error reading interrupt status!\n", sm);
+
+               return IRQ_NONE;
+       }
+
+       spin_lock(&counts_lock);
+
+       if (0 != (0x00000002 & status) || 0 != (0x00000004 & status))
+               printk(KERN_CRIT "smmon(%d): Illegal Access!\n", sm);
+
+       if (0 != (0x00000002 & status))
+               ++counts.illegal_access[sm];
+
+       if (0 != (0x00000004 & status))
+               ++counts.multiple_illegal_access[sm];
+
+       if (0 != (0x00000008 & status) || 0 != (0x00000010 & status))
+               printk(KERN_NOTICE "smmon(%d): Correctable ECC Error!\n", sm);
+
+       if (0 != (0x00000008 & status))
+               ++counts.correctable_ecc[sm];
+
+       if (0 != (0x00000010 & status))
+               ++counts.multiple_correctable_ecc[sm];
+
+       if (0 != (0x00000020 & status) || 0 != (0x00000040 & status))
+               printk(KERN_CRIT "smmon(%d): Uncorrectable ECC Error!\n", sm);
+
+       if (0 != (0x00000020 & status))
+               ++counts.uncorrectable_ecc[sm];
+
+       if (0 != (0x00000040 & status))
+               ++counts.multiple_uncorrectable_ecc[sm];
+
+       if (0 != (0x00000080 & status)) {
+               ++counts.port_error[sm];
+               printk(KERN_CRIT "smmon(%d): Port Error!\n", sm);
+       }
+
+       if (0 != (0x00000800 & status)) {
+               ++counts.wrap_error[sm];
+               printk(KERN_CRIT "smmon(%d): Wrap Error!\n", sm);
+       }
+
+       if (0 != (0x00080000 & status)) {
+               ++counts.parity_error[sm];
+               printk(KERN_CRIT "smmon(%d): Parity Error!\n", sm);
+       }
+
+       spin_unlock(&counts_lock);
+
+       ncr_write(region, 0x548, 4, &status);
+
+       return IRQ_HANDLED;
+}
+
+/*
+  
------------------------------------------------------------------------------
+  smmon_read_proc
+*/
+
+static int
+smmon_read_proc(char *page, char **start, off_t offset, int count,
+               int *eof, void *data)
+{
+       int length;
+       unsigned long flags;
+
+       spin_lock_irqsave(&counts_lock, flags);
+
+       length = sprintf(page,
+                        "                   Illegal Access: %lu/%lu\n"
+                        "        Multiple Illegal Accesses: %lu/%lu\n"
+                        "            Correctable ECC Error: %lu/%lu\n"
+                        "  Multiple Correctable ECC Errors: %lu/%lu\n"
+                        "          Uncorrectable ECC Error: %lu/%lu\n"
+                        "Multiple Uncorrectable ECC Errors: %lu/%lu\n"
+                        "                      Port Errors: %lu/%lu\n"
+                        "                      Wrap Errors: %lu/%lu\n"
+                        "                    Parity Errors: %lu/%lu\n",
+                        counts.illegal_access[0],
+                        counts.illegal_access[1],
+                        counts.multiple_illegal_access[0],
+                        counts.multiple_illegal_access[1],
+                        counts.correctable_ecc[0],
+                        counts.correctable_ecc[1],
+                        counts.multiple_correctable_ecc[0],
+                        counts.multiple_correctable_ecc[1],
+                        counts.uncorrectable_ecc[0],
+                        counts.uncorrectable_ecc[1],
+                        counts.multiple_uncorrectable_ecc[0],
+                        counts.multiple_uncorrectable_ecc[1],
+                        counts.port_error[0],
+                        counts.port_error[1],
+                        counts.wrap_error[0],
+                        counts.wrap_error[1],
+                        counts.parity_error[0], counts.parity_error[1]);
+
+       spin_unlock_irqrestore(&counts_lock, flags);
+
+       *eof = 1;
+
+       return length;
+}
+
+/*
+  
==============================================================================
+  
==============================================================================
+  Linux Interface
+  
==============================================================================
+  
==============================================================================
+*/
+
+/*
+  
-------------------------------------------------------------------------------
+  smmon_init
+*/
+
+static int __init smmon_init(void)
+{
+       int rc;
+
+       create_proc_read_entry("smmon", 0, NULL, smmon_read_proc, NULL);
+
+       memset(&counts, 0, sizeof(struct smmon_counts));
+
+       rc = request_irq(32 + 161, smmon_isr, IRQF_ONESHOT, "smmon_0", NULL);
+       rc |= request_irq(32 + 160, smmon_isr, IRQF_ONESHOT, "smmon_1", NULL);
+
+       if (0 != rc) {
+               printk(KERN_ERR "smmon: Couldn't connect interrupt handler!\n");
+               return -EBUSY;
+       }
+
+       printk(KERN_INFO "smmon: Monitoring System Memory\n");
+
+       return 0;
+}
+
+module_init(smmon_init);
+
+/*
+  
-------------------------------------------------------------------------------
+  smmon_exit
+*/
+
+static void __exit smmon_exit(void)
+{
+       free_irq(32 + 161, NULL);
+       free_irq(32 + 160, NULL);
+
+       remove_proc_entry("smmon", NULL);
+
+       printk(KERN_INFO "smmon: Not Monitoring System Memory\n");
+
+       return;
+}
+
+module_exit(smmon_exit);
-- 
1.7.9.5

-- 
_______________________________________________
linux-yocto mailing list
linux-yocto@yoctoproject.org
https://lists.yoctoproject.org/listinfo/linux-yocto

Reply via email to