[linux-yocto] [PATCH 75/94] drivers/misc: System memory monitoring for AXM55xx.

Paul Butler butler.paul at gmail.com
Thu Nov 7 17:13:29 PST 2013


From: John Jacques <john.jacques at lsi.com>

Handles interrupts from the system memory controller.

Signed-off-by: John Jacques <john.jacques at lsi.com>
---
 arch/arm/configs/lsi_defconfig |   1 +
 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 +++++++++++++++++++++++++++++++++++++++++
 6 files changed, 330 insertions(+), 67 deletions(-)
 create mode 100644 drivers/misc/lsi-smmon.c

diff --git a/arch/arm/configs/lsi_defconfig b/arch/arm/configs/lsi_defconfig
index ce0f5d4..91ee2b8 100644
--- a/arch/arm/configs/lsi_defconfig
+++ b/arch/arm/configs/lsi_defconfig
@@ -765,6 +765,7 @@ CONFIG_BLK_DEV_RAM_SIZE=4096
 # CONFIG_USB_SWITCH_FSA9480 is not set
 CONFIG_LSI_MTC=y
 CONFIG_LSI_NCR=y
+CONFIG_LSI_SMMON=y
 # CONFIG_C2PORT is not set
 
 #
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 0679a8c..93f5634 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -512,6 +512,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 837a7fd..c512ba9 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -51,3 +51,4 @@ obj-$(CONFIG_ALTERA_STAPL)	+=altera-stapl/
 obj-$(CONFIG_MAX8997_MUIC)	+= max8997-muic.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.8.3.4




More information about the linux-yocto mailing list