aboutsummaryrefslogtreecommitdiff
path: root/hw/net/xilinx_axienet.c
diff options
context:
space:
mode:
authorPeter Crosthwaite <peter.crosthwaite@xilinx.com>2013-04-16 10:27:55 +1000
committerEdgar E. Iglesias <edgar.iglesias@gmail.com>2013-04-16 10:04:23 +0200
commit3630ae952a17c2107db25f397233536ef874558e (patch)
treeb5a92b065b5550fcf4616553fedd906324918e75 /hw/net/xilinx_axienet.c
parent35e60bfdbc14ce31bba55cc82144f8a2a82ede68 (diff)
xilinx_axienet/dma: Implement rx path flow control
Implement flow control for the RX data path from xilinx_axienet->xilinx_axidma. On short return from axidma, then ethernet sets up the notify callback to resume transfer from where it left off. This also allows the ethernet to track whether there is an in progress transaction and return false from ethernet can_receive() as appropriate. If the DMA backs up or is disabled it waits for enablement. When the rx stream IO region is touched, the can_push() notify function is called if set. Signed-off-by: Peter Crosthwaite <peter.crosthwaite@xilinx.com> Signed-off-by: Edgar E. Iglesias <edgar.iglesias@gmail.com>
Diffstat (limited to 'hw/net/xilinx_axienet.c')
-rw-r--r--hw/net/xilinx_axienet.c28
1 files changed, 25 insertions, 3 deletions
diff --git a/hw/net/xilinx_axienet.c b/hw/net/xilinx_axienet.c
index 6d27546b29..544c3ec9c3 100644
--- a/hw/net/xilinx_axienet.c
+++ b/hw/net/xilinx_axienet.c
@@ -383,6 +383,9 @@ struct XilinxAXIEnet {
uint8_t *rxmem;
+ uint32_t *rxapp;
+ uint32_t rxsize;
+ uint32_t rxpos;
};
static void axienet_rx_reset(XilinxAXIEnet *s)
@@ -645,7 +648,7 @@ static int eth_can_rx(NetClientState *nc)
XilinxAXIEnet *s = qemu_get_nic_opaque(nc);
/* RX enabled? */
- return !axienet_rx_resetting(s) && axienet_rx_enabled(s);
+ return !s->rxsize && !axienet_rx_resetting(s) && axienet_rx_enabled(s);
}
static int enet_match_addr(const uint8_t *buf, uint32_t f0, uint32_t f1)
@@ -663,6 +666,23 @@ static int enet_match_addr(const uint8_t *buf, uint32_t f0, uint32_t f1)
return match;
}
+static void axienet_eth_rx_notify(void *opaque)
+{
+ XilinxAXIEnet *s = XILINX_AXI_ENET(opaque);
+
+ while (s->rxsize && stream_can_push(s->tx_dev, axienet_eth_rx_notify, s)) {
+ size_t ret = stream_push(s->tx_dev, (void *)s->rxmem + s->rxpos,
+ s->rxsize, s->rxapp);
+ s->rxsize -= ret;
+ s->rxpos += ret;
+ if (!s->rxsize) {
+ s->regs[R_IS] |= IS_RX_COMPLETE;
+ g_free(s->rxapp);
+ }
+ }
+ enet_update_irq(s);
+}
+
static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size)
{
XilinxAXIEnet *s = qemu_get_nic_opaque(nc);
@@ -800,9 +820,11 @@ static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size)
/* Good frame. */
app[2] |= 1 << 6;
- stream_push(s->tx_dev, (void *)s->rxmem, size, app);
+ s->rxsize = size;
+ s->rxpos = 0;
+ s->rxapp = g_memdup(app, sizeof(app));
+ axienet_eth_rx_notify(s);
- s->regs[R_IS] |= IS_RX_COMPLETE;
enet_update_irq(s);
return size;
}