On Sat, Jan 25, 2014 at 11:37 PM, Beniamino Galvani <b.galv...@gmail.com> wrote: > On Thu, Jan 23, 2014 at 11:04:32PM +1000, Peter Crosthwaite wrote: >> On Mon, Jan 20, 2014 at 9:25 AM, Beniamino Galvani <b.galv...@gmail.com> >> wrote: >> > This patch adds support for the Fast Ethernet MAC found on Allwinner >> > SoCs, together with a basic emulation of Realtek RTL8201CP PHY. >> > >> > Since there is no public documentation of the Allwinner controller, the >> > implementation is based on Linux kernel driver. >> > >> > Signed-off-by: Beniamino Galvani <b.galv...@gmail.com> >> > --- >> > default-configs/arm-softmmu.mak | 1 + >> > hw/net/Makefile.objs | 1 + >> > hw/net/allwinner_emac.c | 589 >> > +++++++++++++++++++++++++++++++++++++++ >> > include/hw/net/allwinner_emac.h | 222 +++++++++++++++ >> > 4 files changed, 813 insertions(+) >> > create mode 100644 hw/net/allwinner_emac.c >> > create mode 100644 include/hw/net/allwinner_emac.h >> > >> > diff --git a/default-configs/arm-softmmu.mak >> > b/default-configs/arm-softmmu.mak >> > index ce1d620..f3513fa 100644 >> > --- a/default-configs/arm-softmmu.mak >> > +++ b/default-configs/arm-softmmu.mak >> > @@ -27,6 +27,7 @@ CONFIG_SSI_SD=y >> > CONFIG_SSI_M25P80=y >> > CONFIG_LAN9118=y >> > CONFIG_SMC91C111=y >> > +CONFIG_ALLWINNER_EMAC=y >> > CONFIG_DS1338=y >> > CONFIG_PFLASH_CFI01=y >> > CONFIG_PFLASH_CFI02=y >> > diff --git a/hw/net/Makefile.objs b/hw/net/Makefile.objs >> > index 951cca3..75e80c2 100644 >> > --- a/hw/net/Makefile.objs >> > +++ b/hw/net/Makefile.objs >> > @@ -18,6 +18,7 @@ common-obj-$(CONFIG_OPENCORES_ETH) += opencores_eth.o >> > common-obj-$(CONFIG_XGMAC) += xgmac.o >> > common-obj-$(CONFIG_MIPSNET) += mipsnet.o >> > common-obj-$(CONFIG_XILINX_AXI) += xilinx_axienet.o >> > +common-obj-$(CONFIG_ALLWINNER_EMAC) += allwinner_emac.o >> > >> > common-obj-$(CONFIG_CADENCE) += cadence_gem.o >> > common-obj-$(CONFIG_STELLARIS_ENET) += stellaris_enet.o >> > diff --git a/hw/net/allwinner_emac.c b/hw/net/allwinner_emac.c >> > new file mode 100644 >> > index 0000000..4cad7e0 >> > --- /dev/null >> > +++ b/hw/net/allwinner_emac.c >> > @@ -0,0 +1,589 @@ >> > +/* >> > + * Emulation of Allwinner EMAC Fast Ethernet controller and >> > + * Realtek RTL8201CP PHY >> > + * >> > + * Copyright (C) 2014 Beniamino Galvani <b.galv...@gmail.com> >> > + * >> > + * This program is free software; you can redistribute it and/or modify >> > + * it under the terms of the GNU General Public License as published by >> > + * the Free Software Foundation; either version 2 of the License, or >> > + * (at your option) any later version. >> > + * >> > + * This program is distributed in the hope that it will be useful, >> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of >> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >> > + * GNU General Public License for more details. >> > + * >> > + */ >> > +#include "hw/sysbus.h" >> > +#include "net/net.h" >> > +#include "hw/net/allwinner_emac.h" >> > +#include <zlib.h> >> > + >> > +static uint8_t padding[60]; >> > + >> > +static void mii_set_link(RTL8201CPState *mii, bool link_ok) >> > +{ >> > + if (link_ok) { >> > + mii->bmsr |= MII_BMSR_LINK_ST; >> > + mii->anlpar |= MII_ANAR_TXFD | MII_ANAR_10FD | MII_ANAR_10 | >> > + MII_ANAR_CSMACD; >> > + } else { >> > + mii->bmsr &= ~MII_BMSR_LINK_ST; >> > + mii->anlpar = MII_ANAR_TX; >> > + } >> > +} >> > + >> > +static void mii_reset(RTL8201CPState *mii, bool link_ok) >> > +{ >> > + mii->bmcr = MII_BMCR_FD | MII_BMCR_AUTOEN | MII_BMCR_SPEED; >> > + mii->bmsr = MII_BMSR_100TX_FD | MII_BMSR_100TX_HD | MII_BMSR_10T_FD | >> > + MII_BMSR_10T_HD | MII_BMSR_MFPS | MII_BMSR_AUTONEG; >> > + mii->anar = MII_ANAR_TXFD | MII_ANAR_TX | MII_ANAR_10FD | MII_ANAR_10 >> > | >> > + MII_ANAR_CSMACD; >> > + mii->anlpar = MII_ANAR_TX; >> > + >> > + mii_set_link(mii, link_ok); >> > +} >> > + >> > +static uint16_t aw_emac_mdio_read(AwEmacState *s, uint8_t addr, uint8_t >> > reg) >> >> Drop the AW reference here (replace with "RTL8201"). >> >> > +{ >> > + RTL8201CPState *mii = &s->mii; >> > + uint16_t ret = 0xffff; >> > + >> > + if (addr == s->phy_addr) { >> > + switch (reg) { >> > + case MII_BMCR: >> > + return mii->bmcr; >> > + case MII_BMSR: >> > + return mii->bmsr; >> > + case MII_PHYID1: >> > + return RTL8201CP_PHYID1; >> > + case MII_PHYID2: >> > + return RTL8201CP_PHYID2; >> > + case MII_ANAR: >> > + return mii->anar; >> > + case MII_ANLPAR: >> > + return mii->anlpar; >> > + case MII_ANER: >> > + case MII_NSR: >> > + case MII_LBREMR: >> > + case MII_REC: >> > + case MII_SNRDR: >> > + case MII_TEST: >> > + qemu_log_mask(LOG_UNIMP, >> > + "allwinner_emac: read from unimpl. mii reg >> > 0x%x\n", >> > + reg); >> > + return 0; >> > + default: >> > + qemu_log_mask(LOG_GUEST_ERROR, >> > + "allwinner_emac: read from invalid mii reg >> > 0x%x\n", >> > + reg); >> > + return 0; >> > + } >> > + } >> > + return ret; >> > +} >> > + >> > +static void aw_emac_mdio_write(AwEmacState *s, uint8_t addr, uint8_t reg, >> > + uint16_t value) >> > +{ >> > + RTL8201CPState *mii = &s->mii; >> > + NetClientState *nc; >> > + >> > + if (addr == s->phy_addr) { >> > + switch (reg) { >> > + case MII_BMCR: >> > + if (value & MII_BMCR_RESET) { >> > + nc = qemu_get_queue(s->nic); >> > + mii_reset(mii, !nc->link_down); >> > + } else { >> > + mii->bmcr = value; >> > + } >> > + break; >> > + case MII_ANAR: >> > + mii->anar = value; >> > + break; >> > + case MII_BMSR: >> > + case MII_PHYID1: >> > + case MII_PHYID2: >> > + case MII_ANLPAR: >> > + case MII_ANER: >> > + qemu_log_mask(LOG_GUEST_ERROR, >> > + "allwinner_emac: write to read-only mii reg >> > 0x%x\n", >> > + reg); >> > + break; >> >> Theres plenty of precedent for not bothering making the distinction >> between read_only and out of range if you just want to collapse into >> the one GUEST_ERROR message. Either way though. >> >> > + case MII_NSR: >> > + case MII_LBREMR: >> > + case MII_REC: >> > + case MII_SNRDR: >> > + case MII_TEST: >> > + qemu_log_mask(LOG_UNIMP, >> > + "allwinner_emac: write to unimpl. mii reg >> > 0x%x\n", >> > + reg); >> > + break; >> > + default: >> > + qemu_log_mask(LOG_GUEST_ERROR, >> > + "allwinner_emac: write to invalid mii reg >> > 0x%x\n", >> > + reg); >> > + } >> > + } >> > +} >> > + >> > +static void aw_emac_update_irq(AwEmacState *s) >> > +{ >> > + qemu_set_irq(s->irq, (s->int_sta & s->int_ctl) != 0); >> > +} >> > + >> > +static void aw_emac_tx_reset(AwEmacTxFifo *fifo) >> > +{ >> > + fifo->length = 0; >> > + fifo->offset = 0; >> > +} >> > + >> > +static int aw_emac_tx_has_space(AwEmacTxFifo *fifo, int n) >> > +{ >> > + return fifo->offset + n <= TX_FIFO_SIZE; >> > +} >> > + >> > +static void aw_emac_tx_pushw(AwEmacTxFifo *fifo, uint32_t val) >> > +{ >> > + assert(fifo->offset + 4 <= TX_FIFO_SIZE); >> > + fifo->data[fifo->offset++] = val; >> > + fifo->data[fifo->offset++] = val >> 8; >> > + fifo->data[fifo->offset++] = val >> 16; >> > + fifo->data[fifo->offset++] = val >> 24; >> > +} >> > + >> > +static void aw_emac_rx_reset(AwEmacRxFifo *fifo) >> > +{ >> > + fifo->head = 0; >> > + fifo->num = 0; >> > + fifo->num_packets = 0; >> > + fifo->packet_size = 0; >> > + fifo->packet_pos = 0; >> > +} >> > + >> > +static int aw_emac_rx_has_space(AwEmacRxFifo *fifo, int n) >> > +{ >> > + return fifo->num + n <= RX_FIFO_SIZE; >> > +} >> > + >> > +static void aw_emac_rx_push(AwEmacRxFifo *fifo, const uint8_t *data, int >> > size) >> >> This is pretty much exactly the proposed addition to the fifo API to >> solve the no-buffer-write incompletness. I'm happy to ACK a patch to >> that API with this implementation, especially if it means you can >> collapse use for the rx fifo here to save on the ring-buffer >> boilerplate. > > Should I base the patch on top of your proposed generalisation to > different element widths or on current master? >
Current master. You are much closer to a merge here, than I am with that one. Regards, Peter >> >> > +{ >> > + int index; >> > + >> > + assert(fifo->num + size <= RX_FIFO_SIZE); >> > + index = (fifo->head + fifo->num) % RX_FIFO_SIZE; >> > + >> > + if (index + size <= RX_FIFO_SIZE) { >> > + memcpy(&fifo->data[index], data, size); >> > + } else { >> > + memcpy(&fifo->data[index], data, RX_FIFO_SIZE - index); >> > + memcpy(&fifo->data[0], &data[RX_FIFO_SIZE - index], >> > + size - RX_FIFO_SIZE + index); >> > + } >> > + >> > + fifo->num += size; >> > +} >> > + >> > +static void aw_emac_rx_pushb(AwEmacRxFifo *fifo, uint8_t val) >> > +{ >> > + return aw_emac_rx_push(fifo, &val, 1); >> > +} >> > + >> > +static void aw_emac_rx_pushw(AwEmacRxFifo *fifo, uint32_t val) >> > +{ >> > + aw_emac_rx_pushb(fifo, val); >> > + aw_emac_rx_pushb(fifo, val >> 8); >> > + aw_emac_rx_pushb(fifo, val >> 16); >> > + aw_emac_rx_pushb(fifo, val >> 24); >> > +} >> > + >> > +static uint8_t aw_emac_rx_popb(AwEmacRxFifo *fifo) >> > +{ >> > + uint8_t ret; >> > + >> > + assert(fifo->num > 0); >> > + ret = fifo->data[fifo->head]; >> > + fifo->head = (fifo->head + 1) % RX_FIFO_SIZE; >> > + fifo->num--; >> > + >> > + return ret; >> > +} >> > + >> > +static uint32_t aw_emac_rx_popw(AwEmacRxFifo *fifo) >> > +{ >> > + uint32_t ret; >> > + >> > + ret = aw_emac_rx_popb(fifo); >> > + ret |= aw_emac_rx_popb(fifo) << 8; >> > + ret |= aw_emac_rx_popb(fifo) << 16; >> > + ret |= aw_emac_rx_popb(fifo) << 24; >> > + >> > + return ret; >> > +} >> > + >> > +static int aw_emac_can_receive(NetClientState *nc) >> > +{ >> > + AwEmacState *s = qemu_get_nic_opaque(nc); >> > + >> > + return s->ctl & EMAC_CTL_RX_EN; >> >> So I'm not a fan of ethernets that just drop all packets on a full >> fifo (they tend to have very poor tftp performance esp. when using >> u-boot as a guest). Since we completely fabricated the size of the rx >> fifo (due to no specs), you could just make a conservative assumption >> that you need a full frames-worth of space spare before you accept a >> packet of any size. There simple is no known or specified behaviour to >> preserve here WRT to fifo occupancy, so having some form of fifo-full >> awareness will at least give you good performance. > > Ok, I will add this check in next version. > > Beniamino >