linux/drivers/tty/serial/sprd_serial.c

1270 lines
30 KiB
C
Raw Normal View History

tty: add SPDX identifiers to all remaining files in drivers/tty/ It's good to have SPDX identifiers in all files to make it easier to audit the kernel tree for correct licenses. Update the drivers/tty files files with the correct SPDX license identifier based on the license text in the file itself. The SPDX identifier is a legally binding shorthand, which can be used instead of the full boiler plate text. This work is based on a script and data from Thomas Gleixner, Philippe Ombredanne, and Kate Stewart. Cc: Jiri Slaby <jslaby@suse.com> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org> Cc: Paul Mackerras <paulus@samba.org> Cc: Michael Ellerman <mpe@ellerman.id.au> Cc: Chris Metcalf <cmetcalf@mellanox.com> Cc: Jiri Kosina <jikos@kernel.org> Cc: David Sterba <dsterba@suse.com> Cc: James Hogan <jhogan@kernel.org> Cc: Rob Herring <robh@kernel.org> Cc: Eric Anholt <eric@anholt.net> Cc: Stefan Wahren <stefan.wahren@i2se.com> Cc: Florian Fainelli <f.fainelli@gmail.com> Cc: Ray Jui <rjui@broadcom.com> Cc: Scott Branden <sbranden@broadcom.com> Cc: bcm-kernel-feedback-list@broadcom.com Cc: "James E.J. Bottomley" <jejb@parisc-linux.org> Cc: Helge Deller <deller@gmx.de> Cc: Joachim Eastwood <manabian@gmail.com> Cc: Matthias Brugger <matthias.bgg@gmail.com> Cc: Masahiro Yamada <yamada.masahiro@socionext.com> Cc: Tobias Klauser <tklauser@distanz.ch> Cc: Russell King <linux@armlinux.org.uk> Cc: Vineet Gupta <vgupta@synopsys.com> Cc: Richard Genoud <richard.genoud@gmail.com> Cc: Alexander Shiyan <shc_work@mail.ru> Cc: Baruch Siach <baruch@tkos.co.il> Cc: "Maciej W. Rozycki" <macro@linux-mips.org> Cc: "Uwe Kleine-König" <kernel@pengutronix.de> Cc: Pat Gefre <pfg@sgi.com> Cc: "Guilherme G. Piccoli" <gpiccoli@linux.vnet.ibm.com> Cc: Jason Wessel <jason.wessel@windriver.com> Cc: Vladimir Zapolskiy <vz@mleia.com> Cc: Sylvain Lemieux <slemieux.tyco@gmail.com> Cc: Carlo Caione <carlo@caione.org> Cc: Kevin Hilman <khilman@baylibre.com> Cc: Liviu Dudau <liviu.dudau@arm.com> Cc: Sudeep Holla <sudeep.holla@arm.com> Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> Cc: Andy Gross <andy.gross@linaro.org> Cc: David Brown <david.brown@linaro.org> Cc: "Andreas Färber" <afaerber@suse.de> Cc: Kevin Cernekee <cernekee@gmail.com> Cc: Laxman Dewangan <ldewangan@nvidia.com> Cc: Thierry Reding <thierry.reding@gmail.com> Cc: Jonathan Hunter <jonathanh@nvidia.com> Cc: Barry Song <baohua@kernel.org> Cc: Patrice Chotard <patrice.chotard@st.com> Cc: Maxime Coquelin <mcoquelin.stm32@gmail.com> Cc: Alexandre Torgue <alexandre.torgue@st.com> Cc: "David S. Miller" <davem@davemloft.net> Cc: Peter Korsgaard <jacmet@sunsite.dk> Cc: Timur Tabi <timur@tabi.org> Cc: Tony Prisk <linux@prisktech.co.nz> Cc: Michal Simek <michal.simek@xilinx.com> Cc: "Sören Brinkmann" <soren.brinkmann@xilinx.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Kate Stewart <kstewart@linuxfoundation.org> Cc: Philippe Ombredanne <pombredanne@nexb.com> Cc: Jiri Slaby <jslaby@suse.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2017-11-06 17:11:51 +00:00
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2012-2015 Spreadtrum Communications Inc.
*/
#include <linux/clk.h>
#include <linux/console.h>
#include <linux/delay.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/dma/sprd-dma.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/serial_core.h>
#include <linux/serial.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
/* device name */
#define UART_NR_MAX 8
#define SPRD_TTY_NAME "ttyS"
#define SPRD_FIFO_SIZE 128
#define SPRD_DEF_RATE 26000000
#define SPRD_BAUD_IO_LIMIT 3000000
#define SPRD_TIMEOUT 256000
/* the offset of serial registers and BITs for them */
/* data registers */
#define SPRD_TXD 0x0000
#define SPRD_RXD 0x0004
/* line status register and its BITs */
#define SPRD_LSR 0x0008
#define SPRD_LSR_OE BIT(4)
#define SPRD_LSR_FE BIT(3)
#define SPRD_LSR_PE BIT(2)
#define SPRD_LSR_BI BIT(7)
#define SPRD_LSR_TX_OVER BIT(15)
/* data number in TX and RX fifo */
#define SPRD_STS1 0x000C
#define SPRD_RX_FIFO_CNT_MASK GENMASK(7, 0)
#define SPRD_TX_FIFO_CNT_MASK GENMASK(15, 8)
/* interrupt enable register and its BITs */
#define SPRD_IEN 0x0010
#define SPRD_IEN_RX_FULL BIT(0)
#define SPRD_IEN_TX_EMPTY BIT(1)
#define SPRD_IEN_BREAK_DETECT BIT(7)
#define SPRD_IEN_TIMEOUT BIT(13)
/* interrupt clear register */
#define SPRD_ICLR 0x0014
#define SPRD_ICLR_TIMEOUT BIT(13)
/* line control register */
#define SPRD_LCR 0x0018
#define SPRD_LCR_STOP_1BIT 0x10
#define SPRD_LCR_STOP_2BIT 0x30
#define SPRD_LCR_DATA_LEN (BIT(2) | BIT(3))
#define SPRD_LCR_DATA_LEN5 0x0
#define SPRD_LCR_DATA_LEN6 0x4
#define SPRD_LCR_DATA_LEN7 0x8
#define SPRD_LCR_DATA_LEN8 0xc
#define SPRD_LCR_PARITY (BIT(0) | BIT(1))
#define SPRD_LCR_PARITY_EN 0x2
#define SPRD_LCR_EVEN_PAR 0x0
#define SPRD_LCR_ODD_PAR 0x1
/* control register 1 */
#define SPRD_CTL1 0x001C
#define SPRD_DMA_EN BIT(15)
#define SPRD_LOOPBACK_EN BIT(14)
#define RX_HW_FLOW_CTL_THLD BIT(6)
#define RX_HW_FLOW_CTL_EN BIT(7)
#define TX_HW_FLOW_CTL_EN BIT(8)
#define RX_TOUT_THLD_DEF 0x3E00
#define RX_HFC_THLD_DEF 0x40
/* fifo threshold register */
#define SPRD_CTL2 0x0020
#define THLD_TX_EMPTY 0x40
#define THLD_TX_EMPTY_SHIFT 8
#define THLD_RX_FULL 0x40
#define THLD_RX_FULL_MASK GENMASK(6, 0)
/* config baud rate register */
#define SPRD_CLKD0 0x0024
#define SPRD_CLKD0_MASK GENMASK(15, 0)
#define SPRD_CLKD1 0x0028
#define SPRD_CLKD1_MASK GENMASK(20, 16)
#define SPRD_CLKD1_SHIFT 16
/* interrupt mask status register */
#define SPRD_IMSR 0x002C
#define SPRD_IMSR_RX_FIFO_FULL BIT(0)
#define SPRD_IMSR_TX_FIFO_EMPTY BIT(1)
#define SPRD_IMSR_BREAK_DETECT BIT(7)
#define SPRD_IMSR_TIMEOUT BIT(13)
#define SPRD_DEFAULT_SOURCE_CLK 26000000
#define SPRD_RX_DMA_STEP 1
#define SPRD_RX_FIFO_FULL 1
#define SPRD_TX_FIFO_FULL 0x20
#define SPRD_UART_RX_SIZE (UART_XMIT_SIZE / 4)
struct sprd_uart_dma {
struct dma_chan *chn;
unsigned char *virt;
dma_addr_t phys_addr;
dma_cookie_t cookie;
u32 trans_len;
bool enable;
};
struct sprd_uart_port {
struct uart_port port;
char name[16];
struct clk *clk;
struct sprd_uart_dma tx_dma;
struct sprd_uart_dma rx_dma;
dma_addr_t pos;
unsigned char *rx_buf_tail;
};
static struct sprd_uart_port *sprd_port[UART_NR_MAX];
static int sprd_ports_num;
static int sprd_start_dma_rx(struct uart_port *port);
static int sprd_tx_dma_config(struct uart_port *port);
static inline unsigned int serial_in(struct uart_port *port,
unsigned int offset)
{
return readl_relaxed(port->membase + offset);
}
static inline void serial_out(struct uart_port *port, unsigned int offset,
int value)
{
writel_relaxed(value, port->membase + offset);
}
static unsigned int sprd_tx_empty(struct uart_port *port)
{
if (serial_in(port, SPRD_STS1) & SPRD_TX_FIFO_CNT_MASK)
return 0;
else
return TIOCSER_TEMT;
}
static unsigned int sprd_get_mctrl(struct uart_port *port)
{
return TIOCM_DSR | TIOCM_CTS;
}
static void sprd_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
u32 val = serial_in(port, SPRD_CTL1);
if (mctrl & TIOCM_LOOP)
val |= SPRD_LOOPBACK_EN;
else
val &= ~SPRD_LOOPBACK_EN;
serial_out(port, SPRD_CTL1, val);
}
static void sprd_stop_rx(struct uart_port *port)
{
struct sprd_uart_port *sp =
container_of(port, struct sprd_uart_port, port);
unsigned int ien, iclr;
if (sp->rx_dma.enable)
dmaengine_terminate_all(sp->rx_dma.chn);
iclr = serial_in(port, SPRD_ICLR);
ien = serial_in(port, SPRD_IEN);
ien &= ~(SPRD_IEN_RX_FULL | SPRD_IEN_BREAK_DETECT);
iclr |= SPRD_IEN_RX_FULL | SPRD_IEN_BREAK_DETECT;
serial_out(port, SPRD_IEN, ien);
serial_out(port, SPRD_ICLR, iclr);
}
static void sprd_uart_dma_enable(struct uart_port *port, bool enable)
{
u32 val = serial_in(port, SPRD_CTL1);
if (enable)
val |= SPRD_DMA_EN;
else
val &= ~SPRD_DMA_EN;
serial_out(port, SPRD_CTL1, val);
}
static void sprd_stop_tx_dma(struct uart_port *port)
{
struct sprd_uart_port *sp =
container_of(port, struct sprd_uart_port, port);
struct dma_tx_state state;
u32 trans_len;
dmaengine_pause(sp->tx_dma.chn);
dmaengine_tx_status(sp->tx_dma.chn, sp->tx_dma.cookie, &state);
if (state.residue) {
trans_len = state.residue - sp->tx_dma.phys_addr;
uart_xmit_advance(port, trans_len);
dma_unmap_single(port->dev, sp->tx_dma.phys_addr,
sp->tx_dma.trans_len, DMA_TO_DEVICE);
}
dmaengine_terminate_all(sp->tx_dma.chn);
sp->tx_dma.trans_len = 0;
}
static int sprd_tx_buf_remap(struct uart_port *port)
{
struct sprd_uart_port *sp =
container_of(port, struct sprd_uart_port, port);
tty: serial: switch from circ_buf to kfifo 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>
2024-04-05 06:08:23 +00:00
struct tty_port *tport = &port->state->port;
unsigned char *tail;
tty: serial: switch from circ_buf to kfifo 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>
2024-04-05 06:08:23 +00:00
sp->tx_dma.trans_len = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail,
UART_XMIT_SIZE);
tty: serial: switch from circ_buf to kfifo 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>
2024-04-05 06:08:23 +00:00
sp->tx_dma.phys_addr = dma_map_single(port->dev, tail,
sp->tx_dma.trans_len,
DMA_TO_DEVICE);
return dma_mapping_error(port->dev, sp->tx_dma.phys_addr);
}
static void sprd_complete_tx_dma(void *data)
{
struct uart_port *port = (struct uart_port *)data;
struct sprd_uart_port *sp =
container_of(port, struct sprd_uart_port, port);
tty: serial: switch from circ_buf to kfifo 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>
2024-04-05 06:08:23 +00:00
struct tty_port *tport = &port->state->port;
unsigned long flags;
uart_port_lock_irqsave(port, &flags);
dma_unmap_single(port->dev, sp->tx_dma.phys_addr,
sp->tx_dma.trans_len, DMA_TO_DEVICE);
uart_xmit_advance(port, sp->tx_dma.trans_len);
tty: serial: switch from circ_buf to kfifo 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>
2024-04-05 06:08:23 +00:00
if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(port);
tty: serial: switch from circ_buf to kfifo 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>
2024-04-05 06:08:23 +00:00
if (kfifo_is_empty(&tport->xmit_fifo) || sprd_tx_buf_remap(port) ||
sprd_tx_dma_config(port))
sp->tx_dma.trans_len = 0;
uart_port_unlock_irqrestore(port, flags);
}
static int sprd_uart_dma_submit(struct uart_port *port,
struct sprd_uart_dma *ud, u32 trans_len,
enum dma_transfer_direction direction,
dma_async_tx_callback callback)
{
struct dma_async_tx_descriptor *dma_des;
unsigned long flags;
flags = SPRD_DMA_FLAGS(SPRD_DMA_CHN_MODE_NONE,
SPRD_DMA_NO_TRG,
SPRD_DMA_FRAG_REQ,
SPRD_DMA_TRANS_INT);
dma_des = dmaengine_prep_slave_single(ud->chn, ud->phys_addr, trans_len,
direction, flags);
if (!dma_des)
return -ENODEV;
dma_des->callback = callback;
dma_des->callback_param = port;
ud->cookie = dmaengine_submit(dma_des);
if (dma_submit_error(ud->cookie))
return dma_submit_error(ud->cookie);
dma_async_issue_pending(ud->chn);
return 0;
}
static int sprd_tx_dma_config(struct uart_port *port)
{
struct sprd_uart_port *sp =
container_of(port, struct sprd_uart_port, port);
u32 burst = sp->tx_dma.trans_len > SPRD_TX_FIFO_FULL ?
SPRD_TX_FIFO_FULL : sp->tx_dma.trans_len;
int ret;
struct dma_slave_config cfg = {
.dst_addr = port->mapbase + SPRD_TXD,
.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
.src_maxburst = burst,
};
ret = dmaengine_slave_config(sp->tx_dma.chn, &cfg);
if (ret < 0)
return ret;
return sprd_uart_dma_submit(port, &sp->tx_dma, sp->tx_dma.trans_len,
DMA_MEM_TO_DEV, sprd_complete_tx_dma);
}
static void sprd_start_tx_dma(struct uart_port *port)
{
struct sprd_uart_port *sp =
container_of(port, struct sprd_uart_port, port);
tty: serial: switch from circ_buf to kfifo 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>
2024-04-05 06:08:23 +00:00
struct tty_port *tport = &port->state->port;
if (port->x_char) {
serial_out(port, SPRD_TXD, port->x_char);
port->icount.tx++;
port->x_char = 0;
return;
}
tty: serial: switch from circ_buf to kfifo 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>
2024-04-05 06:08:23 +00:00
if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) {
sprd_stop_tx_dma(port);
return;
}
if (sp->tx_dma.trans_len)
return;
if (sprd_tx_buf_remap(port) || sprd_tx_dma_config(port))
sp->tx_dma.trans_len = 0;
}
static void sprd_rx_full_thld(struct uart_port *port, u32 thld)
{
u32 val = serial_in(port, SPRD_CTL2);
val &= ~THLD_RX_FULL_MASK;
val |= thld & THLD_RX_FULL_MASK;
serial_out(port, SPRD_CTL2, val);
}
static int sprd_rx_alloc_buf(struct sprd_uart_port *sp)
{
sp->rx_dma.virt = dma_alloc_coherent(sp->port.dev, SPRD_UART_RX_SIZE,
&sp->rx_dma.phys_addr, GFP_KERNEL);
if (!sp->rx_dma.virt)
return -ENOMEM;
return 0;
}
static void sprd_rx_free_buf(struct sprd_uart_port *sp)
{
if (sp->rx_dma.virt)
dma_free_coherent(sp->port.dev, SPRD_UART_RX_SIZE,
sp->rx_dma.virt, sp->rx_dma.phys_addr);
sp->rx_dma.virt = NULL;
}
static int sprd_rx_dma_config(struct uart_port *port, u32 burst)
{
struct sprd_uart_port *sp =
container_of(port, struct sprd_uart_port, port);
struct dma_slave_config cfg = {
.src_addr = port->mapbase + SPRD_RXD,
.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
.src_maxburst = burst,
};
return dmaengine_slave_config(sp->rx_dma.chn, &cfg);
}
static void sprd_uart_dma_rx(struct uart_port *port)
{
struct sprd_uart_port *sp =
container_of(port, struct sprd_uart_port, port);
struct tty_port *tty = &port->state->port;
port->icount.rx += sp->rx_dma.trans_len;
tty_insert_flip_string(tty, sp->rx_buf_tail, sp->rx_dma.trans_len);
tty_flip_buffer_push(tty);
}
static void sprd_uart_dma_irq(struct uart_port *port)
{
struct sprd_uart_port *sp =
container_of(port, struct sprd_uart_port, port);
struct dma_tx_state state;
enum dma_status status;
status = dmaengine_tx_status(sp->rx_dma.chn,
sp->rx_dma.cookie, &state);
if (status == DMA_ERROR)
sprd_stop_rx(port);
if (!state.residue && sp->pos == sp->rx_dma.phys_addr)
return;
if (!state.residue) {
sp->rx_dma.trans_len = SPRD_UART_RX_SIZE +
sp->rx_dma.phys_addr - sp->pos;
sp->pos = sp->rx_dma.phys_addr;
} else {
sp->rx_dma.trans_len = state.residue - sp->pos;
sp->pos = state.residue;
}
sprd_uart_dma_rx(port);
sp->rx_buf_tail += sp->rx_dma.trans_len;
}
static void sprd_complete_rx_dma(void *data)
{
struct uart_port *port = (struct uart_port *)data;
struct sprd_uart_port *sp =
container_of(port, struct sprd_uart_port, port);
struct dma_tx_state state;
enum dma_status status;
unsigned long flags;
uart_port_lock_irqsave(port, &flags);
status = dmaengine_tx_status(sp->rx_dma.chn,
sp->rx_dma.cookie, &state);
if (status != DMA_COMPLETE) {
sprd_stop_rx(port);
uart_port_unlock_irqrestore(port, flags);
return;
}
if (sp->pos != sp->rx_dma.phys_addr) {
sp->rx_dma.trans_len = SPRD_UART_RX_SIZE +
sp->rx_dma.phys_addr - sp->pos;
sprd_uart_dma_rx(port);
sp->rx_buf_tail += sp->rx_dma.trans_len;
}
if (sprd_start_dma_rx(port))
sprd_stop_rx(port);
uart_port_unlock_irqrestore(port, flags);
}
static int sprd_start_dma_rx(struct uart_port *port)
{
struct sprd_uart_port *sp =
container_of(port, struct sprd_uart_port, port);
int ret;
if (!sp->rx_dma.enable)
return 0;
sp->pos = sp->rx_dma.phys_addr;
sp->rx_buf_tail = sp->rx_dma.virt;
sprd_rx_full_thld(port, SPRD_RX_FIFO_FULL);
ret = sprd_rx_dma_config(port, SPRD_RX_DMA_STEP);
if (ret)
return ret;
return sprd_uart_dma_submit(port, &sp->rx_dma, SPRD_UART_RX_SIZE,
DMA_DEV_TO_MEM, sprd_complete_rx_dma);
}
static void sprd_release_dma(struct uart_port *port)
{
struct sprd_uart_port *sp =
container_of(port, struct sprd_uart_port, port);
sprd_uart_dma_enable(port, false);
if (sp->rx_dma.enable)
dma_release_channel(sp->rx_dma.chn);
if (sp->tx_dma.enable)
dma_release_channel(sp->tx_dma.chn);
sp->tx_dma.enable = false;
sp->rx_dma.enable = false;
}
static void sprd_request_dma(struct uart_port *port)
{
struct sprd_uart_port *sp =
container_of(port, struct sprd_uart_port, port);
sp->tx_dma.enable = true;
sp->rx_dma.enable = true;
sp->tx_dma.chn = dma_request_chan(port->dev, "tx");
if (IS_ERR(sp->tx_dma.chn)) {
dev_err(port->dev, "request TX DMA channel failed, ret = %ld\n",
PTR_ERR(sp->tx_dma.chn));
sp->tx_dma.enable = false;
}
sp->rx_dma.chn = dma_request_chan(port->dev, "rx");
if (IS_ERR(sp->rx_dma.chn)) {
dev_err(port->dev, "request RX DMA channel failed, ret = %ld\n",
PTR_ERR(sp->rx_dma.chn));
sp->rx_dma.enable = false;
}
}
static void sprd_stop_tx(struct uart_port *port)
{
struct sprd_uart_port *sp = container_of(port, struct sprd_uart_port,
port);
unsigned int ien, iclr;
if (sp->tx_dma.enable) {
sprd_stop_tx_dma(port);
return;
}
iclr = serial_in(port, SPRD_ICLR);
ien = serial_in(port, SPRD_IEN);
iclr |= SPRD_IEN_TX_EMPTY;
ien &= ~SPRD_IEN_TX_EMPTY;
serial_out(port, SPRD_IEN, ien);
serial_out(port, SPRD_ICLR, iclr);
}
static void sprd_start_tx(struct uart_port *port)
{
struct sprd_uart_port *sp = container_of(port, struct sprd_uart_port,
port);
unsigned int ien;
if (sp->tx_dma.enable) {
sprd_start_tx_dma(port);
return;
}
ien = serial_in(port, SPRD_IEN);
if (!(ien & SPRD_IEN_TX_EMPTY)) {
ien |= SPRD_IEN_TX_EMPTY;
serial_out(port, SPRD_IEN, ien);
}
}
/* The Sprd serial does not support this function. */
static void sprd_break_ctl(struct uart_port *port, int break_state)
{
/* nothing to do */
}
static int handle_lsr_errors(struct uart_port *port,
serial: drivers: switch ch and flag to u8 Now that the serial layer explicitly expects 'u8' for flags and characters, propagate this type to drivers' (RX) routines. Note that amba-pl011's, clps711x's and st-asc's 'ch' are left unchanged because 'ch' contains not only a character, but whole status. Signed-off-by: Jiri Slaby (SUSE) <jirislaby@kernel.org> Cc: Tobias Klauser <tklauser@distanz.ch> 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@microchip.com> Cc: Alexander Shiyan <shc_work@mail.ru> Cc: Baruch Siach <baruch@tkos.co.il> Cc: "Maciej W. Rozycki" <macro@orcam.me.uk> Cc: Taichi Sugaya <sugaya.taichi@socionext.com> Cc: Takao Orito <orito.takao@socionext.com> Cc: Shawn Guo <shawnguo@kernel.org> Cc: Sascha Hauer <s.hauer@pengutronix.de> Cc: Pengutronix Kernel Team <kernel@pengutronix.de> Cc: Fabio Estevam <festevam@gmail.com> Cc: NXP Linux Team <linux-imx@nxp.com> Cc: Kevin Cernekee <cernekee@gmail.com> 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: Palmer Dabbelt <palmer@dabbelt.com> Cc: Paul Walmsley <paul.walmsley@sifive.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: Hammer Hsieh <hammerh0314@gmail.com> Acked-by: Richard GENOUD <richard.genoud@gmail.com> Acked-by: Tobias Klauser <tklauser@distanz.ch> Acked-by: Thierry Reding <treding@nvidia.com> Acked-by: Maciej W. Rozycki <macro@orcam.me.uk> Link: https://lore.kernel.org/r/20230712081811.29004-11-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-07-12 08:18:11 +00:00
u8 *flag,
unsigned int *lsr)
{
int ret = 0;
/* statistics */
if (*lsr & SPRD_LSR_BI) {
*lsr &= ~(SPRD_LSR_FE | SPRD_LSR_PE);
port->icount.brk++;
ret = uart_handle_break(port);
if (ret)
return ret;
} else if (*lsr & SPRD_LSR_PE)
port->icount.parity++;
else if (*lsr & SPRD_LSR_FE)
port->icount.frame++;
if (*lsr & SPRD_LSR_OE)
port->icount.overrun++;
/* mask off conditions which should be ignored */
*lsr &= port->read_status_mask;
if (*lsr & SPRD_LSR_BI)
*flag = TTY_BREAK;
else if (*lsr & SPRD_LSR_PE)
*flag = TTY_PARITY;
else if (*lsr & SPRD_LSR_FE)
*flag = TTY_FRAME;
return ret;
}
static inline void sprd_rx(struct uart_port *port)
{
struct sprd_uart_port *sp = container_of(port, struct sprd_uart_port,
port);
struct tty_port *tty = &port->state->port;
serial: drivers: switch ch and flag to u8 Now that the serial layer explicitly expects 'u8' for flags and characters, propagate this type to drivers' (RX) routines. Note that amba-pl011's, clps711x's and st-asc's 'ch' are left unchanged because 'ch' contains not only a character, but whole status. Signed-off-by: Jiri Slaby (SUSE) <jirislaby@kernel.org> Cc: Tobias Klauser <tklauser@distanz.ch> 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@microchip.com> Cc: Alexander Shiyan <shc_work@mail.ru> Cc: Baruch Siach <baruch@tkos.co.il> Cc: "Maciej W. Rozycki" <macro@orcam.me.uk> Cc: Taichi Sugaya <sugaya.taichi@socionext.com> Cc: Takao Orito <orito.takao@socionext.com> Cc: Shawn Guo <shawnguo@kernel.org> Cc: Sascha Hauer <s.hauer@pengutronix.de> Cc: Pengutronix Kernel Team <kernel@pengutronix.de> Cc: Fabio Estevam <festevam@gmail.com> Cc: NXP Linux Team <linux-imx@nxp.com> Cc: Kevin Cernekee <cernekee@gmail.com> 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: Palmer Dabbelt <palmer@dabbelt.com> Cc: Paul Walmsley <paul.walmsley@sifive.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: Hammer Hsieh <hammerh0314@gmail.com> Acked-by: Richard GENOUD <richard.genoud@gmail.com> Acked-by: Tobias Klauser <tklauser@distanz.ch> Acked-by: Thierry Reding <treding@nvidia.com> Acked-by: Maciej W. Rozycki <macro@orcam.me.uk> Link: https://lore.kernel.org/r/20230712081811.29004-11-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-07-12 08:18:11 +00:00
unsigned int lsr, max_count = SPRD_TIMEOUT;
u8 ch, flag;
if (sp->rx_dma.enable) {
sprd_uart_dma_irq(port);
return;
}
while ((serial_in(port, SPRD_STS1) & SPRD_RX_FIFO_CNT_MASK) &&
max_count--) {
lsr = serial_in(port, SPRD_LSR);
ch = serial_in(port, SPRD_RXD);
flag = TTY_NORMAL;
port->icount.rx++;
if (lsr & (SPRD_LSR_BI | SPRD_LSR_PE |
SPRD_LSR_FE | SPRD_LSR_OE))
if (handle_lsr_errors(port, &flag, &lsr))
continue;
if (uart_handle_sysrq_char(port, ch))
continue;
uart_insert_char(port, lsr, SPRD_LSR_OE, ch, flag);
}
tty_flip_buffer_push(tty);
}
static inline void sprd_tx(struct uart_port *port)
{
u8 ch;
uart_port_tx_limited(port, ch, THLD_TX_EMPTY,
true,
serial_out(port, SPRD_TXD, ch),
({}));
}
/* this handles the interrupt from one port */
static irqreturn_t sprd_handle_irq(int irq, void *dev_id)
{
struct uart_port *port = dev_id;
unsigned int ims;
uart_port_lock(port);
ims = serial_in(port, SPRD_IMSR);
if (!ims) {
uart_port_unlock(port);
return IRQ_NONE;
}
if (ims & SPRD_IMSR_TIMEOUT)
serial_out(port, SPRD_ICLR, SPRD_ICLR_TIMEOUT);
if (ims & SPRD_IMSR_BREAK_DETECT)
serial_out(port, SPRD_ICLR, SPRD_IMSR_BREAK_DETECT);
if (ims & (SPRD_IMSR_RX_FIFO_FULL | SPRD_IMSR_BREAK_DETECT |
SPRD_IMSR_TIMEOUT))
sprd_rx(port);
if (ims & SPRD_IMSR_TX_FIFO_EMPTY)
sprd_tx(port);
uart_port_unlock(port);
return IRQ_HANDLED;
}
static void sprd_uart_dma_startup(struct uart_port *port,
struct sprd_uart_port *sp)
{
int ret;
sprd_request_dma(port);
if (!(sp->rx_dma.enable || sp->tx_dma.enable))
return;
ret = sprd_start_dma_rx(port);
if (ret) {
sp->rx_dma.enable = false;
dma_release_channel(sp->rx_dma.chn);
dev_warn(port->dev, "fail to start RX dma mode\n");
}
sprd_uart_dma_enable(port, true);
}
static int sprd_startup(struct uart_port *port)
{
int ret = 0;
unsigned int ien, fc;
unsigned int timeout;
struct sprd_uart_port *sp;
unsigned long flags;
serial_out(port, SPRD_CTL2,
THLD_TX_EMPTY << THLD_TX_EMPTY_SHIFT | THLD_RX_FULL);
/* clear rx fifo */
timeout = SPRD_TIMEOUT;
while (timeout-- && serial_in(port, SPRD_STS1) & SPRD_RX_FIFO_CNT_MASK)
serial_in(port, SPRD_RXD);
/* clear tx fifo */
timeout = SPRD_TIMEOUT;
while (timeout-- && serial_in(port, SPRD_STS1) & SPRD_TX_FIFO_CNT_MASK)
cpu_relax();
/* clear interrupt */
serial_out(port, SPRD_IEN, 0);
serial_out(port, SPRD_ICLR, ~0);
/* allocate irq */
sp = container_of(port, struct sprd_uart_port, port);
snprintf(sp->name, sizeof(sp->name), "sprd_serial%d", port->line);
sprd_uart_dma_startup(port, sp);
ret = devm_request_irq(port->dev, port->irq, sprd_handle_irq,
IRQF_SHARED, sp->name, port);
if (ret) {
dev_err(port->dev, "fail to request serial irq %d, ret=%d\n",
port->irq, ret);
return ret;
}
fc = serial_in(port, SPRD_CTL1);
fc |= RX_TOUT_THLD_DEF | RX_HFC_THLD_DEF;
serial_out(port, SPRD_CTL1, fc);
/* enable interrupt */
uart_port_lock_irqsave(port, &flags);
ien = serial_in(port, SPRD_IEN);
ien |= SPRD_IEN_BREAK_DETECT | SPRD_IEN_TIMEOUT;
if (!sp->rx_dma.enable)
ien |= SPRD_IEN_RX_FULL;
serial_out(port, SPRD_IEN, ien);
uart_port_unlock_irqrestore(port, flags);
return 0;
}
static void sprd_shutdown(struct uart_port *port)
{
sprd_release_dma(port);
serial_out(port, SPRD_IEN, 0);
serial_out(port, SPRD_ICLR, ~0);
devm_free_irq(port->dev, port->irq, port);
}
static void sprd_set_termios(struct uart_port *port, struct ktermios *termios,
const struct ktermios *old)
{
unsigned int baud, quot;
unsigned int lcr = 0, fc;
unsigned long flags;
/* ask the core to calculate the divisor for us */
baud = uart_get_baud_rate(port, termios, old, 0, SPRD_BAUD_IO_LIMIT);
quot = port->uartclk / baud;
/* set data length */
switch (termios->c_cflag & CSIZE) {
case CS5:
lcr |= SPRD_LCR_DATA_LEN5;
break;
case CS6:
lcr |= SPRD_LCR_DATA_LEN6;
break;
case CS7:
lcr |= SPRD_LCR_DATA_LEN7;
break;
case CS8:
default:
lcr |= SPRD_LCR_DATA_LEN8;
break;
}
/* calculate stop bits */
lcr &= ~(SPRD_LCR_STOP_1BIT | SPRD_LCR_STOP_2BIT);
if (termios->c_cflag & CSTOPB)
lcr |= SPRD_LCR_STOP_2BIT;
else
lcr |= SPRD_LCR_STOP_1BIT;
/* calculate parity */
lcr &= ~SPRD_LCR_PARITY;
termios->c_cflag &= ~CMSPAR; /* no support mark/space */
if (termios->c_cflag & PARENB) {
lcr |= SPRD_LCR_PARITY_EN;
if (termios->c_cflag & PARODD)
lcr |= SPRD_LCR_ODD_PAR;
else
lcr |= SPRD_LCR_EVEN_PAR;
}
uart_port_lock_irqsave(port, &flags);
/* update the per-port timeout */
uart_update_timeout(port, termios->c_cflag, baud);
port->read_status_mask = SPRD_LSR_OE;
if (termios->c_iflag & INPCK)
port->read_status_mask |= SPRD_LSR_FE | SPRD_LSR_PE;
if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
port->read_status_mask |= SPRD_LSR_BI;
/* characters to ignore */
port->ignore_status_mask = 0;
if (termios->c_iflag & IGNPAR)
port->ignore_status_mask |= SPRD_LSR_PE | SPRD_LSR_FE;
if (termios->c_iflag & IGNBRK) {
port->ignore_status_mask |= SPRD_LSR_BI;
/*
* If we're ignoring parity and break indicators,
* ignore overruns too (for real raw support).
*/
if (termios->c_iflag & IGNPAR)
port->ignore_status_mask |= SPRD_LSR_OE;
}
/* flow control */
fc = serial_in(port, SPRD_CTL1);
fc &= ~(RX_HW_FLOW_CTL_THLD | RX_HW_FLOW_CTL_EN | TX_HW_FLOW_CTL_EN);
if (termios->c_cflag & CRTSCTS) {
fc |= RX_HW_FLOW_CTL_THLD;
fc |= RX_HW_FLOW_CTL_EN;
fc |= TX_HW_FLOW_CTL_EN;
}
/* clock divider bit0~bit15 */
serial_out(port, SPRD_CLKD0, quot & SPRD_CLKD0_MASK);
/* clock divider bit16~bit20 */
serial_out(port, SPRD_CLKD1,
(quot & SPRD_CLKD1_MASK) >> SPRD_CLKD1_SHIFT);
serial_out(port, SPRD_LCR, lcr);
fc |= RX_TOUT_THLD_DEF | RX_HFC_THLD_DEF;
serial_out(port, SPRD_CTL1, fc);
uart_port_unlock_irqrestore(port, flags);
/* Don't rewrite B0 */
if (tty_termios_baud_rate(termios))
tty_termios_encode_baud_rate(termios, baud, baud);
}
static const char *sprd_type(struct uart_port *port)
{
return "SPX";
}
static void sprd_release_port(struct uart_port *port)
{
/* nothing to do */
}
static int sprd_request_port(struct uart_port *port)
{
return 0;
}
static void sprd_config_port(struct uart_port *port, int flags)
{
if (flags & UART_CONFIG_TYPE)
port->type = PORT_SPRD;
}
static int sprd_verify_port(struct uart_port *port, struct serial_struct *ser)
{
if (ser->type != PORT_SPRD)
return -EINVAL;
if (port->irq != ser->irq)
return -EINVAL;
if (port->iotype != ser->io_type)
return -EINVAL;
return 0;
}
static void sprd_pm(struct uart_port *port, unsigned int state,
unsigned int oldstate)
{
struct sprd_uart_port *sup =
container_of(port, struct sprd_uart_port, port);
switch (state) {
case UART_PM_STATE_ON:
clk_prepare_enable(sup->clk);
break;
case UART_PM_STATE_OFF:
clk_disable_unprepare(sup->clk);
break;
}
}
#ifdef CONFIG_CONSOLE_POLL
static int sprd_poll_init(struct uart_port *port)
{
if (port->state->pm_state != UART_PM_STATE_ON) {
sprd_pm(port, UART_PM_STATE_ON, 0);
port->state->pm_state = UART_PM_STATE_ON;
}
return 0;
}
static int sprd_poll_get_char(struct uart_port *port)
{
while (!(serial_in(port, SPRD_STS1) & SPRD_RX_FIFO_CNT_MASK))
cpu_relax();
return serial_in(port, SPRD_RXD);
}
static void sprd_poll_put_char(struct uart_port *port, unsigned char ch)
{
while (serial_in(port, SPRD_STS1) & SPRD_TX_FIFO_CNT_MASK)
cpu_relax();
serial_out(port, SPRD_TXD, ch);
}
#endif
tty: serial: constify uart_ops structures Declare uart_ops structures as const as they are only stored in the ops field of an uart_port structure. This field is of type const, so uart_ops structures having this property can be made const too. File size details before and after patching. First line of every .o file shows the file size before patching and second line shows the size after patching. text data bss dec hex filename 2977 456 64 3497 da9 drivers/tty/serial/amba-pl010.o 3169 272 64 3505 db1 drivers/tty/serial/amba-pl010.o 3109 456 0 3565 ded drivers/tty/serial/efm32-uart.o 3301 272 0 3573 df5 drivers/tty/serial/efm32-uart.o 10668 753 1 11422 2c9e drivers/tty/serial/icom.o 10860 561 1 11422 2c9e drivers/tty/serial/icom.o 23904 408 8 24320 5f00 drivers/tty/serial/ioc3_serial.o 24088 224 8 24320 5f00 drivers/tty/serial/ioc3_serial.o 10516 560 4 11080 2b48 drivers/tty/serial/ioc4_serial.o 10709 368 4 11081 2b49 drivers/tty/serial/ioc4_serial.o 7853 648 1216 9717 25f5 drivers/tty/serial/mpsc.o 8037 456 1216 9709 25ed drivers/tty/serial/mpsc.o 10248 456 0 10704 29d0 drivers/tty/serial/omap-serial.o 10440 272 0 10712 29d8 drivers/tty/serial/omap-serial.o 8122 532 1984 10638 298e drivers/tty/serial/pmac_zilog.o 8306 340 1984 10630 2986 drivers/tty/serial/pmac_zilog.o 3808 456 0 4264 10a8 drivers/tty/serial/pxa.o 4000 264 0 4264 10a8 drivers/tty/serial/pxa.o 21781 3864 0 25645 642d drivers/tty/serial/serial-tegra.o 22037 3608 0 25645 642d drivers/tty/serial/serial-tegra.o 2481 456 96 3033 bd9 drivers/tty/serial/sprd_serial.o 2673 272 96 3041 be1 drivers/tty/serial/sprd_serial.o 5534 300 512 6346 18ca drivers/tty/serial/vr41xx_siu.o 5630 204 512 6346 18ca drivers/tty/serial/vr41xx_siu.o 6730 1576 128 8434 20f2 drivers/tty/serial/vt8500_serial.o 6986 1320 128 8434 20f2 drivers/tty/serial/vt8500_serial.o Cross compiled for mips architecture. 3005 488 0 3493 da5 drivers/tty/serial/pnx8xxx_uart.o 3189 304 0 3493 da5 drivers/tty/serial/pnx8xxx_uart.o 4272 196 1056 5524 1594 drivers/tty/serial/dz.o 4368 100 1056 5524 1594 drivers/tty/serial/dz.o 6551 144 16 6711 1a37 drivers/tty/serial/ip22zilog.o 6647 48 16 6711 1a37 drivers/tty/serial/ip22zilog.o 9612 428 1520 11560 2d28 drivers/tty/serial/serial_txx9.o 9708 332 1520 11560 2d28 drivers/tty/serial/serial_txx9.o 4156 296 16 4468 1174 drivers/tty/serial/ar933x_uart.o 4252 200 16 4468 1174 drivers/tty/serial/ar933x_uart.o Cross compiled for arm archiecture. 11716 1780 44 13540 34e4 drivers/tty/serial/sirfsoc_uart.o 11808 1688 44 13540 34e4 drivers/tty/serial/sirfsoc_uart.o 13352 596 56 14004 36b4 drivers/tty/serial/amba-pl011.o 13444 504 56 14004 36b4 drivers/tty/serial/amba-pl011.o Cross compiled for sparc architecture. 4664 528 32 5224 1468 drivers/tty/serial/sunhv.o 4848 344 32 5224 1468 drivers/tty/serial/sunhv.o 8080 332 28 8440 20f8 drivers/tty/serial/sunzilog.o 8184 228 28 8440 20f8 drivers/tty/serial/sunzilog.o Cross compiled for ia64 architecture. 10226 549 472 11247 2bef drivers/tty/serial/sn_console.o 10414 365 472 11251 2bf3 drivers/tty/serial/sn_console.o The files drivers/tty/serial/zs.o, drivers/tty/serial/lpc32xx_hs.o and drivers/tty/serial/lantiq.o did not compile. Signed-off-by: Bhumika Goyal <bhumirks@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2017-01-25 17:48:52 +00:00
static const struct uart_ops serial_sprd_ops = {
.tx_empty = sprd_tx_empty,
.get_mctrl = sprd_get_mctrl,
.set_mctrl = sprd_set_mctrl,
.stop_tx = sprd_stop_tx,
.start_tx = sprd_start_tx,
.stop_rx = sprd_stop_rx,
.break_ctl = sprd_break_ctl,
.startup = sprd_startup,
.shutdown = sprd_shutdown,
.set_termios = sprd_set_termios,
.type = sprd_type,
.release_port = sprd_release_port,
.request_port = sprd_request_port,
.config_port = sprd_config_port,
.verify_port = sprd_verify_port,
.pm = sprd_pm,
#ifdef CONFIG_CONSOLE_POLL
.poll_init = sprd_poll_init,
.poll_get_char = sprd_poll_get_char,
.poll_put_char = sprd_poll_put_char,
#endif
};
#ifdef CONFIG_SERIAL_SPRD_CONSOLE
static void wait_for_xmitr(struct uart_port *port)
{
unsigned int status, tmout = 10000;
/* wait up to 10ms for the character(s) to be sent */
do {
status = serial_in(port, SPRD_STS1);
if (--tmout == 0)
break;
udelay(1);
} while (status & SPRD_TX_FIFO_CNT_MASK);
}
serial: make uart_console_write->putchar()'s character an unsigned char Currently, uart_console_write->putchar's second parameter (the character) is of type int. It makes little sense, provided uart_console_write() accepts the input string as "const char *s" and passes its content -- the characters -- to putchar(). So switch the character's type to unsigned char. We don't use char as that is signed on some platforms. That would cause troubles for drivers which (implicitly) cast the char to u16 when writing to the device. Sign extension would happen in that case and the value written would be completely different to the provided char. DZ is an example of such a driver -- on MIPS, it uses u16 for dz_out in dz_console_putchar(). Note we do the char -> uchar conversion implicitly in uart_console_write(). Provided we do not change size of the data type, sign extension does not happen there, so the problem is void. This makes the types consistent and unified with the rest of the uart layer, which uses unsigned char in most places already. One exception is xmit_buf, but that is going to be converted later. Cc: Paul Cercueil <paul@crapouillou.net> Cc: Tobias Klauser <tklauser@distanz.ch> Cc: Russell King <linux@armlinux.org.uk> Cc: Vineet Gupta <vgupta@kernel.org> Cc: Nicolas Ferre <nicolas.ferre@microchip.com> Cc: Alexandre Belloni <alexandre.belloni@bootlin.com> Cc: Ludovic Desroches <ludovic.desroches@microchip.com> Cc: Florian Fainelli <f.fainelli@gmail.com> Cc: bcm-kernel-feedback-list@broadcom.com Cc: Alexander Shiyan <shc_work@mail.ru> Cc: Baruch Siach <baruch@tkos.co.il> Cc: "Maciej W. Rozycki" <macro@orcam.me.uk> Cc: Paul Walmsley <paul.walmsley@sifive.com> Cc: Palmer Dabbelt <palmer@dabbelt.com> Cc: Albert Ou <aou@eecs.berkeley.edu> Cc: Shawn Guo <shawnguo@kernel.org> Cc: Sascha Hauer <s.hauer@pengutronix.de> Cc: Pengutronix Kernel Team <kernel@pengutronix.de> Cc: Fabio Estevam <festevam@gmail.com> Cc: NXP Linux Team <linux-imx@nxp.com> Cc: Karol Gugala <kgugala@antmicro.com> Cc: Mateusz Holenko <mholenko@antmicro.com> Cc: Vladimir Zapolskiy <vz@mleia.com> Cc: Neil Armstrong <narmstrong@baylibre.com> 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: Liviu Dudau <liviu.dudau@arm.com> Cc: Sudeep Holla <sudeep.holla@arm.com> Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> Cc: "Andreas Färber" <afaerber@suse.de> Cc: Manivannan Sadhasivam <mani@kernel.org> Cc: Michael Ellerman <mpe@ellerman.id.au> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org> Cc: Paul Mackerras <paulus@samba.org> Cc: Andy Gross <agross@kernel.org> Cc: Bjorn Andersson <bjorn.andersson@linaro.org> Cc: Krzysztof Kozlowski <krzysztof.kozlowski@canonical.com> Cc: Orson Zhai <orsonzhai@gmail.com> Cc: Baolin Wang <baolin.wang7@gmail.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: Peter Korsgaard <peter@korsgaard.com> Cc: Michal Simek <michal.simek@xilinx.com> Acked-by: Richard Genoud <richard.genoud@gmail.com> [atmel_serial] Acked-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de> Acked-by: Paul Cercueil <paul@crapouillou.net> Acked-by: Neil Armstrong <narmstrong@baylibre.com> # meson_serial Signed-off-by: Jiri Slaby <jslaby@suse.cz> Link: https://lore.kernel.org/r/20220303080831.21783-1-jslaby@suse.cz Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2022-03-03 08:08:31 +00:00
static void sprd_console_putchar(struct uart_port *port, unsigned char ch)
{
wait_for_xmitr(port);
serial_out(port, SPRD_TXD, ch);
}
static void sprd_console_write(struct console *co, const char *s,
unsigned int count)
{
struct uart_port *port = &sprd_port[co->index]->port;
int locked = 1;
unsigned long flags;
if (port->sysrq)
locked = 0;
else if (oops_in_progress)
locked = uart_port_trylock_irqsave(port, &flags);
else
uart_port_lock_irqsave(port, &flags);
uart_console_write(port, s, count, sprd_console_putchar);
/* wait for transmitter to become empty */
wait_for_xmitr(port);
if (locked)
uart_port_unlock_irqrestore(port, flags);
}
static int sprd_console_setup(struct console *co, char *options)
{
struct sprd_uart_port *sprd_uart_port;
int baud = 115200;
int bits = 8;
int parity = 'n';
int flow = 'n';
if (co->index >= UART_NR_MAX || co->index < 0)
co->index = 0;
sprd_uart_port = sprd_port[co->index];
if (!sprd_uart_port || !sprd_uart_port->port.membase) {
pr_info("serial port %d not yet initialized\n", co->index);
return -ENODEV;
}
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
return uart_set_options(&sprd_uart_port->port, co, baud,
parity, bits, flow);
}
static struct uart_driver sprd_uart_driver;
static struct console sprd_console = {
.name = SPRD_TTY_NAME,
.write = sprd_console_write,
.device = uart_console_device,
.setup = sprd_console_setup,
.flags = CON_PRINTBUFFER,
.index = -1,
.data = &sprd_uart_driver,
};
static int __init sprd_serial_console_init(void)
{
register_console(&sprd_console);
return 0;
}
console_initcall(sprd_serial_console_init);
#define SPRD_CONSOLE (&sprd_console)
/* Support for earlycon */
serial: make uart_console_write->putchar()'s character an unsigned char Currently, uart_console_write->putchar's second parameter (the character) is of type int. It makes little sense, provided uart_console_write() accepts the input string as "const char *s" and passes its content -- the characters -- to putchar(). So switch the character's type to unsigned char. We don't use char as that is signed on some platforms. That would cause troubles for drivers which (implicitly) cast the char to u16 when writing to the device. Sign extension would happen in that case and the value written would be completely different to the provided char. DZ is an example of such a driver -- on MIPS, it uses u16 for dz_out in dz_console_putchar(). Note we do the char -> uchar conversion implicitly in uart_console_write(). Provided we do not change size of the data type, sign extension does not happen there, so the problem is void. This makes the types consistent and unified with the rest of the uart layer, which uses unsigned char in most places already. One exception is xmit_buf, but that is going to be converted later. Cc: Paul Cercueil <paul@crapouillou.net> Cc: Tobias Klauser <tklauser@distanz.ch> Cc: Russell King <linux@armlinux.org.uk> Cc: Vineet Gupta <vgupta@kernel.org> Cc: Nicolas Ferre <nicolas.ferre@microchip.com> Cc: Alexandre Belloni <alexandre.belloni@bootlin.com> Cc: Ludovic Desroches <ludovic.desroches@microchip.com> Cc: Florian Fainelli <f.fainelli@gmail.com> Cc: bcm-kernel-feedback-list@broadcom.com Cc: Alexander Shiyan <shc_work@mail.ru> Cc: Baruch Siach <baruch@tkos.co.il> Cc: "Maciej W. Rozycki" <macro@orcam.me.uk> Cc: Paul Walmsley <paul.walmsley@sifive.com> Cc: Palmer Dabbelt <palmer@dabbelt.com> Cc: Albert Ou <aou@eecs.berkeley.edu> Cc: Shawn Guo <shawnguo@kernel.org> Cc: Sascha Hauer <s.hauer@pengutronix.de> Cc: Pengutronix Kernel Team <kernel@pengutronix.de> Cc: Fabio Estevam <festevam@gmail.com> Cc: NXP Linux Team <linux-imx@nxp.com> Cc: Karol Gugala <kgugala@antmicro.com> Cc: Mateusz Holenko <mholenko@antmicro.com> Cc: Vladimir Zapolskiy <vz@mleia.com> Cc: Neil Armstrong <narmstrong@baylibre.com> 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: Liviu Dudau <liviu.dudau@arm.com> Cc: Sudeep Holla <sudeep.holla@arm.com> Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> Cc: "Andreas Färber" <afaerber@suse.de> Cc: Manivannan Sadhasivam <mani@kernel.org> Cc: Michael Ellerman <mpe@ellerman.id.au> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org> Cc: Paul Mackerras <paulus@samba.org> Cc: Andy Gross <agross@kernel.org> Cc: Bjorn Andersson <bjorn.andersson@linaro.org> Cc: Krzysztof Kozlowski <krzysztof.kozlowski@canonical.com> Cc: Orson Zhai <orsonzhai@gmail.com> Cc: Baolin Wang <baolin.wang7@gmail.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: Peter Korsgaard <peter@korsgaard.com> Cc: Michal Simek <michal.simek@xilinx.com> Acked-by: Richard Genoud <richard.genoud@gmail.com> [atmel_serial] Acked-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de> Acked-by: Paul Cercueil <paul@crapouillou.net> Acked-by: Neil Armstrong <narmstrong@baylibre.com> # meson_serial Signed-off-by: Jiri Slaby <jslaby@suse.cz> Link: https://lore.kernel.org/r/20220303080831.21783-1-jslaby@suse.cz Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2022-03-03 08:08:31 +00:00
static void sprd_putc(struct uart_port *port, unsigned char c)
{
unsigned int timeout = SPRD_TIMEOUT;
while (timeout-- &&
!(readl(port->membase + SPRD_LSR) & SPRD_LSR_TX_OVER))
cpu_relax();
writeb(c, port->membase + SPRD_TXD);
}
static void sprd_early_write(struct console *con, const char *s, unsigned int n)
{
struct earlycon_device *dev = con->data;
uart_console_write(&dev->port, s, n, sprd_putc);
}
static int __init sprd_early_console_setup(struct earlycon_device *device,
const char *opt)
{
if (!device->port.membase)
return -ENODEV;
device->con->write = sprd_early_write;
return 0;
}
OF_EARLYCON_DECLARE(sprd_serial, "sprd,sc9836-uart",
sprd_early_console_setup);
#else /* !CONFIG_SERIAL_SPRD_CONSOLE */
#define SPRD_CONSOLE NULL
#endif
static struct uart_driver sprd_uart_driver = {
.owner = THIS_MODULE,
.driver_name = "sprd_serial",
.dev_name = SPRD_TTY_NAME,
.major = 0,
.minor = 0,
.nr = UART_NR_MAX,
.cons = SPRD_CONSOLE,
};
static void sprd_remove(struct platform_device *dev)
{
struct sprd_uart_port *sup = platform_get_drvdata(dev);
if (sup) {
uart_remove_one_port(&sprd_uart_driver, &sup->port);
sprd_port[sup->port.line] = NULL;
sprd_rx_free_buf(sup);
sprd_ports_num--;
}
if (!sprd_ports_num)
uart_unregister_driver(&sprd_uart_driver);
}
static bool sprd_uart_is_console(struct uart_port *uport)
{
struct console *cons = sprd_uart_driver.cons;
if ((cons && cons->index >= 0 && cons->index == uport->line) ||
of_console_check(uport->dev->of_node, SPRD_TTY_NAME, uport->line))
return true;
return false;
}
static int sprd_clk_init(struct uart_port *uport)
{
struct clk *clk_uart, *clk_parent;
struct sprd_uart_port *u = container_of(uport, struct sprd_uart_port, port);
clk_uart = devm_clk_get(uport->dev, "uart");
if (IS_ERR(clk_uart)) {
dev_warn(uport->dev, "uart%d can't get uart clock\n",
uport->line);
clk_uart = NULL;
}
clk_parent = devm_clk_get(uport->dev, "source");
if (IS_ERR(clk_parent)) {
dev_warn(uport->dev, "uart%d can't get source clock\n",
uport->line);
clk_parent = NULL;
}
if (!clk_uart || clk_set_parent(clk_uart, clk_parent))
uport->uartclk = SPRD_DEFAULT_SOURCE_CLK;
else
uport->uartclk = clk_get_rate(clk_uart);
u->clk = devm_clk_get(uport->dev, "enable");
if (IS_ERR(u->clk)) {
if (PTR_ERR(u->clk) == -EPROBE_DEFER)
return -EPROBE_DEFER;
dev_warn(uport->dev, "uart%d can't get enable clock\n",
uport->line);
/* To keep console alive even if the error occurred */
if (!sprd_uart_is_console(uport))
return PTR_ERR(u->clk);
u->clk = NULL;
}
return 0;
}
static int sprd_probe(struct platform_device *pdev)
{
struct resource *res;
struct uart_port *up;
struct sprd_uart_port *sport;
int irq;
int index;
int ret;
index = of_alias_get_id(pdev->dev.of_node, "serial");
if (index < 0 || index >= UART_NR_MAX) {
dev_err(&pdev->dev, "got a wrong serial alias id %d\n", index);
return -EINVAL;
}
sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL);
if (!sport)
return -ENOMEM;
up = &sport->port;
up->dev = &pdev->dev;
up->line = index;
up->type = PORT_SPRD;
up->iotype = UPIO_MEM;
up->uartclk = SPRD_DEF_RATE;
up->fifosize = SPRD_FIFO_SIZE;
up->ops = &serial_sprd_ops;
up->flags = UPF_BOOT_AUTOCONF;
up->has_sysrq = IS_ENABLED(CONFIG_SERIAL_SPRD_CONSOLE);
ret = sprd_clk_init(up);
if (ret)
return ret;
up->membase = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(up->membase))
return PTR_ERR(up->membase);
up->mapbase = res->start;
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
up->irq = irq;
/*
* Allocate one dma buffer to prepare for receive transfer, in case
* memory allocation failure at runtime.
*/
ret = sprd_rx_alloc_buf(sport);
if (ret)
return ret;
if (!sprd_ports_num) {
ret = uart_register_driver(&sprd_uart_driver);
if (ret < 0) {
pr_err("Failed to register SPRD-UART driver\n");
goto free_rx_buf;
}
}
sprd_ports_num++;
sprd_port[index] = sport;
ret = uart_add_one_port(&sprd_uart_driver, up);
if (ret)
goto clean_port;
platform_set_drvdata(pdev, up);
return 0;
clean_port:
sprd_port[index] = NULL;
if (--sprd_ports_num == 0)
uart_unregister_driver(&sprd_uart_driver);
free_rx_buf:
sprd_rx_free_buf(sport);
return ret;
}
#ifdef CONFIG_PM_SLEEP
static int sprd_suspend(struct device *dev)
{
struct sprd_uart_port *sup = dev_get_drvdata(dev);
uart_suspend_port(&sprd_uart_driver, &sup->port);
return 0;
}
static int sprd_resume(struct device *dev)
{
struct sprd_uart_port *sup = dev_get_drvdata(dev);
uart_resume_port(&sprd_uart_driver, &sup->port);
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(sprd_pm_ops, sprd_suspend, sprd_resume);
static const struct of_device_id serial_ids[] = {
{.compatible = "sprd,sc9836-uart",},
{}
};
MODULE_DEVICE_TABLE(of, serial_ids);
static struct platform_driver sprd_platform_driver = {
.probe = sprd_probe,
.remove_new = sprd_remove,
.driver = {
.name = "sprd_serial",
.of_match_table = serial_ids,
.pm = &sprd_pm_ops,
},
};
module_platform_driver(sprd_platform_driver);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Spreadtrum SoC serial driver series");