openwrt/target/linux/layerscape/patches-5.4/819-uart-0005-tty-serial-fsl_lpuart-enable-dma-mode-for-imx8qxp.patch
Hauke Mehrtens 079ce0413a kernel: bump 5.4 to 5.4.224
Manually adapted:
 generic/pending-5.4/680-NET-skip-GRO-for-foreign-MAC-addresses.patch
 lantiq/patches-5.4/0028-NET-lantiq-various-etop-fixes.patch

Compile-tested: x86/64
Run-tested: x86/64

Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
2022-11-13 01:06:12 +01:00

559 lines
16 KiB
Diff

From 0d6e214f5a257f9b53619ef8aa3b6e767189bdcf Mon Sep 17 00:00:00 2001
From: Fugang Duan <fugang.duan@nxp.com>
Date: Wed, 11 Sep 2019 16:21:06 +0800
Subject: [PATCH] tty: serial: fsl_lpuart: enable dma mode for imx8qxp
imx8qxp lpuart support eDMA for dma mode, support EOP (end-of-packet)
feature. But eDMA cannot detect the correct DADDR for current major
loop in cyclic mode, so it doesn't support cyclic mode.
The patch is to enable lpuart prep slave sg dma mode for imx8qxp.
Signed-off-by: Fugang Duan <fugang.duan@nxp.com>
---
drivers/tty/serial/fsl_lpuart.c | 280 +++++++++++++++++++++++++++++++---------
1 file changed, 219 insertions(+), 61 deletions(-)
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -131,6 +131,7 @@
#define UARTBAUD_M10 0x20000000
#define UARTBAUD_TDMAE 0x00800000
#define UARTBAUD_RDMAE 0x00200000
+#define UARTBAUD_RIDMAE 0x00100000
#define UARTBAUD_MATCFG 0x00400000
#define UARTBAUD_BOTHEDGE 0x00020000
#define UARTBAUD_RESYNCDIS 0x00010000
@@ -179,7 +180,7 @@
#define UARTCTRL_SBK 0x00010000
#define UARTCTRL_MA1IE 0x00008000
#define UARTCTRL_MA2IE 0x00004000
-#define UARTCTRL_IDLECFG 0x00000100
+#define UARTCTRL_IDLECFG_OFF 0x8
#define UARTCTRL_LOOPS 0x00000080
#define UARTCTRL_DOZEEN 0x00000040
#define UARTCTRL_RSRC 0x00000020
@@ -197,6 +198,7 @@
#define UARTDATA_MASK 0x3ff
#define UARTMODIR_IREN 0x00020000
+#define UARTMODIR_RTSWATER_S 0x8
#define UARTMODIR_TXCTSSRC 0x00000020
#define UARTMODIR_TXCTSC 0x00000010
#define UARTMODIR_RXRTSE 0x00000008
@@ -210,6 +212,8 @@
#define UARTFIFO_RXUF 0x00010000
#define UARTFIFO_TXFLUSH 0x00008000
#define UARTFIFO_RXFLUSH 0x00004000
+#define UARTFIFO_RXIDEN_MASK 0x7
+#define UARTFIFO_RXIDEN_OFF 10
#define UARTFIFO_TXOFE 0x00000200
#define UARTFIFO_RXUFE 0x00000100
#define UARTFIFO_TXFE 0x00000080
@@ -226,6 +230,9 @@
#define UARTWATER_TXWATER_OFF 0
#define UARTWATER_RXWATER_OFF 16
+#define UARTFIFO_RXIDEN_RDRF 0x3
+#define UARTCTRL_IDLECFG 0x7
+
/* Rx DMA timeout in ms, which is used to calculate Rx ring buffer size */
#define DMA_RX_TIMEOUT (10)
@@ -252,6 +259,9 @@ struct lpuart_port {
unsigned int txfifo_size;
unsigned int rxfifo_size;
+ u8 rx_watermark;
+ bool dma_eeop;
+ bool rx_dma_cyclic;
bool lpuart_dma_tx_use;
bool lpuart_dma_rx_use;
struct dma_chan *dma_tx_chan;
@@ -276,33 +286,45 @@ struct lpuart_soc_data {
enum lpuart_type devtype;
char iotype;
u8 reg_off;
+ u8 rx_watermark;
+ bool rx_dma_cyclic;
};
static const struct lpuart_soc_data vf_data = {
.devtype = VF610_LPUART,
.iotype = UPIO_MEM,
+ .rx_watermark = 1,
+ .rx_dma_cyclic = true,
};
static const struct lpuart_soc_data ls1021a_data = {
.devtype = LS1021A_LPUART,
.iotype = UPIO_MEM32BE,
+ .rx_watermark = 0,
+ .rx_dma_cyclic = true,
};
static const struct lpuart_soc_data ls1028a_data = {
.devtype = LS1028A_LPUART,
.iotype = UPIO_MEM32,
+ .rx_watermark = 0,
+ .rx_dma_cyclic = true,
};
static struct lpuart_soc_data imx7ulp_data = {
.devtype = IMX7ULP_LPUART,
.iotype = UPIO_MEM32,
.reg_off = IMX_REG_OFF,
+ .rx_watermark = 0,
+ .rx_dma_cyclic = true,
};
static struct lpuart_soc_data imx8qxp_data = {
.devtype = IMX8QXP_LPUART,
.iotype = UPIO_MEM32,
.reg_off = IMX_REG_OFF,
+ .rx_watermark = 31,
+ .rx_dma_cyclic = false,
};
static const struct of_device_id lpuart_dt_ids[] = {
@@ -317,6 +339,7 @@ MODULE_DEVICE_TABLE(of, lpuart_dt_ids);
/* Forward declare this for the dma callbacks*/
static void lpuart_dma_tx_complete(void *arg);
+static int lpuart_sched_rx_dma(struct lpuart_port *sport);
static inline bool is_layerscape_lpuart(struct lpuart_port *sport)
{
@@ -1008,19 +1031,15 @@ static irqreturn_t lpuart32_int(int irq,
if ((sts & UARTSTAT_TDRE) && !sport->lpuart_dma_tx_use)
lpuart32_txint(sport);
+ if (sport->lpuart_dma_rx_use && sport->dma_eeop)
+ sts &= ~UARTSTAT_IDLE;
+
lpuart32_write(&sport->port, sts, UARTSTAT);
return IRQ_HANDLED;
}
-static void lpuart_copy_rx_to_tty(struct lpuart_port *sport)
+static void lpuart_rx_error_stat(struct lpuart_port *sport)
{
- struct tty_port *port = &sport->port.state->port;
- struct dma_tx_state state;
- enum dma_status dmastat;
- struct circ_buf *ring = &sport->rx_ring;
- unsigned long flags;
- int count = 0;
-
if (lpuart_is_32(sport)) {
unsigned long sr = lpuart32_read(&sport->port, UARTSTAT);
@@ -1072,8 +1091,21 @@ static void lpuart_copy_rx_to_tty(struct
writeb(cr2, sport->port.membase + UARTCR2);
}
}
+}
+
+static void lpuart_copy_rx_to_tty(struct lpuart_port *sport)
+{
+ struct tty_port *port = &sport->port.state->port;
+ struct dma_tx_state state;
+ enum dma_status dmastat;
+ struct circ_buf *ring = &sport->rx_ring;
+ unsigned long flags;
+ int count = 0;
- async_tx_ack(sport->dma_rx_desc);
+ if (!is_imx8qxp_lpuart(sport)) {
+ lpuart_rx_error_stat(sport);
+ async_tx_ack(sport->dma_rx_desc);
+ }
spin_lock_irqsave(&sport->port.lock, flags);
@@ -1136,7 +1168,33 @@ static void lpuart_copy_rx_to_tty(struct
spin_unlock_irqrestore(&sport->port.lock, flags);
tty_flip_buffer_push(port);
- mod_timer(&sport->lpuart_timer, jiffies + sport->dma_rx_timeout);
+
+ if (!sport->dma_eeop)
+ mod_timer(&sport->lpuart_timer,
+ jiffies + sport->dma_rx_timeout);
+}
+
+static void lpuart_dma_rx_post_handler(struct lpuart_port *sport)
+{
+ unsigned long flags;
+ unsigned long rxcount;
+
+ spin_lock_irqsave(&sport->port.lock, flags);
+
+ /* For end of packet, clear the idle flag to avoid to trigger
+ * the next transfer. Only i.MX8x lpuart support EEOP.
+ */
+ if (sport->dma_eeop && lpuart_is_32(sport)) {
+ rxcount = lpuart32_read(&sport->port, UARTWATER);
+ rxcount = rxcount >> UARTWATER_RXCNT_OFF;
+ if (!rxcount)
+ lpuart32_write(&sport->port, UARTSTAT_IDLE, UARTSTAT);
+ }
+
+ lpuart_sched_rx_dma(sport);
+
+ spin_unlock_irqrestore(&sport->port.lock, flags);
+
}
static void lpuart_dma_rx_complete(void *arg)
@@ -1144,6 +1202,8 @@ static void lpuart_dma_rx_complete(void
struct lpuart_port *sport = arg;
lpuart_copy_rx_to_tty(sport);
+ if (!sport->rx_dma_cyclic)
+ lpuart_dma_rx_post_handler(sport);
}
static void lpuart_timer_func(struct timer_list *t)
@@ -1151,13 +1211,78 @@ static void lpuart_timer_func(struct tim
struct lpuart_port *sport = from_timer(sport, t, lpuart_timer);
lpuart_copy_rx_to_tty(sport);
+ if (!sport->rx_dma_cyclic) {
+ dmaengine_terminate_async(sport->dma_rx_chan);
+ lpuart_dma_rx_post_handler(sport);
+ }
}
-static inline int lpuart_start_rx_dma(struct lpuart_port *sport)
+static int lpuart_sched_rxdma_cyclic(struct lpuart_port *sport)
+{
+ sport->dma_rx_desc = dmaengine_prep_dma_cyclic(sport->dma_rx_chan,
+ sg_dma_address(&sport->rx_sgl),
+ sport->rx_sgl.length,
+ sport->rx_sgl.length / 2,
+ DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT);
+ if (!sport->dma_rx_desc) {
+ dev_err(sport->port.dev, "Cannot prepare cyclic DMA\n");
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int lpuart_sched_rxdma_slave_sg(struct lpuart_port *sport)
+{
+ dma_sync_sg_for_device(sport->port.dev, &sport->rx_sgl, 1,
+ DMA_FROM_DEVICE);
+ sport->dma_rx_desc = dmaengine_prep_slave_sg(sport->dma_rx_chan,
+ &sport->rx_sgl,
+ 1,
+ DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT);
+ if (!sport->dma_rx_desc) {
+ dev_err(sport->port.dev, "Cannot prepare slave_sg DMA\n");
+ return -EFAULT;
+ }
+ sport->rx_ring.tail = 0;
+ sport->rx_ring.head = 0;
+
+ return 0;
+}
+
+static int lpuart_sched_rx_dma(struct lpuart_port *sport)
+{
+ unsigned long temp;
+ int ret;
+
+ if (sport->rx_dma_cyclic)
+ ret = lpuart_sched_rxdma_cyclic(sport);
+ else
+ ret = lpuart_sched_rxdma_slave_sg(sport);
+
+ sport->dma_rx_desc->callback = lpuart_dma_rx_complete;
+ sport->dma_rx_desc->callback_param = sport;
+ sport->dma_rx_cookie = dmaengine_submit(sport->dma_rx_desc);
+ dma_async_issue_pending(sport->dma_rx_chan);
+
+ if (lpuart_is_32(sport)) {
+ temp = lpuart32_read(&sport->port, UARTBAUD);
+ if (sport->dma_eeop)
+ temp |= UARTBAUD_RIDMAE;
+ temp |= UARTBAUD_RDMAE;
+ lpuart32_write(&sport->port, temp, UARTBAUD);
+ } else {
+ writeb(readb(sport->port.membase + UARTCR5) | UARTCR5_RDMAS,
+ sport->port.membase + UARTCR5);
+ }
+
+ return ret;
+}
+
+static void lpuart_get_rx_dma_rng_len(struct lpuart_port *sport)
{
- struct dma_slave_config dma_rx_sconfig = {};
- struct circ_buf *ring = &sport->rx_ring;
- int ret, nent;
int bits, baud;
struct tty_port *port = &sport->port.state->port;
struct tty_struct *tty = port->tty;
@@ -1177,6 +1302,18 @@ static inline int lpuart_start_rx_dma(st
sport->rx_dma_rng_buf_len = (1 << (fls(sport->rx_dma_rng_buf_len) - 1));
if (sport->rx_dma_rng_buf_len < 16)
sport->rx_dma_rng_buf_len = 16;
+}
+
+static inline int lpuart_start_rx_dma(struct lpuart_port *sport)
+{
+ struct dma_slave_config dma_rx_sconfig = {};
+ struct circ_buf *ring = &sport->rx_ring;
+ int ret, nent;
+
+ if (!sport->dma_eeop)
+ lpuart_get_rx_dma_rng_len(sport);
+ else
+ sport->rx_dma_rng_buf_len = PAGE_SIZE;
ring->buf = kzalloc(sport->rx_dma_rng_buf_len, GFP_ATOMIC);
if (!ring->buf)
@@ -1202,32 +1339,7 @@ static inline int lpuart_start_rx_dma(st
return ret;
}
- sport->dma_rx_desc = dmaengine_prep_dma_cyclic(sport->dma_rx_chan,
- sg_dma_address(&sport->rx_sgl),
- sport->rx_sgl.length,
- sport->rx_sgl.length / 2,
- DMA_DEV_TO_MEM,
- DMA_PREP_INTERRUPT);
- if (!sport->dma_rx_desc) {
- dev_err(sport->port.dev, "Cannot prepare cyclic DMA\n");
- return -EFAULT;
- }
-
- sport->dma_rx_desc->callback = lpuart_dma_rx_complete;
- sport->dma_rx_desc->callback_param = sport;
- sport->dma_rx_cookie = dmaengine_submit(sport->dma_rx_desc);
- dma_async_issue_pending(sport->dma_rx_chan);
-
- if (lpuart_is_32(sport)) {
- unsigned long temp = lpuart32_read(&sport->port, UARTBAUD);
-
- lpuart32_write(&sport->port, temp | UARTBAUD_RDMAE, UARTBAUD);
- } else {
- writeb(readb(sport->port.membase + UARTCR5) | UARTCR5_RDMAS,
- sport->port.membase + UARTCR5);
- }
-
- return 0;
+ return lpuart_sched_rx_dma(sport);
}
static void lpuart_dma_rx_free(struct uart_port *port)
@@ -1413,8 +1525,10 @@ static void lpuart_setup_watermark(struc
writeb(UARTSFIFO_RXUF, sport->port.membase + UARTSFIFO);
}
+ if (uart_console(&sport->port))
+ sport->rx_watermark = 1;
writeb(0, sport->port.membase + UARTTWFIFO);
- writeb(1, sport->port.membase + UARTRWFIFO);
+ writeb(sport->rx_watermark, sport->port.membase + UARTRWFIFO);
/* Restore cr2 */
writeb(cr2_saved, sport->port.membase + UARTCR2);
@@ -1435,6 +1549,7 @@ static void lpuart32_setup_watermark(str
{
unsigned long val, ctrl;
unsigned long ctrl_saved;
+ unsigned long rxiden_cnt;
ctrl = lpuart32_read(&sport->port, UARTCTRL);
ctrl_saved = ctrl;
@@ -1446,12 +1561,26 @@ static void lpuart32_setup_watermark(str
val = lpuart32_read(&sport->port, UARTFIFO);
val |= UARTFIFO_TXFE | UARTFIFO_RXFE;
val |= UARTFIFO_TXFLUSH | UARTFIFO_RXFLUSH;
+ val &= ~(UARTFIFO_RXIDEN_MASK << UARTFIFO_RXIDEN_OFF);
+ rxiden_cnt = sport->dma_eeop ? 0 : UARTFIFO_RXIDEN_RDRF;
+ val |= ((rxiden_cnt & UARTFIFO_RXIDEN_MASK) <<
+ UARTFIFO_RXIDEN_OFF);
lpuart32_write(&sport->port, val, UARTFIFO);
/* set the watermark */
- val = (0x1 << UARTWATER_RXWATER_OFF) | (0x0 << UARTWATER_TXWATER_OFF);
+ if (uart_console(&sport->port))
+ sport->rx_watermark = 1;
+ val = (sport->rx_watermark << UARTWATER_RXWATER_OFF) |
+ (0x0 << UARTWATER_TXWATER_OFF);
lpuart32_write(&sport->port, val, UARTWATER);
+ /* set RTS watermark */
+ if (!uart_console(&sport->port)) {
+ val = lpuart32_read(&sport->port, UARTMODIR);
+ val = (sport->rxfifo_size >> 1) << UARTMODIR_RTSWATER_S;
+ lpuart32_write(&sport->port, val, UARTMODIR);
+ }
+
/* Restore cr2 */
lpuart32_write(&sport->port, ctrl_saved, UARTCTRL);
}
@@ -1463,17 +1592,29 @@ static void lpuart32_setup_watermark_ena
lpuart32_setup_watermark(sport);
temp = lpuart32_read(&sport->port, UARTCTRL);
- temp |= UARTCTRL_RE | UARTCTRL_TE | UARTCTRL_ILIE;
+ temp |= UARTCTRL_RE | UARTCTRL_TE;
+ temp |= UARTCTRL_IDLECFG << UARTCTRL_IDLECFG_OFF;
lpuart32_write(&sport->port, temp, UARTCTRL);
}
static void rx_dma_timer_init(struct lpuart_port *sport)
{
+ if (sport->dma_eeop)
+ return;
+
timer_setup(&sport->lpuart_timer, lpuart_timer_func, 0);
sport->lpuart_timer.expires = jiffies + sport->dma_rx_timeout;
add_timer(&sport->lpuart_timer);
}
+static void lpuart_del_timer_sync(struct lpuart_port *sport)
+{
+ if (sport->dma_eeop)
+ return;
+
+ del_timer_sync(&sport->lpuart_timer);
+}
+
static void lpuart_tx_dma_startup(struct lpuart_port *sport)
{
u32 uartbaud;
@@ -1537,19 +1678,23 @@ static int lpuart_startup(struct uart_po
return 0;
}
+static void lpuart32_hw_disable(struct lpuart_port *sport)
+{
+ unsigned long temp;
+
+ temp = lpuart32_read(&sport->port, UARTCTRL);
+ temp &= ~(UARTCTRL_RIE | UARTCTRL_ILIE | UARTCTRL_RE |
+ UARTCTRL_TIE | UARTCTRL_TE);
+ lpuart32_write(&sport->port, temp, UARTCTRL);
+}
+
static void lpuart32_configure(struct lpuart_port *sport)
{
unsigned long temp;
- if (sport->lpuart_dma_rx_use) {
- /* RXWATER must be 0 */
- temp = lpuart32_read(&sport->port, UARTWATER);
- temp &= ~(UARTWATER_WATER_MASK << UARTWATER_RXWATER_OFF);
- lpuart32_write(&sport->port, temp, UARTWATER);
- }
temp = lpuart32_read(&sport->port, UARTCTRL);
if (!sport->lpuart_dma_rx_use)
- temp |= UARTCTRL_RIE;
+ temp |= UARTCTRL_RIE | UARTCTRL_ILIE;
if (!sport->lpuart_dma_tx_use)
temp |= UARTCTRL_TIE;
lpuart32_write(&sport->port, temp, UARTCTRL);
@@ -1593,12 +1738,12 @@ static int lpuart32_startup(struct uart_
spin_lock_irqsave(&sport->port.lock, flags);
- lpuart32_setup_watermark_enable(sport);
-
+ lpuart32_hw_disable(sport);
lpuart_rx_dma_startup(sport);
lpuart_tx_dma_startup(sport);
+ lpuart32_setup_watermark_enable(sport);
lpuart32_configure(sport);
spin_unlock_irqrestore(&sport->port.lock, flags);
@@ -1608,7 +1753,7 @@ static int lpuart32_startup(struct uart_
static void lpuart_dma_shutdown(struct lpuart_port *sport)
{
if (sport->lpuart_dma_rx_use) {
- del_timer_sync(&sport->lpuart_timer);
+ lpuart_del_timer_sync(sport);
lpuart_dma_rx_free(&sport->port);
sport->lpuart_dma_rx_use = false;
}
@@ -1651,11 +1796,22 @@ static void lpuart32_shutdown(struct uar
spin_lock_irqsave(&port->lock, flags);
+ /* clear statue */
+ temp = lpuart32_read(&sport->port, UARTSTAT);
+ lpuart32_write(&sport->port, temp, UARTSTAT);
+
+ /* disable Rx/Tx DMA */
+ temp = lpuart32_read(port, UARTBAUD);
+ temp &= ~(UARTBAUD_TDMAE | UARTBAUD_RDMAE | UARTBAUD_RIDMAE);
+ lpuart32_write(port, temp, UARTBAUD);
+
/* disable Rx/Tx and interrupts */
temp = lpuart32_read(port, UARTCTRL);
- temp &= ~(UARTCTRL_TE | UARTCTRL_RE |
- UARTCTRL_TIE | UARTCTRL_TCIE | UARTCTRL_RIE);
+ temp &= ~(UARTCTRL_TE | UARTCTRL_RE | UARTCTRL_TIE |
+ UARTCTRL_TCIE | UARTCTRL_RIE | UARTCTRL_ILIE |
+ UARTCTRL_LOOPS);
lpuart32_write(port, temp, UARTCTRL);
+ lpuart32_write(port, 0, UARTMODIR);
spin_unlock_irqrestore(&port->lock, flags);
@@ -1752,10 +1908,10 @@ lpuart_set_termios(struct uart_port *por
* baud rate and restart Rx DMA path.
*
* Since timer function acqures sport->port.lock, need to stop before
- * acquring same lock because otherwise del_timer_sync() can deadlock.
+ * acquring same lock because otherwise lpuart_del_timer_sync() can deadlock.
*/
if (old && sport->lpuart_dma_rx_use) {
- del_timer_sync(&sport->lpuart_timer);
+ lpuart_del_timer_sync(sport);
lpuart_dma_rx_free(&sport->port);
}
@@ -1967,10 +2123,10 @@ lpuart32_set_termios(struct uart_port *p
* baud rate and restart Rx DMA path.
*
* Since timer function acqures sport->port.lock, need to stop before
- * acquring same lock because otherwise del_timer_sync() can deadlock.
+ * acquring same lock because otherwise lpuart_del_timer_sync() can deadlock.
*/
if (old && sport->lpuart_dma_rx_use) {
- del_timer_sync(&sport->lpuart_timer);
+ lpuart_del_timer_sync(sport);
lpuart_dma_rx_free(&sport->port);
}
@@ -2483,6 +2639,10 @@ static int lpuart_probe(struct platform_
sport->port.dev = &pdev->dev;
sport->port.type = PORT_LPUART;
sport->devtype = sdata->devtype;
+ sport->rx_dma_cyclic = sdata->rx_dma_cyclic;
+ sport->rx_watermark = sdata->rx_watermark;
+ sport->dma_eeop = is_imx8qxp_lpuart(sport);
+
ret = platform_get_irq(pdev, 0);
if (ret < 0)
return ret;
@@ -2633,7 +2793,7 @@ static int lpuart_suspend(struct device
* Rx DMA path before suspend and start Rx DMA path on resume.
*/
if (irq_wake) {
- del_timer_sync(&sport->lpuart_timer);
+ lpuart_del_timer_sync(sport);
lpuart_dma_rx_free(&sport->port);
}