From: Beniamino Galvani <b.galvani@gmail.com>
To: Peter Crosthwaite <peter.crosthwaite@xilinx.com>
Cc: Peter Maydell <peter.maydell@linaro.org>,
"qemu-devel@nongnu.org Developers" <qemu-devel@nongnu.org>,
Li Guang <lig.fnst@cn.fujitsu.com>
Subject: Re: [Qemu-devel] [PATCH 1/2] hw/net: add support for Allwinner EMAC Fast Ethernet controller
Date: Sat, 4 Jan 2014 10:36:23 +0100 [thread overview]
Message-ID: <20140104093622.GA4282@gmail.com> (raw)
In-Reply-To: <CAEgOgz64P7+-n_Ai_DvQTzn7QcJBugLkz79v5L7XBXpeKwe6JA@mail.gmail.com>
On Sat, Jan 04, 2014 at 10:56:13AM +1000, Peter Crosthwaite wrote:
> On Thu, Jan 2, 2014 at 7:18 PM, Beniamino Galvani <b.galvani@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.galvani@gmail.com>
> > ---
> > default-configs/arm-softmmu.mak | 1 +
> > hw/net/Makefile.objs | 1 +
> > hw/net/allwinner_emac.c | 466 +++++++++++++++++++++++++++++++++++++++
> > include/hw/net/allwinner_emac.h | 204 +++++++++++++++++
> > 4 files changed, 672 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..9791be4
> > --- /dev/null
> > +++ b/hw/net/allwinner_emac.c
> > @@ -0,0 +1,466 @@
> > +/*
> > + * Emulation of Allwinner EMAC Fast Ethernet controller and
> > + * Realtek RTL8201CP PHY
> > + *
> > + * Copyright (C) 2013 Beniamino Galvani <b.galvani@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>
> > +
> > +#undef AW_EMAC_DEBUG
> > +
> > +#ifdef AW_EMAC_DEBUG
> > +#define debug(...) \
> > + do { \
> > + fprintf(stderr, "allwinner_emac : %s: ", __func__); \
> > + fprintf(stderr, ## __VA_ARGS__); \
> > + } while (0)
> > +#else
> > +#define debug(...) do {} while (0)
> > +#endif
> > +
> > +static void mii_set_link(AwEmacMii *mii, bool link_ok)
> > +{
> > + if (link_ok) {
> > + mii->bmsr |= MII_BMSR_LINK_ST;
> > + mii->anlpar |= MII_ANAR_TXFD | MII_ANAR_TX | MII_ANAR_10FD |
> > + MII_ANAR_10 | MII_ANAR_CSMACD;
> > + } else {
> > + mii->bmsr &= ~MII_BMSR_LINK_ST;
> > + mii->anlpar = MII_ANAR_TX;
> > + }
> > + mii->link_ok = link_ok;
> > +}
> > +
> > +static void mii_reset(AwEmacMii *mii)
> > +{
> > + 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, mii->link_ok);
> > +}
> > +
> > +static uint16_t mii_read(AwEmacMii *mii, uint8_t phy_addr, uint8_t reg)
> > +{
> > + uint16_t ret = 0xffff;
> > +
> > + if (phy_addr == BOARD_PHY_ADDRESS) {
> > + switch (reg) {
> > + case MII_BMCR:
> > + ret = mii->bmcr;
> > + break;
> > + case MII_BMSR:
> > + ret = mii->bmsr;
> > + break;
> > + case MII_PHYID1:
> > + ret = RTL8201CP_PHYID1;
> > + break;
> > + case MII_PHYID2:
> > + ret = RTL8201CP_PHYID2;
> > + break;
> > + case MII_ANAR:
> > + ret = mii->anar;
> > + break;
> > + case MII_ANLPAR:
> > + ret = mii->anlpar;
> > + break;
> > + default:
> > + debug("unknown mii register %x\n", reg);
> > + ret = 0;
> > + }
> > + }
> > + return ret;
> > +}
> > +
> > +static void mii_write(AwEmacMii *mii, uint8_t phy_addr, uint8_t reg,
> > + uint16_t value)
> > +{
> > + if (phy_addr == BOARD_PHY_ADDRESS) {
> > + switch (reg) {
> > + case MII_BMCR:
> > + if (value & MII_BMCR_RESET) {
> > + mii_reset(mii);
> > + } else {
> > + mii->bmcr = value;
> > + }
> > + break;
> > + case MII_BMSR:
> > + case MII_PHYID1:
> > + case MII_PHYID2:
> > + case MII_ANLPAR:
> > + qemu_log_mask(LOG_GUEST_ERROR, "%s: write to mii register %x\n",
> > + __func__, reg);
> > + break;
> > + case MII_ANAR:
> > + mii->anar = value;
> > + break;
> > + default:
> > + debug("unknown mii register %x\n", reg);
> > + }
> > + }
> > +}
> > +
> > +static void aw_emac_update_irq(AwEmacState *s)
> > +{
> > + qemu_set_irq(s->irq, (s->int_sta & s->int_ctl) != 0);
> > +}
> > +
> > +static int aw_emac_can_receive(NetClientState *nc)
> > +{
> > + AwEmacState *s = qemu_get_nic_opaque(nc);
> > +
> > + return (s->ctl & EMAC_CTL_RX_EN) && (s->num_rx < MAX_RX);
> > +}
> > +
> > +static ssize_t aw_emac_receive(NetClientState *nc, const uint8_t *buf,
> > + size_t size)
> > +{
> > + AwEmacState *s = qemu_get_nic_opaque(nc);
> > + uint32_t *fifo;
> > + uint32_t crc;
> > + char *dest;
> > +
> > + if (s->num_rx >= MAX_RX) {
> > + debug("rx queue full - packed dropped\n");
> > + return -1;
> > + }
> > +
> > + if (size + RX_HDR_SIZE > FIFO_SIZE) {
> > + debug("packet too big\n");
> > + return -1;
> > + }
> > +
> > + fifo = s->rx_fifos[(s->first_rx + s->num_rx) % MAX_RX];
> > + dest = (char *)&fifo[2];
> > + s->num_rx++;
> > +
> > + memcpy(dest, buf, size);
> > +
> > + /* Fill to minimum frame length */
> > + if (size < 60) {
> > + memset(dest + size, 0, 60 - size);
> > + size = 60;
> > + }
> > +
> > + /* Append FCS */
> > + crc = crc32(~0, buf, size);
> > + memcpy(dest + size, &crc, 4);
> > +
> > + fifo[0] = EMAC_UNDOCUMENTED_MAGIC;
> > + fifo[1] = EMAC_RX_HEADER(size + 4, EMAC_RX_IO_DATA_STATUS_OK);
> > +
> > + /* Set rx interrupt flag */
> > + s->int_sta |= EMAC_INT_RX;
> > + aw_emac_update_irq(s);
> > +
> > + return size;
> > +}
> > +
> > +static void aw_emac_cleanup(NetClientState *nc)
> > +{
> > + AwEmacState *s = qemu_get_nic_opaque(nc);
> > +
> > + s->nic = NULL;
> > +}
> > +
> > +static void aw_emac_reset(AwEmacState *s)
> > +{
> > + s->ctl = 0;
> > + s->tx_mode = 0;
> > + s->int_ctl = 0;
> > + s->int_sta = 0;
> > + s->tx_channel = 0;
> > + s->first_rx = 0;
> > + s->num_rx = 0;
> > + s->rx_offset = 0;
> > + s->phy_target = 0;
> > +}
> > +
> > +static uint64_t aw_emac_read(void *opaque, hwaddr offset, unsigned size)
> > +{
> > + AwEmacState *s = opaque;
> > + uint64_t ret;
> > + uint32_t *rx_fifo;
> > +
> > + switch (offset) {
> > + case EMAC_CTL_REG:
> > + ret = s->ctl;
> > + break;
> > + case EMAC_TX_MODE_REG:
> > + ret = s->tx_mode;
> > + break;
> > + case EMAC_TX_INS_REG:
> > + ret = s->tx_channel;
> > + break;
> > + case EMAC_RX_CTL_REG:
> > + ret = s->rx_ctl;
> > + break;
> > + case EMAC_RX_IO_DATA_REG:
> > + if (!s->num_rx) {
> > + ret = 0;
> > + break;
> > + }
> > + rx_fifo = s->rx_fifos[s->first_rx];
> > +
> > + if (s->rx_offset >= FIFO_SIZE ||
> > + s->rx_offset >= EMAC_RX_IO_DATA_LEN(rx_fifo[1]) + RX_HDR_SIZE) {
> > + /* This should never happen */
> > + debug("RX fifo overrun\n");
> > + s->first_rx = (s->first_rx + 1) % MAX_RX;
> > + s->num_rx--;
> > + s->rx_offset = 0;
> > + ret = 0;
> > + break;
> > + }
> > +
> > + ret = rx_fifo[s->rx_offset >> 2];
> > + s->rx_offset += 4;
> > +
> > + if (s->rx_offset >= EMAC_RX_IO_DATA_LEN(rx_fifo[1]) + RX_HDR_SIZE) {
> > + s->first_rx = (s->first_rx + 1) % MAX_RX;
> > + s->num_rx--;
> > + s->rx_offset = 0;
> > + }
> > + break;
> > + case EMAC_RX_FBC_REG:
> > + ret = s->num_rx;
> > + break;
> > + case EMAC_INT_CTL_REG:
> > + ret = s->int_ctl;
> > + break;
> > + case EMAC_INT_STA_REG:
> > + ret = s->int_sta;
> > + break;
> > + case EMAC_MAC_MRDD_REG:
> > + ret = mii_read(&s->mii, PHY_TARGET_ADDR(s->phy_target),
> > + PHY_TARGET_REG(s->phy_target));
> > + break;
> > + default:
> > + debug("unknown offset %08x\n", (unsigned int)offset);
> > + ret = 0;
> > + }
> > +
> > + return ret;
> > +}
> > +
> > +static void aw_emac_write(void *opaque, hwaddr offset, uint64_t value,
> > + unsigned size)
> > +{
> > + AwEmacState *s = opaque;
> > + AwEmacTxFifo *fifo;
> > + int chan;
> > +
> > + switch (offset) {
> > + case EMAC_CTL_REG:
> > + if (value & EMAC_CTL_RESET) {
> > + debug("reset\n");
> > + aw_emac_reset(s);
> > + value &= ~EMAC_CTL_RESET;
> > + }
> > + s->ctl = value;
> > + break;
> > + case EMAC_TX_MODE_REG:
> > + s->tx_mode = value;
> > + break;
> > + case EMAC_TX_CTL0_REG:
> > + case EMAC_TX_CTL1_REG:
> > + chan = (offset == EMAC_TX_CTL0_REG ? 0 : 1);
> > + if ((value & 1) && (s->ctl & EMAC_CTL_TX_EN)) {
> > + qemu_send_packet(qemu_get_queue(s->nic),
> > + (uint8_t *)s->tx_fifos[chan].data,
>
> So this cast here is perhaps suspicious. Your write handler just loads
> uint32_t values from the bus access into s->tx_fifos[chan] without any
> endianness checking. This means that s->tx_fifos[chan].data is a
> collection of uint32_t's of host endianness.
>
> Ultimately, the net layer wants a sequence of bytes (uint8_t *) while
> the bus access logic is thinking in (uint32_t *). I would suggest
> going lowest common denominitor with uint8_t * as your FIFO data type.
> Implement your endianess check in the bus access.
The same problem is present also in the RX code; I will change both to
use a uint8_t array with endianness conversion in read/write handlers.
> > + s->tx_fifos[chan].length);
> > +
> > + /* Raise TX interrupt */
> > + s->int_sta |= EMAC_INT_TX_CHAN(chan);
> > + s->tx_fifos[chan].offset = 0;
> > + aw_emac_update_irq(s);
> > + }
> > + break;
> > + case EMAC_TX_INS_REG:
> > + s->tx_channel = value < NUM_CHAN ? value : 0;
> > + break;
> > + case EMAC_TX_PL0_REG:
> > + case EMAC_TX_PL1_REG:
> > + chan = (offset == EMAC_TX_PL0_REG ? 0 : 1);
> > + if (value > FIFO_SIZE) {
> > + debug("invalid TX frame length %d\n", (int)value);
> > + value = FIFO_SIZE;
> > + }
> > + s->tx_fifos[chan].length = value;
> > + break;
> > + case EMAC_TX_IO_DATA_REG:
> > + fifo = &s->tx_fifos[s->tx_channel];
> > + if (fifo->offset >= FIFO_SIZE) {
> > + debug("TX frame data overruns fifo (%d)\n", fifo->offset);
> > + break;
> > + }
> > + fifo->data[fifo->offset >> 2] = value;
>
> This is where the endianess issue occurs. Also, if you convert to
> uint8_t * you can ditch on the shift. Theres probably some neat way to
> do this using cpu_to_le32 (or_be, depending on what the device is) to
> avoid a 4 iteration byte loop.
Ok.
>
> > + fifo->offset += 4;
> > + break;
> > + case EMAC_RX_CTL_REG:
> > + s->rx_ctl = value;
> > + break;
> > + case EMAC_RX_FBC_REG:
> > + if (value == 0) {
> > + s->num_rx = 0;
> > + }
> > + break;
> > + case EMAC_INT_CTL_REG:
> > + s->int_ctl = value;
> > + break;
> > + case EMAC_INT_STA_REG:
> > + s->int_sta &= ~value;
> > + break;
> > + case EMAC_MAC_MADR_REG:
> > + s->phy_target = value;
> > + break;
> > + case EMAC_MAC_MWTD_REG:
> > + mii_write(&s->mii, PHY_TARGET_ADDR(s->phy_target),
> > + PHY_TARGET_REG(s->phy_target), value);
> > + break;
> > + default:
> > + debug("unknown offset %08x\n", (unsigned int)offset);
> > + }
> > +}
> > +
> > +static void aw_emac_set_link(NetClientState *nc)
> > +{
> > + AwEmacState *s = qemu_get_nic_opaque(nc);
> > +
> > + mii_set_link(&s->mii, !nc->link_down);
> > +}
> > +
> > +static const MemoryRegionOps aw_emac_mem_ops = {
> > + .read = aw_emac_read,
> > + .write = aw_emac_write,
> > + .endianness = DEVICE_NATIVE_ENDIAN,
> > +};
> > +
> > +static NetClientInfo net_aw_emac_info = {
> > + .type = NET_CLIENT_OPTIONS_KIND_NIC,
> > + .size = sizeof(NICState),
> > + .can_receive = aw_emac_can_receive,
> > + .receive = aw_emac_receive,
> > + .cleanup = aw_emac_cleanup,
> > + .link_status_changed = aw_emac_set_link,
> > +};
> > +
> > +static void aw_emac_init(Object *obj)
> > +{
> > + SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
> > + AwEmacState *s = AW_EMAC(obj);
> > +
> > + memory_region_init_io(&s->iomem, OBJECT(s), &aw_emac_mem_ops, s,
> > + "aw_emac", 0x1000);
> > + sysbus_init_mmio(sbd, &s->iomem);
> > + sysbus_init_irq(sbd, &s->irq);
> > +}
> > +
> > +static void aw_emac_realize(DeviceState *dev, Error **errp)
> > +{
> > + AwEmacState *s = AW_EMAC(dev);
> > +
> > + qemu_macaddr_default_if_unset(&s->conf.macaddr);
> > +
> > + s->nic = qemu_new_nic(&net_aw_emac_info, &s->conf,
> > + object_get_typename(OBJECT(dev)), dev->id, s);
> > + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
> > +
> > + aw_emac_reset(s);
> > + aw_emac_set_link(qemu_get_queue(s->nic));
> > + mii_reset(&s->mii);
> > +}
> > +
> > +static Property aw_emac_properties[] = {
> > + DEFINE_NIC_PROPERTIES(AwEmacState, conf),
> > + DEFINE_PROP_END_OF_LIST(),
> > +};
> > +
> > +static const VMStateDescription vmstate_mii = {
> > + .name = "allwinner_emac_mii",
> > + .version_id = 1,
> > + .minimum_version_id = 1,
> > + .fields = (VMStateField[]) {
> > + VMSTATE_UINT16(bmcr, AwEmacMii),
> > + VMSTATE_UINT16(bmsr, AwEmacMii),
> > + VMSTATE_UINT16(anar, AwEmacMii),
> > + VMSTATE_UINT16(anlpar, AwEmacMii),
> > + VMSTATE_BOOL(link_ok, AwEmacMii),
> > + VMSTATE_END_OF_LIST()
> > + }
> > +};
> > +
> > +static const VMStateDescription vmstate_tx_fifo = {
> > + .name = "allwinner_emac_tx_fifo",
> > + .version_id = 1,
> > + .minimum_version_id = 1,
> > + .fields = (VMStateField[]) {
> > + VMSTATE_UINT32_ARRAY(data, AwEmacTxFifo, FIFO_SIZE >> 2),
> > + VMSTATE_INT32(length, AwEmacTxFifo),
> > + VMSTATE_INT32(offset, AwEmacTxFifo),
> > + VMSTATE_END_OF_LIST()
> > + }
> > +};
> > +
> > +static const VMStateDescription vmstate_aw_emac = {
> > + .name = "allwinner_emac",
> > + .version_id = 1,
> > + .minimum_version_id = 1,
> > + .fields = (VMStateField[]) {
> > + VMSTATE_STRUCT(mii, AwEmacState, 1, vmstate_mii, AwEmacMii),
> > + VMSTATE_UINT32(ctl, AwEmacState),
> > + VMSTATE_UINT32(tx_mode, AwEmacState),
> > + VMSTATE_UINT32(rx_ctl, AwEmacState),
> > + VMSTATE_UINT32(int_ctl, AwEmacState),
> > + VMSTATE_UINT32(int_sta, AwEmacState),
> > + VMSTATE_UINT32(phy_target, AwEmacState),
> > + VMSTATE_INT32(tx_channel, AwEmacState),
> > + VMSTATE_STRUCT_ARRAY(tx_fifos, AwEmacState,
> > + NUM_CHAN, 1, vmstate_tx_fifo, AwEmacTxFifo),
> > + VMSTATE_BUFFER_UNSAFE(tx_fifos, AwEmacState, 1, MAX_RX * FIFO_SIZE),
> > + VMSTATE_INT32(first_rx, AwEmacState),
> > + VMSTATE_INT32(num_rx, AwEmacState),
> > + VMSTATE_INT32(rx_offset, AwEmacState),
> > + VMSTATE_END_OF_LIST()
> > + }
> > +};
> > +
> > +static void aw_emac_class_init(ObjectClass *klass, void *data)
> > +{
> > + DeviceClass *dc = DEVICE_CLASS(klass);
> > +
> > + dc->realize = aw_emac_realize;
> > + dc->props = aw_emac_properties;
> > + dc->vmsd = &vmstate_aw_emac;
> > +}
> > +
> > +static const TypeInfo aw_emac_info = {
> > + .name = TYPE_AW_EMAC,
> > + .parent = TYPE_SYS_BUS_DEVICE,
> > + .instance_size = sizeof(AwEmacState),
> > + .instance_init = aw_emac_init,
> > + .class_init = aw_emac_class_init,
> > +};
> > +
> > +static void aw_emac_register_types(void)
> > +{
> > + type_register_static(&aw_emac_info);
> > +}
> > +
> > +type_init(aw_emac_register_types)
> > diff --git a/include/hw/net/allwinner_emac.h b/include/hw/net/allwinner_emac.h
> > new file mode 100644
> > index 0000000..f9f9682
> > --- /dev/null
> > +++ b/include/hw/net/allwinner_emac.h
> > @@ -0,0 +1,204 @@
> > +/*
> > + * Emulation of Allwinner EMAC Fast Ethernet controller and
> > + * Realtek RTL8201CP PHY
> > + *
> > + * Copyright (C) 2013 Beniamino Galvani <b.galvani@gmail.com>
> > + *
> > + * Allwinner EMAC register definitions from Linux kernel are:
> > + * Copyright 2012 Stefan Roese <sr@denx.de>
> > + * Copyright 2013 Maxime Ripard <maxime.ripard@free-electrons.com>
> > + * Copyright 1997 Sten Wang
> > + *
> > + * 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.
> > + *
> > + */
> > +#ifndef AW_EMAC_H
> > +#define AW_EMAC_H
> > +
> > +#include "net/net.h"
> > +
> > +#define TYPE_AW_EMAC "allwinner_emac"
> > +#define AW_EMAC(obj) OBJECT_CHECK(AwEmacState, (obj), TYPE_AW_EMAC)
> > +
> > +/*
> > + * Allwinner EMAC register list
> > + */
> > +#define EMAC_CTL_REG 0x00
> > +#define EMAC_TX_MODE_REG 0x04
> > +#define EMAC_TX_FLOW_REG 0x08
> > +#define EMAC_TX_CTL0_REG 0x0C
> > +#define EMAC_TX_CTL1_REG 0x10
> > +#define EMAC_TX_INS_REG 0x14
> > +#define EMAC_TX_PL0_REG 0x18
> > +#define EMAC_TX_PL1_REG 0x1C
> > +#define EMAC_TX_STA_REG 0x20
> > +#define EMAC_TX_IO_DATA_REG 0x24
> > +#define EMAC_TX_IO_DATA1_REG 0x28
> > +#define EMAC_TX_TSVL0_REG 0x2C
> > +#define EMAC_TX_TSVH0_REG 0x30
> > +#define EMAC_TX_TSVL1_REG 0x34
> > +#define EMAC_TX_TSVH1_REG 0x38
> > +#define EMAC_RX_CTL_REG 0x3C
> > +#define EMAC_RX_HASH0_REG 0x40
> > +#define EMAC_RX_HASH1_REG 0x44
> > +#define EMAC_RX_STA_REG 0x48
> > +#define EMAC_RX_IO_DATA_REG 0x4C
> > +#define EMAC_RX_FBC_REG 0x50
> > +#define EMAC_INT_CTL_REG 0x54
> > +#define EMAC_INT_STA_REG 0x58
> > +#define EMAC_MAC_CTL0_REG 0x5C
> > +#define EMAC_MAC_CTL1_REG 0x60
> > +#define EMAC_MAC_IPGT_REG 0x64
> > +#define EMAC_MAC_IPGR_REG 0x68
> > +#define EMAC_MAC_CLRT_REG 0x6C
> > +#define EMAC_MAC_MAXF_REG 0x70
> > +#define EMAC_MAC_SUPP_REG 0x74
> > +#define EMAC_MAC_TEST_REG 0x78
> > +#define EMAC_MAC_MCFG_REG 0x7C
> > +#define EMAC_MAC_MCMD_REG 0x80
> > +#define EMAC_MAC_MADR_REG 0x84
> > +#define EMAC_MAC_MWTD_REG 0x88
> > +#define EMAC_MAC_MRDD_REG 0x8C
> > +#define EMAC_MAC_MIND_REG 0x90
> > +#define EMAC_MAC_SSRR_REG 0x94
> > +#define EMAC_MAC_A0_REG 0x98
> > +#define EMAC_MAC_A1_REG 0x9C
> > +#define EMAC_MAC_A2_REG 0xA0
> > +#define EMAC_SAFX_L_REG0 0xA4
> > +#define EMAC_SAFX_H_REG0 0xA8
> > +#define EMAC_SAFX_L_REG1 0xAC
> > +#define EMAC_SAFX_H_REG1 0xB0
> > +#define EMAC_SAFX_L_REG2 0xB4
> > +#define EMAC_SAFX_H_REG2 0xB8
> > +#define EMAC_SAFX_L_REG3 0xBC
> > +#define EMAC_SAFX_H_REG3 0xC0
> > +
> > +/* CTL register fields */
> > +#define EMAC_CTL_RESET (1 << 0)
> > +#define EMAC_CTL_TX_EN (1 << 1)
> > +#define EMAC_CTL_RX_EN (1 << 2)
> > +
> > +/* TX MODE register fields */
> > +#define EMAC_TX_MODE_ABORTED_FRAME_EN (1 << 0)
> > +#define EMAC_TX_MODE_DMA_EN (1 << 1)
> > +
> > +/* RX CTL register fields */
> > +#define EMAC_RX_CTL_AUTO_DRQ_EN (1 << 1)
> > +#define EMAC_RX_CTL_DMA_EN (1 << 2)
> > +#define EMAC_RX_CTL_PASS_ALL_EN (1 << 4)
> > +#define EMAC_RX_CTL_PASS_CTL_EN (1 << 5)
> > +#define EMAC_RX_CTL_PASS_CRC_ERR_EN (1 << 6)
> > +#define EMAC_RX_CTL_PASS_LEN_ERR_EN (1 << 7)
> > +#define EMAC_RX_CTL_PASS_LEN_OOR_EN (1 << 8)
> > +#define EMAC_RX_CTL_ACCEPT_UNICAST_EN (1 << 16)
> > +#define EMAC_RX_CTL_DA_FILTER_EN (1 << 17)
> > +#define EMAC_RX_CTL_ACCEPT_MULTICAST_EN (1 << 20)
> > +#define EMAC_RX_CTL_HASH_FILTER_EN (1 << 21)
> > +#define EMAC_RX_CTL_ACCEPT_BROADCAST_EN (1 << 22)
> > +#define EMAC_RX_CTL_SA_FILTER_EN (1 << 24)
> > +#define EMAC_RX_CTL_SA_FILTER_INVERT_EN (1 << 25)
> > +
> > +/* RX IO DATA register fields */
> > +#define EMAC_RX_IO_DATA_LEN(x) (x & 0xffff)
> > +#define EMAC_RX_IO_DATA_STATUS(x) ((x >> 16) & 0xffff)
> > +#define EMAC_RX_HEADER(len, status) (((len) & 0xffff) | ((status) << 16))
> > +#define EMAC_RX_IO_DATA_STATUS_CRC_ERR (1 << 4)
> > +#define EMAC_RX_IO_DATA_STATUS_LEN_ERR (3 << 5)
> > +#define EMAC_RX_IO_DATA_STATUS_OK (1 << 7)
> > +#define EMAC_UNDOCUMENTED_MAGIC 0x0143414d /* header for RX frames */
> > +
> > +/* PHY registers */
> > +#define MII_BMCR 0
> > +#define MII_BMSR 1
> > +#define MII_PHYID1 2
> > +#define MII_PHYID2 3
> > +#define MII_ANAR 4
> > +#define MII_ANLPAR 5
> > +
> > +/* PHY registers fields */
> > +#define MII_BMCR_RESET (1 << 15)
> > +#define MII_BMCR_LOOPBACK (1 << 14)
> > +#define MII_BMCR_SPEED (1 << 13)
> > +#define MII_BMCR_AUTOEN (1 << 12)
> > +#define MII_BMCR_FD (1 << 8)
> > +
> > +#define MII_BMSR_100TX_FD (1 << 14)
> > +#define MII_BMSR_100TX_HD (1 << 13)
> > +#define MII_BMSR_10T_FD (1 << 12)
> > +#define MII_BMSR_10T_HD (1 << 11)
> > +#define MII_BMSR_MFPS (1 << 6)
> > +#define MII_BMSR_AUTONEG (1 << 3)
> > +#define MII_BMSR_LINK_ST (1 << 2)
> > +
> > +#define MII_ANAR_TXFD (1 << 8)
> > +#define MII_ANAR_TX (1 << 7)
> > +#define MII_ANAR_10FD (1 << 6)
> > +#define MII_ANAR_10 (1 << 5)
> > +#define MII_ANAR_CSMACD (1 << 0)
> > +
> > +#define RTL8201CP_PHYID1 0x0000
> > +#define RTL8201CP_PHYID2 0x8201
> > +
> > +/* INT CTL and INT STA registers fields */
> > +#define EMAC_INT_TX_CHAN(x) (1 << (x))
> > +#define EMAC_INT_RX (1 << 8)
> > +
> > +#define BOARD_PHY_ADDRESS 1
> > +
> > +#define NUM_CHAN 2
> > +#define FIFO_SIZE 2048
> > +#define MAX_RX 12
> > +#define RX_HDR_SIZE 8
> > +
> > +#define PHY_TARGET_ADDR(x) (((x) >> 8) & 0xff)
> > +#define PHY_TARGET_REG(x) ((x) & 0xff)
> > +
> > +typedef struct AwEmacTxFifo {
> > + uint32_t data[FIFO_SIZE >> 2];
>
> uint8_t data[FIFO_SIZE];
>
>
> > + int length;
> > + int offset;
> > +} AwEmacTxFifo;
> > +
> > +typedef struct AwEmacMii {
> > + uint16_t bmcr;
> > + uint16_t bmsr;
> > + uint16_t anar;
> > + uint16_t anlpar;
> > + bool link_ok;
> > +} AwEmacMii;
> > +
> > +typedef struct AwEmacState {
> > + /*< private >*/
> > + SysBusDevice parent_obj;
> > + /*< public >*/
> > +
> > + MemoryRegion iomem;
> > + qemu_irq irq;
> > + NICState *nic;
> > + NICConf conf;
> > + AwEmacMii mii;
> > +
> > + uint32_t ctl;
> > + uint32_t tx_mode;
> > + uint32_t rx_ctl;
> > + uint32_t int_ctl;
> > + uint32_t int_sta;
> > + uint32_t phy_target;
> > +
> > + int tx_channel; /* Currently selected TX channel */
> > + AwEmacTxFifo tx_fifos[NUM_CHAN];
> > + uint32_t rx_fifos[MAX_RX][FIFO_SIZE >> 2];
> > + int first_rx; /* First packet in the RX ring */
> > + int num_rx; /* Number of packets in RX ring */
> > + int rx_offset; /* Offset of next read */
> > +} AwEmacState;
> > +
> > +#endif
> > --
> > 1.7.10.4
> >
>
> I'll look into your direct buffer access problem hopefully soon, and
> see what can be done to make Fifo8 DMA (for want of a better term)
> friendly. I'm thinking two functions:
>
> /** pop a number of elements from the FIFO up to a maximum of max. The
> buffer containing the popped data is returned. This buffer points
> directly into the Fifo backing store and data is invalidated once any
> of the fifo_* APIs are called on the FIFO. May short return, the
> number of valid bytes returned in populated in *num. Will always
> return at least 1 byte. Behavior is undefined if max > the number of
> bytes in the Fifo or max == 0.
> */
>
> const uint8_t *fifo_pop_buf(Fifo *fifo, uint32_t max, uint32_t *num);
>
> /** Pop num bytes from the Fifo into a newly created buffer. Behavior
> is undefined if num > the number of bytes in the Fifo. The caller is
> responsible for freeing the returned buffer via g_free. */
>
> uint8_t *fifo_pop_all(Fifo *fifo, uint32_t num);
>
> fifo_push is much simpler. Just as simple as:
>
> void fifo_push_all(Fifo *fifo, uint32_t num)
>
> Would this handle your cases without need for roll-your-own Fifo's?
Yes, the functions cover those cases.
Thanks,
Beniamino
next prev parent reply other threads:[~2014-01-04 9:37 UTC|newest]
Thread overview: 26+ messages / expand[flat|nested] mbox.gz Atom feed top
2014-01-02 9:18 [Qemu-devel] [PATCH 0/2] hw/arm: add ethernet support to Allwinner A10 Beniamino Galvani
2014-01-02 9:18 ` [Qemu-devel] [PATCH 1/2] hw/net: add support for Allwinner EMAC Fast Ethernet controller Beniamino Galvani
2014-01-02 10:25 ` Peter Crosthwaite
2014-01-02 14:58 ` Beniamino Galvani
2014-01-03 1:26 ` Peter Crosthwaite
2014-01-03 17:42 ` Beniamino Galvani
2014-01-06 3:27 ` Stefan Hajnoczi
2014-01-06 3:46 ` Peter Crosthwaite
2014-01-06 6:12 ` Stefan Hajnoczi
2014-01-10 21:48 ` Beniamino Galvani
2014-01-10 22:16 ` Peter Crosthwaite
2014-01-10 22:48 ` Peter Crosthwaite
2014-01-12 13:59 ` Edgar E. Iglesias
2014-01-12 22:00 ` Peter Crosthwaite
2014-01-13 4:00 ` Stefan Hajnoczi
2014-01-04 0:56 ` Peter Crosthwaite
2014-01-04 9:36 ` Beniamino Galvani [this message]
2014-01-02 9:18 ` [Qemu-devel] [PATCH 2/2] hw/arm/allwinner-a10: initialize EMAC Beniamino Galvani
2014-01-02 10:20 ` Peter Crosthwaite
2014-01-02 10:21 ` Peter Crosthwaite
2014-01-02 17:19 ` Beniamino Galvani
2014-01-02 22:32 ` Peter Crosthwaite
2014-01-06 0:49 ` Li Guang
2014-01-06 13:56 ` Beniamino Galvani
2014-01-08 7:27 ` Li Guang
2014-01-08 8:14 ` Peter Crosthwaite
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20140104093622.GA4282@gmail.com \
--to=b.galvani@gmail.com \
--cc=lig.fnst@cn.fujitsu.com \
--cc=peter.crosthwaite@xilinx.com \
--cc=peter.maydell@linaro.org \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.