diff options
author | Artyom Tarasenko <atar4qemu@googlemail.com> | 2010-08-15 16:04:41 +0200 |
---|---|---|
committer | Blue Swirl <blauwirbel@gmail.com> | 2010-08-15 17:56:12 +0000 |
commit | 9fc391f8b54ea0be47baece3983e0f2224958f25 (patch) | |
tree | af18c33b645df4ceac47899f0b82c73101ce32ad /hw/escc.c | |
parent | 68c18d1ccff03ef4763bd7c0cbb6fabc4fd761cb (diff) | |
download | qemu-9fc391f8b54ea0be47baece3983e0f2224958f25.zip qemu-9fc391f8b54ea0be47baece3983e0f2224958f25.tar.gz qemu-9fc391f8b54ea0be47baece3983e0f2224958f25.tar.bz2 |
sparc escc IUS improvements (SunOS 4.1.4 fix)
According to scc_escc_um.pdf:
- Reset Highest IUS must update irq status to allow processing
of the next priority interrupt.
- rx interrupt has always higher priority than tx on same channel
The documentation only explicitly says that Reset Highest IUS
command (0x38) clears IUS bits, not that it clears the corresponding
interrupt too, so don't clear interrupts on this command.
The patch allows SunOS 4.1.4 to use the serial ports
Signed-off-by: Artyom Tarasenko <atar4qemu@gmail.com>
Signed-off-by: Blue Swirl <blauwirbel@gmail.com>
Diffstat (limited to 'hw/escc.c')
-rw-r--r-- | hw/escc.c | 56 |
1 files changed, 30 insertions, 26 deletions
@@ -65,6 +65,8 @@ * 2006-Aug-10 Igor Kovalenko : Renamed KBDQueue to SERIOQueue, implemented * serial mouse queue. * Implemented serial mouse protocol. + * + * 2010-May-23 Artyom Tarasenko: Reworked IUS logic */ #ifdef DEBUG_SERIAL @@ -279,7 +281,7 @@ static uint32_t get_queue(void *opaque) static int escc_update_irq_chn(ChannelState *s) { - if ((((s->wregs[W_INTR] & INTR_TXINT) && s->txint == 1) || + if ((((s->wregs[W_INTR] & INTR_TXINT) && (s->txint == 1)) || // tx ints enabled, pending ((((s->wregs[W_INTR] & INTR_RXMODEMSK) == INTR_RXINT1ST) || ((s->wregs[W_INTR] & INTR_RXMODEMSK) == INTR_RXINTALL)) && @@ -342,24 +344,22 @@ static void escc_reset(DeviceState *d) static inline void set_rxint(ChannelState *s) { s->rxint = 1; - if (!s->txint_under_svc) { - s->rxint_under_svc = 1; - if (s->chn == chn_a) { - if (s->wregs[W_MINTR] & MINTR_STATUSHI) - s->otherchn->rregs[R_IVEC] = IVEC_HIRXINTA; - else - s->otherchn->rregs[R_IVEC] = IVEC_LORXINTA; - } else { - if (s->wregs[W_MINTR] & MINTR_STATUSHI) - s->rregs[R_IVEC] = IVEC_HIRXINTB; - else - s->rregs[R_IVEC] = IVEC_LORXINTB; - } - } - if (s->chn == chn_a) + /* XXX: missing daisy chainnig: chn_b rx should have a lower priority + than chn_a rx/tx/special_condition service*/ + s->rxint_under_svc = 1; + if (s->chn == chn_a) { s->rregs[R_INTR] |= INTR_RXINTA; - else + if (s->wregs[W_MINTR] & MINTR_STATUSHI) + s->otherchn->rregs[R_IVEC] = IVEC_HIRXINTA; + else + s->otherchn->rregs[R_IVEC] = IVEC_LORXINTA; + } else { s->otherchn->rregs[R_INTR] |= INTR_RXINTB; + if (s->wregs[W_MINTR] & MINTR_STATUSHI) + s->rregs[R_IVEC] = IVEC_HIRXINTB; + else + s->rregs[R_IVEC] = IVEC_LORXINTB; + } escc_update_irq(s); } @@ -369,19 +369,17 @@ static inline void set_txint(ChannelState *s) if (!s->rxint_under_svc) { s->txint_under_svc = 1; if (s->chn == chn_a) { + s->rregs[R_INTR] |= INTR_TXINTA; if (s->wregs[W_MINTR] & MINTR_STATUSHI) s->otherchn->rregs[R_IVEC] = IVEC_HITXINTA; else s->otherchn->rregs[R_IVEC] = IVEC_LOTXINTA; } else { s->rregs[R_IVEC] = IVEC_TXINTB; + s->otherchn->rregs[R_INTR] |= INTR_TXINTB; } - } - if (s->chn == chn_a) - s->rregs[R_INTR] |= INTR_TXINTA; - else - s->otherchn->rregs[R_INTR] |= INTR_TXINTB; escc_update_irq(s); + } } static inline void clr_rxint(ChannelState *s) @@ -417,6 +415,7 @@ static inline void clr_txint(ChannelState *s) s->otherchn->rregs[R_IVEC] = IVEC_LONOINT; s->rregs[R_INTR] &= ~INTR_TXINTA; } else { + s->otherchn->rregs[R_INTR] &= ~INTR_TXINTB; if (s->wregs[W_MINTR] & MINTR_STATUSHI) s->rregs[R_IVEC] = IVEC_HINOINT; else @@ -515,10 +514,15 @@ static void escc_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) clr_txint(s); break; case CMD_CLR_IUS: - if (s->rxint_under_svc) - clr_rxint(s); - else if (s->txint_under_svc) - clr_txint(s); + if (s->rxint_under_svc) { + s->rxint_under_svc = 0; + if (s->txint) { + set_txint(s); + } + } else if (s->txint_under_svc) { + s->txint_under_svc = 0; + } + escc_update_irq(s); break; default: break; |