[linux-yocto] [PATCH 67/89] drivers/misc: System memory monitoring for AXM55xx.
Paul Butler
butler.paul at gmail.com
Sun Oct 27 12:33:32 PDT 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