diff --git a/board/CZ.NIC/turris_mox/turris_mox.c b/board/CZ.NIC/turris_mox/turris_mox.c
index 21a3e63864..3361579d7c 100644
--- a/board/CZ.NIC/turris_mox/turris_mox.c
+++ b/board/CZ.NIC/turris_mox/turris_mox.c
@@ -3,6 +3,7 @@
  * Copyright (C) 2018 Marek Behun <marek.behun@nic.cz>
  */
 
+#include <stdarg.h>
 #include <common.h>
 #include <asm/gpio.h>
 #include <asm/io.h>
@@ -34,7 +35,11 @@
 #define ARMADA_37XX_SPI_DOUT	0xd0010608
 #define ARMADA_37XX_SPI_DIN	0xd001060c
 
+#define ETH1_PATH	"/soc/internal-regs@d0000000/ethernet@40000"
+#define MDIO_PATH	"/soc/internal-regs@d0000000/mdio@32004"
+#define MOXTET_SFP_PATH	"/soc/internal-regs@d0000000/spi@10600/moxtet@1/moxtet-sfp@0"
 #define PCIE_PATH	"/soc/pcie@d0070000"
+#define SFP_PATH	"/sfp"
 
 typedef enum {
 	MOX_UNKNOWN,
@@ -530,3 +535,220 @@ int last_stage_init(void)
 
 	return 0;
 }
