Subject: [PATCH v9 16/16] serial: imx: Add support for KGDB's FIQ/NMI mode

From: [email-masked] (Dirk Behme)
Date: Mon, 18 Aug 2014 19:32:59 +0200

On 18.08.2014 16:28, Daniel Thompson wrote:
> This patch makes it possible to use the imx uart with KGDB's FIQ/NMI
> mode.
>
> Main changes are:
>
> .poll_init() will, if KGDB+FIQ are enabled, perform deeper hardware
> initialization to ensure the serial port is always active (required
> otherwise FIQ is not triggered by UART activity). This has an impact on
> power usage so it is conservatively enabled.
>
> imx_put_poll_char() has been simplified to remove the code to disable
> interrupts. The present code can corrupt register state when re-entered
> from FIQ handler.
>
> Both imx_put_poll_char() and imx_get_poll_char() adopt _relaxed()
> MMIO functions (which are safe for polled I/O and needed to avoid taking
> spin locks).
>
> Signed-off-by: Daniel Thompson <[email-masked]>
> Cc: Greg Kroah-Hartman <[email-masked]>
> Cc: Jiri Slaby <[email-masked]>
> Cc: linux-serial at vger.kernel.org

Acked-by: Dirk Behme <[email-masked]>

Thanks

Dirk

> ---
>   drivers/tty/serial/imx.c | 71 +++++++++++++++++++++++++++++++++++-------------
>   1 file changed, 52 insertions(+), 19 deletions(-)
>
> diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
> index 983668a..a201c61 100644
> --- a/drivers/tty/serial/imx.c
> +++ b/drivers/tty/serial/imx.c
> @@ -49,6 +49,7 @@
>   #include <linux/of_device.h>
>   #include <linux/io.h>
>   #include <linux/dma-mapping.h>
> +#include <linux/kgdb.h>
>
>   #include <asm/irq.h>
>   #include <linux/platform_data/serial-imx.h>
> @@ -1505,44 +1506,73 @@ imx_verify_port(struct uart_port *port, struct serial_struct *ser)
>   }
>
>   #if defined(CONFIG_CONSOLE_POLL)
> +
> +#if defined(CONFIG_KGDB_FIQ)
> +/*
> + * Prepare the UART to be used from kgdb's NMI support.
> + */
> +static int imx_poll_init(struct uart_port *port)
> +{
> +	struct imx_port *sport = (struct imx_port *)port;
> +	unsigned long flags;
> +	unsigned long temp;
> +	int retval;
> +
> +	retval = clk_prepare_enable(sport->clk_ipg);
> +	if (retval)
> +		return retval;
> +	retval = clk_prepare_enable(sport->clk_per);
> +	if (retval)
> +		clk_disable_unprepare(sport->clk_ipg);
> +
> +	imx_setup_ufcr(sport, 0);
> +
> +	spin_lock_irqsave(&sport->port.lock, flags);
> +
> +	temp = readl(sport->port.membase + UCR1);
> +	if (is_imx1_uart(sport))
> +		temp |= IMX1_UCR1_UARTCLKEN;
> +	temp |= UCR1_UARTEN | UCR1_RRDYEN;
> +	temp &= ~(UCR1_TXMPTYEN | UCR1_RTSDEN);
> +	writel(temp, sport->port.membase + UCR1);
> +
> +	temp = readl(sport->port.membase + UCR2);
> +	temp |= UCR2_RXEN;
> +	writel(temp, sport->port.membase + UCR2);
> +
> +	spin_unlock_irqrestore(&sport->port.lock, flags);
> +
> +	/* register the FIQ with kgdb */
> +	kgdb_register_fiq(sport->port.irq);
> +
> +	return 0;
> +}
> +#endif /* CONFIG_KGDB_FIQ */
> +
>   static int imx_poll_get_char(struct uart_port *port)
>   {
> -	if (!(readl(port->membase + USR2) & USR2_RDR))
> +	if (!(readl_relaxed(port->membase + USR2) & USR2_RDR))
>   		return NO_POLL_CHAR;
>
> -	return readl(port->membase + URXD0) & URXD_RX_DATA;
> +	return readl_relaxed(port->membase + URXD0) & URXD_RX_DATA;
>   }
>
>   static void imx_poll_put_char(struct uart_port *port, unsigned char c)
>   {
> -	struct imx_port_ucrs old_ucr;
>   	unsigned int status;
>
> -	/* save control registers */
> -	imx_port_ucrs_save(port, &old_ucr);
> -
> -	/* disable interrupts */
> -	writel(UCR1_UARTEN, port->membase + UCR1);
> -	writel(old_ucr.ucr2 & ~(UCR2_ATEN | UCR2_RTSEN | UCR2_ESCI),
> -	       port->membase + UCR2);
> -	writel(old_ucr.ucr3 & ~(UCR3_DCD | UCR3_RI | UCR3_DTREN),
> -	       port->membase + UCR3);
> -
>   	/* drain */
>   	do {
> -		status = readl(port->membase + USR1);
> +		status = readl_relaxed(port->membase + USR1);
>   	} while (~status & USR1_TRDY);
>
>   	/* write */
> -	writel(c, port->membase + URTX0);
> +	writel_relaxed(c, port->membase + URTX0);
>
>   	/* flush */
>   	do {
> -		status = readl(port->membase + USR2);
> +		status = readl_relaxed(port->membase + USR2);
>   	} while (~status & USR2_TXDC);
> -
> -	/* restore control registers */
> -	imx_port_ucrs_restore(port, &old_ucr);
>   }
>   #endif
>
> @@ -1563,6 +1593,9 @@ static struct uart_ops imx_pops = {
>   	.config_port	= imx_config_port,
>   	.verify_port	= imx_verify_port,
>   #if defined(CONFIG_CONSOLE_POLL)
> +#if defined(CONFIG_KGDB_FIQ)
> +	.poll_init      = imx_poll_init,
> +#endif
>   	.poll_get_char  = imx_poll_get_char,
>   	.poll_put_char  = imx_poll_put_char,
>   #endif
>