]>
Commit | Line | Data |
---|---|---|
b5b06fb7 YS |
1 | /* |
2 | * Copyright 2011-2012 Freescale Semiconductor, Inc. | |
3 | * | |
1a459660 | 4 | * SPDX-License-Identifier: GPL-2.0+ |
b5b06fb7 YS |
5 | */ |
6 | ||
7 | #include <common.h> | |
8 | #include <command.h> | |
9 | #include <i2c.h> | |
10 | #include <netdev.h> | |
11 | #include <linux/compiler.h> | |
12 | #include <asm/mmu.h> | |
13 | #include <asm/processor.h> | |
14 | #include <asm/cache.h> | |
15 | #include <asm/immap_85xx.h> | |
16 | #include <asm/fsl_law.h> | |
17 | #include <asm/fsl_serdes.h> | |
18 | #include <asm/fsl_portals.h> | |
19 | #include <asm/fsl_liodn.h> | |
20 | #include <fm_eth.h> | |
21 | ||
22 | #include "../common/qixis.h" | |
23 | #include "../common/vsc3316_3308.h" | |
24 | #include "b4860qds.h" | |
25 | #include "b4860qds_qixis.h" | |
26 | #include "b4860qds_crossbar_con.h" | |
27 | ||
28 | #define CLK_MUX_SEL_MASK 0x4 | |
29 | #define ETH_PHY_CLK_OUT 0x4 | |
30 | ||
31 | DECLARE_GLOBAL_DATA_PTR; | |
32 | ||
33 | int checkboard(void) | |
34 | { | |
35 | char buf[64]; | |
36 | u8 sw; | |
67ac13b1 | 37 | struct cpu_type *cpu = gd->arch.cpu; |
b5b06fb7 YS |
38 | ccsr_gur_t *gur = (void *)CONFIG_SYS_MPC85xx_GUTS_ADDR; |
39 | unsigned int i; | |
40 | static const char *const freq[] = {"100", "125", "156.25", "161.13", | |
41 | "122.88", "122.88", "122.88"}; | |
42 | int clock; | |
43 | ||
44 | printf("Board: %sQDS, ", cpu->name); | |
45 | printf("Sys ID: 0x%02x, Sys Ver: 0x%02x, ", | |
46 | QIXIS_READ(id), QIXIS_READ(arch)); | |
47 | ||
48 | sw = QIXIS_READ(brdcfg[0]); | |
49 | sw = (sw & QIXIS_LBMAP_MASK) >> QIXIS_LBMAP_SHIFT; | |
50 | ||
51 | if (sw < 0x8) | |
52 | printf("vBank: %d\n", sw); | |
53 | else if (sw >= 0x8 && sw <= 0xE) | |
54 | puts("NAND\n"); | |
55 | else | |
56 | printf("invalid setting of SW%u\n", QIXIS_LBMAP_SWITCH); | |
57 | ||
58 | printf("FPGA: v%d (%s), build %d", | |
59 | (int)QIXIS_READ(scver), qixis_read_tag(buf), | |
60 | (int)qixis_read_minor()); | |
61 | /* the timestamp string contains "\n" at the end */ | |
62 | printf(" on %s", qixis_read_time(buf)); | |
63 | ||
64 | /* Display the RCW, so that no one gets confused as to what RCW | |
65 | * we're actually using for this boot. | |
66 | */ | |
67 | puts("Reset Configuration Word (RCW):"); | |
68 | for (i = 0; i < ARRAY_SIZE(gur->rcwsr); i++) { | |
69 | u32 rcw = in_be32(&gur->rcwsr[i]); | |
70 | ||
71 | if ((i % 4) == 0) | |
72 | printf("\n %08x:", i * 4); | |
73 | printf(" %08x", rcw); | |
74 | } | |
75 | puts("\n"); | |
76 | ||
77 | /* | |
78 | * Display the actual SERDES reference clocks as configured by the | |
79 | * dip switches on the board. Note that the SWx registers could | |
80 | * technically be set to force the reference clocks to match the | |
81 | * values that the SERDES expects (or vice versa). For now, however, | |
82 | * we just display both values and hope the user notices when they | |
83 | * don't match. | |
84 | */ | |
85 | puts("SERDES Reference Clocks: "); | |
86 | sw = QIXIS_READ(brdcfg[2]); | |
87 | clock = (sw >> 5) & 7; | |
88 | printf("Bank1=%sMHz ", freq[clock]); | |
89 | sw = QIXIS_READ(brdcfg[4]); | |
90 | clock = (sw >> 6) & 3; | |
91 | printf("Bank2=%sMHz\n", freq[clock]); | |
92 | ||
93 | return 0; | |
94 | } | |
95 | ||
96 | int select_i2c_ch_pca(u8 ch) | |
97 | { | |
98 | int ret; | |
99 | ||
100 | /* Selecting proper channel via PCA*/ | |
101 | ret = i2c_write(I2C_MUX_PCA_ADDR, 0x0, 1, &ch, 1); | |
102 | if (ret) { | |
103 | printf("PCA: failed to select proper channel.\n"); | |
104 | return ret; | |
105 | } | |
106 | ||
107 | return 0; | |
108 | } | |
109 | ||
110 | int configure_vsc3316_3308(void) | |
111 | { | |
112 | ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); | |
113 | unsigned int num_vsc16_con, num_vsc08_con; | |
114 | u32 serdes1_prtcl, serdes2_prtcl; | |
115 | int ret; | |
116 | ||
117 | serdes1_prtcl = in_be32(&gur->rcwsr[4]) & | |
118 | FSL_CORENET2_RCWSR4_SRDS1_PRTCL; | |
119 | if (!serdes1_prtcl) { | |
120 | printf("SERDES1 is not enabled\n"); | |
121 | return 0; | |
122 | } | |
123 | serdes1_prtcl >>= FSL_CORENET2_RCWSR4_SRDS1_PRTCL_SHIFT; | |
124 | debug("Using SERDES1 Protocol: 0x%x:\n", serdes1_prtcl); | |
125 | ||
126 | serdes2_prtcl = in_be32(&gur->rcwsr[4]) & | |
127 | FSL_CORENET2_RCWSR4_SRDS2_PRTCL; | |
128 | if (!serdes2_prtcl) { | |
129 | printf("SERDES2 is not enabled\n"); | |
130 | return 0; | |
131 | } | |
132 | serdes2_prtcl >>= FSL_CORENET2_RCWSR4_SRDS2_PRTCL_SHIFT; | |
133 | debug("Using SERDES2 Protocol: 0x%x:\n", serdes2_prtcl); | |
134 | ||
135 | switch (serdes1_prtcl) { | |
136 | case 0x2a: | |
137 | case 0x2C: | |
138 | case 0x2D: | |
139 | case 0x2E: | |
140 | /* | |
141 | * Configuration: | |
142 | * SERDES: 1 | |
143 | * Lanes: A,B: SGMII | |
144 | * Lanes: C,D,E,F,G,H: CPRI | |
145 | */ | |
146 | debug("Configuring crossbar to use onboard SGMII PHYs:" | |
147 | "srds_prctl:%x\n", serdes1_prtcl); | |
148 | num_vsc16_con = NUM_CON_VSC3316; | |
149 | /* Configure VSC3316 crossbar switch */ | |
150 | ret = select_i2c_ch_pca(I2C_CH_VSC3316); | |
151 | if (!ret) { | |
152 | ret = vsc3316_config(VSC3316_TX_ADDRESS, | |
0fecbba8 SL |
153 | vsc16_tx_4sfp_sgmii_12_56, |
154 | num_vsc16_con); | |
b5b06fb7 YS |
155 | if (ret) |
156 | return ret; | |
157 | ret = vsc3316_config(VSC3316_RX_ADDRESS, | |
0fecbba8 SL |
158 | vsc16_rx_4sfp_sgmii_12_56, |
159 | num_vsc16_con); | |
b5b06fb7 YS |
160 | if (ret) |
161 | return ret; | |
162 | } else { | |
163 | return ret; | |
164 | } | |
165 | break; | |
166 | ||
167 | #ifdef CONFIG_PPC_B4420 | |
168 | case 0x18: | |
169 | /* | |
170 | * Configuration: | |
171 | * SERDES: 1 | |
172 | * Lanes: A,B,C,D: SGMII | |
173 | * Lanes: E,F,G,H: CPRI | |
174 | */ | |
175 | debug("Configuring crossbar to use onboard SGMII PHYs:" | |
176 | "srds_prctl:%x\n", serdes1_prtcl); | |
177 | num_vsc16_con = NUM_CON_VSC3316; | |
178 | /* Configure VSC3316 crossbar switch */ | |
179 | ret = select_i2c_ch_pca(I2C_CH_VSC3316); | |
180 | if (!ret) { | |
181 | ret = vsc3316_config(VSC3316_TX_ADDRESS, | |
182 | vsc16_tx_sgmii_lane_cd, num_vsc16_con); | |
183 | if (ret) | |
184 | return ret; | |
185 | ret = vsc3316_config(VSC3316_RX_ADDRESS, | |
186 | vsc16_rx_sgmii_lane_cd, num_vsc16_con); | |
187 | if (ret) | |
188 | return ret; | |
189 | } else { | |
190 | return ret; | |
191 | } | |
192 | break; | |
193 | #endif | |
194 | ||
195 | case 0x3E: | |
196 | case 0x0D: | |
197 | case 0x0E: | |
198 | case 0x12: | |
199 | num_vsc16_con = NUM_CON_VSC3316; | |
200 | /* Configure VSC3316 crossbar switch */ | |
201 | ret = select_i2c_ch_pca(I2C_CH_VSC3316); | |
202 | if (!ret) { | |
203 | ret = vsc3316_config(VSC3316_TX_ADDRESS, | |
204 | vsc16_tx_sfp, num_vsc16_con); | |
205 | if (ret) | |
206 | return ret; | |
207 | ret = vsc3316_config(VSC3316_RX_ADDRESS, | |
208 | vsc16_rx_sfp, num_vsc16_con); | |
209 | if (ret) | |
210 | return ret; | |
211 | } else { | |
212 | return ret; | |
213 | } | |
214 | break; | |
215 | default: | |
216 | printf("WARNING:VSC crossbars programming not supported for:%x" | |
217 | " SerDes1 Protocol.\n", serdes1_prtcl); | |
218 | return -1; | |
219 | } | |
220 | ||
221 | switch (serdes2_prtcl) { | |
222 | case 0x9E: | |
223 | case 0x9A: | |
224 | case 0x98: | |
225 | case 0xb2: | |
226 | case 0x49: | |
227 | case 0x4E: | |
228 | case 0x8D: | |
229 | case 0x7A: | |
230 | num_vsc08_con = NUM_CON_VSC3308; | |
231 | /* Configure VSC3308 crossbar switch */ | |
232 | ret = select_i2c_ch_pca(I2C_CH_VSC3308); | |
233 | if (!ret) { | |
234 | ret = vsc3308_config(VSC3308_TX_ADDRESS, | |
235 | vsc08_tx_amc, num_vsc08_con); | |
236 | if (ret) | |
237 | return ret; | |
238 | ret = vsc3308_config(VSC3308_RX_ADDRESS, | |
239 | vsc08_rx_amc, num_vsc08_con); | |
240 | if (ret) | |
241 | return ret; | |
242 | } else { | |
243 | return ret; | |
244 | } | |
245 | break; | |
246 | default: | |
247 | printf("WARNING:VSC crossbars programming not supported for: %x" | |
248 | " SerDes2 Protocol.\n", serdes2_prtcl); | |
249 | return -1; | |
250 | } | |
251 | ||
252 | return 0; | |
253 | } | |
254 | ||
255 | int board_early_init_r(void) | |
256 | { | |
257 | const unsigned int flashbase = CONFIG_SYS_FLASH_BASE; | |
258 | const u8 flash_esel = find_tlb_idx((void *)flashbase, 1); | |
259 | ||
260 | /* | |
261 | * Remap Boot flash + PROMJET region to caching-inhibited | |
262 | * so that flash can be erased properly. | |
263 | */ | |
264 | ||
265 | /* Flush d-cache and invalidate i-cache of any FLASH data */ | |
266 | flush_dcache(); | |
267 | invalidate_icache(); | |
268 | ||
269 | /* invalidate existing TLB entry for flash + promjet */ | |
270 | disable_tlb(flash_esel); | |
271 | ||
272 | set_tlb(1, flashbase, CONFIG_SYS_FLASH_BASE_PHYS, | |
273 | MAS3_SX|MAS3_SW|MAS3_SR, MAS2_I|MAS2_G, | |
274 | 0, flash_esel, BOOKE_PAGESZ_256M, 1); | |
275 | ||
276 | set_liodns(); | |
277 | #ifdef CONFIG_SYS_DPAA_QBMAN | |
278 | setup_portals(); | |
279 | #endif | |
280 | ||
281 | /* Configure VSC3316 and VSC3308 crossbar switches */ | |
282 | if (configure_vsc3316_3308()) | |
283 | printf("VSC:failed to configure VSC3316/3308.\n"); | |
284 | else | |
285 | printf("VSC:VSC3316/3308 successfully configured.\n"); | |
286 | ||
287 | select_i2c_ch_pca(I2C_CH_DEFAULT); | |
288 | ||
289 | return 0; | |
290 | } | |
291 | ||
292 | unsigned long get_board_sys_clk(void) | |
293 | { | |
294 | u8 sysclk_conf = QIXIS_READ(brdcfg[1]); | |
295 | ||
296 | switch ((sysclk_conf & 0x0C) >> 2) { | |
297 | case QIXIS_CLK_100: | |
298 | return 100000000; | |
299 | case QIXIS_CLK_125: | |
300 | return 125000000; | |
301 | case QIXIS_CLK_133: | |
302 | return 133333333; | |
303 | } | |
304 | return 66666666; | |
305 | } | |
306 | ||
307 | unsigned long get_board_ddr_clk(void) | |
308 | { | |
309 | u8 ddrclk_conf = QIXIS_READ(brdcfg[1]); | |
310 | ||
311 | switch (ddrclk_conf & 0x03) { | |
312 | case QIXIS_CLK_100: | |
313 | return 100000000; | |
314 | case QIXIS_CLK_125: | |
315 | return 125000000; | |
316 | case QIXIS_CLK_133: | |
317 | return 133333333; | |
318 | } | |
319 | return 66666666; | |
320 | } | |
321 | ||
322 | static int serdes_refclock(u8 sw, u8 sdclk) | |
323 | { | |
324 | unsigned int clock; | |
325 | int ret = -1; | |
326 | u8 brdcfg4; | |
327 | ||
328 | if (sdclk == 1) { | |
329 | brdcfg4 = QIXIS_READ(brdcfg[4]); | |
330 | if ((brdcfg4 & CLK_MUX_SEL_MASK) == ETH_PHY_CLK_OUT) | |
331 | return SRDS_PLLCR0_RFCK_SEL_125; | |
332 | else | |
333 | clock = (sw >> 5) & 7; | |
334 | } else | |
335 | clock = (sw >> 6) & 3; | |
336 | ||
337 | switch (clock) { | |
338 | case 0: | |
339 | ret = SRDS_PLLCR0_RFCK_SEL_100; | |
340 | break; | |
341 | case 1: | |
342 | ret = SRDS_PLLCR0_RFCK_SEL_125; | |
343 | break; | |
344 | case 2: | |
345 | ret = SRDS_PLLCR0_RFCK_SEL_156_25; | |
346 | break; | |
347 | case 3: | |
348 | ret = SRDS_PLLCR0_RFCK_SEL_161_13; | |
349 | break; | |
350 | case 4: | |
351 | case 5: | |
352 | case 6: | |
353 | ret = SRDS_PLLCR0_RFCK_SEL_122_88; | |
354 | break; | |
355 | default: | |
356 | ret = -1; | |
357 | break; | |
358 | } | |
359 | ||
360 | return ret; | |
361 | } | |
362 | ||
363 | static const char *serdes_clock_to_string(u32 clock) | |
364 | { | |
365 | switch (clock) { | |
366 | case SRDS_PLLCR0_RFCK_SEL_100: | |
367 | return "100"; | |
368 | case SRDS_PLLCR0_RFCK_SEL_125: | |
369 | return "125"; | |
370 | case SRDS_PLLCR0_RFCK_SEL_156_25: | |
371 | return "156.25"; | |
372 | case SRDS_PLLCR0_RFCK_SEL_161_13: | |
373 | return "161.13"; | |
374 | default: | |
375 | return "122.88"; | |
376 | } | |
377 | } | |
378 | ||
379 | #define NUM_SRDS_BANKS 2 | |
380 | ||
381 | int misc_init_r(void) | |
382 | { | |
383 | u8 sw; | |
384 | serdes_corenet_t *srds_regs = | |
385 | (void *)CONFIG_SYS_FSL_CORENET_SERDES_ADDR; | |
386 | u32 actual[NUM_SRDS_BANKS]; | |
387 | unsigned int i; | |
388 | int clock; | |
389 | ||
390 | sw = QIXIS_READ(brdcfg[2]); | |
391 | clock = serdes_refclock(sw, 1); | |
392 | if (clock >= 0) | |
393 | actual[0] = clock; | |
394 | else | |
395 | printf("Warning: SDREFCLK1 switch setting is unsupported\n"); | |
396 | ||
397 | sw = QIXIS_READ(brdcfg[4]); | |
398 | clock = serdes_refclock(sw, 2); | |
399 | if (clock >= 0) | |
400 | actual[1] = clock; | |
401 | else | |
402 | printf("Warning: SDREFCLK2 switch setting unsupported\n"); | |
403 | ||
404 | for (i = 0; i < NUM_SRDS_BANKS; i++) { | |
405 | u32 pllcr0 = srds_regs->bank[i].pllcr0; | |
406 | u32 expected = pllcr0 & SRDS_PLLCR0_RFCK_SEL_MASK; | |
407 | if (expected != actual[i]) { | |
408 | printf("Warning: SERDES bank %u expects reference clock" | |
409 | " %sMHz, but actual is %sMHz\n", i + 1, | |
410 | serdes_clock_to_string(expected), | |
411 | serdes_clock_to_string(actual[i])); | |
412 | } | |
413 | } | |
414 | ||
415 | return 0; | |
416 | } | |
417 | ||
418 | void ft_board_setup(void *blob, bd_t *bd) | |
419 | { | |
420 | phys_addr_t base; | |
421 | phys_size_t size; | |
422 | ||
423 | ft_cpu_setup(blob, bd); | |
424 | ||
425 | base = getenv_bootm_low(); | |
426 | size = getenv_bootm_size(); | |
427 | ||
428 | fdt_fixup_memory(blob, (u64)base, (u64)size); | |
429 | ||
430 | #ifdef CONFIG_PCI | |
431 | pci_of_setup(blob, bd); | |
432 | #endif | |
433 | ||
434 | fdt_fixup_liodn(blob); | |
435 | ||
436 | #ifdef CONFIG_HAS_FSL_DR_USB | |
437 | fdt_fixup_dr_usb(blob, bd); | |
438 | #endif | |
439 | ||
440 | #ifdef CONFIG_SYS_DPAA_FMAN | |
441 | fdt_fixup_fman_ethernet(blob); | |
442 | fdt_fixup_board_enet(blob); | |
443 | #endif | |
444 | } | |
4354889b SL |
445 | |
446 | /* | |
447 | * Dump board switch settings. | |
448 | * The bits that cannot be read/sampled via some FPGA or some | |
449 | * registers, they will be displayed as | |
450 | * underscore in binary format. mask[] has those bits. | |
451 | * Some bits are calculated differently than the actual switches | |
452 | * if booting with overriding by FPGA. | |
453 | */ | |
454 | void qixis_dump_switch(void) | |
455 | { | |
456 | int i; | |
457 | u8 sw[5]; | |
458 | ||
459 | /* | |
460 | * Any bit with 1 means that bit cannot be reverse engineered. | |
461 | * It will be displayed as _ in binary format. | |
462 | */ | |
463 | static const u8 mask[] = {0x07, 0, 0, 0xff, 0}; | |
464 | char buf[10]; | |
465 | u8 brdcfg[16], dutcfg[16]; | |
466 | ||
467 | for (i = 0; i < 16; i++) { | |
468 | brdcfg[i] = qixis_read(offsetof(struct qixis, brdcfg[0]) + i); | |
469 | dutcfg[i] = qixis_read(offsetof(struct qixis, dutcfg[0]) + i); | |
470 | } | |
471 | ||
472 | sw[0] = ((brdcfg[0] & 0x0f) << 4) | \ | |
473 | (brdcfg[9] & 0x08); | |
474 | sw[1] = ((dutcfg[1] & 0x01) << 7) | \ | |
475 | ((dutcfg[2] & 0x07) << 4) | \ | |
476 | ((dutcfg[6] & 0x10) >> 1) | \ | |
477 | ((dutcfg[6] & 0x80) >> 5) | \ | |
478 | ((dutcfg[1] & 0x40) >> 5) | \ | |
479 | (dutcfg[6] & 0x01); | |
480 | sw[2] = dutcfg[0]; | |
481 | sw[3] = 0; | |
482 | sw[4] = ((brdcfg[1] & 0x30) << 2) | \ | |
483 | ((brdcfg[1] & 0xc0) >> 2) | \ | |
484 | (brdcfg[1] & 0x0f); | |
485 | ||
486 | puts("DIP switch settings:\n"); | |
487 | for (i = 0; i < 5; i++) { | |
488 | printf("SW%d = 0b%s (0x%02x)\n", | |
489 | i + 1, byte_to_binary_mask(sw[i], mask[i], buf), sw[i]); | |
490 | } | |
491 | } |