On 8 May 2016 at 20:41, Jean-Christophe Dubois <j...@tribudubois.net> wrote: > The ENET device (present in i.MX6) is "derived" from FEC and backward > compatible with it. > > This patch adds the necessary support of the added feature in the ENET > device to allow Linux to use it (on supported processors). > > Signed-off-by: Jean-Christophe Dubois <j...@tribudubois.net> > ---
Thanks; I have one significant question below (about the two irq lines) and a handful of nits. > Changes since v1: > * Not present on v1 > > Changes since v2: > * Not present on v2 > > hw/arm/fsl-imx25.c | 3 + > hw/net/imx_fec.c | 706 > ++++++++++++++++++++++++++++++++++++++--------- > include/hw/net/imx_fec.h | 188 ++++++++++--- > 3 files changed, 735 insertions(+), 162 deletions(-) > > diff --git a/hw/arm/fsl-imx25.c b/hw/arm/fsl-imx25.c > index 2f878b9..ddb2b22 100644 > --- a/hw/arm/fsl-imx25.c > +++ b/hw/arm/fsl-imx25.c > @@ -191,6 +191,9 @@ static void fsl_imx25_realize(DeviceState *dev, Error > **errp) > } > > qdev_set_nic_properties(DEVICE(&s->fec), &nd_table[0]); > + > + object_property_set_bool(OBJECT(&s->fec), true, "is-fec", &error_abort); > + > object_property_set_bool(OBJECT(&s->fec), true, "realized", &err); > if (err) { > error_propagate(errp, err); > diff --git a/hw/net/imx_fec.c b/hw/net/imx_fec.c > index 20e6524..b9893ee 100644 > --- a/hw/net/imx_fec.c > +++ b/hw/net/imx_fec.c > @@ -24,6 +24,8 @@ > #include "qemu/osdep.h" > #include "hw/net/imx_fec.h" > #include "sysemu/dma.h" > +#include "net/checksum.h" > +#include "net/eth.h" > > /* For crc32 */ > #include <zlib.h> > @@ -52,10 +54,93 @@ > } \ > } while (0) > > -static const char *imx_fec_reg_name(IMXFECState *s, uint32_t index) > +static const char *imx_default_reg_name(IMXFECState *s, uint32_t index) > { > static char tmp[20]; > + sprintf(tmp, "index %d", index); > + return tmp; > +} > + > +static const char *imx_fec_reg_name(IMXFECState *s, uint32_t index) > +{ > + switch (index) { > + case ENET_FRBR: > + return "FRBR"; > + case ENET_FRSR: > + return "FRSR"; > + case ENET_MIIGSK_CFGR: > + return "MIIGSK_CFGR"; > + case ENET_MIIGSK_ENR: > + return "MIIGSK_ENR"; > + default: > + return imx_default_reg_name(s, index); > + } > +} > > +static const char *imx_enet_reg_name(IMXFECState *s, uint32_t index) > +{ > + switch (index) { > + case ENET_RSFL: > + return "RSFL"; > + case ENET_RSEM: > + return "RSEM"; > + case ENET_RAEM: > + return "RAEM"; > + case ENET_RAFL: > + return "RAFL"; > + case ENET_TSEM: > + return "TSEM"; > + case ENET_TAEM: > + return "TAEM"; > + case ENET_TAFL: > + return "TAFL"; > + case ENET_TIPG: > + return "TIPG"; > + case ENET_FTRL: > + return "FTRL"; > + case ENET_TACC: > + return "TACC"; > + case ENET_RACC: > + return "RACC"; > + case ENET_ATCR: > + return "ATCR"; > + case ENET_ATVR: > + return "ATVR"; > + case ENET_ATOFF: > + return "ATOFF"; > + case ENET_ATPER: > + return "ATPER"; > + case ENET_ATCOR: > + return "ATCOR"; > + case ENET_ATINC: > + return "ATINC"; > + case ENET_ATSTMP: > + return "ATSTMP"; > + case ENET_TGSR: > + return "TGSR"; > + case ENET_TCSR0: > + return "TCSR0"; > + case ENET_TCCR0: > + return "TCCR0"; > + case ENET_TCSR1: > + return "TCSR1"; > + case ENET_TCCR1: > + return "TCCR1"; > + case ENET_TCSR2: > + return "TCSR2"; > + case ENET_TCCR2: > + return "TCCR2"; > + case ENET_TCSR3: > + return "TCSR3"; > + case ENET_TCCR3: > + return "TCCR3"; > + default: > + return imx_default_reg_name(s, index); > + } > +} > + > +static const char *imx_eth_reg_name(IMXFECState *s, uint32_t index) > +{ > switch (index) { > case ENET_EIR: > return "EIR"; > @@ -99,21 +184,16 @@ static const char *imx_fec_reg_name(IMXFECState *s, > uint32_t index) > return "TDSR"; > case ENET_MRBR: > return "MRBR"; > - case ENET_FRBR: > - return "FRBR"; > - case ENET_FRSR: > - return "FRSR"; > - case ENET_MIIGSK_CFGR: > - return "MIIGSK_CFGR"; > - case ENET_MIIGSK_ENR: > - return "MIIGSK_ENR"; > default: > - sprintf(tmp, "index %d", index); > - return tmp; > + if (s->is_fec) { > + return imx_fec_reg_name(s, index); > + } else { > + return imx_enet_reg_name(s, index); > + } > } > } > > -static const VMStateDescription vmstate_imx_fec = { > +static const VMStateDescription vmstate_imx_eth = { > .name = TYPE_IMX_FEC, > .version_id = 2, > .minimum_version_id = 2, > @@ -127,6 +207,7 @@ static const VMStateDescription vmstate_imx_fec = { > VMSTATE_UINT32(phy_advertise, IMXFECState), > VMSTATE_UINT32(phy_int, IMXFECState), > VMSTATE_UINT32(phy_int_mask, IMXFECState), > + > VMSTATE_END_OF_LIST() Stray whitespace change. > } > }; > @@ -139,7 +220,7 @@ static const VMStateDescription vmstate_imx_fec = { > #define PHY_INT_PARFAULT (1 << 2) > #define PHY_INT_AUTONEG_PAGE (1 << 1) > > -static void imx_fec_update(IMXFECState *s); > +static void imx_eth_update(IMXFECState *s); > > /* > * The MII phy could raise a GPIO to the processor which in turn > @@ -149,7 +230,7 @@ static void imx_fec_update(IMXFECState *s); > */ > static void phy_update_irq(IMXFECState *s) > { > - imx_fec_update(s); > + imx_eth_update(s); > } > > static void phy_update_link(IMXFECState *s) > @@ -168,7 +249,7 @@ static void phy_update_link(IMXFECState *s) > phy_update_irq(s); > } > > -static void imx_fec_set_link(NetClientState *nc) > +static void imx_eth_set_link(NetClientState *nc) > { > phy_update_link(IMX_FEC(qemu_get_nic_opaque(nc))); > } > @@ -294,21 +375,33 @@ 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) > +static void imx_enet_read_bd(IMXENETBufDesc *bd, dma_addr_t addr) > +{ > + dma_memory_read(&address_space_memory, addr, bd, sizeof(*bd)); > +} > + > +static void imx_enet_write_bd(IMXENETBufDesc *bd, dma_addr_t addr) > +{ > + dma_memory_write(&address_space_memory, addr, bd, sizeof(*bd)); > +} > + > +static void imx_eth_update(IMXFECState *s) > { > if (s->regs[ENET_EIR] & s->regs[ENET_EIMR]) { > FEC_PRINTF("interupt raised\n"); > - qemu_set_irq(s->irq, 1); > + qemu_set_irq(s->irq[0], 1); > + qemu_set_irq(s->irq[1], 1); > } else { > FEC_PRINTF("interupt lowered\n"); > - qemu_set_irq(s->irq, 0); > + qemu_set_irq(s->irq[0], 0); > + qemu_set_irq(s->irq[1], 0); Does this device really have two IRQ outputs which always update in lockstep? What's their purpose? > } > } > > static void imx_fec_do_tx(IMXFECState *s) > { > int frame_size = 0; > - uint8_t frame[FEC_MAX_FRAME_SIZE]; > + uint8_t frame[ENET_MAX_FRAME_SIZE]; > uint8_t *ptr = frame; > uint32_t addr = s->tx_descriptor; > > @@ -318,32 +411,105 @@ static void imx_fec_do_tx(IMXFECState *s) > > 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) { > + addr, bd.flags, bd.length, bd.data); > + if ((bd.flags & ENET_BD_R) == 0) { > /* Run out of descriptors to transmit. */ > + FEC_PRINTF("tx_bd running out of descriptor to transmit\n"); "ran 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->regs[ENET_EIR] |= FEC_INT_BABT; > + if (frame_size + len > ENET_MAX_FRAME_SIZE) { > + len = ENET_MAX_FRAME_SIZE - frame_size; > + s->regs[ENET_EIR] |= ENET_INT_BABT; > } > dma_memory_read(&address_space_memory, bd.data, ptr, len); > ptr += len; > frame_size += len; > - if (bd.flags & FEC_BD_L) { > + if (bd.flags & ENET_BD_L) { > + FEC_PRINTF("sending %d bytes frame\n", len); > /* Last buffer in frame. */ > qemu_send_packet(qemu_get_queue(s->nic), frame, len); > ptr = frame; > frame_size = 0; > - s->regs[ENET_EIR] |= FEC_INT_TXF; > + s->regs[ENET_EIR] |= ENET_INT_TXF; > } > - s->regs[ENET_EIR] |= FEC_INT_TXB; > - bd.flags &= ~FEC_BD_R; > + s->regs[ENET_EIR] |= ENET_INT_TXB; > + bd.flags &= ~ENET_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) { > + if ((bd.flags & ENET_BD_W) != 0) { > + addr = s->regs[ENET_TDSR]; > + } else { > + addr += sizeof(bd); > + } > + } > + > + s->tx_descriptor = addr; > + > + imx_eth_update(s); > +} > + > +static void imx_enet_do_tx(IMXFECState *s) > +{ > + int frame_size = 0; > + uint8_t frame[ENET_MAX_FRAME_SIZE]; > + uint8_t *ptr = frame; > + uint32_t addr = s->tx_descriptor; > + > + while (1) { > + IMXENETBufDesc bd; > + int len; > + > + imx_enet_read_bd(&bd, addr); > + FEC_PRINTF("tx_bd %x flags %04x len %d data %08x option %04x " > + "status %04x\n", addr, bd.flags, bd.length, bd.data, > + bd.option, bd.status); > + if ((bd.flags & ENET_BD_R) == 0) { > + /* Run out of descriptors to transmit. */ > + break; > + } > + len = bd.length; > + if (frame_size + len > ENET_MAX_FRAME_SIZE) { > + len = ENET_MAX_FRAME_SIZE - frame_size; > + s->regs[ENET_EIR] |= ENET_INT_BABT; > + } > + dma_memory_read(&address_space_memory, bd.data, ptr, len); > + ptr += len; > + frame_size += len; > + if (bd.flags & ENET_BD_L) { > + if (bd.option & ENET_BD_PINS) { > + struct ip_header *ip_hd = PKT_GET_IP_HDR(frame); > + if (IP_HEADER_VERSION(ip_hd) == 4) { > + net_checksum_calculate(frame, frame_size); > + } > + } > + if (bd.option & ENET_BD_IINS) { > + struct ip_header *ip_hd = PKT_GET_IP_HDR(frame); > + /* We compute checksum only for IPv4 frames */ > + if (IP_HEADER_VERSION(ip_hd) == 4) { > + uint16_t csum; > + ip_hd->ip_sum = 0; > + csum = net_raw_checksum((uint8_t *)ip_hd, > sizeof(*ip_hd)); > + ip_hd->ip_sum = cpu_to_be16(csum); > + } > + } > + /* Last buffer in frame. */ > + qemu_send_packet(qemu_get_queue(s->nic), frame, len); > + ptr = frame; > + frame_size = 0; > + if (bd.option & ENET_BD_TX_INT) { > + s->regs[ENET_EIR] |= ENET_INT_TXF; > + } > + } > + if (bd.option & ENET_BD_TX_INT) { > + s->regs[ENET_EIR] |= ENET_INT_TXB; > + } > + bd.flags &= ~ENET_BD_R; > + /* Write back the modified descriptor. */ > + imx_enet_write_bd(&bd, addr); > + /* Advance to the next descriptor. */ > + if ((bd.flags & ENET_BD_W) != 0) { > addr = s->regs[ENET_TDSR]; > } else { > addr += sizeof(bd); > @@ -352,17 +518,26 @@ static void imx_fec_do_tx(IMXFECState *s) > > s->tx_descriptor = addr; > > - imx_fec_update(s); > + imx_eth_update(s); > +} > + > +static void imx_eth_do_tx(IMXFECState *s) > +{ > + if (!s->is_fec && (s->regs[ENET_ECR] & ENET_ECR_EN1588)) { > + imx_enet_do_tx(s); > + } else { > + imx_fec_do_tx(s); > + } > } > > -static void imx_fec_enable_rx(IMXFECState *s) > +static void imx_eth_enable_rx(IMXFECState *s) > { > IMXFECBufDesc bd; > bool tmp; > > imx_fec_read_bd(&bd, s->rx_descriptor); > > - tmp = ((bd.flags & FEC_BD_E) != 0); > + tmp = ((bd.flags & ENET_BD_E) != 0); > > if (!tmp) { > FEC_PRINTF("RX buffer full\n"); > @@ -373,11 +548,11 @@ static void imx_fec_enable_rx(IMXFECState *s) > s->regs[ENET_RDAR] = tmp ? ENET_RDAR_RDAR : 0; > } > > -static void imx_fec_reset(DeviceState *d) > +static void imx_eth_reset(DeviceState *d) > { > IMXFECState *s = IMX_FEC(d); > > - /* Reset the FEC */ > + /* Reset the Device */ > memset(s->regs, 0, sizeof(s->regs)); > s->regs[ENET_ECR] = 0xf0000000; > s->regs[ENET_MIBC] = 0xc0000000; > @@ -392,19 +567,85 @@ static void imx_fec_reset(DeviceState *d) > | (s->conf.macaddr.a[5] << 16) > | 0x8808; > > - s->regs[ENET_FRBR] = 0x00000600; > - s->regs[ENET_FRSR] = 0x00000500; > - s->regs[ENET_MIIGSK_ENR] = 0x00000006; > + if (s->is_fec) { > + s->regs[ENET_FRBR] = 0x00000600; > + s->regs[ENET_FRSR] = 0x00000500; > + s->regs[ENET_MIIGSK_ENR] = 0x00000006; > + } else { > + s->regs[ENET_RAEM] = 0x00000004; > + s->regs[ENET_RAFL] = 0x00000004; > + s->regs[ENET_TAEM] = 0x00000004; > + s->regs[ENET_TAFL] = 0x00000008; > + s->regs[ENET_TIPG] = 0x0000000c; > + s->regs[ENET_FTRL] = 0x000007ff; > + s->regs[ENET_ATPER] = 0x3b9aca00; > + } > > /* We also reset the PHY */ > phy_reset(s); > } > > -static uint64_t imx_fec_read(void *opaque, hwaddr addr, unsigned size) > +static uint32_t imx_default_read(IMXFECState *s, uint32_t index) > +{ > + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" > + PRIx32 "\n", TYPE_IMX_FEC, __func__, index * 4); > + return 0; > +} > + > +static uint32_t imx_fec_read(IMXFECState *s, uint32_t index) > +{ > + switch (index) { > + case ENET_FRBR: > + case ENET_FRSR: > + case ENET_MIIGSK_CFGR: > + case ENET_MIIGSK_ENR: > + return s->regs[index]; > + default: > + return imx_default_read(s, index); > + } > +} > + > +static uint32_t imx_enet_read(IMXFECState *s, uint32_t index) > +{ > + switch (index) { > + case ENET_RSFL: > + case ENET_RSEM: > + case ENET_RAEM: > + case ENET_RAFL: > + case ENET_TSEM: > + case ENET_TAEM: > + case ENET_TAFL: > + case ENET_TIPG: > + case ENET_FTRL: > + case ENET_TACC: > + case ENET_RACC: > + case ENET_ATCR: > + case ENET_ATVR: > + case ENET_ATOFF: > + case ENET_ATPER: > + case ENET_ATCOR: > + case ENET_ATINC: > + case ENET_ATSTMP: > + case ENET_TGSR: > + case ENET_TCSR0: > + case ENET_TCCR0: > + case ENET_TCSR1: > + case ENET_TCCR1: > + case ENET_TCSR2: > + case ENET_TCCR2: > + case ENET_TCSR3: > + case ENET_TCCR3: > + return s->regs[index]; > + default: > + return imx_default_read(s, index); > + } > +} > + > +static uint64_t imx_eth_read(void *opaque, hwaddr offset, unsigned size) > { > uint32_t value = 0; > IMXFECState *s = IMX_FEC(opaque); > - uint32_t index = addr >> 2; > + uint32_t index = offset >> 2; > > switch (index) { > case ENET_EIR: > @@ -428,32 +669,126 @@ static uint64_t imx_fec_read(void *opaque, hwaddr > addr, unsigned size) > case ENET_RDSR: > case ENET_TDSR: > case ENET_MRBR: > - case ENET_FRBR: > - case ENET_FRSR: > - case ENET_MIIGSK_CFGR: > - case ENET_MIIGSK_ENR: > value = s->regs[index]; > break; > default: > - qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" > - PRIx32 "\n", TYPE_IMX_FEC, __func__, index * 4); > + if (s->is_fec) { > + value = imx_fec_read(s, index); > + } else { > + value = imx_enet_read(s, index); > + } > break; > } > > - FEC_PRINTF("reg[%s] => 0x%" PRIx32 "\n", imx_fec_reg_name(s, index), > + FEC_PRINTF("reg[%s] => 0x%" PRIx32 "\n", imx_eth_reg_name(s, index), > value); > > return value; > } > > -static void imx_fec_write(void *opaque, hwaddr addr, > - uint64_t value, unsigned size) > +static void imx_default_write(IMXFECState *s, uint32_t index, uint32_t value) > +{ > + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%" > + PRIx32 "\n", TYPE_IMX_FEC, __func__, index * 4); > + return; > +} > + > +static void imx_fec_write(IMXFECState *s, uint32_t index, uint32_t value) > +{ > + switch (index) { > + case ENET_FRBR: > + /* FRBR is read only */ > + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Register FRBR is read > only\n", > + TYPE_IMX_FEC, __func__); > + break; > + case ENET_FRSR: > + s->regs[index] = (value & 0x000003fc) | 0x00000400; > + break; > + case ENET_MIIGSK_CFGR: > + s->regs[index] = value & 0x00000053; > + break; > + case ENET_MIIGSK_ENR: > + s->regs[index] = (value & 0x00000002) ? 0x00000006 : 0; > + break; > + default: > + imx_default_write(s, index, value); > + break; > + } > +} > + > +static void imx_enet_write(IMXFECState *s, uint32_t index, uint32_t value) > +{ > + switch (index) { > + case ENET_RSFL: > + case ENET_RSEM: > + case ENET_RAEM: > + case ENET_RAFL: > + case ENET_TSEM: > + case ENET_TAEM: > + case ENET_TAFL: > + s->regs[index] = value & 0x000001ff; > + break; > + case ENET_TIPG: > + s->regs[index] = value & 0x0000001f; > + break; > + case ENET_FTRL: > + s->regs[index] = value & 0x00003fff; > + break; > + case ENET_TACC: > + s->regs[index] = value & 0x00000019; > + break; > + case ENET_RACC: > + s->regs[index] = value & 0x000000C7; > + break; > + case ENET_ATCR: > + s->regs[index] = value & 0x00002a9d; > + break; > + case ENET_ATVR: > + case ENET_ATOFF: > + case ENET_ATPER: > + s->regs[index] = value; > + break; > + case ENET_ATSTMP: > + /* ATSTMP is read only */ > + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Register ATSTMP is read > only\n", > + TYPE_IMX_FEC, __func__); > + break; > + case ENET_ATCOR: > + s->regs[index] = value & 0x7fffffff; > + break; > + case ENET_ATINC: > + s->regs[index] = value & 0x00007f7f; > + break; > + case ENET_TGSR: > + /* implement clear timer flag */ > + value = value & 0x0000000f; > + break; > + case ENET_TCSR0: > + case ENET_TCSR1: > + case ENET_TCSR2: > + case ENET_TCSR3: > + value = value & 0x000000fd; > + break; > + case ENET_TCCR0: > + case ENET_TCCR1: > + case ENET_TCCR2: > + case ENET_TCCR3: > + s->regs[index] = value; > + break; > + default: > + imx_default_write(s, index, value); > + break; > + } > +} > + > +static void imx_eth_write(void *opaque, hwaddr offset, uint64_t value, > + unsigned size) > { > IMXFECState *s = IMX_FEC(opaque); > - uint32_t index = addr >> 2; > + uint32_t index = offset >> 2; > > - FEC_PRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx_fec_reg_name(s, index), > - (uint32_t)value); > + FEC_PRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx_eth_reg_name(s, index), > + (uint32_t)value); > > switch (index) { > case ENET_EIR: > @@ -465,26 +800,26 @@ static void imx_fec_write(void *opaque, hwaddr addr, > case ENET_RDAR: > value = ENET_RDAR_RDAR; > s->regs[index] = value; > - if ((s->regs[ENET_ECR] & FEC_EN) && !s->regs[ENET_RDAR]) { > - imx_fec_enable_rx(s); > + if ((s->regs[ENET_ECR] & ENET_ECR_ETHEREN) && !s->regs[ENET_RDAR]) { > + imx_eth_enable_rx(s); > } > break; > case ENET_TDAR: > /* TDAR bit is always writen to 1 whatever the value writen to reg */ > value = ENET_TDAR_TDAR; > s->regs[index] = value; > - if (s->regs[ENET_ECR] & FEC_EN) { > - imx_fec_do_tx(s); > + if (s->regs[ENET_ECR] & ENET_ECR_ETHEREN) { > + imx_eth_do_tx(s); > } > break; > case ENET_ECR: > - if (value & FEC_RESET) { > - value &= ~FEC_EN; > + if (value & ENET_ECR_RESET) { > + value &= ~ENET_ECR_ETHEREN; > s->regs[ENET_RDAR] = 0; > s->regs[ENET_TDAR] = 0; > } > s->regs[index] = value; > - if ((s->regs[ENET_ECR] & FEC_EN) == 0) { > + if ((s->regs[ENET_ECR] & ENET_ECR_ETHEREN) == 0) { > s->regs[ENET_RDAR] = 0; > } > break; > @@ -500,7 +835,7 @@ static void imx_fec_write(void *opaque, hwaddr addr, > do_phy_write(s, extract32(value, 18, 10), extract32(value, 0, > 16)); > } > /* raise the interrupt as the PHY operation is done */ > - s->regs[ENET_EIR] |= FEC_INT_MII; > + s->regs[ENET_EIR] |= ENET_INT_MII; > break; > case ENET_MSCR: > s->regs[index] = value & 0xfe; > @@ -517,7 +852,7 @@ static void imx_fec_write(void *opaque, hwaddr addr, > /* We transmit immediately, so raise GRA immediately. */ > s->regs[index] = value; > if (value & 1) { > - s->regs[ENET_EIR] |= FEC_INT_GRA; > + s->regs[ENET_EIR] |= ENET_INT_GRA; > } > break; > case ENET_PALR: > @@ -542,46 +877,49 @@ static void imx_fec_write(void *opaque, hwaddr addr, > /* TODO: implement MAC hash filtering. */ > break; > case ENET_TFWR: > - s->regs[index] = value & 0x00000003; > - break; > - case ENET_FRBR: > - /* FRBR is read only */ > - qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Register FRBR is read > only\n", > - TYPE_IMX_FEC, __func__); > - break; > - case ENET_FRSR: > - s->regs[index] = (value & 0x000003fc) | 0x00000400; > + if (s->is_fec) { > + s->regs[index] = value & 0x00000003; > + } else { > + s->regs[index] = value & 0x0000013f; > + } > break; > case ENET_RDSR: > - s->regs[index] = value & ~3; > + if (s->is_fec) { > + s->regs[index] = value & ~3; > + } else { > + s->regs[index] = value & ~7; > + } > s->rx_descriptor = s->regs[index]; > break; > case ENET_TDSR: > - s->regs[index] = value & ~3; > + if (s->is_fec) { > + s->regs[index] = value & ~3; > + } else { > + s->regs[index] = value & ~7; > + } > s->tx_descriptor = s->regs[index]; > break; > case ENET_MRBR: > - s->regs[index] = value & 0x000007f0; > - break; > - case ENET_MIIGSK_CFGR: > - s->regs[index] = value & 0x00000053; > - break; > - case ENET_MIIGSK_ENR: > - s->regs[index] = (value & 0x00000002) ? 0x00000006 : 0; > + s->regs[index] = value & 0x00003ff0; > break; > default: > - qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%" > - PRIx32 "\n", TYPE_IMX_FEC, __func__, index * 4); > - break; > + if (s->is_fec) { > + imx_fec_write(s, index, value); > + } else { > + imx_enet_write(s, index, value); > + } > + return; > } > > - imx_fec_update(s); > + imx_eth_update(s); > } > > -static int imx_fec_can_receive(NetClientState *nc) > +static int imx_eth_can_receive(NetClientState *nc) > { > IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc)); > > + FEC_PRINTF("\n"); > + > return s->regs[ENET_RDAR] ? 1 : 0; > } > > @@ -612,20 +950,20 @@ static ssize_t imx_fec_receive(NetClientState *nc, > const uint8_t *buf, > 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; > + if (size > ENET_MAX_FRAME_SIZE) { > + size = ENET_MAX_FRAME_SIZE; > + flags |= ENET_BD_TR | ENET_BD_LG; > } > > /* Frames larger than the user limit just set error flags. */ > if (size > (s->regs[ENET_RCR] >> 16)) { > - flags |= FEC_BD_LG; > + flags |= ENET_BD_LG; > } > > addr = s->rx_descriptor; > while (size > 0) { > imx_fec_read_bd(&bd, addr); > - if ((bd.flags & FEC_BD_E) == 0) { > + if ((bd.flags & ENET_BD_E) == 0) { > /* No descriptors available. Bail out. */ > /* > * FIXME: This is wrong. We should probably either > @@ -654,99 +992,213 @@ static ssize_t imx_fec_receive(NetClientState *nc, > const uint8_t *buf, > crc_ptr, 4 - size); > crc_ptr += 4 - size; > } > - bd.flags &= ~FEC_BD_E; > + bd.flags &= ~ENET_BD_E; > if (size == 0) { > /* Last buffer in frame. */ > - bd.flags |= flags | FEC_BD_L; > + bd.flags |= flags | ENET_BD_L; > FEC_PRINTF("rx frame flags %04x\n", bd.flags); > - s->regs[ENET_EIR] |= FEC_INT_RXF; > + s->regs[ENET_EIR] |= ENET_INT_RXF; > } else { > - s->regs[ENET_EIR] |= FEC_INT_RXB; > + s->regs[ENET_EIR] |= ENET_INT_RXB; > } > imx_fec_write_bd(&bd, addr); > /* Advance to the next descriptor. */ > - if ((bd.flags & FEC_BD_W) != 0) { > + if ((bd.flags & ENET_BD_W) != 0) { > + addr = s->regs[ENET_RDSR]; > + } else { > + addr += sizeof(bd); > + } > + } > + s->rx_descriptor = addr; > + imx_eth_enable_rx(s); > + imx_eth_update(s); > + return len; > +} > + > +static ssize_t imx_enet_receive(NetClientState *nc, const uint8_t *buf, > + size_t len) > +{ > + IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc)); > + IMXENETBufDesc 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->regs[ENET_RDAR]) { > + 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. */ "truncated" > + if (size > ENET_MAX_FRAME_SIZE) { > + size = ENET_MAX_FRAME_SIZE; > + flags |= ENET_BD_TR | ENET_BD_LG; > + } > + > + /* Frames larger than the user limit just set error flags. */ > + if (size > (s->regs[ENET_RCR] >> 16)) { > + flags |= ENET_BD_LG; > + } > + > + addr = s->rx_descriptor; > + while (size > 0) { > + imx_enet_read_bd(&bd, addr); > + if ((bd.flags & ENET_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->regs[ENET_MRBR]) ? size : s->regs[ENET_MRBR]; > + bd.length = buf_len; > + size -= buf_len; > + > + FEC_PRINTF("rx_bd 0x%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 &= ~ENET_BD_E; > + if (size == 0) { > + /* Last buffer in frame. */ > + bd.flags |= flags | ENET_BD_L; > + FEC_PRINTF("rx frame flags %04x\n", bd.flags); > + if (bd.option & ENET_BD_RX_INT) { > + s->regs[ENET_EIR] |= ENET_INT_RXF; > + } > + } else { > + if (bd.option & ENET_BD_RX_INT) { > + s->regs[ENET_EIR] |= ENET_INT_RXB; > + } > + } > + imx_enet_write_bd(&bd, addr); > + /* Advance to the next descriptor. */ > + if ((bd.flags & ENET_BD_W) != 0) { > addr = s->regs[ENET_RDSR]; > } else { > addr += sizeof(bd); > } > } > s->rx_descriptor = addr; > - imx_fec_enable_rx(s); > - imx_fec_update(s); > + imx_eth_enable_rx(s); > + imx_eth_update(s); > return len; > } > > -static const MemoryRegionOps imx_fec_ops = { > - .read = imx_fec_read, > - .write = imx_fec_write, > +static ssize_t imx_eth_receive(NetClientState *nc, const uint8_t *buf, > + size_t len) > +{ > + IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc)); > + > + if (!s->is_fec && (s->regs[ENET_ECR] & ENET_ECR_EN1588)) { > + return imx_enet_receive(nc, buf, len); > + } else { > + return imx_fec_receive(nc, buf, len); > + } > +} > + > +static const MemoryRegionOps imx_eth_ops = { > + .read = imx_eth_read, > + .write = imx_eth_write, > .valid.min_access_size = 4, > .valid.max_access_size = 4, > - .endianness = DEVICE_NATIVE_ENDIAN, > + .endianness = DEVICE_NATIVE_ENDIAN, > }; > > -static void imx_fec_cleanup(NetClientState *nc) > +static void imx_eth_cleanup(NetClientState *nc) > { > IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc)); > > + FEC_PRINTF("\n"); I guess it's only debug printing, but why print a blank line here? > + > 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 NetClientInfo imx_eth_net_info = { > + .type = NET_CLIENT_OPTIONS_KIND_NIC, > + .size = sizeof(NICState), > + .can_receive = imx_eth_can_receive, > + .receive = imx_eth_receive, > + .cleanup = imx_eth_cleanup, > + .link_status_changed = imx_eth_set_link, > }; > > > -static void imx_fec_realize(DeviceState *dev, Error **errp) > +static void imx_eth_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, > + memory_region_init_io(&s->iomem, OBJECT(dev), &imx_eth_ops, s, > TYPE_IMX_FEC, 0x400); > sysbus_init_mmio(sbd, &s->iomem); > - sysbus_init_irq(sbd, &s->irq); > + sysbus_init_irq(sbd, &s->irq[0]); > + sysbus_init_irq(sbd, &s->irq[1]); > + > 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); > + s->nic = qemu_new_nic(&imx_eth_net_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[] = { > +static Property imx_eth_properties[] = { > DEFINE_NIC_PROPERTIES(IMXFECState, conf), > + DEFINE_PROP_BOOL("is-fec", IMXFECState, is_fec, false), > DEFINE_PROP_END_OF_LIST(), > }; > > -static void imx_fec_class_init(ObjectClass *klass, void *data) > +static void imx_eth_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; > - dc->desc = "i.MX FEC Ethernet Controller"; > + dc->vmsd = &vmstate_imx_eth; > + dc->reset = imx_eth_reset; > + dc->props = imx_eth_properties; > + dc->realize = imx_eth_realize; > + dc->desc = "i.MX FEC/ENET Ethernet Controller"; > } > > -static const TypeInfo imx_fec_info = { > - .name = TYPE_IMX_FEC, > - .parent = TYPE_SYS_BUS_DEVICE, > +static const TypeInfo imx_eth_info = { > + .name = TYPE_IMX_FEC, > + .parent = TYPE_SYS_BUS_DEVICE, > .instance_size = sizeof(IMXFECState), > - .class_init = imx_fec_class_init, > + .class_init = imx_eth_class_init, > }; > > -static void imx_fec_register_types(void) > +static void imx_eth_register_types(void) > { > - type_register_static(&imx_fec_info); > + type_register_static(&imx_eth_info); > } > > -type_init(imx_fec_register_types) > +type_init(imx_eth_register_types) > diff --git a/include/hw/net/imx_fec.h b/include/hw/net/imx_fec.h > index 709f8a0..176716b 100644 > --- a/include/hw/net/imx_fec.h > +++ b/include/hw/net/imx_fec.h > @@ -1,5 +1,5 @@ > /* > - * i.MX Fast Ethernet Controller emulation. > + * i.MX FEC/ENET Ethernet Controller emulation. > * > * Copyright (c) 2013 Jean-Christophe Dubois. <j...@tribudubois.net> > * > @@ -53,25 +53,57 @@ > #define ENET_RDSR 96 > #define ENET_TDSR 97 > #define ENET_MRBR 98 > +#define ENET_RSFL 100 > +#define ENET_RSEM 101 > +#define ENET_RAEM 102 > +#define ENET_RAFL 103 > +#define ENET_TSEM 104 > +#define ENET_TAEM 105 > +#define ENET_TAFL 106 > +#define ENET_TIPG 107 > +#define ENET_FTRL 108 > +#define ENET_TACC 112 > +#define ENET_RACC 113 > #define ENET_MIIGSK_CFGR 192 > #define ENET_MIIGSK_ENR 194 > +#define ENET_ATCR 256 > +#define ENET_ATVR 257 > +#define ENET_ATOFF 258 > +#define ENET_ATPER 259 > +#define ENET_ATCOR 260 > +#define ENET_ATINC 261 > +#define ENET_ATSTMP 262 > +#define ENET_TGSR 385 > +#define ENET_TCSR0 386 > +#define ENET_TCCR0 387 > +#define ENET_TCSR1 388 > +#define ENET_TCCR1 389 > +#define ENET_TCSR2 390 > +#define ENET_TCCR2 391 > +#define ENET_TCSR3 392 > +#define ENET_TCCR3 393 > #define ENET_MAX 400 > > -#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 ENET_MAX_FRAME_SIZE 2032 > + > +/* EIR and EIMR */ > +#define ENET_INT_HB (1 << 31) > +#define ENET_INT_BABR (1 << 30) > +#define ENET_INT_BABT (1 << 29) > +#define ENET_INT_GRA (1 << 28) > +#define ENET_INT_TXF (1 << 27) > +#define ENET_INT_TXB (1 << 26) > +#define ENET_INT_RXF (1 << 25) > +#define ENET_INT_RXB (1 << 24) > +#define ENET_INT_MII (1 << 23) > +#define ENET_INT_EBERR (1 << 22) > +#define ENET_INT_LC (1 << 21) > +#define ENET_INT_RL (1 << 20) > +#define ENET_INT_UN (1 << 19) > +#define ENET_INT_PLR (1 << 18) > +#define ENET_INT_WAKEUP (1 << 17) > +#define ENET_INT_TS_AVAIL (1 << 16) > +#define ENET_INT_TS_TIMER (1 << 15) > > /* RDAR */ > #define ENET_RDAR_RDAR (1 << 24) > @@ -79,8 +111,54 @@ > /* TDAR */ > #define ENET_TDAR_TDAR (1 << 24) > > -#define FEC_EN (1 << 1) > -#define FEC_RESET (1 << 0) > +/* ECR */ > +#define ENET_ECR_RESET (1 << 0) > +#define ENET_ECR_ETHEREN (1 << 1) > +#define ENET_ECR_MAGICEN (1 << 2) > +#define ENET_ECR_SLEEP (1 << 3) > +#define ENET_ECR_EN1588 (1 << 4) > +#define ENET_ECR_SPEED (1 << 5) > +#define ENET_ECR_DBGEN (1 << 6) > +#define ENET_ECR_STOPEN (1 << 7) > +#define ENET_ECR_DSBWP (1 << 8) > + > +/* MIBC */ > +#define ENET_MIBC_MIB_DIS (1 << 31) > +#define ENET_MIBC_MIB_IDLE (1 << 30) > +#define ENET_MIBC_MIB_CLEAR (1 << 29) > + > +/* RCR */ > +#define ENET_RCR_LOOP (1 << 0) > +#define ENET_RCR_DRT (1 << 1) > +#define ENET_RCR_MII_MODE (1 << 2) > +#define ENET_RCR_PROM (1 << 3) > +#define ENET_RCR_BC_REJ (1 << 4) > +#define ENET_RCR_FCE (1 << 5) > +#define ENET_RCR_RGMII_EN (1 << 6) > +#define ENET_RCR_RMII_MODE (1 << 8) > +#define ENET_RCR_RMII_10T (1 << 9) > +#define ENET_RCR_PADEN (1 << 12) > +#define ENET_RCR_PAUFWD (1 << 13) > +#define ENET_RCR_CRCFWD (1 << 14) > +#define ENET_RCR_CFEN (1 << 15) > +#define ENET_RCR_MAX_FL_SHIFT (16) > +#define ENET_RCR_MAX_FL_LENGTH (14) > +#define ENET_RCR_NLC (1 << 30) > +#define ENET_RCR_GRS (1 << 31) > + > +/* TCR */ > +#define ENET_TCR_GTS (1 << 0) > +#define ENET_TCR_FDEN (1 << 2) > +#define ENET_TCR_TFC_PAUSE (1 << 3) > +#define ENET_TCR_RFC_PAUSE (1 << 4) > +#define ENET_TCR_ADDSEL_SHIFT (5) > +#define ENET_TCR_ADDSEL_LENGTH (3) > +#define ENET_TCR_CRCFWD (1 << 9) > + > +/* RDSR */ > +#define ENET_TWFR_TFWR_SHIFT (0) > +#define ENET_TWFR_TFWR_LENGTH (6) > +#define ENET_TWFR_STRFWD (1 << 8) > > /* Buffer Descriptor. */ > typedef struct { > @@ -89,22 +167,60 @@ typedef struct { > 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) > +#define ENET_BD_R (1 << 15) > +#define ENET_BD_E (1 << 15) > +#define ENET_BD_O1 (1 << 14) > +#define ENET_BD_W (1 << 13) > +#define ENET_BD_O2 (1 << 12) > +#define ENET_BD_L (1 << 11) > +#define ENET_BD_TC (1 << 10) > +#define ENET_BD_ABC (1 << 9) > +#define ENET_BD_M (1 << 8) > +#define ENET_BD_BC (1 << 7) > +#define ENET_BD_MC (1 << 6) > +#define ENET_BD_LG (1 << 5) > +#define ENET_BD_NO (1 << 4) > +#define ENET_BD_CR (1 << 2) > +#define ENET_BD_OV (1 << 1) > +#define ENET_BD_TR (1 << 0) > + > +typedef struct { > + uint16_t length; > + uint16_t flags; > + uint32_t data; > + uint16_t status; > + uint16_t option; > + uint16_t checksum; > + uint16_t head_proto; > + uint32_t last_buffer; > + uint32_t timestamp; > + uint32_t reserved[2]; > +} IMXENETBufDesc; > + > +#define ENET_BD_ME (1 << 15) > +#define ENET_BD_TX_INT (1 << 14) > +#define ENET_BD_TS (1 << 13) > +#define ENET_BD_PINS (1 << 12) > +#define ENET_BD_IINS (1 << 11) > +#define ENET_BD_PE (1 << 10) > +#define ENET_BD_CE (1 << 9) > +#define ENET_BD_UC (1 << 8) > +#define ENET_BD_RX_INT (1 << 7) > + > +#define ENET_BD_TXE (1 << 15) > +#define ENET_BD_UE (1 << 13) > +#define ENET_BD_EE (1 << 12) > +#define ENET_BD_FE (1 << 11) > +#define ENET_BD_LCE (1 << 10) > +#define ENET_BD_OE (1 << 9) > +#define ENET_BD_TSE (1 << 8) > +#define ENET_BD_ICE (1 << 5) > +#define ENET_BD_PCR (1 << 4) > +#define ENET_BD_VLAN (1 << 2) > +#define ENET_BD_IPV6 (1 << 1) > +#define ENET_BD_FRAG (1 << 0) > + > +#define ENET_BD_BDU (1 << 31) > > typedef struct IMXFECState { > /*< private >*/ > @@ -113,7 +229,7 @@ typedef struct IMXFECState { > /*< public >*/ > NICState *nic; > NICConf conf; > - qemu_irq irq; > + qemu_irq irq[2]; > MemoryRegion iomem; > > uint32_t regs[ENET_MAX]; > @@ -125,6 +241,8 @@ typedef struct IMXFECState { > uint32_t phy_advertise; > uint32_t phy_int; > uint32_t phy_int_mask; > + > + bool is_fec; > } IMXFECState; thanks -- PMM