1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Device Tree support for Armada 370 and XP platforms. |
4 | * |
5 | * Copyright (C) 2012 Marvell |
6 | * |
7 | * Lior Amsalem <alior@marvell.com> |
8 | * Gregory CLEMENT <gregory.clement@free-electrons.com> |
9 | * Thomas Petazzoni <thomas.petazzoni@free-electrons.com> |
10 | */ |
11 | |
12 | #include <linux/kernel.h> |
13 | #include <linux/init.h> |
14 | #include <linux/of_address.h> |
15 | #include <linux/of_fdt.h> |
16 | #include <linux/io.h> |
17 | #include <linux/clocksource.h> |
18 | #include <linux/dma-mapping.h> |
19 | #include <linux/memblock.h> |
20 | #include <linux/mbus.h> |
21 | #include <linux/slab.h> |
22 | #include <linux/irqchip.h> |
23 | #include <asm/hardware/cache-l2x0.h> |
24 | #include <asm/mach/arch.h> |
25 | #include <asm/mach/map.h> |
26 | #include <asm/mach/time.h> |
27 | #include <asm/smp_scu.h> |
28 | #include "armada-370-xp.h" |
29 | #include "common.h" |
30 | #include "coherency.h" |
31 | #include "mvebu-soc-id.h" |
32 | |
33 | static void __iomem *scu_base; |
34 | |
35 | /* |
36 | * Enables the SCU when available. Obviously, this is only useful on |
37 | * Cortex-A based SOCs, not on PJ4B based ones. |
38 | */ |
39 | static void __init mvebu_scu_enable(void) |
40 | { |
41 | struct device_node *np = |
42 | of_find_compatible_node(NULL, NULL, compat: "arm,cortex-a9-scu" ); |
43 | if (np) { |
44 | scu_base = of_iomap(node: np, index: 0); |
45 | scu_enable(scu_base); |
46 | of_node_put(node: np); |
47 | } |
48 | } |
49 | |
50 | void __iomem *mvebu_get_scu_base(void) |
51 | { |
52 | return scu_base; |
53 | } |
54 | |
55 | /* |
56 | * When returning from suspend, the platform goes through the |
57 | * bootloader, which executes its DDR3 training code. This code has |
58 | * the unfortunate idea of using the first 10 KB of each DRAM bank to |
59 | * exercise the RAM and calculate the optimal timings. Therefore, this |
60 | * area of RAM is overwritten, and shouldn't be used by the kernel if |
61 | * suspend/resume is supported. |
62 | */ |
63 | |
64 | #ifdef CONFIG_SUSPEND |
65 | #define MVEBU_DDR_TRAINING_AREA_SZ (10 * SZ_1K) |
66 | static int __init mvebu_scan_mem(unsigned long node, const char *uname, |
67 | int depth, void *data) |
68 | { |
69 | const char *type = of_get_flat_dt_prop(node, name: "device_type" , NULL); |
70 | const __be32 *reg, *endp; |
71 | int l; |
72 | |
73 | if (type == NULL || strcmp(type, "memory" )) |
74 | return 0; |
75 | |
76 | reg = of_get_flat_dt_prop(node, name: "linux,usable-memory" , size: &l); |
77 | if (reg == NULL) |
78 | reg = of_get_flat_dt_prop(node, name: "reg" , size: &l); |
79 | if (reg == NULL) |
80 | return 0; |
81 | |
82 | endp = reg + (l / sizeof(__be32)); |
83 | while ((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells)) { |
84 | u64 base, size; |
85 | |
86 | base = dt_mem_next_cell(s: dt_root_addr_cells, cellp: ®); |
87 | size = dt_mem_next_cell(s: dt_root_size_cells, cellp: ®); |
88 | |
89 | memblock_reserve(base, MVEBU_DDR_TRAINING_AREA_SZ); |
90 | } |
91 | |
92 | return 0; |
93 | } |
94 | |
95 | static void __init mvebu_memblock_reserve(void) |
96 | { |
97 | of_scan_flat_dt(it: mvebu_scan_mem, NULL); |
98 | } |
99 | #else |
100 | static void __init mvebu_memblock_reserve(void) {} |
101 | #endif |
102 | |
103 | static void __init mvebu_init_irq(void) |
104 | { |
105 | irqchip_init(); |
106 | mvebu_scu_enable(); |
107 | coherency_init(); |
108 | BUG_ON(mvebu_mbus_dt_init(coherency_available())); |
109 | } |
110 | |
111 | static void __init i2c_quirk(void) |
112 | { |
113 | struct device_node *np; |
114 | u32 dev, rev; |
115 | |
116 | /* |
117 | * Only revisons more recent than A0 support the offload |
118 | * mechanism. We can exit only if we are sure that we can |
119 | * get the SoC revision and it is more recent than A0. |
120 | */ |
121 | if (mvebu_get_soc_id(dev: &dev, rev: &rev) == 0 && rev > MV78XX0_A0_REV) |
122 | return; |
123 | |
124 | for_each_compatible_node(np, NULL, "marvell,mv78230-i2c" ) { |
125 | struct property *new_compat; |
126 | |
127 | new_compat = kzalloc(size: sizeof(*new_compat), GFP_KERNEL); |
128 | |
129 | new_compat->name = kstrdup(s: "compatible" , GFP_KERNEL); |
130 | new_compat->length = sizeof("marvell,mv78230-a0-i2c" ); |
131 | new_compat->value = kstrdup(s: "marvell,mv78230-a0-i2c" , |
132 | GFP_KERNEL); |
133 | |
134 | of_update_property(np, newprop: new_compat); |
135 | } |
136 | } |
137 | |
138 | static void __init mvebu_dt_init(void) |
139 | { |
140 | if (of_machine_is_compatible(compat: "marvell,armadaxp" )) |
141 | i2c_quirk(); |
142 | } |
143 | |
144 | static void __init armada_370_xp_dt_fixup(void) |
145 | { |
146 | #ifdef CONFIG_SMP |
147 | smp_set_ops(smp_ops(armada_xp_smp_ops)); |
148 | #endif |
149 | } |
150 | |
151 | static const char * const armada_370_xp_dt_compat[] __initconst = { |
152 | "marvell,armada-370-xp" , |
153 | NULL, |
154 | }; |
155 | |
156 | DT_MACHINE_START(ARMADA_370_XP_DT, "Marvell Armada 370/XP (Device Tree)" ) |
157 | .l2c_aux_val = 0, |
158 | .l2c_aux_mask = ~0, |
159 | .init_machine = mvebu_dt_init, |
160 | .init_irq = mvebu_init_irq, |
161 | .restart = mvebu_restart, |
162 | .reserve = mvebu_memblock_reserve, |
163 | .dt_compat = armada_370_xp_dt_compat, |
164 | .dt_fixup = armada_370_xp_dt_fixup, |
165 | MACHINE_END |
166 | |
167 | static const char * const armada_375_dt_compat[] __initconst = { |
168 | "marvell,armada375" , |
169 | NULL, |
170 | }; |
171 | |
172 | DT_MACHINE_START(ARMADA_375_DT, "Marvell Armada 375 (Device Tree)" ) |
173 | .l2c_aux_val = 0, |
174 | .l2c_aux_mask = ~0, |
175 | .init_irq = mvebu_init_irq, |
176 | .init_machine = mvebu_dt_init, |
177 | .restart = mvebu_restart, |
178 | .dt_compat = armada_375_dt_compat, |
179 | MACHINE_END |
180 | |
181 | static const char * const armada_38x_dt_compat[] __initconst = { |
182 | "marvell,armada380" , |
183 | "marvell,armada385" , |
184 | NULL, |
185 | }; |
186 | |
187 | DT_MACHINE_START(ARMADA_38X_DT, "Marvell Armada 380/385 (Device Tree)" ) |
188 | .l2c_aux_val = 0, |
189 | .l2c_aux_mask = ~0, |
190 | .init_irq = mvebu_init_irq, |
191 | .restart = mvebu_restart, |
192 | .dt_compat = armada_38x_dt_compat, |
193 | MACHINE_END |
194 | |
195 | static const char * const armada_39x_dt_compat[] __initconst = { |
196 | "marvell,armada390" , |
197 | "marvell,armada398" , |
198 | NULL, |
199 | }; |
200 | |
201 | DT_MACHINE_START(ARMADA_39X_DT, "Marvell Armada 39x (Device Tree)" ) |
202 | .l2c_aux_val = 0, |
203 | .l2c_aux_mask = ~0, |
204 | .init_irq = mvebu_init_irq, |
205 | .restart = mvebu_restart, |
206 | .dt_compat = armada_39x_dt_compat, |
207 | MACHINE_END |
208 | |