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