drivers: net: fsl-mc: Include MAC addr fixup to DPL

Previous to MC v10.x, port mac address was specified via DPL. Since
newer MC versions are compatible with old style DPLs, make the u-boot
env mac addresses visible there. This applies only to DPLs that have
an older version.

DPLs use 32 bit values for specifying MAC addresses. U-boot
environment variables take precedence over the MAC addresses already
visible in the DPL/DPC.

Signed-off-by: Bogdan Purcareata <bogdan.purcareata@nxp.com>
Signed-off-by: Heinz Wrobel <heinz.wrobel@nxp.com>
Reviewed-by: York Sun <york.sun@nxp.com>
diff --git a/drivers/net/fsl-mc/mc.c b/drivers/net/fsl-mc/mc.c
index 8da0428..8bf25c7 100644
--- a/drivers/net/fsl-mc/mc.c
+++ b/drivers/net/fsl-mc/mc.c
@@ -156,19 +156,142 @@
 }
 #endif
 
-static int mc_fixup_dpc_mac_addr(void *blob, int noff, int dpmac_id,
-		struct eth_device *eth_dev)
+#define MC_DT_INCREASE_SIZE	64
+
+enum mc_fixup_type {
+	MC_FIXUP_DPL,
+	MC_FIXUP_DPC
+};
+
+static int mc_fixup_mac_addr(void *blob, int nodeoffset,
+			     const char *propname, struct eth_device *eth_dev,
+			     enum mc_fixup_type type)
 {
-	int nodeoffset, err = 0;
+	int err = 0, len = 0, size, i;
+	unsigned char env_enetaddr[ARP_HLEN];
+	unsigned int enetaddr_32[ARP_HLEN];
+	void *val = NULL;
+
+	switch (type) {
+	case MC_FIXUP_DPL:
+	/* DPL likes its addresses on 32 * ARP_HLEN bits */
+	for (i = 0; i < ARP_HLEN; i++)
+		enetaddr_32[i] = cpu_to_fdt32(eth_dev->enetaddr[i]);
+	val = enetaddr_32;
+	len = sizeof(enetaddr_32);
+	break;
+
+	case MC_FIXUP_DPC:
+	val = eth_dev->enetaddr;
+	len = ARP_HLEN;
+	break;
+	}
+
+	/* MAC address property present */
+	if (fdt_get_property(blob, nodeoffset, propname, NULL)) {
+		/* u-boot MAC addr randomly assigned - leave the present one */
+		if (!eth_getenv_enetaddr_by_index("eth", eth_dev->index,
+						  env_enetaddr))
+			return err;
+	} else {
+		size = MC_DT_INCREASE_SIZE + strlen(propname) + len;
+		/* make room for mac address property */
+		err = fdt_increase_size(blob, size);
+		if (err) {
+			printf("fdt_increase_size: err=%s\n",
+			       fdt_strerror(err));
+			return err;
+		}
+	}
+
+	err = fdt_setprop(blob, nodeoffset, propname, val, len);
+	if (err) {
+		printf("fdt_setprop: err=%s\n", fdt_strerror(err));
+		return err;
+	}
+
+	return err;
+}
+
+#define is_dpni(s) (s != NULL ? !strncmp(s, "dpni@", 5) : 0)
+
+const char *dpl_get_connection_endpoint(void *blob, char *endpoint)
+{
+	int connoffset = fdt_path_offset(blob, "/connections"), off;
+	const char *s1, *s2;
+
+	for (off = fdt_first_subnode(blob, connoffset);
+	     off >= 0;
+	     off = fdt_next_subnode(blob, off)) {
+		s1 = fdt_stringlist_get(blob, off, "endpoint1", 0, NULL);
+		s2 = fdt_stringlist_get(blob, off, "endpoint2", 0, NULL);
+
+		if (!s1 || !s2)
+			continue;
+
+		if (strcmp(endpoint, s1) == 0)
+			return s2;
+
+		if (strcmp(endpoint, s2) == 0)
+			return s1;
+	}
+
+	return NULL;
+}
+
+static int mc_fixup_dpl_mac_addr(void *blob, int dpmac_id,
+				 struct eth_device *eth_dev)
+{
+	int objoff = fdt_path_offset(blob, "/objects");
+	int dpmacoff = -1, dpnioff = -1;
+	const char *endpoint;
+	char mac_name[10];
+	int err;
+
+	sprintf(mac_name, "dpmac@%d", dpmac_id);
+	dpmacoff = fdt_subnode_offset(blob, objoff, mac_name);
+	if (dpmacoff < 0)
+		/* dpmac not defined in DPL, so skip it. */
+		return 0;
+
+	err = mc_fixup_mac_addr(blob, dpmacoff, "mac_addr", eth_dev,
+				MC_FIXUP_DPL);
+	if (err) {
+		printf("Error fixing up dpmac mac_addr in DPL\n");
+		return err;
+	}
+
+	/* now we need to figure out if there is any
+	 * DPNI connected to this MAC, so we walk the
+	 * connection list
+	 */
+	endpoint = dpl_get_connection_endpoint(blob, mac_name);
+	if (!is_dpni(endpoint))
+		return 0;
+
+	/* let's see if we can fixup the DPNI as well */
+	dpnioff = fdt_subnode_offset(blob, objoff, endpoint);
+	if (dpnioff < 0)
+		/* DPNI not defined in DPL in the objects area */
+		return 0;
+
+	return mc_fixup_mac_addr(blob, dpnioff, "mac_addr", eth_dev,
+				 MC_FIXUP_DPL);
+}
+
+static int mc_fixup_dpc_mac_addr(void *blob, int dpmac_id,
+				 struct eth_device *eth_dev)
+{
+	int nodeoffset = fdt_path_offset(blob, "/board_info/ports"), noff;
+	int err = 0;
 	char mac_name[10];
-	const char link_type_mode[] = "FIXED_LINK";
-	unsigned char env_enetaddr[6];
+	const char link_type_mode[] = "MAC_LINK_TYPE_FIXED";
 
 	sprintf(mac_name, "mac@%d", dpmac_id);
 
 	/* node not found - create it */
-	nodeoffset = fdt_subnode_offset(blob, noff, (const char *) mac_name);
-	if (nodeoffset < 0) {
+	noff = fdt_subnode_offset(blob, nodeoffset, (const char *)mac_name);
+	if (noff < 0) {
 		err = fdt_increase_size(blob, 200);
 		if (err) {
 			printf("fdt_increase_size: err=%s\n",
@@ -176,10 +299,15 @@
 			return err;
 		}
 
-		nodeoffset = fdt_add_subnode(blob, noff, mac_name);
+		noff = fdt_add_subnode(blob, nodeoffset, mac_name);
+		if (noff < 0) {
+			printf("fdt_add_subnode: err=%s\n",
+			       fdt_strerror(err));
+			return err;
+		}
 
 		/* add default property of fixed link */
-		err = fdt_appendprop_string(blob, nodeoffset,
+		err = fdt_appendprop_string(blob, noff,
 					    "link_type", link_type_mode);
 		if (err) {
 			printf("fdt_appendprop_string: err=%s\n",
@@ -188,49 +316,53 @@
 		}
 	}
 
-	/* port_mac_address property present in DPC */
-	if (fdt_get_property(blob, nodeoffset, "port_mac_address", NULL)) {
-		/* MAC addr randomly assigned - leave the one in DPC */
-		eth_getenv_enetaddr_by_index("eth", eth_dev->index,
-						env_enetaddr);
-		if (is_zero_ethaddr(env_enetaddr))
-			return err;
+	return mc_fixup_mac_addr(blob, noff, "port_mac_address", eth_dev,
+				 MC_FIXUP_DPC);
+}
 
-		/* replace DPC MAC address with u-boot env one */
-		err = fdt_setprop(blob, nodeoffset, "port_mac_address",
-				  eth_dev->enetaddr, 6);
-		if (err) {
-			printf("fdt_setprop mac: err=%s\n", fdt_strerror(err));
-			return err;
-		}
+static int mc_fixup_mac_addrs(void *blob, enum mc_fixup_type type)
+{
+	int i, err = 0, ret = 0;
+	char ethname[10];
+	struct eth_device *eth_dev;
 
-		return 0;
-	}
+	for (i = WRIOP1_DPMAC1; i < NUM_WRIOP_PORTS; i++) {
+		/* port not enabled */
+		if ((wriop_is_enabled_dpmac(i) != 1) ||
+		    (wriop_get_phy_address(i) == -1))
+			continue;
 
-	/* append port_mac_address property to mac node in DPC */
-	err = fdt_increase_size(blob, 80);
-	if (err) {
-		printf("fdt_increase_size: err=%s\n", fdt_strerror(err));
-		return err;
-	}
+		sprintf(ethname, "DPMAC%d@%s", i,
+			phy_interface_strings[wriop_get_enet_if(i)]);
 
-	err = fdt_appendprop(blob, nodeoffset,
-			     "port_mac_address", eth_dev->enetaddr, 6);
-	if (err) {
-		printf("fdt_appendprop: err=%s\n", fdt_strerror(err));
-		return err;
+		eth_dev = eth_get_dev_by_name(ethname);
+		if (eth_dev == NULL)
+			continue;
+
+		switch (type) {
+		case MC_FIXUP_DPL:
+			err = mc_fixup_dpl_mac_addr(blob, i, eth_dev);
+			break;
+		case MC_FIXUP_DPC:
+			err = mc_fixup_dpc_mac_addr(blob, i, eth_dev);
+			break;
+		default:
+			break;
+		}
+
+		if (err)
+			printf("fsl-mc: ERROR fixing mac address for %s\n",
+			       ethname);
+		ret |= err;
 	}
 
-	return err;
+	return ret;
 }
 
 static int mc_fixup_dpc(u64 dpc_addr)
 {
 	void *blob = (void *)dpc_addr;
 	int nodeoffset, err = 0;
-	char ethname[10];
-	struct eth_device *eth_dev;
-	int i;
 
 	/* delete any existing ICID pools */
 	nodeoffset = fdt_path_offset(blob, "/resources/icid_pools");
@@ -255,30 +387,9 @@
 	/* fixup MAC addresses for dpmac ports */
 	nodeoffset = fdt_path_offset(blob, "/board_info/ports");
 	if (nodeoffset < 0)
-		goto out;
-
-	for (i = WRIOP1_DPMAC1; i < NUM_WRIOP_PORTS; i++) {
-		/* port not enabled */
-		if ((wriop_is_enabled_dpmac(i) != 1) ||
-		    (wriop_get_phy_address(i) == -1))
-			continue;
-
-		sprintf(ethname, "DPMAC%d@%s", i,
-			phy_interface_strings[wriop_get_enet_if(i)]);
-
-		eth_dev = eth_get_dev_by_name(ethname);
-		if (eth_dev == NULL)
-			continue;
-
-		err = mc_fixup_dpc_mac_addr(blob, nodeoffset, i, eth_dev);
-		if (err) {
-			printf("mc_fixup_dpc_mac_addr failed: err=%s\n",
-			fdt_strerror(err));
-			goto out;
-		}
-	}
+		return 0;
 
-out:
+	err = mc_fixup_mac_addrs(blob, MC_FIXUP_DPC);
 	flush_dcache_range(dpc_addr, dpc_addr + fdt_totalsize(blob));
 
 	return err;
@@ -341,6 +452,25 @@
 	return 0;
 }
 
+
+static int mc_fixup_dpl(u64 dpl_addr)
+{
+	void *blob = (void *)dpl_addr;
+	u32 ver = fdt_getprop_u32_default(blob, "/", "dpl-version", 0);
+	int err = 0;
+
+	/* The DPL fixup for mac addresses is only relevant
+	 * for old-style DPLs
+	 */
+	if (ver >= 10)
+		return 0;
+
+	err = mc_fixup_mac_addrs(blob, MC_FIXUP_DPL);
+	flush_dcache_range(dpl_addr, dpl_addr + fdt_totalsize(blob));
+
+	return err;
+}
+
 static int load_mc_dpl(u64 mc_ram_addr, size_t mc_ram_size, u64 mc_dpl_addr)
 {
 	u64 mc_dpl_offset;
@@ -387,6 +517,8 @@
 		      (u64)dpl_fdt_hdr, dpl_size, mc_ram_addr + mc_dpl_offset);
 #endif /* not defined CONFIG_SYS_LS_MC_DPL_IN_DDR */
 
+	if (mc_fixup_dpl(mc_ram_addr + mc_dpl_offset))
+		return -EINVAL;
 	dump_ram_words("DPL", (void *)(mc_ram_addr + mc_dpl_offset));
 	return 0;
 }