mirror of https://github.com/xqemu/xqemu.git
etsec: fix IRQ (un)masking
Interrupt conditions occurring while masked are not being signaled when later unmasked. The fix is to raise/lower IRQs when IMASK is changed. To avoid problems like this in future, consolidate IRQ pin update logic in one function. Also fix probable typo "IEVENT_TXF | IEVENT_TXF", and update IRQ pins on reset. Signed-off-by: Michael Davidsaver <mdavidsaver@gmail.com> Reviewed-by: Cédric Le Goater <clg@kaod.org> Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
This commit is contained in:
parent
b585395b65
commit
fd8e3381a0
|
@ -49,6 +49,28 @@ static const int debug_etsec;
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
/* call after any change to IEVENT or IMASK */
|
||||||
|
void etsec_update_irq(eTSEC *etsec)
|
||||||
|
{
|
||||||
|
uint32_t ievent = etsec->regs[IEVENT].value;
|
||||||
|
uint32_t imask = etsec->regs[IMASK].value;
|
||||||
|
uint32_t active = ievent & imask;
|
||||||
|
|
||||||
|
int tx = !!(active & IEVENT_TX_MASK);
|
||||||
|
int rx = !!(active & IEVENT_RX_MASK);
|
||||||
|
int err = !!(active & IEVENT_ERR_MASK);
|
||||||
|
|
||||||
|
DPRINTF("%s IRQ ievent=%"PRIx32" imask=%"PRIx32" %c%c%c",
|
||||||
|
__func__, ievent, imask,
|
||||||
|
tx ? 'T' : '_',
|
||||||
|
rx ? 'R' : '_',
|
||||||
|
err ? 'E' : '_');
|
||||||
|
|
||||||
|
qemu_set_irq(etsec->tx_irq, tx);
|
||||||
|
qemu_set_irq(etsec->rx_irq, rx);
|
||||||
|
qemu_set_irq(etsec->err_irq, err);
|
||||||
|
}
|
||||||
|
|
||||||
static uint64_t etsec_read(void *opaque, hwaddr addr, unsigned size)
|
static uint64_t etsec_read(void *opaque, hwaddr addr, unsigned size)
|
||||||
{
|
{
|
||||||
eTSEC *etsec = opaque;
|
eTSEC *etsec = opaque;
|
||||||
|
@ -139,31 +161,6 @@ static void write_rbasex(eTSEC *etsec,
|
||||||
etsec->regs[RBPTR0 + (reg_index - RBASE0)].value = value & ~0x7;
|
etsec->regs[RBPTR0 + (reg_index - RBASE0)].value = value & ~0x7;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void write_ievent(eTSEC *etsec,
|
|
||||||
eTSEC_Register *reg,
|
|
||||||
uint32_t reg_index,
|
|
||||||
uint32_t value)
|
|
||||||
{
|
|
||||||
/* Write 1 to clear */
|
|
||||||
reg->value &= ~value;
|
|
||||||
|
|
||||||
if (!(reg->value & (IEVENT_TXF | IEVENT_TXF))) {
|
|
||||||
qemu_irq_lower(etsec->tx_irq);
|
|
||||||
}
|
|
||||||
if (!(reg->value & (IEVENT_RXF | IEVENT_RXF))) {
|
|
||||||
qemu_irq_lower(etsec->rx_irq);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(reg->value & (IEVENT_MAG | IEVENT_GTSC | IEVENT_GRSC | IEVENT_TXC |
|
|
||||||
IEVENT_RXC | IEVENT_BABR | IEVENT_BABT | IEVENT_LC |
|
|
||||||
IEVENT_CRL | IEVENT_FGPI | IEVENT_FIR | IEVENT_FIQ |
|
|
||||||
IEVENT_DPE | IEVENT_PERR | IEVENT_EBERR | IEVENT_TXE |
|
|
||||||
IEVENT_XFUN | IEVENT_BSY | IEVENT_MSRO | IEVENT_MMRD |
|
|
||||||
IEVENT_MMRW))) {
|
|
||||||
qemu_irq_lower(etsec->err_irq);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void write_dmactrl(eTSEC *etsec,
|
static void write_dmactrl(eTSEC *etsec,
|
||||||
eTSEC_Register *reg,
|
eTSEC_Register *reg,
|
||||||
uint32_t reg_index,
|
uint32_t reg_index,
|
||||||
|
@ -178,9 +175,7 @@ static void write_dmactrl(eTSEC *etsec,
|
||||||
} else {
|
} else {
|
||||||
/* Graceful receive stop now */
|
/* Graceful receive stop now */
|
||||||
etsec->regs[IEVENT].value |= IEVENT_GRSC;
|
etsec->regs[IEVENT].value |= IEVENT_GRSC;
|
||||||
if (etsec->regs[IMASK].value & IMASK_GRSCEN) {
|
etsec_update_irq(etsec);
|
||||||
qemu_irq_raise(etsec->err_irq);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,9 +186,7 @@ static void write_dmactrl(eTSEC *etsec,
|
||||||
} else {
|
} else {
|
||||||
/* Graceful transmit stop now */
|
/* Graceful transmit stop now */
|
||||||
etsec->regs[IEVENT].value |= IEVENT_GTSC;
|
etsec->regs[IEVENT].value |= IEVENT_GTSC;
|
||||||
if (etsec->regs[IMASK].value & IMASK_GTSCEN) {
|
etsec_update_irq(etsec);
|
||||||
qemu_irq_raise(etsec->err_irq);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,7 +215,16 @@ static void etsec_write(void *opaque,
|
||||||
|
|
||||||
switch (reg_index) {
|
switch (reg_index) {
|
||||||
case IEVENT:
|
case IEVENT:
|
||||||
write_ievent(etsec, reg, reg_index, value);
|
/* Write 1 to clear */
|
||||||
|
reg->value &= ~value;
|
||||||
|
|
||||||
|
etsec_update_irq(etsec);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IMASK:
|
||||||
|
reg->value = value;
|
||||||
|
|
||||||
|
etsec_update_irq(etsec);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DMACTRL:
|
case DMACTRL:
|
||||||
|
@ -337,6 +339,8 @@ static void etsec_reset(DeviceState *d)
|
||||||
MII_SR_EXTENDED_STATUS | MII_SR_100T2_HD_CAPS | MII_SR_100T2_FD_CAPS |
|
MII_SR_EXTENDED_STATUS | MII_SR_100T2_HD_CAPS | MII_SR_100T2_FD_CAPS |
|
||||||
MII_SR_10T_HD_CAPS | MII_SR_10T_FD_CAPS | MII_SR_100X_HD_CAPS |
|
MII_SR_10T_HD_CAPS | MII_SR_10T_FD_CAPS | MII_SR_100X_HD_CAPS |
|
||||||
MII_SR_100X_FD_CAPS | MII_SR_100T4_CAPS;
|
MII_SR_100X_FD_CAPS | MII_SR_100T4_CAPS;
|
||||||
|
|
||||||
|
etsec_update_irq(etsec);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t etsec_receive(NetClientState *nc,
|
static ssize_t etsec_receive(NetClientState *nc,
|
||||||
|
|
|
@ -163,6 +163,8 @@ DeviceState *etsec_create(hwaddr base,
|
||||||
qemu_irq rx_irq,
|
qemu_irq rx_irq,
|
||||||
qemu_irq err_irq);
|
qemu_irq err_irq);
|
||||||
|
|
||||||
|
void etsec_update_irq(eTSEC *etsec);
|
||||||
|
|
||||||
void etsec_walk_tx_ring(eTSEC *etsec, int ring_nbr);
|
void etsec_walk_tx_ring(eTSEC *etsec, int ring_nbr);
|
||||||
void etsec_walk_rx_ring(eTSEC *etsec, int ring_nbr);
|
void etsec_walk_rx_ring(eTSEC *etsec, int ring_nbr);
|
||||||
ssize_t etsec_rx_ring_write(eTSEC *etsec, const uint8_t *buf, size_t size);
|
ssize_t etsec_rx_ring_write(eTSEC *etsec, const uint8_t *buf, size_t size);
|
||||||
|
|
|
@ -74,6 +74,16 @@ extern const eTSEC_Register_Definition eTSEC_registers_def[];
|
||||||
#define IEVENT_RXC (1 << 30)
|
#define IEVENT_RXC (1 << 30)
|
||||||
#define IEVENT_BABR (1 << 31)
|
#define IEVENT_BABR (1 << 31)
|
||||||
|
|
||||||
|
/* Mapping between interrupt pin and interrupt flags */
|
||||||
|
#define IEVENT_RX_MASK (IEVENT_RXF | IEVENT_RXB)
|
||||||
|
#define IEVENT_TX_MASK (IEVENT_TXF | IEVENT_TXB)
|
||||||
|
#define IEVENT_ERR_MASK (IEVENT_MAG | IEVENT_GTSC | IEVENT_GRSC | IEVENT_TXC | \
|
||||||
|
IEVENT_RXC | IEVENT_BABR | IEVENT_BABT | IEVENT_LC | \
|
||||||
|
IEVENT_CRL | IEVENT_FGPI | IEVENT_FIR | IEVENT_FIQ | \
|
||||||
|
IEVENT_DPE | IEVENT_PERR | IEVENT_EBERR | IEVENT_TXE | \
|
||||||
|
IEVENT_XFUN | IEVENT_BSY | IEVENT_MSRO | IEVENT_MMRD | \
|
||||||
|
IEVENT_MMRW)
|
||||||
|
|
||||||
#define IMASK_RXFEN (1 << 7)
|
#define IMASK_RXFEN (1 << 7)
|
||||||
#define IMASK_GRSCEN (1 << 8)
|
#define IMASK_GRSCEN (1 << 8)
|
||||||
#define IMASK_RXBEN (1 << 15)
|
#define IMASK_RXBEN (1 << 15)
|
||||||
|
|
|
@ -152,17 +152,7 @@ static void ievent_set(eTSEC *etsec,
|
||||||
{
|
{
|
||||||
etsec->regs[IEVENT].value |= flags;
|
etsec->regs[IEVENT].value |= flags;
|
||||||
|
|
||||||
if ((flags & IEVENT_TXB && etsec->regs[IMASK].value & IMASK_TXBEN)
|
etsec_update_irq(etsec);
|
||||||
|| (flags & IEVENT_TXF && etsec->regs[IMASK].value & IMASK_TXFEN)) {
|
|
||||||
qemu_irq_raise(etsec->tx_irq);
|
|
||||||
RING_DEBUG("%s Raise Tx IRQ\n", __func__);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((flags & IEVENT_RXB && etsec->regs[IMASK].value & IMASK_RXBEN)
|
|
||||||
|| (flags & IEVENT_RXF && etsec->regs[IMASK].value & IMASK_RXFEN)) {
|
|
||||||
qemu_irq_raise(etsec->rx_irq);
|
|
||||||
RING_DEBUG("%s Raise Rx IRQ\n", __func__);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tx_padding_and_crc(eTSEC *etsec, uint32_t min_frame_len)
|
static void tx_padding_and_crc(eTSEC *etsec, uint32_t min_frame_len)
|
||||||
|
|
Loading…
Reference in New Issue