blob: 5fd84b260b5882984335d583ae5bb19de4bbff8f [file] [log] [blame]
Stefan Roesea6f2ea42020-06-30 12:08:58 +02001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (C) 2020 Stefan Roese <sr@denx.de>
4 */
5
Stefan Roese82ba2782020-09-02 08:29:10 +02006#include <dm.h>
Aaron Williams900e6ca2020-12-11 17:06:10 +01007#include <fdt_support.h>
Stefan Roese82ba2782020-09-02 08:29:10 +02008#include <ram.h>
Aaron Williams900e6ca2020-12-11 17:06:10 +01009#include <asm/gpio.h>
Stefan Roese82ba2782020-09-02 08:29:10 +020010
11#include <mach/octeon_ddr.h>
Aaron Williams900e6ca2020-12-11 17:06:10 +010012#include <mach/cvmx-qlm.h>
13#include <mach/octeon_qlm.h>
14#include <mach/octeon_fdt.h>
15#include <mach/cvmx-helper.h>
16#include <mach/cvmx-helper-cfg.h>
17#include <mach/cvmx-helper-util.h>
18#include <mach/cvmx-bgxx-defs.h>
Stefan Roese82ba2782020-09-02 08:29:10 +020019
20#include "board_ddr.h"
21
Aaron Williams900e6ca2020-12-11 17:06:10 +010022#define MAX_MIX_ENV_VARS 4
23
Stefan Roese82ba2782020-09-02 08:29:10 +020024#define EBB7304_DEF_DRAM_FREQ 800
25
26static struct ddr_conf board_ddr_conf[] = {
Aaron Williams900e6ca2020-12-11 17:06:10 +010027 OCTEON_EBB7304_DDR_CONFIGURATION
Stefan Roese82ba2782020-09-02 08:29:10 +020028};
29
Aaron Williams900e6ca2020-12-11 17:06:10 +010030static int no_phy[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
31
Stefan Roese82ba2782020-09-02 08:29:10 +020032struct ddr_conf *octeon_ddr_conf_table_get(int *count, int *def_ddr_freq)
33{
34 *count = ARRAY_SIZE(board_ddr_conf);
35 *def_ddr_freq = EBB7304_DEF_DRAM_FREQ;
36
37 return board_ddr_conf;
38}
Aaron Williams900e6ca2020-12-11 17:06:10 +010039
40/*
41 * parse_env_var: Parse the environment variable ("bgx_for_mix%d") to
42 * extract the lmac it is set to.
43 *
44 * index: Index of environment variable to parse.
45 * environment variable.
46 * env_bgx: Updated with the bgx of the lmac in the environment
47 * variable.
48 * env_lmac: Updated with the index of lmac in the environment
49 * variable.
50 *
51 * returns: Zero on success, error otherwise.
52 */
53static int parse_env_var(int index, int *env_bgx, int *env_lmac)
54{
55 char env_var[20];
56 ulong xipd_port;
57
58 sprintf(env_var, "bgx_for_mix%d", index);
59 xipd_port = env_get_ulong(env_var, 0, 0xffff);
60 if (xipd_port != 0xffff) {
61 int xiface;
62 struct cvmx_xiface xi;
63 struct cvmx_xport xp;
64
65 /*
66 * The environemt variable is set to the xipd port. Convert the
67 * xipd port to numa node, bgx, and lmac.
68 */
69 xiface = cvmx_helper_get_interface_num(xipd_port);
70 xi = cvmx_helper_xiface_to_node_interface(xiface);
71 xp = cvmx_helper_ipd_port_to_xport(xipd_port);
72 *env_bgx = xi.interface;
73 *env_lmac = cvmx_helper_get_interface_index_num(xp.port);
74 return 0;
75 }
76
77 return -1;
78}
79
80/*
81 * get_lmac_fdt_node: Search the device tree for the node corresponding to
82 * a given bgx lmac.
83 *
84 * fdt: Pointer to flat device tree
85 * search_node: Numa node of the lmac to search for.
86 * search_bgx: Bgx of the lmac to search for.
87 * search_lmac: Lmac index to search for.
88 * compat: Compatible string to search for.
89
90 * returns: The device tree node of the lmac if found,
91 * or -1 otherwise.
92 */
93static int get_lmac_fdt_node(const void *fdt, int search_node, int search_bgx, int search_lmac,
94 const char *compat)
95{
96 int node;
97 const fdt32_t *reg;
98 u64 addr;
99 int fdt_node = -1;
100 int fdt_bgx = -1;
101 int fdt_lmac = -1;
102 int len;
103 int parent;
104
105 /* Iterate through all bgx ports */
Marek BehĂșn5d6b4482022-01-20 01:04:42 +0100106 fdt_for_each_node_by_compatible(node, (void *)fdt, -1, compat) {
Aaron Williams900e6ca2020-12-11 17:06:10 +0100107 /* Get the node and bgx from the physical address */
108 parent = fdt_parent_offset(fdt, node);
109 reg = fdt_getprop(fdt, parent, "reg", &len);
110 if (parent < 0 || !reg)
111 continue;
112
113 addr = fdt_translate_address((void *)fdt, parent, reg);
114 fdt_node = (addr >> 36) & 0x7;
115 fdt_bgx = (addr >> 24) & 0xf;
116
117 /* Get the lmac index from the reg property */
118 reg = fdt_getprop(fdt, node, "reg", &len);
119 if (reg)
120 fdt_lmac = *reg;
121
122 /* Check for a match */
123 if (search_node == fdt_node && search_bgx == fdt_bgx &&
124 search_lmac == fdt_lmac)
125 return node;
126 }
127
128 return -1;
129}
130
131/*
132 * get_mix_fdt_node: Search the device tree for the node corresponding to
133 * a given mix.
134 *
135 * fdt: Pointer to flat device tree
136 * search_node: Mix numa node to search for.
137 * search_index: Mix index to search for.
138 *
139 * returns: The device tree node of the lmac if found,
140 * or -1 otherwise.
141 */
142static int get_mix_fdt_node(const void *fdt, int search_node, int search_index)
143{
144 int node;
145
146 /* Iterate through all the mix fdt nodes */
Marek BehĂșn5d6b4482022-01-20 01:04:42 +0100147 fdt_for_each_node_by_compatible(node, (void *)fdt, -1,
148 "cavium,octeon-7890-mix") {
Aaron Williams900e6ca2020-12-11 17:06:10 +0100149 int parent;
150 int len;
151 const char *name;
152 int mix_numa_node;
153 const fdt32_t *reg;
154 int mix_index = -1;
155 u64 addr;
156
157 /* Get the numa node of the mix from the parent node name */
158 parent = fdt_parent_offset(fdt, node);
159 if (parent < 0 ||
160 ((name = fdt_get_name(fdt, parent, &len)) == NULL) ||
161 ((name = strchr(name, '@')) == NULL))
162 continue;
163
164 name++;
165 mix_numa_node = simple_strtol(name, NULL, 0) ? 1 : 0;
166
167 /* Get the mix index from the reg property */
168 reg = fdt_getprop(fdt, node, "reg", &len);
169 if (reg) {
170 addr = fdt_translate_address((void *)fdt, parent, reg);
171 mix_index = (addr >> 11) & 1;
172 }
173
174 /* Check for a match */
175 if (mix_numa_node == search_node && mix_index == search_index)
176 return node;
177 }
178
179 return -1;
180}
181
182/*
183 * fdt_fix_mix: Fix the mix nodes in the device tree. Only the mix nodes
184 * configured by the user will be preserved. All other mix
185 * nodes will be trimmed.
186 *
187 * fdt: Pointer to flat device tree
188 *
189 * returns: Zero on success, error otherwise.
190 */
191static int fdt_fix_mix(const void *fdt)
192{
193 int node;
194 int next_node;
195 int len;
196 int i;
197
198 /* Parse all the mix port environment variables */
199 for (i = 0; i < MAX_MIX_ENV_VARS; i++) {
200 int env_node = 0;
201 int env_bgx = -1;
202 int env_lmac = -1;
203 int lmac_fdt_node = -1;
204 int mix_fdt_node = -1;
Marek BehĂșna4273f52021-11-26 14:57:06 +0100205 unsigned int lmac_phandle;
Aaron Williams900e6ca2020-12-11 17:06:10 +0100206 char *compat;
207
208 /* Get the lmac for this environment variable */
209 if (parse_env_var(i, &env_bgx, &env_lmac))
210 continue;
211
212 /* Get the fdt node for this lmac and add a phandle to it */
213 compat = "cavium,octeon-7890-bgx-port";
214 lmac_fdt_node = get_lmac_fdt_node(fdt, env_node, env_bgx,
215 env_lmac, compat);
216 if (lmac_fdt_node < 0) {
217 /* Must check for the xcv compatible string too */
218 compat = "cavium,octeon-7360-xcv";
219 lmac_fdt_node = get_lmac_fdt_node(fdt, env_node,
220 env_bgx, env_lmac,
221 compat);
222 if (lmac_fdt_node < 0) {
223 printf("WARNING: Failed to get lmac fdt node for %d%d%d\n",
224 env_node, env_bgx, env_lmac);
225 continue;
226 }
227 }
228
Marek BehĂșna4273f52021-11-26 14:57:06 +0100229 lmac_phandle = fdt_create_phandle((void *)fdt, lmac_fdt_node);
Aaron Williams900e6ca2020-12-11 17:06:10 +0100230
231 /* Get the fdt mix node corresponding to this lmac */
232 mix_fdt_node = get_mix_fdt_node(fdt, env_node, env_lmac);
233 if (mix_fdt_node < 0)
234 continue;
235
236 /* Point the mix to the lmac */
237 fdt_getprop(fdt, mix_fdt_node, "cavium,mac-handle", &len);
238 fdt_setprop_inplace((void *)fdt, mix_fdt_node,
239 "cavium,mac-handle", &lmac_phandle, len);
240 }
241
242 /* Trim unused mix'es from the device tree */
243 for (node = fdt_next_node(fdt, -1, NULL); node >= 0; node = next_node) {
244 const char *compat;
245 const fdt32_t *reg;
246
247 next_node = fdt_next_node(fdt, node, NULL);
248
249 compat = fdt_getprop(fdt, node, "compatible", &len);
250 if (compat) {
251 if (strcmp(compat, "cavium,octeon-7890-mix"))
252 continue;
253
254 reg = fdt_getprop(fdt, node, "cavium,mac-handle", &len);
255 if (reg) {
256 if (*reg == 0xffff)
257 fdt_nop_node((void *)fdt, node);
258 }
259 }
260 }
261
262 return 0;
263}
264
265static void kill_fdt_phy(void *fdt, int offset, void *arg)
266{
267 int len, phy_offset;
268 const fdt32_t *php;
269 u32 phandle;
270
271 php = fdt_getprop(fdt, offset, "phy-handle", &len);
272 if (php && len == sizeof(*php)) {
273 phandle = fdt32_to_cpu(*php);
274 fdt_nop_property(fdt, offset, "phy-handle");
275 phy_offset = fdt_node_offset_by_phandle(fdt, phandle);
276 if (phy_offset > 0)
277 fdt_nop_node(fdt, phy_offset);
278 }
279}
280
281void __fixup_xcv(void)
282{
283 unsigned long bgx = env_get_ulong("bgx_for_rgmii", 10,
284 (unsigned long)-1);
285 char fdt_key[16];
286 int i;
287
288 debug("%s: BGX %d\n", __func__, (int)bgx);
289
290 for (i = 0; i < 3; i++) {
291 snprintf(fdt_key, sizeof(fdt_key),
292 bgx == i ? "%d,xcv" : "%d,not-xcv", i);
293 debug("%s: trimming bgx %lu with key %s\n",
294 __func__, bgx, fdt_key);
295
296 octeon_fdt_patch_rename((void *)gd->fdt_blob, fdt_key,
297 "cavium,xcv-trim", true, NULL, NULL);
298 }
299}
300
301/* QLM0 - QLM6 */
302void __fixup_fdt(void)
303{
304 int qlm;
305 int speed = 0;
306
307 for (qlm = 0; qlm < 7; qlm++) {
308 enum cvmx_qlm_mode mode;
309 char fdt_key[16];
310 const char *type_str = "none";
311
312 mode = cvmx_qlm_get_mode(qlm);
313 switch (mode) {
314 case CVMX_QLM_MODE_SGMII:
315 case CVMX_QLM_MODE_RGMII_SGMII:
316 case CVMX_QLM_MODE_RGMII_SGMII_1X1:
317 type_str = "sgmii";
318 break;
319 case CVMX_QLM_MODE_XAUI:
320 case CVMX_QLM_MODE_RGMII_XAUI:
321 speed = (cvmx_qlm_get_gbaud_mhz(qlm) * 8 / 10) * 4;
322 if (speed == 10000)
323 type_str = "xaui";
324 else
325 type_str = "dxaui";
326 break;
327 case CVMX_QLM_MODE_RXAUI:
328 case CVMX_QLM_MODE_RGMII_RXAUI:
329 type_str = "rxaui";
330 break;
331 case CVMX_QLM_MODE_XLAUI:
332 case CVMX_QLM_MODE_RGMII_XLAUI:
333 type_str = "xlaui";
334 break;
335 case CVMX_QLM_MODE_XFI:
336 case CVMX_QLM_MODE_RGMII_XFI:
337 case CVMX_QLM_MODE_RGMII_XFI_1X1:
Vladimir Oltean6a6e4022021-09-18 15:32:34 +0300338 type_str = "10gbase-r";
Aaron Williams900e6ca2020-12-11 17:06:10 +0100339 break;
340 case CVMX_QLM_MODE_10G_KR:
341 case CVMX_QLM_MODE_RGMII_10G_KR:
342 type_str = "10G_KR";
343 break;
344 case CVMX_QLM_MODE_40G_KR4:
345 case CVMX_QLM_MODE_RGMII_40G_KR4:
346 type_str = "40G_KR4";
347 break;
348 case CVMX_QLM_MODE_SATA_2X1:
349 type_str = "sata";
350 break;
351 case CVMX_QLM_MODE_SGMII_2X1:
352 case CVMX_QLM_MODE_XFI_1X2:
353 case CVMX_QLM_MODE_10G_KR_1X2:
354 case CVMX_QLM_MODE_RXAUI_1X2:
355 case CVMX_QLM_MODE_MIXED: // special for DLM5 & DLM6
356 {
357 cvmx_bgxx_cmrx_config_t cmr_config;
358 cvmx_bgxx_spux_br_pmd_control_t pmd_control;
359 int mux = cvmx_qlm_mux_interface(2);
360
361 if (mux == 2) { // only dlm6
362 cmr_config.u64 = csr_rd(CVMX_BGXX_CMRX_CONFIG(2, 2));
363 pmd_control.u64 =
364 csr_rd(CVMX_BGXX_SPUX_BR_PMD_CONTROL(2, 2));
365 } else {
366 if (qlm == 5) {
367 cmr_config.u64 =
368 csr_rd(CVMX_BGXX_CMRX_CONFIG(0, 2));
369 pmd_control.u64 =
370 csr_rd(CVMX_BGXX_SPUX_BR_PMD_CONTROL(0, 2));
371 } else {
372 cmr_config.u64 =
373 csr_rd(CVMX_BGXX_CMRX_CONFIG(2, 2));
374 pmd_control.u64 =
375 csr_rd(CVMX_BGXX_SPUX_BR_PMD_CONTROL(2, 2));
376 }
377 }
378 switch (cmr_config.s.lmac_type) {
379 case 0:
380 type_str = "sgmii";
381 break;
382 case 1:
383 type_str = "xaui";
384 break;
385 case 2:
386 type_str = "rxaui";
387 break;
388 case 3:
389 if (pmd_control.s.train_en)
390 type_str = "10G_KR";
391 else
Vladimir Oltean6a6e4022021-09-18 15:32:34 +0300392 type_str = "10gbase-r";
Aaron Williams900e6ca2020-12-11 17:06:10 +0100393 break;
394 case 4:
395 if (pmd_control.s.train_en)
396 type_str = "40G_KR4";
397 else
398 type_str = "xlaui";
399 break;
400 default:
401 type_str = "none";
402 break;
403 }
404 break;
405 }
406 default:
407 type_str = "none";
408 break;
409 }
410 sprintf(fdt_key, "%d,%s", qlm, type_str);
411 debug("Patching qlm %d for %s for mode %d%s\n", qlm, fdt_key, mode,
412 no_phy[qlm] ? ", removing PHY" : "");
413 octeon_fdt_patch_rename((void *)gd->fdt_blob, fdt_key, NULL, true,
414 no_phy[qlm] ? kill_fdt_phy : NULL, NULL);
415 }
416}
417
418int board_fix_fdt(void)
419{
420 __fixup_fdt();
421 __fixup_xcv();
422
423 /* Fix the mix ports */
424 fdt_fix_mix(gd->fdt_blob);
425
426 return 0;
427}
428
429/*
430 * Here is the description of the parameters that are passed to QLM
431 * configuration:
432 *
433 * param0 : The QLM to configure
434 * param1 : Speed to configure the QLM at
435 * param2 : Mode the QLM to configure
436 * param3 : 1 = RC, 0 = EP
437 * param4 : 0 = GEN1, 1 = GEN2, 2 = GEN3
438 * param5 : ref clock select, 0 = 100Mhz, 1 = 125MHz, 2 = 156MHz
439 * param6 : ref clock input to use:
440 * 0 - external reference (QLMx_REF_CLK)
441 * 1 = common clock 0 (QLMC_REF_CLK0)
442 * 2 = common_clock 1 (QLMC_REF_CLK1)
443 */
444static void board_configure_qlms(void)
445{
446 int speed[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
447 int mode[8] = { -1, -1, -1, -1, -1, -1, -1, -1 };
448 int pcie_rc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
449 int pcie_gen[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
450 int ref_clock_sel[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
451 int ref_clock_input[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
452 struct gpio_desc desc;
453 int rbgx, rqlm;
454 char env_var[16];
455 int qlm;
456 int ret;
457
458 /* RGMII PHY reset GPIO */
459 ret = dm_gpio_lookup_name("gpio-controllerA27", &desc);
460 if (ret)
461 debug("gpio ret=%d\n", ret);
462 ret = dm_gpio_request(&desc, "rgmii_phy_reset");
463 if (ret)
464 debug("gpio_request ret=%d\n", ret);
465 ret = dm_gpio_set_dir_flags(&desc, GPIOD_IS_OUT);
466 if (ret)
467 debug("gpio dir ret=%d\n", ret);
468
469 /* Put RGMII PHY in reset */
470 dm_gpio_set_value(&desc, 0);
471
472 octeon_init_qlm(0);
473
474 rbgx = env_get_ulong("bgx_for_rgmii", 10, (unsigned long)-1);
475 switch (rbgx) {
476 case 0:
477 rqlm = 2;
478 break;
479 case 1:
480 rqlm = 3;
481 break;
482 case 2:
483 rqlm = 5;
484 break;
485 default:
486 rqlm = -1;
487 break;
488 }
489
490 for (qlm = 0; qlm < 7; qlm++) {
491 const char *mode_str;
492 char spd_env[16];
493
494 mode[qlm] = CVMX_QLM_MODE_DISABLED;
495 sprintf(env_var, "qlm%d_mode", qlm);
496 mode_str = env_get(env_var);
497 if (!mode_str)
498 continue;
499
500 if (qlm == 4 && mode[4] != -1 &&
501 mode[4] != CVMX_QLM_MODE_SATA_2X1) {
502 printf("Error: DLM 4 can only be configured for SATA\n");
503 continue;
504 }
505
506 if (strstr(mode_str, ",no_phy"))
507 no_phy[qlm] = 1;
508
509 if (!strncmp(mode_str, "sgmii", 5)) {
510 bool rgmii = false;
511
512 speed[qlm] = 1250;
513 if (rqlm == qlm && qlm < 5) {
514 mode[qlm] = CVMX_QLM_MODE_RGMII_SGMII;
515 rgmii = true;
516 } else if (qlm == 6 || qlm == 5) {
517 if (rqlm == qlm && qlm == 5) {
518 mode[qlm] = CVMX_QLM_MODE_RGMII_SGMII_1X1;
519 rgmii = true;
520 } else if (rqlm == 5 && qlm == 6 &&
521 mode[5] != CVMX_QLM_MODE_RGMII_SGMII_1X1) {
522 mode[qlm] = CVMX_QLM_MODE_RGMII_SGMII_2X1;
523 rgmii = true;
524 } else {
525 mode[qlm] = CVMX_QLM_MODE_SGMII_2X1;
526 }
527 } else {
528 mode[qlm] = CVMX_QLM_MODE_SGMII;
529 }
530 ref_clock_sel[qlm] = 2;
531
532 if (qlm == 5 || qlm == 6)
533 ref_clock_input[qlm] = 2; // use QLMC_REF_CLK1
534
535 if (no_phy[qlm]) {
536 int i;
537 int start = 0, stop = 2;
538
539 rbgx = 0;
540 switch (qlm) {
541 case 3:
542 rbgx = 1;
543 case 2:
544 for (i = 0; i < 4; i++) {
545 printf("Ignoring PHY for interface: %d, port: %d\n",
546 rbgx, i);
547 cvmx_helper_set_port_force_link_up(rbgx, i, true);
548 }
549 break;
550 case 6:
551 start = 2;
552 stop = 4;
553 case 5:
554 for (i = start; i < stop; i++) {
555 printf("Ignoring PHY for interface: %d, port: %d\n",
556 2, i);
557 cvmx_helper_set_port_force_link_up(2, i, true);
558 }
559 break;
560 default:
561 printf("SGMII not supported for QLM/DLM %d\n",
562 qlm);
563 break;
564 }
565 }
566 printf("QLM %d: SGMII%s\n",
567 qlm, rgmii ? ", RGMII" : "");
568 } else if (!strncmp(mode_str, "xaui", 4)) {
569 speed[qlm] = 3125;
570 mode[qlm] = CVMX_QLM_MODE_XAUI;
571 ref_clock_sel[qlm] = 2;
572 if (qlm == 5 || qlm == 6)
573 ref_clock_input[qlm] = 2; // use QLMC_REF_CLK1
574 printf("QLM %d: XAUI\n", qlm);
575 } else if (!strncmp(mode_str, "dxaui", 5)) {
576 speed[qlm] = 6250;
577 mode[qlm] = CVMX_QLM_MODE_XAUI;
578 ref_clock_sel[qlm] = 2;
579 if (qlm == 5 || qlm == 6)
580 ref_clock_input[qlm] = 2; // use QLMC_REF_CLK1
581 printf("QLM %d: DXAUI\n", qlm);
582 } else if (!strncmp(mode_str, "rxaui", 5)) {
583 bool rgmii = false;
584
585 speed[qlm] = 6250;
586 if (qlm == 5 || qlm == 6) {
587 if (rqlm == qlm && qlm == 5) {
588 mode[qlm] = CVMX_QLM_MODE_RGMII_RXAUI;
589 rgmii = true;
590 } else {
591 mode[qlm] = CVMX_QLM_MODE_RXAUI_1X2;
592 }
593 } else {
594 mode[qlm] = CVMX_QLM_MODE_RXAUI;
595 }
596 ref_clock_sel[qlm] = 2;
597 if (qlm == 5 || qlm == 6)
598 ref_clock_input[qlm] = 2; // use QLMC_REF_CLK1
599 printf("QLM %d: RXAUI%s\n",
600 qlm, rgmii ? ", rgmii" : "");
601 } else if (!strncmp(mode_str, "xlaui", 5)) {
602 speed[qlm] = 103125;
603 mode[qlm] = CVMX_QLM_MODE_XLAUI;
604 ref_clock_sel[qlm] = 2;
605 if (qlm == 5 || qlm == 6)
606 ref_clock_input[qlm] = 2; // use QLMC_REF_CLK1
607 sprintf(spd_env, "qlm%d_speed", qlm);
608 if (env_get(spd_env)) {
609 int spd = env_get_ulong(spd_env, 0, 8);
610
611 if (spd)
612 speed[qlm] = spd;
613 else
614 speed[qlm] = 103125;
615 }
616 printf("QLM %d: XLAUI\n", qlm);
Vladimir Oltean6a6e4022021-09-18 15:32:34 +0300617 } else if (!strncmp(mode_str, "10gbase-r", 3)) {
Aaron Williams900e6ca2020-12-11 17:06:10 +0100618 bool rgmii = false;
619
620 speed[qlm] = 103125;
621 if (rqlm == qlm) {
622 mode[qlm] = CVMX_QLM_MODE_RGMII_XFI;
623 rgmii = true;
624 } else if (qlm == 5 || qlm == 6) {
625 mode[qlm] = CVMX_QLM_MODE_XFI_1X2;
626 } else {
627 mode[qlm] = CVMX_QLM_MODE_XFI;
628 }
629 ref_clock_sel[qlm] = 2;
630 if (qlm == 5 || qlm == 6)
631 ref_clock_input[qlm] = 2; // use QLMC_REF_CLK1
632 printf("QLM %d: XFI%s\n", qlm, rgmii ? ", RGMII" : "");
633 } else if (!strncmp(mode_str, "10G_KR", 6)) {
634 speed[qlm] = 103125;
635 if (rqlm == qlm && qlm == 5)
636 mode[qlm] = CVMX_QLM_MODE_RGMII_10G_KR;
637 else if (qlm == 5 || qlm == 6)
638 mode[qlm] = CVMX_QLM_MODE_10G_KR_1X2;
639 else
640 mode[qlm] = CVMX_QLM_MODE_10G_KR;
641 ref_clock_sel[qlm] = 2;
642 if (qlm == 5 || qlm == 6)
643 ref_clock_input[qlm] = 2; // use QLMC_REF_CLK1
644 printf("QLM %d: 10G_KR\n", qlm);
645 } else if (!strncmp(mode_str, "40G_KR4", 7)) {
646 speed[qlm] = 103125;
647 mode[qlm] = CVMX_QLM_MODE_40G_KR4;
648 ref_clock_sel[qlm] = 2;
649 if (qlm == 5 || qlm == 6)
650 ref_clock_input[qlm] = 2; // use QLMC_REF_CLK1
651 printf("QLM %d: 40G_KR4\n", qlm);
652 } else if (!strcmp(mode_str, "pcie")) {
653 char *pmode;
654 int lanes = 0;
655
656 sprintf(env_var, "pcie%d_mode", qlm);
657 pmode = env_get(env_var);
658 if (pmode && !strcmp(pmode, "ep"))
659 pcie_rc[qlm] = 0;
660 else
661 pcie_rc[qlm] = 1;
662 sprintf(env_var, "pcie%d_gen", qlm);
663 pcie_gen[qlm] = env_get_ulong(env_var, 0, 3);
664 sprintf(env_var, "pcie%d_lanes", qlm);
665 lanes = env_get_ulong(env_var, 0, 8);
666 if (lanes == 8) {
667 mode[qlm] = CVMX_QLM_MODE_PCIE_1X8;
668 } else if (qlm == 5 || qlm == 6) {
669 if (lanes != 2) {
670 printf("QLM%d: Invalid lanes selected, defaulting to 2 lanes\n",
671 qlm);
672 }
673 mode[qlm] = CVMX_QLM_MODE_PCIE_1X2;
674 ref_clock_input[qlm] = 1; // use QLMC_REF_CLK0
675 } else {
676 mode[qlm] = CVMX_QLM_MODE_PCIE;
677 }
678 ref_clock_sel[qlm] = 0;
679 printf("QLM %d: PCIe gen%d %s, x%d lanes\n",
680 qlm, pcie_gen[qlm] + 1,
681 pcie_rc[qlm] ? "root complex" : "endpoint",
682 lanes);
683 } else if (!strcmp(mode_str, "sata")) {
684 mode[qlm] = CVMX_QLM_MODE_SATA_2X1;
685 ref_clock_sel[qlm] = 0;
686 ref_clock_input[qlm] = 1;
687 sprintf(spd_env, "qlm%d_speed", qlm);
688 if (env_get(spd_env)) {
689 int spd = env_get_ulong(spd_env, 0, 8);
690
691 if (spd == 1500 || spd == 3000 || spd == 3000)
692 speed[qlm] = spd;
693 else
694 speed[qlm] = 6000;
695 } else {
696 speed[qlm] = 6000;
697 }
698 } else {
699 printf("QLM %d: disabled\n", qlm);
700 }
701 }
702
703 for (qlm = 0; qlm < 7; qlm++) {
704 int rc;
705
706 if (mode[qlm] == -1)
707 continue;
708
709 debug("Configuring qlm%d with speed(%d), mode(%d), RC(%d), Gen(%d), REF_CLK(%d), CLK_SOURCE(%d)\n",
710 qlm, speed[qlm], mode[qlm], pcie_rc[qlm],
711 pcie_gen[qlm] + 1,
712 ref_clock_sel[qlm], ref_clock_input[qlm]);
713 rc = octeon_configure_qlm(qlm, speed[qlm], mode[qlm],
714 pcie_rc[qlm], pcie_gen[qlm],
715 ref_clock_sel[qlm],
716 ref_clock_input[qlm]);
717
718 if (speed[qlm] == 6250) {
719 if (mode[qlm] == CVMX_QLM_MODE_RXAUI) {
720 octeon_qlm_tune_v3(0, qlm, speed[qlm], 0x12,
721 0xa0, -1, -1);
722 } else {
723 octeon_qlm_tune_v3(0, qlm, speed[qlm], 0xa,
724 0xa0, -1, -1);
725 }
726 } else if (speed[qlm] == 103125) {
727 octeon_qlm_tune_v3(0, qlm, speed[qlm], 0xd, 0xd0,
728 -1, -1);
729 }
730
731 if (qlm == 4 && rc != 0)
732 /*
733 * There is a bug with SATA with 73xx. Until it's
734 * fixed we need to strip it from the device tree.
735 */
736 octeon_fdt_patch_rename((void *)gd->fdt_blob, "4,none",
737 NULL, true, NULL, NULL);
738 }
739
740 dm_gpio_set_value(&desc, 0); /* Put RGMII PHY in reset */
741 mdelay(10);
742 dm_gpio_set_value(&desc, 1); /* Take RGMII PHY out of reset */
743}
744
745int board_late_init(void)
746{
747 board_configure_qlms();
748
749 return 0;
750}