diff options
| author | Thor Thayer <tthayer@opensource.altera.com> | 2016-06-22 09:58:57 -0400 |
|---|---|---|
| committer | Borislav Petkov <bp@suse.de> | 2016-06-24 15:16:38 -0400 |
| commit | 1166fde93d5b1d7267992edc7e2c8ee129d6667e (patch) | |
| tree | b57786ef284fc152879ae6a21d855832880e2c20 | |
| parent | f103ad1556aa985eb094cdfa8a8e4d618732bad2 (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.c | 192 | ||||
| -rw-r--r-- | drivers/edac/altera_edac.h | 8 |
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 | |||
| 880 | static 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 | |||
| 891 | static 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 | |||
| 899 | static 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 | |||
| 907 | static 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 | */ | ||
| 918 | static 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 | |||
| 950 | static __init int __maybe_unused | ||
| 951 | altr_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(); | ||
| 1018 | out: | ||
| 1019 | iounmap(ecc_block_base); | ||
| 1020 | return ret; | ||
| 1021 | } | ||
| 1022 | |||
| 1023 | static int validate_parent_available(struct device_node *np); | ||
| 1024 | static const struct of_device_id altr_edac_a10_device_of_match[]; | ||
| 1025 | static 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 | |||
| 283 | struct altr_edac_device_dev; | 291 | struct altr_edac_device_dev; |
| 284 | 292 | ||
| 285 | struct edac_device_prv_data { | 293 | struct edac_device_prv_data { |
