2015-06-02 14:18:13

by Cyrille Pitchen

[permalink] [raw]
Subject: [PATCH linux-next 0/4] tty/serial: at91: add support to FIFOs

ChangeLog

v1:
This series of patches add support to FIFOs which will be introduced with Atmel
sama5d2x SoC.

FIFOs allow to reduce the number of I/O access. Indeed depending on the data
size and the USART mode, FIFOs work in either single or multiple data. For
multiple data, up to 4 data can be written into the Transmit Holding Register
or read from the Receive Holding Register in a single word access.

Also the RX FIFO allows to reduce the risk of receive overrun and data loss,
especially when the DMA controller is not used.

Finally, when the hardware handshake mode is selected, the RTS line can now be
controlled by two thresholds on the RX FIFO. When FIFO level (the number of
data available to be read) crosses the high threshold, the RTS line is set to
high level telling the peer that it should stop sending new data. Data are read
from the RX FIFO and when the FIFO level crosses the low threshold, the RTS
is set back to low level telling the remote peer that it can send data again.

This new feature resolves a long time hardware limitation where the RTS line
was directly controlled by a PDC signal. There is no equivalent of that signal
for the DMA controller so for all SoCs without PDC for USART there was no mean
to control the RTS line properly: once the hardware handshake mode selected,
the RTS line remains high level. Next SoCs with FIFOs will be able to use the
hardware handshake mode thanks to the RX FIFO.

Cyrille Pitchen (4):
ARM: at91/dt: add new DT properties for Atmel usart
tty/serial: at91: fix some macro definitions to fit coding style
tty/serial: at91: add support to FIFOs
tty/serial: at91: use 32bit writes into TX FIFO when DMA is enabled

.../devicetree/bindings/serial/atmel-usart.txt | 15 ++
drivers/tty/serial/atmel_serial.c | 195 +++++++++++++----
include/linux/atmel_serial.h | 240 ++++++++++++---------
3 files changed, 307 insertions(+), 143 deletions(-)

--
1.8.2.2


2015-06-02 14:18:46

by Cyrille Pitchen

[permalink] [raw]
Subject: [PATCH linux-next 1/4] ARM: at91/dt: add new DT properties for Atmel usart

add 3 new properties:
- "atmel,fifo-size": to enable FIFO support
- "atmel,rts-low-threshold" and "atmel,rts-high-threshold": to control
the hardware handshake flow control.

Signed-off-by: Cyrille Pitchen <[email protected]>
---
Documentation/devicetree/bindings/serial/atmel-usart.txt | 15 +++++++++++++++
1 file changed, 15 insertions(+)

diff --git a/Documentation/devicetree/bindings/serial/atmel-usart.txt b/Documentation/devicetree/bindings/serial/atmel-usart.txt
index 90787aa..6158418 100644
--- a/Documentation/devicetree/bindings/serial/atmel-usart.txt
+++ b/Documentation/devicetree/bindings/serial/atmel-usart.txt
@@ -22,6 +22,21 @@ Optional properties:
memory peripheral interface and USART DMA channel ID, FIFO configuration.
Refer to dma.txt and atmel-dma.txt for details.
- dma-names: "rx" for RX channel, "tx" for TX channel.
+- atmel,fifo-size: size of the TX and RX FIFOs. That is to say the maximum
+ number of data each FIFO can store. A positive size enables FIFO support.
+ A zero size or a missing property disables FIFO support.
+- atmel,rts-low-threshold: when the RX FIFO level, ie the number of data
+ available to be read from the RX FIFO, crosses down this threshold the RTS
+ line is driven to low level to tell the remote peer that it can (re)start
+ sending new data.
+- atmel,rts-high-threshold: when the RX FIFO level crosses up this threshold,
+ the RTS line is driven to high level to tell the remote peer that it should
+ stop sending new data.
+
+0 < rts-low-threshold < rts-high-threshold <= fifo-size
+
+These two thresholds are only used when both the FIFO support and the hardware
+handshake mode are enabled.

<chip> compatible description:
- at91rm9200: legacy USART support
--
1.8.2.2

2015-06-02 14:19:08

by Cyrille Pitchen

[permalink] [raw]
Subject: [PATCH linux-next 2/4] tty/serial: at91: fix some macro definitions to fit coding style

This patch updates macro definitions in atmel_serial.h to fit the
80 column rule.

Please note that some deprecated comments such as "[AT91SAM9261 only]"
are removed as the corresponding bits also exist in some later chips.

The patch also fix macro definitions in atmel_serial.c to replace
(port,v) by (port, v).

Signed-off-by: Cyrille Pitchen <[email protected]>
---
drivers/tty/serial/atmel_serial.c | 52 +++++-----
include/linux/atmel_serial.h | 204 +++++++++++++++++++-------------------
2 files changed, 128 insertions(+), 128 deletions(-)

diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index 2a8f528..112e74b 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -89,35 +89,35 @@ static void atmel_stop_rx(struct uart_port *port);
#define ATMEL_ISR_PASS_LIMIT 256

