tty: serial: meson_uart: Add support for kernel debugger

The kgdb invokes the poll_put_char and poll_get_char when communicating
with the host. This patch implement the serial polling hooks for the
meson_uart to be used for KGDB debugging over serial line.

Signed-off-by: Julien Masson <jmasson@baylibre.com>
Link: https://lore.kernel.org/r/867e1klo48.fsf@julienm-fedora-R90NQGV9.i-did-not-set--mail-host-address--so-tickle-me
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Julien Masson 2020-01-21 18:22:52 +01:00 committed by Greg Kroah-Hartman
parent 101aa46bd2
commit 8412ba1db8
1 changed files with 65 additions and 0 deletions

View File

@ -10,6 +10,7 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/of.h> #include <linux/of.h>
@ -72,6 +73,8 @@
#define AML_UART_PORT_OFFSET 6 #define AML_UART_PORT_OFFSET 6
#define AML_UART_DEV_NAME "ttyAML" #define AML_UART_DEV_NAME "ttyAML"
#define AML_UART_POLL_USEC 5
#define AML_UART_TIMEOUT_USEC 10000
static struct uart_driver meson_uart_driver; static struct uart_driver meson_uart_driver;
@ -423,6 +426,64 @@ static void meson_uart_config_port(struct uart_port *port, int flags)
} }
} }
#ifdef CONFIG_CONSOLE_POLL
/*
* Console polling routines for writing and reading from the uart while
* in an interrupt or debug context (i.e. kgdb).
*/
static int meson_uart_poll_get_char(struct uart_port *port)
{
u32 c;
unsigned long flags;
spin_lock_irqsave(&port->lock, flags);
if (readl(port->membase + AML_UART_STATUS) & AML_UART_RX_EMPTY)
c = NO_POLL_CHAR;
else
c = readl(port->membase + AML_UART_RFIFO);
spin_unlock_irqrestore(&port->lock, flags);
return c;
}
static void meson_uart_poll_put_char(struct uart_port *port, unsigned char c)
{
unsigned long flags;
u32 reg;
int ret;
spin_lock_irqsave(&port->lock, flags);
/* Wait until FIFO is empty or timeout */
ret = readl_poll_timeout_atomic(port->membase + AML_UART_STATUS, reg,
reg & AML_UART_TX_EMPTY,
AML_UART_POLL_USEC,
AML_UART_TIMEOUT_USEC);
if (ret == -ETIMEDOUT) {
dev_err(port->dev, "Timeout waiting for UART TX EMPTY\n");
goto out;
}
/* Write the character */
writel(c, port->membase + AML_UART_WFIFO);
/* Wait until FIFO is empty or timeout */
ret = readl_poll_timeout_atomic(port->membase + AML_UART_STATUS, reg,
reg & AML_UART_TX_EMPTY,
AML_UART_POLL_USEC,
AML_UART_TIMEOUT_USEC);
if (ret == -ETIMEDOUT)
dev_err(port->dev, "Timeout waiting for UART TX EMPTY\n");
out:
spin_unlock_irqrestore(&port->lock, flags);
}
#endif /* CONFIG_CONSOLE_POLL */
static const struct uart_ops meson_uart_ops = { static const struct uart_ops meson_uart_ops = {
.set_mctrl = meson_uart_set_mctrl, .set_mctrl = meson_uart_set_mctrl,
.get_mctrl = meson_uart_get_mctrl, .get_mctrl = meson_uart_get_mctrl,
@ -438,6 +499,10 @@ static const struct uart_ops meson_uart_ops = {
.request_port = meson_uart_request_port, .request_port = meson_uart_request_port,
.release_port = meson_uart_release_port, .release_port = meson_uart_release_port,
.verify_port = meson_uart_verify_port, .verify_port = meson_uart_verify_port,
#ifdef CONFIG_CONSOLE_POLL
.poll_get_char = meson_uart_poll_get_char,
.poll_put_char = meson_uart_poll_put_char,
#endif
}; };
#ifdef CONFIG_SERIAL_MESON_CONSOLE #ifdef CONFIG_SERIAL_MESON_CONSOLE