hw/char/exynos4210_uart: Add receive DMA support
To support receive DMA, we need to inform the DMA controller if receive data is available. Otherwise the DMA controller keeps requesting data, causing receive errors. Implement this using an interrupt line. The instantiating code then needs to connect the interrupt with the matching DMA controller GPIO pin. Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Guenter Roeck <linux@roeck-us.net> Message-id: 20200123052540.6132-8-linux@roeck-us.net Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
parent
3a5d3a6f58
commit
3c77412b4a
@ -154,6 +154,7 @@ typedef struct Exynos4210UartState {
|
|||||||
|
|
||||||
CharBackend chr;
|
CharBackend chr;
|
||||||
qemu_irq irq;
|
qemu_irq irq;
|
||||||
|
qemu_irq dmairq;
|
||||||
|
|
||||||
uint32_t channel;
|
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);
|
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)
|
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);
|
count = fifo_elements_number(&s->rx);
|
||||||
if ((count && !(s->reg[I_(UCON)] & 0x80)) ||
|
if ((count && !(s->reg[I_(UCON)] & 0x80)) ||
|
||||||
count >= exynos4210_uart_Rx_FIFO_trigger_level(s)) {
|
count >= exynos4210_uart_Rx_FIFO_trigger_level(s)) {
|
||||||
|
exynos4210_uart_update_dmabusy(s);
|
||||||
s->reg[I_(UINTSP)] |= UINTSP_RXD;
|
s->reg[I_(UINTSP)] |= UINTSP_RXD;
|
||||||
timer_del(s->fifo_timeout_timer);
|
timer_del(s->fifo_timeout_timer);
|
||||||
}
|
}
|
||||||
} else if (s->reg[I_(UTRSTAT)] & UTRSTAT_Rx_BUFFER_DATA_READY) {
|
} else if (s->reg[I_(UTRSTAT)] & UTRSTAT_Rx_BUFFER_DATA_READY) {
|
||||||
|
exynos4210_uart_update_dmabusy(s);
|
||||||
s->reg[I_(UINTSP)] |= UINTSP_RXD;
|
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_(UCON)] & (1 << 11))) {
|
||||||
s->reg[I_(UINTSP)] |= UINTSP_RXD;
|
s->reg[I_(UINTSP)] |= UINTSP_RXD;
|
||||||
s->reg[I_(UTRSTAT)] |= UTRSTAT_Rx_TIMEOUT;
|
s->reg[I_(UTRSTAT)] |= UTRSTAT_Rx_TIMEOUT;
|
||||||
|
exynos4210_uart_update_dmabusy(s);
|
||||||
exynos4210_uart_update_irq(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;
|
s->reg[I_(UTRSTAT)] &= ~UTRSTAT_Rx_BUFFER_DATA_READY;
|
||||||
res = s->reg[I_(URXH)];
|
res = s->reg[I_(URXH)];
|
||||||
}
|
}
|
||||||
|
exynos4210_uart_update_dmabusy(s);
|
||||||
trace_exynos_uart_read(s->channel, offset,
|
trace_exynos_uart_read(s->channel, offset,
|
||||||
exynos4210_uart_regname(offset), res);
|
exynos4210_uart_regname(offset), res);
|
||||||
return res;
|
return res;
|
||||||
@ -661,6 +684,7 @@ static void exynos4210_uart_init(Object *obj)
|
|||||||
sysbus_init_mmio(dev, &s->iomem);
|
sysbus_init_mmio(dev, &s->iomem);
|
||||||
|
|
||||||
sysbus_init_irq(dev, &s->irq);
|
sysbus_init_irq(dev, &s->irq);
|
||||||
|
sysbus_init_irq(dev, &s->dmairq);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void exynos4210_uart_realize(DeviceState *dev, Error **errp)
|
static void exynos4210_uart_realize(DeviceState *dev, Error **errp)
|
||||||
|
@ -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"
|
nrf51_uart_write(uint64_t addr, uint64_t value, unsigned int size) "addr 0x%" PRIx64 " value 0x%" PRIx64 " size %u"
|
||||||
|
|
||||||
# exynos4210_uart.c
|
# 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_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_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"
|
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"
|
||||||
|
Loading…
Reference in New Issue
Block a user