aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hw/net/imx_fec.c398
-rw-r--r--include/hw/net/imx_fec.h51
2 files changed, 256 insertions, 193 deletions
diff --git a/hw/net/imx_fec.c b/hw/net/imx_fec.c
index f5eede8902..d8e414571a 100644
--- a/hw/net/imx_fec.c
+++ b/hw/net/imx_fec.c
@@ -53,30 +53,75 @@
} \
} while (0)
+static const char *imx_fec_reg_name(IMXFECState *s, uint32_t index)
+{
+ static char tmp[20];
+
+ switch (index) {
+ case ENET_EIR:
+ return "EIR";
+ case ENET_EIMR:
+ return "EIMR";
+ case ENET_RDAR:
+ return "RDAR";
+ case ENET_TDAR:
+ return "TDAR";
+ case ENET_ECR:
+ return "ECR";
+ case ENET_MMFR:
+ return "MMFR";
+ case ENET_MSCR:
+ return "MSCR";
+ case ENET_MIBC:
+ return "MIBC";
+ case ENET_RCR:
+ return "RCR";
+ case ENET_TCR:
+ return "TCR";
+ case ENET_PALR:
+ return "PALR";
+ case ENET_PAUR:
+ return "PAUR";
+ case ENET_OPD:
+ return "OPD";
+ case ENET_IAUR:
+ return "IAUR";
+ case ENET_IALR:
+ return "IALR";
+ case ENET_GAUR:
+ return "GAUR";
+ case ENET_GALR:
+ return "GALR";
+ case ENET_TFWR:
+ return "TFWR";
+ case ENET_RDSR:
+ return "RDSR";
+ case ENET_TDSR:
+ 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;
+ }
+}
+
static const VMStateDescription vmstate_imx_fec = {
.name = TYPE_IMX_FEC,
- .version_id = 1,
- .minimum_version_id = 1,
+ .version_id = 2,
+ .minimum_version_id = 2,
.fields = (VMStateField[]) {
- VMSTATE_UINT32(irq_state, IMXFECState),
- VMSTATE_UINT32(eir, IMXFECState),
- VMSTATE_UINT32(eimr, IMXFECState),
- VMSTATE_UINT32(rx_enabled, IMXFECState),
+ VMSTATE_UINT32_ARRAY(regs, IMXFECState, ENET_MAX),
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),
@@ -252,15 +297,13 @@ static void imx_fec_write_bd(IMXFECBufDesc *bd, dma_addr_t addr)
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);
+ if (s->regs[ENET_EIR] & s->regs[ENET_EIMR]) {
+ FEC_PRINTF("interrupt raised\n");
+ qemu_set_irq(s->irq, 1);
+ } else {
+ FEC_PRINTF("interrupt lowered\n");
+ qemu_set_irq(s->irq, 0);
}
- s->irq_state = active;
}
static void imx_fec_do_tx(IMXFECState *s)
@@ -284,7 +327,7 @@ static void imx_fec_do_tx(IMXFECState *s)
len = bd.length;
if (frame_size + len > ENET_MAX_FRAME_SIZE) {
len = ENET_MAX_FRAME_SIZE - frame_size;
- s->eir |= ENET_INT_BABT;
+ s->regs[ENET_EIR] |= ENET_INT_BABT;
}
dma_memory_read(&address_space_memory, bd.data, ptr, len);
ptr += len;
@@ -294,17 +337,17 @@ static void imx_fec_do_tx(IMXFECState *s)
qemu_send_packet(qemu_get_queue(s->nic), frame, len);
ptr = frame;
frame_size = 0;
- s->eir |= ENET_INT_TXF;
+ s->regs[ENET_EIR] |= ENET_INT_TXF;
}
- s->eir |= ENET_INT_TXB;
+ 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 & ENET_BD_W) != 0) {
- addr = s->etdsr;
+ addr = s->regs[ENET_TDSR];
} else {
- addr += 8;
+ addr += sizeof(bd);
}
}
@@ -316,7 +359,7 @@ static void imx_fec_do_tx(IMXFECState *s)
static void imx_fec_enable_rx(IMXFECState *s)
{
IMXFECBufDesc bd;
- uint32_t tmp;
+ bool tmp;
imx_fec_read_bd(&bd, s->rx_descriptor);
@@ -324,11 +367,11 @@ static void imx_fec_enable_rx(IMXFECState *s)
if (!tmp) {
FEC_PRINTF("RX buffer full\n");
- } else if (!s->rx_enabled) {
+ } else if (!s->regs[ENET_RDAR]) {
qemu_flush_queued_packets(qemu_get_queue(s->nic));
}
- s->rx_enabled = tmp;
+ s->regs[ENET_RDAR] = tmp ? ENET_RDAR_RDAR : 0;
}
static void imx_fec_reset(DeviceState *d)
@@ -336,18 +379,26 @@ 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 = 0xf0000000;
- 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;
+ memset(s->regs, 0, sizeof(s->regs));
+ s->regs[ENET_ECR] = 0xf0000000;
+ s->regs[ENET_MIBC] = 0xc0000000;
+ s->regs[ENET_RCR] = 0x05ee0001;
+ s->regs[ENET_OPD] = 0x00010000;
+
+ s->regs[ENET_PALR] = (s->conf.macaddr.a[0] << 24)
+ | (s->conf.macaddr.a[1] << 16)
+ | (s->conf.macaddr.a[2] << 8)
+ | s->conf.macaddr.a[3];
+ s->regs[ENET_PAUR] = (s->conf.macaddr.a[4] << 24)
+ | (s->conf.macaddr.a[5] << 16)
+ | 0x8808;
+
+ s->regs[ENET_FRBR] = 0x00000600;
+ s->regs[ENET_FRSR] = 0x00000500;
+ s->regs[ENET_MIIGSK_ENR] = 0x00000006;
+
+ s->rx_descriptor = 0;
+ s->tx_descriptor = 0;
/* We also reset the PHY */
phy_reset(s);
@@ -355,185 +406,180 @@ static void imx_fec_reset(DeviceState *d)
static uint64_t imx_fec_read(void *opaque, hwaddr addr, unsigned size)
{
+ uint32_t value = 0;
IMXFECState *s = IMX_FEC(opaque);
-
- FEC_PRINTF("reading from @ 0x%" HWADDR_PRIx "\n", 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;
+ uint32_t index = addr >> 2;
+
+ switch (index) {
+ case ENET_EIR:
+ case ENET_EIMR:
+ case ENET_RDAR:
+ case ENET_TDAR:
+ case ENET_ECR:
+ case ENET_MMFR:
+ case ENET_MSCR:
+ case ENET_MIBC:
+ case ENET_RCR:
+ case ENET_TCR:
+ case ENET_PALR:
+ case ENET_PAUR:
+ case ENET_OPD:
+ case ENET_IAUR:
+ case ENET_IALR:
+ case ENET_GAUR:
+ case ENET_GALR:
+ case ENET_TFWR:
+ 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 address at offset 0x%"
- HWADDR_PRIx "\n", TYPE_IMX_FEC, __func__, addr);
- return 0;
+ qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+ PRIx32 "\n", TYPE_IMX_FEC, __func__, index * 4);
+ break;
}
+
+ FEC_PRINTF("reg[%s] => 0x%" PRIx32 "\n", imx_fec_reg_name(s, index),
+ value);
+
+ return value;
}
static void imx_fec_write(void *opaque, hwaddr addr,
uint64_t value, unsigned size)
{
IMXFECState *s = IMX_FEC(opaque);
+ uint32_t index = addr >> 2;
- FEC_PRINTF("writing 0x%08x @ 0x%" HWADDR_PRIx "\n", (int)value, addr);
+ FEC_PRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx_fec_reg_name(s, index),
+ (uint32_t)value);
- switch (addr & 0x3ff) {
- case 0x004: /* EIR */
- s->eir &= ~value;
+ switch (index) {
+ case ENET_EIR:
+ s->regs[index] &= ~value;
break;
- case 0x008: /* EIMR */
- s->eimr = value;
+ case ENET_EIMR:
+ s->regs[index] = value;
break;
- case 0x010: /* RDAR */
- if ((s->ecr & ENET_ECR_ETHEREN) && !s->rx_enabled) {
- imx_fec_enable_rx(s);
+ case ENET_RDAR:
+ if (s->regs[ENET_ECR] & ENET_ECR_ETHEREN) {
+ if (!s->regs[index]) {
+ s->regs[index] = ENET_RDAR_RDAR;
+ imx_fec_enable_rx(s);
+ }
+ } else {
+ s->regs[index] = 0;
}
break;
- case 0x014: /* TDAR */
- if (s->ecr & ENET_ECR_ETHEREN) {
+ case ENET_TDAR:
+ if (s->regs[ENET_ECR] & ENET_ECR_ETHEREN) {
+ s->regs[index] = ENET_TDAR_TDAR;
imx_fec_do_tx(s);
}
+ s->regs[index] = 0;
break;
- case 0x024: /* ECR */
- s->ecr = value;
+ case ENET_ECR:
if (value & ENET_ECR_RESET) {
- imx_fec_reset(DEVICE(s));
+ return imx_fec_reset(DEVICE(s));
}
- if ((s->ecr & ENET_ECR_ETHEREN) == 0) {
- s->rx_enabled = 0;
- s->rx_descriptor = s->erdsr;
- s->tx_descriptor = s->etdsr;
+ s->regs[index] = value;
+ if ((s->regs[index] & ENET_ECR_ETHEREN) == 0) {
+ s->regs[ENET_RDAR] = 0;
+ s->rx_descriptor = s->regs[ENET_RDSR];
+ s->regs[ENET_TDAR] = 0;
+ s->tx_descriptor = s->regs[ENET_TDSR];
}
break;
- case 0x040: /* MMFR */
- /* store the value */
- s->mmfr = value;
+ case ENET_MMFR:
+ s->regs[index] = value;
if (extract32(value, 29, 1)) {
- s->mmfr = do_phy_read(s, extract32(value, 18, 10));
+ /* This is a read operation */
+ s->regs[ENET_MMFR] = deposit32(s->regs[ENET_MMFR], 0, 16,
+ do_phy_read(s,
+ extract32(value,
+ 18, 10)));
} else {
+ /* This a write operation */
do_phy_write(s, extract32(value, 18, 10), extract32(value, 0, 16));
}
/* raise the interrupt as the PHY operation is done */
- s->eir |= ENET_INT_MII;
+ s->regs[ENET_EIR] |= ENET_INT_MII;
break;
- case 0x044: /* MSCR */
- s->mscr = value & 0xfe;
+ case ENET_MSCR:
+ s->regs[index] = value & 0xfe;
break;
- case 0x064: /* MIBC */
+ case ENET_MIBC:
/* TODO: Implement MIB. */
- s->mibc = (value & 0x80000000) ? 0xc0000000 : 0;
+ s->regs[index] = (value & 0x80000000) ? 0xc0000000 : 0;
break;
- case 0x084: /* RCR */
- s->rcr = value & 0x07ff003f;
+ case ENET_RCR:
+ s->regs[index] = value & 0x07ff003f;
/* TODO: Implement LOOP mode. */
break;
- case 0x0c4: /* TCR */
+ case ENET_TCR:
/* We transmit immediately, so raise GRA immediately. */
- s->tcr = value;
+ s->regs[index] = value;
if (value & 1) {
- s->eir |= ENET_INT_GRA;
+ s->regs[ENET_EIR] |= ENET_INT_GRA;
}
break;
- case 0x0e4: /* PALR */
+ case ENET_PALR:
+ s->regs[index] = value;
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 */
+ case ENET_PAUR:
+ s->regs[index] = (value | 0x0000ffff) & 0xffff8808;
s->conf.macaddr.a[4] = value >> 24;
s->conf.macaddr.a[5] = value >> 16;
break;
- case 0x0ec: /* OPDR */
+ case ENET_OPD:
+ s->regs[index] = (value & 0x0000ffff) | 0x00010000;
break;
- case 0x118: /* IAUR */
- case 0x11c: /* IALR */
- case 0x120: /* GAUR */
- case 0x124: /* GALR */
+ case ENET_IAUR:
+ case ENET_IALR:
+ case ENET_GAUR:
+ case ENET_GALR:
/* TODO: implement MAC hash filtering. */
break;
- case 0x144: /* TFWR */
- s->tfwr = value & 3;
+ case ENET_TFWR:
+ s->regs[index] = value & 3;
break;
- case 0x14c: /* FRBR */
- /* FRBR writes ignored. */
+ 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 0x150: /* FRSR */
- s->frsr = (value & 0x3fc) | 0x400;
+ case ENET_FRSR:
+ s->regs[index] = (value & 0x000003fc) | 0x00000400;
break;
- case 0x180: /* ERDSR */
- s->erdsr = value & ~3;
- s->rx_descriptor = s->erdsr;
+ case ENET_RDSR:
+ s->regs[index] = value & ~3;
+ s->rx_descriptor = s->regs[index];
break;
- case 0x184: /* ETDSR */
- s->etdsr = value & ~3;
- s->tx_descriptor = s->etdsr;
+ case ENET_TDSR:
+ s->regs[index] = value & ~3;
+ s->tx_descriptor = s->regs[index];
break;
- case 0x188: /* EMRBR */
- s->emrbr = value & 0x7f0;
+ case ENET_MRBR:
+ s->regs[index] = value & 0x000007f0;
break;
- case 0x300: /* MIIGSK_CFGR */
- s->miigsk_cfgr = value & 0x53;
+ case ENET_MIIGSK_CFGR:
+ s->regs[index] = value & 0x00000053;
break;
- case 0x308: /* MIIGSK_ENR */
- s->miigsk_enr = (value & 0x2) ? 0x6 : 0;
+ case ENET_MIIGSK_ENR:
+ s->regs[index] = (value & 0x00000002) ? 0x00000006 : 0;
break;
default:
qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%"
- HWADDR_PRIx "\n", TYPE_IMX_FEC, __func__, addr);
+ PRIx32 "\n", TYPE_IMX_FEC, __func__, index * 4);
break;
}
@@ -544,7 +590,7 @@ static int imx_fec_can_receive(NetClientState *nc)
{
IMXFECState *s = IMX_FEC(qemu_get_nic_opaque(nc));
- return s->rx_enabled;
+ return s->regs[ENET_RDAR] ? 1 : 0;
}
static ssize_t imx_fec_receive(NetClientState *nc, const uint8_t *buf,
@@ -562,7 +608,7 @@ static ssize_t imx_fec_receive(NetClientState *nc, const uint8_t *buf,
FEC_PRINTF("len %d\n", (int)size);
- if (!s->rx_enabled) {
+ if (!s->regs[ENET_RDAR]) {
qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Unexpected packet\n",
TYPE_IMX_FEC, __func__);
return 0;
@@ -580,7 +626,7 @@ static ssize_t imx_fec_receive(NetClientState *nc, const uint8_t *buf,
}
/* Frames larger than the user limit just set error flags. */
- if (size > (s->rcr >> 16)) {
+ if (size > (s->regs[ENET_RCR] >> 16)) {
flags |= ENET_BD_LG;
}
@@ -598,7 +644,7 @@ static ssize_t imx_fec_receive(NetClientState *nc, const uint8_t *buf,
TYPE_IMX_FEC, __func__);
break;
}
- buf_len = (size <= s->emrbr) ? size : s->emrbr;
+ buf_len = (size <= s->regs[ENET_MRBR]) ? size : s->regs[ENET_MRBR];
bd.length = buf_len;
size -= buf_len;
@@ -621,16 +667,16 @@ static ssize_t imx_fec_receive(NetClientState *nc, const uint8_t *buf,
/* Last buffer in frame. */
bd.flags |= flags | ENET_BD_L;
FEC_PRINTF("rx frame flags %04x\n", bd.flags);
- s->eir |= ENET_INT_RXF;
+ s->regs[ENET_EIR] |= ENET_INT_RXF;
} else {
- s->eir |= ENET_INT_RXB;
+ s->regs[ENET_EIR] |= ENET_INT_RXB;
}
imx_fec_write_bd(&bd, addr);
/* Advance to the next descriptor. */
if ((bd.flags & ENET_BD_W) != 0) {
- addr = s->erdsr;
+ addr = s->regs[ENET_RDSR];
} else {
- addr += 8;
+ addr += sizeof(bd);
}
}
s->rx_descriptor = addr;
diff --git a/include/hw/net/imx_fec.h b/include/hw/net/imx_fec.h
index e5e2f11837..ed7a3b54ac 100644
--- a/include/hw/net/imx_fec.h
+++ b/include/hw/net/imx_fec.h
@@ -30,6 +30,33 @@
#include "hw/sysbus.h"
#include "net/net.h"
+#define ENET_EIR 1
+#define ENET_EIMR 2
+#define ENET_RDAR 4
+#define ENET_TDAR 5
+#define ENET_ECR 9
+#define ENET_MMFR 16
+#define ENET_MSCR 17
+#define ENET_MIBC 25
+#define ENET_RCR 33
+#define ENET_TCR 49
+#define ENET_PALR 57
+#define ENET_PAUR 58
+#define ENET_OPD 59
+#define ENET_IAUR 70
+#define ENET_IALR 71
+#define ENET_GAUR 72
+#define ENET_GALR 73
+#define ENET_TFWR 81
+#define ENET_FRBR 83
+#define ENET_FRSR 84
+#define ENET_RDSR 96
+#define ENET_TDSR 97
+#define ENET_MRBR 98
+#define ENET_MIIGSK_CFGR 192
+#define ENET_MIIGSK_ENR 194
+#define ENET_MAX 400
+
#define ENET_MAX_FRAME_SIZE 2032
#define ENET_INT_HB (1 << 31)
@@ -46,6 +73,12 @@
#define ENET_INT_RL (1 << 20)
#define ENET_INT_UN (1 << 19)
+/* RDAR */
+#define ENET_RDAR_RDAR (1 << 24)
+
+/* TDAR */
+#define ENET_TDAR_TDAR (1 << 24)
+
#define ENET_ECR_RESET (1 << 0)
#define ENET_ECR_ETHEREN (1 << 1)
@@ -83,25 +116,9 @@ typedef struct IMXFECState {
qemu_irq irq;
MemoryRegion iomem;
- uint32_t irq_state;
- uint32_t eir;
- uint32_t eimr;
- uint32_t rx_enabled;
+ uint32_t regs[ENET_MAX];
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;