/* UART registers. CR is write-only, hence no GET macro */
-#define UART_PUT_CR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_CR)
-#define UART_GET_MR(port) __raw_readl((port)->membase + ATMEL_US_MR)
-#define UART_PUT_MR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_MR)
-#define UART_PUT_IER(port,v) __raw_writel(v, (port)->membase + ATMEL_US_IER)
-#define UART_PUT_IDR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_IDR)
-#define UART_GET_IMR(port) __raw_readl((port)->membase + ATMEL_US_IMR)
-#define UART_GET_CSR(port) __raw_readl((port)->membase + ATMEL_US_CSR)
-#define UART_GET_CHAR(port) __raw_readl((port)->membase + ATMEL_US_RHR)
-#define UART_PUT_CHAR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_THR)
-#define UART_GET_BRGR(port) __raw_readl((port)->membase + ATMEL_US_BRGR)
-#define UART_PUT_BRGR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_BRGR)
-#define UART_PUT_RTOR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_RTOR)
-#define UART_PUT_TTGR(port, v) __raw_writel(v, (port)->membase + ATMEL_US_TTGR)
-#define UART_GET_IP_NAME(port) __raw_readl((port)->membase + ATMEL_US_NAME)
-#define UART_GET_IP_VERSION(port) __raw_readl((port)->membase + ATMEL_US_VERSION)
+#define UART_PUT_CR(port, v) __raw_writel(v, (port)->membase + ATMEL_US_CR)
+#define UART_GET_MR(port) __raw_readl((port)->membase + ATMEL_US_MR)
+#define UART_PUT_MR(port, v) __raw_writel(v, (port)->membase + ATMEL_US_MR)
+#define UART_PUT_IER(port, v) __raw_writel(v, (port)->membase + ATMEL_US_IER)
+#define UART_PUT_IDR(port, v) __raw_writel(v, (port)->membase + ATMEL_US_IDR)
+#define UART_GET_IMR(port) __raw_readl((port)->membase + ATMEL_US_IMR)
+#define UART_GET_CSR(port) __raw_readl((port)->membase + ATMEL_US_CSR)
+#define UART_GET_CHAR(port) __raw_readl((port)->membase + ATMEL_US_RHR)
+#define UART_PUT_CHAR(port, v) __raw_writel(v, (port)->membase + ATMEL_US_THR)
+#define UART_GET_BRGR(port) __raw_readl((port)->membase + ATMEL_US_BRGR)
+#define UART_PUT_BRGR(port, v) __raw_writel(v, (port)->membase + ATMEL_US_BRGR)
+#define UART_PUT_RTOR(port, v) __raw_writel(v, (port)->membase + ATMEL_US_RTOR)
+#define UART_PUT_TTGR(port, v) __raw_writel(v, (port)->membase + ATMEL_US_TTGR)
+#define UART_GET_IP_NAME(port) __raw_readl((port)->membase + ATMEL_US_NAME)
+#define UART_GET_IP_VERS(port) __raw_readl((port)->membase + ATMEL_US_VERSION)

/* PDC registers */
-#define UART_PUT_PTCR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_PTCR)
-#define UART_GET_PTSR(port) __raw_readl((port)->membase + ATMEL_PDC_PTSR)
+#define UART_PUT_PTCR(port, v) __raw_writel(v, (port)->membase + ATMEL_PDC_PTCR)
+#define UART_GET_PTSR(port) __raw_readl((port)->membase + ATMEL_PDC_PTSR)

-#define UART_PUT_RPR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_RPR)
-#define UART_GET_RPR(port) __raw_readl((port)->membase + ATMEL_PDC_RPR)
-#define UART_PUT_RCR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_RCR)
-#define UART_PUT_RNPR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_RNPR)
-#define UART_PUT_RNCR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_RNCR)
+#define UART_PUT_RPR(port, v) __raw_writel(v, (port)->membase + ATMEL_PDC_RPR)
+#define UART_GET_RPR(port) __raw_readl((port)->membase + ATMEL_PDC_RPR)
+#define UART_PUT_RCR(port, v) __raw_writel(v, (port)->membase + ATMEL_PDC_RCR)
+#define UART_PUT_RNPR(port, v) __raw_writel(v, (port)->membase + ATMEL_PDC_RNPR)
+#define UART_PUT_RNCR(port, v) __raw_writel(v, (port)->membase + ATMEL_PDC_RNCR)

-#define UART_PUT_TPR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_TPR)
-#define UART_PUT_TCR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_TCR)
-#define UART_GET_TCR(port) __raw_readl((port)->membase + ATMEL_PDC_TCR)
+#define UART_PUT_TPR(port, v) __raw_writel(v, (port)->membase + ATMEL_PDC_TPR)
+#define UART_PUT_TCR(port, v) __raw_writel(v, (port)->membase + ATMEL_PDC_TCR)
+#define UART_GET_TCR(port) __raw_readl((port)->membase + ATMEL_PDC_TCR)

