Skip to content

Commit

Permalink
tty: serial: switch from circ_buf to kfifo
Browse files Browse the repository at this point in the history
Switch from struct circ_buf to proper kfifo. kfifo provides much better
API, esp. when wrap-around of the buffer needs to be taken into account.
Look at pl011_dma_tx_refill() or cpm_uart_tx_pump() changes for example.

Kfifo API can also fill in scatter-gather DMA structures, so it easier
for that use case too. Look at lpuart_dma_tx() for example. Note that
not all drivers can be converted to that (like atmel_serial), they
handle DMA specially.

Note that usb-serial uses kfifo for TX for ages.

omap needed a bit more care as it needs to put a char into FIFO to start
the DMA transfer when OMAP_DMA_TX_KICK is set. In that case, we have to
do kfifo_dma_out_prepare twice: once to find out the tx_size (to find
out if it is worths to do DMA at all -- size >= 4), the second time for
the actual transfer.

All traces of circ_buf are removed from serial_core.h (and its struct
uart_state).

Signed-off-by: Jiri Slaby (SUSE) <jirislaby@kernel.org>
Cc: Al Cooper <alcooperx@gmail.com>
Cc: Matthias Brugger <matthias.bgg@gmail.com>
Cc: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Cc: Kumaravel Thiagarajan <kumaravel.thiagarajan@microchip.com>
Cc: Tharun Kumar P <tharunkumar.pasumarthi@microchip.com>
Cc: Russell King <linux@armlinux.org.uk>
Cc: Vineet Gupta <vgupta@kernel.org>
Cc: Richard Genoud <richard.genoud@gmail.com>
Cc: Nicolas Ferre <nicolas.ferre@microchip.com>
Cc: Alexandre Belloni <alexandre.belloni@bootlin.com>
Cc: Claudiu Beznea <claudiu.beznea@tuxon.dev>
Cc: Alexander Shiyan <shc_work@mail.ru>
Cc: Baruch Siach <baruch@tkos.co.il>
Cc: Maciej W. Rozycki <macro@orcam.me.uk>
Cc: Shawn Guo <shawnguo@kernel.org>
Cc: Sascha Hauer <s.hauer@pengutronix.de>
Cc: Fabio Estevam <festevam@gmail.com>
Cc: Neil Armstrong <neil.armstrong@linaro.org>
Cc: Kevin Hilman <khilman@baylibre.com>
Cc: Jerome Brunet <jbrunet@baylibre.com>
Cc: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
Cc: Taichi Sugaya <sugaya.taichi@socionext.com>
Cc: Takao Orito <orito.takao@socionext.com>
Cc: Bjorn Andersson <andersson@kernel.org>
Cc: Konrad Dybcio <konrad.dybcio@linaro.org>
Cc: Pali Rohár <pali@kernel.org>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Nicholas Piggin <npiggin@gmail.com>
Cc: Christophe Leroy <christophe.leroy@csgroup.eu>
Cc: Aneesh Kumar K.V <aneesh.kumar@kernel.org>
Cc: Naveen N. Rao <naveen.n.rao@linux.ibm.com>
Cc: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Cc: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Cc: Alim Akhtar <alim.akhtar@samsung.com>
Cc: Laxman Dewangan <ldewangan@nvidia.com>
Cc: Thierry Reding <thierry.reding@gmail.com>
Cc: Jonathan Hunter <jonathanh@nvidia.com>
Cc: Orson Zhai <orsonzhai@gmail.com>
Cc: Baolin Wang <baolin.wang@linux.alibaba.com>
Cc: Chunyan Zhang <zhang.lyra@gmail.com>
Cc: Patrice Chotard <patrice.chotard@foss.st.com>
Cc: Maxime Coquelin <mcoquelin.stm32@gmail.com>
Cc: Alexandre Torgue <alexandre.torgue@foss.st.com>
Cc: David S. Miller <davem@davemloft.net>
Cc: Hammer Hsieh <hammerh0314@gmail.com>
Cc: Peter Korsgaard <jacmet@sunsite.dk>
Cc: Timur Tabi <timur@kernel.org>
Cc: Michal Simek <michal.simek@amd.com>
Cc: Sumit Semwal <sumit.semwal@linaro.org>
Cc: Christian König <christian.koenig@amd.com>
Link: https://lore.kernel.org/r/20240405060826.2521-13-jirislaby@kernel.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
Jiri Slaby (SUSE) authored and Greg Kroah-Hartman committed Apr 9, 2024
1 parent f8fef2f commit 1788cf6
Show file tree
Hide file tree
Showing 59 changed files with 688 additions and 780 deletions.
14 changes: 6 additions & 8 deletions drivers/tty/serial/8250/8250_bcm7271.c
Original file line number Diff line number Diff line change
Expand Up @@ -413,20 +413,18 @@ static int stop_tx_dma(struct uart_8250_port *p)
static int brcmuart_tx_dma(struct uart_8250_port *p)
{
struct brcmuart_priv *priv = p->port.private_data;
struct circ_buf *xmit = &p->port.state->xmit;
struct tty_port *tport = &p->port.state->port;
u32 tx_size;

if (uart_tx_stopped(&p->port) || priv->tx_running ||
uart_circ_empty(xmit)) {
kfifo_is_empty(&tport->xmit_fifo)) {
return 0;
}
tx_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);

