i.MX: move FEC device to a register array structure.

This is to prepare for the ENET Gb device of the i.MX6.

Signed-off-by: Jean-Christophe Dubois <jcd@tribudubois.net>
Signed-off-by: Jason Wang <jasowang@redhat.com>
This commit is contained in:
Jean-Christophe Dubois 2016-05-30 19:26:05 +02:00 committed by Jason Wang
parent 1bb3c37182
commit db0de35268
2 changed files with 254 additions and 191 deletions

View File

@ -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);
uint32_t index = addr >> 2;
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];
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;
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 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;

View File

@ -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;