1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright Altera Corporation (C) 2016. All rights reserved. |
4 | */ |
5 | #include <linux/delay.h> |
6 | #include <linux/io.h> |
7 | #include <linux/of.h> |
8 | #include <linux/of_address.h> |
9 | |
10 | #include "core.h" |
11 | |
12 | #define ALTR_OCRAM_CLEAR_ECC 0x00000018 |
13 | #define ALTR_OCRAM_ECC_EN 0x00000019 |
14 | |
15 | void socfpga_init_ocram_ecc(void) |
16 | { |
17 | struct device_node *np; |
18 | void __iomem *mapped_ocr_edac_addr; |
19 | |
20 | /* Find the OCRAM EDAC device tree node */ |
21 | np = of_find_compatible_node(NULL, NULL, compat: "altr,socfpga-ocram-ecc" ); |
22 | if (!np) { |
23 | pr_err("Unable to find socfpga-ocram-ecc\n" ); |
24 | return; |
25 | } |
26 | |
27 | mapped_ocr_edac_addr = of_iomap(node: np, index: 0); |
28 | of_node_put(node: np); |
29 | if (!mapped_ocr_edac_addr) { |
30 | pr_err("Unable to map OCRAM ecc regs.\n" ); |
31 | return; |
32 | } |
33 | |
34 | /* Clear any pending OCRAM ECC interrupts, then enable ECC */ |
35 | writel(ALTR_OCRAM_CLEAR_ECC, addr: mapped_ocr_edac_addr); |
36 | writel(ALTR_OCRAM_ECC_EN, addr: mapped_ocr_edac_addr); |
37 | |
38 | iounmap(addr: mapped_ocr_edac_addr); |
39 | } |
40 | |
41 | /* Arria10 OCRAM Section */ |
42 | #define ALTR_A10_ECC_CTRL_OFST 0x08 |
43 | #define ALTR_A10_OCRAM_ECC_EN_CTL (BIT(1) | BIT(0)) |
44 | #define ALTR_A10_ECC_INITA BIT(16) |
45 | |
46 | #define ALTR_A10_ECC_INITSTAT_OFST 0x0C |
47 | #define ALTR_A10_ECC_INITCOMPLETEA BIT(0) |
48 | #define ALTR_A10_ECC_INITCOMPLETEB BIT(8) |
49 | |
50 | #define ALTR_A10_ECC_ERRINTEN_OFST 0x10 |
51 | #define ALTR_A10_ECC_SERRINTEN BIT(0) |
52 | |
53 | #define ALTR_A10_ECC_INTSTAT_OFST 0x20 |
54 | #define ALTR_A10_ECC_SERRPENA BIT(0) |
55 | #define ALTR_A10_ECC_DERRPENA BIT(8) |
56 | #define ALTR_A10_ECC_ERRPENA_MASK (ALTR_A10_ECC_SERRPENA | \ |
57 | ALTR_A10_ECC_DERRPENA) |
58 | /* ECC Manager Defines */ |
59 | #define A10_SYSMGR_ECC_INTMASK_SET_OFST 0x94 |
60 | #define A10_SYSMGR_ECC_INTMASK_CLR_OFST 0x98 |
61 | #define A10_SYSMGR_ECC_INTMASK_OCRAM BIT(1) |
62 | |
63 | #define ALTR_A10_ECC_INIT_WATCHDOG_10US 10000 |
64 | |
65 | static inline void ecc_set_bits(u32 bit_mask, void __iomem *ioaddr) |
66 | { |
67 | u32 value = readl(addr: ioaddr); |
68 | |
69 | value |= bit_mask; |
70 | writel(val: value, addr: ioaddr); |
71 | } |
72 | |
73 | static inline void ecc_clear_bits(u32 bit_mask, void __iomem *ioaddr) |
74 | { |
75 | u32 value = readl(addr: ioaddr); |
76 | |
77 | value &= ~bit_mask; |
78 | writel(val: value, addr: ioaddr); |
79 | } |
80 | |
81 | static inline int ecc_test_bits(u32 bit_mask, void __iomem *ioaddr) |
82 | { |
83 | u32 value = readl(addr: ioaddr); |
84 | |
85 | return (value & bit_mask) ? 1 : 0; |
86 | } |
87 | |
88 | /* |
89 | * This function uses the memory initialization block in the Arria10 ECC |
90 | * controller to initialize/clear the entire memory data and ECC data. |
91 | */ |
92 | static int altr_init_memory_port(void __iomem *ioaddr) |
93 | { |
94 | int limit = ALTR_A10_ECC_INIT_WATCHDOG_10US; |
95 | |
96 | ecc_set_bits(ALTR_A10_ECC_INITA, ioaddr: (ioaddr + ALTR_A10_ECC_CTRL_OFST)); |
97 | while (limit--) { |
98 | if (ecc_test_bits(ALTR_A10_ECC_INITCOMPLETEA, |
99 | ioaddr: (ioaddr + ALTR_A10_ECC_INITSTAT_OFST))) |
100 | break; |
101 | udelay(1); |
102 | } |
103 | if (limit < 0) |
104 | return -EBUSY; |
105 | |
106 | /* Clear any pending ECC interrupts */ |
107 | writel(ALTR_A10_ECC_ERRPENA_MASK, |
108 | addr: (ioaddr + ALTR_A10_ECC_INTSTAT_OFST)); |
109 | |
110 | return 0; |
111 | } |
112 | |
113 | void socfpga_init_arria10_ocram_ecc(void) |
114 | { |
115 | struct device_node *np; |
116 | int ret = 0; |
117 | void __iomem *ecc_block_base; |
118 | |
119 | if (!sys_manager_base_addr) { |
120 | pr_err("SOCFPGA: sys-mgr is not initialized\n" ); |
121 | return; |
122 | } |
123 | |
124 | /* Find the OCRAM EDAC device tree node */ |
125 | np = of_find_compatible_node(NULL, NULL, compat: "altr,socfpga-a10-ocram-ecc" ); |
126 | if (!np) { |
127 | pr_err("Unable to find socfpga-a10-ocram-ecc\n" ); |
128 | return; |
129 | } |
130 | |
131 | /* Map the ECC Block */ |
132 | ecc_block_base = of_iomap(node: np, index: 0); |
133 | of_node_put(node: np); |
134 | if (!ecc_block_base) { |
135 | pr_err("Unable to map OCRAM ECC block\n" ); |
136 | return; |
137 | } |
138 | |
139 | /* Disable ECC */ |
140 | writel(ALTR_A10_OCRAM_ECC_EN_CTL, |
141 | addr: sys_manager_base_addr + A10_SYSMGR_ECC_INTMASK_SET_OFST); |
142 | ecc_clear_bits(ALTR_A10_ECC_SERRINTEN, |
143 | ioaddr: (ecc_block_base + ALTR_A10_ECC_ERRINTEN_OFST)); |
144 | ecc_clear_bits(ALTR_A10_OCRAM_ECC_EN_CTL, |
145 | ioaddr: (ecc_block_base + ALTR_A10_ECC_CTRL_OFST)); |
146 | |
147 | /* Ensure all writes complete */ |
148 | wmb(); |
149 | |
150 | /* Use HW initialization block to initialize memory for ECC */ |
151 | ret = altr_init_memory_port(ioaddr: ecc_block_base); |
152 | if (ret) { |
153 | pr_err("ECC: cannot init OCRAM PORTA memory\n" ); |
154 | goto exit; |
155 | } |
156 | |
157 | /* Enable ECC */ |
158 | ecc_set_bits(ALTR_A10_OCRAM_ECC_EN_CTL, |
159 | ioaddr: (ecc_block_base + ALTR_A10_ECC_CTRL_OFST)); |
160 | ecc_set_bits(ALTR_A10_ECC_SERRINTEN, |
161 | ioaddr: (ecc_block_base + ALTR_A10_ECC_ERRINTEN_OFST)); |
162 | writel(ALTR_A10_OCRAM_ECC_EN_CTL, |
163 | addr: sys_manager_base_addr + A10_SYSMGR_ECC_INTMASK_CLR_OFST); |
164 | |
165 | /* Ensure all writes complete */ |
166 | wmb(); |
167 | exit: |
168 | iounmap(addr: ecc_block_base); |
169 | } |
170 | |