]>
Commit | Line | Data |
---|---|---|
fa08d395 AG |
1 | /* |
2 | * Copyright 2007,2009-2014 Freescale Semiconductor, Inc. | |
3 | * | |
4 | * SPDX-License-Identifier: GPL-2.0+ | |
5 | */ | |
6 | ||
7 | #include <common.h> | |
8 | #include <command.h> | |
9 | #include <pci.h> | |
10 | #include <asm/processor.h> | |
11 | #include <asm/mmu.h> | |
12 | #include <asm/fsl_pci.h> | |
13 | #include <asm/io.h> | |
14 | #include <libfdt.h> | |
15 | #include <fdt_support.h> | |
16 | #include <netdev.h> | |
17 | #include <fdtdec.h> | |
18 | #include <errno.h> | |
19 | #include <malloc.h> | |
20 | ||
21 | DECLARE_GLOBAL_DATA_PTR; | |
22 | ||
23 | static void *get_fdt_virt(void) | |
24 | { | |
25 | return (void *)CONFIG_SYS_TMPVIRT; | |
26 | } | |
27 | ||
28 | static uint64_t get_fdt_phys(void) | |
29 | { | |
30 | return (uint64_t)(uintptr_t)gd->fdt_blob; | |
31 | } | |
32 | ||
33 | static void map_fdt_as(int esel) | |
34 | { | |
35 | u32 mas0, mas1, mas2, mas3, mas7; | |
36 | uint64_t fdt_phys = get_fdt_phys(); | |
37 | unsigned long fdt_phys_tlb = fdt_phys & ~0xffffful; | |
38 | unsigned long fdt_virt_tlb = (ulong)get_fdt_virt() & ~0xffffful; | |
39 | ||
40 | mas0 = MAS0_TLBSEL(1) | MAS0_ESEL(esel); | |
41 | mas1 = MAS1_VALID | MAS1_TID(0) | MAS1_TS | MAS1_TSIZE(BOOKE_PAGESZ_1M); | |
42 | mas2 = FSL_BOOKE_MAS2(fdt_virt_tlb, 0); | |
43 | mas3 = FSL_BOOKE_MAS3(fdt_phys_tlb, 0, MAS3_SW|MAS3_SR); | |
44 | mas7 = FSL_BOOKE_MAS7(fdt_phys_tlb); | |
45 | ||
46 | write_tlb(mas0, mas1, mas2, mas3, mas7); | |
47 | } | |
48 | ||
49 | uint64_t get_phys_ccsrbar_addr_early(void) | |
50 | { | |
51 | void *fdt = get_fdt_virt(); | |
52 | uint64_t r; | |
53 | ||
54 | /* | |
55 | * To be able to read the FDT we need to create a temporary TLB | |
56 | * map for it. | |
57 | */ | |
58 | map_fdt_as(10); | |
59 | r = fdt_get_base_address(fdt, fdt_path_offset(fdt, "/soc")); | |
60 | disable_tlb(10); | |
61 | ||
62 | return r; | |
63 | } | |
64 | ||
65 | int board_early_init_f(void) | |
66 | { | |
67 | return 0; | |
68 | } | |
69 | ||
70 | int checkboard(void) | |
71 | { | |
72 | return 0; | |
73 | } | |
74 | ||
75 | static int pci_map_region(void *fdt, int pci_node, int range_id, | |
76 | phys_size_t *ppaddr, pci_addr_t *pvaddr, | |
77 | pci_size_t *psize, ulong *pmap_addr) | |
78 | { | |
79 | uint64_t addr; | |
80 | uint64_t size; | |
81 | ulong map_addr; | |
82 | int r; | |
83 | ||
84 | r = fdt_read_range(fdt, pci_node, 0, NULL, &addr, &size); | |
85 | if (r) | |
86 | return r; | |
87 | ||
88 | if (ppaddr) | |
89 | *ppaddr = addr; | |
90 | if (psize) | |
91 | *psize = size; | |
92 | ||
93 | if (!pmap_addr) | |
94 | return 0; | |
95 | ||
96 | map_addr = *pmap_addr; | |
97 | ||
98 | /* Align map_addr */ | |
99 | map_addr += size - 1; | |
100 | map_addr &= ~(size - 1); | |
101 | ||
102 | if (map_addr + size >= CONFIG_SYS_PCI_MAP_END) | |
103 | return -1; | |
104 | ||
105 | /* Map virtual memory for range */ | |
106 | assert(!tlb_map_range(map_addr, addr, size, TLB_MAP_IO)); | |
107 | *pmap_addr = map_addr + size; | |
108 | ||
109 | if (pvaddr) | |
110 | *pvaddr = map_addr; | |
111 | ||
112 | return 0; | |
113 | } | |
114 | ||
115 | void pci_init_board(void) | |
116 | { | |
117 | struct pci_controller *pci_hoses; | |
118 | void *fdt = get_fdt_virt(); | |
119 | int pci_node = -1; | |
120 | int pci_num = 0; | |
121 | int pci_count = 0; | |
122 | ulong map_addr; | |
123 | ||
124 | puts("\n"); | |
125 | ||
126 | /* Start MMIO and PIO range maps above RAM */ | |
127 | map_addr = CONFIG_SYS_PCI_MAP_START; | |
128 | ||
129 | /* Count and allocate PCI buses */ | |
130 | pci_node = fdt_node_offset_by_prop_value(fdt, pci_node, | |
131 | "device_type", "pci", 4); | |
132 | while (pci_node != -FDT_ERR_NOTFOUND) { | |
133 | pci_node = fdt_node_offset_by_prop_value(fdt, pci_node, | |
134 | "device_type", "pci", 4); | |
135 | pci_count++; | |
136 | } | |
137 | ||
138 | if (pci_count) { | |
139 | pci_hoses = malloc(sizeof(struct pci_controller) * pci_count); | |
140 | } else { | |
141 | printf("PCI: disabled\n\n"); | |
142 | return; | |
143 | } | |
144 | ||
145 | /* Spawn PCI buses based on device tree */ | |
146 | pci_node = fdt_node_offset_by_prop_value(fdt, pci_node, | |
147 | "device_type", "pci", 4); | |
148 | while (pci_node != -FDT_ERR_NOTFOUND) { | |
149 | struct fsl_pci_info pci_info = { }; | |
150 | const fdt32_t *reg; | |
151 | int r; | |
152 | ||
153 | reg = fdt_getprop(fdt, pci_node, "reg", NULL); | |
154 | pci_info.regs = fdt_translate_address(fdt, pci_node, reg); | |
155 | ||
156 | /* Map MMIO range */ | |
157 | r = pci_map_region(fdt, pci_node, 0, &pci_info.mem_phys, NULL, | |
158 | &pci_info.mem_size, &map_addr); | |
159 | if (r) | |
160 | break; | |
161 | ||
162 | /* Map PIO range */ | |
163 | r = pci_map_region(fdt, pci_node, 1, &pci_info.io_phys, NULL, | |
164 | &pci_info.io_size, &map_addr); | |
165 | if (r) | |
166 | break; | |
167 | ||
168 | /* | |
169 | * The PCI framework finds virtual addresses for the buses | |
170 | * through our address map, so tell it the physical addresses. | |
171 | */ | |
172 | pci_info.mem_bus = pci_info.mem_phys; | |
173 | pci_info.io_bus = pci_info.io_phys; | |
174 | ||
175 | /* Instantiate */ | |
176 | pci_info.pci_num = pci_num + 1; | |
177 | ||
178 | fsl_setup_hose(&pci_hoses[pci_num], pci_info.regs); | |
179 | printf("PCI: base address %lx\n", pci_info.regs); | |
180 | ||
181 | fsl_pci_init_port(&pci_info, &pci_hoses[pci_num], pci_num); | |
182 | ||
183 | /* Jump to next PCI node */ | |
184 | pci_node = fdt_node_offset_by_prop_value(fdt, pci_node, | |
185 | "device_type", "pci", 4); | |
186 | pci_num++; | |
187 | } | |
188 | ||
189 | puts("\n"); | |
190 | } | |
191 | ||
192 | int last_stage_init(void) | |
193 | { | |
194 | void *fdt = get_fdt_virt(); | |
195 | int len = 0; | |
196 | const uint64_t *prop; | |
197 | int chosen; | |
198 | ||
199 | chosen = fdt_path_offset(fdt, "/chosen"); | |
200 | if (chosen < 0) { | |
201 | printf("Couldn't find /chosen node in fdt\n"); | |
202 | return -EIO; | |
203 | } | |
204 | ||
205 | /* -kernel boot */ | |
206 | prop = fdt_getprop(fdt, chosen, "qemu,boot-kernel", &len); | |
207 | if (prop && (len >= 8)) | |
208 | setenv_hex("qemu_kernel_addr", *prop); | |
209 | ||
210 | /* Give the user a variable for the host fdt */ | |
211 | setenv_hex("fdt_addr_r", (ulong)fdt); | |
212 | ||
213 | return 0; | |
214 | } | |
215 | ||
216 | static uint64_t get_linear_ram_size(void) | |
217 | { | |
218 | void *fdt = get_fdt_virt(); | |
219 | const void *prop; | |
220 | int memory; | |
221 | int len; | |
222 | ||
223 | memory = fdt_path_offset(fdt, "/memory"); | |
224 | prop = fdt_getprop(fdt, memory, "reg", &len); | |
225 | ||
226 | if (prop && len >= 16) | |
227 | return *(uint64_t *)(prop+8); | |
228 | ||
229 | panic("Couldn't determine RAM size"); | |
230 | } | |
231 | ||
232 | int board_eth_init(bd_t *bis) | |
233 | { | |
234 | return pci_eth_init(bis); | |
235 | } | |
236 | ||
237 | #if defined(CONFIG_OF_BOARD_SETUP) | |
e895a4b0 | 238 | int ft_board_setup(void *blob, bd_t *bd) |
fa08d395 AG |
239 | { |
240 | FT_FSL_PCI_SETUP; | |
e895a4b0 SG |
241 | |
242 | return 0; | |
fa08d395 AG |
243 | } |
244 | #endif | |
245 | ||
246 | void print_laws(void) | |
247 | { | |
248 | /* We don't emulate LAWs yet */ | |
249 | } | |
250 | ||
251 | phys_size_t fixed_sdram(void) | |
252 | { | |
253 | return get_linear_ram_size(); | |
254 | } | |
255 | ||
256 | phys_size_t fsl_ddr_sdram_size(void) | |
257 | { | |
258 | return get_linear_ram_size(); | |
259 | } | |
260 | ||
261 | void init_tlbs(void) | |
262 | { | |
263 | phys_size_t ram_size; | |
264 | ||
265 | /* | |
266 | * Create a temporary AS=1 map for the fdt | |
267 | * | |
268 | * We use ESEL=0 here to overwrite the previous AS=0 map for ourselves | |
269 | * which was only 4k big. This way we don't have to clear any other maps. | |
270 | */ | |
271 | map_fdt_as(0); | |
272 | ||
273 | /* Fetch RAM size from the fdt */ | |
274 | ram_size = get_linear_ram_size(); | |
275 | ||
276 | /* And remove our fdt map again */ | |
277 | disable_tlb(0); | |
278 | ||
279 | /* Create an internal map of manually created TLB maps */ | |
280 | init_used_tlb_cams(); | |
281 | ||
282 | /* Create a dynamic AS=0 CCSRBAR mapping */ | |
283 | assert(!tlb_map_range(CONFIG_SYS_CCSRBAR, CONFIG_SYS_CCSRBAR_PHYS, | |
284 | 1024 * 1024, TLB_MAP_IO)); | |
285 | ||
286 | /* Create a RAM map that spans all accessible RAM */ | |
287 | setup_ddr_tlbs(ram_size >> 20); | |
288 | ||
289 | /* Create a map for the TLB */ | |
290 | assert(!tlb_map_range((ulong)get_fdt_virt(), get_fdt_phys(), | |
291 | 1024 * 1024, TLB_MAP_RAM)); | |
292 | } | |
293 | ||
294 | void init_laws(void) | |
295 | { | |
296 | /* We don't emulate LAWs yet */ | |
297 | } | |
298 | ||
299 | static uint32_t get_cpu_freq(void) | |
300 | { | |
301 | void *fdt = get_fdt_virt(); | |
302 | int cpus_node = fdt_path_offset(fdt, "/cpus"); | |
303 | int cpu_node = fdt_first_subnode(fdt, cpus_node); | |
304 | const char *prop = "clock-frequency"; | |
305 | return fdt_getprop_u32_default_node(fdt, cpu_node, 0, prop, 0); | |
306 | } | |
307 | ||
308 | void get_sys_info(sys_info_t *sys_info) | |
309 | { | |
310 | int freq = get_cpu_freq(); | |
311 | ||
312 | memset(sys_info, 0, sizeof(sys_info_t)); | |
313 | sys_info->freq_systembus = freq; | |
314 | sys_info->freq_ddrbus = freq; | |
315 | sys_info->freq_processor[0] = freq; | |
316 | } | |
317 | ||
318 | int get_clocks (void) | |
319 | { | |
320 | sys_info_t sys_info; | |
321 | ||
322 | get_sys_info(&sys_info); | |
323 | ||
324 | gd->cpu_clk = sys_info.freq_processor[0]; | |
325 | gd->bus_clk = sys_info.freq_systembus; | |
326 | gd->mem_clk = sys_info.freq_ddrbus; | |
327 | gd->arch.lbc_clk = sys_info.freq_ddrbus; | |
328 | ||
329 | return 0; | |
330 | } | |
331 | ||
332 | unsigned long get_tbclk (void) | |
333 | { | |
334 | void *fdt = get_fdt_virt(); | |
335 | int cpus_node = fdt_path_offset(fdt, "/cpus"); | |
336 | int cpu_node = fdt_first_subnode(fdt, cpus_node); | |
337 | const char *prop = "timebase-frequency"; | |
338 | return fdt_getprop_u32_default_node(fdt, cpu_node, 0, prop, 0); | |
339 | } | |
340 | ||
341 | /******************************************** | |
342 | * get_bus_freq | |
343 | * return system bus freq in Hz | |
344 | *********************************************/ | |
345 | ulong get_bus_freq (ulong dummy) | |
346 | { | |
347 | sys_info_t sys_info; | |
348 | get_sys_info(&sys_info); | |
349 | return sys_info.freq_systembus; | |
350 | } | |
b539534d AG |
351 | |
352 | /* | |
353 | * Return the number of cores on this SOC. | |
354 | */ | |
355 | int cpu_numcores(void) | |
356 | { | |
357 | /* | |
358 | * The QEMU u-boot target only needs to drive the first core, | |
359 | * spinning and device tree nodes get driven by QEMU itself | |
360 | */ | |
361 | return 1; | |
362 | } | |
363 | ||
364 | /* | |
365 | * Return a 32-bit mask indicating which cores are present on this SOC. | |
366 | */ | |
367 | u32 cpu_mask(void) | |
368 | { | |
369 | return (1 << cpu_numcores()) - 1; | |
370 | } |