qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [PATCH v3 1/2] hw/net: add support for Allwinner EMAC F


From: Peter Crosthwaite
Subject: Re: [Qemu-devel] [PATCH v3 1/2] hw/net: add support for Allwinner EMAC Fast Ethernet controller
Date: Sun, 26 Jan 2014 09:58:03 +1000

On Sat, Jan 25, 2014 at 11:37 PM, Beniamino Galvani <address@hidden> 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 <address@hidden> 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 <address@hidden>
>> > ---
>> >  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 <address@hidden>
>> > + *
>> > + * 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
>



reply via email to

[Prev in Thread] Current Thread [Next in Thread]