diff mbox series

[RFC,INTERNAL,v3,4/4] serial: amba-pl011: Enable NMI aware polling mode

Message ID 1594966857-5215-5-git-send-email-sumit.garg@linaro.org
State New
Headers show
Series Introduce NMI aware serial drivers | expand

Commit Message

Sumit Garg July 17, 2020, 6:20 a.m. UTC
Allow serial interrupt to be requested as an NMI in polling mode.

Currently this NMI aware polling mode only supports NMI driven RX and
TX data. DMA operation isn't supported.

Also, while operating in NMI mode, RX always remains active irrespective
of whether corresponding TTY port is active or not. So we directly bail
out of startup, shutdown and rx_stop APIs if NMI mode is active.

Signed-off-by: Sumit Garg <sumit.garg@linaro.org>

---
 drivers/tty/serial/amba-pl011.c | 128 ++++++++++++++++++++++++++++++++++++----
 1 file changed, 117 insertions(+), 11 deletions(-)

-- 
2.7.4

Comments

Daniel Thompson July 20, 2020, 2:14 p.m. UTC | #1
On Fri, Jul 17, 2020 at 11:50:57AM +0530, Sumit Garg wrote:
> Allow serial interrupt to be requested as an NMI in polling mode.

>

> Currently this NMI aware polling mode only supports NMI driven RX and

> TX data. DMA operation isn't supported.


This introduction doesn't make any sense to me at all, it almost seems
like you have got PIO (programmed I/O) confused with polled I/O (driving
peripherals without using interrupts).

Polled mode is, sort of, relevant, because we currently we only
activate NMI mode when we initialized the driver ready for polled I/O.
However other than that everything that is changing in this patch
is entirely unrelated to polled I/O.


> Also, while operating in NMI mode, RX always remains active irrespective

> of whether corresponding TTY port is active or not. So we directly bail

> out of startup, shutdown and rx_stop APIs if NMI mode is active.

> 

> Signed-off-by: Sumit Garg <sumit.garg@linaro.org>

> ---

>  drivers/tty/serial/amba-pl011.c | 128 ++++++++++++++++++++++++++++++++++++----

>  1 file changed, 117 insertions(+), 11 deletions(-)

> 

> diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c

> index 0983c5e..36c32b8 100644

> --- a/drivers/tty/serial/amba-pl011.c

> +++ b/drivers/tty/serial/amba-pl011.c

> @@ -41,6 +41,8 @@

>  #include <linux/sizes.h>

>  #include <linux/io.h>

>  #include <linux/acpi.h>

> +#include <linux/irq.h>

> +#include <linux/irqdesc.h>

>  

>  #include "amba-pl011.h"

>  

> @@ -347,6 +349,10 @@ static int pl011_fifo_to_tty(struct uart_amba_port *uap)

>  		if (uart_handle_sysrq_char(&uap->port, ch & 255))

>  			continue;

>  

> +		if (uart_nmi_handle_char(&uap->port, ch, UART011_DR_OE, ch,

> +					 flag))

> +			continue;

> +

>  		uart_insert_char(&uap->port, ch, UART011_DR_OE, ch, flag);

>  	}

>  

> @@ -1316,6 +1322,9 @@ static void pl011_stop_rx(struct uart_port *port)

>  	struct uart_amba_port *uap =

>  	    container_of(port, struct uart_amba_port, port);

>  

> +	if (uart_nmi_active(port))

> +		return;

> +

>  	uap->im &= ~(UART011_RXIM|UART011_RTIM|UART011_FEIM|

>  		     UART011_PEIM|UART011_BEIM|UART011_OEIM);

>  	pl011_write(uap->im, uap, REG_IMSC);

> @@ -1604,13 +1613,6 @@ static int pl011_hwinit(struct uart_port *port)

>  		    UART011_FEIS | UART011_RTIS | UART011_RXIS,

>  		    uap, REG_ICR);

>  

> -	/*

> -	 * Save interrupts enable mask, and enable RX interrupts in case if

> -	 * the interrupt is used for NMI entry.

> -	 */

> -	uap->im = pl011_read(uap, REG_IMSC);

> -	pl011_write(UART011_RTIM | UART011_RXIM, uap, REG_IMSC);

> -

