net/ethoc: support private memory configurations

The ethoc device can be configured to have a private memory region
instead of having access to the main memory. In that case egress packets
must be copied into that memory for transmission and pointers to that
memory need to be passed to net_process_received_packet or returned from
the recv callback.

Signed-off-by: Max Filippov <jcmvbkbc@gmail.com>
Acked-by: Joe Hershberger <joe.hershberger@ni.com>
diff --git a/drivers/net/ethoc.c b/drivers/net/ethoc.c
index 198827f..d825aaa 100644
--- a/drivers/net/ethoc.c
+++ b/drivers/net/ethoc.c
@@ -179,6 +179,8 @@
 	u32 num_rx;
 	u32 cur_rx;
 	void __iomem *iobase;
+	void __iomem *packet;
+	phys_addr_t packet_phys;
 };
 
 /**
@@ -247,6 +249,7 @@
 static int ethoc_init_ring(struct ethoc *priv)
 {
 	struct ethoc_bd bd;
+	phys_addr_t addr = priv->packet_phys;
 	int i;
 
 	priv->cur_tx = 0;
@@ -258,6 +261,10 @@
 	bd.addr = 0;
 
 	for (i = 0; i < priv->num_tx; i++) {
+		if (addr) {
+			bd.addr = addr;
+			addr += PKTSIZE_ALIGN;
+		}
 		if (i == priv->num_tx - 1)
 			bd.stat |= TX_BD_WRAP;
 
@@ -267,7 +274,12 @@
 	bd.stat = RX_BD_EMPTY | RX_BD_IRQ;
 
 	for (i = 0; i < priv->num_rx; i++) {
-		bd.addr = virt_to_phys(net_rx_packets[i]);
+		if (addr) {
+			bd.addr = addr;
+			addr += PKTSIZE_ALIGN;
+		} else {
+			bd.addr = virt_to_phys(net_rx_packets[i]);
+		}
 		if (i == priv->num_rx - 1)
 			bd.stat |= RX_BD_WRAP;
 
@@ -367,7 +379,10 @@
 		int size = bd.stat >> 16;
 
 		size -= 4;	/* strip the CRC */
-		*packetp = net_rx_packets[i];
+		if (priv->packet)
+			*packetp = priv->packet + entry * PKTSIZE_ALIGN;
+		else
+			*packetp = net_rx_packets[i];
 		return size;
 	} else {
 		return 0;
@@ -430,8 +445,15 @@
 		bd.stat |= TX_BD_PAD;
 	else
 		bd.stat &= ~TX_BD_PAD;
-	bd.addr = virt_to_phys(packet);
 
+	if (priv->packet) {
+		void *p = priv->packet + entry * PKTSIZE_ALIGN;
+
+		memcpy(p, packet, length);
+		packet = p;
+	} else {
+		bd.addr = virt_to_phys(packet);
+	}
 	flush_dcache_range((ulong)packet, (ulong)packet + length);
 	bd.stat &= ~(TX_BD_STATS | TX_BD_LEN_MASK);
 	bd.stat |= TX_BD_LEN(length);
@@ -468,12 +490,17 @@
 	struct ethoc_bd bd;
 	u32 i = priv->cur_rx % priv->num_rx;
 	u32 entry = priv->num_tx + i;
+	void *src;
 
 	ethoc_read_bd(priv, entry, &bd);
 
+	if (priv->packet)
+		src = priv->packet + entry * PKTSIZE_ALIGN;
+	else
+		src = net_rx_packets[i];
 	/* clear the buffer descriptor so it can be reused */
-	flush_dcache_range((ulong)net_rx_packets[i],
-			   (ulong)net_rx_packets[i] + PKTSIZE_ALIGN);
+	flush_dcache_range((ulong)src,
+			   (ulong)src + PKTSIZE_ALIGN);
 	bd.stat &= ~RX_BD_STATS;
 	bd.stat |= RX_BD_EMPTY;
 	ethoc_write_bd(priv, entry, &bd);
@@ -529,8 +556,12 @@
 static int ethoc_ofdata_to_platdata(struct udevice *dev)
 {
 	struct ethoc_eth_pdata *pdata = dev_get_platdata(dev);
+	fdt_addr_t addr;
 
 	pdata->eth_pdata.iobase = dev_get_addr(dev);
+	addr = dev_get_addr_index(dev, 1);
+	if (addr != FDT_ADDR_T_NONE)
+		pdata->packet_base = addr;
 	return 0;
 }
 
@@ -540,6 +571,11 @@
 	struct ethoc *priv = dev_get_priv(dev);
 
 	priv->iobase = ioremap(pdata->eth_pdata.iobase, ETHOC_IOSIZE);
+	if (pdata->packet_base) {
+		priv->packet_phys = pdata->packet_base;
+		priv->packet = ioremap(pdata->packet_base,
+				       (1 + PKTBUFSRX) * PKTSIZE_ALIGN);
+	}
 	return 0;
 }