aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThor Thayer <tthayer@opensource.altera.com>2016-06-22 09:58:57 -0400
committerBorislav Petkov <bp@suse.de>2016-06-24 15:16:38 -0400
commit1166fde93d5b1d7267992edc7e2c8ee129d6667e (patch)
treeb57786ef284fc152879ae6a21d855832880e2c20
parentf103ad1556aa985eb094cdfa8a8e4d618732bad2 (diff)
EDAC, altera: Add Arria10 ECC memory init functions
In preparation for additional memory module ECCs, add the memory initialization functions and helpers. Signed-off-by: Thor Thayer <tthayer@opensource.altera.com> Cc: linux-arm-kernel@lists.infradead.org Cc: linux-edac <linux-edac@vger.kernel.org> Link: http://lkml.kernel.org/r/1466603939-7526-7-git-send-email-tthayer@opensource.altera.com Signed-off-by: Borislav Petkov <bp@suse.de>
-rw-r--r--drivers/edac/altera_edac.c192
-rw-r--r--drivers/edac/altera_edac.h8
2 files changed, 200 insertions, 0 deletions
diff --git a/drivers/edac/altera_edac.c b/drivers/edac/altera_edac.c
index 2e5940e5801e..6091b76646c4 100644
--- a/drivers/edac/altera_edac.c
+++ b/drivers/edac/altera_edac.c
@@ -19,6 +19,7 @@
19 19
20#include <asm/cacheflush.h> 20#include <asm/cacheflush.h>
21#include <linux/ctype.h> 21#include <linux/ctype.h>
22#include <linux/delay.h>
22#include <linux/edac.h> 23#include <linux/edac.h>
23#include <linux/genalloc.h> 24#include <linux/genalloc.h>
24#include <linux/interrupt.h> 25#include <linux/interrupt.h>
@@ -874,6 +875,197 @@ static irqreturn_t __maybe_unused altr_edac_a10_ecc_irq(int irq, void *dev_id)
874 return IRQ_NONE; 875 return IRQ_NONE;
875} 876}
876 877
878/******************* Arria10 Memory Buffer Functions *********************/
879
880static inline int a10_get_irq_mask(struct device_node *np)
881{
882 int irq;
883 const u32 *handle = of_get_property(np, "interrupts", NULL);
884
885 if (!handle)
886 return -ENODEV;
887 irq = be32_to_cpup(handle);
888 return irq;
889}
890
891static inline void ecc_set_bits(u32 bit_mask, void __iomem *ioaddr)
892{
893 u32 value = readl(ioaddr);
894
895 value |= bit_mask;
896 writel(value, ioaddr);
897}
898
899static inline void ecc_clear_bits(u32 bit_mask, void __iomem *ioaddr)
900{
901 u32 value = readl(ioaddr);
902
903 value &= ~bit_mask;
904 writel(value, ioaddr);
905}
906
907static inline int ecc_test_bits(u32 bit_mask, void __iomem *ioaddr)
908{
909 u32 value = readl(ioaddr);
910
911 return (value & bit_mask) ? 1 : 0;
912}
913
914/*
915 * This function uses the memory initialization block in the Arria10 ECC
916 * controller to initialize/clear the entire memory data and ECC data.
917 */
918static int __maybe_unused altr_init_memory_port(void __iomem *ioaddr, int port)
919{
920 int limit = ALTR_A10_ECC_INIT_WATCHDOG_10US;
921 u32 init_mask, stat_mask, clear_mask;
922 int ret = 0;
923
924 if (port) {
925 init_mask = ALTR_A10_ECC_INITB;
926 stat_mask = ALTR_A10_ECC_INITCOMPLETEB;
927 clear_mask = ALTR_A10_ECC_ERRPENB_MASK;
928 } else {
929 init_mask = ALTR_A10_ECC_INITA;
930 stat_mask = ALTR_A10_ECC_INITCOMPLETEA;
931 clear_mask = ALTR_A10_ECC_ERRPENA_MASK;
932 }
933
934 ecc_set_bits(init_mask, (ioaddr + ALTR_A10_ECC_CTRL_OFST));
935 while (limit--) {
936 if (ecc_test_bits(stat_mask,
937 (ioaddr + ALTR_A10_ECC_INITSTAT_OFST)))
938 break;
939 udelay(1);
940 }
941 if (limit < 0)
942 ret = -EBUSY;
943
944 /* Clear any pending ECC interrupts */
945 writel(clear_mask, (ioaddr + ALTR_A10_ECC_INTSTAT_OFST));
946
947 return ret;
948}
949
950static __init int __maybe_unused
951altr_init_a10_ecc_block(struct device_node *np, u32 irq_mask,
952 u32 ecc_ctrl_en_mask, bool dual_port)
953{
954 int ret = 0;
955 void __iomem *ecc_block_base;
956 struct regmap *ecc_mgr_map;
957 char *ecc_name;
958 struct device_node *np_eccmgr;
959
960 ecc_name = (char *)np->name;
961
962 /* Get the ECC Manager - parent of the device EDACs */
963 np_eccmgr = of_get_parent(np);
964 ecc_mgr_map = syscon_regmap_lookup_by_phandle(np_eccmgr,
965 "altr,sysmgr-syscon");
966 of_node_put(np_eccmgr);
967 if (IS_ERR(ecc_mgr_map)) {
968 edac_printk(KERN_ERR, EDAC_DEVICE,
969 "Unable to get syscon altr,sysmgr-syscon\n");
970 return -ENODEV;
971 }
972
973 /* Map the ECC Block */
974 ecc_block_base = of_iomap(np, 0);
975 if (!ecc_block_base) {
976 edac_printk(KERN_ERR, EDAC_DEVICE,
977 "Unable to map %s ECC block\n", ecc_name);
978 return -ENODEV;
979 }
980
981 /* Disable ECC */
982 regmap_write(ecc_mgr_map, A10_SYSMGR_ECC_INTMASK_SET_OFST, irq_mask);
983 writel(ALTR_A10_ECC_SERRINTEN,
984 (ecc_block_base + ALTR_A10_ECC_ERRINTENR_OFST));
985 ecc_clear_bits(ecc_ctrl_en_mask,
986 (ecc_block_base + ALTR_A10_ECC_CTRL_OFST));
987 /* Ensure all writes complete */
988 wmb();
989 /* Use HW initialization block to initialize memory for ECC */
990 ret = altr_init_memory_port(ecc_block_base, 0);
991 if (ret) {
992 edac_printk(KERN_ERR, EDAC_DEVICE,
993 "ECC: cannot init %s PORTA memory\n", ecc_name);
994 goto out;
995 }
996
997 if (dual_port) {
998 ret = altr_init_memory_port(ecc_block_base, 1);
999 if (ret) {
1000 edac_printk(KERN_ERR, EDAC_DEVICE,
1001 "ECC: cannot init %s PORTB memory\n",
1002 ecc_name);
1003 goto out;
1004 }
1005 }
1006
1007 /* Interrupt mode set to every SBERR */
1008 regmap_write(ecc_mgr_map, ALTR_A10_ECC_INTMODE_OFST,
1009 ALTR_A10_ECC_INTMODE);
1010 /* Enable ECC */
1011 ecc_set_bits(ecc_ctrl_en_mask, (ecc_block_base +
1012 ALTR_A10_ECC_CTRL_OFST));
1013 writel(ALTR_A10_ECC_SERRINTEN,
1014 (ecc_block_base + ALTR_A10_ECC_ERRINTENS_OFST));
1015 regmap_write(ecc_mgr_map, A10_SYSMGR_ECC_INTMASK_CLR_OFST, irq_mask);
1016 /* Ensure all writes complete */
1017 wmb();
1018out:
1019 iounmap(ecc_block_base);
1020 return ret;
1021}
1022
1023static int validate_parent_available(struct device_node *np);
1024static const struct of_device_id altr_edac_a10_device_of_match[];
1025static int __init __maybe_unused altr_init_a10_ecc_device_type(char *compat)
1026{
1027 int irq;
1028 struct device_node *child, *np = of_find_compatible_node(NULL, NULL,
1029 "altr,socfpga-a10-ecc-manager");
1030 if (!np) {
1031 edac_printk(KERN_ERR, EDAC_DEVICE, "ECC Manager not found\n");
1032 return -ENODEV;
1033 }
1034
1035 for_each_child_of_node(np, child) {
1036 const struct of_device_id *pdev_id;
1037 const struct edac_device_prv_data *prv;
1038
1039 if (!of_device_is_available(child))
1040 continue;
1041 if (!of_device_is_compatible(child, compat))
1042 continue;
1043
1044 if (validate_parent_available(child))
1045 continue;
1046
1047 irq = a10_get_irq_mask(child);
1048 if (irq < 0)
1049 continue;
1050
1051 /* Get matching node and check for valid result */
1052 pdev_id = of_match_node(altr_edac_a10_device_of_match, child);
1053 if (IS_ERR_OR_NULL(pdev_id))
1054 continue;
1055
1056 /* Validate private data pointer before dereferencing */
1057 prv = pdev_id->data;
1058 if (!prv)
1059 continue;
1060
1061 altr_init_a10_ecc_block(child, BIT(irq),
1062 prv->ecc_enable_mask, 0);
1063 }
1064
1065 of_node_put(np);
1066 return 0;
1067}
1068
877/*********************** OCRAM EDAC Device Functions *********************/ 1069/*********************** OCRAM EDAC Device Functions *********************/
878 1070
879#ifdef CONFIG_EDAC_ALTERA_OCRAM 1071#ifdef CONFIG_EDAC_ALTERA_OCRAM
diff --git a/drivers/edac/altera_edac.h b/drivers/edac/altera_edac.h
index cf4e8cb1d120..aa7c6906d0d5 100644
--- a/drivers/edac/altera_edac.h
+++ b/drivers/edac/altera_edac.h
@@ -230,8 +230,13 @@ struct altr_sdram_mc_data {
230#define ALTR_A10_ECC_INITCOMPLETEB BIT(8) 230#define ALTR_A10_ECC_INITCOMPLETEB BIT(8)
231 231
232#define ALTR_A10_ECC_ERRINTEN_OFST 0x10 232#define ALTR_A10_ECC_ERRINTEN_OFST 0x10
233#define ALTR_A10_ECC_ERRINTENS_OFST 0x14
234#define ALTR_A10_ECC_ERRINTENR_OFST 0x18
233#define ALTR_A10_ECC_SERRINTEN BIT(0) 235#define ALTR_A10_ECC_SERRINTEN BIT(0)
234 236
237#define ALTR_A10_ECC_INTMODE_OFST 0x1C
238#define ALTR_A10_ECC_INTMODE BIT(0)
239
235#define ALTR_A10_ECC_INTSTAT_OFST 0x20 240#define ALTR_A10_ECC_INTSTAT_OFST 0x20
236#define ALTR_A10_ECC_SERRPENA BIT(0) 241#define ALTR_A10_ECC_SERRPENA BIT(0)
237#define ALTR_A10_ECC_DERRPENA BIT(8) 242#define ALTR_A10_ECC_DERRPENA BIT(8)
@@ -280,6 +285,9 @@ struct altr_sdram_mc_data {
280/* Arria 10 OCRAM ECC Management Group Defines */ 285/* Arria 10 OCRAM ECC Management Group Defines */
281#define ALTR_A10_OCRAM_ECC_EN_CTL (BIT(1) | BIT(0)) 286#define ALTR_A10_OCRAM_ECC_EN_CTL (BIT(1) | BIT(0))
282 287
288/* A10 ECC Controller memory initialization timeout */
289#define ALTR_A10_ECC_INIT_WATCHDOG_10US 10000
290
283struct altr_edac_device_dev; 291struct altr_edac_device_dev;
284 292
285struct edac_device_prv_data { 293struct edac_device_prv_data {