struct atmel_dma_buffer {
unsigned char *buf;
@@ -1684,7 +1684,7 @@ static void atmel_get_ip_name(struct uart_port *port)
atmel_port->is_usart = false;
} else {
/* fallback for older SoCs: use version field */
- version = UART_GET_IP_VERSION(port);
+ version = UART_GET_IP_VERS(port);
switch (version) {
case 0x302:
case 0x10213:
diff --git a/include/linux/atmel_serial.h b/include/linux/atmel_serial.h
index 00beddf..c384c21 100644
--- a/include/linux/atmel_serial.h
+++ b/include/linux/atmel_serial.h
@@ -16,115 +16,115 @@
#ifndef ATMEL_SERIAL_H
#define ATMEL_SERIAL_H

-#define ATMEL_US_CR 0x00 /* Control Register */
-#define ATMEL_US_RSTRX (1 << 2) /* Reset Receiver */
-#define ATMEL_US_RSTTX (1 << 3) /* Reset Transmitter */
-#define ATMEL_US_RXEN (1 << 4) /* Receiver Enable */
-#define ATMEL_US_RXDIS (1 << 5) /* Receiver Disable */
-#define ATMEL_US_TXEN (1 << 6) /* Transmitter Enable */
-#define ATMEL_US_TXDIS (1 << 7) /* Transmitter Disable */
-#define ATMEL_US_RSTSTA (1 << 8) /* Reset Status Bits */
-#define ATMEL_US_STTBRK (1 << 9) /* Start Break */
-#define ATMEL_US_STPBRK (1 << 10) /* Stop Break */
-#define ATMEL_US_STTTO (1 << 11) /* Start Time-out */
-#define ATMEL_US_SENDA (1 << 12) /* Send Address */
-#define ATMEL_US_RSTIT (1 << 13) /* Reset Iterations */
-#define ATMEL_US_RSTNACK (1 << 14) /* Reset Non Acknowledge */
-#define ATMEL_US_RETTO (1 << 15) /* Rearm Time-out */
-#define ATMEL_US_DTREN (1 << 16) /* Data Terminal Ready Enable [AT91RM9200 only] */
-#define ATMEL_US_DTRDIS (1 << 17) /* Data Terminal Ready Disable [AT91RM9200 only] */
-#define ATMEL_US_RTSEN (1 << 18) /* Request To Send Enable */
-#define ATMEL_US_RTSDIS (1 << 19) /* Request To Send Disable */
+#define ATMEL_US_CR 0x00 /* Control Register */
+#define ATMEL_US_RSTRX BIT(2) /* Reset Receiver */
+#define ATMEL_US_RSTTX BIT(3) /* Reset Transmitter */
+#define ATMEL_US_RXEN BIT(4) /* Receiver Enable */
+#define ATMEL_US_RXDIS BIT(5) /* Receiver Disable */
+#define ATMEL_US_TXEN BIT(6) /* Transmitter Enable */
+#define ATMEL_US_TXDIS BIT(7) /* Transmitter Disable */
+#define ATMEL_US_RSTSTA BIT(8) /* Reset Status Bits */
+#define ATMEL_US_STTBRK BIT(9) /* Start Break */
+#define ATMEL_US_STPBRK BIT(10) /* Stop Break */
+#define ATMEL_US_STTTO BIT(11) /* Start Time-out */
+#define ATMEL_US_SENDA BIT(12) /* Send Address */
+#define ATMEL_US_RSTIT BIT(13) /* Reset Iterations */
+#define ATMEL_US_RSTNACK BIT(14) /* Reset Non Acknowledge */
+#define ATMEL_US_RETTO BIT(15) /* Rearm Time-out */
+#define ATMEL_US_DTREN BIT(16) /* Data Terminal Ready Enable */
+#define ATMEL_US_DTRDIS BIT(17) /* Data Terminal Ready Disable */
+#define ATMEL_US_RTSEN BIT(18) /* Request To Send Enable */
+#define ATMEL_US_RTSDIS BIT(19) /* Request To Send Disable */

-#define ATMEL_US_MR 0x04 /* Mode Register */
-#define ATMEL_US_USMODE (0xf << 0) /* Mode of the USART */
-#define ATMEL_US_USMODE_NORMAL 0
-#define ATMEL_US_USMODE_RS485 1
-#define ATMEL_US_USMODE_HWHS 2
-#define ATMEL_US_USMODE_MODEM 3
-#define ATMEL_US_USMODE_ISO7816_T0 4
-#define ATMEL_US_USMODE_ISO7816_T1 6
-#define ATMEL_US_USMODE_IRDA 8
-#define ATMEL_US_USCLKS (3 << 4) /* Clock Selection */
-#define ATMEL_US_USCLKS_MCK (0 << 4)
-#define ATMEL_US_USCLKS_MCK_DIV8 (1 << 4)
-#define ATMEL_US_USCLKS_SCK (3 << 4)
-#define ATMEL_US_CHRL (3 << 6) /* Character Length */
-#define ATMEL_US_CHRL_5 (0 << 6)
-#define ATMEL_US_CHRL_6 (1 << 6)
-#define ATMEL_US_CHRL_7 (2 << 6)
-#define ATMEL_US_CHRL_8 (3 << 6)
-#define ATMEL_US_SYNC (1 << 8) /* Synchronous Mode Select */
-#define ATMEL_US_PAR (7 << 9) /* Parity Type */
-#define ATMEL_US_PAR_EVEN (0 << 9)
-#define ATMEL_US_PAR_ODD (1 << 9)
-#define ATMEL_US_PAR_SPACE (2 << 9)
-#define ATMEL_US_PAR_MARK (3 << 9)
-#define ATMEL_US_PAR_NONE (4 << 9)
-#define ATMEL_US_PAR_MULTI_DROP (6 << 9)
-#define ATMEL_US_NBSTOP (3 << 12) /* Number of Stop Bits */
-#define ATMEL_US_NBSTOP_1 (0 << 12)
-#define ATMEL_US_NBSTOP_1_5 (1 << 12)
-#define ATMEL_US_NBSTOP_2 (2 << 12)
-#define ATMEL_US_CHMODE (3 << 14) /* Channel Mode */
-#define ATMEL_US_CHMODE_NORMAL (0 << 14)
-#define ATMEL_US_CHMODE_ECHO (1 << 14)
-#define ATMEL_US_CHMODE_LOC_LOOP (2 << 14)
-#define ATMEL_US_CHMODE_REM_LOOP (3 << 14)
-#define ATMEL_US_MSBF (1 << 16) /* Bit Order */
-#define ATMEL_US_MODE9 (1 << 17) /* 9-bit Character Length */
-#define ATMEL_US_CLKO (1 << 18) /* Clock Output Select */
-#define ATMEL_US_OVER (1 << 19) /* Oversampling Mode */
-#define ATMEL_US_INACK (1 << 20) /* Inhibit Non Acknowledge */
-#define ATMEL_US_DSNACK (1 << 21) /* Disable Successive NACK */
-#define ATMEL_US_MAX_ITER (7 << 24) /* Max Iterations */
-#define ATMEL_US_FILTER (1 << 28) /* Infrared Receive Line Filter */
+#define ATMEL_US_MR 0x04 /* Mode Register */
+#define ATMEL_US_USMODE GENMASK(3, 0) /* Mode of the USART */
+#define ATMEL_US_USMODE_NORMAL 0
+#define ATMEL_US_USMODE_RS485 1
+#define ATMEL_US_USMODE_HWHS 2
+#define ATMEL_US_USMODE_MODEM 3
+#define ATMEL_US_USMODE_ISO7816_T0 4
+#define ATMEL_US_USMODE_ISO7816_T1 6
+#define ATMEL_US_USMODE_IRDA 8
+#define ATMEL_US_USCLKS GENMASK(5, 4) /* Clock Selection */
+#define ATMEL_US_USCLKS_MCK (0 << 4)
+#define ATMEL_US_USCLKS_MCK_DIV8 (1 << 4)
+#define ATMEL_US_USCLKS_SCK (3 << 4)
+#define ATMEL_US_CHRL GENMASK(7, 6) /* Character Length */
+#define ATMEL_US_CHRL_5 (0 << 6)
+#define ATMEL_US_CHRL_6 (1 << 6)
+#define ATMEL_US_CHRL_7 (2 << 6)
+#define ATMEL_US_CHRL_8 (3 << 6)
+#define ATMEL_US_SYNC BIT(8) /* Synchronous Mode Select */
+#define ATMEL_US_PAR GENMASK(11, 9) /* Parity Type */
+#define ATMEL_US_PAR_EVEN (0 << 9)
+#define ATMEL_US_PAR_ODD (1 << 9)
+#define ATMEL_US_PAR_SPACE (2 << 9)
+#define ATMEL_US_PAR_MARK (3 << 9)
+#define ATMEL_US_PAR_NONE (4 << 9)
+#define ATMEL_US_PAR_MULTI_DROP (6 << 9)
+#define ATMEL_US_NBSTOP GENMASK(13, 12) /* Number of Stop Bits */
+#define ATMEL_US_NBSTOP_1 (0 << 12)
+#define ATMEL_US_NBSTOP_1_5 (1 << 12)
+#define ATMEL_US_NBSTOP_2 (2 << 12)
+#define ATMEL_US_CHMODE GENMASK(15, 14) /* Channel Mode */
+#define ATMEL_US_CHMODE_NORMAL (0 << 14)
+#define ATMEL_US_CHMODE_ECHO (1 << 14)
+#define ATMEL_US_CHMODE_LOC_LOOP (2 << 14)
+#define ATMEL_US_CHMODE_REM_LOOP (3 << 14)
+#define ATMEL_US_MSBF BIT(16) /* Bit Order */
+#define ATMEL_US_MODE9 BIT(17) /* 9-bit Character Length */
+#define ATMEL_US_CLKO BIT(18) /* Clock Output Select */
+#define ATMEL_US_OVER BIT(19) /* Oversampling Mode */
+#define ATMEL_US_INACK BIT(20) /* Inhibit Non Acknowledge */
+#define ATMEL_US_DSNACK BIT(21) /* Disable Successive NACK */
+#define ATMEL_US_MAX_ITER GENMASK(26, 24) /* Max Iterations */
+#define ATMEL_US_FILTER BIT(28) /* Infrared Receive Line Filter */

-#define ATMEL_US_IER 0x08 /* Interrupt Enable Register */
-#define ATMEL_US_RXRDY (1 << 0) /* Receiver Ready */
-#define ATMEL_US_TXRDY (1 << 1) /* Transmitter Ready */
-#define ATMEL_US_RXBRK (1 << 2) /* Break Received / End of Break */
-#define ATMEL_US_ENDRX (1 << 3) /* End of Receiver Transfer */
-#define ATMEL_US_ENDTX (1 << 4) /* End of Transmitter Transfer */
-#define ATMEL_US_OVRE (1 << 5) /* Overrun Error */
-#define ATMEL_US_FRAME (1 << 6) /* Framing Error */
-#define ATMEL_US_PARE (1 << 7) /* Parity Error */
-#define ATMEL_US_TIMEOUT (1 << 8) /* Receiver Time-out */
-#define ATMEL_US_TXEMPTY (1 << 9) /* Transmitter Empty */
-#define ATMEL_US_ITERATION (1 << 10) /* Max number of Repetitions Reached */
-#define ATMEL_US_TXBUFE (1 << 11) /* Transmission Buffer Empty */
-#define ATMEL_US_RXBUFF (1 << 12) /* Reception Buffer Full */
-#define ATMEL_US_NACK (1 << 13) /* Non Acknowledge */
-#define ATMEL_US_RIIC (1 << 16) /* Ring Indicator Input Change [AT91RM9200 only] */
-#define ATMEL_US_DSRIC (1 << 17) /* Data Set Ready Input Change [AT91RM9200 only] */
-#define ATMEL_US_DCDIC (1 << 18) /* Data Carrier Detect Input Change [AT91RM9200 only] */
-#define ATMEL_US_CTSIC (1 << 19) /* Clear to Send Input Change */
-#define ATMEL_US_RI (1 << 20) /* RI */
-#define ATMEL_US_DSR (1 << 21) /* DSR */
-#define ATMEL_US_DCD (1 << 22) /* DCD */
-#define ATMEL_US_CTS (1 << 23) /* CTS */
+#define ATMEL_US_IER 0x08 /* Interrupt Enable Register */
+#define ATMEL_US_RXRDY BIT(0) /* Receiver Ready */
+#define ATMEL_US_TXRDY BIT(1) /* Transmitter Ready */
+#define ATMEL_US_RXBRK BIT(2) /* Break Received / End of Break */
+#define ATMEL_US_ENDRX BIT(3) /* End of Receiver Transfer */
+#define ATMEL_US_ENDTX BIT(4) /* End of Transmitter Transfer */
+#define ATMEL_US_OVRE BIT(5) /* Overrun Error */
+#define ATMEL_US_FRAME BIT(6) /* Framing Error */
+#define ATMEL_US_PARE BIT(7) /* Parity Error */
+#define ATMEL_US_TIMEOUT BIT(8) /* Receiver Time-out */
+#define ATMEL_US_TXEMPTY BIT(9) /* Transmitter Empty */
+#define ATMEL_US_ITERATION BIT(10) /* Max number of Repetitions Reached */
+#define ATMEL_US_TXBUFE BIT(11) /* Transmission Buffer Empty */
+#define ATMEL_US_RXBUFF BIT(12) /* Reception Buffer Full */
+#define ATMEL_US_NACK BIT(13) /* Non Acknowledge */
+#define ATMEL_US_RIIC BIT(16) /* Ring Indicator Input Change */
+#define ATMEL_US_DSRIC BIT(17) /* Data Set Ready Input Change */
+#define ATMEL_US_DCDIC BIT(18) /* Data Carrier Detect Input Change */
+#define ATMEL_US_CTSIC BIT(19) /* Clear to Send Input Change */
+#define ATMEL_US_RI BIT(20) /* RI */
+#define ATMEL_US_DSR BIT(21) /* DSR */
+#define ATMEL_US_DCD BIT(22) /* DCD */
+#define ATMEL_US_CTS BIT(23) /* CTS */

-#define ATMEL_US_IDR 0x0c /* Interrupt Disable Register */
-#define ATMEL_US_IMR 0x10 /* Interrupt Mask Register */
-#define ATMEL_US_CSR 0x14 /* Channel Status Register */
-#define ATMEL_US_RHR 0x18 /* Receiver Holding Register */
-#define ATMEL_US_THR 0x1c /* Transmitter Holding Register */
-#define ATMEL_US_SYNH (1 << 15) /* Transmit/Receive Sync [AT91SAM9261 only] */
+#define ATMEL_US_IDR 0x0c /* Interrupt Disable Register */
+#define ATMEL_US_IMR 0x10 /* Interrupt Mask Register */
+#define ATMEL_US_CSR 0x14 /* Channel Status Register */
+#define ATMEL_US_RHR 0x18 /* Receiver Holding Register */
+#define ATMEL_US_THR 0x1c /* Transmitter Holding Register */
+#define ATMEL_US_SYNH BIT(15) /* Transmit/Receive Sync */

-#define ATMEL_US_BRGR 0x20 /* Baud Rate Generator Register */
-#define ATMEL_US_CD (0xffff << 0) /* Clock Divider */
+#define ATMEL_US_BRGR 0x20 /* Baud Rate Generator Register */
+#define ATMEL_US_CD GENMASK(15, 0) /* Clock Divider */

-#define ATMEL_US_RTOR 0x24 /* Receiver Time-out Register */
-#define ATMEL_US_TO (0xffff << 0) /* Time-out Value */
+#define ATMEL_US_RTOR 0x24 /* Receiver Time-out Register */
+#define ATMEL_US_TO GENMASK(15, 0) /* Time-out Value */

-#define ATMEL_US_TTGR 0x28 /* Transmitter Timeguard Register */
-#define ATMEL_US_TG (0xff << 0) /* Timeguard Value */
+#define ATMEL_US_TTGR 0x28 /* Transmitter Timeguard Register */
+#define ATMEL_US_TG GENMASK(7, 0) /* Timeguard Value */

-#define ATMEL_US_FIDI 0x40 /* FI DI Ratio Register */
-#define ATMEL_US_NER 0x44 /* Number of Errors Register */
-#define ATMEL_US_IF 0x4c /* IrDA Filter Register */
+#define ATMEL_US_FIDI 0x40 /* FI DI Ratio Register */
+#define ATMEL_US_NER 0x44 /* Number of Errors Register */
+#define ATMEL_US_IF 0x4c /* IrDA Filter Register */

-#define ATMEL_US_NAME 0xf0 /* Ip Name */
-#define ATMEL_US_VERSION 0xfc /* Ip Version */
+#define ATMEL_US_NAME 0xf0 /* Ip Name */
+#define ATMEL_US_VERSION 0xfc /* Ip Version */

#endif
--
1.8.2.2

2015-06-02 14:19:34

by Cyrille Pitchen

[permalink] [raw]
Subject: [PATCH linux-next 3/4] tty/serial: at91: add support to FIFOs

Depending on the hardware, TX and RX FIFOs may be available. The RX
FIFO can avoid receive overruns, especially when DMA transfers are
not used to read data from the Receive Holding Register. For heavy
system load, The CPU is likely not be able to fetch data fast enough
from the RHR.

In addition, the RX FIFO can supersede the DMA/PDC to control the RTS
line when the Hardware Handshaking mode is enabled. Two thresholds
are to be set for that purpose:
- When the number of data in the RX FIFO crosses and becomes lower
than or equal to the low threshold, the RTS line is set to low
level: the remote peer is requested to send data.
- When the number of data in the RX FIFO crosses and becomes greater
than or equal to the high threshold, the RTS line is set to high
level: the remote peer should stop sending new data.
- low threshold < high threshold
Once these two thresholds are set properly, this new feature is
enabled by setting the FIFO RTS Control bit of the FIFO Mode Register.

FIFOs also introduce a new multiple data mode: the USART works either
in multiple data mode or in single data (legacy) mode.

If MODE9 bit is set into the Mode Register or if USMODE is set to
either LIN_MASTER, LIN_SLAVE or LON_MODE, FIFOs operate in single
data mode. Otherwise, they operate in multiple data mode.

In this new multiple data mode, accesses to the Receive Holding
Register or Transmit Holding Register slightly change.

Since this driver implements neither the 9bit data feature (MODE9 bit
set into the Mode Register) nor LIN modes, the USART works in
multiple data mode whenever FIFOs are available and enabled. We also
assume that data are 8bit wide.

In single data mode, 32bit access CAN be used to read a single data
from RHR or write a single data into THR.
However in multiple data mode, a 32bit access to RHR now allows us to
read four consecutive data from RX FIFO. Also a 32bit access to THR
now allows to write four consecutive data into TX FIFO. So we MUST
use 8bit access whenever only one data have to be read/written at a
time.

Signed-off-by: Cyrille Pitchen <[email protected]>
---
drivers/tty/serial/atmel_serial.c | 77 +++++++++++++++++++++++++++++++++++++++
include/linux/atmel_serial.h | 36 ++++++++++++++++++
2 files changed, 113 insertions(+)

diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index 112e74b..8a32f7c 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -119,6 +119,16 @@ static void atmel_stop_rx(struct uart_port *port);
#define UART_PUT_TCR(port, v) __raw_writel(v, (port)->membase + ATMEL_PDC_TCR)
#define UART_GET_TCR(port) __raw_readl((port)->membase + ATMEL_PDC_TCR)

+/* FIFO registers */
+#define UART_PUT_FMR(port, v) __raw_writel(v, (port)->membase + ATMEL_US_FMR)
+#define UART_GET_FMR(port) __raw_readl((port)->membase + ATMEL_US_FMR)
+#define UART_GET_FESR(port) __raw_readl((port)->membase + ATMEL_US_FESR)
+#define UART_PUT_FIER(port, v) __raw_writel(v, (port)->membase + ATMEL_US_FIER)
+#define UART_PUT_FIDR(port, v) __raw_writel(v, (port)->membase + ATMEL_US_FIDR)
+#define UART_GET_FIMR(port) __raw_readl((port)->membase + ATMEL_US_FIMR)
+#define UART_GET_FLR(port) __raw_readl((port)->membase + ATMEL_US_FLR)
+
+
struct atmel_dma_buffer {
unsigned char *buf;
dma_addr_t dma_addr;
@@ -172,6 +182,9 @@ struct atmel_uart_port {
struct mctrl_gpios *gpios;
int gpio_irq[UART_GPIO_MAX];
unsigned int tx_done_mask;
+ u32 fifo_size;
+ u32 rts_high;
+ u32 rts_low;
bool ms_irq_enabled;
bool is_usart; /* usart or uart */
struct timer_list uart_timer; /* uart timer */
@@ -1797,6 +1810,29 @@ static int atmel_startup(struct uart_port *port)
atmel_set_ops(port);
}

+ /*
+ * Enable FIFO when available
+ */
+ if (atmel_port->fifo_size) {
+ unsigned int txrdym = ATMEL_US_ONE_DATA;
+ unsigned int rxrdym = ATMEL_US_ONE_DATA;
+ unsigned int fmr;
+
+ UART_PUT_CR(port,
+ ATMEL_US_FIFOEN |
+ ATMEL_US_RXFCLR |
+ ATMEL_US_TXFLCLR);
+
+ fmr = ATMEL_US_TXRDYM(txrdym) | ATMEL_US_RXRDYM(rxrdym);
+ if (atmel_port->rts_high &&
+ atmel_port->rts_low)
+ fmr |= ATMEL_US_FRTSC |
+ ATMEL_US_RXFTHRES(atmel_port->rts_high) |
+ ATMEL_US_RXFTHRES2(atmel_port->rts_low);
+
+ UART_PUT_FMR(port, fmr);
+ }
+
/* Save current CSR for comparison in atmel_tasklet_func() */
atmel_port->irq_status_prev = atmel_get_lines_status(port);
atmel_port->irq_status = atmel_port->irq_status_prev;
@@ -2599,6 +2635,46 @@ static int atmel_init_gpios(struct atmel_uart_port *p, struct device *dev)
return 0;
}

+static void atmel_serial_probe_fifos(struct atmel_uart_port *port,
+ struct platform_device *pdev)
+{
+ port->fifo_size = 0;
+ port->rts_low = 0;
+ port->rts_high = 0;
+
+ if (of_property_read_u32(pdev->dev.of_node,
+ "atmel,fifo-size",
+ &port->fifo_size) ||
+ !port->fifo_size)
+ return;
+
+ if (of_property_read_u32(pdev->dev.of_node,
+ "atmel,rts-low-threshold",
+ &port->rts_low))
+ port->rts_low = port->fifo_size >> 2;
+
+ if (of_property_read_u32(pdev->dev.of_node,
+ "atmel,rts-high-threshold",
+ &port->rts_high))
+ port->rts_high = port->fifo_size >> 1;
+
+ dev_info(&pdev->dev, "Using FIFO (%u data)\n",
+ port->fifo_size);
+
+ if (!port->rts_high ||
+ !port->rts_low ||
+ port->rts_high <= port->rts_low) {
+ port->rts_low = port->rts_high = 0;
+ dev_warn(&pdev->dev, "Invalid FIFO thresholds\n");
+ return;
+ }
+
+ dev_info(&pdev->dev, "RTS High Threshold : %2u data\n",
+ port->rts_high);
+ dev_info(&pdev->dev, "RTS Low Threshold : %2u data\n",
+ port->rts_low);
+}
+
static int atmel_serial_probe(struct platform_device *pdev)
{
struct atmel_uart_port *port;
@@ -2635,6 +2711,7 @@ static int atmel_serial_probe(struct platform_device *pdev)
port = &atmel_ports[ret];
port->backup_imr = 0;
port->uart.line = ret;
+ atmel_serial_probe_fifos(port, pdev);

spin_lock_init(&port->lock_suspended);

diff --git a/include/linux/atmel_serial.h b/include/linux/atmel_serial.h
index c384c21..ee696d7 100644
--- a/include/linux/atmel_serial.h
+++ b/include/linux/atmel_serial.h
@@ -35,6 +35,11 @@
#define ATMEL_US_DTRDIS BIT(17) /* Data Terminal Ready Disable */
#define ATMEL_US_RTSEN BIT(18) /* Request To Send Enable */
#define ATMEL_US_RTSDIS BIT(19) /* Request To Send Disable */
+#define ATMEL_US_TXFCLR BIT(24) /* Transmit FIFO Clear */
+#define ATMEL_US_RXFCLR BIT(25) /* Receive FIFO Clear */
+#define ATMEL_US_TXFLCLR BIT(26) /* Transmit FIFO Lock Clear */
+#define ATMEL_US_FIFOEN BIT(30) /* FIFO enable */
+#define ATMEL_US_FIFODIS BIT(31) /* FIFO disable */

#define ATMEL_US_MR 0x04 /* Mode Register */
#define ATMEL_US_USMODE GENMASK(3, 0) /* Mode of the USART */
@@ -124,6 +129,37 @@
#define ATMEL_US_NER 0x44 /* Number of Errors Register */
#define ATMEL_US_IF 0x4c /* IrDA Filter Register */

+#define ATMEL_US_CMPR 0x90 /* Comparaison Register */
+#define ATMEL_US_FMR 0xa0 /* FIFO Mode Register */
+#define ATMEL_US_TXRDYM(data) (((data) & 0x3) << 0) /* TX Ready Mode */
+#define ATMEL_US_RXRDYM(data) (((data) & 0x3) << 4) /* RX Ready Mode */
+#define ATMEL_US_ONE_DATA 0x0
+#define ATMEL_US_TWO_DATA 0x1
+#define ATMEL_US_FOUR_DATA 0x2
+#define ATMEL_US_FRTSC BIT(7) /* FIFO RTS pin Control */
+#define ATMEL_US_TXFTHRES(thr) (((thr) & 0x3f) << 8) /* TX FIFO Threshold */
+#define ATMEL_US_RXFTHRES(thr) (((thr) & 0x3f) << 16) /* RX FIFO Threshold */
+#define ATMEL_US_RXFTHRES2(thr) (((thr) & 0x3f) << 24) /* RX FIFO Threshold2 */
+
+#define ATMEL_US_FLR 0xa4 /* FIFO Level Register */
+#define ATMEL_US_TXFL(reg) (((reg) >> 0) & 0x3f) /* TX FIFO Level */
+#define ATMEL_US_RXFL(reg) (((reg) >> 16) & 0x3f) /* RX FIFO Level */
+
+#define ATMEL_US_FIER 0xa8 /* FIFO Interrupt Enable Register */
+#define ATMEL_US_FIDR 0xac /* FIFO Interrupt Disable Register */
+#define ATMEL_US_FIMR 0xb0 /* FIFO Interrupt Mask Register */
+#define ATMEL_US_FESR 0xb4 /* FIFO Event Status Register */
+#define ATMEL_US_TXFEF BIT(0) /* Transmit FIFO Empty Flag */
+#define ATMEL_US_TXFFF BIT(1) /* Transmit FIFO Full Flag */
+#define ATMEL_US_TXFTHF BIT(2) /* Transmit FIFO Threshold Flag */
+#define ATMEL_US_RXFEF BIT(3) /* Receive FIFO Empty Flag */
+#define ATMEL_US_RXFFF BIT(4) /* Receive FIFO Full Flag */
+#define ATMEL_US_RXFTHF BIT(5) /* Receive FIFO Threshold Flag */
+#define ATMEL_US_TXFPTEF BIT(6) /* Transmit FIFO Pointer Error Flag */
+#define ATMEL_US_RXFPTEF BIT(7) /* Receive FIFO Pointer Error Flag */
+#define ATMEL_US_TXFLOCK BIT(8) /* Transmit FIFO Lock (FESR only) */
+#define ATMEL_US_RXFTHF2 BIT(9) /* Receive FIFO Threshold Flag 2 */
+
#define ATMEL_US_NAME 0xf0 /* Ip Name */
#define ATMEL_US_VERSION 0xfc /* Ip Version */

--
1.8.2.2

2015-06-02 14:19:58

by Cyrille Pitchen

[permalink] [raw]
Subject: [PATCH linux-next 4/4] tty/serial: at91: use 32bit writes into TX FIFO when DMA is enabled

For now this improvement is only used with TX DMA transfers. The data
width must be set properly when configuring the DMA controller. Also
the FIFO configuration must be set to match the DMA transfer data
width:
TXRDYM (Transmitter Ready Mode) and RXRDYM (Receiver Ready Mode) must
be set into the FIFO Mode Register. These values are used by the
USART to trigger the DMA controller. In single data mode they are not
used and should be reset to 0.
So the TXRDYM bits are changed to FOUR_DATA; then USART triggers the
DMA controller when at least 4 data can be written into the TX FIFO
througth the THR. On the other hand the RXRDYM bits are left unchanged
to ONE_DATA.

Atmel eXtended DMA controller allows us to set a different data width
for each part of a scatter-gather transfer. So when calling
dmaengine_slave_config() to configure the TX path, we just need to set
dst_addr_width to the maximum data width. Then DMA writes into THR are
split into up to two parts. The first part carries the first data to
be sent and has a length equal to the greatest multiple of 4 (bytes)
lower than or equal to the total length of the TX DMA transfer. The
second part carries the trailing data (up to 3 bytes). The first part
is written by the DMA into THR using 32 bit accesses, whereas 8bit
accesses are used for the second part.

When DMA is not use, we think a single 32bit access isn't worth the
overhead of testing whether multiple data mode is used then splitting
the 4 read bytes.

Signed-off-by: Cyrille Pitchen <[email protected]>

Conflicts:
drivers/tty/serial/atmel_serial.c
---
drivers/tty/serial/atmel_serial.c | 66 ++++++++++++++++++++++++++++++---------
1 file changed, 51 insertions(+), 15 deletions(-)

diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index 8a32f7c..fe8a3fd 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -176,6 +176,7 @@ struct atmel_uart_port {
unsigned int irq_status;
unsigned int irq_status_prev;
unsigned int status_change;
+ unsigned int tx_len;

struct circ_buf rx_ring;

@@ -743,10 +744,10 @@ static void atmel_complete_tx_dma(void *arg)

if (chan)
dmaengine_terminate_all(chan);
- xmit->tail += sg_dma_len(&atmel_port->sg_tx);
+ xmit->tail += atmel_port->tx_len;
xmit->tail &= UART_XMIT_SIZE - 1;

- port->icount.tx += sg_dma_len(&atmel_port->sg_tx);
+ port->icount.tx += atmel_port->tx_len;

spin_lock_irq(&atmel_port->lock_tx);
async_tx_ack(atmel_port->desc_tx);
@@ -794,7 +795,9 @@ static void atmel_tx_dma(struct uart_port *port)
struct circ_buf *xmit = &port->state->xmit;
struct dma_chan *chan = atmel_port->chan_tx;
struct dma_async_tx_descriptor *desc;
- struct scatterlist *sg = &atmel_port->sg_tx;
+ struct scatterlist sgl[2], *sg, *sg_tx = &atmel_port->sg_tx;
+ unsigned int tx_len, part1_len, part2_len, sg_len;
+ dma_addr_t phys_addr;

/* Make sure we have an idle channel */
if (atmel_port->desc_tx != NULL)
@@ -810,18 +813,46 @@ static void atmel_tx_dma(struct uart_port *port)
* Take the port lock to get a
* consistent xmit buffer state.
*/
- sg->offset = xmit->tail & (UART_XMIT_SIZE - 1);
- sg_dma_address(sg) = (sg_dma_address(sg) &
- ~(UART_XMIT_SIZE - 1))
- + sg->offset;
- sg_dma_len(sg) = CIRC_CNT_TO_END(xmit->head,
- xmit->tail,
- UART_XMIT_SIZE);
- BUG_ON(!sg_dma_len(sg));
+ tx_len = CIRC_CNT_TO_END(xmit->head,
+ xmit->tail,
+ UART_XMIT_SIZE);
+
+ if (atmel_port->fifo_size) {
+ /* multi data mode */
+ part1_len = (tx_len & ~0x3); /* DWORD access */
+ part2_len = (tx_len & 0x3); /* BYTE access */
+ } else {
+ /* single data (legacy) mode */
+ part1_len = 0;
+ part2_len = tx_len; /* BYTE access only */
+ }
+
+ sg_init_table(sgl, 2);
+ sg_len = 0;
+ phys_addr = sg_dma_address(sg_tx) + xmit->tail;
+ if (part1_len) {
+ sg = &sgl[sg_len++];
+ sg_dma_address(sg) = phys_addr;
+ sg_dma_len(sg) = part1_len;
+
+ phys_addr += part1_len;
+ }
+
+ if (part2_len) {
+ sg = &sgl[sg_len++];
+ sg_dma_address(sg) = phys_addr;
+ sg_dma_len(sg) = part2_len;
+ }
+
+ /*
+ * save tx_len so atmel_complete_tx_dma() will increase
+ * xmit->tail correctly
+ */
+ atmel_port->tx_len = tx_len;

desc = dmaengine_prep_slave_sg(chan,
- sg,
- 1,
+ sgl,
+ sg_len,
DMA_MEM_TO_DEV,
DMA_PREP_INTERRUPT |
DMA_CTRL_ACK);
@@ -830,7 +861,7 @@ static void atmel_tx_dma(struct uart_port *port)
return;
}

- dma_sync_sg_for_device(port->dev, sg, 1, DMA_TO_DEVICE);
+ dma_sync_sg_for_device(port->dev, sg_tx, 1, DMA_TO_DEVICE);

atmel_port->desc_tx = desc;
desc->callback = atmel_complete_tx_dma;
@@ -890,7 +921,9 @@ static int atmel_prepare_tx_dma(struct uart_port *port)
/* Configure the slave DMA */
memset(&config, 0, sizeof(config));
config.direction = DMA_MEM_TO_DEV;
- config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ config.dst_addr_width = (atmel_port->fifo_size) ?
+ DMA_SLAVE_BUSWIDTH_4_BYTES :
+ DMA_SLAVE_BUSWIDTH_1_BYTE;
config.dst_addr = port->mapbase + ATMEL_US_THR;
config.dst_maxburst = 1;

@@ -1823,6 +1856,9 @@ static int atmel_startup(struct uart_port *port)
ATMEL_US_RXFCLR |
ATMEL_US_TXFLCLR);

+ if (atmel_use_dma_tx(port))
+ txrdym = ATMEL_US_FOUR_DATA;
+
fmr = ATMEL_US_TXRDYM(txrdym) | ATMEL_US_RXRDYM(rxrdym);
if (atmel_port->rts_high &&
atmel_port->rts_low)
--
1.8.2.2

2015-06-04 12:35:04

by Jakub Kicinski

[permalink] [raw]
Subject: Re: [PATCH linux-next 1/4] ARM: at91/dt: add new DT properties for Atmel usart

On Tue, 2 Jun 2015 16:18:21 +0200, Cyrille Pitchen wrote:
> +- atmel,rts-low-threshold: when the RX FIFO level, ie the number of data
> + available to be read from the RX FIFO, crosses down this threshold the RTS
> + line is driven to low level to tell the remote peer that it can (re)start
> + sending new data.
> +- atmel,rts-high-threshold: when the RX FIFO level crosses up this threshold,
> + the RTS line is driven to high level to tell the remote peer that it should
> + stop sending new data.

Maintainers, are there any guidelines for what is appropriate to put
into DT? The parameters above look like they could be programmed at
runtime through some user space API like sysfs.