]> git.ipfire.org Git - thirdparty/u-boot.git/commitdiff
net:xilinx_axi_emac: Implement packet buffer
authorJason Wu <j.wu@xilinx.com>
Tue, 9 Apr 2013 06:16:32 +0000 (16:16 +1000)
committerMichal Simek <michal.simek@xilinx.com>
Tue, 9 Apr 2013 13:20:27 +0000 (15:20 +0200)
Implement the ring buffer for xilinx_axi_emac driver and set the
length to 2.

This is required to workaround the issue reported by Peter:
Report from Peter:
The xilinx_axi_emac driver only define a length 1 ring buffer. The issue comes
from the fact that the next packet is transmitted before the receive is acked.
So this is what happens:

 - Uboot sends DHCP discover
 - Server responds with DHCP offer -> Length 1 RX buffer is full
 - Uboot traps the RX and TXs the DHCP request
 - Server responds with DHCP ack -> dropped and RX buffer is still full
 - Uboot returns RX buffer to hardware control

The last two are racing against each other. I dont know what the deal with DHCP
and dropped packets is supposed to be, I think you are supposed to start from
scratch. But in QEMU u-boot loses the race to the hardware every time, so DHCP
never acquires. This is probably because QEMU networking is unrealistically
fast. The real hardware would suffer the packet transmission time which would
generally be greater than the time it take the NetReceive path to return back to
to the rx_handler where the RX ring buffer is then refreshed.

Reported-by: Peter Crosthwaite <peter.crosthwaite@xilinx.com>
Reported-by: Wendy Liang <jliang@xilinx.com>
Signed-off-by: Jason Wu <huanyu@xilinx.com>
Signed-off-by: Michal Simek <michal.simek@xilinx.com>
drivers/net/xilinx_axi_emac.c

index 1ff9abf94da74b2c3961ba53bd94eef5653d2947..eb774c50388f2d60d36b41c8e21e1919843c8fb6 100644 (file)
@@ -82,7 +82,8 @@
 
 #define DMAALIGN       128
 
-static u8 rxframe[PKTSIZE_ALIGN] __attribute((aligned(DMAALIGN)));
+#define RX_BUF 2
+static u8 rxframe[PKTSIZE_ALIGN * RX_BUF] __attribute((aligned(DMAALIGN)));
 
 /* Reflect dma offsets */
 struct axidma_reg {
@@ -98,6 +99,7 @@ struct axidma_priv {
        struct axidma_reg *dmatx;
        struct axidma_reg *dmarx;
        int phyaddr;
+       u32 rxbd_current;
 
        struct phy_device *phydev;
        struct mii_dev *bus;
@@ -492,7 +494,7 @@ static int axiemac_init(struct eth_device *dev, bd_t * bis)
 {
        struct axidma_priv *priv = dev->priv;
        struct axi_regs *regs = (struct axi_regs *)dev->iobase;
-       u32 temp;
+       u32 frame_addr, temp;
 
        debug("axiemac: Init started\n");
        /*
@@ -515,17 +517,23 @@ static int axiemac_init(struct eth_device *dev, bd_t * bis)
        /* Start DMA RX channel. Now it's ready to receive data.*/
        out_be32(&priv->dmarx->current, (u32)&rx_bd);
 
+       /* Create the RxBD ring */
+       memset(&rxframe, 0, sizeof(rxframe));
+
+       /* Identify the rx buffer address */
+       frame_addr = rxframe + priv->rxbd_current * PKTSIZE_ALIGN;
+
        /* Setup the BD. */
        memset(&rx_bd, 0, sizeof(rx_bd));
        rx_bd.next = (u32)&rx_bd;
-       rx_bd.phys = (u32)&rxframe;
-       rx_bd.cntrl = sizeof(rxframe);
+       rx_bd.phys = frame_addr;
+       rx_bd.cntrl = PKTSIZE_ALIGN;
        /* Flush the last BD so DMA core could see the updates */
        flush_cache((u32)&rx_bd, sizeof(rx_bd));
 
        /* It is necessary to flush rxframe because if you don't do it
         * then cache can contain uninitialized data */
-       flush_cache((u32)&rxframe, sizeof(rxframe));
+       flush_cache(frame_addr, PKTSIZE_ALIGN);
 
        /* Start the hardware */
        temp = in_be32(&priv->dmarx->control);
@@ -627,7 +635,7 @@ static int axiemac_recv(struct eth_device *dev)
 {
        u32 length;
        struct axidma_priv *priv = dev->priv;
-       u32 temp;
+       u32 frame_addr, nextframe_addr, temp;
 
        /* Wait for an incoming packet */
        if (!isrxready(dev))
@@ -641,36 +649,46 @@ static int axiemac_recv(struct eth_device *dev)
        out_be32(&priv->dmarx->control, temp);
 
        length = rx_bd.app4 & 0xFFFF; /* max length mask */
-#ifdef DEBUG
-       print_buffer(&rxframe, &rxframe[0], 1, length, 16);
-#endif
-       /* Pass the received frame up for processing */
-       if (length)
-               NetReceive(rxframe, length);
+
+       frame_addr = rxframe + priv->rxbd_current * PKTSIZE_ALIGN;
 
 #ifdef DEBUG
-       /* It is useful to clear buffer to be sure that it is consistent */
-       memset(rxframe, 0, sizeof(rxframe));
+       print_buffer(frame_addr, frame_addr[0], 1, length, 16);
 #endif
+
+       if ((++priv->rxbd_current) >= RX_BUF)
+               priv->rxbd_current = 0;
+       /* Get next frame address */
+       nextframe_addr = rxframe + priv->rxbd_current * PKTSIZE_ALIGN;
+
        /* Setup RxBD */
        /* Clear the whole buffer and setup it again - all flags are cleared */
        memset(&rx_bd, 0, sizeof(rx_bd));
        rx_bd.next = (u32)&rx_bd;
-       rx_bd.phys = (u32)&rxframe;
-       rx_bd.cntrl = sizeof(rxframe);
+       rx_bd.phys = nextframe_addr;
+       rx_bd.cntrl = PKTSIZE_ALIGN;
 
        /* Write bd to HW */
        flush_cache((u32)&rx_bd, sizeof(rx_bd));
 
        /* It is necessary to flush rxframe because if you don't do it
         * then cache will contain previous packet */
-       flush_cache((u32)&rxframe, sizeof(rxframe));
+       flush_cache(nextframe_addr, PKTSIZE_ALIGN);
 
        /* Rx BD is ready - start again */
        out_be32(&priv->dmarx->tail, (u32)&rx_bd);
 
        debug("axiemac: RX completed, framelength = %d\n", length);
 
+       /* Pass the received frame up for processing */
+       if (length)
+               NetReceive((u8 *)(frame_addr), length);
+
+#ifdef DEBUG
+       /* It is useful to clear buffer to be sure that it is consistent */
+       memset((u8 *)(frame_addr), 0, PKTSIZE_ALIGN);
+#endif
+
        return length;
 }