* [Qemu-devel] [PATCH v3 0/2] hw/arm: add ethernet support to Allwinner A10 @ 2014-01-19 23:25 Beniamino Galvani 2014-01-19 23:25 ` [Qemu-devel] [PATCH v3 1/2] hw/net: add support for Allwinner EMAC Fast Ethernet controller Beniamino Galvani 2014-01-19 23:25 ` [Qemu-devel] [PATCH v3 2/2] hw/arm/allwinner-a10: initialize EMAC Beniamino Galvani 0 siblings, 2 replies; 9+ messages in thread From: Beniamino Galvani @ 2014-01-19 23:25 UTC (permalink / raw) To: qemu-devel Cc: Beniamino Galvani, Peter Maydell, Peter Crosthwaite, Li Guang, Stefan Hajnoczi This patch series adds support for the EMAC Fast Ethernet controller found on Allwinner SoCs to the Allwinner A10. The main change in this version is the use of a single rx fifo instead of multiple per-packet fifos. I added functions to manipulate the rx fifo and, for consistency, also functions to manipulate tx fifo. Another difference from v2 is that the function can_receive() now can't really know if the next packet can be stored in the fifo because with a single fifo this depends on the size of packet. So the functions only checks if rx is enabled; if the packet can't be stored it will be dropped in receive(). Changelog: v3: Address comments from Peter Crosthwaite and Stefan Hajnoczi: * Use a single, big rx fifo instead of per-packet fifo * Remove link_ok field from PHY state * Call set_link() on post_load * Add missing PHY registers * Implement reset() method * Rename AwEmacMii -> RTL8201CPState * Rename mii_{read,write} -> aw_emac_mdio_{read,write} v2: Address Peter Crosthwaite's comments: * Make phy address customizable through a property * Call qemu_flush_queued_packets() when rx becomes possible * Always create EMAC instance in SoC * Use uint8 arrays for fifos * Minor cleanups Beniamino Galvani (2): hw/net: add support for Allwinner EMAC Fast Ethernet controller hw/arm/allwinner-a10: initialize EMAC default-configs/arm-softmmu.mak | 1 + hw/arm/allwinner-a10.c | 16 ++ hw/arm/cubieboard.c | 7 + hw/net/Makefile.objs | 1 + hw/net/allwinner_emac.c | 589 +++++++++++++++++++++++++++++++++++++++ include/hw/arm/allwinner-a10.h | 3 + include/hw/net/allwinner_emac.h | 222 +++++++++++++++ 7 files changed, 839 insertions(+) create mode 100644 hw/net/allwinner_emac.c create mode 100644 include/hw/net/allwinner_emac.h -- 1.7.10.4 ^ permalink raw reply [flat|nested] 9+ messages in thread
* [Qemu-devel] [PATCH v3 1/2] hw/net: add support for Allwinner EMAC Fast Ethernet controller 2014-01-19 23:25 [Qemu-devel] [PATCH v3 0/2] hw/arm: add ethernet support to Allwinner A10 Beniamino Galvani @ 2014-01-19 23:25 ` Beniamino Galvani 2014-01-23 13:04 ` Peter Crosthwaite 2014-01-19 23:25 ` [Qemu-devel] [PATCH v3 2/2] hw/arm/allwinner-a10: initialize EMAC Beniamino Galvani 1 sibling, 1 reply; 9+ messages in thread From: Beniamino Galvani @ 2014-01-19 23:25 UTC (permalink / raw) To: qemu-devel Cc: Beniamino Galvani, Peter Maydell, Peter Crosthwaite, Li Guang, Stefan Hajnoczi 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 | 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.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> + +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) +{ + 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; + 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) +{ + 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; +} + +static ssize_t aw_emac_receive(NetClientState *nc, const uint8_t *buf, + size_t size) +{ + AwEmacState *s = qemu_get_nic_opaque(nc); + AwEmacRxFifo *fifo = &s->rx_fifo; + size_t padded_size, total_size; + uint32_t crc; + + padded_size = size > 60 ? size : 60; + total_size = QEMU_ALIGN_UP(RX_HDR_SIZE + padded_size + CRC_SIZE, 4); + + if (!(s->ctl & EMAC_CTL_RX_EN) || !aw_emac_rx_has_space(fifo, total_size)) { + return -1; + } + + aw_emac_rx_pushw(fifo, EMAC_UNDOCUMENTED_MAGIC); + aw_emac_rx_pushw(fifo, EMAC_RX_HEADER(padded_size + CRC_SIZE, + EMAC_RX_IO_DATA_STATUS_OK)); + aw_emac_rx_push(fifo, buf, size); + crc = crc32(~0, buf, size); + + if (padded_size != size) { + aw_emac_rx_push(fifo, padding, padded_size - size); + crc = crc32(crc, padding, padded_size - size); + } + + aw_emac_rx_pushw(fifo, crc); + aw_emac_rx_push(fifo, padding, QEMU_ALIGN_UP(padded_size, 4) - padded_size); + s->rx_fifo.num_packets++; + + 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(DeviceState *dev) +{ + AwEmacState *s = AW_EMAC(dev); + NetClientState *nc = qemu_get_queue(s->nic); + + s->ctl = 0; + s->tx_mode = 0; + s->int_ctl = 0; + s->int_sta = 0; + s->tx_channel = 0; + s->phy_target = 0; + + aw_emac_tx_reset(&s->tx_fifo[0]); + aw_emac_tx_reset(&s->tx_fifo[1]); + aw_emac_rx_reset(&s->rx_fifo); + + mii_reset(&s->mii, !nc->link_down); +} + +static uint64_t aw_emac_read(void *opaque, hwaddr offset, unsigned size) +{ + AwEmacState *s = opaque; + AwEmacRxFifo *fifo = &s->rx_fifo; + uint64_t ret; + + switch (offset) { + case EMAC_CTL_REG: + return s->ctl; + case EMAC_TX_MODE_REG: + return s->tx_mode; + case EMAC_TX_INS_REG: + return s->tx_channel; + case EMAC_RX_CTL_REG: + return s->rx_ctl; + case EMAC_RX_IO_DATA_REG: + if (!fifo->num_packets) { + qemu_log_mask(LOG_GUEST_ERROR, + "Read IO data register when no packet available"); + return 0; + } + + ret = aw_emac_rx_popw(fifo); + + switch (fifo->packet_pos) { + case 0: /* Word is magic header */ + fifo->packet_pos += 4; + break; + case 4: /* Word is rx info header */ + fifo->packet_pos += 4; + fifo->packet_size = QEMU_ALIGN_UP(extract32(ret, 0, 16), 4); + break; + default: /* Word is packet data */ + fifo->packet_pos += 4; + fifo->packet_size -= 4; + + if (!fifo->packet_size) { + fifo->packet_pos = 0; + fifo->num_packets--; + } + } + return ret; + case EMAC_RX_FBC_REG: + return s->rx_fifo.num_packets; + case EMAC_INT_CTL_REG: + return s->int_ctl; + case EMAC_INT_STA_REG: + return s->int_sta; + case EMAC_MAC_MRDD_REG: + return aw_emac_mdio_read(s, extract32(s->phy_target, PHY_ADDR_SHIFT, 8), + extract32(s->phy_target, PHY_REG_SHIFT, 8)); + default: + qemu_log_mask(LOG_UNIMP, + "allwinner_emac: read access to unknown register 0x" + TARGET_FMT_plx "\n", offset); + ret = 0; + } + + return ret; +} + +static void aw_emac_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + AwEmacState *s = opaque; + AwEmacTxFifo *fifo; + NetClientState *nc = qemu_get_queue(s->nic); + int chan; + + switch (offset) { + case EMAC_CTL_REG: + if (value & EMAC_CTL_RESET) { + aw_emac_reset(DEVICE(s)); + value &= ~EMAC_CTL_RESET; + } + s->ctl = value; + if (aw_emac_can_receive(nc)) { + qemu_flush_queued_packets(nc); + } + 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(nc, s->tx_fifo[chan].data, + s->tx_fifo[chan].length); + /* Raise TX interrupt */ + s->int_sta |= EMAC_INT_TX_CHAN(chan); + s->tx_fifo[chan].offset = 0; + aw_emac_update_irq(s); + } + break; + case EMAC_TX_INS_REG: + s->tx_channel = value < NUM_TX_FIFOS ? value : 0; + break; + case EMAC_TX_PL0_REG: + case EMAC_TX_PL1_REG: + chan = (offset == EMAC_TX_PL0_REG ? 0 : 1); + if (value > TX_FIFO_SIZE) { + qemu_log_mask(LOG_GUEST_ERROR, + "allwinner_emac: invalid TX frame length %d\n", + (int)value); + value = TX_FIFO_SIZE; + } + s->tx_fifo[chan].length = value; + break; + case EMAC_TX_IO_DATA_REG: + fifo = &s->tx_fifo[s->tx_channel]; + if (!aw_emac_tx_has_space(fifo, 4)) { + qemu_log_mask(LOG_GUEST_ERROR, + "allwinner_emac: TX data overruns fifo (%d)\n", + fifo->offset); + break; + } + aw_emac_tx_pushw(fifo, value); + break; + case EMAC_RX_CTL_REG: + s->rx_ctl = value; + break; + case EMAC_RX_FBC_REG: + if (value == 0) { + aw_emac_rx_reset(&s->rx_fifo); + } + 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: + aw_emac_mdio_write(s, extract32(s->phy_target, PHY_ADDR_SHIFT, 8), + extract32(s->phy_target, PHY_REG_SHIFT, 8), value); + break; + default: + qemu_log_mask(LOG_UNIMP, + "allwinner_emac: write access to unknown register 0x" + TARGET_FMT_plx "\n", 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, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +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); +} + +static Property aw_emac_properties[] = { + DEFINE_NIC_PROPERTIES(AwEmacState, conf), + DEFINE_PROP_UINT8("phyaddr", AwEmacState, phy_addr, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static const VMStateDescription vmstate_mii = { + .name = "rtl8201cp", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT16(bmcr, RTL8201CPState), + VMSTATE_UINT16(bmsr, RTL8201CPState), + VMSTATE_UINT16(anar, RTL8201CPState), + VMSTATE_UINT16(anlpar, RTL8201CPState), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_rx_fifo = { + .name = "allwinner_emac_rx_fifo", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8_ARRAY(data, AwEmacRxFifo, RX_FIFO_SIZE), + VMSTATE_INT32(head, AwEmacRxFifo), + VMSTATE_INT32(num, AwEmacRxFifo), + VMSTATE_INT32(num_packets, AwEmacRxFifo), + VMSTATE_INT32(packet_size, AwEmacRxFifo), + VMSTATE_INT32(packet_pos, AwEmacRxFifo), + 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_UINT8_ARRAY(data, AwEmacTxFifo, TX_FIFO_SIZE), + VMSTATE_INT32(offset, AwEmacTxFifo), + VMSTATE_INT32(length, AwEmacTxFifo), + VMSTATE_END_OF_LIST() + } +}; + +static int aw_emac_post_load(void *opaque, int version_id) +{ + AwEmacState *s = opaque; + + aw_emac_set_link(qemu_get_queue(s->nic)); + + return 0; +} + +static const VMStateDescription vmstate_aw_emac = { + .name = "allwinner_emac", + .version_id = 1, + .minimum_version_id = 1, + .post_load = aw_emac_post_load, + .fields = (VMStateField[]) { + VMSTATE_STRUCT(mii, AwEmacState, 1, vmstate_mii, RTL8201CPState), + 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_STRUCT(rx_fifo, AwEmacState, 1, vmstate_rx_fifo, AwEmacRxFifo), + VMSTATE_STRUCT_ARRAY(tx_fifo, AwEmacState, + NUM_TX_FIFOS, 1, vmstate_tx_fifo, AwEmacTxFifo), + VMSTATE_INT32(tx_channel, 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->reset = aw_emac_reset; + 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..3909733 --- /dev/null +++ b/include/hw/net/allwinner_emac.h @@ -0,0 +1,222 @@ +/* + * Emulation of Allwinner EMAC Fast Ethernet controller and + * Realtek RTL8201CP PHY + * + * Copyright (C) 2014 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_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 +#define MII_ANER 6 +#define MII_NSR 16 +#define MII_LBREMR 17 +#define MII_REC 18 +#define MII_SNRDR 19 +#define MII_TEST 25 + +/* 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) + +/* Due to lack of specifications, size of fifos is chosen arbitrarily */ +#define TX_FIFO_SIZE (4 * 1024) +#define RX_FIFO_SIZE (32 * 1024) + +#define NUM_TX_FIFOS 2 +#define RX_HDR_SIZE 8 +#define CRC_SIZE 4 + +#define PHY_REG_SHIFT 0 +#define PHY_ADDR_SHIFT 8 + +/* Buffer that holds a single packet to be transmitted */ +typedef struct AwEmacTxFifo { + uint8_t data[TX_FIFO_SIZE]; + int offset; + int length; +} AwEmacTxFifo; + +/* Circular buffer for received packets */ +typedef struct AwEmacRxFifo { + uint8_t data[RX_FIFO_SIZE]; + int head; + int num; + int num_packets; /* Number of packets stored in fifo */ + int packet_size; /* Remaining size of current packet */ + int packet_pos; /* Position in current packet */ +} AwEmacRxFifo; + +typedef struct RTL8201CPState { + uint16_t bmcr; + uint16_t bmsr; + uint16_t anar; + uint16_t anlpar; +} RTL8201CPState; + +typedef struct AwEmacState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + MemoryRegion iomem; + qemu_irq irq; + NICState *nic; + NICConf conf; + RTL8201CPState mii; + uint8_t phy_addr; + + uint32_t ctl; + uint32_t tx_mode; + uint32_t rx_ctl; + uint32_t int_ctl; + uint32_t int_sta; + uint32_t phy_target; + + AwEmacRxFifo rx_fifo; + AwEmacTxFifo tx_fifo[NUM_TX_FIFOS]; + int tx_channel; +} AwEmacState; + +#endif -- 1.7.10.4 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [Qemu-devel] [PATCH v3 1/2] hw/net: add support for Allwinner EMAC Fast Ethernet controller 2014-01-19 23:25 ` [Qemu-devel] [PATCH v3 1/2] hw/net: add support for Allwinner EMAC Fast Ethernet controller Beniamino Galvani @ 2014-01-23 13:04 ` Peter Crosthwaite 2014-01-25 13:37 ` Beniamino Galvani 0 siblings, 1 reply; 9+ messages in thread From: Peter Crosthwaite @ 2014-01-23 13:04 UTC (permalink / raw) To: Beniamino Galvani Cc: Peter Maydell, qemu-devel@nongnu.org Developers, Li Guang, Stefan Hajnoczi On Mon, Jan 20, 2014 at 9:25 AM, 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 | 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.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> > + > +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. > +{ > + 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. Regards, Peter > +} > + > +static ssize_t aw_emac_receive(NetClientState *nc, const uint8_t *buf, > + size_t size) > +{ > + AwEmacState *s = qemu_get_nic_opaque(nc); > + AwEmacRxFifo *fifo = &s->rx_fifo; > + size_t padded_size, total_size; > + uint32_t crc; > + > + padded_size = sizae > 60 ? size : 60; > + total_size = QEMU_ALIGN_UP(RX_HDR_SIZE + padded_size + CRC_SIZE, 4); > + > + if (!(s->ctl & EMAC_CTL_RX_EN) || !aw_emac_rx_has_space(fifo, total_size)) { > + return -1; > + } > + > + aw_emac_rx_pushw(fifo, EMAC_UNDOCUMENTED_MAGIC); > + aw_emac_rx_pushw(fifo, EMAC_RX_HEADER(padded_size + CRC_SIZE, > + EMAC_RX_IO_DATA_STATUS_OK)); > + aw_emac_rx_push(fifo, buf, size); > + crc = crc32(~0, buf, size); > + > + if (padded_size != size) { > + aw_emac_rx_push(fifo, padding, padded_size - size); > + crc = crc32(crc, padding, padded_size - size); > + } > + > + aw_emac_rx_pushw(fifo, crc); > + aw_emac_rx_push(fifo, padding, QEMU_ALIGN_UP(padded_size, 4) - padded_size); > + s->rx_fifo.num_packets++; > + > + 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(DeviceState *dev) > +{ > + AwEmacState *s = AW_EMAC(dev); > + NetClientState *nc = qemu_get_queue(s->nic); > + > + s->ctl = 0; > + s->tx_mode = 0; > + s->int_ctl = 0; > + s->int_sta = 0; > + s->tx_channel = 0; > + s->phy_target = 0; > + > + aw_emac_tx_reset(&s->tx_fifo[0]); > + aw_emac_tx_reset(&s->tx_fifo[1]); > + aw_emac_rx_reset(&s->rx_fifo); > + > + mii_reset(&s->mii, !nc->link_down); > +} > + > +static uint64_t aw_emac_read(void *opaque, hwaddr offset, unsigned size) > +{ > + AwEmacState *s = opaque; > + AwEmacRxFifo *fifo = &s->rx_fifo; > + uint64_t ret; > + > + switch (offset) { > + case EMAC_CTL_REG: > + return s->ctl; > + case EMAC_TX_MODE_REG: > + return s->tx_mode; > + case EMAC_TX_INS_REG: > + return s->tx_channel; > + case EMAC_RX_CTL_REG: > + return s->rx_ctl; > + case EMAC_RX_IO_DATA_REG: > + if (!fifo->num_packets) { > + qemu_log_mask(LOG_GUEST_ERROR, > + "Read IO data register when no packet available"); > + return 0; > + } > + > + ret = aw_emac_rx_popw(fifo); > + > + switch (fifo->packet_pos) { > + case 0: /* Word is magic header */ > + fifo->packet_pos += 4; > + break; > + case 4: /* Word is rx info header */ > + fifo->packet_pos += 4; > + fifo->packet_size = QEMU_ALIGN_UP(extract32(ret, 0, 16), 4); > + break; > + default: /* Word is packet data */ > + fifo->packet_pos += 4; > + fifo->packet_size -= 4; > + > + if (!fifo->packet_size) { > + fifo->packet_pos = 0; > + fifo->num_packets--; > + } > + } > + return ret; > + case EMAC_RX_FBC_REG: > + return s->rx_fifo.num_packets; > + case EMAC_INT_CTL_REG: > + return s->int_ctl; > + case EMAC_INT_STA_REG: > + return s->int_sta; > + case EMAC_MAC_MRDD_REG: > + return aw_emac_mdio_read(s, extract32(s->phy_target, PHY_ADDR_SHIFT, 8), > + extract32(s->phy_target, PHY_REG_SHIFT, 8)); > + default: > + qemu_log_mask(LOG_UNIMP, > + "allwinner_emac: read access to unknown register 0x" > + TARGET_FMT_plx "\n", offset); > + ret = 0; > + } > + > + return ret; > +} > + > +static void aw_emac_write(void *opaque, hwaddr offset, uint64_t value, > + unsigned size) > +{ > + AwEmacState *s = opaque; > + AwEmacTxFifo *fifo; > + NetClientState *nc = qemu_get_queue(s->nic); > + int chan; > + > + switch (offset) { > + case EMAC_CTL_REG: > + if (value & EMAC_CTL_RESET) { > + aw_emac_reset(DEVICE(s)); > + value &= ~EMAC_CTL_RESET; > + } > + s->ctl = value; > + if (aw_emac_can_receive(nc)) { > + qemu_flush_queued_packets(nc); > + } > + 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(nc, s->tx_fifo[chan].data, > + s->tx_fifo[chan].length); > + /* Raise TX interrupt */ > + s->int_sta |= EMAC_INT_TX_CHAN(chan); > + s->tx_fifo[chan].offset = 0; > + aw_emac_update_irq(s); > + } > + break; > + case EMAC_TX_INS_REG: > + s->tx_channel = value < NUM_TX_FIFOS ? value : 0; > + break; > + case EMAC_TX_PL0_REG: > + case EMAC_TX_PL1_REG: > + chan = (offset == EMAC_TX_PL0_REG ? 0 : 1); > + if (value > TX_FIFO_SIZE) { > + qemu_log_mask(LOG_GUEST_ERROR, > + "allwinner_emac: invalid TX frame length %d\n", > + (int)value); > + value = TX_FIFO_SIZE; > + } > + s->tx_fifo[chan].length = value; > + break; > + case EMAC_TX_IO_DATA_REG: > + fifo = &s->tx_fifo[s->tx_channel]; > + if (!aw_emac_tx_has_space(fifo, 4)) { > + qemu_log_mask(LOG_GUEST_ERROR, > + "allwinner_emac: TX data overruns fifo (%d)\n", > + fifo->offset); > + break; > + } > + aw_emac_tx_pushw(fifo, value); > + break; > + case EMAC_RX_CTL_REG: > + s->rx_ctl = value; > + break; > + case EMAC_RX_FBC_REG: > + if (value == 0) { > + aw_emac_rx_reset(&s->rx_fifo); > + } > + 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: > + aw_emac_mdio_write(s, extract32(s->phy_target, PHY_ADDR_SHIFT, 8), > + extract32(s->phy_target, PHY_REG_SHIFT, 8), value); > + break; > + default: > + qemu_log_mask(LOG_UNIMP, > + "allwinner_emac: write access to unknown register 0x" > + TARGET_FMT_plx "\n", 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, > + .valid = { > + .min_access_size = 4, > + .max_access_size = 4, > + }, > +}; > + > +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); > +} > + > +static Property aw_emac_properties[] = { > + DEFINE_NIC_PROPERTIES(AwEmacState, conf), > + DEFINE_PROP_UINT8("phyaddr", AwEmacState, phy_addr, 0), > + DEFINE_PROP_END_OF_LIST(), > +}; > + > +static const VMStateDescription vmstate_mii = { > + .name = "rtl8201cp", > + .version_id = 1, > + .minimum_version_id = 1, > + .fields = (VMStateField[]) { > + VMSTATE_UINT16(bmcr, RTL8201CPState), > + VMSTATE_UINT16(bmsr, RTL8201CPState), > + VMSTATE_UINT16(anar, RTL8201CPState), > + VMSTATE_UINT16(anlpar, RTL8201CPState), > + VMSTATE_END_OF_LIST() > + } > +}; > + > +static const VMStateDescription vmstate_rx_fifo = { > + .name = "allwinner_emac_rx_fifo", > + .version_id = 1, > + .minimum_version_id = 1, > + .fields = (VMStateField[]) { > + VMSTATE_UINT8_ARRAY(data, AwEmacRxFifo, RX_FIFO_SIZE), > + VMSTATE_INT32(head, AwEmacRxFifo), > + VMSTATE_INT32(num, AwEmacRxFifo), > + VMSTATE_INT32(num_packets, AwEmacRxFifo), > + VMSTATE_INT32(packet_size, AwEmacRxFifo), > + VMSTATE_INT32(packet_pos, AwEmacRxFifo), > + 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_UINT8_ARRAY(data, AwEmacTxFifo, TX_FIFO_SIZE), > + VMSTATE_INT32(offset, AwEmacTxFifo), > + VMSTATE_INT32(length, AwEmacTxFifo), > + VMSTATE_END_OF_LIST() > + } > +}; > + > +static int aw_emac_post_load(void *opaque, int version_id) > +{ > + AwEmacState *s = opaque; > + > + aw_emac_set_link(qemu_get_queue(s->nic)); > + > + return 0; > +} > + > +static const VMStateDescription vmstate_aw_emac = { > + .name = "allwinner_emac", > + .version_id = 1, > + .minimum_version_id = 1, > + .post_load = aw_emac_post_load, > + .fields = (VMStateField[]) { > + VMSTATE_STRUCT(mii, AwEmacState, 1, vmstate_mii, RTL8201CPState), > + 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_STRUCT(rx_fifo, AwEmacState, 1, vmstate_rx_fifo, AwEmacRxFifo), > + VMSTATE_STRUCT_ARRAY(tx_fifo, AwEmacState, > + NUM_TX_FIFOS, 1, vmstate_tx_fifo, AwEmacTxFifo), > + VMSTATE_INT32(tx_channel, 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->reset = aw_emac_reset; > + 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..3909733 > --- /dev/null > +++ b/include/hw/net/allwinner_emac.h > @@ -0,0 +1,222 @@ > +/* > + * Emulation of Allwinner EMAC Fast Ethernet controller and > + * Realtek RTL8201CP PHY > + * > + * Copyright (C) 2014 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_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 > +#define MII_ANER 6 > +#define MII_NSR 16 > +#define MII_LBREMR 17 > +#define MII_REC 18 > +#define MII_SNRDR 19 > +#define MII_TEST 25 > + > +/* 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) > + > +/* Due to lack of specifications, size of fifos is chosen arbitrarily */ > +#define TX_FIFO_SIZE (4 * 1024) > +#define RX_FIFO_SIZE (32 * 1024) > + > +#define NUM_TX_FIFOS 2 > +#define RX_HDR_SIZE 8 > +#define CRC_SIZE 4 > + > +#define PHY_REG_SHIFT 0 > +#define PHY_ADDR_SHIFT 8 > + > +/* Buffer that holds a single packet to be transmitted */ > +typedef struct AwEmacTxFifo { > + uint8_t data[TX_FIFO_SIZE]; > + int offset; > + int length; > +} AwEmacTxFifo; > + > +/* Circular buffer for received packets */ > +typedef struct AwEmacRxFifo { > + uint8_t data[RX_FIFO_SIZE]; > + int head; > + int num; > + int num_packets; /* Number of packets stored in fifo */ > + int packet_size; /* Remaining size of current packet */ > + int packet_pos; /* Position in current packet */ > +} AwEmacRxFifo; > + > +typedef struct RTL8201CPState { > + uint16_t bmcr; > + uint16_t bmsr; > + uint16_t anar; > + uint16_t anlpar; > +} RTL8201CPState; > + > +typedef struct AwEmacState { > + /*< private >*/ > + SysBusDevice parent_obj; > + /*< public >*/ > + > + MemoryRegion iomem; > + qemu_irq irq; > + NICState *nic; > + NICConf conf; > + RTL8201CPState mii; > + uint8_t phy_addr; > + > + uint32_t ctl; > + uint32_t tx_mode; > + uint32_t rx_ctl; > + uint32_t int_ctl; > + uint32_t int_sta; > + uint32_t phy_target; > + > + AwEmacRxFifo rx_fifo; > + AwEmacTxFifo tx_fifo[NUM_TX_FIFOS]; > + int tx_channel; > +} AwEmacState; > + > +#endif > -- > 1.7.10.4 > > ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [Qemu-devel] [PATCH v3 1/2] hw/net: add support for Allwinner EMAC Fast Ethernet controller 2014-01-23 13:04 ` Peter Crosthwaite @ 2014-01-25 13:37 ` Beniamino Galvani 2014-01-25 23:58 ` Peter Crosthwaite 0 siblings, 1 reply; 9+ messages in thread From: Beniamino Galvani @ 2014-01-25 13:37 UTC (permalink / raw) To: Peter Crosthwaite Cc: Peter Maydell, qemu-devel@nongnu.org Developers, Li Guang, Stefan Hajnoczi 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.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 | 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.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> > > + > > +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? > > > +{ > > + 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 ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [Qemu-devel] [PATCH v3 1/2] hw/net: add support for Allwinner EMAC Fast Ethernet controller 2014-01-25 13:37 ` Beniamino Galvani @ 2014-01-25 23:58 ` Peter Crosthwaite 0 siblings, 0 replies; 9+ messages in thread From: Peter Crosthwaite @ 2014-01-25 23:58 UTC (permalink / raw) To: Beniamino Galvani Cc: Peter Maydell, qemu-devel@nongnu.org Developers, Li Guang, Stefan Hajnoczi On Sat, Jan 25, 2014 at 11:37 PM, Beniamino Galvani <b.galvani@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.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 | 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.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> >> > + >> > +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 > ^ permalink raw reply [flat|nested] 9+ messages in thread
* [Qemu-devel] [PATCH v3 2/2] hw/arm/allwinner-a10: initialize EMAC 2014-01-19 23:25 [Qemu-devel] [PATCH v3 0/2] hw/arm: add ethernet support to Allwinner A10 Beniamino Galvani 2014-01-19 23:25 ` [Qemu-devel] [PATCH v3 1/2] hw/net: add support for Allwinner EMAC Fast Ethernet controller Beniamino Galvani @ 2014-01-19 23:25 ` Beniamino Galvani 2014-01-23 13:06 ` Peter Crosthwaite 2014-01-25 23:42 ` Andreas Färber 1 sibling, 2 replies; 9+ messages in thread From: Beniamino Galvani @ 2014-01-19 23:25 UTC (permalink / raw) To: qemu-devel Cc: Beniamino Galvani, Peter Maydell, Peter Crosthwaite, Li Guang, Stefan Hajnoczi Signed-off-by: Beniamino Galvani <b.galvani@gmail.com> --- hw/arm/allwinner-a10.c | 16 ++++++++++++++++ hw/arm/cubieboard.c | 7 +++++++ include/hw/arm/allwinner-a10.h | 3 +++ 3 files changed, 26 insertions(+) diff --git a/hw/arm/allwinner-a10.c b/hw/arm/allwinner-a10.c index 4658e19..416cd49 100644 --- a/hw/arm/allwinner-a10.c +++ b/hw/arm/allwinner-a10.c @@ -31,6 +31,13 @@ static void aw_a10_init(Object *obj) object_initialize(&s->timer, sizeof(s->timer), TYPE_AW_A10_PIT); qdev_set_parent_bus(DEVICE(&s->timer), sysbus_get_default()); + + object_initialize(&s->emac, sizeof(s->emac), TYPE_AW_EMAC); + qdev_set_parent_bus(DEVICE(&s->emac), sysbus_get_default()); + if (nd_table[0].used) { + qemu_check_nic_model(&nd_table[0], "allwinner_emac"); + qdev_set_nic_properties(DEVICE(&s->emac), &nd_table[0]); + } } static void aw_a10_realize(DeviceState *dev, Error **errp) @@ -76,6 +83,15 @@ static void aw_a10_realize(DeviceState *dev, Error **errp) sysbus_connect_irq(sysbusdev, 4, s->irq[67]); sysbus_connect_irq(sysbusdev, 5, s->irq[68]); + object_property_set_bool(OBJECT(&s->emac), true, "realized", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + sysbusdev = SYS_BUS_DEVICE(&s->emac); + sysbus_mmio_map(sysbusdev, 0, AW_A10_EMAC_BASE); + sysbus_connect_irq(sysbusdev, 0, s->irq[55]); + serial_mm_init(get_system_memory(), AW_A10_UART0_REG_BASE, 2, s->irq[1], 115200, serial_hds[0], DEVICE_NATIVE_ENDIAN); } diff --git a/hw/arm/cubieboard.c b/hw/arm/cubieboard.c index 3fcb6d2..b3f8f51 100644 --- a/hw/arm/cubieboard.c +++ b/hw/arm/cubieboard.c @@ -36,6 +36,13 @@ static void cubieboard_init(QEMUMachineInitArgs *args) Error *err = NULL; s->a10 = AW_A10(object_new(TYPE_AW_A10)); + + object_property_set_int(OBJECT(&s->a10->emac), 1, "phyaddr", &err); + if (err != NULL) { + error_report("Couldn't set phy address: %s\n", error_get_pretty(err)); + exit(1); + } + object_property_set_bool(OBJECT(s->a10), true, "realized", &err); if (err != NULL) { error_report("Couldn't realize Allwinner A10: %s\n", diff --git a/include/hw/arm/allwinner-a10.h b/include/hw/arm/allwinner-a10.h index da36647..01a189b 100644 --- a/include/hw/arm/allwinner-a10.h +++ b/include/hw/arm/allwinner-a10.h @@ -6,6 +6,7 @@ #include "hw/arm/arm.h" #include "hw/timer/allwinner-a10-pit.h" #include "hw/intc/allwinner-a10-pic.h" +#include "hw/net/allwinner_emac.h" #include "sysemu/sysemu.h" #include "exec/address-spaces.h" @@ -14,6 +15,7 @@ #define AW_A10_PIC_REG_BASE 0x01c20400 #define AW_A10_PIT_REG_BASE 0x01c20c00 #define AW_A10_UART0_REG_BASE 0x01c28000 +#define AW_A10_EMAC_BASE 0x01c0b000 #define AW_A10_SDRAM_BASE 0x40000000 @@ -29,6 +31,7 @@ typedef struct AwA10State { qemu_irq irq[AW_A10_PIC_INT_NR]; AwA10PITState timer; AwA10PICState intc; + AwEmacState emac; } AwA10State; #define ALLWINNER_H_ -- 1.7.10.4 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [Qemu-devel] [PATCH v3 2/2] hw/arm/allwinner-a10: initialize EMAC 2014-01-19 23:25 ` [Qemu-devel] [PATCH v3 2/2] hw/arm/allwinner-a10: initialize EMAC Beniamino Galvani @ 2014-01-23 13:06 ` Peter Crosthwaite 2014-01-25 23:42 ` Andreas Färber 1 sibling, 0 replies; 9+ messages in thread From: Peter Crosthwaite @ 2014-01-23 13:06 UTC (permalink / raw) To: Beniamino Galvani Cc: Peter Maydell, qemu-devel@nongnu.org Developers, Li Guang, Stefan Hajnoczi On Mon, Jan 20, 2014 at 9:25 AM, Beniamino Galvani <b.galvani@gmail.com> wrote: > Signed-off-by: Beniamino Galvani <b.galvani@gmail.com> > --- > hw/arm/allwinner-a10.c | 16 ++++++++++++++++ > hw/arm/cubieboard.c | 7 +++++++ > include/hw/arm/allwinner-a10.h | 3 +++ > 3 files changed, 26 insertions(+) > > diff --git a/hw/arm/allwinner-a10.c b/hw/arm/allwinner-a10.c > index 4658e19..416cd49 100644 > --- a/hw/arm/allwinner-a10.c > +++ b/hw/arm/allwinner-a10.c > @@ -31,6 +31,13 @@ static void aw_a10_init(Object *obj) > > object_initialize(&s->timer, sizeof(s->timer), TYPE_AW_A10_PIT); > qdev_set_parent_bus(DEVICE(&s->timer), sysbus_get_default()); > + > + object_initialize(&s->emac, sizeof(s->emac), TYPE_AW_EMAC); > + qdev_set_parent_bus(DEVICE(&s->emac), sysbus_get_default()); > + if (nd_table[0].used) { > + qemu_check_nic_model(&nd_table[0], "allwinner_emac"); Any reason to not use TYPE_AW_EMAC? Regards, Peter > + qdev_set_nic_properties(DEVICE(&s->emac), &nd_table[0]); > + } > } > > static void aw_a10_realize(DeviceState *dev, Error **errp) > @@ -76,6 +83,15 @@ static void aw_a10_realize(DeviceState *dev, Error **errp) > sysbus_connect_irq(sysbusdev, 4, s->irq[67]); > sysbus_connect_irq(sysbusdev, 5, s->irq[68]); > > + object_property_set_bool(OBJECT(&s->emac), true, "realized", &err); > + if (err != NULL) { > + error_propagate(errp, err); > + return; > + } > + sysbusdev = SYS_BUS_DEVICE(&s->emac); > + sysbus_mmio_map(sysbusdev, 0, AW_A10_EMAC_BASE); > + sysbus_connect_irq(sysbusdev, 0, s->irq[55]); > + > serial_mm_init(get_system_memory(), AW_A10_UART0_REG_BASE, 2, s->irq[1], > 115200, serial_hds[0], DEVICE_NATIVE_ENDIAN); > } > diff --git a/hw/arm/cubieboard.c b/hw/arm/cubieboard.c > index 3fcb6d2..b3f8f51 100644 > --- a/hw/arm/cubieboard.c > +++ b/hw/arm/cubieboard.c > @@ -36,6 +36,13 @@ static void cubieboard_init(QEMUMachineInitArgs *args) > Error *err = NULL; > > s->a10 = AW_A10(object_new(TYPE_AW_A10)); > + > + object_property_set_int(OBJECT(&s->a10->emac), 1, "phyaddr", &err); > + if (err != NULL) { > + error_report("Couldn't set phy address: %s\n", error_get_pretty(err)); > + exit(1); > + } > + > object_property_set_bool(OBJECT(s->a10), true, "realized", &err); > if (err != NULL) { > error_report("Couldn't realize Allwinner A10: %s\n", > diff --git a/include/hw/arm/allwinner-a10.h b/include/hw/arm/allwinner-a10.h > index da36647..01a189b 100644 > --- a/include/hw/arm/allwinner-a10.h > +++ b/include/hw/arm/allwinner-a10.h > @@ -6,6 +6,7 @@ > #include "hw/arm/arm.h" > #include "hw/timer/allwinner-a10-pit.h" > #include "hw/intc/allwinner-a10-pic.h" > +#include "hw/net/allwinner_emac.h" > > #include "sysemu/sysemu.h" > #include "exec/address-spaces.h" > @@ -14,6 +15,7 @@ > #define AW_A10_PIC_REG_BASE 0x01c20400 > #define AW_A10_PIT_REG_BASE 0x01c20c00 > #define AW_A10_UART0_REG_BASE 0x01c28000 > +#define AW_A10_EMAC_BASE 0x01c0b000 > > #define AW_A10_SDRAM_BASE 0x40000000 > > @@ -29,6 +31,7 @@ typedef struct AwA10State { > qemu_irq irq[AW_A10_PIC_INT_NR]; > AwA10PITState timer; > AwA10PICState intc; > + AwEmacState emac; > } AwA10State; > > #define ALLWINNER_H_ > -- > 1.7.10.4 > > ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [Qemu-devel] [PATCH v3 2/2] hw/arm/allwinner-a10: initialize EMAC 2014-01-19 23:25 ` [Qemu-devel] [PATCH v3 2/2] hw/arm/allwinner-a10: initialize EMAC Beniamino Galvani 2014-01-23 13:06 ` Peter Crosthwaite @ 2014-01-25 23:42 ` Andreas Färber 2014-01-26 21:25 ` Beniamino Galvani 1 sibling, 1 reply; 9+ messages in thread From: Andreas Färber @ 2014-01-25 23:42 UTC (permalink / raw) To: Beniamino Galvani, qemu-devel Cc: Peter Maydell, Peter Crosthwaite, Li Guang, Stefan Hajnoczi Am 20.01.2014 00:25, schrieb Beniamino Galvani: > Signed-off-by: Beniamino Galvani <b.galvani@gmail.com> > --- > hw/arm/allwinner-a10.c | 16 ++++++++++++++++ > hw/arm/cubieboard.c | 7 +++++++ > include/hw/arm/allwinner-a10.h | 3 +++ > 3 files changed, 26 insertions(+) > > diff --git a/hw/arm/allwinner-a10.c b/hw/arm/allwinner-a10.c > index 4658e19..416cd49 100644 > --- a/hw/arm/allwinner-a10.c > +++ b/hw/arm/allwinner-a10.c > @@ -31,6 +31,13 @@ static void aw_a10_init(Object *obj) > > object_initialize(&s->timer, sizeof(s->timer), TYPE_AW_A10_PIT); > qdev_set_parent_bus(DEVICE(&s->timer), sysbus_get_default()); > + > + object_initialize(&s->emac, sizeof(s->emac), TYPE_AW_EMAC); > + qdev_set_parent_bus(DEVICE(&s->emac), sysbus_get_default()); > + if (nd_table[0].used) { > + qemu_check_nic_model(&nd_table[0], "allwinner_emac"); Please adopt new-style names with dashes, i.e. "allwinner-emac". Peter C.'s comment wrt TYPE_* still applies though. > + qdev_set_nic_properties(DEVICE(&s->emac), &nd_table[0]); Since you're using DEVICE() twice, you should consider using a local variable like you did for sysbusdev below. It's a one-time initialization though, so not mandatory. > + } > } > > static void aw_a10_realize(DeviceState *dev, Error **errp) > @@ -76,6 +83,15 @@ static void aw_a10_realize(DeviceState *dev, Error **errp) > sysbus_connect_irq(sysbusdev, 4, s->irq[67]); > sysbus_connect_irq(sysbusdev, 5, s->irq[68]); > > + object_property_set_bool(OBJECT(&s->emac), true, "realized", &err); > + if (err != NULL) { > + error_propagate(errp, err); > + return; > + } > + sysbusdev = SYS_BUS_DEVICE(&s->emac); > + sysbus_mmio_map(sysbusdev, 0, AW_A10_EMAC_BASE); > + sysbus_connect_irq(sysbusdev, 0, s->irq[55]); > + > serial_mm_init(get_system_memory(), AW_A10_UART0_REG_BASE, 2, s->irq[1], > 115200, serial_hds[0], DEVICE_NATIVE_ENDIAN); > } > diff --git a/hw/arm/cubieboard.c b/hw/arm/cubieboard.c > index 3fcb6d2..b3f8f51 100644 > --- a/hw/arm/cubieboard.c > +++ b/hw/arm/cubieboard.c > @@ -36,6 +36,13 @@ static void cubieboard_init(QEMUMachineInitArgs *args) > Error *err = NULL; > > s->a10 = AW_A10(object_new(TYPE_AW_A10)); > + > + object_property_set_int(OBJECT(&s->a10->emac), 1, "phyaddr", &err); "phy-addr"? > + if (err != NULL) { > + error_report("Couldn't set phy address: %s\n", error_get_pretty(err)); error_report() always without trailing \n. > + exit(1); > + } > + > object_property_set_bool(OBJECT(s->a10), true, "realized", &err); > if (err != NULL) { > error_report("Couldn't realize Allwinner A10: %s\n", Bad example. ;) Fixes welcome. Cheers, Andreas > diff --git a/include/hw/arm/allwinner-a10.h b/include/hw/arm/allwinner-a10.h > index da36647..01a189b 100644 > --- a/include/hw/arm/allwinner-a10.h > +++ b/include/hw/arm/allwinner-a10.h > @@ -6,6 +6,7 @@ > #include "hw/arm/arm.h" > #include "hw/timer/allwinner-a10-pit.h" > #include "hw/intc/allwinner-a10-pic.h" > +#include "hw/net/allwinner_emac.h" > > #include "sysemu/sysemu.h" > #include "exec/address-spaces.h" > @@ -14,6 +15,7 @@ > #define AW_A10_PIC_REG_BASE 0x01c20400 > #define AW_A10_PIT_REG_BASE 0x01c20c00 > #define AW_A10_UART0_REG_BASE 0x01c28000 > +#define AW_A10_EMAC_BASE 0x01c0b000 > > #define AW_A10_SDRAM_BASE 0x40000000 > > @@ -29,6 +31,7 @@ typedef struct AwA10State { > qemu_irq irq[AW_A10_PIC_INT_NR]; > AwA10PITState timer; > AwA10PICState intc; > + AwEmacState emac; > } AwA10State; > > #define ALLWINNER_H_ > -- SUSE LINUX Products GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany GF: Jeff Hawn, Jennifer Guild, Felix Imendörffer; HRB 16746 AG Nürnberg ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [Qemu-devel] [PATCH v3 2/2] hw/arm/allwinner-a10: initialize EMAC 2014-01-25 23:42 ` Andreas Färber @ 2014-01-26 21:25 ` Beniamino Galvani 0 siblings, 0 replies; 9+ messages in thread From: Beniamino Galvani @ 2014-01-26 21:25 UTC (permalink / raw) To: Andreas Färber Cc: Peter Maydell, Peter Crosthwaite, qemu-devel, Li Guang, Stefan Hajnoczi On Sun, Jan 26, 2014 at 12:42:59AM +0100, Andreas Färber wrote: > Am 20.01.2014 00:25, schrieb Beniamino Galvani: > > Signed-off-by: Beniamino Galvani <b.galvani@gmail.com> > > --- > > hw/arm/allwinner-a10.c | 16 ++++++++++++++++ > > hw/arm/cubieboard.c | 7 +++++++ > > include/hw/arm/allwinner-a10.h | 3 +++ > > 3 files changed, 26 insertions(+) > > > > diff --git a/hw/arm/allwinner-a10.c b/hw/arm/allwinner-a10.c > > index 4658e19..416cd49 100644 > > --- a/hw/arm/allwinner-a10.c > > +++ b/hw/arm/allwinner-a10.c > > @@ -31,6 +31,13 @@ static void aw_a10_init(Object *obj) > > > > object_initialize(&s->timer, sizeof(s->timer), TYPE_AW_A10_PIT); > > qdev_set_parent_bus(DEVICE(&s->timer), sysbus_get_default()); > > + > > + object_initialize(&s->emac, sizeof(s->emac), TYPE_AW_EMAC); > > + qdev_set_parent_bus(DEVICE(&s->emac), sysbus_get_default()); > > + if (nd_table[0].used) { > > + qemu_check_nic_model(&nd_table[0], "allwinner_emac"); > > Please adopt new-style names with dashes, i.e. "allwinner-emac". Peter > C.'s comment wrt TYPE_* still applies though. Ok. > > > + qdev_set_nic_properties(DEVICE(&s->emac), &nd_table[0]); > > Since you're using DEVICE() twice, you should consider using a local > variable like you did for sysbusdev below. It's a one-time > initialization though, so not mandatory. For readability and also, as suggested by Peter in a previous review, consistency with nearby code I'd prefer to keep the two casts. > > > + } > > } > > > > static void aw_a10_realize(DeviceState *dev, Error **errp) > > @@ -76,6 +83,15 @@ static void aw_a10_realize(DeviceState *dev, Error **errp) > > sysbus_connect_irq(sysbusdev, 4, s->irq[67]); > > sysbus_connect_irq(sysbusdev, 5, s->irq[68]); > > > > + object_property_set_bool(OBJECT(&s->emac), true, "realized", &err); > > + if (err != NULL) { > > + error_propagate(errp, err); > > + return; > > + } > > + sysbusdev = SYS_BUS_DEVICE(&s->emac); > > + sysbus_mmio_map(sysbusdev, 0, AW_A10_EMAC_BASE); > > + sysbus_connect_irq(sysbusdev, 0, s->irq[55]); > > + > > serial_mm_init(get_system_memory(), AW_A10_UART0_REG_BASE, 2, s->irq[1], > > 115200, serial_hds[0], DEVICE_NATIVE_ENDIAN); > > } > > diff --git a/hw/arm/cubieboard.c b/hw/arm/cubieboard.c > > index 3fcb6d2..b3f8f51 100644 > > --- a/hw/arm/cubieboard.c > > +++ b/hw/arm/cubieboard.c > > @@ -36,6 +36,13 @@ static void cubieboard_init(QEMUMachineInitArgs *args) > > Error *err = NULL; > > > > s->a10 = AW_A10(object_new(TYPE_AW_A10)); > > + > > + object_property_set_int(OBJECT(&s->a10->emac), 1, "phyaddr", &err); > > "phy-addr"? Other network adapters are using "phyaddr" so I simply copied that. But I agree, "phy-addr" sounds better. I will change it in v4. > > > + if (err != NULL) { > > + error_report("Couldn't set phy address: %s\n", error_get_pretty(err)); > > error_report() always without trailing \n. > > > + exit(1); > > + } > > + > > object_property_set_bool(OBJECT(s->a10), true, "realized", &err); > > if (err != NULL) { > > error_report("Couldn't realize Allwinner A10: %s\n", > > Bad example. ;) Fixes welcome. I will fix both. Beniamino > > Cheers, > Andreas > > > diff --git a/include/hw/arm/allwinner-a10.h b/include/hw/arm/allwinner-a10.h > > index da36647..01a189b 100644 > > --- a/include/hw/arm/allwinner-a10.h > > +++ b/include/hw/arm/allwinner-a10.h > > @@ -6,6 +6,7 @@ > > #include "hw/arm/arm.h" > > #include "hw/timer/allwinner-a10-pit.h" > > #include "hw/intc/allwinner-a10-pic.h" > > +#include "hw/net/allwinner_emac.h" > > > > #include "sysemu/sysemu.h" > > #include "exec/address-spaces.h" > > @@ -14,6 +15,7 @@ > > #define AW_A10_PIC_REG_BASE 0x01c20400 > > #define AW_A10_PIT_REG_BASE 0x01c20c00 > > #define AW_A10_UART0_REG_BASE 0x01c28000 > > +#define AW_A10_EMAC_BASE 0x01c0b000 > > > > #define AW_A10_SDRAM_BASE 0x40000000 > > > > @@ -29,6 +31,7 @@ typedef struct AwA10State { > > qemu_irq irq[AW_A10_PIC_INT_NR]; > > AwA10PITState timer; > > AwA10PICState intc; > > + AwEmacState emac; > > } AwA10State; > > > > #define ALLWINNER_H_ > > > > > -- > SUSE LINUX Products GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany > GF: Jeff Hawn, Jennifer Guild, Felix Imendörffer; HRB 16746 AG Nürnberg ^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2014-01-26 21:26 UTC | newest] Thread overview: 9+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2014-01-19 23:25 [Qemu-devel] [PATCH v3 0/2] hw/arm: add ethernet support to Allwinner A10 Beniamino Galvani 2014-01-19 23:25 ` [Qemu-devel] [PATCH v3 1/2] hw/net: add support for Allwinner EMAC Fast Ethernet controller Beniamino Galvani 2014-01-23 13:04 ` Peter Crosthwaite 2014-01-25 13:37 ` Beniamino Galvani 2014-01-25 23:58 ` Peter Crosthwaite 2014-01-19 23:25 ` [Qemu-devel] [PATCH v3 2/2] hw/arm/allwinner-a10: initialize EMAC Beniamino Galvani 2014-01-23 13:06 ` Peter Crosthwaite 2014-01-25 23:42 ` Andreas Färber 2014-01-26 21:25 ` Beniamino Galvani
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).