Commit 7dd7c2e796ca6481c9341cf9df0328bdb5bd9fd8
Committed by
Stefan Roese
1 parent
9433cd1109
Exists in
smarc_8mq_lf_v2020.04
and in
11 other branches
arm: mvebu: turris_mox: Check and configure modules
Check if Mox modules are connected in supported mode, then configure the MDIO addresses of switch modules. Signed-off-by: Marek Behún <marek.behun@nic.cz> Signed-off-by: Stefan Roese <sr@denx.de>
Showing 2 changed files with 244 additions and 1 deletions Side-by-side Diff
arch/arm/dts/armada-3720-turris-mox.dts
... | ... | @@ -110,6 +110,17 @@ |
110 | 110 | spi-max-frequency = <20000000>; |
111 | 111 | m25p,fast-read; |
112 | 112 | }; |
113 | + | |
114 | + moxtet@1 { | |
115 | + #address-cells = <1>; | |
116 | + #size-cells = <0>; | |
117 | + compatible = "cznic,moxtet"; | |
118 | + reg = <1>; | |
119 | + reset-gpios = <&gpiosb 2 GPIO_ACTIVE_LOW>; | |
120 | + spi-max-frequency = <1000000>; | |
121 | + spi-cpol; | |
122 | + spi-cpha; | |
123 | + }; | |
113 | 124 | }; |
114 | 125 | |
115 | 126 | &uart0 { |
board/CZ.NIC/turris_mox/turris_mox.c
... | ... | @@ -4,11 +4,13 @@ |
4 | 4 | */ |
5 | 5 | |
6 | 6 | #include <common.h> |
7 | +#include <asm/gpio.h> | |
7 | 8 | #include <asm/io.h> |
8 | 9 | #include <dm.h> |
9 | 10 | #include <clk.h> |
10 | 11 | #include <spi.h> |
11 | 12 | #include <mvebu/comphy.h> |
13 | +#include <miiphy.h> | |
12 | 14 | #include <linux/string.h> |
13 | 15 | #include <linux/libfdt.h> |
14 | 16 | #include <fdt_support.h> |
15 | 17 | |
... | ... | @@ -239,11 +241,138 @@ |
239 | 241 | return 0; |
240 | 242 | } |
241 | 243 | |
244 | +#define SW_SMI_CMD_R(d, r) (0x9800 | (((d) & 0x1f) << 5) | ((r) & 0x1f)) | |
245 | +#define SW_SMI_CMD_W(d, r) (0x9400 | (((d) & 0x1f) << 5) | ((r) & 0x1f)) | |
246 | + | |
247 | +static int sw_multi_read(struct mii_dev *bus, int sw, int dev, int reg) | |
248 | +{ | |
249 | + bus->write(bus, sw, 0, 0, SW_SMI_CMD_R(dev, reg)); | |
250 | + mdelay(5); | |
251 | + return bus->read(bus, sw, 0, 1); | |
252 | +} | |
253 | + | |
254 | +static void sw_multi_write(struct mii_dev *bus, int sw, int dev, int reg, | |
255 | + u16 val) | |
256 | +{ | |
257 | + bus->write(bus, sw, 0, 1, val); | |
258 | + bus->write(bus, sw, 0, 0, SW_SMI_CMD_W(dev, reg)); | |
259 | + mdelay(5); | |
260 | +} | |
261 | + | |
262 | +static int sw_scratch_read(struct mii_dev *bus, int sw, int reg) | |
263 | +{ | |
264 | + sw_multi_write(bus, sw, 0x1c, 0x1a, (reg & 0x7f) << 8); | |
265 | + return sw_multi_read(bus, sw, 0x1c, 0x1a) & 0xff; | |
266 | +} | |
267 | + | |
268 | +static void sw_led_write(struct mii_dev *bus, int sw, int port, int reg, | |
269 | + u16 val) | |
270 | +{ | |
271 | + sw_multi_write(bus, sw, port, 0x16, 0x8000 | ((reg & 7) << 12) | |
272 | + | (val & 0x7ff)); | |
273 | +} | |
274 | + | |
275 | +static void sw_blink_leds(struct mii_dev *bus, int peridot, int topaz) | |
276 | +{ | |
277 | + int i, p; | |
278 | + struct { | |
279 | + int port; | |
280 | + u16 val; | |
281 | + int wait; | |
282 | + } regs[] = { | |
283 | + { 2, 0xef, 1 }, { 2, 0xfe, 1 }, { 2, 0x33, 0 }, | |
284 | + { 4, 0xef, 1 }, { 4, 0xfe, 1 }, { 4, 0x33, 0 }, | |
285 | + { 3, 0xfe, 1 }, { 3, 0xef, 1 }, { 3, 0x33, 0 }, | |
286 | + { 1, 0xfe, 1 }, { 1, 0xef, 1 }, { 1, 0x33, 0 } | |
287 | + }; | |
288 | + | |
289 | + for (i = 0; i < 12; ++i) { | |
290 | + for (p = 0; p < peridot; ++p) { | |
291 | + sw_led_write(bus, 0x10 + p, regs[i].port, 0, | |
292 | + regs[i].val); | |
293 | + sw_led_write(bus, 0x10 + p, regs[i].port + 4, 0, | |
294 | + regs[i].val); | |
295 | + } | |
296 | + if (topaz) { | |
297 | + sw_led_write(bus, 0x2, 0x10 + regs[i].port, 0, | |
298 | + regs[i].val); | |
299 | + } | |
300 | + | |
301 | + if (regs[i].wait) | |
302 | + mdelay(75); | |
303 | + } | |
304 | +} | |
305 | + | |
306 | +static void check_switch_address(struct mii_dev *bus, int addr) | |
307 | +{ | |
308 | + if (sw_scratch_read(bus, addr, 0x70) >> 3 != addr) | |
309 | + printf("Check of switch MDIO address failed for 0x%02x\n", | |
310 | + addr); | |
311 | +} | |
312 | + | |
313 | +static int sfp, pci, topaz, peridot, usb, passpci; | |
314 | +static int sfp_pos, peridot_pos[3]; | |
315 | +static int module_count; | |
316 | + | |
317 | +static int configure_peridots(struct gpio_desc *reset_gpio) | |
318 | +{ | |
319 | + int i, ret; | |
320 | + u8 dout[MAX_MOX_MODULES]; | |
321 | + | |
322 | + memset(dout, 0, MAX_MOX_MODULES); | |
323 | + | |
324 | + /* set addresses of Peridot modules */ | |
325 | + for (i = 0; i < peridot; ++i) | |
326 | + dout[module_count - peridot_pos[i]] = (~i) & 3; | |
327 | + | |
328 | + /* | |
329 | + * if there is a SFP module connected to the last Peridot module, set | |
330 | + * the P10_SMODE to 1 for the Peridot module | |
331 | + */ | |
332 | + if (sfp) | |
333 | + dout[module_count - peridot_pos[i - 1]] |= 1 << 3; | |
334 | + | |
335 | + dm_gpio_set_value(reset_gpio, 1); | |
336 | + mdelay(10); | |
337 | + | |
338 | + ret = mox_do_spi(NULL, dout, module_count + 1); | |
339 | + | |
340 | + mdelay(10); | |
341 | + dm_gpio_set_value(reset_gpio, 0); | |
342 | + | |
343 | + mdelay(50); | |
344 | + | |
345 | + return ret; | |
346 | +} | |
347 | + | |
348 | +static int get_reset_gpio(struct gpio_desc *reset_gpio) | |
349 | +{ | |
350 | + int node; | |
351 | + | |
352 | + node = fdt_node_offset_by_compatible(gd->fdt_blob, 0, "cznic,moxtet"); | |
353 | + if (node < 0) { | |
354 | + printf("Cannot find Moxtet bus device node!\n"); | |
355 | + return -1; | |
356 | + } | |
357 | + | |
358 | + gpio_request_by_name_nodev(offset_to_ofnode(node), "reset-gpios", 0, | |
359 | + reset_gpio, GPIOD_IS_OUT); | |
360 | + | |
361 | + if (!dm_gpio_is_valid(reset_gpio)) { | |
362 | + printf("Cannot find reset GPIO for Moxtet bus!\n"); | |
363 | + return -1; | |
364 | + } | |
365 | + | |
366 | + return 0; | |
367 | +} | |
368 | + | |
242 | 369 | int last_stage_init(void) |
243 | 370 | { |
244 | 371 | int ret, i; |
245 | 372 | const u8 *topology; |
246 | - int module_count, is_sd; | |
373 | + int is_sd; | |
374 | + struct mii_dev *bus; | |
375 | + struct gpio_desc reset_gpio = {}; | |
247 | 376 | |
248 | 377 | ret = mox_get_topology(&topology, &module_count, &is_sd); |
249 | 378 | if (ret) { |
... | ... | @@ -275,6 +404,109 @@ |
275 | 404 | break; |
276 | 405 | default: |
277 | 406 | printf("% 4i: unknown (ID %i)\n", i + 1, topology[i]); |
407 | + } | |
408 | + } | |
409 | + | |
410 | + /* now check if modules are connected in supported mode */ | |
411 | + | |
412 | + for (i = 0; i < module_count; ++i) { | |
413 | + switch (topology[i]) { | |
414 | + case MOX_MODULE_SFP: | |
415 | + if (sfp) { | |
416 | + printf("Error: Only one SFP module is supported!\n"); | |
417 | + } else if (topaz) { | |
418 | + printf("Error: SFP module cannot be connected after Topaz Switch module!\n"); | |
419 | + } else { | |
420 | + sfp_pos = i; | |
421 | + ++sfp; | |
422 | + } | |
423 | + break; | |
424 | + case MOX_MODULE_PCI: | |
425 | + if (pci) { | |
426 | + printf("Error: Only one Mini-PCIe module is supported!\n"); | |
427 | + } else if (usb) { | |
428 | + printf("Error: Mini-PCIe module cannot come after USB 3.0 module!\n"); | |
429 | + } else if (i && (i != 1 || !passpci)) { | |
430 | + printf("Error: Mini-PCIe module should be the first connected module or come right after Passthrough Mini-PCIe module!\n"); | |
431 | + } else { | |
432 | + ++pci; | |
433 | + } | |
434 | + break; | |
435 | + case MOX_MODULE_TOPAZ: | |
436 | + if (topaz) { | |
437 | + printf("Error: Only one Topaz module is supported!\n"); | |
438 | + } else if (peridot >= 3) { | |
439 | + printf("Error: At most two Peridot modules can come before Topaz module!\n"); | |
440 | + } else { | |
441 | + ++topaz; | |
442 | + } | |
443 | + break; | |
444 | + case MOX_MODULE_PERIDOT: | |
445 | + if (sfp || topaz) { | |
446 | + printf("Error: Peridot module must come before SFP or Topaz module!\n"); | |
447 | + } else if (peridot >= 3) { | |
448 | + printf("Error: At most three Peridot modules are supported!\n"); | |
449 | + } else { | |
450 | + peridot_pos[peridot] = i; | |
451 | + ++peridot; | |
452 | + } | |
453 | + break; | |
454 | + case MOX_MODULE_USB3: | |
455 | + if (pci) { | |
456 | + printf("Error: USB 3.0 module cannot come after Mini-PCIe module!\n"); | |
457 | + } else if (usb) { | |
458 | + printf("Error: Only one USB 3.0 module is supported!\n"); | |
459 | + } else if (i && (i != 1 || !passpci)) { | |
460 | + printf("Error: USB 3.0 module should be the first connected module or come right after Passthrough Mini-PCIe module!\n"); | |
461 | + } else { | |
462 | + ++usb; | |
463 | + } | |
464 | + break; | |
465 | + case MOX_MODULE_PASSPCI: | |
466 | + if (passpci) { | |
467 | + printf("Error: Only one Passthrough Mini-PCIe module is supported!\n"); | |
468 | + } else if (i != 0) { | |
469 | + printf("Error: Passthrough Mini-PCIe module should be the first connected module!\n"); | |
470 | + } else { | |
471 | + ++passpci; | |
472 | + } | |
473 | + } | |
474 | + } | |
475 | + | |
476 | + /* now configure modules */ | |
477 | + | |
478 | + if (get_reset_gpio(&reset_gpio) < 0) | |
479 | + return 0; | |
480 | + | |
481 | + if (peridot > 0) { | |
482 | + if (configure_peridots(&reset_gpio) < 0) { | |
483 | + printf("Cannot configure Peridot modules!\n"); | |
484 | + peridot = 0; | |
485 | + } | |
486 | + } else { | |
487 | + dm_gpio_set_value(&reset_gpio, 1); | |
488 | + mdelay(50); | |
489 | + dm_gpio_set_value(&reset_gpio, 0); | |
490 | + mdelay(50); | |
491 | + } | |
492 | + | |
493 | + if (peridot || topaz) { | |
494 | + /* | |
495 | + * now check if the addresses are set by reading Scratch & Misc | |
496 | + * register 0x70 of Peridot (and potentially Topaz) modules | |
497 | + */ | |
498 | + | |
499 | + bus = miiphy_get_dev_by_name("neta@30000"); | |
500 | + if (!bus) { | |
501 | + printf("Cannot get MDIO bus device!\n"); | |
502 | + } else { | |
503 | + for (i = 0; i < peridot; ++i) | |
504 | + check_switch_address(bus, 0x10 + i); | |
505 | + | |
506 | + if (topaz) | |
507 | + check_switch_address(bus, 0x2); | |
508 | + | |
509 | + sw_blink_leds(bus, peridot, topaz); | |
278 | 510 | } |
279 | 511 | } |
280 | 512 |