[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [PATCH v5 1/4] Add i.MX FEC Ethernet emulator
From: |
Peter Crosthwaite |
Subject: |
Re: [Qemu-devel] [PATCH v5 1/4] Add i.MX FEC Ethernet emulator |
Date: |
Fri, 24 May 2013 15:38:56 +1000 |
Hi JC,
All major comments addressed. Few minor suggestions.
On Wed, May 8, 2013 at 6:28 PM, Jean-Christophe DUBOIS
<address@hidden> wrote:
> This is based on the mcf_fec.c FEC implementation for ColdFire.
>
> * a generic phy was added (borrowed from lan9118).
> * The buffer management is also modified as buffers are
> slightly different between coldfire and i.MX.
>
> Signed-off-by: Jean-Christophe DUBOIS <address@hidden>
Reviewed-by: Peter Crosthwaite <address@hidden>
> ---
>
> Changes since V1:
> * none
>
> Changes since v2:
> * use QOM cast
> * reworked debug printf
> * use CamelCase for state type
> * warn with qemu_log_mask(LOG_GUEST_ERROR) or qemu_log_mask(LOG_UNIMP)
> * move to dma_memory_read/write API
> * rework interrupt handling
> * use qemu_flush_queued_packets() in rx_enable()
>
> Changes since v3:
> * use realise for device initialization
> * More QOM cast
> * reworked debug printf some more
> * standardise GPL header
> * use CamelCase for buffer descriptor type
>
> Changes since v4:
> * none
>
> default-configs/arm-softmmu.mak | 1 +
> hw/net/Makefile.objs | 1 +
> hw/net/imx_fec.c | 818
> ++++++++++++++++++++++++++++++++++++++++
> include/hw/arm/imx.h | 10 +-
> 4 files changed, 824 insertions(+), 6 deletions(-)
> create mode 100644 hw/net/imx_fec.c
>
> diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
> index 27cbe3d..b3a0207 100644
> --- a/default-configs/arm-softmmu.mak
> +++ b/default-configs/arm-softmmu.mak
> @@ -28,6 +28,7 @@ CONFIG_SSI_SD=y
> CONFIG_SSI_M25P80=y
> CONFIG_LAN9118=y
> CONFIG_SMC91C111=y
> +CONFIG_IMX_FEC=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..5c84727 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_IMX_FEC) += imx_fec.o
>
> common-obj-$(CONFIG_CADENCE) += cadence_gem.o
> common-obj-$(CONFIG_STELLARIS_ENET) += stellaris_enet.o
> diff --git a/hw/net/imx_fec.c b/hw/net/imx_fec.c
> new file mode 100644
> index 0000000..e25d6cd
> --- /dev/null
> +++ b/hw/net/imx_fec.c
> @@ -0,0 +1,818 @@
> +/*
> + * i.MX Fast Ethernet Controller emulation.
> + *
> + * Copyright (c) 2013 Jean-Christophe Dubois.
> + *
> + * Based on Coldfire Fast Ethernet Controller emulation.
> + *
> + * Copyright (c) 2007 CodeSourcery.
> + *
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "qemu/bitops.h"
> +
> +#include "sysemu/dma.h"
> +
> +#include "net/net.h"
> +
> +#include "hw/sysbus.h"
> +
The spaces between individual lines are probably un-needed.
> +/* For crc32 */
> +#include <zlib.h>
> +
> +#include "hw/arm/imx.h"
> +
> +#ifndef IMX_FEC_DEBUG
> +#define IMX_FEC_DEBUG 0
> +#endif
> +
> +#ifndef IMX_PHY_DEBUG
> +#define IMX_PHY_DEBUG 0
> +#endif
> +
> +#define TYPE_IMX_FEC "imx.fec"
> +
> +#if IMX_FEC_DEBUG
> +#define FEC_PRINTF(fmt, ...) \
> + do { fprintf(stderr, "%s[%s]: " fmt , TYPE_IMX_FEC, __func__, \
> + ## __VA_ARGS__); \
> + } while (0)
> +#else
> +#define FEC_PRINTF(fmt, ...) do {} while (0)
> +#endif
> +
> +#if IMX_PHY_DEBUG
> +#define PHY_PRINTF(fmt, ...) \
> + do { fprintf(stderr, "%s.phy[%s]: " fmt , TYPE_IMX_FEC, __func__, \
> + ## __VA_ARGS__); \
> + } while (0)
> +#else
> +#define PHY_PRINTF(fmt, ...) do {} while (0)
> +#endif
> +
> +#define IMX_FEC(obj) \
> + OBJECT_CHECK(IMXFECState, (obj), TYPE_IMX_FEC)
> +
> +#define FEC_MAX_FRAME_SIZE 2032
> +
> +typedef struct {
> + SysBusDevice parent_obj;
> +
> + NICState *nic;
> + NICConf conf;
> + qemu_irq irq;
> + MemoryRegion iomem;
> +
> + uint32_t irq_state;
> + uint32_t eir;
> + uint32_t eimr;
> + uint32_t rx_enabled;
> + uint32_t rx_descriptor;
> + uint32_t tx_descriptor;
> + uint32_t ecr;
> + uint32_t mmfr;
> + uint32_t mscr;
> + uint32_t mibc;
> + uint32_t rcr;
> + uint32_t tcr;
> + uint32_t tfwr;
> + uint32_t frsr;
> + uint32_t erdsr;
> + uint32_t etdsr;
> + uint32_t emrbr;
> + uint32_t miigsk_cfgr;
> + uint32_t miigsk_enr;
> +
> + uint32_t phy_status;
> + uint32_t phy_control;
> + uint32_t phy_advertise;
> + uint32_t phy_int;
> + uint32_t phy_int_mask;
> +} IMXFECState;
> +
> +static const VMStateDescription vmstate_imx_fec = {
> + .name = TYPE_IMX_FEC,
> + .version_id = 1,
> + .minimum_version_id = 1,
> + .fields = (VMStateField[]) {
> + VMSTATE_UINT32(irq_state, IMXFECState),
> + VMSTATE_UINT32(eir, IMXFECState),
> + VMSTATE_UINT32(eimr, IMXFECState),
> + VMSTATE_UINT32(rx_enabled, IMXFECState),
> + VMSTATE_UINT32(rx_descriptor, IMXFECState),
> + VMSTATE_UINT32(tx_descriptor, IMXFECState),
> + VMSTATE_UINT32(ecr, IMXFECState),
> + VMSTATE_UINT32(mmfr, IMXFECState),
> + VMSTATE_UINT32(mscr, IMXFECState),
> + VMSTATE_UINT32(mibc, IMXFECState),
> + VMSTATE_UINT32(rcr, IMXFECState),
> + VMSTATE_UINT32(tcr, IMXFECState),
> + VMSTATE_UINT32(tfwr, IMXFECState),
> + VMSTATE_UINT32(frsr, IMXFECState),
> + VMSTATE_UINT32(erdsr, IMXFECState),
> + VMSTATE_UINT32(etdsr, IMXFECState),
> + VMSTATE_UINT32(emrbr, IMXFECState),
> + VMSTATE_UINT32(miigsk_cfgr, IMXFECState),
> + VMSTATE_UINT32(miigsk_enr, IMXFECState),
> +
> + VMSTATE_UINT32(phy_status, IMXFECState),
> + VMSTATE_UINT32(phy_control, IMXFECState),
> + VMSTATE_UINT32(phy_advertise, IMXFECState),
> + VMSTATE_UINT32(phy_int, IMXFECState),
> + VMSTATE_UINT32(phy_int_mask, IMXFECState),
> +
> + VMSTATE_END_OF_LIST()
> + }
> +};
> +
> +#define PHY_INT_ENERGYON (1 << 7)
> +#define PHY_INT_AUTONEG_COMPLETE (1 << 6)
> +#define PHY_INT_FAULT (1 << 5)
> +#define PHY_INT_DOWN (1 << 4)
> +#define PHY_INT_AUTONEG_LP (1 << 3)
> +#define PHY_INT_PARFAULT (1 << 2)
> +#define PHY_INT_AUTONEG_PAGE (1 << 1)
> +
> +static void imx_fec_update(IMXFECState *s);
> +
> +/*
> + * The MII phy could raise a GPIO to the guest which in turn
> + * could be handled as an interrupt by the guest.
> + * For now we don't handle any GPIO/interrupt line, so the guest will
> + * have to poll for the PHY status.
> + */
> +static void phy_update_irq(IMXFECState *s)
> +{
> + imx_fec_update(s);
> +}
> +
> +static void phy_update_link(IMXFECState *s)
> +{
> + /* Autonegotiation status mirrors link status. */
> + if (qemu_get_queue(s->nic)->link_down) {
> + PHY_PRINTF("link is down\n");
> + s->phy_status &= ~0x0024;
Theres a lot of magic numbers in the phy code but they all from the
copy-pasted bits AFAIK.
> + s->phy_int |= PHY_INT_DOWN;
> + } else {
> + PHY_PRINTF("link is up\n");
> + s->phy_status |= 0x0024;
> + s->phy_int |= PHY_INT_ENERGYON;
> + s->phy_int |= PHY_INT_AUTONEG_COMPLETE;
> + }
> + phy_update_irq(s);
> +}
> +
> +static void imx_fec_set_link(NetClientState *nc)
> +{
> + phy_update_link(IMX_FEC(qemu_get_nic_opaque(nc)));
> +}
> +
> +static void phy_reset(IMXFECState *s)
> +{
> + s->phy_status = 0x7809;
> + s->phy_control = 0x3000;
> + s->phy_advertise = 0x01e1;
> + s->phy_int_mask = 0;
> + s->phy_int = 0;
> + phy_update_link(s);
> +}
> +
> +static uint32_t do_phy_read(IMXFECState *s, int reg)
> +{
> + uint32_t val;
> +
> + if (reg > 31) {
> + /* we only advertise one phy */
> + return 0;
> + }
> +
> + switch (reg) {
> + case 0: /* Basic Control */
> + val = s->phy_control;
> + break;
> + case 1: /* Basic Status */
> + val = s->phy_status;
> + break;
> + case 2: /* ID1 */
> + val = 0x0007;
> + break;
> + case 3: /* ID2 */
> + val = 0xc0d1;
> + break;
> + case 4: /* Auto-neg advertisement */
> + val = s->phy_advertise;
> + break;
> + case 5: /* Auto-neg Link Partner Ability */
> + val = 0x0f71;
> + break;
> + case 6: /* Auto-neg Expansion */
> + val = 1;
> + break;
> + case 29: /* Interrupt source. */
> + val = s->phy_int;
> + s->phy_int = 0;
> + phy_update_irq(s);
> + break;
> + case 30: /* Interrupt mask */
> + val = s->phy_int_mask;
> + break;
> + case 17:
> + case 18:
> + case 27:
> + case 31:
> + qemu_log_mask(LOG_UNIMP, "%s.phy[%s]: reg %d not implemented\n",
> + TYPE_IMX_FEC, __func__, reg);
> + val = 0;
> + break;
> + default:
> + hw_error("%s.phy[%s]: Bad address 0x%x\n", TYPE_IMX_FEC, __func__,
> + (int)reg);
Same as before with the I2C patch.
Regards,
Peter
> + val = 0;
> + break;
> + }
> +
> + PHY_PRINTF("read 0x%04x @ %d\n", val, reg);
> +
> + return val;
> +}
> +
> +static void do_phy_write(IMXFECState *s, int reg, uint32_t val)
> +{
> + PHY_PRINTF("write 0x%04x @ %d\n", val, reg);
> +
> + if (reg > 31) {
> + /* we only advertise one phy */
> + return;
> + }
> +
> + switch (reg) {
> + case 0: /* Basic Control */
> + if (val & 0x8000) {
> + phy_reset(s);
> + } else {
> + s->phy_control = val & 0x7980;
> + /* Complete autonegotiation immediately. */
> + if (val & 0x1000) {
> + s->phy_status |= 0x0020;
> + }
> + }
> + break;
> + case 4: /* Auto-neg advertisement */
> + s->phy_advertise = (val & 0x2d7f) | 0x80;
> + break;
> + case 17:
> + case 18:
> + case 27:
> + case 31:
> + qemu_log_mask(LOG_UNIMP, "%s.phy[%s]: reg %d not implemented\n",
> + TYPE_IMX_FEC, __func__, reg);
> + break;
> + case 30: /* Interrupt mask */
> + s->phy_int_mask = val & 0xff;
> + phy_update_irq(s);
> + break;
> + default:
> + hw_error("%s.phy[%s]: Bad address 0x%x\n", TYPE_IMX_FEC, __func__,
> + (int)reg);
> + break;
> + }
> +}
> +
> +#define FEC_INT_HB (1 << 31)
> +#define FEC_INT_BABR (1 << 30)
> +#define FEC_INT_BABT (1 << 29)
> +#define FEC_INT_GRA (1 << 28)
> +#define FEC_INT_TXF (1 << 27)
> +#define FEC_INT_TXB (1 << 26)
> +#define FEC_INT_RXF (1 << 25)
> +#define FEC_INT_RXB (1 << 24)
> +#define FEC_INT_MII (1 << 23)
> +#define FEC_INT_EBERR (1 << 22)
> +#define FEC_INT_LC (1 << 21)
> +#define FEC_INT_RL (1 << 20)
> +#define FEC_INT_UN (1 << 19)
> +
> +#define FEC_EN 2
> +#define FEC_RESET 1
> +
> +/* Buffer Descriptor. */
> +typedef struct {
> + uint16_t length;
> + uint16_t flags;
> + uint32_t data;
> +} IMXFECBufDesc;
> +
> +#define FEC_BD_R (1 << 15)
> +#define FEC_BD_E (1 << 15)
> +#define FEC_BD_O1 (1 << 14)
> +#define FEC_BD_W (1 << 13)
> +#define FEC_BD_O2 (1 << 12)
> +#define FEC_BD_L (1 << 11)
> +#define FEC_BD_TC (1 << 10)
> +#define FEC_BD_ABC (1 << 9)
> +#define FEC_BD_M (1 << 8)
> +#define FEC_BD_BC (1 << 7)
> +#define FEC_BD_MC (1 << 6)
> +#define FEC_BD_LG (1 << 5)
> +#define FEC_BD_NO (1 << 4)
> +#define FEC_BD_CR (1 << 2)
> +#define FEC_BD_OV (1 << 1)
> +#define FEC_BD_TR (1 << 0)
> +
> +static void imx_fec_read_bd(IMXFECBufDesc *bd, dma_addr_t addr)
> +{
> + dma_memory_read(&dma_context_memory, addr, bd, sizeof(*bd));
> +}
> +
> +static void imx_fec_write_bd(IMXFECBufDesc *bd, dma_addr_t addr)
> +{
> + dma_memory_write(&dma_context_memory, addr, bd, sizeof(*bd));
> +}
> +
> +static void imx_fec_update(IMXFECState *s)
> +{
> + uint32_t active;
> + uint32_t changed;
> +
> + active = s->eir & s->eimr;
> + changed = active ^ s->irq_state;
> + if (changed) {
> + qemu_set_irq(s->irq, active);
> + }
> + s->irq_state = active;
> +}
> +
> +static void imx_fec_do_tx(IMXFECState *s)
> +{
> + int frame_size = 0;
> + uint8_t frame[FEC_MAX_FRAME_SIZE];
> + uint8_t *ptr = frame;
> + uint32_t addr = s->tx_descriptor;
> +
> + while (1) {
> + IMXFECBufDesc bd;
> + int len;
> +
> + imx_fec_read_bd(&bd, addr);
> + FEC_PRINTF("tx_bd %x flags %04x len %d data %08x\n",
> + addr, bd.flags, bd.length, bd.data);
> + if ((bd.flags & FEC_BD_R) == 0) {
> + /* Run out of descriptors to transmit. */
> + break;
> + }
> + len = bd.length;
> + if (frame_size + len > FEC_MAX_FRAME_SIZE) {
> + len = FEC_MAX_FRAME_SIZE - frame_size;
> + s->eir |= FEC_INT_BABT;
> + }
> + dma_memory_read(&dma_context_memory, bd.data, ptr, len);
> + ptr += len;
> + frame_size += len;
> + if (bd.flags & FEC_BD_L) {
> + /* Last buffer in frame. */
> + qemu_send_packet(qemu_get_queue(s->nic), frame, len);
> + ptr = frame;
> + frame_size = 0;
> + s->eir |= FEC_INT_TXF;
> + }
> + s->eir |= FEC_INT_TXB;
> + bd.flags &= ~FEC_BD_R;
> + /* Write back the modified descriptor. */
> + imx_fec_write_bd(&bd, addr);
> + /* Advance to the next descriptor. */
> + if ((bd.flags & FEC_BD_W) != 0) {
> + addr = s->etdsr;
> + } else {
> + addr += 8;
> + }
> + }
> +
> + s->tx_descriptor = addr;
> +
> + imx_fec_update(s);
> +}
> +
> +static void imx_fec_enable_rx(IMXFECState *s)
> +{
> + IMXFECBufDesc bd;
> + uint32_t tmp;
> +
> + imx_fec_read_bd(&bd, s->rx_descriptor);
> +
> + tmp = ((bd.flags & FEC_BD_E) != 0);
> +
> + if (!tmp) {
> + FEC_PRINTF("RX buffer full\n");
> + } else if (!s->rx_enabled) {
> + qemu_flush_queued_packets(qemu_get_queue(s->nic));
> + }
> +
> + s->rx_enabled = tmp;
> +}
> +
> +static void imx_fec_reset(DeviceState *d)
> +{
> + IMXFECState *s = IMX_FEC(d);
> +
> + /* Reset the FEC */
> + s->eir = 0;
> + s->eimr = 0;
> + s->rx_enabled = 0;
> + s->ecr = 0;
> + s->mscr = 0;
> + s->mibc = 0xc0000000;
> + s->rcr = 0x05ee0001;
> + s->tcr = 0;
> + s->tfwr = 0;
> + s->frsr = 0x500;
> + s->miigsk_cfgr = 0;
> + s->miigsk_enr = 0x6;
> +
> + /* We also reset the PHY */
> + phy_reset(s);
> +}
> +
> +static uint64_t imx_fec_read(void *opaque, hwaddr addr, unsigned size)
> +{
> + IMXFECState *s = IMX_FEC(opaque);
> +
> + FEC_PRINTF("reading from @ 0x%03x\n", (int)addr);
> +
> + switch (addr & 0x3ff) {
> + case 0x004:
> + return s->eir;
> + case 0x008:
> + return s->eimr;
> + case 0x010:
> + return s->rx_enabled ? (1 << 24) : 0; /* RDAR */
> + case 0x014:
> + return 0; /* TDAR */
> + case 0x024:
> + return s->ecr;
> + case 0x040:
> + return s->mmfr;
> + case 0x044:
> + return s->mscr;
> + case 0x064:
> + return s->mibc; /* MIBC */
> + case 0x084:
> + return s->rcr;
> + case 0x0c4:
> + return s->tcr;
> + case 0x0e4: /* PALR */
> + return (s->conf.macaddr.a[0] << 24)
> + | (s->conf.macaddr.a[1] << 16)
> + | (s->conf.macaddr.a[2] << 8)
> + | s->conf.macaddr.a[3];
> + break;
> + case 0x0e8: /* PAUR */
> + return (s->conf.macaddr.a[4] << 24)
> + | (s->conf.macaddr.a[5] << 16)
> + | 0x8808;
> + case 0x0ec:
> + return 0x10000; /* OPD */
> + case 0x118:
> + return 0;
> + case 0x11c:
> + return 0;
> + case 0x120:
> + return 0;
> + case 0x124:
> + return 0;
> + case 0x144:
> + return s->tfwr;
> + case 0x14c:
> + return 0x600;
> + case 0x150:
> + return s->frsr;
> + case 0x180:
> + return s->erdsr;
> + case 0x184:
> + return s->etdsr;
> + case 0x188:
> + return s->emrbr;
> + case 0x300:
> + return s->miigsk_cfgr;
> + case 0x308:
> + return s->miigsk_enr;
> + default:
> + hw_error("%s[%s]: Bad address 0x%x\n", TYPE_IMX_FEC, __func__,
> + (int)addr);
> + return 0;
> + }
> +}
> +
> +static void imx_fec_write(void *opaque, hwaddr addr,
> + uint64_t value, unsigned size)
> +{
> + IMXFECState *s = IMX_FEC(opaque);
> +
> + FEC_PRINTF("writing 0x%08x @ 0x%03x\n", (int)value, (int)addr);
> +
> + switch (addr & 0x3ff) {
> + case 0x004: /* EIR */
> + s->eir &= ~value;
> + break;
> + case 0x008: /* EIMR */
> + s->eimr = value;
> + break;
> + case 0x010: /* RDAR */
> + if ((s->ecr & FEC_EN) && !s->rx_enabled) {
> + imx_fec_enable_rx(s);
> + }
> + break;
> + case 0x014: /* TDAR */
> + if (s->ecr & FEC_EN) {
> + imx_fec_do_tx(s);
> + }
> + break;
> + case 0x024: /* ECR */
> + s->ecr = value;
> + if (value & FEC_RESET) {
> + imx_fec_reset(DEVICE(s));
> + }
> + if ((s->ecr & FEC_EN) == 0) {
> + s->rx_enabled = 0;
> + }
> + break;
> + case 0x040: /* MMFR */
> + /* store the value */
> + s->mmfr = value;
> + if (extract32(value, 28, 1)) {
> + do_phy_write(s, extract32(value, 18, 9), extract32(value, 0,
> 16));
> + } else {
> + s->mmfr = do_phy_read(s, extract32(value, 18, 9));
> + }
> + /* raise the interrupt as the PHY operation is done */
> + s->eir |= FEC_INT_MII;
> + break;
> + case 0x044: /* MSCR */
> + s->mscr = value & 0xfe;
> + break;
> + case 0x064: /* MIBC */
> + /* TODO: Implement MIB. */
> + s->mibc = (value & 0x80000000) ? 0xc0000000 : 0;
> + break;
> + case 0x084: /* RCR */
> + s->rcr = value & 0x07ff003f;
> + /* TODO: Implement LOOP mode. */
> + break;
> + case 0x0c4: /* TCR */
> + /* We transmit immediately, so raise GRA immediately. */
> + s->tcr = value;
> + if (value & 1) {
> + s->eir |= FEC_INT_GRA;
> + }
> + break;
> + case 0x0e4: /* PALR */
> + s->conf.macaddr.a[0] = value >> 24;
> + s->conf.macaddr.a[1] = value >> 16;
> + s->conf.macaddr.a[2] = value >> 8;
> + s->conf.macaddr.a[3] = value;
> + break;
> + case 0x0e8: /* PAUR */
> + s->conf.macaddr.a[4] = value >> 24;
> + s->conf.macaddr.a[5] = value >> 16;
> + break;
> + case 0x0ec: /* OPDR */
> + break;
> + case 0x118: /* IAUR */
> + case 0x11c: /* IALR */
> + case 0x120: /* GAUR */
> + case 0x124: /* GALR */
> + /* TODO: implement MAC hash filtering. */
> + break;
> + case 0x144: /* TFWR */
> + s->tfwr = value & 3;
> + break;
> + case 0x14c: /* FRBR */
> + /* FRBR writes ignored. */
> + break;
> + case 0x150: /* FRSR */
> + s->frsr = (value & 0x3fc) | 0x400;
> + break;
> + case 0x180: /* ERDSR */
> + s->erdsr = value & ~3;
> + s->rx_descriptor = s->erdsr;
> + break;
> + case 0x184: /* ETDSR */
> + s->etdsr = value & ~3;
> + s->tx_descriptor = s->etdsr;
> + break;
> + case 0x188: /* EMRBR */
> + s->emrbr = value & 0x7f0;
> + break;
> + case 0x300: /* MIIGSK_CFGR */
> + s->miigsk_cfgr = value & 0x53;
> + break;
> + case 0x308: /* MIIGSK_ENR */
> + s->miigsk_enr = (value & 0x2) ? 0x6 : 0;
> + break;
> + default:
> + hw_error("%s[%s]: Bad address 0x%x\n", TYPE_IMX_FEC, __func__,
> + (int)addr);
> + break;
> + }
> +
> + imx_fec_update(s);
> +}
> +
> +static int imx_fec_can_receive(NetClientState *nc)
> +{
> + IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc));
> +
> + return s->rx_enabled;
> +}
> +
> +static ssize_t imx_fec_receive(NetClientState *nc, const uint8_t *buf,
> + size_t len)
> +{
> + IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc));
> + IMXFECBufDesc bd;
> + uint32_t flags = 0;
> + uint32_t addr;
> + uint32_t crc;
> + uint32_t buf_addr;
> + uint8_t *crc_ptr;
> + unsigned int buf_len;
> + size_t size = len;
> +
> + FEC_PRINTF("len %d\n", (int)size);
> +
> + if (!s->rx_enabled) {
> + qemu_log_mask(LOG_GUEST_ERROR, "%s[%s]: Unexpected packet\n",
> + TYPE_IMX_FEC, __func__);
> + return 0;
> + }
> + /* 4 bytes for the CRC. */
> + size += 4;
> + crc = cpu_to_be32(crc32(~0, buf, size));
> + crc_ptr = (uint8_t *) &crc;
> + /* Huge frames are truncted. */
> + if (size > FEC_MAX_FRAME_SIZE) {
> + size = FEC_MAX_FRAME_SIZE;
> + flags |= FEC_BD_TR | FEC_BD_LG;
> + }
> + /* Frames larger than the user limit just set error flags. */
> + if (size > (s->rcr >> 16)) {
> + flags |= FEC_BD_LG;
> + }
> + addr = s->rx_descriptor;
> + while (size > 0) {
> + imx_fec_read_bd(&bd, addr);
> + if ((bd.flags & FEC_BD_E) == 0) {
> + /* No descriptors available. Bail out. */
> + /*
> + * FIXME: This is wrong. We should probably either
> + * save the remainder for when more RX buffers are
> + * available, or flag an error.
> + */
> + qemu_log_mask(LOG_GUEST_ERROR, "%s[%s]: Lost end of frame\n",
> + TYPE_IMX_FEC, __func__);
> + break;
> + }
> + buf_len = (size <= s->emrbr) ? size : s->emrbr;
> + bd.length = buf_len;
> + size -= buf_len;
> + FEC_PRINTF("rx_bd %x length %d\n", addr, bd.length);
> + /* The last 4 bytes are the CRC. */
> + if (size < 4) {
> + buf_len += size - 4;
> + }
> + buf_addr = bd.data;
> + dma_memory_write(&dma_context_memory, buf_addr, buf, buf_len);
> + buf += buf_len;
> + if (size < 4) {
> + dma_memory_write(&dma_context_memory, buf_addr + buf_len,
> + crc_ptr, 4 - size);
> + crc_ptr += 4 - size;
> + }
> + bd.flags &= ~FEC_BD_E;
> + if (size == 0) {
> + /* Last buffer in frame. */
> + bd.flags |= flags | FEC_BD_L;
> + FEC_PRINTF("rx frame flags %04x\n", bd.flags);
> + s->eir |= FEC_INT_RXF;
> + } else {
> + s->eir |= FEC_INT_RXB;
> + }
> + imx_fec_write_bd(&bd, addr);
> + /* Advance to the next descriptor. */
> + if ((bd.flags & FEC_BD_W) != 0) {
> + addr = s->erdsr;
> + } else {
> + addr += 8;
> + }
> + }
> + s->rx_descriptor = addr;
> + imx_fec_enable_rx(s);
> + imx_fec_update(s);
> + return len;
> +}
> +
> +static const MemoryRegionOps imx_fec_ops = {
> + .read = imx_fec_read,
> + .write = imx_fec_write,
> + .valid.min_access_size = 4,
> + .valid.max_access_size = 4,
> + .endianness = DEVICE_NATIVE_ENDIAN,
> +};
> +
> +static void imx_fec_cleanup(NetClientState *nc)
> +{
> + IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc));
> +
> + s->nic = NULL;
> +}
> +
> +static NetClientInfo net_imx_fec_info = {
> + .type = NET_CLIENT_OPTIONS_KIND_NIC,
> + .size = sizeof(NICState),
> + .can_receive = imx_fec_can_receive,
> + .receive = imx_fec_receive,
> + .cleanup = imx_fec_cleanup,
> + .link_status_changed = imx_fec_set_link,
> +};
> +
> +static void imx_fec_realize(DeviceState *dev, Error **errp)
> +{
> + IMXFECState *s = IMX_FEC(dev);
> + SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
> +
> + memory_region_init_io(&s->iomem, &imx_fec_ops, s, TYPE_IMX_FEC, 0x400);
> + sysbus_init_mmio(sbd, &s->iomem);
> + sysbus_init_irq(sbd, &s->irq);
> + qemu_macaddr_default_if_unset(&s->conf.macaddr);
> +
> + s->conf.peers.ncs[0] = nd_table[0].netdev;
> +
> + s->nic = qemu_new_nic(&net_imx_fec_info, &s->conf,
> + object_get_typename(OBJECT(dev)), DEVICE(dev)->id,
> + s);
> + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
> +}
> +
> +static Property imx_fec_properties[] = {
> + DEFINE_NIC_PROPERTIES(IMXFECState, conf),
> + DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void imx_fec_class_init(ObjectClass *klass, void *data)
> +{
> + DeviceClass *dc = DEVICE_CLASS(klass);
> +
> + dc->vmsd = &vmstate_imx_fec;
> + dc->reset = imx_fec_reset;
> + dc->props = imx_fec_properties;
> + dc->realize = imx_fec_realize;
> +}
> +
> +static const TypeInfo imx_fec_info = {
> + .name = TYPE_IMX_FEC,
> + .parent = TYPE_SYS_BUS_DEVICE,
> + .instance_size = sizeof(IMXFECState),
> + .class_init = imx_fec_class_init,
> +};
> +
> +static void imx_fec_register_types(void)
> +{
> + type_register_static(&imx_fec_info);
> +}
> +
> +DeviceState *imx_fec_create(int nic, const hwaddr base, qemu_irq irq)
> +{
> + NICInfo *nd;
> + DeviceState *dev;
> + SysBusDevice *sbd;
> +
> + if (nic >= MAX_NICS) {
> + hw_error("Cannot assign nic %d: QEMU supports only %d ports\n",
> + nic, MAX_NICS);
> + }
> +
> + nd = &nd_table[nic];
> +
> + qemu_check_nic_model(nd, TYPE_IMX_FEC);
> + dev = qdev_create(NULL, TYPE_IMX_FEC);
> + qdev_set_nic_properties(dev, nd);
> + qdev_init_nofail(dev);
> + sbd = SYS_BUS_DEVICE(dev);
> + sysbus_mmio_map(sbd, 0, base);
> + sysbus_connect_irq(sbd, 0, irq);
> +
> + return dev;
> +};
> +
> +type_init(imx_fec_register_types)
> diff --git a/include/hw/arm/imx.h b/include/hw/arm/imx.h
> index ea9e093..d16c468 100644
> --- a/include/hw/arm/imx.h
> +++ b/include/hw/arm/imx.h
> @@ -23,12 +23,10 @@ typedef enum {
>
> uint32_t imx_clock_frequency(DeviceState *s, IMXClk clock);
>
> -void imx_timerp_create(const hwaddr addr,
> - qemu_irq irq,
> - DeviceState *ccm);
> -void imx_timerg_create(const hwaddr addr,
> - qemu_irq irq,
> - DeviceState *ccm);
> +void imx_timerp_create(const hwaddr addr, qemu_irq irq, DeviceState *ccm);
>
> +void imx_timerg_create(const hwaddr addr, qemu_irq irq, DeviceState *ccm);
> +
> +DeviceState *imx_fec_create(int nic, const hwaddr base, qemu_irq irq);
>
> #endif /* IMX_H */
> --
> 1.8.1.2
>
>
- [Qemu-devel] [PATCH v5 0/4] Add i.MX25 support through the 3DS evaluation board, Jean-Christophe DUBOIS, 2013/05/08
- [Qemu-devel] [PATCH v5 2/4] Add i.MX I2C controller emulator, Jean-Christophe DUBOIS, 2013/05/08
- [Qemu-devel] [PATCH v5 3/4] Add i.MX25 3DS evaluation board support., Jean-Christophe DUBOIS, 2013/05/08
- [Qemu-devel] [PATCH v5 4/4] Add qtest support for i.MX I2C device emulation., Jean-Christophe DUBOIS, 2013/05/08
- [Qemu-devel] [PATCH v5 1/4] Add i.MX FEC Ethernet emulator, Jean-Christophe DUBOIS, 2013/05/08
- Re: [Qemu-devel] [PATCH v5 1/4] Add i.MX FEC Ethernet emulator,
Peter Crosthwaite <=
- Re: [Qemu-devel] [PATCH v5 0/4] Add i.MX25 support through the 3DS evaluation board, Jean-Christophe DUBOIS, 2013/05/12