+
+#if defined(CONFIG_OF_BOARD_SETUP)
+
+static int vnode_by_path(void *blob, const char *fmt, va_list ap)
+{
+	char path[128];
+
+	vsprintf(path, fmt, ap);
+	return fdt_path_offset(blob, path);
+}
+
+static int node_by_path(void *blob, const char *fmt, ...)
+{
+	va_list ap;
+	int res;
+
+	va_start(ap, fmt);
+	res = vnode_by_path(blob, fmt, ap);
+	va_end(ap);
+
+	return res;
+}
+
+static int phandle_by_path(void *blob, const char *fmt, ...)
+{
+	va_list ap;
+	int node, phandle, res;
+
+	va_start(ap, fmt);
+	node = vnode_by_path(blob, fmt, ap);
+	va_end(ap);
+
+	if (node < 0)
+		return node;
+
+	phandle = fdt_get_phandle(blob, node);
+	if (phandle > 0)
+		return phandle;
+
+	phandle = fdt_get_max_phandle(blob);
+	if (phandle < 0)
+		return phandle;
+
+	phandle += 1;
+
+	res = fdt_setprop_u32(blob, node, "linux,phandle", phandle);
+	if (res < 0)
+		return res;
+
+	res = fdt_setprop_u32(blob, node, "phandle", phandle);
+	if (res < 0)
+		return res;
+
+	return phandle;
+}
+
+static int enable_by_path(void *blob, const char *fmt, ...)
+{
+	va_list ap;
+	int node;
+
+	va_start(ap, fmt);
+	node = vnode_by_path(blob, fmt, ap);
+	va_end(ap);
+
+	if (node < 0)
+		return node;
+
+	return fdt_setprop_string(blob, node, "status", "okay");
+}
+
+static bool is_topaz(int id)
+{
+	return topaz && id == peridot + topaz - 1;
+}
+
+static int switch_addr(int id)
+{
+	return is_topaz(id) ? 0x2 : 0x10 + id;
+}
+
+static int setup_switch(void *blob, int id)
+{
+	int res, addr, i, node, phandle;
+
+	addr = switch_addr(id);
+
+	/* first enable the switch by setting status = "okay" */
+	res = enable_by_path(blob, MDIO_PATH "/switch%i@%x", id, addr);
+	if (res < 0)
+		return res;
+
+	/*
+	 * now if there are more switches or a SFP module coming after,
+	 * enable corresponding ports
+	 */
+	if (id < peridot + topaz - 1)
+		res = enable_by_path(blob,
+				     MDIO_PATH "/switch%i@%x/ports/port@a",
+				     id, addr);
+	else if (id == peridot - 1 && !topaz && sfp)
+		res = enable_by_path(blob,
+				     MDIO_PATH "/switch%i@%x/ports/port-sfp@a",
+				     id, addr);
+	else
+		res = 0;
+	if (res < 0)
+		return res;
+
+	if (id >= peridot + topaz - 1)
+		return 0;
+
+	/* finally change link property if needed */
+	node = node_by_path(blob, MDIO_PATH "/switch%i@%x/ports/port@a", id,
+			    addr);
+	if (node < 0)
+		return node;
+
+	for (i = id + 1; i < peridot + topaz; ++i) {
+		phandle = phandle_by_path(blob,
+					  MDIO_PATH "/switch%i@%x/ports/port@%x",
+					  i, switch_addr(i),
+					  is_topaz(i) ? 5 : 9);
+		if (phandle < 0)
+			return phandle;
+
+		if (i == id + 1)
+			res = fdt_setprop_u32(blob, node, "link", phandle);
+		else
+			res = fdt_appendprop_u32(blob, node, "link", phandle);
+		if (res < 0)
+			return res;
+	}
+
+	return 0;
+}
+
+int ft_board_setup(void *blob, bd_t *bd)
+{
+	int node, phandle, res;
+
+	if (pci || usb) {
+		node = fdt_path_offset(blob, PCIE_PATH);
+		if (node < 0)
+			return node;
+
+		res = fdt_setprop_string(blob, node, "status", "okay");
+		if (res < 0)
+			return res;
+	}
+
+	if (peridot || topaz) {
+		int i;
+
+		res = enable_by_path(blob, ETH1_PATH);
+		if (res < 0)
+			return res;
+
+		for (i = 0; i < peridot + topaz; ++i) {
+			res = setup_switch(blob, i);
+			if (res < 0)
+				return res;
+		}
+	}
+
+	if (sfp) {
+		res = enable_by_path(blob, SFP_PATH);
+		if (res < 0)
+			return res;
+
+		if (!peridot) {
+			phandle = phandle_by_path(blob, SFP_PATH);
+			if (phandle < 0)
+				return res;
+
+			node = node_by_path(blob, ETH1_PATH);
+			if (node < 0)
+				return node;
+
+			res = fdt_setprop_u32(blob, node, "sfp", phandle);
+			if (res < 0)
+				return res;
+
+			res = fdt_setprop_string(blob, node, "phy-mode",
+						 "sgmii");
+			if (res < 0)
+				return res;
+		}
+
+		res = enable_by_path(blob, MOXTET_SFP_PATH);
+		if (res < 0)
+			return res;
+
+		if (sfp_pos) {
+			char newname[16];
+
+			/* moxtet-sfp is on non-zero position, change default */
+			node = node_by_path(blob, MOXTET_SFP_PATH);
+			if (node < 0)
+				return node;
+
+			res = fdt_setprop_u32(blob, node, "reg", sfp_pos);
+			if (res < 0)
+				return res;
+
+			sprintf(newname, "moxtet-sfp@%x", sfp_pos);
+
+			res = fdt_set_name(blob, node, newname);
+			if (res < 0)
+				return res;
+		}
+	}
+
+	return 0;
+}
+
+#endif
diff --git a/configs/turris_mox_defconfig b/configs/turris_mox_defconfig
index 1dd6826dbc..fb4192df56 100644
--- a/configs/turris_mox_defconfig
+++ b/configs/turris_mox_defconfig
@@ -14,6 +14,7 @@ CONFIG_SYS_CONSOLE_INFO_QUIET=y
 # CONFIG_DISPLAY_BOARDINFO is not set
 CONFIG_ARCH_EARLY_INIT_R=y
 CONFIG_OF_BOARD_FIXUP=y
+CONFIG_OF_BOARD_SETUP=y
 CONFIG_CMD_CLK=y
 # CONFIG_CMD_FLASH is not set
 CONFIG_CMD_I2C=y