priv->dma.tx_err = 0;
memcpy(priv->tx_buf, &xmit->buf[xmit->tail], tx_size);
uart_xmit_advance(&p->port, tx_size);
tx_size = uart_fifo_out(&p->port, priv->tx_buf, UART_XMIT_SIZE);

if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(&p->port);

udma_writel(priv, REGS_DMA_TX, UDMA_TX_TRANSFER_LEN, tx_size);
Expand Down Expand Up @@ -540,15 +538,15 @@ static void brcmuart_tx_isr(struct uart_port *up, u32 isr)
struct brcmuart_priv *priv = up->private_data;
struct device *dev = up->dev;
struct uart_8250_port *port_8250 = up_to_u8250p(up);
struct circ_buf *xmit = &port_8250->port.state->xmit;
struct tty_port *tport = &port_8250->port.state->port;

if (isr & UDMA_INTR_TX_ABORT) {
if (priv->tx_running)
dev_err(dev, "Unexpected TX_ABORT interrupt\n");
return;
}
priv->tx_running = false;
if (!uart_circ_empty(xmit) && !uart_tx_stopped(up))
if (!kfifo_is_empty(&tport->xmit_fifo) && !uart_tx_stopped(up))
brcmuart_tx_dma(port_8250);
}

Expand Down
3 changes: 2 additions & 1 deletion drivers/tty/serial/8250/8250_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,8 @@ static void serial8250_backup_timeout(struct timer_list *t)
*/
lsr = serial_lsr_in(up);
if ((iir & UART_IIR_NO_INT) && (up->ier & UART_IER_THRI) &&
(!uart_circ_empty(&up->port.state->xmit) || up->port.x_char) &&
(!kfifo_is_empty(&up->port.state->port.xmit_fifo) ||
up->port.x_char) &&
(lsr & UART_LSR_THRE)) {
iir &= ~(UART_IIR_ID | UART_IIR_NO_INT);
iir |= UART_IIR_THRI;
Expand Down
23 changes: 14 additions & 9 deletions drivers/tty/serial/8250/8250_dma.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ static void __dma_tx_complete(void *param)
{
struct uart_8250_port *p = param;
struct uart_8250_dma *dma = p->dma;
struct circ_buf *xmit = &p->port.state->xmit;
struct tty_port *tport = &p->port.state->port;
unsigned long flags;
int ret;

Expand All @@ -28,7 +28,7 @@ static void __dma_tx_complete(void *param)

uart_xmit_advance(&p->port, dma->tx_size);

if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(&p->port);

ret = serial8250_tx_dma(p);
Expand Down Expand Up @@ -86,7 +86,7 @@ static void dma_rx_complete(void *param)
int serial8250_tx_dma(struct uart_8250_port *p)
{
struct uart_8250_dma *dma = p->dma;
struct circ_buf *xmit = &p->port.state->xmit;
struct tty_port *tport = &p->port.state->port;
struct dma_async_tx_descriptor *desc;
struct uart_port *up = &p->port;
struct scatterlist sg;
Expand All @@ -103,18 +103,23 @@ int serial8250_tx_dma(struct uart_8250_port *p)
uart_xchar_out(up, UART_TX);
}

if (uart_tx_stopped(&p->port) || uart_circ_empty(xmit)) {
if (uart_tx_stopped(&p->port) || kfifo_is_empty(&tport->xmit_fifo)) {
/* We have been called from __dma_tx_complete() */
return 0;
}

dma->tx_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);

serial8250_do_prepare_tx_dma(p);

sg_init_table(&sg, 1);
sg_dma_address(&sg) = dma->tx_addr + xmit->tail;
sg_dma_len(&sg) = dma->tx_size;
/* kfifo can do more than one sg, we don't (quite yet) */
ret = kfifo_dma_out_prepare_mapped(&tport->xmit_fifo, &sg, 1,
UART_XMIT_SIZE, dma->tx_addr);

/* we already checked empty fifo above, so there should be something */
if (WARN_ON_ONCE(ret != 1))
return 0;

dma->tx_size = sg_dma_len(&sg);

desc = dmaengine_prep_slave_sg(dma->txchan, &sg, 1,
DMA_MEM_TO_DEV,
Expand Down Expand Up @@ -257,7 +262,7 @@ int serial8250_request_dma(struct uart_8250_port *p)

/* TX buffer */
dma->tx_addr = dma_map_single(dma->txchan->device->dev,
p->port.state->xmit.buf,
p->port.state->port.xmit_buf,
UART_XMIT_SIZE,
DMA_TO_DEVICE);
if (dma_mapping_error(dma->txchan->device->dev, dma->tx_addr)) {
Expand Down
5 changes: 3 additions & 2 deletions drivers/tty/serial/8250/8250_exar.c
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ static void exar_shutdown(struct uart_port *port)
{
bool tx_complete = false;
struct uart_8250_port *up = up_to_u8250p(port);
struct circ_buf *xmit = &port->state->xmit;
struct tty_port *tport = &port->state->port;
int i = 0;
u16 lsr;

Expand All @@ -225,7 +225,8 @@ static void exar_shutdown(struct uart_port *port)
else
tx_complete = false;
usleep_range(1000, 1100);
} while (!uart_circ_empty(xmit) && !tx_complete && i++ < 1000);
} while (!kfifo_is_empty(&tport->xmit_fifo) &&
!tx_complete && i++ < 1000);

serial8250_do_shutdown(port);
}
Expand Down
2 changes: 1 addition & 1 deletion drivers/tty/serial/8250/8250_mtk.c
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ static int mtk8250_startup(struct uart_port *port)

if (up->dma) {
data->rx_status = DMA_RX_START;
uart_circ_clear(&port->state->xmit);
kfifo_reset(&port->state->port.xmit_fifo);
}
#endif
memset(&port->icount, 0, sizeof(port->icount));
Expand Down
47 changes: 31 additions & 16 deletions drivers/tty/serial/8250/8250_omap.c
Original file line number Diff line number Diff line change
Expand Up @@ -1094,7 +1094,7 @@ static void omap_8250_dma_tx_complete(void *param)
{
struct uart_8250_port *p = param;
struct uart_8250_dma *dma = p->dma;
struct circ_buf *xmit = &p->port.state->xmit;
struct tty_port *tport = &p->port.state->port;
unsigned long flags;
bool en_thri = false;
struct omap8250_priv *priv = p->port.private_data;
Expand All @@ -1113,10 +1113,10 @@ static void omap_8250_dma_tx_complete(void *param)
omap8250_restore_regs(p);
}

if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(&p->port);

if (!uart_circ_empty(xmit) && !uart_tx_stopped(&p->port)) {
if (!kfifo_is_empty(&tport->xmit_fifo) && !uart_tx_stopped(&p->port)) {
int ret;

ret = omap_8250_tx_dma(p);
Expand All @@ -1138,15 +1138,15 @@ static int omap_8250_tx_dma(struct uart_8250_port *p)
{
struct uart_8250_dma *dma = p->dma;
struct omap8250_priv *priv = p->port.private_data;
struct circ_buf *xmit = &p->port.state->xmit;
struct tty_port *tport = &p->port.state->port;
struct dma_async_tx_descriptor *desc;
struct scatterlist sg;
unsigned int skip_byte = 0;
int skip_byte = -1;
int ret;

if (dma->tx_running)
return 0;
if (uart_tx_stopped(&p->port) || uart_circ_empty(xmit)) {
if (uart_tx_stopped(&p->port) || kfifo_is_empty(&tport->xmit_fifo)) {

/*
* Even if no data, we need to return an error for the two cases
Expand All @@ -1161,8 +1161,18 @@ static int omap_8250_tx_dma(struct uart_8250_port *p)
return 0;
}

dma->tx_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
sg_init_table(&sg, 1);
ret = kfifo_dma_out_prepare_mapped(&tport->xmit_fifo, &sg, 1,
UART_XMIT_SIZE, dma->tx_addr);
if (ret != 1) {
serial8250_clear_THRI(p);
return 0;
}

dma->tx_size = sg_dma_len(&sg);

if (priv->habit & OMAP_DMA_TX_KICK) {
unsigned char c;
u8 tx_lvl;

/*
Expand All @@ -1189,13 +1199,16 @@ static int omap_8250_tx_dma(struct uart_8250_port *p)
ret = -EINVAL;
goto err;
}
skip_byte = 1;
if (!kfifo_get(&tport->xmit_fifo, &c)) {
ret = -EINVAL;
goto err;
}
skip_byte = c;
/* now we need to recompute due to kfifo_get */
kfifo_dma_out_prepare_mapped(&tport->xmit_fifo, &sg, 1,
UART_XMIT_SIZE, dma->tx_addr);
}

sg_init_table(&sg, 1);
sg_dma_address(&sg) = dma->tx_addr + xmit->tail + skip_byte;
sg_dma_len(&sg) = dma->tx_size - skip_byte;

desc = dmaengine_prep_slave_sg(dma->txchan, &sg, 1, DMA_MEM_TO_DEV,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc) {
Expand All @@ -1218,11 +1231,13 @@ static int omap_8250_tx_dma(struct uart_8250_port *p)
dma->tx_err = 0;

serial8250_clear_THRI(p);
if (skip_byte)
serial_out(p, UART_TX, xmit->buf[xmit->tail]);
return 0;
ret = 0;
goto out_skip;
err:
dma->tx_err = 1;
out_skip:
if (skip_byte >= 0)
serial_out(p, UART_TX, skip_byte);
return ret;
}

Expand Down Expand Up @@ -1311,7 +1326,7 @@ static int omap_8250_dma_handle_irq(struct uart_port *port)
serial8250_modem_status(up);
if (status & UART_LSR_THRE && up->dma->tx_err) {
if (uart_tx_stopped(&up->port) ||
uart_circ_empty(&up->port.state->xmit)) {
kfifo_is_empty(&up->port.state->port.xmit_fifo)) {
up->dma->tx_err = 0;
serial8250_tx_chars(up);
} else {
Expand Down
50 changes: 22 additions & 28 deletions drivers/tty/serial/8250/8250_pci1xxxx.c
Original file line number Diff line number Diff line change
Expand Up @@ -382,10 +382,10 @@ static void pci1xxxx_rx_burst(struct uart_port *port, u32 uart_status)
}

static void pci1xxxx_process_write_data(struct uart_port *port,
struct circ_buf *xmit,
int *data_empty_count,
u32 *valid_byte_count)
{
struct tty_port *tport = &port->state->port;
u32 valid_burst_count = *valid_byte_count / UART_BURST_SIZE;

/*
Expand All @@ -395,53 +395,46 @@ static void pci1xxxx_process_write_data(struct uart_port *port,
* one byte at a time.
*/
while (valid_burst_count) {
u32 c;

if (*data_empty_count - UART_BURST_SIZE < 0)
break;
if (xmit->tail > (UART_XMIT_SIZE - UART_BURST_SIZE))
if (kfifo_len(&tport->xmit_fifo) < UART_BURST_SIZE)
break;
if (WARN_ON(kfifo_out(&tport->xmit_fifo, (u8 *)&c, sizeof(c)) !=
sizeof(c)))
break;
writel(*(unsigned int *)&xmit->buf[xmit->tail],
port->membase + UART_TX_BURST_FIFO);
writel(c, port->membase + UART_TX_BURST_FIFO);
*valid_byte_count -= UART_BURST_SIZE;
*data_empty_count -= UART_BURST_SIZE;
valid_burst_count -= UART_BYTE_SIZE;

xmit->tail = (xmit->tail + UART_BURST_SIZE) &
(UART_XMIT_SIZE - 1);
}

while (*valid_byte_count) {
if (*data_empty_count - UART_BYTE_SIZE < 0)
u8 c;

if (!kfifo_get(&tport->xmit_fifo, &c))
break;
writeb(xmit->buf[xmit->tail], port->membase +
UART_TX_BYTE_FIFO);
writeb(c, port->membase + UART_TX_BYTE_FIFO);
*data_empty_count -= UART_BYTE_SIZE;
*valid_byte_count -= UART_BYTE_SIZE;

/*
* When the tail of the circular buffer is reached, the next
* byte is transferred to the beginning of the buffer.
*/
xmit->tail = (xmit->tail + UART_BYTE_SIZE) &
(UART_XMIT_SIZE - 1);

/*
* If there are any pending burst count, data is handled by
* transmitting DWORDs at a time.
*/
if (valid_burst_count && (xmit->tail <
(UART_XMIT_SIZE - UART_BURST_SIZE)))
if (valid_burst_count &&
kfifo_len(&tport->xmit_fifo) >= UART_BURST_SIZE)
break;
}
}

static void pci1xxxx_tx_burst(struct uart_port *port, u32 uart_status)
{
struct uart_8250_port *up = up_to_u8250p(port);
struct tty_port *tport = &port->state->port;
u32 valid_byte_count;
int data_empty_count;
struct circ_buf *xmit;

xmit = &port->state->xmit;

if (port->x_char) {
writeb(port->x_char, port->membase + UART_TX);
Expand All @@ -450,33 +443,34 @@ static void pci1xxxx_tx_burst(struct uart_port *port, u32 uart_status)
return;
}

if ((uart_tx_stopped(port)) || (uart_circ_empty(xmit))) {
if ((uart_tx_stopped(port)) || kfifo_is_empty(&tport->xmit_fifo)) {
port->ops->stop_tx(port);
} else {
data_empty_count = (pci1xxxx_read_burst_status(port) &
UART_BST_STAT_TX_COUNT_MASK) >> 8;
do {
valid_byte_count = uart_circ_chars_pending(xmit);
valid_byte_count = kfifo_len(&tport->xmit_fifo);

pci1xxxx_process_write_data(port, xmit,
pci1xxxx_process_write_data(port,
&data_empty_count,
&valid_byte_count);

port->icount.tx++;
if (uart_circ_empty(xmit))
if (kfifo_is_empty(&tport->xmit_fifo))
break;
} while (data_empty_count && valid_byte_count);
}

if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(port);

/*
* With RPM enabled, we have to wait until the FIFO is empty before
* the HW can go idle. So we get here once again with empty FIFO and
* disable the interrupt and RPM in __stop_tx()
*/
if (uart_circ_empty(xmit) && !(up->capabilities & UART_CAP_RPM))
if (kfifo_is_empty(&tport->xmit_fifo) &&
!(up->capabilities & UART_CAP_RPM))
port->ops->stop_tx(port);
}

Expand Down
Loading

0 comments on commit 1788cf6

Please sign in to comment.