On Mon, Aug 10, 2015 at 3:02 PM, Jean-Christophe Dubois <j...@tribudubois.net> wrote: > This is based on mcf_fec.c FEC implementation for Coldfire > > * A generic PHY was added (borrowwed from LAN9118) > * The buffer management is also modified as buffers are > slightly different between Coldfire and i.MX > > Signed-off-by: Jean-Christophe Dubois <j...@tribudubois.net> > Reviewed-by: Peter Crosthwaite <crosthwaite.pe...@gmail.com>
Revert to: Reviewed-by: Peter Crosthwaite <peter.crosthwa...@xilinx.com> Regards, Peter > --- > > 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 > > Changes since v5: > * replace hw_error() with qemu_log_mask(LOG_GUEST_ERROR, ...) > * remove reformating of imx.h header file. > * remove unnecessary spaces. > > Changes since v6: > * port to new memory API > > Changes since v7: > * refactor to be used by SOC > > Changes since v8: > * no change > > Changes since v9: > * no change > > Changes since v10: > * no change > > Changes since v11: > * no change > > Changes since v12: > * no change > > Changes since v13: > * no change > > default-configs/arm-softmmu.mak | 1 + > hw/net/Makefile.objs | 1 + > hw/net/imx_fec.c | 709 > ++++++++++++++++++++++++++++++++++++++++ > include/hw/net/imx_fec.h | 113 +++++++ > 4 files changed, 824 insertions(+) > create mode 100644 hw/net/imx_fec.c > create mode 100644 include/hw/net/imx_fec.h > > diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak > index 47390db..5fa84c6 100644 > --- a/default-configs/arm-softmmu.mak > +++ b/default-configs/arm-softmmu.mak > @@ -28,6 +28,7 @@ CONFIG_SSI_M25P80=y > CONFIG_LAN9118=y > CONFIG_SMC91C111=y > CONFIG_ALLWINNER_EMAC=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 9880173..64d0449 100644 > --- a/hw/net/Makefile.objs > +++ b/hw/net/Makefile.objs > @@ -19,6 +19,7 @@ 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_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..725f3fa > --- /dev/null > +++ b/hw/net/imx_fec.c > @@ -0,0 +1,709 @@ > +/* > + * i.MX Fast Ethernet Controller emulation. > + * > + * Copyright (c) 2013 Jean-Christophe Dubois. <j...@tribudubois.net> > + * > + * 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 "hw/net/imx_fec.h" > +#include "sysemu/dma.h" > + > +/* For crc32 */ > +#include <zlib.h> > + > +#ifndef IMX_FEC_DEBUG > +#define IMX_FEC_DEBUG 0 > +#endif > + > +#ifndef IMX_PHY_DEBUG > +#define IMX_PHY_DEBUG 0 > +#endif > + > +#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 > + > +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 processor which in turn > + * could be handled as an interrpt by the OS. > + * For now we don't handle any GPIO/interrupt line, so the OS 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; > + 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: > + qemu_log_mask(LOG_GUEST_ERROR, "%s[%s]: Bad address at offset %d\n", > + TYPE_IMX_FEC, __func__, reg); > + 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 30: /* Interrupt mask */ > + s->phy_int_mask = val & 0xff; > + phy_update_irq(s); > + 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; > + default: > + qemu_log_mask(LOG_GUEST_ERROR, "%s.phy[%s]: Bad address at offset > %d\n", > + TYPE_IMX_FEC, __func__, reg); > + break; > + } > +} > + > +static void imx_fec_read_bd(IMXFECBufDesc *bd, dma_addr_t addr) > +{ > + dma_memory_read(&address_space_memory, addr, bd, sizeof(*bd)); > +} > + > +static void imx_fec_write_bd(IMXFECBufDesc *bd, dma_addr_t addr) > +{ > + dma_memory_write(&address_space_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(&address_space_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: > + qemu_log_mask(LOG_GUEST_ERROR, "%s[%s]: Bad address at offset %d\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: > + qemu_log_mask(LOG_GUEST_ERROR, "%s[%s]: Bad address at offset %d\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(&address_space_memory, buf_addr, buf, buf_len); > + buf += buf_len; > + if (size < 4) { > + dma_memory_write(&address_space_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, OBJECT(dev), &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); > +} > + > +type_init(imx_fec_register_types) > diff --git a/include/hw/net/imx_fec.h b/include/hw/net/imx_fec.h > new file mode 100644 > index 0000000..cbf8650 > --- /dev/null > +++ b/include/hw/net/imx_fec.h > @@ -0,0 +1,113 @@ > +/* > + * i.MX Fast Ethernet Controller emulation. > + * > + * Copyright (c) 2013 Jean-Christophe Dubois. <j...@tribudubois.net> > + * > + * 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/>. > + */ > + > +#ifndef IMX_FEC_H > +#define IMX_FEC_H > + > +#define TYPE_IMX_FEC "imx.fec" > +#define IMX_FEC(obj) OBJECT_CHECK(IMXFECState, (obj), TYPE_IMX_FEC) > + > +#include "hw/sysbus.h" > +#include "net/net.h" > + > +#define FEC_MAX_FRAME_SIZE 2032 > + > +#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) > + > +typedef struct IMXFECState { > + /*< private >*/ > + SysBusDevice parent_obj; > + > + /*< public >*/ > + 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; > + > +#endif > -- > 2.1.4 > >