diff options
-rw-r--r-- | hw/net/imx_fec.c | 398 | ||||
-rw-r--r-- | include/hw/net/imx_fec.h | 51 |
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; |