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