>  	if (dev_get_platdata(uap->port.dev)) {

>  		struct amba_pl011_data *plat;

>  

> @@ -1711,6 +1713,100 @@ static void pl011_put_poll_char(struct uart_port *port,

>  	pl011_write(ch, uap, REG_DR);

>  }

>  

> +static void pl011_nmi_rx_chars(struct uart_amba_port *uap)

> +{

> +	pl011_fifo_to_tty(uap);

> +	irq_work_queue(&uap->port.nmi_state.rx_work);

> +}


This function adds no clarity all to the interrupt handler. It would
be much better to move this code into pl011_nmi_int().



> +static irqreturn_t pl011_nmi_int(int irq, void *dev_id)

> +{

> +	struct uart_amba_port *uap = dev_id;

> +	unsigned int status, pass_counter = AMBA_ISR_PASS_LIMIT;

> +	int handled = 0;

> +

> +	status = pl011_read(uap, REG_MIS);

> +	if (status) {

> +		do {

> +			check_apply_cts_event_workaround(uap);

> +

> +			pl011_write(status, uap, REG_ICR);

> +

> +			if (status & (UART011_RTIS|UART011_RXIS))

> +				pl011_nmi_rx_chars(uap);

> +

> +			if (status & UART011_TXIS)

> +				irq_work_queue(&uap->port.nmi_state.tx_work);

> +

> +			if (pass_counter-- == 0)

> +				break;

> +

> +			status = pl011_read(uap, REG_MIS);

> +		} while (status != 0);

> +		handled = 1;

> +	}

> +

> +	return IRQ_RETVAL(handled);

> +}

> +

> +static int pl011_allocate_nmi(struct uart_amba_port *uap)

> +{

> +	int ret;

> +

> +	irq_set_status_flags(uap->port.irq, IRQ_NOAUTOEN);

> +	ret = request_nmi(uap->port.irq, pl011_nmi_int, IRQF_PERCPU,

> +			  "uart-pl011", uap);

> +	if (ret) {

> +		irq_clear_status_flags(uap->port.irq, IRQ_NOAUTOEN);

> +		return ret;

> +	}

> +

> +	enable_irq(uap->port.irq);

> +

> +	return ret;

> +}

> +

> +static void pl011_tx_irq_callback(struct uart_port *port)

> +{

> +	struct uart_amba_port *uap =

> +	    container_of(port, struct uart_amba_port, port);

> +

> +	spin_lock(&port->lock);

> +	pl011_tx_chars(uap, true);

> +	spin_unlock(&port->lock);

> +}

> +

> +static int pl011_poll_init(struct uart_port *port)

> +{

> +	struct uart_amba_port *uap =

> +	    container_of(port, struct uart_amba_port, port);

> +	int retval;

> +

> +	retval = pl011_hwinit(port);

> +	if (retval)

> +		goto clk_dis;

> +

> +	/* In case NMI isn't supported, fallback to normal interrupt mode */

> +	retval = pl011_allocate_nmi(uap);

> +	if (retval)

> +		return 0;

> +

> +	retval = uart_nmi_state_init(port);

> +	if (retval)

> +		goto clk_dis;

> +

> +	port->nmi_state.tx_irq_callback = pl011_tx_irq_callback;

> +	uart_set_nmi_active(port, true);

> +

> +	pl011_enable_interrupts(uap);

> +

> +	return 0;

> +

> + clk_dis:

> +	clk_disable_unprepare(uap->clk);

> +	return retval;

> +}

> +

>  #endif /* CONFIG_CONSOLE_POLL */

>  

>  static bool pl011_split_lcrh(const struct uart_amba_port *uap)

> @@ -1736,8 +1832,6 @@ static void pl011_write_lcr_h(struct uart_amba_port *uap, unsigned int lcr_h)

>  

>  static int pl011_allocate_irq(struct uart_amba_port *uap)

>  {

> -	pl011_write(uap->im, uap, REG_IMSC);

> -


What is happening here? Does this have anything to do with NMI? Either
way why isn't anything about this mentioned in the patch description.


Daniel.

>  	return request_irq(uap->port.irq, pl011_int, IRQF_SHARED, "uart-pl011", uap);

>  }

>  

> @@ -1748,6 +1842,9 @@ static int pl011_startup(struct uart_port *port)

>  	unsigned int cr;

>  	int retval;

>  

> +	if (uart_nmi_active(port))

> +		return 0;

> +

>  	retval = pl011_hwinit(port);

>  	if (retval)

>  		goto clk_dis;

> @@ -1790,6 +1887,9 @@ static int sbsa_uart_startup(struct uart_port *port)

>  		container_of(port, struct uart_amba_port, port);

>  	int retval;

>  

> +	if (uart_nmi_active(port))

> +		return 0;

> +

>  	retval = pl011_hwinit(port);

>  	if (retval)

>  		return retval;

> @@ -1859,6 +1959,9 @@ static void pl011_shutdown(struct uart_port *port)

>  	struct uart_amba_port *uap =

>  		container_of(port, struct uart_amba_port, port);

>  

> +	if (uart_nmi_active(port))

> +		return;

> +

>  	pl011_disable_interrupts(uap);

>  

>  	pl011_dma_shutdown(uap);

> @@ -1891,6 +1994,9 @@ static void sbsa_uart_shutdown(struct uart_port *port)

>  	struct uart_amba_port *uap =

>  		container_of(port, struct uart_amba_port, port);

>  

> +	if (uart_nmi_active(port))

> +		return;

> +

>  	pl011_disable_interrupts(uap);

>  

>  	free_irq(uap->port.irq, uap);

> @@ -2142,7 +2248,7 @@ static const struct uart_ops amba_pl011_pops = {

>  	.config_port	= pl011_config_port,

>  	.verify_port	= pl011_verify_port,

>  #ifdef CONFIG_CONSOLE_POLL

> -	.poll_init     = pl011_hwinit,

> +	.poll_init     = pl011_poll_init,

>  	.poll_get_char = pl011_get_poll_char,

>  	.poll_put_char = pl011_put_poll_char,

>  #endif

> @@ -2173,7 +2279,7 @@ static const struct uart_ops sbsa_uart_pops = {

>  	.config_port	= pl011_config_port,

>  	.verify_port	= pl011_verify_port,

>  #ifdef CONFIG_CONSOLE_POLL

> -	.poll_init     = pl011_hwinit,

> +	.poll_init     = pl011_poll_init,

>  	.poll_get_char = pl011_get_poll_char,

>  	.poll_put_char = pl011_put_poll_char,

>  #endif

> -- 

> 2.7.4
Sumit Garg July 21, 2020, 6:36 a.m. UTC | #2
On Mon, 20 Jul 2020 at 19:44, Daniel Thompson
<daniel.thompson@linaro.org> wrote:
>

> On Fri, Jul 17, 2020 at 11:50:57AM +0530, Sumit Garg wrote:

> > Allow serial interrupt to be requested as an NMI in polling mode.

> >

> > Currently this NMI aware polling mode only supports NMI driven RX and

> > TX data. DMA operation isn't supported.

>

> This introduction doesn't make any sense to me at all, it almost seems

> like you have got PIO (programmed I/O) confused with polled I/O (driving

> peripherals without using interrupts).

>


Will use programmed I/O here instead and update the introduction.

> Polled mode is, sort of, relevant, because we currently we only

> activate NMI mode when we initialized the driver ready for polled I/O.

> However other than that everything that is changing in this patch

> is entirely unrelated to polled I/O.

>


Agree.

>

> > Also, while operating in NMI mode, RX always remains active irrespective

> > of whether corresponding TTY port is active or not. So we directly bail

> > out of startup, shutdown and rx_stop APIs if NMI mode is active.

> >

> > Signed-off-by: Sumit Garg <sumit.garg@linaro.org>

> > ---

> >  drivers/tty/serial/amba-pl011.c | 128 ++++++++++++++++++++++++++++++++++++----

> >  1 file changed, 117 insertions(+), 11 deletions(-)

> >

> > diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c

> > index 0983c5e..36c32b8 100644

> > --- a/drivers/tty/serial/amba-pl011.c

> > +++ b/drivers/tty/serial/amba-pl011.c

> > @@ -41,6 +41,8 @@

> >  #include <linux/sizes.h>

> >  #include <linux/io.h>

> >  #include <linux/acpi.h>

> > +#include <linux/irq.h>

> > +#include <linux/irqdesc.h>

> >

> >  #include "amba-pl011.h"

> >

> > @@ -347,6 +349,10 @@ static int pl011_fifo_to_tty(struct uart_amba_port *uap)

> >               if (uart_handle_sysrq_char(&uap->port, ch & 255))

> >                       continue;

> >

> > +             if (uart_nmi_handle_char(&uap->port, ch, UART011_DR_OE, ch,

> > +                                      flag))

> > +                     continue;

> > +

> >               uart_insert_char(&uap->port, ch, UART011_DR_OE, ch, flag);

> >       }

> >

> > @@ -1316,6 +1322,9 @@ static void pl011_stop_rx(struct uart_port *port)

> >       struct uart_amba_port *uap =

> >           container_of(port, struct uart_amba_port, port);

> >

> > +     if (uart_nmi_active(port))

> > +             return;

> > +

> >       uap->im &= ~(UART011_RXIM|UART011_RTIM|UART011_FEIM|

> >                    UART011_PEIM|UART011_BEIM|UART011_OEIM);

> >       pl011_write(uap->im, uap, REG_IMSC);

> > @@ -1604,13 +1613,6 @@ static int pl011_hwinit(struct uart_port *port)

> >                   UART011_FEIS | UART011_RTIS | UART011_RXIS,

> >                   uap, REG_ICR);

> >

> > -     /*

> > -      * Save interrupts enable mask, and enable RX interrupts in case if

> > -      * the interrupt is used for NMI entry.

> > -      */

> > -     uap->im = pl011_read(uap, REG_IMSC);

> > -     pl011_write(UART011_RTIM | UART011_RXIM, uap, REG_IMSC);

> > -

> >       if (dev_get_platdata(uap->port.dev)) {

> >               struct amba_pl011_data *plat;

> >

> > @@ -1711,6 +1713,100 @@ static void pl011_put_poll_char(struct uart_port *port,

> >       pl011_write(ch, uap, REG_DR);

> >  }

> >

> > +static void pl011_nmi_rx_chars(struct uart_amba_port *uap)

> > +{

> > +     pl011_fifo_to_tty(uap);

> > +     irq_work_queue(&uap->port.nmi_state.rx_work);

> > +}

>

> This function adds no clarity all to the interrupt handler. It would

> be much better to move this code into pl011_nmi_int().

>


Okay.

>

>

> > +static irqreturn_t pl011_nmi_int(int irq, void *dev_id)

> > +{

> > +     struct uart_amba_port *uap = dev_id;

> > +     unsigned int status, pass_counter = AMBA_ISR_PASS_LIMIT;

> > +     int handled = 0;

> > +

> > +     status = pl011_read(uap, REG_MIS);

> > +     if (status) {

> > +             do {

> > +                     check_apply_cts_event_workaround(uap);

> > +

> > +                     pl011_write(status, uap, REG_ICR);

> > +

> > +                     if (status & (UART011_RTIS|UART011_RXIS))

> > +                             pl011_nmi_rx_chars(uap);

> > +

> > +                     if (status & UART011_TXIS)

> > +                             irq_work_queue(&uap->port.nmi_state.tx_work);

> > +

> > +                     if (pass_counter-- == 0)

> > +                             break;

> > +

> > +                     status = pl011_read(uap, REG_MIS);

> > +             } while (status != 0);

> > +             handled = 1;

> > +     }

> > +

> > +     return IRQ_RETVAL(handled);

> > +}

> > +

> > +static int pl011_allocate_nmi(struct uart_amba_port *uap)

> > +{

> > +     int ret;

> > +

> > +     irq_set_status_flags(uap->port.irq, IRQ_NOAUTOEN);

> > +     ret = request_nmi(uap->port.irq, pl011_nmi_int, IRQF_PERCPU,

> > +                       "uart-pl011", uap);

> > +     if (ret) {

> > +             irq_clear_status_flags(uap->port.irq, IRQ_NOAUTOEN);

> > +             return ret;

> > +     }

> > +

> > +     enable_irq(uap->port.irq);

> > +

> > +     return ret;

> > +}

> > +

> > +static void pl011_tx_irq_callback(struct uart_port *port)

> > +{

> > +     struct uart_amba_port *uap =

> > +         container_of(port, struct uart_amba_port, port);

> > +

> > +     spin_lock(&port->lock);

> > +     pl011_tx_chars(uap, true);

> > +     spin_unlock(&port->lock);

> > +}

> > +

> > +static int pl011_poll_init(struct uart_port *port)

> > +{

> > +     struct uart_amba_port *uap =

> > +         container_of(port, struct uart_amba_port, port);

> > +     int retval;

> > +

> > +     retval = pl011_hwinit(port);

> > +     if (retval)

> > +             goto clk_dis;

> > +

> > +     /* In case NMI isn't supported, fallback to normal interrupt mode */

> > +     retval = pl011_allocate_nmi(uap);

> > +     if (retval)

> > +             return 0;

> > +

> > +     retval = uart_nmi_state_init(port);

> > +     if (retval)

> > +             goto clk_dis;

> > +

> > +     port->nmi_state.tx_irq_callback = pl011_tx_irq_callback;

> > +     uart_set_nmi_active(port, true);

> > +

> > +     pl011_enable_interrupts(uap);

> > +

> > +     return 0;

> > +

> > + clk_dis:

> > +     clk_disable_unprepare(uap->clk);

> > +     return retval;

> > +}

> > +

> >  #endif /* CONFIG_CONSOLE_POLL */

> >

> >  static bool pl011_split_lcrh(const struct uart_amba_port *uap)

> > @@ -1736,8 +1832,6 @@ static void pl011_write_lcr_h(struct uart_amba_port *uap, unsigned int lcr_h)

> >

> >  static int pl011_allocate_irq(struct uart_amba_port *uap)

> >  {

> > -     pl011_write(uap->im, uap, REG_IMSC);

> > -

>

> What is happening here? Does this have anything to do with NMI? Either

> way why isn't anything about this mentioned in the patch description.

>


This is related to restoring interrupts enable mask that was modified
as part of pl011_hwinit() prior to this patch. But since we removed
that code from pl011_hwinit(), we shouldn't need this restore here as
well.

Will update patch description accordingly.

-Sumit

>

> Daniel.

>

> >       return request_irq(uap->port.irq, pl011_int, IRQF_SHARED, "uart-pl011", uap);

> >  }

> >

> > @@ -1748,6 +1842,9 @@ static int pl011_startup(struct uart_port *port)

> >       unsigned int cr;

> >       int retval;

> >

> > +     if (uart_nmi_active(port))

> > +             return 0;

> > +

> >       retval = pl011_hwinit(port);

> >       if (retval)

> >               goto clk_dis;

> > @@ -1790,6 +1887,9 @@ static int sbsa_uart_startup(struct uart_port *port)

> >               container_of(port, struct uart_amba_port, port);

> >       int retval;

> >

> > +     if (uart_nmi_active(port))

> > +             return 0;

> > +

> >       retval = pl011_hwinit(port);

> >       if (retval)

> >               return retval;

> > @@ -1859,6 +1959,9 @@ static void pl011_shutdown(struct uart_port *port)

> >       struct uart_amba_port *uap =

> >               container_of(port, struct uart_amba_port, port);

> >

> > +     if (uart_nmi_active(port))

> > +             return;

> > +

> >       pl011_disable_interrupts(uap);

> >

> >       pl011_dma_shutdown(uap);

> > @@ -1891,6 +1994,9 @@ static void sbsa_uart_shutdown(struct uart_port *port)

> >       struct uart_amba_port *uap =

> >               container_of(port, struct uart_amba_port, port);

> >

> > +     if (uart_nmi_active(port))

> > +             return;

> > +

> >       pl011_disable_interrupts(uap);

> >

> >       free_irq(uap->port.irq, uap);

> > @@ -2142,7 +2248,7 @@ static const struct uart_ops amba_pl011_pops = {

> >       .config_port    = pl011_config_port,

> >       .verify_port    = pl011_verify_port,

> >  #ifdef CONFIG_CONSOLE_POLL

> > -     .poll_init     = pl011_hwinit,

> > +     .poll_init     = pl011_poll_init,

> >       .poll_get_char = pl011_get_poll_char,

> >       .poll_put_char = pl011_put_poll_char,

> >  #endif

> > @@ -2173,7 +2279,7 @@ static const struct uart_ops sbsa_uart_pops = {

> >       .config_port    = pl011_config_port,

> >       .verify_port    = pl011_verify_port,

> >  #ifdef CONFIG_CONSOLE_POLL

> > -     .poll_init     = pl011_hwinit,

> > +     .poll_init     = pl011_poll_init,

> >       .poll_get_char = pl011_get_poll_char,

> >       .poll_put_char = pl011_put_poll_char,

> >  #endif

> > --

> > 2.7.4
diff mbox series

Patch

diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c
index 0983c5e..36c32b8 100644
--- a/drivers/tty/serial/amba-pl011.c
+++ b/drivers/tty/serial/amba-pl011.c
@@ -41,6 +41,8 @@ 
 #include <linux/sizes.h>
 #include <linux/io.h>
 #include <linux/acpi.h>
+#include <linux/irq.h>
+#include <linux/irqdesc.h>
 
 #include "amba-pl011.h"
 
@@ -347,6 +349,10 @@  static int pl011_fifo_to_tty(struct uart_amba_port *uap)
 		if (uart_handle_sysrq_char(&uap->port, ch & 255))
 			continue;
 
+		if (uart_nmi_handle_char(&uap->port, ch, UART011_DR_OE, ch,
+					 flag))
+			continue;
+
 		uart_insert_char(&uap->port, ch, UART011_DR_OE, ch, flag);
 	}
 
@@ -1316,6 +1322,9 @@  static void pl011_stop_rx(struct uart_port *port)
 	struct uart_amba_port *uap =
 	    container_of(port, struct uart_amba_port, port);
 
+	if (uart_nmi_active(port))
+		return;
+
 	uap->im &= ~(UART011_RXIM|UART011_RTIM|UART011_FEIM|
 		     UART011_PEIM|UART011_BEIM|UART011_OEIM);
 	pl011_write(uap->im, uap, REG_IMSC);
@@ -1604,13 +1613,6 @@  static int pl011_hwinit(struct uart_port *port)
 		    UART011_FEIS | UART011_RTIS | UART011_RXIS,
 		    uap, REG_ICR);
 
-	/*
-	 * Save interrupts enable mask, and enable RX interrupts in case if
-	 * the interrupt is used for NMI entry.
-	 */
-	uap->im = pl011_read(uap, REG_IMSC);
-	pl011_write(UART011_RTIM | UART011_RXIM, uap, REG_IMSC);
-
 	if (dev_get_platdata(uap->port.dev)) {
 		struct amba_pl011_data *plat;
 
@@ -1711,6 +1713,100 @@  static void pl011_put_poll_char(struct uart_port *port,
 	pl011_write(ch, uap, REG_DR);
 }
 
+static void pl011_nmi_rx_chars(struct uart_amba_port *uap)
+{
+	pl011_fifo_to_tty(uap);
+	irq_work_queue(&uap->port.nmi_state.rx_work);
+}
+
+static irqreturn_t pl011_nmi_int(int irq, void *dev_id)
+{
+	struct uart_amba_port *uap = dev_id;
+	unsigned int status, pass_counter = AMBA_ISR_PASS_LIMIT;
+	int handled = 0;
+
+	status = pl011_read(uap, REG_MIS);
+	if (status) {
+		do {
+			check_apply_cts_event_workaround(uap);
+
+			pl011_write(status, uap, REG_ICR);
+
+			if (status & (UART011_RTIS|UART011_RXIS))
+				pl011_nmi_rx_chars(uap);
+
+			if (status & UART011_TXIS)
+				irq_work_queue(&uap->port.nmi_state.tx_work);
+
+			if (pass_counter-- == 0)
+				break;
+
+			status = pl011_read(uap, REG_MIS);
+		} while (status != 0);
+		handled = 1;
+	}
+
+	return IRQ_RETVAL(handled);
+}
+
+static int pl011_allocate_nmi(struct uart_amba_port *uap)
+{
+	int ret;
+
+	irq_set_status_flags(uap->port.irq, IRQ_NOAUTOEN);
+	ret = request_nmi(uap->port.irq, pl011_nmi_int, IRQF_PERCPU,
+			  "uart-pl011", uap);
+	if (ret) {
+		irq_clear_status_flags(uap->port.irq, IRQ_NOAUTOEN);
+		return ret;
+	}
+
+	enable_irq(uap->port.irq);
+
+	return ret;
+}
+
+static void pl011_tx_irq_callback(struct uart_port *port)
+{
+	struct uart_amba_port *uap =
+	    container_of(port, struct uart_amba_port, port);
+
+	spin_lock(&port->lock);
+	pl011_tx_chars(uap, true);
+	spin_unlock(&port->lock);
+}
+
+static int pl011_poll_init(struct uart_port *port)
+{
+	struct uart_amba_port *uap =
+	    container_of(port, struct uart_amba_port, port);
+	int retval;
+
+	retval = pl011_hwinit(port);
+	if (retval)
+		goto clk_dis;
+
+	/* In case NMI isn't supported, fallback to normal interrupt mode */
+	retval = pl011_allocate_nmi(uap);
+	if (retval)
+		return 0;
+
+	retval = uart_nmi_state_init(port);
+	if (retval)
+		goto clk_dis;
+
+	port->nmi_state.tx_irq_callback = pl011_tx_irq_callback;
+	uart_set_nmi_active(port, true);
+
+	pl011_enable_interrupts(uap);
+
+	return 0;
+
+ clk_dis:
+	clk_disable_unprepare(uap->clk);
+	return retval;
+}
+
 #endif /* CONFIG_CONSOLE_POLL */
 
 static bool pl011_split_lcrh(const struct uart_amba_port *uap)
@@ -1736,8 +1832,6 @@  static void pl011_write_lcr_h(struct uart_amba_port *uap, unsigned int lcr_h)
 
 static int pl011_allocate_irq(struct uart_amba_port *uap)
 {
-	pl011_write(uap->im, uap, REG_IMSC);
-
 	return request_irq(uap->port.irq, pl011_int, IRQF_SHARED, "uart-pl011", uap);
 }
 
@@ -1748,6 +1842,9 @@  static int pl011_startup(struct uart_port *port)
 	unsigned int cr;
 	int retval;
 
+	if (uart_nmi_active(port))
+		return 0;
+
 	retval = pl011_hwinit(port);
 	if (retval)
 		goto clk_dis;
@@ -1790,6 +1887,9 @@  static int sbsa_uart_startup(struct uart_port *port)
 		container_of(port, struct uart_amba_port, port);
 	int retval;
 
+	if (uart_nmi_active(port))
+		return 0;
+
 	retval = pl011_hwinit(port);
 	if (retval)
 		return retval;
@@ -1859,6 +1959,9 @@  static void pl011_shutdown(struct uart_port *port)
 	struct uart_amba_port *uap =
 		container_of(port, struct uart_amba_port, port);
 
+	if (uart_nmi_active(port))
+		return;
+
 	pl011_disable_interrupts(uap);
 
 	pl011_dma_shutdown(uap);
@@ -1891,6 +1994,9 @@  static void sbsa_uart_shutdown(struct uart_port *port)
 	struct uart_amba_port *uap =
 		container_of(port, struct uart_amba_port, port);
 
+	if (uart_nmi_active(port))
+		return;
+
 	pl011_disable_interrupts(uap);
 
 	free_irq(uap->port.irq, uap);
@@ -2142,7 +2248,7 @@  static const struct uart_ops amba_pl011_pops = {
 	.config_port	= pl011_config_port,
 	.verify_port	= pl011_verify_port,
 #ifdef CONFIG_CONSOLE_POLL
-	.poll_init     = pl011_hwinit,
+	.poll_init     = pl011_poll_init,
 	.poll_get_char = pl011_get_poll_char,
 	.poll_put_char = pl011_put_poll_char,
 #endif
@@ -2173,7 +2279,7 @@  static const struct uart_ops sbsa_uart_pops = {
 	.config_port	= pl011_config_port,
 	.verify_port	= pl011_verify_port,
 #ifdef CONFIG_CONSOLE_POLL
-	.poll_init     = pl011_hwinit,
+	.poll_init     = pl011_poll_init,
 	.poll_get_char = pl011_get_poll_char,
 	.poll_put_char = pl011_put_poll_char,
 #endif