|
19 | 19 |
|
20 | 20 | #include <asm/cacheflush.h>
|
21 | 21 | #include <linux/ctype.h>
|
| 22 | +#include <linux/delay.h> |
22 | 23 | #include <linux/edac.h>
|
23 | 24 | #include <linux/genalloc.h>
|
24 | 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 | 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 | 1069 | /*********************** OCRAM EDAC Device Functions *********************/
|
878 | 1070 |
|
879 | 1071 | #ifdef CONFIG_EDAC_ALTERA_OCRAM
|
|
0 commit comments