Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
a518d637 AB |
2 | /* |
3 | * ARC HSDK Platform support code | |
4 | * | |
5 | * Copyright (C) 2017 Synopsys, Inc. (www.synopsys.com) | |
a518d637 AB |
6 | */ |
7 | ||
8 | #include <linux/init.h> | |
ce0eff0d EP |
9 | #include <linux/of_fdt.h> |
10 | #include <linux/libfdt.h> | |
a518d637 AB |
11 | #include <linux/smp.h> |
12 | #include <asm/arcregs.h> | |
13 | #include <asm/io.h> | |
14 | #include <asm/mach_desc.h> | |
15 | ||
33def849 | 16 | int arc_hsdk_axi_dmac_coherent __section(".data") = 0; |
ce0eff0d | 17 | |
a518d637 AB |
18 | #define ARC_CCM_UNUSED_ADDR 0x60000000 |
19 | ||
a518d637 AB |
20 | |
21 | #define ARC_PERIPHERAL_BASE 0xf0000000 | |
22 | #define CREG_BASE (ARC_PERIPHERAL_BASE + 0x1000) | |
a518d637 | 23 | |
753affba EP |
24 | #define SDIO_BASE (ARC_PERIPHERAL_BASE + 0xA000) |
25 | #define SDIO_UHS_REG_EXT (SDIO_BASE + 0x108) | |
26 | #define SDIO_UHS_REG_EXT_DIV_2 (2 << 30) | |
27 | ||
ec58ba16 GP |
28 | #define HSDK_GPIO_INTC (ARC_PERIPHERAL_BASE + 0x3000) |
29 | ||
30 | static void __init hsdk_enable_gpio_intc_wire(void) | |
31 | { | |
32 | /* | |
33 | * Peripherals on CPU Card are wired to cpu intc via intermediate | |
34 | * DW APB GPIO blocks (mainly for debouncing) | |
35 | * | |
36 | * --------------------- | |
37 | * | snps,archs-intc | | |
38 | * --------------------- | |
39 | * | | |
40 | * ---------------------- | |
41 | * | snps,archs-idu-intc | | |
42 | * ---------------------- | |
43 | * | | | | | | |
44 | * | [eth] [USB] [... other peripherals] | |
45 | * | | |
46 | * ------------------- | |
47 | * | snps,dw-apb-intc | | |
48 | * ------------------- | |
49 | * | | | | | |
50 | * [Bt] [HAPS] [... other peripherals] | |
51 | * | |
52 | * Current implementation of "irq-dw-apb-ictl" driver doesn't work well | |
53 | * with stacked INTCs. In particular problem happens if its master INTC | |
54 | * not yet instantiated. See discussion here - | |
7e5b06b8 | 55 | * https://lore.kernel.org/lkml/54F6FE2C.7020309@synopsys.com |
ec58ba16 GP |
56 | * |
57 | * So setup the first gpio block as a passive pass thru and hide it from | |
58 | * DT hardware topology - connect intc directly to cpu intc | |
59 | * The GPIO "wire" needs to be init nevertheless (here) | |
60 | * | |
61 | * One side adv is that peripheral interrupt handling avoids one nested | |
62 | * intc ISR hop | |
63 | * | |
64 | * According to HSDK User's Manual [1], "Table 2 Interrupt Mapping" | |
65 | * we have the following GPIO input lines used as sources of interrupt: | |
66 | * - GPIO[0] - Bluetooth interrupt of RS9113 module | |
67 | * - GPIO[2] - HAPS interrupt (on HapsTrak 3 connector) | |
68 | * - GPIO[3] - Audio codec (MAX9880A) interrupt | |
69 | * - GPIO[8-23] - Available on Arduino and PMOD_x headers | |
70 | * For now there's no use of Arduino and PMOD_x headers in Linux | |
71 | * use-case so we only enable lines 0, 2 and 3. | |
72 | * | |
73 | * [1] https://github.com/foss-for-synopsys-dwc-arc-processors/ARC-Development-Systems-Forum/wiki/docs/ARC_HSDK_User_Guide.pdf | |
74 | */ | |
75 | #define GPIO_INTEN (HSDK_GPIO_INTC + 0x30) | |
76 | #define GPIO_INTMASK (HSDK_GPIO_INTC + 0x34) | |
77 | #define GPIO_INTTYPE_LEVEL (HSDK_GPIO_INTC + 0x38) | |
78 | #define GPIO_INT_POLARITY (HSDK_GPIO_INTC + 0x3c) | |
79 | #define GPIO_INT_CONNECTED_MASK 0x0d | |
80 | ||
81 | iowrite32(0xffffffff, (void __iomem *) GPIO_INTMASK); | |
82 | iowrite32(~GPIO_INT_CONNECTED_MASK, (void __iomem *) GPIO_INTMASK); | |
83 | iowrite32(0x00000000, (void __iomem *) GPIO_INTTYPE_LEVEL); | |
84 | iowrite32(0xffffffff, (void __iomem *) GPIO_INT_POLARITY); | |
85 | iowrite32(GPIO_INT_CONNECTED_MASK, (void __iomem *) GPIO_INTEN); | |
86 | } | |
87 | ||
ce0eff0d EP |
88 | static int __init hsdk_tweak_node_coherency(const char *path, bool coherent) |
89 | { | |
90 | void *fdt = initial_boot_params; | |
91 | const void *prop; | |
92 | int node, ret; | |
93 | bool dt_coh_set; | |
94 | ||
95 | node = fdt_path_offset(fdt, path); | |
96 | if (node < 0) | |
97 | goto tweak_fail; | |
98 | ||
99 | prop = fdt_getprop(fdt, node, "dma-coherent", &ret); | |
100 | if (!prop && ret != -FDT_ERR_NOTFOUND) | |
101 | goto tweak_fail; | |
102 | ||
103 | dt_coh_set = ret != -FDT_ERR_NOTFOUND; | |
104 | ret = 0; | |
105 | ||
106 | /* need to remove "dma-coherent" property */ | |
107 | if (dt_coh_set && !coherent) | |
108 | ret = fdt_delprop(fdt, node, "dma-coherent"); | |
109 | ||
110 | /* need to set "dma-coherent" property */ | |
111 | if (!dt_coh_set && coherent) | |
112 | ret = fdt_setprop(fdt, node, "dma-coherent", NULL, 0); | |
113 | ||
114 | if (ret < 0) | |
115 | goto tweak_fail; | |
116 | ||
117 | return 0; | |
118 | ||
119 | tweak_fail: | |
120 | pr_err("failed to tweak %s to %scoherent\n", path, coherent ? "" : "non"); | |
121 | return -EFAULT; | |
122 | } | |
123 | ||
ec9b4feb EP |
124 | enum hsdk_axi_masters { |
125 | M_HS_CORE = 0, | |
126 | M_HS_RTT, | |
127 | M_AXI_TUN, | |
128 | M_HDMI_VIDEO, | |
129 | M_HDMI_AUDIO, | |
130 | M_USB_HOST, | |
131 | M_ETHERNET, | |
132 | M_SDIO, | |
133 | M_GPU, | |
134 | M_DMAC_0, | |
135 | M_DMAC_1, | |
136 | M_DVFS | |
137 | }; | |
138 | ||
139 | #define UPDATE_VAL 1 | |
140 | ||
141 | /* | |
142 | * This is modified configuration of AXI bridge. Default settings | |
143 | * are specified in "Table 111 CREG Address Decoder register reset values". | |
144 | * | |
145 | * AXI_M_m_SLV{0|1} - Slave Select register for master 'm'. | |
146 | * Possible slaves are: | |
147 | * - 0 => no slave selected | |
148 | * - 1 => DDR controller port #1 | |
149 | * - 2 => SRAM controller | |
150 | * - 3 => AXI tunnel | |
151 | * - 4 => EBI controller | |
152 | * - 5 => ROM controller | |
153 | * - 6 => AXI2APB bridge | |
154 | * - 7 => DDR controller port #2 | |
155 | * - 8 => DDR controller port #3 | |
156 | * - 9 => HS38x4 IOC | |
157 | * - 10 => HS38x4 DMI | |
158 | * AXI_M_m_OFFSET{0|1} - Addr Offset register for master 'm' | |
159 | * | |
160 | * Please read ARC HS Development IC Specification, section 17.2 for more | |
161 | * information about apertures configuration. | |
162 | * | |
163 | * m master AXI_M_m_SLV0 AXI_M_m_SLV1 AXI_M_m_OFFSET0 AXI_M_m_OFFSET1 | |
164 | * 0 HS (CBU) 0x11111111 0x63111111 0xFEDCBA98 0x0E543210 | |
165 | * 1 HS (RTT) 0x77777777 0x77777777 0xFEDCBA98 0x76543210 | |
166 | * 2 AXI Tunnel 0x88888888 0x88888888 0xFEDCBA98 0x76543210 | |
167 | * 3 HDMI-VIDEO 0x77777777 0x77777777 0xFEDCBA98 0x76543210 | |
168 | * 4 HDMI-ADUIO 0x77777777 0x77777777 0xFEDCBA98 0x76543210 | |
169 | * 5 USB-HOST 0x77777777 0x77999999 0xFEDCBA98 0x76DCBA98 | |
170 | * 6 ETHERNET 0x77777777 0x77999999 0xFEDCBA98 0x76DCBA98 | |
171 | * 7 SDIO 0x77777777 0x77999999 0xFEDCBA98 0x76DCBA98 | |
172 | * 8 GPU 0x77777777 0x77777777 0xFEDCBA98 0x76543210 | |
173 | * 9 DMAC (port #1) 0x77777777 0x77777777 0xFEDCBA98 0x76543210 | |
174 | * 10 DMAC (port #2) 0x77777777 0x77777777 0xFEDCBA98 0x76543210 | |
175 | * 11 DVFS 0x00000000 0x60000000 0x00000000 0x00000000 | |
176 | */ | |
177 | ||
178 | #define CREG_AXI_M_SLV0(m) ((void __iomem *)(CREG_BASE + 0x20 * (m))) | |
179 | #define CREG_AXI_M_SLV1(m) ((void __iomem *)(CREG_BASE + 0x20 * (m) + 0x04)) | |
180 | #define CREG_AXI_M_OFT0(m) ((void __iomem *)(CREG_BASE + 0x20 * (m) + 0x08)) | |
181 | #define CREG_AXI_M_OFT1(m) ((void __iomem *)(CREG_BASE + 0x20 * (m) + 0x0C)) | |
182 | #define CREG_AXI_M_UPDT(m) ((void __iomem *)(CREG_BASE + 0x20 * (m) + 0x14)) | |
183 | ||
184 | #define CREG_AXI_M_HS_CORE_BOOT ((void __iomem *)(CREG_BASE + 0x010)) | |
185 | ||
186 | #define CREG_PAE ((void __iomem *)(CREG_BASE + 0x180)) | |
187 | #define CREG_PAE_UPDT ((void __iomem *)(CREG_BASE + 0x194)) | |
188 | ||
ce0eff0d EP |
189 | static void __init hsdk_init_memory_bridge_axi_dmac(void) |
190 | { | |
191 | bool coherent = !!arc_hsdk_axi_dmac_coherent; | |
192 | u32 axi_m_slv1, axi_m_oft1; | |
193 | ||
194 | /* | |
195 | * Don't tweak memory bridge configuration if we failed to tweak DTB | |
196 | * as we will end up in a inconsistent state. | |
197 | */ | |
198 | if (hsdk_tweak_node_coherency("/soc/dmac@80000", coherent)) | |
199 | return; | |
200 | ||
201 | if (coherent) { | |
202 | axi_m_slv1 = 0x77999999; | |
203 | axi_m_oft1 = 0x76DCBA98; | |
204 | } else { | |
205 | axi_m_slv1 = 0x77777777; | |
206 | axi_m_oft1 = 0x76543210; | |
207 | } | |
208 | ||
209 | writel(0x77777777, CREG_AXI_M_SLV0(M_DMAC_0)); | |
210 | writel(0xFEDCBA98, CREG_AXI_M_OFT0(M_DMAC_0)); | |
211 | writel(axi_m_slv1, CREG_AXI_M_SLV1(M_DMAC_0)); | |
212 | writel(axi_m_oft1, CREG_AXI_M_OFT1(M_DMAC_0)); | |
213 | writel(UPDATE_VAL, CREG_AXI_M_UPDT(M_DMAC_0)); | |
214 | ||
215 | writel(0x77777777, CREG_AXI_M_SLV0(M_DMAC_1)); | |
216 | writel(0xFEDCBA98, CREG_AXI_M_OFT0(M_DMAC_1)); | |
217 | writel(axi_m_slv1, CREG_AXI_M_SLV1(M_DMAC_1)); | |
218 | writel(axi_m_oft1, CREG_AXI_M_OFT1(M_DMAC_1)); | |
219 | writel(UPDATE_VAL, CREG_AXI_M_UPDT(M_DMAC_1)); | |
220 | } | |
221 | ||
ec9b4feb | 222 | static void __init hsdk_init_memory_bridge(void) |
a518d637 | 223 | { |
ec9b4feb EP |
224 | u32 reg; |
225 | ||
226 | /* | |
227 | * M_HS_CORE has one unique register - BOOT. | |
228 | * We need to clean boot mirror (BOOT[1:0]) bits in them to avoid first | |
229 | * aperture to be masked by 'boot mirror'. | |
230 | */ | |
231 | reg = readl(CREG_AXI_M_HS_CORE_BOOT) & (~0x3); | |
232 | writel(reg, CREG_AXI_M_HS_CORE_BOOT); | |
233 | writel(0x11111111, CREG_AXI_M_SLV0(M_HS_CORE)); | |
234 | writel(0x63111111, CREG_AXI_M_SLV1(M_HS_CORE)); | |
235 | writel(0xFEDCBA98, CREG_AXI_M_OFT0(M_HS_CORE)); | |
236 | writel(0x0E543210, CREG_AXI_M_OFT1(M_HS_CORE)); | |
237 | writel(UPDATE_VAL, CREG_AXI_M_UPDT(M_HS_CORE)); | |
238 | ||
239 | writel(0x77777777, CREG_AXI_M_SLV0(M_HS_RTT)); | |
240 | writel(0x77777777, CREG_AXI_M_SLV1(M_HS_RTT)); | |
241 | writel(0xFEDCBA98, CREG_AXI_M_OFT0(M_HS_RTT)); | |
242 | writel(0x76543210, CREG_AXI_M_OFT1(M_HS_RTT)); | |
243 | writel(UPDATE_VAL, CREG_AXI_M_UPDT(M_HS_RTT)); | |
244 | ||
245 | writel(0x88888888, CREG_AXI_M_SLV0(M_AXI_TUN)); | |
246 | writel(0x88888888, CREG_AXI_M_SLV1(M_AXI_TUN)); | |
247 | writel(0xFEDCBA98, CREG_AXI_M_OFT0(M_AXI_TUN)); | |
248 | writel(0x76543210, CREG_AXI_M_OFT1(M_AXI_TUN)); | |
249 | writel(UPDATE_VAL, CREG_AXI_M_UPDT(M_AXI_TUN)); | |
250 | ||
251 | writel(0x77777777, CREG_AXI_M_SLV0(M_HDMI_VIDEO)); | |
252 | writel(0x77777777, CREG_AXI_M_SLV1(M_HDMI_VIDEO)); | |
253 | writel(0xFEDCBA98, CREG_AXI_M_OFT0(M_HDMI_VIDEO)); | |
254 | writel(0x76543210, CREG_AXI_M_OFT1(M_HDMI_VIDEO)); | |
255 | writel(UPDATE_VAL, CREG_AXI_M_UPDT(M_HDMI_VIDEO)); | |
256 | ||
257 | writel(0x77777777, CREG_AXI_M_SLV0(M_HDMI_AUDIO)); | |
258 | writel(0x77777777, CREG_AXI_M_SLV1(M_HDMI_AUDIO)); | |
259 | writel(0xFEDCBA98, CREG_AXI_M_OFT0(M_HDMI_AUDIO)); | |
260 | writel(0x76543210, CREG_AXI_M_OFT1(M_HDMI_AUDIO)); | |
261 | writel(UPDATE_VAL, CREG_AXI_M_UPDT(M_HDMI_AUDIO)); | |
262 | ||
263 | writel(0x77777777, CREG_AXI_M_SLV0(M_USB_HOST)); | |
264 | writel(0x77999999, CREG_AXI_M_SLV1(M_USB_HOST)); | |
265 | writel(0xFEDCBA98, CREG_AXI_M_OFT0(M_USB_HOST)); | |
266 | writel(0x76DCBA98, CREG_AXI_M_OFT1(M_USB_HOST)); | |
267 | writel(UPDATE_VAL, CREG_AXI_M_UPDT(M_USB_HOST)); | |
268 | ||
269 | writel(0x77777777, CREG_AXI_M_SLV0(M_ETHERNET)); | |
270 | writel(0x77999999, CREG_AXI_M_SLV1(M_ETHERNET)); | |
271 | writel(0xFEDCBA98, CREG_AXI_M_OFT0(M_ETHERNET)); | |
272 | writel(0x76DCBA98, CREG_AXI_M_OFT1(M_ETHERNET)); | |
273 | writel(UPDATE_VAL, CREG_AXI_M_UPDT(M_ETHERNET)); | |
274 | ||
275 | writel(0x77777777, CREG_AXI_M_SLV0(M_SDIO)); | |
276 | writel(0x77999999, CREG_AXI_M_SLV1(M_SDIO)); | |
277 | writel(0xFEDCBA98, CREG_AXI_M_OFT0(M_SDIO)); | |
278 | writel(0x76DCBA98, CREG_AXI_M_OFT1(M_SDIO)); | |
279 | writel(UPDATE_VAL, CREG_AXI_M_UPDT(M_SDIO)); | |
280 | ||
281 | writel(0x77777777, CREG_AXI_M_SLV0(M_GPU)); | |
282 | writel(0x77777777, CREG_AXI_M_SLV1(M_GPU)); | |
283 | writel(0xFEDCBA98, CREG_AXI_M_OFT0(M_GPU)); | |
284 | writel(0x76543210, CREG_AXI_M_OFT1(M_GPU)); | |
285 | writel(UPDATE_VAL, CREG_AXI_M_UPDT(M_GPU)); | |
286 | ||
ec9b4feb EP |
287 | writel(0x00000000, CREG_AXI_M_SLV0(M_DVFS)); |
288 | writel(0x60000000, CREG_AXI_M_SLV1(M_DVFS)); | |
289 | writel(0x00000000, CREG_AXI_M_OFT0(M_DVFS)); | |
290 | writel(0x00000000, CREG_AXI_M_OFT1(M_DVFS)); | |
291 | writel(UPDATE_VAL, CREG_AXI_M_UPDT(M_DVFS)); | |
292 | ||
ce0eff0d EP |
293 | hsdk_init_memory_bridge_axi_dmac(); |
294 | ||
a518d637 AB |
295 | /* |
296 | * PAE remapping for DMA clients does not work due to an RTL bug, so | |
297 | * CREG_PAE register must be programmed to all zeroes, otherwise it | |
298 | * will cause problems with DMA to/from peripherals even if PAE40 is | |
299 | * not used. | |
300 | */ | |
ec9b4feb EP |
301 | writel(0x00000000, CREG_PAE); |
302 | writel(UPDATE_VAL, CREG_PAE_UPDT); | |
303 | } | |
a518d637 | 304 | |
ec9b4feb EP |
305 | static void __init hsdk_init_early(void) |
306 | { | |
307 | hsdk_init_memory_bridge(); | |
edb40d74 | 308 | |
753affba EP |
309 | /* |
310 | * Switch SDIO external ciu clock divider from default div-by-8 to | |
311 | * minimum possible div-by-2. | |
312 | */ | |
313 | iowrite32(SDIO_UHS_REG_EXT_DIV_2, (void __iomem *) SDIO_UHS_REG_EXT); | |
ec58ba16 GP |
314 | |
315 | hsdk_enable_gpio_intc_wire(); | |
a518d637 AB |
316 | } |
317 | ||
318 | static const char *hsdk_compat[] __initconst = { | |
319 | "snps,hsdk", | |
320 | NULL, | |
321 | }; | |
322 | ||
323 | MACHINE_START(SIMULATION, "hsdk") | |
324 | .dt_compat = hsdk_compat, | |
325 | .init_early = hsdk_init_early, | |
a518d637 | 326 | MACHINE_END |