diff --git a/hw/char/exynos4210_uart.c b/hw/char/exynos4210_uart.c index 363393cc75..20d8509107 100644 --- a/hw/char/exynos4210_uart.c +++ b/hw/char/exynos4210_uart.c @@ -154,6 +154,7 @@ typedef struct Exynos4210UartState { CharBackend chr; qemu_irq irq; + qemu_irq dmairq; uint32_t channel; @@ -261,6 +262,24 @@ exynos4210_uart_Rx_FIFO_trigger_level(const Exynos4210UartState *s) return exynos4210_uart_FIFO_trigger_level(s->channel, reg); } +/* + * Update Rx DMA busy signal if Rx DMA is enabled. For simplicity, + * mark DMA as busy if DMA is enabled and the receive buffer is empty. + */ +static void exynos4210_uart_update_dmabusy(Exynos4210UartState *s) +{ + bool rx_dma_enabled = (s->reg[I_(UCON)] & 0x03) == 0x02; + uint32_t count = fifo_elements_number(&s->rx); + + if (rx_dma_enabled && !count) { + qemu_irq_raise(s->dmairq); + trace_exynos_uart_dmabusy(s->channel); + } else { + qemu_irq_lower(s->dmairq); + trace_exynos_uart_dmaready(s->channel); + } +} + static void exynos4210_uart_update_irq(Exynos4210UartState *s) { /* @@ -282,10 +301,12 @@ static void exynos4210_uart_update_irq(Exynos4210UartState *s) count = fifo_elements_number(&s->rx); if ((count && !(s->reg[I_(UCON)] & 0x80)) || count >= exynos4210_uart_Rx_FIFO_trigger_level(s)) { + exynos4210_uart_update_dmabusy(s); s->reg[I_(UINTSP)] |= UINTSP_RXD; timer_del(s->fifo_timeout_timer); } } else if (s->reg[I_(UTRSTAT)] & UTRSTAT_Rx_BUFFER_DATA_READY) { + exynos4210_uart_update_dmabusy(s); s->reg[I_(UINTSP)] |= UINTSP_RXD; } @@ -311,6 +332,7 @@ static void exynos4210_uart_timeout_int(void *opaque) (s->reg[I_(UCON)] & (1 << 11))) { s->reg[I_(UINTSP)] |= UINTSP_RXD; s->reg[I_(UTRSTAT)] |= UTRSTAT_Rx_TIMEOUT; + exynos4210_uart_update_dmabusy(s); exynos4210_uart_update_irq(s); } } @@ -495,6 +517,7 @@ static uint64_t exynos4210_uart_read(void *opaque, hwaddr offset, s->reg[I_(UTRSTAT)] &= ~UTRSTAT_Rx_BUFFER_DATA_READY; res = s->reg[I_(URXH)]; } + exynos4210_uart_update_dmabusy(s); trace_exynos_uart_read(s->channel, offset, exynos4210_uart_regname(offset), res); return res; @@ -661,6 +684,7 @@ static void exynos4210_uart_init(Object *obj) sysbus_init_mmio(dev, &s->iomem); sysbus_init_irq(dev, &s->irq); + sysbus_init_irq(dev, &s->dmairq); } static void exynos4210_uart_realize(DeviceState *dev, Error **errp) diff --git a/hw/char/trace-events b/hw/char/trace-events index cb73fee6a9..6f938301d9 100644 --- a/hw/char/trace-events +++ b/hw/char/trace-events @@ -79,6 +79,8 @@ nrf51_uart_read(uint64_t addr, uint64_t r, unsigned int size) "addr 0x%" PRIx64 nrf51_uart_write(uint64_t addr, uint64_t value, unsigned int size) "addr 0x%" PRIx64 " value 0x%" PRIx64 " size %u" # exynos4210_uart.c +exynos_uart_dmabusy(uint32_t channel) "UART%d: DMA busy (Rx buffer empty)" +exynos_uart_dmaready(uint32_t channel) "UART%d: DMA ready" exynos_uart_irq_raised(uint32_t channel, uint32_t reg) "UART%d: IRQ raised: 0x%08"PRIx32 exynos_uart_irq_lowered(uint32_t channel) "UART%d: IRQ lowered" exynos_uart_update_params(uint32_t channel, int speed, uint8_t parity, int data, int stop, uint64_t wordtime) "UART%d: speed: %d, parity: %c, data bits: %d, stop bits: %d wordtime: %"PRId64"ns"