Re: [PATCH v3 13/13] tty: serial: qcom-geni-serial: add support for serial engine DMA

Srinivas Kandagatla
2022-11-25T14:38:01Z

Thanks Bartosz for the patch,

On 23/11/2022 11:07, Bartosz Golaszewski wrote:
> From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
>
> The qcom-geni-serial driver currently only works in SE FIFO mode. This
> limits the UART speed to around 180 kB/s. In order to achieve higher
> speeds we need to use SE DMA mode.
>
> Keep the console port working in FIFO mode but extend the code to use DMA
> for the high-speed port.
>
> Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
> ---
> drivers/tty/serial/qcom_geni_serial.c | 289 ++++++++++++++++++++++----
> 1 file changed, 247 insertions(+), 42 deletions(-)
>
> diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c
> index 82242a40a95a..0f4ba909c792 100644
> --- a/drivers/tty/serial/qcom_geni_serial.c
> +++ b/drivers/tty/serial/qcom_geni_serial.c
> @@ -70,6 +70,8 @@
> #define UART_START_TX 0x1
> /* UART S_CMD OP codes */
> #define UART_START_READ 0x1
> +#define UART_PARAM 0x1
> +#define UART_PARAM_RFR_OPEN BIT(7)
>
> #define UART_OVERSAMPLING 32
> #define STALE_TIMEOUT 16
> @@ -95,9 +97,11 @@
> /* We always configure 4 bytes per FIFO word */
> #define BYTES_PER_FIFO_WORD 4
>
> +#define DMA_RX_BUF_SIZE 2048
> +
> struct qcom_geni_device_data {
> bool console;
> - void (*handle_rx)(struct uart_port *uport, u32 bytes, bool drop);
> + enum geni_se_xfer_mode mode;
> };
>
> struct qcom_geni_private_data {
> @@ -118,9 +122,11 @@ struct qcom_geni_serial_port {
> u32 tx_fifo_depth;
> u32 tx_fifo_width;
> u32 rx_fifo_depth;
> + dma_addr_t tx_dma_addr;
> + dma_addr_t rx_dma_addr;
> bool setup;
> unsigned int baud;
> - void *rx_fifo;
> + void *rx_buf;
> u32 loopback;
> bool brk;
>
> @@ -552,18 +558,11 @@ static void handle_rx_console(struct uart_port *uport, u32 bytes, bool drop)
>
> static void handle_rx_uart(struct uart_port *uport, u32 bytes, bool drop)
> {
> - struct tty_port *tport;
> struct qcom_geni_serial_port *port = to_dev_port(uport);
> - u32 num_bytes_pw = port->tx_fifo_width / BITS_PER_BYTE;
> - u32 words = ALIGN(bytes, num_bytes_pw) / num_bytes_pw;
> + struct tty_port *tport = &uport->state->port;
> int ret;
>
> - tport = &uport->state->port;
> - ioread32_rep(uport->membase + SE_GENI_RX_FIFOn, port->rx_fifo, words);
> - if (drop)
> - return;
> -

Are we removing FIFO support for uart?

It it not a overhead to use dma for small transfers that are less than
fifo size?

> - ret = tty_insert_flip_string(tport, port->rx_fifo, bytes);
> + ret = tty_insert_flip_string(tport, port->rx_buf, bytes);
> if (ret != bytes) {
> dev_err(uport->dev, "%s:Unable to push data ret %d_bytes %d\n",
> __func__, ret, bytes);
> @@ -578,7 +577,70 @@ static unsigned int qcom_geni_serial_tx_empty(struct uart_port *uport)
> return !readl(uport->membase + SE_GENI_TX_FIFO_STATUS);
> }
>
> -static void qcom_geni_serial_start_tx(struct uart_port *uport)
> +static void qcom_geni_serial_stop_tx_dma(struct uart_port *uport)
> +{
> + struct qcom_geni_serial_port *port = to_dev_port(uport);
> + bool done;

-->
> + u32 status;
...
> +
> + status = readl(uport->membase + SE_GENI_STATUS);
> + if (!(status & M_GENI_CMD_ACTIVE))
> + return;
<---

this code snippet is repeating more than few times in the patches, looks
like it could be made to a inline helper.

> +
> + if (port->rx_dma_addr) {
> + geni_se_tx_dma_unprep(&port->se, port->tx_dma_addr,
> + port->tx_remaining);
> + port->tx_dma_addr = (dma_addr_t)NULL;
> + port->tx_remaining = 0;
> + }
> +
> + m_irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN);
> + writel(m_irq_en, uport->membase + SE_GENI_M_IRQ_EN);
> + geni_se_cancel_m_cmd(&port->se);
> +
> + done = qcom_geni_serial_poll_bit(uport, SE_GENI_S_IRQ_STATUS,
> + S_CMD_CANCEL_EN, true);
> + if (!done) {
> + geni_se_abort_m_cmd(&port->se);
> + qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
> + M_CMD_ABORT_EN, true);

return is not checked, there are few more such instances in the patch.

> + writel(M_CMD_ABORT_EN, uport->membase + SE_GENI_M_IRQ_CLEAR);
> + }
> +
> + writel(M_CMD_CANCEL_EN, uport->membase + SE_GENI_M_IRQ_CLEAR);
> +}
> +
> +static void qcom_geni_serial_start_tx_dma(struct uart_port *uport)
> +{
> + struct qcom_geni_serial_port *port = to_dev_port(uport);
> + struct circ_buf *xmit = &uport->state->xmit;
> + unsigned int xmit_size;
> + int ret;
> +
> + if (port->tx_dma_addr)
> + return;
Is this condition actually possible?

> +
> + xmit_size = uart_circ_chars_pending(xmit);
> + if (xmit_size < WAKEUP_CHARS)
> + uart_write_wakeup(uport);
> +
> + xmit_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
> +
> + qcom_geni_serial_setup_tx(uport, xmit_size);
> +
> + ret = geni_se_tx_dma_prep(&port->se, &xmit->buf[xmit->tail],
> + xmit_size, &port->tx_dma_addr);
> + if (ret) {
> + dev_err(uport->dev, "unable to start TX SE DMA: %d\n", ret);
> + qcom_geni_serial_stop_tx_dma(uport);
> + return;
> + }
> +
> + port->tx_remaining = xmit_size;
> +}
> +

...