summaryrefslogtreecommitdiff
path: root/drivers/tty/serial/fsl_lpuart.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/tty/serial/fsl_lpuart.c')
-rw-r--r--drivers/tty/serial/fsl_lpuart.c715
1 files changed, 539 insertions, 176 deletions
diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index ac5112def40d..de1475ed947d 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -17,6 +17,10 @@
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_dma.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
#include <linux/serial_core.h>
#include <linux/slab.h>
#include <linux/tty_flip.h>
@@ -130,6 +134,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
@@ -178,7 +183,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
@@ -196,6 +201,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
@@ -209,6 +215,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
@@ -229,8 +237,12 @@
#define GLOBAL_RST_MIN_US 20
#define GLOBAL_RST_MAX_US 40
+#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)
+#define UART_AUTOSUSPEND_TIMEOUT 3000
#define DRIVER_NAME "fsl-lpuart"
#define DEV_NAME "ttyLP"
@@ -239,13 +251,12 @@
/* IMX lpuart has four extra unused regs located at the beginning */
#define IMX_REG_OFF 0x10
-static DEFINE_IDA(fsl_lpuart_ida);
-
enum lpuart_type {
VF610_LPUART,
LS1021A_LPUART,
LS1028A_LPUART,
IMX7ULP_LPUART,
+ IMX8ULP_LPUART,
IMX8QXP_LPUART,
};
@@ -257,8 +268,11 @@ struct lpuart_port {
unsigned int txfifo_size;
unsigned int rxfifo_size;
+ u8 rx_watermark;
+ bool dma_eeop;
bool lpuart_dma_tx_use;
bool lpuart_dma_rx_use;
+ bool dma_rx_chan_active;
struct dma_chan *dma_tx_chan;
struct dma_chan *dma_rx_chan;
struct dma_async_tx_descriptor *dma_tx_desc;
@@ -273,42 +287,57 @@ struct lpuart_port {
struct scatterlist rx_sgl, tx_sgl[2];
struct circ_buf rx_ring;
int rx_dma_rng_buf_len;
+ int rx_dma_periods;
unsigned int dma_tx_nents;
wait_queue_head_t dma_wait;
- bool id_allocated;
+ bool is_cs7; /* Set to true when character size is 7 */
+ /* and the parity is enabled */
};
struct lpuart_soc_data {
enum lpuart_type devtype;
char iotype;
u8 reg_off;
+ u8 rx_watermark;
};
static const struct lpuart_soc_data vf_data = {
.devtype = VF610_LPUART,
.iotype = UPIO_MEM,
+ .rx_watermark = 1,
};
static const struct lpuart_soc_data ls1021a_data = {
.devtype = LS1021A_LPUART,
.iotype = UPIO_MEM32BE,
+ .rx_watermark = 0,
};
static const struct lpuart_soc_data ls1028a_data = {
.devtype = LS1028A_LPUART,
.iotype = UPIO_MEM32,
+ .rx_watermark = 0,
};
static struct lpuart_soc_data imx7ulp_data = {
.devtype = IMX7ULP_LPUART,
.iotype = UPIO_MEM32,
.reg_off = IMX_REG_OFF,
+ .rx_watermark = 0,
+};
+
+static struct lpuart_soc_data imx8ulp_data = {
+ .devtype = IMX8ULP_LPUART,
+ .iotype = UPIO_MEM32,
+ .reg_off = IMX_REG_OFF,
+ .rx_watermark = 3,
};
static struct lpuart_soc_data imx8qxp_data = {
.devtype = IMX8QXP_LPUART,
.iotype = UPIO_MEM32,
.reg_off = IMX_REG_OFF,
+ .rx_watermark = 31,
};
static const struct of_device_id lpuart_dt_ids[] = {
@@ -316,6 +345,7 @@ static const struct of_device_id lpuart_dt_ids[] = {
{ .compatible = "fsl,ls1021a-lpuart", .data = &ls1021a_data, },
{ .compatible = "fsl,ls1028a-lpuart", .data = &ls1028a_data, },
{ .compatible = "fsl,imx7ulp-lpuart", .data = &imx7ulp_data, },
+ { .compatible = "fsl,imx8ulp-lpuart", .data = &imx8ulp_data, },
{ .compatible = "fsl,imx8qxp-lpuart", .data = &imx8qxp_data, },
{ /* sentinel */ }
};
@@ -323,6 +353,7 @@ MODULE_DEVICE_TABLE(of, lpuart_dt_ids);
/* Forward declare this for the dma callbacks*/
static void lpuart_dma_tx_complete(void *arg);
+static void lpuart_dma_rx_free(struct uart_port *port, bool dma_terminate);
static inline bool is_layerscape_lpuart(struct lpuart_port *sport)
{
@@ -335,6 +366,11 @@ static inline bool is_imx7ulp_lpuart(struct lpuart_port *sport)
return sport->devtype == IMX7ULP_LPUART;
}
+static inline bool is_imx8ulp_lpuart(struct lpuart_port *sport)
+{
+ return sport->devtype == IMX8ULP_LPUART;
+}
+
static inline bool is_imx8qxp_lpuart(struct lpuart_port *sport)
{
return sport->devtype == IMX8QXP_LPUART;
@@ -413,7 +449,7 @@ static int lpuart_global_reset(struct lpuart_port *sport)
return ret;
}
- if (is_imx7ulp_lpuart(sport) || is_imx8qxp_lpuart(sport)) {
+ if (is_imx7ulp_lpuart(sport) || is_imx8ulp_lpuart(sport) || is_imx8qxp_lpuart(sport)) {
global_addr = port->membase + UART_GLOBAL - IMX_REG_OFF;
writel(UART_GLOBAL_RST, global_addr);
usleep_range(GLOBAL_RST_MIN_US, GLOBAL_RST_MAX_US);
@@ -835,6 +871,20 @@ static void lpuart32_start_tx(struct uart_port *port)
}
}
+static void
+lpuart_uart_pm(struct uart_port *port, unsigned int state, unsigned int oldstate)
+{
+ switch (state) {
+ case UART_PM_STATE_OFF:
+ pm_runtime_mark_last_busy(port->dev);
+ pm_runtime_put_autosuspend(port->dev);
+ break;
+ default:
+ pm_runtime_get_sync(port->dev);
+ break;
+ }
+}
+
/* return TIOCSER_TEMT when transmitter is not busy */
static unsigned int lpuart_tx_empty(struct uart_port *port)
{
@@ -924,7 +974,8 @@ static void lpuart_rxint(struct lpuart_port *sport)
sport->port.sysrq = 0;
}
- tty_insert_flip_char(port, rx, flg);
+ if (tty_insert_flip_char(port, rx, flg) == 0)
+ sport->port.icount.buf_overrun++;
}
out:
@@ -1017,7 +1068,11 @@ static void lpuart32_rxint(struct lpuart_port *sport)
flg = TTY_OVERRUN;
}
- tty_insert_flip_char(port, rx, flg);
+ if (sport->is_cs7)
+ rx &= 0x7F;
+
+ if (tty_insert_flip_char(port, rx, flg) == 0)
+ sport->port.icount.buf_overrun++;
}
out:
@@ -1066,11 +1121,13 @@ static irqreturn_t lpuart32_int(int irq, void *dev_id)
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 inline void lpuart_handle_sysrq_chars(struct uart_port *port,
unsigned char *p, int count)
{
@@ -1101,16 +1158,8 @@ static void lpuart_handle_sysrq(struct lpuart_port *sport)
}
}
-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 dma_chan *chan = sport->dma_rx_chan;
- 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);
@@ -1162,8 +1211,34 @@ static void lpuart_copy_rx_to_tty(struct lpuart_port *sport)
writeb(cr2, sport->port.membase + UARTCR2);
}
}
+}
+
+static inline int lpuart_tty_insert_flip_string(struct tty_port *port,
+ unsigned char *chars, size_t size, bool is_cs7)
+{
+ int i;
+
+ if (is_cs7)
+ for (i = 0; i < size; i++)
+ chars[i] &= 0x7F;
+
+ return tty_insert_flip_string(port, chars, size);
+}
+
+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 dma_chan *chan = sport->dma_rx_chan;
+ struct circ_buf *ring = &sport->rx_ring;
+ unsigned long flags;
+ int count = 0, copied;
- async_tx_ack(sport->dma_rx_desc);
+ if (!is_imx8qxp_lpuart(sport) && !is_imx8ulp_lpuart(sport)) {
+ lpuart_rx_error_stat(sport);
+ async_tx_ack(sport->dma_rx_desc);
+ }
spin_lock_irqsave(&sport->port.lock, flags);
@@ -1188,6 +1263,13 @@ static void lpuart_copy_rx_to_tty(struct lpuart_port *sport)
ring->head = sport->rx_sgl.length - state.residue;
BUG_ON(ring->head > sport->rx_sgl.length);
+ if (sport->dma_eeop) {
+ unsigned int tcd_size;
+
+ /* Calculate the tail. */
+ tcd_size = sg_dma_len(&sport->rx_sgl) / sport->rx_dma_periods;
+ ring->tail = ((ring->head - 1) / tcd_size) * tcd_size;
+ }
/*
* Silent handling of keys pressed in the sysrq timeframe
*/
@@ -1211,20 +1293,26 @@ static void lpuart_copy_rx_to_tty(struct lpuart_port *sport)
if (ring->head < ring->tail) {
count = sport->rx_sgl.length - ring->tail;
- tty_insert_flip_string(port, ring->buf + ring->tail, count);
+ copied = lpuart_tty_insert_flip_string(port, ring->buf + ring->tail,
+ count, sport->is_cs7);
+ if (copied != count)
+ sport->port.icount.buf_overrun++;
ring->tail = 0;
- sport->port.icount.rx += count;
+ sport->port.icount.rx += copied;
}
/* Finally we read data from tail to head */
if (ring->tail < ring->head) {
count = ring->head - ring->tail;
- tty_insert_flip_string(port, ring->buf + ring->tail, count);
+ copied = lpuart_tty_insert_flip_string(port, ring->buf + ring->tail,
+ count, sport->is_cs7);
+ if (copied != count)
+ sport->port.icount.buf_overrun++;
/* Wrap ring->head if needed */
if (ring->head >= sport->rx_sgl.length)
ring->head = 0;
ring->tail = ring->head;
- sport->port.icount.rx += count;
+ sport->port.icount.rx += copied;
}
exit:
@@ -1234,7 +1322,10 @@ exit:
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_complete(void *arg)
@@ -1251,16 +1342,50 @@ static void lpuart_timer_func(struct timer_list *t)
lpuart_copy_rx_to_tty(sport);
}
-static inline int lpuart_start_rx_dma(struct lpuart_port *sport)
+static int lpuart_sched_rx_dma(struct lpuart_port *sport)
+{
+ unsigned long temp;
+
+ if (!sport->dma_rx_chan_active)
+ return -EINVAL;
+
+ sport->rx_dma_periods = 2;
+ 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 / sport->rx_dma_periods,
+ 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)) {
+ 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 0;
+}
+
+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;
struct ktermios *termios = &tty->termios;
- struct dma_chan *chan = sport->dma_rx_chan;
baud = tty_get_baud_rate(tty);
@@ -1276,6 +1401,19 @@ static inline int lpuart_start_rx_dma(struct lpuart_port *sport)
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;
+ struct dma_chan *chan = sport->dma_rx_chan;
+ 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)
@@ -1302,41 +1440,25 @@ static inline int lpuart_start_rx_dma(struct lpuart_port *sport)
return ret;
}
- sport->dma_rx_desc = dmaengine_prep_dma_cyclic(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(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);
+ sport->dma_rx_chan_active = true;
+ ret = lpuart_sched_rx_dma(sport);
+ if (ret) {
+ sport->dma_rx_chan_active = false;
+ lpuart_dma_rx_free(&sport->port, false);
}
- return 0;
+ return ret;
}
-static void lpuart_dma_rx_free(struct uart_port *port)
+static void lpuart_dma_rx_free(struct uart_port *port, bool dma_terminate)
{
struct lpuart_port *sport = container_of(port,
struct lpuart_port, port);
struct dma_chan *chan = sport->dma_rx_chan;
- dmaengine_terminate_all(chan);
+ if (dma_terminate)
+ dmaengine_terminate_sync(sport->dma_rx_chan);
+
dma_unmap_sg(chan->device->dev, &sport->rx_sgl, 1, DMA_FROM_DEVICE);
kfree(sport->rx_ring.buf);
sport->rx_ring.tail = 0;
@@ -1435,9 +1557,9 @@ static int lpuart32_config_rs485(struct uart_port *port,
* Note: UART is assumed to be active high.
*/
if (rs485->flags & SER_RS485_RTS_ON_SEND)
- modem &= ~UARTMODEM_TXRTSPOL;
- else if (rs485->flags & SER_RS485_RTS_AFTER_SEND)
modem |= UARTMODEM_TXRTSPOL;
+ else if (rs485->flags & SER_RS485_RTS_AFTER_SEND)
+ modem &= ~UARTMODEM_TXRTSPOL;
}
/* Store the new configuration */
@@ -1548,8 +1670,10 @@ static void lpuart_setup_watermark(struct lpuart_port *sport)
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);
@@ -1570,6 +1694,7 @@ static void lpuart32_setup_watermark(struct lpuart_port *sport)
{
unsigned long val, ctrl;
unsigned long ctrl_saved;
+ unsigned long rxiden_cnt;
ctrl = lpuart32_read(&sport->port, UARTCTRL);
ctrl_saved = ctrl;
@@ -1581,12 +1706,26 @@ static void lpuart32_setup_watermark(struct lpuart_port *sport)
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);
}
@@ -1598,17 +1737,29 @@ static void lpuart32_setup_watermark_enable(struct lpuart_port *sport)
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_request_dma(struct lpuart_port *sport)
{
sport->dma_tx_chan = dma_request_chan(sport->port.dev, "tx");
@@ -1695,10 +1846,23 @@ err:
sport->lpuart_dma_rx_use = false;
}
+static void lpuart_hw_setup(struct lpuart_port *sport)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&sport->port.lock, flags);
+
+ lpuart_setup_watermark_enable(sport);
+
+ lpuart_rx_dma_startup(sport);
+ lpuart_tx_dma_startup(sport);
+
+ spin_unlock_irqrestore(&sport->port.lock, flags);
+}
+
static int lpuart_startup(struct uart_port *port)
{
struct lpuart_port *sport = container_of(port, struct lpuart_port, port);
- unsigned long flags;
unsigned char temp;
/* determine FIFO size and enable FIFO mode */
@@ -1712,42 +1876,63 @@ static int lpuart_startup(struct uart_port *port)
UARTPFIFO_FIFOSIZE_MASK);
lpuart_request_dma(sport);
+ lpuart_hw_setup(sport);
- spin_lock_irqsave(&sport->port.lock, flags);
-
- lpuart_setup_watermark_enable(sport);
-
- lpuart_rx_dma_startup(sport);
- lpuart_tx_dma_startup(sport);
+ return 0;
+}
- spin_unlock_irqrestore(&sport->port.lock, flags);
+static void lpuart32_hw_disable(struct lpuart_port *sport)
+{
+ unsigned long temp;
- return 0;
+ 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);
}
+static void lpuart32_hw_setup(struct lpuart_port *sport)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&sport->port.lock, flags);
+
+ 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);
+}
+
static int lpuart32_startup(struct uart_port *port)
{
struct lpuart_port *sport = container_of(port, struct lpuart_port, port);
- unsigned long flags;
+ struct tty_port *tty_port = &sport->port.state->port;
unsigned long temp;
+ int ret;
+
+ /* some modem may need reset */
+ if (!tty_port_suspended(tty_port)) {
+ ret = device_reset(sport->port.dev);
+ if (ret && ret != -ENOENT)
+ return ret;
+ }
/* determine FIFO size */
temp = lpuart32_read(&sport->port, UARTFIFO);
@@ -1771,32 +1956,23 @@ static int lpuart32_startup(struct uart_port *port)
}
lpuart_request_dma(sport);
+ lpuart32_hw_setup(sport);
- spin_lock_irqsave(&sport->port.lock, flags);
-
- lpuart32_setup_watermark_enable(sport);
-
- lpuart_rx_dma_startup(sport);
- lpuart_tx_dma_startup(sport);
-
- lpuart32_configure(sport);
-
- spin_unlock_irqrestore(&sport->port.lock, flags);
return 0;
}
static void lpuart_dma_shutdown(struct lpuart_port *sport)
{
if (sport->lpuart_dma_rx_use) {
- del_timer_sync(&sport->lpuart_timer);
- lpuart_dma_rx_free(&sport->port);
+ lpuart_del_timer_sync(sport);
+ lpuart_dma_rx_free(&sport->port, true);
}
if (sport->lpuart_dma_tx_use) {
- if (wait_event_interruptible(sport->dma_wait,
- !sport->dma_tx_in_progress) != false) {
+ if (wait_event_interruptible_timeout(sport->dma_wait,
+ !sport->dma_tx_in_progress, msecs_to_jiffies(300)) <= 0) {
sport->dma_tx_in_progress = false;
- dmaengine_terminate_all(sport->dma_tx_chan);
+ dmaengine_terminate_sync(sport->dma_tx_chan);
}
}
@@ -1820,6 +1996,8 @@ static void lpuart_shutdown(struct uart_port *port)
UARTCR2_TIE | UARTCR2_TCIE | UARTCR2_RIE);
writeb(temp, port->membase + UARTCR2);
+ if (sport->lpuart_dma_rx_use)
+ sport->dma_rx_chan_active = false;
spin_unlock_irqrestore(&port->lock, flags);
lpuart_dma_shutdown(sport);
@@ -1834,12 +2012,25 @@ static void lpuart32_shutdown(struct uart_port *port)
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);
+ if (sport->lpuart_dma_rx_use)
+ sport->dma_rx_chan_active = false;
spin_unlock_irqrestore(&port->lock, flags);
lpuart_dma_shutdown(sport);
@@ -1935,11 +2126,12 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios,
* 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_dma_rx_free(&sport->port);
+ sport->dma_rx_chan_active = false;
+ lpuart_del_timer_sync(sport);
+ lpuart_dma_rx_free(&sport->port, true);
}
spin_lock_irqsave(&sport->port.lock, flags);
@@ -2098,6 +2290,8 @@ lpuart32_set_termios(struct uart_port *port, struct ktermios *termios,
ctrl = old_ctrl = lpuart32_read(&sport->port, UARTCTRL);
bd = lpuart32_read(&sport->port, UARTBAUD);
modem = lpuart32_read(&sport->port, UARTMODIR);
+ sport->is_cs7 = false;
+
/*
* only support CS8 and CS7, and for CS7 must enable PE.
* supported mode:
@@ -2173,11 +2367,12 @@ lpuart32_set_termios(struct uart_port *port, struct ktermios *termios,
* 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_dma_rx_free(&sport->port);
+ sport->dma_rx_chan_active = false;
+ lpuart_del_timer_sync(sport);
+ lpuart_dma_rx_free(&sport->port, true);
}
spin_lock_irqsave(&sport->port.lock, flags);
@@ -2206,6 +2401,7 @@ lpuart32_set_termios(struct uart_port *port, struct ktermios *termios,
uart_update_timeout(port, termios->c_cflag, baud);
/* wait transmit engin complete */
+ lpuart32_write(&sport->port, 0, UARTMODIR);
lpuart32_wait_bit_set(&sport->port, UARTSTAT, UARTSTAT_TC);
/* disable transmit and receive */
@@ -2218,6 +2414,9 @@ lpuart32_set_termios(struct uart_port *port, struct ktermios *termios,
lpuart32_write(&sport->port, ctrl, UARTCTRL);
/* restore control register */
+ if ((ctrl & (UARTCTRL_PE | UARTCTRL_M)) == UARTCTRL_PE)
+ sport->is_cs7 = true;
+
if (old && sport->lpuart_dma_rx_use) {
if (!lpuart_start_rx_dma(sport))
rx_dma_timer_init(sport);
@@ -2279,6 +2478,7 @@ static const struct uart_ops lpuart_pops = {
.break_ctl = lpuart_break_ctl,
.startup = lpuart_startup,
.shutdown = lpuart_shutdown,
+ .pm = lpuart_uart_pm,
.set_termios = lpuart_set_termios,
.type = lpuart_type,
.request_port = lpuart_request_port,
@@ -2303,6 +2503,7 @@ static const struct uart_ops lpuart32_pops = {
.break_ctl = lpuart32_break_ctl,
.startup = lpuart32_startup,
.shutdown = lpuart32_shutdown,
+ .pm = lpuart_uart_pm,
.set_termios = lpuart32_set_termios,
.type = lpuart_type,
.request_port = lpuart_request_port,
@@ -2644,6 +2845,29 @@ static struct uart_driver lpuart_reg = {
.cons = LPUART_CONSOLE,
};
+static int lpuart_attach_pd(struct device *dev)
+{
+ struct device *pd_uart;
+ struct device_link *link;
+
+ if (dev->pm_domain)
+ return 0;
+
+ pd_uart = dev_pm_domain_attach_by_name(dev, "uart");
+ if (IS_ERR(pd_uart))
+ return PTR_ERR(pd_uart);
+ link = device_link_add(dev, pd_uart, DL_FLAG_STATELESS |
+ DL_FLAG_PM_RUNTIME |
+ DL_FLAG_RPM_ACTIVE);
+ if (IS_ERR(link)) {
+ dev_err(dev, "Failed to add device_link to uart pd: %ld\n",
+ PTR_ERR(link));
+ return PTR_ERR(link);
+ }
+
+ return 0;
+}
+
static int lpuart_probe(struct platform_device *pdev)
{
const struct lpuart_soc_data *sdata = of_device_get_match_data(&pdev->dev);
@@ -2666,6 +2890,9 @@ static int lpuart_probe(struct platform_device *pdev)
sport->port.dev = &pdev->dev;
sport->port.type = PORT_LPUART;
sport->devtype = sdata->devtype;
+ sport->rx_watermark = sdata->rx_watermark;
+ sport->dma_eeop = is_imx8qxp_lpuart(sport) || is_imx8ulp_lpuart(sport);
+
ret = platform_get_irq(pdev, 0);
if (ret < 0)
return ret;
@@ -2683,6 +2910,10 @@ static int lpuart_probe(struct platform_device *pdev)
else
sport->port.rs485_config = lpuart_config_rs485;
+ ret = lpuart_attach_pd(&pdev->dev);
+ if (ret)
+ return ret;
+
sport->ipg_clk = devm_clk_get(&pdev->dev, "ipg");
if (IS_ERR(sport->ipg_clk)) {
ret = PTR_ERR(sport->ipg_clk);
@@ -2702,23 +2933,18 @@ static int lpuart_probe(struct platform_device *pdev)
ret = of_alias_get_id(np, "serial");
if (ret < 0) {
- ret = ida_simple_get(&fsl_lpuart_ida, 0, UART_NR, GFP_KERNEL);
- if (ret < 0) {
- dev_err(&pdev->dev, "port line is full, add device failed\n");
- return ret;
- }
- sport->id_allocated = true;
+ dev_err(&pdev->dev, "failed to get alias id, errno %d\n", ret);
+ return ret;
}
if (ret >= ARRAY_SIZE(lpuart_ports)) {
dev_err(&pdev->dev, "serial%d out of range\n", ret);
- ret = -EINVAL;
- goto failed_out_of_range;
+ return -EINVAL;
}
sport->port.line = ret;
ret = lpuart_enable_clks(sport);
if (ret)
- goto failed_clock_enable;
+ return ret;
sport->port.uartclk = lpuart_get_baud_clk_rate(sport);
lpuart_ports[sport->port.line] = sport;
@@ -2738,6 +2964,11 @@ static int lpuart_probe(struct platform_device *pdev)
if (ret)
goto failed_irq_request;
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_set_autosuspend_delay(&pdev->dev, UART_AUTOSUSPEND_TIMEOUT);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+
ret = uart_add_one_port(&lpuart_reg, &sport->port);
if (ret)
goto failed_attach_port;
@@ -2765,12 +2996,11 @@ failed_get_rs485:
failed_reset:
uart_remove_one_port(&lpuart_reg, &sport->port);
failed_attach_port:
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_dont_use_autosuspend(&pdev->dev);
failed_irq_request:
lpuart_disable_clks(sport);
-failed_clock_enable:
-failed_out_of_range:
- if (sport->id_allocated)
- ida_simple_remove(&fsl_lpuart_ida, sport->port.line);
return ret;
}
@@ -2780,9 +3010,6 @@ static int lpuart_remove(struct platform_device *pdev)
uart_remove_one_port(&lpuart_reg, &sport->port);
- if (sport->id_allocated)
- ida_simple_remove(&fsl_lpuart_ida, sport->port.line);
-
lpuart_disable_clks(sport);
if (sport->dma_tx_chan)
@@ -2791,100 +3018,237 @@ static int lpuart_remove(struct platform_device *pdev)
if (sport->dma_rx_chan)
dma_release_channel(sport->dma_rx_chan);
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_dont_use_autosuspend(&pdev->dev);
return 0;
}
-static int __maybe_unused lpuart_suspend(struct device *dev)
+static int __maybe_unused lpuart_runtime_suspend(struct device *dev)
{
- struct lpuart_port *sport = dev_get_drvdata(dev);
- unsigned long temp;
- bool irq_wake;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lpuart_port *sport = platform_get_drvdata(pdev);
- if (lpuart_is_32(sport)) {
- /* disable Rx/Tx and interrupts */
- temp = lpuart32_read(&sport->port, UARTCTRL);
- temp &= ~(UARTCTRL_TE | UARTCTRL_TIE | UARTCTRL_TCIE);
- lpuart32_write(&sport->port, temp, UARTCTRL);
- } else {
- /* disable Rx/Tx and interrupts */
- temp = readb(sport->port.membase + UARTCR2);
- temp &= ~(UARTCR2_TE | UARTCR2_TIE | UARTCR2_TCIE);
- writeb(temp, sport->port.membase + UARTCR2);
- }
+ lpuart_disable_clks(sport);
- uart_suspend_port(&lpuart_reg, &sport->port);
+ return 0;
+};
- /* uart_suspend_port() might set wakeup flag */
- irq_wake = irqd_is_wakeup_set(irq_get_irq_data(sport->port.irq));
+static int __maybe_unused lpuart_runtime_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lpuart_port *sport = platform_get_drvdata(pdev);
- if (sport->lpuart_dma_rx_use) {
- /*
- * EDMA driver during suspend will forcefully release any
- * non-idle DMA channels. If port wakeup is enabled or if port
- * is console port or 'no_console_suspend' is set the Rx DMA
- * cannot resume as as expected, hence gracefully release the
- * Rx DMA path before suspend and start Rx DMA path on resume.
- */
- if (irq_wake) {
- del_timer_sync(&sport->lpuart_timer);
- lpuart_dma_rx_free(&sport->port);
- }
+ return lpuart_enable_clks(sport);
+};
- /* Disable Rx DMA to use UART port as wakeup source */
- if (lpuart_is_32(sport)) {
- temp = lpuart32_read(&sport->port, UARTBAUD);
- lpuart32_write(&sport->port, temp & ~UARTBAUD_RDMAE,
- UARTBAUD);
+static void serial_lpuart_enable_wakeup(struct lpuart_port *sport, bool on)
+{
+ unsigned int val;
+
+ if (lpuart_is_32(sport)) {
+ val = lpuart32_read(&sport->port, UARTCTRL);
+ if (on) {
+ /* set rx_watermark to 0 in wakeup source mode */
+ lpuart32_write(&sport->port, 0, UARTWATER);
+ val |= UARTCTRL_RIE;
} else {
- writeb(readb(sport->port.membase + UARTCR5) &
- ~UARTCR5_RDMAS, sport->port.membase + UARTCR5);
+ val &= ~UARTCTRL_RIE;
}
+ lpuart32_write(&sport->port, val, UARTCTRL);
+ } else {
+ val = readb(sport->port.membase + UARTCR2);
+ if (on)
+ val |= UARTCR2_RIE;
+ else
+ val &= ~UARTCR2_RIE;
+ writeb(val, sport->port.membase + UARTCR2);
}
+}
- if (sport->lpuart_dma_tx_use) {
- sport->dma_tx_in_progress = false;
- dmaengine_terminate_all(sport->dma_tx_chan);
+static bool lpuart_uport_is_active(struct lpuart_port *sport)
+{
+ struct tty_port *port = &sport->port.state->port;
+ struct tty_struct *tty;
+ struct device *tty_dev;
+ int may_wake = 0;
+
+ tty = tty_port_tty_get(port);
+ if (tty) {
+ tty_dev = tty->dev;
+ may_wake = device_may_wakeup(tty_dev);
+ tty_kref_put(tty);
}
- if (sport->port.suspended && !irq_wake)
- lpuart_disable_clks(sport);
+ if ((tty_port_initialized(port) && may_wake) ||
+ (!console_suspend_enabled && uart_console(&sport->port)))
+ return true;
- return 0;
+ return false;
}
-static int __maybe_unused lpuart_resume(struct device *dev)
+static int lpuart_suspend_noirq(struct device *dev)
{
struct lpuart_port *sport = dev_get_drvdata(dev);
bool irq_wake = irqd_is_wakeup_set(irq_get_irq_data(sport->port.irq));
- if (sport->port.suspended && !irq_wake)
- lpuart_enable_clks(sport);
+ if (lpuart_uport_is_active(sport))
+ serial_lpuart_enable_wakeup(sport, !!irq_wake);
- if (lpuart_is_32(sport))
- lpuart32_setup_watermark_enable(sport);
- else
- lpuart_setup_watermark_enable(sport);
+ pinctrl_pm_select_sleep_state(dev);
- if (sport->lpuart_dma_rx_use) {
- if (irq_wake) {
- if (!lpuart_start_rx_dma(sport))
- rx_dma_timer_init(sport);
- else
- sport->lpuart_dma_rx_use = false;
+ return 0;
+}
+
+static int lpuart_resume_noirq(struct device *dev)
+{
+ struct lpuart_port *sport = dev_get_drvdata(dev);
+ unsigned int val;
+
+ pinctrl_pm_select_default_state(dev);
+
+ if (lpuart_uport_is_active(sport)) {
+ serial_lpuart_enable_wakeup(sport, false);
+
+ /* clear the wakeup flags */
+ if (lpuart_is_32(sport)) {
+ val = lpuart32_read(&sport->port, UARTSTAT);
+ lpuart32_write(&sport->port, val, UARTSTAT);
}
}
- lpuart_tx_dma_startup(sport);
+ return 0;
+}
- if (lpuart_is_32(sport))
- lpuart32_configure(sport);
+static int __maybe_unused lpuart_suspend(struct device *dev)
+{
+ struct lpuart_port *sport = dev_get_drvdata(dev);
+ unsigned long temp;
+ unsigned long flags;
+ uart_suspend_port(&lpuart_reg, &sport->port);
+
+ if (lpuart_uport_is_active(sport)) {
+ spin_lock_irqsave(&sport->port.lock, flags);
+ if (lpuart_is_32(sport)) {
+ temp = lpuart32_read(&sport->port, UARTCTRL);
+ temp &= ~(UARTCTRL_TE | UARTCTRL_TIE | UARTCTRL_TCIE);
+ lpuart32_write(&sport->port, temp, UARTCTRL);
+ } else {
+ temp = readb(sport->port.membase + UARTCR2);
+ temp &= ~(UARTCR2_TE | UARTCR2_TIE | UARTCR2_TCIE);
+ writeb(temp, sport->port.membase + UARTCR2);
+ }
+
+ if (sport->lpuart_dma_rx_use)
+ sport->dma_rx_chan_active = false;
+ spin_unlock_irqrestore(&sport->port.lock, flags);
+
+ if (sport->lpuart_dma_rx_use) {
+ /*
+ * EDMA driver during suspend will forcefully release any
+ * non-idle DMA channels. If port wakeup is enabled or if port
+ * is console port or 'no_console_suspend' is set the Rx DMA
+ * cannot resume as as expected, hence gracefully release the
+ * Rx DMA path before suspend and start Rx DMA path on resume.
+ */
+ lpuart_del_timer_sync(sport);
+ lpuart_dma_rx_free(&sport->port, true);
+
+ /* Disable Rx DMA to use UART port as wakeup source */
+ spin_lock_irqsave(&sport->port.lock, flags);
+ if (lpuart_is_32(sport)) {
+ 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);
+ }
+ spin_unlock_irqrestore(&sport->port.lock, flags);
+ }
+
+ if (sport->lpuart_dma_tx_use) {
+ spin_lock_irqsave(&sport->port.lock, flags);
+ if (lpuart_is_32(sport)) {
+ temp = lpuart32_read(&sport->port, UARTBAUD);
+ temp &= ~UARTBAUD_TDMAE;
+ lpuart32_write(&sport->port, temp, UARTBAUD);
+ } else {
+ temp = readb(sport->port.membase + UARTCR5);
+ temp &= ~UARTCR5_TDMAS;
+ writeb(temp, sport->port.membase + UARTCR5);
+ }
+ spin_unlock_irqrestore(&sport->port.lock, flags);
+ sport->dma_tx_in_progress = false;
+ dmaengine_terminate_sync(sport->dma_tx_chan);
+ }
+ } else if (pm_runtime_active(sport->port.dev)) {
+ lpuart_disable_clks(sport);
+ pm_runtime_disable(sport->port.dev);
+ pm_runtime_set_suspended(sport->port.dev);
+ }
+
+ return 0;
+}
+
+static void lpuart_console_fixup(struct lpuart_port *sport)
+{
+ struct tty_port *port = &sport->port.state->port;
+ struct uart_port *uport = &sport->port;
+ struct ktermios termios;
+
+ /* i.MX7ULP enter VLLS mode that lpuart module power off and registers
+ * all lost no matter the port is wakeup source.
+ * For console port, console baud rate setting lost and print messy
+ * log when enable the console port as wakeup source. To avoid the
+ * issue happen, user should not enable uart port as wakeup source
+ * in VLLS mode, or restore console setting here.
+ */
+ if (is_imx7ulp_lpuart(sport) && lpuart_uport_is_active(sport) &&
+ console_suspend_enabled && uart_console(&sport->port)) {
+
+ mutex_lock(&port->mutex);
+ memset(&termios, 0, sizeof(struct ktermios));
+ termios.c_cflag = uport->cons->cflag;
+ if (port->tty && termios.c_cflag == 0)
+ termios = port->tty->termios;
+ uport->ops->set_termios(uport, &termios, NULL);
+ mutex_unlock(&port->mutex);
+ }
+}
+
+static int __maybe_unused lpuart_resume(struct device *dev)
+{
+ struct lpuart_port *sport = dev_get_drvdata(dev);
+ int ret;
+
+ if (lpuart_uport_is_active(sport)) {
+ if (lpuart_is_32(sport))
+ lpuart32_hw_setup(sport);
+ else
+ lpuart_hw_setup(sport);
+ } else if (pm_runtime_active(sport->port.dev)) {
+ ret = lpuart_enable_clks(sport);
+ if (ret)
+ return ret;
+ pm_runtime_set_active(sport->port.dev);
+ pm_runtime_enable(sport->port.dev);
+ }
+
+ lpuart_console_fixup(sport);
uart_resume_port(&lpuart_reg, &sport->port);
return 0;
}
-static SIMPLE_DEV_PM_OPS(lpuart_pm_ops, lpuart_suspend, lpuart_resume);
+static const struct dev_pm_ops lpuart_pm_ops = {
+ SET_RUNTIME_PM_OPS(lpuart_runtime_suspend,
+ lpuart_runtime_resume, NULL)
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(lpuart_suspend_noirq,
+ lpuart_resume_noirq)
+ SET_SYSTEM_SLEEP_PM_OPS(lpuart_suspend, lpuart_resume)
+};
static struct platform_driver lpuart_driver = {
.probe = lpuart_probe,
@@ -2912,7 +3276,6 @@ static int __init lpuart_serial_init(void)
static void __exit lpuart_serial_exit(void)
{
- ida_destroy(&fsl_lpuart_ida);
platform_driver_unregister(&lpuart_driver);
uart_unregister_driver(&lpuart_reg);
}