From 68b65f7305e54b822b2483c60de7d7b017526a92 Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 22 Dec 2010 17:24:39 +0000 Subject: ARM: PL011: Add support for transmit DMA Add DMA engine support for transmit to the PL011 driver. Based on a patch from Linus Walliej, with the following changes: - remove RX DMA support. As PL011 doesn't give us receive timeout interrupts, we only get notified of received data when the RX DMA has completed. This rather sucks for interactive use of the TTY. - remove abuse of completions. Completions are supposed to be for events, not to tell what condition buffers are in. Replace it with a simple 'queued' bool. - fix locking - it is only safe to access the circular buffer with the port lock held. - only map the DMA buffer when required - if we're ever behind an IOMMU this helps keep IOMMU usage down, and also ensures that we're legal when we change the scatterlist entry length. - fix XON/XOFF sending - we must send XON/XOFF characters out as soon as possible - waiting for up to 4095 characters in the DMA buffer to be sent first is not acceptable. - fix XON/XOFF receive handling - we need to stop DMA when instructed to by the TTY layer, and restart it again when instructed to. There is a subtle problem here: we must not completely empty the circular buffer with DMA, otherwise we will not be notified of XON. - change the 'enable_dma' flag into a 'using DMA' flag, and track whether we can use TX DMA by whether the channel pointer is non-NULL. This gives us more control over whether we use DMA in the driver. - we don't need to have the TX DMA buffer continually allocated for each port - instead, allocate it when the port starts up, and free it when it's shut down. Update the 'using DMA' flag if we get the buffer, and adjust the TTY FIFO size appropriately. - if we're going to use PIO to send characters, use the existing IRQ based functionality rather than reimplementing it. This also ensures we call uart_write_wakeup() at the appropriate time, otherwise we'll stall. - use DMA engine helper functions for type safety. - fix init when built as a module - we can't have to initcall functions, so we must settle on one. This means we can eliminate the deferred DMA initialization. - there is no need to terminate transfers on a failed prep_slave_sg() call - nothing has been setup, so nothing needs to be terminated. This avoids a potential deadlock in the DMA engine code (tasklet->callback->failed prepare->terminate->tasklet_disable which then ends up waiting for the tasklet to finish running.) - Dan says that the submission callback should not return an error: | dma_submit_error() is something I should have removed after commit | a0587bcf "ioat1: move descriptor allocation from submit to prep" all | errors should be notified by prep failing to return a descriptor | handle. Negative dma_cookie_t values are only returned by the | dma_async_memcpy* calls which translate a prep failure into -ENOMEM. So remove the error handling at that point. This also solves the potential deadlock mentioned in the previous comment. Acked-by: Linus Walleij Signed-off-by: Russell King --- include/linux/amba/serial.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'include/linux/amba/serial.h') diff --git a/include/linux/amba/serial.h b/include/linux/amba/serial.h index 6021588ba0a8..577f22eb9225 100644 --- a/include/linux/amba/serial.h +++ b/include/linux/amba/serial.h @@ -180,6 +180,13 @@ struct amba_device; /* in uncompress this is included but amba/bus.h is not */ struct amba_pl010_data { void (*set_mctrl)(struct amba_device *dev, void __iomem *base, unsigned int mctrl); }; + +struct dma_chan; +struct amba_pl011_data { + bool (*dma_filter)(struct dma_chan *chan, void *filter_param); + void *dma_rx_param; + void *dma_tx_param; +}; #endif #endif -- cgit v1.2.3 From 38d624361b2a82d6317c379aebf81b1b28210bb0 Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 22 Dec 2010 17:59:16 +0000 Subject: ARM: PL011: add DMA burst threshold support for ST variants ST Micro variants has some specific dma burst threshold compensation, which allows them to make better use of a DMA controller. Add support to set this up. Based on a patch from Linus Walleij. Acked-by: Linus Walleij Signed-off-by: Russell King --- drivers/serial/amba-pl011.c | 12 ++++++++++++ include/linux/amba/serial.h | 15 +++++++++++++++ 2 files changed, 27 insertions(+) (limited to 'include/linux/amba/serial.h') diff --git a/drivers/serial/amba-pl011.c b/drivers/serial/amba-pl011.c index ab025dc52fa4..e76d7d000128 100644 --- a/drivers/serial/amba-pl011.c +++ b/drivers/serial/amba-pl011.c @@ -74,6 +74,7 @@ struct vendor_data { unsigned int lcrh_tx; unsigned int lcrh_rx; bool oversampling; + bool dma_threshold; }; static struct vendor_data vendor_arm = { @@ -82,6 +83,7 @@ static struct vendor_data vendor_arm = { .lcrh_tx = UART011_LCRH, .lcrh_rx = UART011_LCRH, .oversampling = false, + .dma_threshold = false, }; static struct vendor_data vendor_st = { @@ -90,6 +92,7 @@ static struct vendor_data vendor_st = { .lcrh_tx = ST_UART011_LCRH_TX, .lcrh_rx = ST_UART011_LCRH_RX, .oversampling = true, + .dma_threshold = true, }; /* Deals with DMA transactions */ @@ -527,6 +530,15 @@ static void pl011_dma_startup(struct uart_amba_port *uap) /* Turn on DMA error (RX/TX will be enabled on demand) */ uap->dmacr |= UART011_DMAONERR; writew(uap->dmacr, uap->port.membase + UART011_DMACR); + + /* + * ST Micro variants has some specific dma burst threshold + * compensation. Set this to 16 bytes, so burst will only + * be issued above/below 16 bytes. + */ + if (uap->vendor->dma_threshold) + writew(ST_UART011_DMAWM_RX_16 | ST_UART011_DMAWM_TX_16, + uap->port.membase + ST_UART011_DMAWM); } static void pl011_dma_shutdown(struct uart_amba_port *uap) diff --git a/include/linux/amba/serial.h b/include/linux/amba/serial.h index 577f22eb9225..5479fdc849e9 100644 --- a/include/linux/amba/serial.h +++ b/include/linux/amba/serial.h @@ -113,6 +113,21 @@ #define UART01x_LCRH_PEN 0x02 #define UART01x_LCRH_BRK 0x01 +#define ST_UART011_DMAWM_RX_1 (0 << 3) +#define ST_UART011_DMAWM_RX_2 (1 << 3) +#define ST_UART011_DMAWM_RX_4 (2 << 3) +#define ST_UART011_DMAWM_RX_8 (3 << 3) +#define ST_UART011_DMAWM_RX_16 (4 << 3) +#define ST_UART011_DMAWM_RX_32 (5 << 3) +#define ST_UART011_DMAWM_RX_48 (6 << 3) +#define ST_UART011_DMAWM_TX_1 0 +#define ST_UART011_DMAWM_TX_2 1 +#define ST_UART011_DMAWM_TX_4 2 +#define ST_UART011_DMAWM_TX_8 3 +#define ST_UART011_DMAWM_TX_16 4 +#define ST_UART011_DMAWM_TX_32 5 +#define ST_UART011_DMAWM_TX_48 6 + #define UART010_IIR_RTIS 0x08 #define UART010_IIR_TIS 0x04 #define UART010_IIR_RIS 0x02 -- cgit v1.2.3