diff options
author | Thor Thayer <tthayer@opensource.altera.com> | 2016-04-06 21:22:54 -0400 |
---|---|---|
committer | Borislav Petkov <bp@suse.de> | 2016-04-07 06:42:56 -0400 |
commit | c7b4be8db8bc33ec60d21940b3d78b203cdffaac (patch) | |
tree | 35ededd3a77a324501391887e5a3d9b456153038 | |
parent | abd56b3c8483ca73413ade0e7c7a0d13b9870016 (diff) |
EDAC, altera: Add Arria10 OCRAM ECC support
Add Arria10 On-Chip RAM ECC handling.
Signed-off-by: Thor Thayer <tthayer@opensource.altera.com>
Cc: devicetree@vger.kernel.org
Cc: dinguyen@opensource.altera.com
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux@arm.linux.org.uk
Cc: linux-edac <linux-edac@vger.kernel.org>
Link: http://lkml.kernel.org/r/1459992174-8015-1-git-send-email-tthayer@opensource.altera.com
Signed-off-by: Borislav Petkov <bp@suse.de>
-rw-r--r-- | drivers/edac/altera_edac.c | 78 | ||||
-rw-r--r-- | drivers/edac/altera_edac.h | 35 |
2 files changed, 113 insertions, 0 deletions
diff --git a/drivers/edac/altera_edac.c b/drivers/edac/altera_edac.c index f7ffc77998a4..11775dc0b139 100644 --- a/drivers/edac/altera_edac.c +++ b/drivers/edac/altera_edac.c | |||
@@ -550,6 +550,7 @@ module_platform_driver(altr_edac_driver); | |||
550 | 550 | ||
551 | const struct edac_device_prv_data ocramecc_data; | 551 | const struct edac_device_prv_data ocramecc_data; |
552 | const struct edac_device_prv_data l2ecc_data; | 552 | const struct edac_device_prv_data l2ecc_data; |
553 | const struct edac_device_prv_data a10_ocramecc_data; | ||
553 | const struct edac_device_prv_data a10_l2ecc_data; | 554 | const struct edac_device_prv_data a10_l2ecc_data; |
554 | 555 | ||
555 | static irqreturn_t altr_edac_device_handler(int irq, void *dev_id) | 556 | static irqreturn_t altr_edac_device_handler(int irq, void *dev_id) |
@@ -674,6 +675,16 @@ static const struct file_operations altr_edac_device_inject_fops = { | |||
674 | .llseek = generic_file_llseek, | 675 | .llseek = generic_file_llseek, |
675 | }; | 676 | }; |
676 | 677 | ||
678 | static ssize_t altr_edac_a10_device_trig(struct file *file, | ||
679 | const char __user *user_buf, | ||
680 | size_t count, loff_t *ppos); | ||
681 | |||
682 | static const struct file_operations altr_edac_a10_device_inject_fops = { | ||
683 | .open = simple_open, | ||
684 | .write = altr_edac_a10_device_trig, | ||
685 | .llseek = generic_file_llseek, | ||
686 | }; | ||
687 | |||
677 | static void altr_create_edacdev_dbgfs(struct edac_device_ctl_info *edac_dci, | 688 | static void altr_create_edacdev_dbgfs(struct edac_device_ctl_info *edac_dci, |
678 | const struct edac_device_prv_data *priv) | 689 | const struct edac_device_prv_data *priv) |
679 | { | 690 | { |
@@ -701,6 +712,8 @@ static const struct of_device_id altr_edac_device_of_match[] = { | |||
701 | #ifdef CONFIG_EDAC_ALTERA_OCRAM | 712 | #ifdef CONFIG_EDAC_ALTERA_OCRAM |
702 | { .compatible = "altr,socfpga-ocram-ecc", | 713 | { .compatible = "altr,socfpga-ocram-ecc", |
703 | .data = (void *)&ocramecc_data }, | 714 | .data = (void *)&ocramecc_data }, |
715 | { .compatible = "altr,socfpga-a10-ocram-ecc", | ||
716 | .data = (void *)&a10_ocramecc_data }, | ||
704 | #endif | 717 | #endif |
705 | {}, | 718 | {}, |
706 | }; | 719 | }; |
@@ -889,6 +902,24 @@ const struct edac_device_prv_data ocramecc_data = { | |||
889 | .inject_fops = &altr_edac_device_inject_fops, | 902 | .inject_fops = &altr_edac_device_inject_fops, |
890 | }; | 903 | }; |
891 | 904 | ||
905 | static irqreturn_t altr_edac_a10_ecc_irq(struct altr_edac_device_dev *dci, | ||
906 | bool sberr); | ||
907 | |||
908 | const struct edac_device_prv_data a10_ocramecc_data = { | ||
909 | .setup = altr_check_ecc_deps, | ||
910 | .ce_clear_mask = ALTR_A10_ECC_SERRPENA, | ||
911 | .ue_clear_mask = ALTR_A10_ECC_DERRPENA, | ||
912 | .irq_status_mask = A10_SYSMGR_ECC_INTSTAT_OCRAM, | ||
913 | .dbgfs_name = "altr_ocram_trigger", | ||
914 | .ecc_enable_mask = ALTR_A10_OCRAM_ECC_EN_CTL, | ||
915 | .ecc_en_ofst = ALTR_A10_ECC_CTRL_OFST, | ||
916 | .ce_set_mask = ALTR_A10_ECC_TSERRA, | ||
917 | .ue_set_mask = ALTR_A10_ECC_TDERRA, | ||
918 | .set_err_ofst = ALTR_A10_ECC_INTTEST_OFST, | ||
919 | .ecc_irq_handler = altr_edac_a10_ecc_irq, | ||
920 | .inject_fops = &altr_edac_a10_device_inject_fops, | ||
921 | }; | ||
922 | |||
892 | #endif /* CONFIG_EDAC_ALTERA_OCRAM */ | 923 | #endif /* CONFIG_EDAC_ALTERA_OCRAM */ |
893 | 924 | ||
894 | /********************* L2 Cache EDAC Device Functions ********************/ | 925 | /********************* L2 Cache EDAC Device Functions ********************/ |
@@ -1007,6 +1038,50 @@ const struct edac_device_prv_data a10_l2ecc_data = { | |||
1007 | * Based on xgene_edac.c peripheral code. | 1038 | * Based on xgene_edac.c peripheral code. |
1008 | */ | 1039 | */ |
1009 | 1040 | ||
1041 | static ssize_t altr_edac_a10_device_trig(struct file *file, | ||
1042 | const char __user *user_buf, | ||
1043 | size_t count, loff_t *ppos) | ||
1044 | { | ||
1045 | struct edac_device_ctl_info *edac_dci = file->private_data; | ||
1046 | struct altr_edac_device_dev *drvdata = edac_dci->pvt_info; | ||
1047 | const struct edac_device_prv_data *priv = drvdata->data; | ||
1048 | void __iomem *set_addr = (drvdata->base + priv->set_err_ofst); | ||
1049 | unsigned long flags; | ||
1050 | u8 trig_type; | ||
1051 | |||
1052 | if (!user_buf || get_user(trig_type, user_buf)) | ||
1053 | return -EFAULT; | ||
1054 | |||
1055 | local_irq_save(flags); | ||
1056 | if (trig_type == ALTR_UE_TRIGGER_CHAR) | ||
1057 | writel(priv->ue_set_mask, set_addr); | ||
1058 | else | ||
1059 | writel(priv->ce_set_mask, set_addr); | ||
1060 | /* Ensure the interrupt test bits are set */ | ||
1061 | wmb(); | ||
1062 | local_irq_restore(flags); | ||
1063 | |||
1064 | return count; | ||
1065 | } | ||
1066 | |||
1067 | static irqreturn_t altr_edac_a10_ecc_irq(struct altr_edac_device_dev *dci, | ||
1068 | bool sberr) | ||
1069 | { | ||
1070 | void __iomem *base = dci->base; | ||
1071 | |||
1072 | if (sberr) { | ||
1073 | writel(ALTR_A10_ECC_SERRPENA, | ||
1074 | base + ALTR_A10_ECC_INTSTAT_OFST); | ||
1075 | edac_device_handle_ce(dci->edac_dev, 0, 0, dci->edac_dev_name); | ||
1076 | } else { | ||
1077 | writel(ALTR_A10_ECC_DERRPENA, | ||
1078 | base + ALTR_A10_ECC_INTSTAT_OFST); | ||
1079 | edac_device_handle_ue(dci->edac_dev, 0, 0, dci->edac_dev_name); | ||
1080 | panic("\nEDAC:ECC_DEVICE[Uncorrectable errors]\n"); | ||
1081 | } | ||
1082 | return IRQ_HANDLED; | ||
1083 | } | ||
1084 | |||
1010 | static irqreturn_t altr_edac_a10_irq_handler(int irq, void *dev_id) | 1085 | static irqreturn_t altr_edac_a10_irq_handler(int irq, void *dev_id) |
1011 | { | 1086 | { |
1012 | irqreturn_t rc = IRQ_NONE; | 1087 | irqreturn_t rc = IRQ_NONE; |
@@ -1171,6 +1246,9 @@ static int altr_edac_a10_probe(struct platform_device *pdev) | |||
1171 | continue; | 1246 | continue; |
1172 | if (of_device_is_compatible(child, "altr,socfpga-a10-l2-ecc")) | 1247 | if (of_device_is_compatible(child, "altr,socfpga-a10-l2-ecc")) |
1173 | altr_edac_a10_device_add(edac, child); | 1248 | altr_edac_a10_device_add(edac, child); |
1249 | else if (of_device_is_compatible(child, | ||
1250 | "altr,socfpga-a10-ocram-ecc")) | ||
1251 | altr_edac_a10_device_add(edac, child); | ||
1174 | } | 1252 | } |
1175 | 1253 | ||
1176 | return 0; | 1254 | return 0; |
diff --git a/drivers/edac/altera_edac.h b/drivers/edac/altera_edac.h index cb6b2b9079e8..42090f36ba6e 100644 --- a/drivers/edac/altera_edac.h +++ b/drivers/edac/altera_edac.h | |||
@@ -220,9 +220,41 @@ struct altr_sdram_mc_data { | |||
220 | #define ALTR_L2_ECC_INJD BIT(2) | 220 | #define ALTR_L2_ECC_INJD BIT(2) |
221 | 221 | ||
222 | /* Arria10 General ECC Block Module Defines */ | 222 | /* Arria10 General ECC Block Module Defines */ |
223 | #define ALTR_A10_ECC_CTRL_OFST 0x08 | ||
224 | #define ALTR_A10_ECC_EN BIT(0) | ||
225 | #define ALTR_A10_ECC_INITA BIT(16) | ||
226 | #define ALTR_A10_ECC_INITB BIT(24) | ||
227 | |||
228 | #define ALTR_A10_ECC_INITSTAT_OFST 0x0C | ||
229 | #define ALTR_A10_ECC_INITCOMPLETEA BIT(0) | ||
230 | #define ALTR_A10_ECC_INITCOMPLETEB BIT(8) | ||
231 | |||
232 | #define ALTR_A10_ECC_ERRINTEN_OFST 0x10 | ||
233 | #define ALTR_A10_ECC_SERRINTEN BIT(0) | ||
234 | |||
235 | #define ALTR_A10_ECC_INTSTAT_OFST 0x20 | ||
236 | #define ALTR_A10_ECC_SERRPENA BIT(0) | ||
237 | #define ALTR_A10_ECC_DERRPENA BIT(8) | ||
238 | #define ALTR_A10_ECC_ERRPENA_MASK (ALTR_A10_ECC_SERRPENA | \ | ||
239 | ALTR_A10_ECC_DERRPENA) | ||
240 | #define ALTR_A10_ECC_SERRPENB BIT(16) | ||
241 | #define ALTR_A10_ECC_DERRPENB BIT(24) | ||
242 | #define ALTR_A10_ECC_ERRPENB_MASK (ALTR_A10_ECC_SERRPENB | \ | ||
243 | ALTR_A10_ECC_DERRPENB) | ||
244 | |||
245 | #define ALTR_A10_ECC_INTTEST_OFST 0x24 | ||
246 | #define ALTR_A10_ECC_TSERRA BIT(0) | ||
247 | #define ALTR_A10_ECC_TDERRA BIT(8) | ||
248 | |||
249 | /* ECC Manager Defines */ | ||
250 | #define A10_SYSMGR_ECC_INTMASK_SET_OFST 0x94 | ||
251 | #define A10_SYSMGR_ECC_INTMASK_CLR_OFST 0x98 | ||
252 | #define A10_SYSMGR_ECC_INTMASK_OCRAM BIT(1) | ||
253 | |||
223 | #define A10_SYSMGR_ECC_INTSTAT_SERR_OFST 0x9C | 254 | #define A10_SYSMGR_ECC_INTSTAT_SERR_OFST 0x9C |
224 | #define A10_SYSMGR_ECC_INTSTAT_DERR_OFST 0xA0 | 255 | #define A10_SYSMGR_ECC_INTSTAT_DERR_OFST 0xA0 |
225 | #define A10_SYSMGR_ECC_INTSTAT_L2 BIT(0) | 256 | #define A10_SYSMGR_ECC_INTSTAT_L2 BIT(0) |
257 | #define A10_SYSMGR_ECC_INTSTAT_OCRAM BIT(1) | ||
226 | 258 | ||
227 | #define A10_SYSGMR_MPU_CLEAR_L2_ECC_OFST 0xA8 | 259 | #define A10_SYSGMR_MPU_CLEAR_L2_ECC_OFST 0xA8 |
228 | #define A10_SYSGMR_MPU_CLEAR_L2_ECC_SB BIT(15) | 260 | #define A10_SYSGMR_MPU_CLEAR_L2_ECC_SB BIT(15) |
@@ -245,6 +277,9 @@ struct altr_sdram_mc_data { | |||
245 | #define ALTR_A10_L2_ECC_CE_INJ_MASK 0x00000101 | 277 | #define ALTR_A10_L2_ECC_CE_INJ_MASK 0x00000101 |
246 | #define ALTR_A10_L2_ECC_UE_INJ_MASK 0x00010101 | 278 | #define ALTR_A10_L2_ECC_UE_INJ_MASK 0x00010101 |
247 | 279 | ||
280 | /* Arria 10 OCRAM ECC Management Group Defines */ | ||
281 | #define ALTR_A10_OCRAM_ECC_EN_CTL (BIT(1) | BIT(0)) | ||
282 | |||
248 | struct altr_edac_device_dev; | 283 | struct altr_edac_device_dev; |
249 | 284 | ||
250 | struct edac_device_prv_data { | 285 | struct edac_device_prv_data { |