From: John Jacques <john.jacq...@lsi.com> Handles interrupts from the system memory controller.
Signed-off-by: John Jacques <john.jacq...@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 _______________________________________________ linux-yocto mailing list linux-yocto@yoctoproject.org https://lists.yoctoproject.org/listinfo/linux-yocto