linux/drivers/tty/serial/imx.c

2860 lines
78 KiB
C
Raw Permalink 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 18:11:51 +01:00
// SPDX-License-Identifier: GPL-2.0+
/*
* Driver for Motorola/Freescale IMX serial ports
*
* Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
*
* Author: Sascha Hauer <sascha@saschahauer.de>
* Copyright (C) 2004 Pengutronix
*/
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 08:08:23 +02:00
#include <linux/circ_buf.h>
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/console.h>
#include <linux/sysrq.h>
#include <linux/platform_device.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/serial_core.h>
#include <linux/serial.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/ktime.h>
#include <linux/pinctrl/consumer.h>
#include <linux/rational.h>
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h percpu.h is included by sched.h and module.h and thus ends up being included when building most .c files. percpu.h includes slab.h which in turn includes gfp.h making everything defined by the two files universally available and complicating inclusion dependencies. percpu.h -> slab.h dependency is about to be removed. Prepare for this change by updating users of gfp and slab facilities include those headers directly instead of assuming availability. As this conversion needs to touch large number of source files, the following script is used as the basis of conversion. http://userweb.kernel.org/~tj/misc/slabh-sweep.py The script does the followings. * Scan files for gfp and slab usages and update includes such that only the necessary includes are there. ie. if only gfp is used, gfp.h, if slab is used, slab.h. * When the script inserts a new include, it looks at the include blocks and try to put the new include such that its order conforms to its surrounding. It's put in the include block which contains core kernel includes, in the same order that the rest are ordered - alphabetical, Christmas tree, rev-Xmas-tree or at the end if there doesn't seem to be any matching order. * If the script can't find a place to put a new include (mostly because the file doesn't have fitting include block), it prints out an error message indicating which .h file needs to be added to the file. The conversion was done in the following steps. 1. The initial automatic conversion of all .c files updated slightly over 4000 files, deleting around 700 includes and adding ~480 gfp.h and ~3000 slab.h inclusions. The script emitted errors for ~400 files. 2. Each error was manually checked. Some didn't need the inclusion, some needed manual addition while adding it to implementation .h or embedding .c file was more appropriate for others. This step added inclusions to around 150 files. 3. The script was run again and the output was compared to the edits from #2 to make sure no file was left behind. 4. Several build tests were done and a couple of problems were fixed. e.g. lib/decompress_*.c used malloc/free() wrappers around slab APIs requiring slab.h to be added manually. 5. The script was run on all .h files but without automatically editing them as sprinkling gfp.h and slab.h inclusions around .h files could easily lead to inclusion dependency hell. Most gfp.h inclusion directives were ignored as stuff from gfp.h was usually wildly available and often used in preprocessor macros. Each slab.h inclusion directive was examined and added manually as necessary. 6. percpu.h was updated not to include slab.h. 7. Build test were done on the following configurations and failures were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my distributed build env didn't work with gcov compiles) and a few more options had to be turned off depending on archs to make things build (like ipr on powerpc/64 which failed due to missing writeq). * x86 and x86_64 UP and SMP allmodconfig and a custom test config. * powerpc and powerpc64 SMP allmodconfig * sparc and sparc64 SMP allmodconfig * ia64 SMP allmodconfig * s390 SMP allmodconfig * alpha SMP allmodconfig * um on x86_64 SMP allmodconfig 8. percpu.h modifications were reverted so that it could be applied as a separate patch and serve as bisection point. Given the fact that I had only a couple of failures from tests on step 6, I'm fairly confident about the coverage of this conversion patch. If there is a breakage, it's likely to be something in one of the arch headers which should be easily discoverable easily on most builds of the specific arch. Signed-off-by: Tejun Heo <tj@kernel.org> Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org> Cc: Ingo Molnar <mingo@redhat.com> Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 17:04:11 +09:00
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/dma-mapping.h>
#include <asm/irq.h>
#include <linux/dma/imx-dma.h>
#include "serial_mctrl_gpio.h"
/* Register definitions */
#define URXD0 0x0 /* Receiver Register */
#define URTX0 0x40 /* Transmitter Register */
#define UCR1 0x80 /* Control Register 1 */
#define UCR2 0x84 /* Control Register 2 */
#define UCR3 0x88 /* Control Register 3 */
#define UCR4 0x8c /* Control Register 4 */
#define UFCR 0x90 /* FIFO Control Register */
#define USR1 0x94 /* Status Register 1 */
#define USR2 0x98 /* Status Register 2 */
#define UESC 0x9c /* Escape Character Register */
#define UTIM 0xa0 /* Escape Timer Register */
#define UBIR 0xa4 /* BRM Incremental Register */
#define UBMR 0xa8 /* BRM Modulator Register */
#define UBRC 0xac /* Baud Rate Count Register */
#define IMX21_ONEMS 0xb0 /* One Millisecond register */
#define IMX1_UTS 0xd0 /* UART Test Register on i.mx1 */
#define IMX21_UTS 0xb4 /* UART Test Register on all other i.mx*/
/* UART Control Register Bit Fields.*/
#define URXD_DUMMY_READ (1<<16)
#define URXD_CHARRDY (1<<15)
#define URXD_ERR (1<<14)
#define URXD_OVRRUN (1<<13)
#define URXD_FRMERR (1<<12)
#define URXD_BRK (1<<11)
#define URXD_PRERR (1<<10)
#define URXD_RX_DATA (0xFF<<0)
#define UCR1_ADEN (1<<15) /* Auto detect interrupt */
#define UCR1_ADBR (1<<14) /* Auto detect baud rate */
#define UCR1_TRDYEN (1<<13) /* Transmitter ready interrupt enable */
#define UCR1_IDEN (1<<12) /* Idle condition interrupt */
#define UCR1_ICD_REG(x) (((x) & 3) << 10) /* idle condition detect */
#define UCR1_RRDYEN (1<<9) /* Recv ready interrupt enable */
#define UCR1_RXDMAEN (1<<8) /* Recv ready DMA enable */
#define UCR1_IREN (1<<7) /* Infrared interface enable */
#define UCR1_TXMPTYEN (1<<6) /* Transimitter empty interrupt enable */
#define UCR1_RTSDEN (1<<5) /* RTS delta interrupt enable */
#define UCR1_SNDBRK (1<<4) /* Send break */
#define UCR1_TXDMAEN (1<<3) /* Transmitter ready DMA enable */
#define IMX1_UCR1_UARTCLKEN (1<<2) /* UART clock enabled, i.mx1 only */
#define UCR1_ATDMAEN (1<<2) /* Aging DMA Timer Enable */
#define UCR1_DOZE (1<<1) /* Doze */
#define UCR1_UARTEN (1<<0) /* UART enabled */
#define UCR2_ESCI (1<<15) /* Escape seq interrupt enable */
#define UCR2_IRTS (1<<14) /* Ignore RTS pin */
#define UCR2_CTSC (1<<13) /* CTS pin control */
#define UCR2_CTS (1<<12) /* Clear to send */
#define UCR2_ESCEN (1<<11) /* Escape enable */
#define UCR2_PREN (1<<8) /* Parity enable */
#define UCR2_PROE (1<<7) /* Parity odd/even */
#define UCR2_STPB (1<<6) /* Stop */
#define UCR2_WS (1<<5) /* Word size */
#define UCR2_RTSEN (1<<4) /* Request to send interrupt enable */
#define UCR2_ATEN (1<<3) /* Aging Timer Enable */
#define UCR2_TXEN (1<<2) /* Transmitter enabled */
#define UCR2_RXEN (1<<1) /* Receiver enabled */
#define UCR2_SRST (1<<0) /* SW reset */
#define UCR3_DTREN (1<<13) /* DTR interrupt enable */
#define UCR3_PARERREN (1<<12) /* Parity enable */
#define UCR3_FRAERREN (1<<11) /* Frame error interrupt enable */
#define UCR3_DSR (1<<10) /* Data set ready */
#define UCR3_DCD (1<<9) /* Data carrier detect */
#define UCR3_RI (1<<8) /* Ring indicator */
#define UCR3_ADNIMP (1<<7) /* Autobaud Detection Not Improved */
#define UCR3_RXDSEN (1<<6) /* Receive status interrupt enable */
#define UCR3_AIRINTEN (1<<5) /* Async IR wake interrupt enable */
#define UCR3_AWAKEN (1<<4) /* Async wake interrupt enable */
#define UCR3_DTRDEN (1<<3) /* Data Terminal Ready Delta Enable. */
#define IMX21_UCR3_RXDMUXSEL (1<<2) /* RXD Muxed Input Select */
#define UCR3_INVT (1<<1) /* Inverted Infrared transmission */
#define UCR3_BPEN (1<<0) /* Preset registers enable */
#define UCR4_CTSTL_SHF 10 /* CTS trigger level shift */
#define UCR4_CTSTL_MASK 0x3F /* CTS trigger is 6 bits wide */
#define UCR4_INVR (1<<9) /* Inverted infrared reception */
#define UCR4_ENIRI (1<<8) /* Serial infrared interrupt enable */
#define UCR4_WKEN (1<<7) /* Wake interrupt enable */
#define UCR4_REF16 (1<<6) /* Ref freq 16 MHz */
#define UCR4_IDDMAEN (1<<6) /* DMA IDLE Condition Detected */
#define UCR4_IRSC (1<<5) /* IR special case */
#define UCR4_TCEN (1<<3) /* Transmit complete interrupt enable */
#define UCR4_BKEN (1<<2) /* Break condition interrupt enable */
#define UCR4_OREN (1<<1) /* Receiver overrun interrupt enable */
#define UCR4_DREN (1<<0) /* Recv data ready interrupt enable */
#define UFCR_RXTL_SHF 0 /* Receiver trigger level shift */
#define UFCR_RXTL_MASK 0x3F /* Receiver trigger 6 bits wide */
#define UFCR_DCEDTE (1<<6) /* DCE/DTE mode select */
#define UFCR_RFDIV (7<<7) /* Reference freq divider mask */
#define UFCR_RFDIV_REG(x) (((x) < 7 ? 6 - (x) : 6) << 7)
#define UFCR_TXTL_SHF 10 /* Transmitter trigger level shift */
#define USR1_PARITYERR (1<<15) /* Parity error interrupt flag */
#define USR1_RTSS (1<<14) /* RTS pin status */
#define USR1_TRDY (1<<13) /* Transmitter ready interrupt/dma flag */
#define USR1_RTSD (1<<12) /* RTS delta */
#define USR1_ESCF (1<<11) /* Escape seq interrupt flag */
#define USR1_FRAMERR (1<<10) /* Frame error interrupt flag */
#define USR1_RRDY (1<<9) /* Receiver ready interrupt/dma flag */
#define USR1_AGTIM (1<<8) /* Ageing timer interrupt flag */
#define USR1_DTRD (1<<7) /* DTR Delta */
#define USR1_RXDS (1<<6) /* Receiver idle interrupt flag */
#define USR1_AIRINT (1<<5) /* Async IR wake interrupt flag */
#define USR1_AWAKE (1<<4) /* Aysnc wake interrupt flag */
#define USR2_ADET (1<<15) /* Auto baud rate detect complete */
#define USR2_TXFE (1<<14) /* Transmit buffer FIFO empty */
#define USR2_DTRF (1<<13) /* DTR edge interrupt flag */
#define USR2_IDLE (1<<12) /* Idle condition */
#define USR2_RIDELT (1<<10) /* Ring Interrupt Delta */
#define USR2_RIIN (1<<9) /* Ring Indicator Input */
#define USR2_IRINT (1<<8) /* Serial infrared interrupt flag */
#define USR2_WAKE (1<<7) /* Wake */
#define USR2_DCDIN (1<<5) /* Data Carrier Detect Input */
#define USR2_RTSF (1<<4) /* RTS edge interrupt flag */
#define USR2_TXDC (1<<3) /* Transmitter complete */
#define USR2_BRCD (1<<2) /* Break condition */
#define USR2_ORE (1<<1) /* Overrun error */
#define USR2_RDR (1<<0) /* Recv data ready */
#define UTS_FRCPERR (1<<13) /* Force parity error */
#define UTS_LOOP (1<<12) /* Loop tx and rx */
#define UTS_TXEMPTY (1<<6) /* TxFIFO empty */
#define UTS_RXEMPTY (1<<5) /* RxFIFO empty */
#define UTS_TXFULL (1<<4) /* TxFIFO full */
#define UTS_RXFULL (1<<3) /* RxFIFO full */
#define UTS_SOFTRST (1<<0) /* Software reset */
/* We've been assigned a range on the "Low-density serial ports" major */
#define SERIAL_IMX_MAJOR 207
#define MINOR_START 16
#define DEV_NAME "ttymxc"
/*
* This determines how often we check the modem status signals
* for any change. They generally aren't connected to an IRQ
* so we have to poll them. We also check immediately before
* filling the TX fifo incase CTS has been dropped.
*/
#define MCTRL_TIMEOUT (250*HZ/1000)
#define DRIVER_NAME "IMX-uart"
#define UART_NR 8
/* i.MX21 type uart runs on all i.mx except i.MX1 and i.MX6q */
enum imx_uart_type {
IMX1_UART,
IMX21_UART,
};
/* device type dependent stuff */
struct imx_uart_data {
unsigned uts_reg;
enum imx_uart_type devtype;
};
enum imx_tx_state {
OFF,
WAIT_AFTER_RTS,
SEND,
WAIT_AFTER_SEND,
};
struct imx_port {
struct uart_port port;
struct timer_list timer;
unsigned int old_status;
unsigned int have_rtscts:1;
unsigned int have_rtsgpio:1;
unsigned int dte_mode:1;
unsigned int inverted_tx:1;
unsigned int inverted_rx:1;
struct clk *clk_ipg;
struct clk *clk_per;
const struct imx_uart_data *devdata;
struct mctrl_gpios *gpios;
/* counter to stop 0xff flood */
int idle_counter;
/* DMA fields */
unsigned int dma_is_enabled:1;
unsigned int dma_is_rxing:1;
unsigned int dma_is_txing:1;
struct dma_chan *dma_chan_rx, *dma_chan_tx;
struct scatterlist rx_sgl, tx_sgl[2];
void *rx_buf;
serial: imx-serial - update UART IMX driver to use cyclic DMA The IMX UART has a 32 bytes HW buffer which can be filled up in 2777us at 115200 baud or 80us at 4Mbaud (supported by IMX53). Taking this in consideration there is a good probability to lose data because of the DMA startup latency. Our tests (explained below) indicates a latency up to 4400us when creating interrupt load and ~70us without. When creating interrupt load I was able to see continuous overrun errors by checking serial driver statistics using the command: `cat /proc/tty/driver/IMX-uart`. Replace manual restart of DMA with cyclic DMA to eliminate data loss due to DMA engine startup latency (similar approch to atmel_serial.c driver). As result the DMA engine will start on the first serial data transfer and stops only when serial port is closed. Tests environment: Using the m53evk board I have used a GPIO for profiling the IMX serial driver. - The RX line and GPIO were connected to oscilloscope. - Run a small test program on the m53evk board that will only open and read data from ttymxc2 port. - Connect the ttymxc2 port to my laptop using a USB serial converter where another test program is running, able to send configurable packet lengths and intervals. - Serial ports configured at 115200 8N1. - Interrupts load created by disconnecting/connecting (3s interval) a USB hub, using a digital switch, with 4 USB devices (USB-Serial converter, USB SD card, etc) connected. (around 160 interrupts/second generated) - The GPIO was toggled HI in the `imx_int` when USR1_RRDY or USR1_AGTIM events are received and toggled back, once the DMA configuration is finalized, at the end of `imx_dma_rxint`. Measurements: The measurements were done from the end of the last byte (RX line) until the GPIO was toggled back LOW. Note: The GPIO toggling was done using `gpiod_set_value` method. Tests performed: 1. Sending 9 bytes packets at 8ms interval. Having the 9 bytes packets will activate the RRDY threshold event and IMX serial interrupt called. Results: - DMA start latency (interrupt start latency + DMA configuration) consistently 70us when system not loaded. - DMA start latency up to 4400us when system loaded. 2. Sending 40 bytes packet at 8mS interval. Results with load: - Able to observe overruns by running: `watch -n1 cat /proc/tty/driver/IMX-uart` Tested-by: Peter Senna Tschudin <peter.senna@collabora.com> Acked-by: Peter Senna Tschudin <peter.senna@collabora.com> Signed-off-by: Nandor Han <nandor.han@ge.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2016-08-08 15:38:27 +03:00
struct circ_buf rx_ring;
unsigned int rx_buf_size;
unsigned int rx_period_length;
serial: imx-serial - update UART IMX driver to use cyclic DMA The IMX UART has a 32 bytes HW buffer which can be filled up in 2777us at 115200 baud or 80us at 4Mbaud (supported by IMX53). Taking this in consideration there is a good probability to lose data because of the DMA startup latency. Our tests (explained below) indicates a latency up to 4400us when creating interrupt load and ~70us without. When creating interrupt load I was able to see continuous overrun errors by checking serial driver statistics using the command: `cat /proc/tty/driver/IMX-uart`. Replace manual restart of DMA with cyclic DMA to eliminate data loss due to DMA engine startup latency (similar approch to atmel_serial.c driver). As result the DMA engine will start on the first serial data transfer and stops only when serial port is closed. Tests environment: Using the m53evk board I have used a GPIO for profiling the IMX serial driver. - The RX line and GPIO were connected to oscilloscope. - Run a small test program on the m53evk board that will only open and read data from ttymxc2 port. - Connect the ttymxc2 port to my laptop using a USB serial converter where another test program is running, able to send configurable packet lengths and intervals. - Serial ports configured at 115200 8N1. - Interrupts load created by disconnecting/connecting (3s interval) a USB hub, using a digital switch, with 4 USB devices (USB-Serial converter, USB SD card, etc) connected. (around 160 interrupts/second generated) - The GPIO was toggled HI in the `imx_int` when USR1_RRDY or USR1_AGTIM events are received and toggled back, once the DMA configuration is finalized, at the end of `imx_dma_rxint`. Measurements: The measurements were done from the end of the last byte (RX line) until the GPIO was toggled back LOW. Note: The GPIO toggling was done using `gpiod_set_value` method. Tests performed: 1. Sending 9 bytes packets at 8ms interval. Having the 9 bytes packets will activate the RRDY threshold event and IMX serial interrupt called. Results: - DMA start latency (interrupt start latency + DMA configuration) consistently 70us when system not loaded. - DMA start latency up to 4400us when system loaded. 2. Sending 40 bytes packet at 8mS interval. Results with load: - Able to observe overruns by running: `watch -n1 cat /proc/tty/driver/IMX-uart` Tested-by: Peter Senna Tschudin <peter.senna@collabora.com> Acked-by: Peter Senna Tschudin <peter.senna@collabora.com> Signed-off-by: Nandor Han <nandor.han@ge.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2016-08-08 15:38:27 +03:00
unsigned int rx_periods;
dma_cookie_t rx_cookie;
unsigned int tx_bytes;
unsigned int dma_tx_nents;
unsigned int saved_reg[10];
bool context_saved;
bool last_putchar_was_newline;
enum imx_tx_state tx_state;
struct hrtimer trigger_start_tx;
struct hrtimer trigger_stop_tx;
unsigned int rxtl;
};
struct imx_port_ucrs {
unsigned int ucr1;
unsigned int ucr2;
unsigned int ucr3;
};
static const struct imx_uart_data imx_uart_imx1_devdata = {
.uts_reg = IMX1_UTS,
.devtype = IMX1_UART,
};
static const struct imx_uart_data imx_uart_imx21_devdata = {
.uts_reg = IMX21_UTS,
.devtype = IMX21_UART,
};
static const struct of_device_id imx_uart_dt_ids[] = {
/*
* For reasons unknown to me, some UART devices (e.g. imx6ul's) are
* compatible to fsl,imx6q-uart, but not fsl,imx21-uart, while the
* original imx6q's UART is compatible to fsl,imx21-uart. This driver
* doesn't make any distinction between these two variants.
*/
{ .compatible = "fsl,imx6q-uart", .data = &imx_uart_imx21_devdata, },
{ .compatible = "fsl,imx1-uart", .data = &imx_uart_imx1_devdata, },
{ .compatible = "fsl,imx21-uart", .data = &imx_uart_imx21_devdata, },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, imx_uart_dt_ids);
static inline struct imx_port *to_imx_port(struct uart_port *port)
{
return container_of(port, struct imx_port, port);
}
static inline void imx_uart_writel(struct imx_port *sport, u32 val, u32 offset)
{
writel(val, sport->port.membase + offset);
}
static inline u32 imx_uart_readl(struct imx_port *sport, u32 offset)
{
return readl(sport->port.membase + offset);
}
static inline unsigned imx_uart_uts_reg(struct imx_port *sport)
{
return sport->devdata->uts_reg;
}
static inline int imx_uart_is_imx1(struct imx_port *sport)
{
return sport->devdata->devtype == IMX1_UART;
}
/*
* Save and restore functions for UCR1, UCR2 and UCR3 registers
*/
#if IS_ENABLED(CONFIG_SERIAL_IMX_CONSOLE)
static void imx_uart_ucrs_save(struct imx_port *sport,
struct imx_port_ucrs *ucr)
{
/* save control registers */
ucr->ucr1 = imx_uart_readl(sport, UCR1);
ucr->ucr2 = imx_uart_readl(sport, UCR2);
ucr->ucr3 = imx_uart_readl(sport, UCR3);
}
static void imx_uart_ucrs_restore(struct imx_port *sport,
struct imx_port_ucrs *ucr)
{
/* restore control registers */
imx_uart_writel(sport, ucr->ucr1, UCR1);
imx_uart_writel(sport, ucr->ucr2, UCR2);
imx_uart_writel(sport, ucr->ucr3, UCR3);
}
#endif
/* called with port.lock taken and irqs caller dependent */
static void imx_uart_rts_active(struct imx_port *sport, u32 *ucr2)
{
*ucr2 &= ~(UCR2_CTSC | UCR2_CTS);
serial: Deassert Transmit Enable on probe in driver-specific way When a UART port is newly registered, uart_configure_port() seeks to deassert RS485 Transmit Enable by setting the RTS bit in port->mctrl. However a number of UART drivers interpret a set RTS bit as *assertion* instead of deassertion: Affected drivers include those using serial8250_em485_config() (except 8250_bcm2835aux.c) and some using mctrl_gpio (e.g. imx.c). Since the interpretation of the RTS bit is driver-specific, it is not suitable as a means to centrally deassert Transmit Enable in the serial core. Instead, the serial core must call on drivers to deassert it in their driver-specific way. One way to achieve that is to call ->rs485_config(). It implicitly deasserts Transmit Enable. So amend uart_configure_port() and uart_resume_port() to invoke uart_rs485_config(). That allows removing calls to uart_rs485_config() from drivers' ->probe() hooks and declaring the function static. Skip any invocation of ->set_mctrl() if RS485 is enabled. RS485 has no hardware flow control, so the modem control lines are irrelevant and need not be touched. When leaving RS485 mode, reset the modem control lines to the state stored in port->mctrl. That way, UARTs which are muxed between RS485 and RS232 transceivers drive the lines correctly when switched to RS232. (serial8250_do_startup() historically raises the OUT1 modem signal because otherwise interrupts are not signaled on ancient PC UARTs, but I believe that no longer applies to modern, RS485-capable UARTs and is thus safe to be skipped.) imx.c modifies port->mctrl whenever Transmit Enable is asserted and deasserted. Stop it from doing that so port->mctrl reflects the RS232 line state. 8250_omap.c deasserts Transmit Enable on ->runtime_resume() by calling ->set_mctrl(). Because that is now a no-op in RS485 mode, amend the function to call serial8250_em485_stop_tx(). fsl_lpuart.c retrieves and applies the RS485 device tree properties after registering the UART port. Because applying now happens on registration in uart_configure_port(), move retrieval of the properties ahead of uart_add_one_port(). Link: https://lore.kernel.org/all/20220329085050.311408-1-matthias.schiffer@ew.tq-group.com/ Link: https://lore.kernel.org/all/8f538a8903795f22f9acc94a9a31b03c9c4ccacb.camel@ginzinger.com/ Fixes: d3b3404df318 ("serial: Fix incorrect rs485 polarity on uart open") Cc: stable@vger.kernel.org # v4.14+ Reported-by: Matthias Schiffer <matthias.schiffer@ew.tq-group.com> Reported-by: Roosen Henri <Henri.Roosen@ginzinger.com> Tested-by: Matthias Schiffer <matthias.schiffer@ew.tq-group.com> Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com> Signed-off-by: Lukas Wunner <lukas@wunner.de> Link: https://lore.kernel.org/r/2de36eba3fbe11278d5002e4e501afe0ceaca039.1663863805.git.lukas@wunner.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2022-09-22 18:27:33 +02:00
mctrl_gpio_set(sport->gpios, sport->port.mctrl | TIOCM_RTS);
}
/* called with port.lock taken and irqs caller dependent */
static void imx_uart_rts_inactive(struct imx_port *sport, u32 *ucr2)
{
*ucr2 &= ~UCR2_CTSC;
*ucr2 |= UCR2_CTS;
serial: Deassert Transmit Enable on probe in driver-specific way When a UART port is newly registered, uart_configure_port() seeks to deassert RS485 Transmit Enable by setting the RTS bit in port->mctrl. However a number of UART drivers interpret a set RTS bit as *assertion* instead of deassertion: Affected drivers include those using serial8250_em485_config() (except 8250_bcm2835aux.c) and some using mctrl_gpio (e.g. imx.c). Since the interpretation of the RTS bit is driver-specific, it is not suitable as a means to centrally deassert Transmit Enable in the serial core. Instead, the serial core must call on drivers to deassert it in their driver-specific way. One way to achieve that is to call ->rs485_config(). It implicitly deasserts Transmit Enable. So amend uart_configure_port() and uart_resume_port() to invoke uart_rs485_config(). That allows removing calls to uart_rs485_config() from drivers' ->probe() hooks and declaring the function static. Skip any invocation of ->set_mctrl() if RS485 is enabled. RS485 has no hardware flow control, so the modem control lines are irrelevant and need not be touched. When leaving RS485 mode, reset the modem control lines to the state stored in port->mctrl. That way, UARTs which are muxed between RS485 and RS232 transceivers drive the lines correctly when switched to RS232. (serial8250_do_startup() historically raises the OUT1 modem signal because otherwise interrupts are not signaled on ancient PC UARTs, but I believe that no longer applies to modern, RS485-capable UARTs and is thus safe to be skipped.) imx.c modifies port->mctrl whenever Transmit Enable is asserted and deasserted. Stop it from doing that so port->mctrl reflects the RS232 line state. 8250_omap.c deasserts Transmit Enable on ->runtime_resume() by calling ->set_mctrl(). Because that is now a no-op in RS485 mode, amend the function to call serial8250_em485_stop_tx(). fsl_lpuart.c retrieves and applies the RS485 device tree properties after registering the UART port. Because applying now happens on registration in uart_configure_port(), move retrieval of the properties ahead of uart_add_one_port(). Link: https://lore.kernel.org/all/20220329085050.311408-1-matthias.schiffer@ew.tq-group.com/ Link: https://lore.kernel.org/all/8f538a8903795f22f9acc94a9a31b03c9c4ccacb.camel@ginzinger.com/ Fixes: d3b3404df318 ("serial: Fix incorrect rs485 polarity on uart open") Cc: stable@vger.kernel.org # v4.14+ Reported-by: Matthias Schiffer <matthias.schiffer@ew.tq-group.com> Reported-by: Roosen Henri <Henri.Roosen@ginzinger.com> Tested-by: Matthias Schiffer <matthias.schiffer@ew.tq-group.com> Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com> Signed-off-by: Lukas Wunner <lukas@wunner.de> Link: https://lore.kernel.org/r/2de36eba3fbe11278d5002e4e501afe0ceaca039.1663863805.git.lukas@wunner.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2022-09-22 18:27:33 +02:00
mctrl_gpio_set(sport->gpios, sport->port.mctrl & ~TIOCM_RTS);
}
static void start_hrtimer_ms(struct hrtimer *hrt, unsigned long msec)
{
hrtimer_start(hrt, ms_to_ktime(msec), HRTIMER_MODE_REL);
}
/* called with port.lock taken and irqs off */
static void imx_uart_soft_reset(struct imx_port *sport)
{
int i = 10;
u32 ucr2, ubir, ubmr, uts;
/*
* According to the Reference Manual description of the UART SRST bit:
*
* "Reset the transmit and receive state machines,
* all FIFOs and register USR1, USR2, UBIR, UBMR, UBRC, URXD, UTXD
* and UTS[6-3]".
*
* We don't need to restore the old values from USR1, USR2, URXD and
* UTXD. UBRC is read only, so only save/restore the other three
* registers.
*/
ubir = imx_uart_readl(sport, UBIR);
ubmr = imx_uart_readl(sport, UBMR);
uts = imx_uart_readl(sport, IMX21_UTS);
ucr2 = imx_uart_readl(sport, UCR2);
imx_uart_writel(sport, ucr2 & ~UCR2_SRST, UCR2);
while (!(imx_uart_readl(sport, UCR2) & UCR2_SRST) && (--i > 0))
udelay(1);
/* Restore the registers */
imx_uart_writel(sport, ubir, UBIR);
imx_uart_writel(sport, ubmr, UBMR);
imx_uart_writel(sport, uts, IMX21_UTS);
sport->idle_counter = 0;
}
/* called with port.lock taken and irqs off */
static void imx_uart_disable_loopback_rs485(struct imx_port *sport)
{
unsigned int uts;
/* See SER_RS485_ENABLED/UTS_LOOP comment in imx_uart_probe() */
uts = imx_uart_readl(sport, imx_uart_uts_reg(sport));
uts &= ~UTS_LOOP;
imx_uart_writel(sport, uts, imx_uart_uts_reg(sport));
}
/* called with port.lock taken and irqs off */
static void imx_uart_start_rx(struct uart_port *port)
{
struct imx_port *sport = to_imx_port(port);
unsigned int ucr1, ucr2;
ucr1 = imx_uart_readl(sport, UCR1);
ucr2 = imx_uart_readl(sport, UCR2);
ucr2 |= UCR2_RXEN;
if (sport->dma_is_enabled) {
ucr1 |= UCR1_RXDMAEN | UCR1_ATDMAEN;
} else {
ucr1 |= UCR1_RRDYEN;
ucr2 |= UCR2_ATEN;
}
/* Write UCR2 first as it includes RXEN */
imx_uart_writel(sport, ucr2, UCR2);
imx_uart_writel(sport, ucr1, UCR1);
imx_uart_disable_loopback_rs485(sport);
}
/* called with port.lock taken and irqs off */
static void imx_uart_stop_tx(struct uart_port *port)
{
struct imx_port *sport = to_imx_port(port);
u32 ucr1, ucr4, usr2;
if (sport->tx_state == OFF)
return;
/*
* We are maybe in the SMP context, so if the DMA TX thread is running
* on other cpu, we have to wait for it to finish.
*/
if (sport->dma_is_txing)
return;
ucr1 = imx_uart_readl(sport, UCR1);
imx_uart_writel(sport, ucr1 & ~UCR1_TRDYEN, UCR1);
ucr4 = imx_uart_readl(sport, UCR4);
usr2 = imx_uart_readl(sport, USR2);
if ((!(usr2 & USR2_TXDC)) && (ucr4 & UCR4_TCEN)) {
/* The shifter is still busy, so retry once TC triggers */
return;
}
ucr4 &= ~UCR4_TCEN;
imx_uart_writel(sport, ucr4, UCR4);
/* in rs485 mode disable transmitter */
if (port->rs485.flags & SER_RS485_ENABLED) {
if (sport->tx_state == SEND) {
sport->tx_state = WAIT_AFTER_SEND;
if (port->rs485.delay_rts_after_send > 0) {
start_hrtimer_ms(&sport->trigger_stop_tx,
port->rs485.delay_rts_after_send);
return;
}
/* continue without any delay */
}
if (sport->tx_state == WAIT_AFTER_RTS ||
sport->tx_state == WAIT_AFTER_SEND) {
u32 ucr2;
hrtimer_try_to_cancel(&sport->trigger_start_tx);
ucr2 = imx_uart_readl(sport, UCR2);
if (port->rs485.flags & SER_RS485_RTS_AFTER_SEND)
imx_uart_rts_active(sport, &ucr2);
else
imx_uart_rts_inactive(sport, &ucr2);
imx_uart_writel(sport, ucr2, UCR2);
if (!port->rs485_rx_during_tx_gpio)
imx_uart_start_rx(port);
sport->tx_state = OFF;
}
} else {
sport->tx_state = OFF;
}
}
/* called with port.lock taken and irqs off */
static void imx_uart_stop_rx_with_loopback_ctrl(struct uart_port *port, bool loopback)
{
struct imx_port *sport = to_imx_port(port);
tty: serial: imx: Handle RS485 DE signal active high The default polarity of RS485 DE signal is active high. This driver does not handle such case properly. Currently, when a pin is multiplexed as a UART CTS_B on boot, this pin is pulled HIGH by the i.MX UART CTS circuit, which activates DE signal on the RS485 transceiver and thus behave as if the RS485 was transmitting data, so the system blocks the RS485 bus when it starts and until user application takes over. This behavior is not OK. The problem consists of two separate parts. First, the i.MX UART IP requires UCR1 UARTEN and UCR2 RXEN to be set for UCR2 CTSC and CTS bits to have any effect. The UCR2 CTSC bit permits the driver to set CTS (RTS_B or RS485 DE signal) to either level sychronous to the internal UART IP clock. Compared to other options, like GPIO CTS control, this has the benefit of being synchronous to the UART IP clock and thus without glitches or bus delays. The reason for the CTS design is likely because when the Receiver is disabled, the UART IP can never indicate that it is ready to receive data by assering CTS signal, so the CTS is always pulled HIGH by default. When the port is closed by user space, imx_uart_stop_rx() clears UCR2 RXEN bit, and imx_uart_shutdown() clears UCR1 UARTEN bit. This disables UART Receiver and UART itself, and forces CTS signal HIGH, which leads to the RS485 bus being blocked because RS485 DE is incorrectly active. The proposed solution for this problem is to keep the Receiver running even after the port is closed, but in loopback mode. This disconnects the RX FIFO input from the RXD external signal, and since UCR2 TXEN is cleared, the UART Transmitter is disabled, so nothing can feed data in the RX FIFO. Because the Receiver is still enabled, the UCR2 CTSC and CTS bits still have effect and the CTS (RS485 DE) control is retained. Note that in case of RS485 DE signal active low, there is no problem and no special handling is necessary. The CTS signal defaults to HIGH, thus the RS485 is by default set to Receive and the bus is not blocked. Note that while there is the possibility to control CTS using GPIO with either CTS polarity, this has the downside of not being synchronous to the UART IP clock and thus glitchy and susceptible to slow DE switching. Second, on boot, before the UART driver probe callback is called, the driver core triggers pinctrl_init_done() and configures the IOMUXC to default state. At this point, UCR1 UARTEN and UCR2 RXEN are both still cleared, but UART CTS_B (RS485 DE) is configured as CTS function, thus the RTS signal is pulled HIGH by the UART IP CTS circuit. One part of the solution here is to enable UCR1 UARTEN and UCR2 RXEN and UTS loopback in this driver probe callback, thus unblocking the CTSC and CTS control early on. But this is still too late, since the pin control is already configured and CTS has been pulled HIGH for a short period of time. When Linux kernel boots and this driver is bound, the pin control is set to special "init" state if the state is available, and driver can switch the "default" state afterward when ready. This state can be used to set the CTS line as a GPIO in DT temporarily, and a GPIO hog can force such GPIO to LOW, thus keeping the RS485 DE line LOW early on boot. Once the driver takes over and UCR1 UARTEN and UCR2 RXEN and UTS loopback are all enabled, the driver can switch to "default" pin control state and control the CTS line as function instead. DT binding example is below: " &gpio6 { rts-init-hog { gpio-hog; gpios = <5 0>; output-low; line-name = "rs485-de"; }; }; &uart5 { /* DHCOM UART2 */ pinctrl-0 = <&pinctrl_uart5>; pinctrl-1 = <&pinctrl_uart5_init>; pinctrl-names = "default", "init"; ... }; pinctrl_uart5_init: uart5-init-grp { fsl,pins = < ... MX6QDL_PAD_CSI0_DAT19__GPIO6_IO05 0x30b1 >; }; pinctrl_uart5: uart5-grp { fsl,pins = < ... MX6QDL_PAD_CSI0_DAT19__UART5_CTS_B 0x30b1 >; }; " Tested-by: Christoph Niedermaier <cniedermaier@dh-electronics.com> Reviewed-by: Fabio Estevam <festevam@denx.de> Signed-off-by: Marek Vasut <marex@denx.de> Link: https://lore.kernel.org/r/20220929144400.13571-1-marex@denx.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2022-09-29 16:44:00 +02:00
u32 ucr1, ucr2, ucr4, uts;
ucr1 = imx_uart_readl(sport, UCR1);
ucr2 = imx_uart_readl(sport, UCR2);
ucr4 = imx_uart_readl(sport, UCR4);
if (sport->dma_is_enabled) {
ucr1 &= ~(UCR1_RXDMAEN | UCR1_ATDMAEN);
} else {
ucr1 &= ~UCR1_RRDYEN;
ucr2 &= ~UCR2_ATEN;
ucr4 &= ~UCR4_OREN;
}
imx_uart_writel(sport, ucr1, UCR1);
imx_uart_writel(sport, ucr4, UCR4);
tty: serial: imx: Handle RS485 DE signal active high The default polarity of RS485 DE signal is active high. This driver does not handle such case properly. Currently, when a pin is multiplexed as a UART CTS_B on boot, this pin is pulled HIGH by the i.MX UART CTS circuit, which activates DE signal on the RS485 transceiver and thus behave as if the RS485 was transmitting data, so the system blocks the RS485 bus when it starts and until user application takes over. This behavior is not OK. The problem consists of two separate parts. First, the i.MX UART IP requires UCR1 UARTEN and UCR2 RXEN to be set for UCR2 CTSC and CTS bits to have any effect. The UCR2 CTSC bit permits the driver to set CTS (RTS_B or RS485 DE signal) to either level sychronous to the internal UART IP clock. Compared to other options, like GPIO CTS control, this has the benefit of being synchronous to the UART IP clock and thus without glitches or bus delays. The reason for the CTS design is likely because when the Receiver is disabled, the UART IP can never indicate that it is ready to receive data by assering CTS signal, so the CTS is always pulled HIGH by default. When the port is closed by user space, imx_uart_stop_rx() clears UCR2 RXEN bit, and imx_uart_shutdown() clears UCR1 UARTEN bit. This disables UART Receiver and UART itself, and forces CTS signal HIGH, which leads to the RS485 bus being blocked because RS485 DE is incorrectly active. The proposed solution for this problem is to keep the Receiver running even after the port is closed, but in loopback mode. This disconnects the RX FIFO input from the RXD external signal, and since UCR2 TXEN is cleared, the UART Transmitter is disabled, so nothing can feed data in the RX FIFO. Because the Receiver is still enabled, the UCR2 CTSC and CTS bits still have effect and the CTS (RS485 DE) control is retained. Note that in case of RS485 DE signal active low, there is no problem and no special handling is necessary. The CTS signal defaults to HIGH, thus the RS485 is by default set to Receive and the bus is not blocked. Note that while there is the possibility to control CTS using GPIO with either CTS polarity, this has the downside of not being synchronous to the UART IP clock and thus glitchy and susceptible to slow DE switching. Second, on boot, before the UART driver probe callback is called, the driver core triggers pinctrl_init_done() and configures the IOMUXC to default state. At this point, UCR1 UARTEN and UCR2 RXEN are both still cleared, but UART CTS_B (RS485 DE) is configured as CTS function, thus the RTS signal is pulled HIGH by the UART IP CTS circuit. One part of the solution here is to enable UCR1 UARTEN and UCR2 RXEN and UTS loopback in this driver probe callback, thus unblocking the CTSC and CTS control early on. But this is still too late, since the pin control is already configured and CTS has been pulled HIGH for a short period of time. When Linux kernel boots and this driver is bound, the pin control is set to special "init" state if the state is available, and driver can switch the "default" state afterward when ready. This state can be used to set the CTS line as a GPIO in DT temporarily, and a GPIO hog can force such GPIO to LOW, thus keeping the RS485 DE line LOW early on boot. Once the driver takes over and UCR1 UARTEN and UCR2 RXEN and UTS loopback are all enabled, the driver can switch to "default" pin control state and control the CTS line as function instead. DT binding example is below: " &gpio6 { rts-init-hog { gpio-hog; gpios = <5 0>; output-low; line-name = "rs485-de"; }; }; &uart5 { /* DHCOM UART2 */ pinctrl-0 = <&pinctrl_uart5>; pinctrl-1 = <&pinctrl_uart5_init>; pinctrl-names = "default", "init"; ... }; pinctrl_uart5_init: uart5-init-grp { fsl,pins = < ... MX6QDL_PAD_CSI0_DAT19__GPIO6_IO05 0x30b1 >; }; pinctrl_uart5: uart5-grp { fsl,pins = < ... MX6QDL_PAD_CSI0_DAT19__UART5_CTS_B 0x30b1 >; }; " Tested-by: Christoph Niedermaier <cniedermaier@dh-electronics.com> Reviewed-by: Fabio Estevam <festevam@denx.de> Signed-off-by: Marek Vasut <marex@denx.de> Link: https://lore.kernel.org/r/20220929144400.13571-1-marex@denx.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2022-09-29 16:44:00 +02:00
/* See SER_RS485_ENABLED/UTS_LOOP comment in imx_uart_probe() */
if (port->rs485.flags & SER_RS485_ENABLED &&
port->rs485.flags & SER_RS485_RTS_ON_SEND &&
sport->have_rtscts && !sport->have_rtsgpio && loopback) {
tty: serial: imx: Handle RS485 DE signal active high The default polarity of RS485 DE signal is active high. This driver does not handle such case properly. Currently, when a pin is multiplexed as a UART CTS_B on boot, this pin is pulled HIGH by the i.MX UART CTS circuit, which activates DE signal on the RS485 transceiver and thus behave as if the RS485 was transmitting data, so the system blocks the RS485 bus when it starts and until user application takes over. This behavior is not OK. The problem consists of two separate parts. First, the i.MX UART IP requires UCR1 UARTEN and UCR2 RXEN to be set for UCR2 CTSC and CTS bits to have any effect. The UCR2 CTSC bit permits the driver to set CTS (RTS_B or RS485 DE signal) to either level sychronous to the internal UART IP clock. Compared to other options, like GPIO CTS control, this has the benefit of being synchronous to the UART IP clock and thus without glitches or bus delays. The reason for the CTS design is likely because when the Receiver is disabled, the UART IP can never indicate that it is ready to receive data by assering CTS signal, so the CTS is always pulled HIGH by default. When the port is closed by user space, imx_uart_stop_rx() clears UCR2 RXEN bit, and imx_uart_shutdown() clears UCR1 UARTEN bit. This disables UART Receiver and UART itself, and forces CTS signal HIGH, which leads to the RS485 bus being blocked because RS485 DE is incorrectly active. The proposed solution for this problem is to keep the Receiver running even after the port is closed, but in loopback mode. This disconnects the RX FIFO input from the RXD external signal, and since UCR2 TXEN is cleared, the UART Transmitter is disabled, so nothing can feed data in the RX FIFO. Because the Receiver is still enabled, the UCR2 CTSC and CTS bits still have effect and the CTS (RS485 DE) control is retained. Note that in case of RS485 DE signal active low, there is no problem and no special handling is necessary. The CTS signal defaults to HIGH, thus the RS485 is by default set to Receive and the bus is not blocked. Note that while there is the possibility to control CTS using GPIO with either CTS polarity, this has the downside of not being synchronous to the UART IP clock and thus glitchy and susceptible to slow DE switching. Second, on boot, before the UART driver probe callback is called, the driver core triggers pinctrl_init_done() and configures the IOMUXC to default state. At this point, UCR1 UARTEN and UCR2 RXEN are both still cleared, but UART CTS_B (RS485 DE) is configured as CTS function, thus the RTS signal is pulled HIGH by the UART IP CTS circuit. One part of the solution here is to enable UCR1 UARTEN and UCR2 RXEN and UTS loopback in this driver probe callback, thus unblocking the CTSC and CTS control early on. But this is still too late, since the pin control is already configured and CTS has been pulled HIGH for a short period of time. When Linux kernel boots and this driver is bound, the pin control is set to special "init" state if the state is available, and driver can switch the "default" state afterward when ready. This state can be used to set the CTS line as a GPIO in DT temporarily, and a GPIO hog can force such GPIO to LOW, thus keeping the RS485 DE line LOW early on boot. Once the driver takes over and UCR1 UARTEN and UCR2 RXEN and UTS loopback are all enabled, the driver can switch to "default" pin control state and control the CTS line as function instead. DT binding example is below: " &gpio6 { rts-init-hog { gpio-hog; gpios = <5 0>; output-low; line-name = "rs485-de"; }; }; &uart5 { /* DHCOM UART2 */ pinctrl-0 = <&pinctrl_uart5>; pinctrl-1 = <&pinctrl_uart5_init>; pinctrl-names = "default", "init"; ... }; pinctrl_uart5_init: uart5-init-grp { fsl,pins = < ... MX6QDL_PAD_CSI0_DAT19__GPIO6_IO05 0x30b1 >; }; pinctrl_uart5: uart5-grp { fsl,pins = < ... MX6QDL_PAD_CSI0_DAT19__UART5_CTS_B 0x30b1 >; }; " Tested-by: Christoph Niedermaier <cniedermaier@dh-electronics.com> Reviewed-by: Fabio Estevam <festevam@denx.de> Signed-off-by: Marek Vasut <marex@denx.de> Link: https://lore.kernel.org/r/20220929144400.13571-1-marex@denx.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2022-09-29 16:44:00 +02:00
uts = imx_uart_readl(sport, imx_uart_uts_reg(sport));
uts |= UTS_LOOP;
imx_uart_writel(sport, uts, imx_uart_uts_reg(sport));
ucr2 |= UCR2_RXEN;
} else {
ucr2 &= ~UCR2_RXEN;
}
imx_uart_writel(sport, ucr2, UCR2);
}
/* called with port.lock taken and irqs off */
static void imx_uart_stop_rx(struct uart_port *port)
{
/*
* Stop RX and enable loopback in order to make sure RS485 bus
* is not blocked. Se comment in imx_uart_probe().
*/
imx_uart_stop_rx_with_loopback_ctrl(port, true);
}
/* called with port.lock taken and irqs off */
static void imx_uart_enable_ms(struct uart_port *port)
{
struct imx_port *sport = to_imx_port(port);
mod_timer(&sport->timer, jiffies);
mctrl_gpio_enable_ms(sport->gpios);
}
static void imx_uart_dma_tx(struct imx_port *sport);
/* called with port.lock taken and irqs off */
static inline void imx_uart_transmit_buffer(struct imx_port *sport)
{
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 08:08:23 +02:00
struct tty_port *tport = &sport->port.state->port;
unsigned char c;
if (sport->port.x_char) {
/* Send next char */
imx_uart_writel(sport, sport->port.x_char, URTX0);
sport->port.icount.tx++;
sport->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 08:08:23 +02:00
if (kfifo_is_empty(&tport->xmit_fifo) ||
uart_tx_stopped(&sport->port)) {
imx_uart_stop_tx(&sport->port);
return;
}
if (sport->dma_is_enabled) {
u32 ucr1;
/*
* We've just sent a X-char Ensure the TX DMA is enabled
* and the TX IRQ is disabled.
**/
ucr1 = imx_uart_readl(sport, UCR1);
ucr1 &= ~UCR1_TRDYEN;
if (sport->dma_is_txing) {
ucr1 |= UCR1_TXDMAEN;
imx_uart_writel(sport, ucr1, UCR1);
} else {
imx_uart_writel(sport, ucr1, UCR1);
imx_uart_dma_tx(sport);
}
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 08:08:23 +02:00
while (!(imx_uart_readl(sport, imx_uart_uts_reg(sport)) & UTS_TXFULL) &&
uart_fifo_get(&sport->port, &c))
imx_uart_writel(sport, c, URTX0);
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 08:08:23 +02:00
if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(&sport->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 08:08:23 +02:00
if (kfifo_is_empty(&tport->xmit_fifo))
imx_uart_stop_tx(&sport->port);
}
static void imx_uart_dma_tx_callback(void *data)
{
struct imx_port *sport = data;
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 08:08:23 +02:00
struct tty_port *tport = &sport->port.state->port;
struct scatterlist *sgl = &sport->tx_sgl[0];
unsigned long flags;
u32 ucr1;
uart_port_lock_irqsave(&sport->port, &flags);
dma_unmap_sg(sport->port.dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);
ucr1 = imx_uart_readl(sport, UCR1);
ucr1 &= ~UCR1_TXDMAEN;
imx_uart_writel(sport, ucr1, UCR1);
uart_xmit_advance(&sport->port, sport->tx_bytes);
dev_dbg(sport->port.dev, "we finish the TX DMA.\n");
sport->dma_is_txing = 0;
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 08:08:23 +02:00
if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(&sport->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 08:08:23 +02:00
if (!kfifo_is_empty(&tport->xmit_fifo) &&
!uart_tx_stopped(&sport->port))
imx_uart_dma_tx(sport);
else if (sport->port.rs485.flags & SER_RS485_ENABLED) {
u32 ucr4 = imx_uart_readl(sport, UCR4);
ucr4 |= UCR4_TCEN;
imx_uart_writel(sport, ucr4, UCR4);
}
uart_port_unlock_irqrestore(&sport->port, flags);
}
/* called with port.lock taken and irqs off */
static void imx_uart_dma_tx(struct imx_port *sport)
{
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 08:08:23 +02:00
struct tty_port *tport = &sport->port.state->port;
struct scatterlist *sgl = sport->tx_sgl;
struct dma_async_tx_descriptor *desc;
struct dma_chan *chan = sport->dma_chan_tx;
struct device *dev = sport->port.dev;
u32 ucr1, ucr4;
int ret;
if (sport->dma_is_txing)
return;
ucr4 = imx_uart_readl(sport, UCR4);
ucr4 &= ~UCR4_TCEN;
imx_uart_writel(sport, ucr4, UCR4);
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 08:08:23 +02:00
sg_init_table(sgl, ARRAY_SIZE(sport->tx_sgl));
sport->tx_bytes = kfifo_len(&tport->xmit_fifo);
sport->dma_tx_nents = kfifo_dma_out_prepare(&tport->xmit_fifo, sgl,
ARRAY_SIZE(sport->tx_sgl), sport->tx_bytes);
ret = dma_map_sg(dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);
if (ret == 0) {
dev_err(dev, "DMA mapping error for TX.\n");
return;
}
desc = dmaengine_prep_slave_sg(chan, sgl, ret,
DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT);
if (!desc) {
dma_unmap_sg(dev, sgl, sport->dma_tx_nents,
DMA_TO_DEVICE);
dev_err(dev, "We cannot prepare for the TX slave dma!\n");
return;
}
desc->callback = imx_uart_dma_tx_callback;
desc->callback_param = sport;
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 08:08:23 +02:00
dev_dbg(dev, "TX: prepare to send %u bytes by DMA.\n", sport->tx_bytes);
ucr1 = imx_uart_readl(sport, UCR1);
ucr1 |= UCR1_TXDMAEN;
imx_uart_writel(sport, ucr1, UCR1);
/* fire it */
sport->dma_is_txing = 1;
dmaengine_submit(desc);
dma_async_issue_pending(chan);
return;
}
/* called with port.lock taken and irqs off */
static void imx_uart_start_tx(struct uart_port *port)
{
struct imx_port *sport = to_imx_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 08:08:23 +02:00
struct tty_port *tport = &sport->port.state->port;
u32 ucr1;
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 08:08:23 +02:00
if (!sport->port.x_char && kfifo_is_empty(&tport->xmit_fifo))
return;
/*
* We cannot simply do nothing here if sport->tx_state == SEND already
* because UCR1_TXMPTYEN might already have been cleared in
* imx_uart_stop_tx(), but tx_state is still SEND.
*/
if (port->rs485.flags & SER_RS485_ENABLED) {
if (sport->tx_state == OFF) {
u32 ucr2 = imx_uart_readl(sport, UCR2);
if (port->rs485.flags & SER_RS485_RTS_ON_SEND)
imx_uart_rts_active(sport, &ucr2);
else
imx_uart_rts_inactive(sport, &ucr2);
imx_uart_writel(sport, ucr2, UCR2);
/*
* Since we are about to transmit we can not stop RX
* with loopback enabled because that will make our
* transmitted data being just looped to RX.
*/
if (!(port->rs485.flags & SER_RS485_RX_DURING_TX) &&
!port->rs485_rx_during_tx_gpio)
imx_uart_stop_rx_with_loopback_ctrl(port, false);
sport->tx_state = WAIT_AFTER_RTS;
if (port->rs485.delay_rts_before_send > 0) {
start_hrtimer_ms(&sport->trigger_start_tx,
port->rs485.delay_rts_before_send);
return;
}
/* continue without any delay */
}
if (sport->tx_state == WAIT_AFTER_SEND
|| sport->tx_state == WAIT_AFTER_RTS) {
hrtimer_try_to_cancel(&sport->trigger_stop_tx);
/*
* Enable transmitter and shifter empty irq only if DMA
* is off. In the DMA case this is done in the
* tx-callback.
*/
if (!sport->dma_is_enabled) {
u32 ucr4 = imx_uart_readl(sport, UCR4);
ucr4 |= UCR4_TCEN;
imx_uart_writel(sport, ucr4, UCR4);
}
sport->tx_state = SEND;
}
} else {
sport->tx_state = SEND;
}
if (!sport->dma_is_enabled) {
ucr1 = imx_uart_readl(sport, UCR1);
imx_uart_writel(sport, ucr1 | UCR1_TRDYEN, UCR1);
}
if (sport->dma_is_enabled) {
if (sport->port.x_char) {
/* We have X-char to send, so enable TX IRQ and
* disable TX DMA to let TX interrupt to send X-char */
ucr1 = imx_uart_readl(sport, UCR1);
ucr1 &= ~UCR1_TXDMAEN;
ucr1 |= UCR1_TRDYEN;
imx_uart_writel(sport, ucr1, UCR1);
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 08:08:23 +02:00
if (!kfifo_is_empty(&tport->xmit_fifo) &&
!uart_tx_stopped(port))
imx_uart_dma_tx(sport);
return;
}
}
static irqreturn_t __imx_uart_rtsint(int irq, void *dev_id)
{
struct imx_port *sport = dev_id;
u32 usr1;
imx_uart_writel(sport, USR1_RTSD, USR1);
usr1 = imx_uart_readl(sport, USR1) & USR1_RTSS;
serial: imx: Update mctrl old_status on RTSD interrupt When sending data using DMA at high baudrate (4 Mbdps in local test case) to a device with small RX buffer which keeps asserting RTS after every received byte, it is possible that the iMX UART driver would not recognize the falling edge of RTS input signal and get stuck, unable to transmit any more data. This condition happens when the following sequence of events occur: - imx_uart_mctrl_check() is called at some point and takes a snapshot of UART control signal status into sport->old_status using imx_uart_get_hwmctrl(). The RTSS/TIOCM_CTS bit is of interest here (*). - DMA transfer occurs, the remote device asserts RTS signal after each byte. The i.MX UART driver recognizes each such RTS signal change, raises an interrupt with USR1 register RTSD bit set, which leads to invocation of __imx_uart_rtsint(), which calls uart_handle_cts_change(). - If the RTS signal is deasserted, uart_handle_cts_change() clears port->hw_stopped and unblocks the port for further data transfers. - If the RTS is asserted, uart_handle_cts_change() sets port->hw_stopped and blocks the port for further data transfers. This may occur as the last interrupt of a transfer, which means port->hw_stopped remains set and the port remains blocked (**). - Any further data transfer attempts will trigger imx_uart_mctrl_check(), which will read current status of UART control signals by calling imx_uart_get_hwmctrl() (***) and compare it with sport->old_status . - If current status differs from sport->old_status for RTS signal, uart_handle_cts_change() is called and possibly unblocks the port by clearing port->hw_stopped . - If current status does not differ from sport->old_status for RTS signal, no action occurs. This may occur in case prior snapshot (*) was taken before any transfer so the RTS is deasserted, current snapshot (***) was taken after a transfer and therefore RTS is deasserted again, which means current status and sport->old_status are identical. In case (**) triggered when RTS got asserted, and made port->hw_stopped set, the port->hw_stopped will remain set because no change on RTS line is recognized by this driver and uart_handle_cts_change() is not called from here to unblock the port->hw_stopped. Update sport->old_status in __imx_uart_rtsint() accordingly to make imx_uart_mctrl_check() detect such RTS change. Note that TIOCM_CAR and TIOCM_RI bits in sport->old_status do not suffer from this problem. Fixes: ceca629e0b48 ("[ARM] 2971/1: i.MX uart handle rts irq") Cc: stable <stable@kernel.org> Reviewed-by: Esben Haabendal <esben@geanix.com> Signed-off-by: Marek Vasut <marex@denx.de> Link: https://lore.kernel.org/r/20241002184133.19427-1-marex@denx.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2024-10-02 20:40:38 +02:00
/*
* Update sport->old_status here, so any follow-up calls to
* imx_uart_mctrl_check() will be able to recognize that RTS
* state changed since last imx_uart_mctrl_check() call.
*
* In case RTS has been detected as asserted here and later on
* deasserted by the time imx_uart_mctrl_check() was called,
* imx_uart_mctrl_check() can detect the RTS state change and
* trigger uart_handle_cts_change() to unblock the port for
* further TX transfers.
*/
if (usr1 & USR1_RTSS)
sport->old_status |= TIOCM_CTS;
else
sport->old_status &= ~TIOCM_CTS;
uart_handle_cts_change(&sport->port, usr1);
wake_up_interruptible(&sport->port.state->port.delta_msr_wait);
return IRQ_HANDLED;
}
static irqreturn_t imx_uart_rtsint(int irq, void *dev_id)
{
struct imx_port *sport = dev_id;
irqreturn_t ret;
uart_port_lock(&sport->port);
ret = __imx_uart_rtsint(irq, dev_id);
uart_port_unlock(&sport->port);
return ret;
}
static irqreturn_t imx_uart_txint(int irq, void *dev_id)
{
struct imx_port *sport = dev_id;
uart_port_lock(&sport->port);
imx_uart_transmit_buffer(sport);
uart_port_unlock(&sport->port);
return IRQ_HANDLED;
}
/* Check if hardware Rx flood is in progress, and issue soft reset to stop it.
* This is to be called from Rx ISRs only when some bytes were actually
* received.
*
* A way to reproduce the flood (checked on iMX6SX) is: open iMX UART at 9600
* 8N1, and from external source send 0xf0 char at 115200 8N1. In about 90% of
* cases this starts a flood of "receiving" of 0xff characters by the iMX6 UART
* that is terminated by any activity on RxD line, or could be stopped by
* issuing soft reset to the UART (just stop/start of RX does not help). Note
* that what we do here is sending isolated start bit about 2.4 times shorter
* than it is to be on UART configured baud rate.
*
* Called with port.lock taken and irqs off.
*/
static void imx_uart_check_flood(struct imx_port *sport, u32 usr2)
{
/* To detect hardware 0xff flood we monitor RxD line between RX
* interrupts to isolate "receiving" of char(s) with no activity
* on RxD line, that'd never happen on actual data transfers.
*
* We use USR2_WAKE bit to check for activity on RxD line, but we have a
* race here if we clear USR2_WAKE when receiving of a char is in
* progress, so we might get RX interrupt later with USR2_WAKE bit
* cleared. Note though that as we don't try to clear USR2_WAKE when we
* detected no activity, this race may hide actual activity only once.
*
* Yet another case where receive interrupt may occur without RxD
* activity is expiration of aging timer, so we consider this as well.
*
* We use 'idle_counter' to ensure that we got at least so many RX
* interrupts without any detected activity on RxD line. 2 cases
* described plus 1 to be on the safe side gives us a margin of 3,
* below. In practice I was not able to produce a false positive to
* induce soft reset at regular data transfers even using 1 as the
* margin, so 3 is actually very strong.
*
* We count interrupts, not chars in 'idle-counter' for simplicity.
*/
if (usr2 & USR2_WAKE) {
imx_uart_writel(sport, USR2_WAKE, USR2);
sport->idle_counter = 0;
} else if (++sport->idle_counter > 3) {
dev_warn(sport->port.dev, "RX flood detected: soft reset.");
imx_uart_soft_reset(sport); /* also clears 'sport->idle_counter' */
}
}
/* called with port.lock taken and irqs off */
static irqreturn_t __imx_uart_rxint(int irq, void *dev_id)
{
struct imx_port *sport = dev_id;
struct tty_port *port = &sport->port.state->port;
u32 usr2, rx;
/* If we received something, check for 0xff flood */
usr2 = imx_uart_readl(sport, USR2);
if (usr2 & USR2_RDR)
imx_uart_check_flood(sport, usr2);
while ((rx = imx_uart_readl(sport, URXD0)) & URXD_CHARRDY) {
unsigned int flg = TTY_NORMAL;
sport->port.icount.rx++;
if (unlikely(rx & URXD_ERR)) {
if (rx & URXD_BRK) {
sport->port.icount.brk++;
if (uart_handle_break(&sport->port))
continue;
}
else if (rx & URXD_PRERR)
sport->port.icount.parity++;
else if (rx & URXD_FRMERR)
sport->port.icount.frame++;
if (rx & URXD_OVRRUN)
sport->port.icount.overrun++;
if (rx & sport->port.ignore_status_mask)
continue;
rx &= (sport->port.read_status_mask | 0xFF);
if (rx & URXD_BRK)
flg = TTY_BREAK;
else if (rx & URXD_PRERR)
flg = TTY_PARITY;
else if (rx & URXD_FRMERR)
flg = TTY_FRAME;
if (rx & URXD_OVRRUN)
flg = TTY_OVERRUN;
sport->port.sysrq = 0;
} else if (uart_handle_sysrq_char(&sport->port, (unsigned char)rx)) {
continue;
}
if (sport->port.ignore_status_mask & URXD_DUMMY_READ)
continue;
if (tty_insert_flip_char(port, rx, flg) == 0)
sport->port.icount.buf_overrun++;
}
tty_flip_buffer_push(port);
return IRQ_HANDLED;
}
static irqreturn_t imx_uart_rxint(int irq, void *dev_id)
{
struct imx_port *sport = dev_id;
irqreturn_t ret;
uart_port_lock(&sport->port);
ret = __imx_uart_rxint(irq, dev_id);
uart_port_unlock(&sport->port);
return ret;
}
static void imx_uart_clear_rx_errors(struct imx_port *sport);
/*
* We have a modem side uart, so the meanings of RTS and CTS are inverted.
*/
/* called with port.lock taken and irqs off */
static unsigned int imx_uart_get_hwmctrl(struct imx_port *sport)
{
unsigned int tmp = TIOCM_DSR;
unsigned usr1 = imx_uart_readl(sport, USR1);
unsigned usr2 = imx_uart_readl(sport, USR2);
if (usr1 & USR1_RTSS)
tmp |= TIOCM_CTS;
/* in DCE mode DCDIN is always 0 */
if (!(usr2 & USR2_DCDIN))
tmp |= TIOCM_CAR;
if (sport->dte_mode)
if (!(imx_uart_readl(sport, USR2) & USR2_RIIN))
tmp |= TIOCM_RI;
return tmp;
}
/*
* Handle any change of modem status signal since we were last called.
*
* Called with port.lock taken and irqs off.
*/
static void imx_uart_mctrl_check(struct imx_port *sport)
{
unsigned int status, changed;
status = imx_uart_get_hwmctrl(sport);
changed = status ^ sport->old_status;
if (changed == 0)
return;
sport->old_status = status;
if (changed & TIOCM_RI && status & TIOCM_RI)
sport->port.icount.rng++;
if (changed & TIOCM_DSR)
sport->port.icount.dsr++;
if (changed & TIOCM_CAR)
uart_handle_dcd_change(&sport->port, status & TIOCM_CAR);
if (changed & TIOCM_CTS)
uart_handle_cts_change(&sport->port, status & TIOCM_CTS);
wake_up_interruptible(&sport->port.state->port.delta_msr_wait);
}
static irqreturn_t imx_uart_int(int irq, void *dev_id)
{
struct imx_port *sport = dev_id;
unsigned int usr1, usr2, ucr1, ucr2, ucr3, ucr4;
irqreturn_t ret = IRQ_NONE;
uart_port_lock(&sport->port);
usr1 = imx_uart_readl(sport, USR1);
usr2 = imx_uart_readl(sport, USR2);
ucr1 = imx_uart_readl(sport, UCR1);
ucr2 = imx_uart_readl(sport, UCR2);
ucr3 = imx_uart_readl(sport, UCR3);
ucr4 = imx_uart_readl(sport, UCR4);
/*
* Even if a condition is true that can trigger an irq only handle it if
* the respective irq source is enabled. This prevents some undesired
* actions, for example if a character that sits in the RX FIFO and that
* should be fetched via DMA is tried to be fetched using PIO. Or the
* receiver is currently off and so reading from URXD0 results in an
* exception. So just mask the (raw) status bits for disabled irqs.
*/
if ((ucr1 & UCR1_RRDYEN) == 0)
usr1 &= ~USR1_RRDY;
if ((ucr2 & UCR2_ATEN) == 0)
usr1 &= ~USR1_AGTIM;
if ((ucr1 & UCR1_TRDYEN) == 0)
usr1 &= ~USR1_TRDY;
if ((ucr4 & UCR4_TCEN) == 0)
usr2 &= ~USR2_TXDC;
if ((ucr3 & UCR3_DTRDEN) == 0)
usr1 &= ~USR1_DTRD;
if ((ucr1 & UCR1_RTSDEN) == 0)
usr1 &= ~USR1_RTSD;
if ((ucr3 & UCR3_AWAKEN) == 0)
usr1 &= ~USR1_AWAKE;
if ((ucr4 & UCR4_OREN) == 0)
usr2 &= ~USR2_ORE;
if (usr1 & (USR1_RRDY | USR1_AGTIM)) {
imx_uart_writel(sport, USR1_AGTIM, USR1);
__imx_uart_rxint(irq, dev_id);
ret = IRQ_HANDLED;
}
if ((usr1 & USR1_TRDY) || (usr2 & USR2_TXDC)) {
imx_uart_transmit_buffer(sport);
ret = IRQ_HANDLED;
}
if (usr1 & USR1_DTRD) {
imx_uart_writel(sport, USR1_DTRD, USR1);
imx_uart_mctrl_check(sport);
ret = IRQ_HANDLED;
}
if (usr1 & USR1_RTSD) {
__imx_uart_rtsint(irq, dev_id);
ret = IRQ_HANDLED;
}
if (usr1 & USR1_AWAKE) {
imx_uart_writel(sport, USR1_AWAKE, USR1);
ret = IRQ_HANDLED;
}
if (usr2 & USR2_ORE) {
sport->port.icount.overrun++;
imx_uart_writel(sport, USR2_ORE, USR2);
ret = IRQ_HANDLED;
}
uart_port_unlock(&sport->port);
return ret;
}
/*
* Return TIOCSER_TEMT when transmitter is not busy.
*/
static unsigned int imx_uart_tx_empty(struct uart_port *port)
{
struct imx_port *sport = to_imx_port(port);
unsigned int ret;
ret = (imx_uart_readl(sport, USR2) & USR2_TXDC) ? TIOCSER_TEMT : 0;
/* If the TX DMA is working, return 0. */
if (sport->dma_is_txing)
ret = 0;
return ret;
}
/* called with port.lock taken and irqs off */
static unsigned int imx_uart_get_mctrl(struct uart_port *port)
{
struct imx_port *sport = to_imx_port(port);
unsigned int ret = imx_uart_get_hwmctrl(sport);
mctrl_gpio_get(sport->gpios, &ret);
return ret;
}
/* called with port.lock taken and irqs off */
static void imx_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
struct imx_port *sport = to_imx_port(port);
u32 ucr3, uts;
if (!(port->rs485.flags & SER_RS485_ENABLED)) {
u32 ucr2;
/*
* Turn off autoRTS if RTS is lowered and restore autoRTS
* setting if RTS is raised.
*/
ucr2 = imx_uart_readl(sport, UCR2);
ucr2 &= ~(UCR2_CTS | UCR2_CTSC);
if (mctrl & TIOCM_RTS) {
ucr2 |= UCR2_CTS;
/*
* UCR2_IRTS is unset if and only if the port is
* configured for CRTSCTS, so we use inverted UCR2_IRTS
* to get the state to restore to.
*/
if (!(ucr2 & UCR2_IRTS))
ucr2 |= UCR2_CTSC;
}
imx_uart_writel(sport, ucr2, UCR2);
}
ucr3 = imx_uart_readl(sport, UCR3) & ~UCR3_DSR;
if (!(mctrl & TIOCM_DTR))
ucr3 |= UCR3_DSR;
imx_uart_writel(sport, ucr3, UCR3);
uts = imx_uart_readl(sport, imx_uart_uts_reg(sport)) & ~UTS_LOOP;
if (mctrl & TIOCM_LOOP)
uts |= UTS_LOOP;
imx_uart_writel(sport, uts, imx_uart_uts_reg(sport));
mctrl_gpio_set(sport->gpios, mctrl);
}
/*
* Interrupts always disabled.
*/
static void imx_uart_break_ctl(struct uart_port *port, int break_state)
{
struct imx_port *sport = to_imx_port(port);
unsigned long flags;
u32 ucr1;
uart_port_lock_irqsave(&sport->port, &flags);
ucr1 = imx_uart_readl(sport, UCR1) & ~UCR1_SNDBRK;
if (break_state != 0)
ucr1 |= UCR1_SNDBRK;
imx_uart_writel(sport, ucr1, UCR1);
uart_port_unlock_irqrestore(&sport->port, flags);
}
/*
* This is our per-port timeout handler, for checking the
* modem status signals.
*/
static void imx_uart_timeout(struct timer_list *t)
{
struct imx_port *sport = timer_container_of(sport, t, timer);
unsigned long flags;
if (sport->port.state) {
uart_port_lock_irqsave(&sport->port, &flags);
imx_uart_mctrl_check(sport);
uart_port_unlock_irqrestore(&sport->port, flags);
mod_timer(&sport->timer, jiffies + MCTRL_TIMEOUT);
}
}
/*
* There are two kinds of RX DMA interrupts(such as in the MX6Q):
* [1] the RX DMA buffer is full.
* [2] the aging timer expires
*
* Condition [2] is triggered when a character has been sitting in the FIFO
* for at least 8 byte durations.
*/
static void imx_uart_dma_rx_callback(void *data)
{
struct imx_port *sport = data;
struct dma_chan *chan = sport->dma_chan_rx;
struct scatterlist *sgl = &sport->rx_sgl;
struct tty_port *port = &sport->port.state->port;
struct dma_tx_state state;
serial: imx-serial - update UART IMX driver to use cyclic DMA The IMX UART has a 32 bytes HW buffer which can be filled up in 2777us at 115200 baud or 80us at 4Mbaud (supported by IMX53). Taking this in consideration there is a good probability to lose data because of the DMA startup latency. Our tests (explained below) indicates a latency up to 4400us when creating interrupt load and ~70us without. When creating interrupt load I was able to see continuous overrun errors by checking serial driver statistics using the command: `cat /proc/tty/driver/IMX-uart`. Replace manual restart of DMA with cyclic DMA to eliminate data loss due to DMA engine startup latency (similar approch to atmel_serial.c driver). As result the DMA engine will start on the first serial data transfer and stops only when serial port is closed. Tests environment: Using the m53evk board I have used a GPIO for profiling the IMX serial driver. - The RX line and GPIO were connected to oscilloscope. - Run a small test program on the m53evk board that will only open and read data from ttymxc2 port. - Connect the ttymxc2 port to my laptop using a USB serial converter where another test program is running, able to send configurable packet lengths and intervals. - Serial ports configured at 115200 8N1. - Interrupts load created by disconnecting/connecting (3s interval) a USB hub, using a digital switch, with 4 USB devices (USB-Serial converter, USB SD card, etc) connected. (around 160 interrupts/second generated) - The GPIO was toggled HI in the `imx_int` when USR1_RRDY or USR1_AGTIM events are received and toggled back, once the DMA configuration is finalized, at the end of `imx_dma_rxint`. Measurements: The measurements were done from the end of the last byte (RX line) until the GPIO was toggled back LOW. Note: The GPIO toggling was done using `gpiod_set_value` method. Tests performed: 1. Sending 9 bytes packets at 8ms interval. Having the 9 bytes packets will activate the RRDY threshold event and IMX serial interrupt called. Results: - DMA start latency (interrupt start latency + DMA configuration) consistently 70us when system not loaded. - DMA start latency up to 4400us when system loaded. 2. Sending 40 bytes packet at 8mS interval. Results with load: - Able to observe overruns by running: `watch -n1 cat /proc/tty/driver/IMX-uart` Tested-by: Peter Senna Tschudin <peter.senna@collabora.com> Acked-by: Peter Senna Tschudin <peter.senna@collabora.com> Signed-off-by: Nandor Han <nandor.han@ge.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2016-08-08 15:38:27 +03:00
struct circ_buf *rx_ring = &sport->rx_ring;
enum dma_status status;
serial: imx-serial - update UART IMX driver to use cyclic DMA The IMX UART has a 32 bytes HW buffer which can be filled up in 2777us at 115200 baud or 80us at 4Mbaud (supported by IMX53). Taking this in consideration there is a good probability to lose data because of the DMA startup latency. Our tests (explained below) indicates a latency up to 4400us when creating interrupt load and ~70us without. When creating interrupt load I was able to see continuous overrun errors by checking serial driver statistics using the command: `cat /proc/tty/driver/IMX-uart`. Replace manual restart of DMA with cyclic DMA to eliminate data loss due to DMA engine startup latency (similar approch to atmel_serial.c driver). As result the DMA engine will start on the first serial data transfer and stops only when serial port is closed. Tests environment: Using the m53evk board I have used a GPIO for profiling the IMX serial driver. - The RX line and GPIO were connected to oscilloscope. - Run a small test program on the m53evk board that will only open and read data from ttymxc2 port. - Connect the ttymxc2 port to my laptop using a USB serial converter where another test program is running, able to send configurable packet lengths and intervals. - Serial ports configured at 115200 8N1. - Interrupts load created by disconnecting/connecting (3s interval) a USB hub, using a digital switch, with 4 USB devices (USB-Serial converter, USB SD card, etc) connected. (around 160 interrupts/second generated) - The GPIO was toggled HI in the `imx_int` when USR1_RRDY or USR1_AGTIM events are received and toggled back, once the DMA configuration is finalized, at the end of `imx_dma_rxint`. Measurements: The measurements were done from the end of the last byte (RX line) until the GPIO was toggled back LOW. Note: The GPIO toggling was done using `gpiod_set_value` method. Tests performed: 1. Sending 9 bytes packets at 8ms interval. Having the 9 bytes packets will activate the RRDY threshold event and IMX serial interrupt called. Results: - DMA start latency (interrupt start latency + DMA configuration) consistently 70us when system not loaded. - DMA start latency up to 4400us when system loaded. 2. Sending 40 bytes packet at 8mS interval. Results with load: - Able to observe overruns by running: `watch -n1 cat /proc/tty/driver/IMX-uart` Tested-by: Peter Senna Tschudin <peter.senna@collabora.com> Acked-by: Peter Senna Tschudin <peter.senna@collabora.com> Signed-off-by: Nandor Han <nandor.han@ge.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2016-08-08 15:38:27 +03:00
unsigned int w_bytes = 0;
unsigned int r_bytes;
unsigned int bd_size;
status = dmaengine_tx_status(chan, sport->rx_cookie, &state);
serial: imx-serial - update UART IMX driver to use cyclic DMA The IMX UART has a 32 bytes HW buffer which can be filled up in 2777us at 115200 baud or 80us at 4Mbaud (supported by IMX53). Taking this in consideration there is a good probability to lose data because of the DMA startup latency. Our tests (explained below) indicates a latency up to 4400us when creating interrupt load and ~70us without. When creating interrupt load I was able to see continuous overrun errors by checking serial driver statistics using the command: `cat /proc/tty/driver/IMX-uart`. Replace manual restart of DMA with cyclic DMA to eliminate data loss due to DMA engine startup latency (similar approch to atmel_serial.c driver). As result the DMA engine will start on the first serial data transfer and stops only when serial port is closed. Tests environment: Using the m53evk board I have used a GPIO for profiling the IMX serial driver. - The RX line and GPIO were connected to oscilloscope. - Run a small test program on the m53evk board that will only open and read data from ttymxc2 port. - Connect the ttymxc2 port to my laptop using a USB serial converter where another test program is running, able to send configurable packet lengths and intervals. - Serial ports configured at 115200 8N1. - Interrupts load created by disconnecting/connecting (3s interval) a USB hub, using a digital switch, with 4 USB devices (USB-Serial converter, USB SD card, etc) connected. (around 160 interrupts/second generated) - The GPIO was toggled HI in the `imx_int` when USR1_RRDY or USR1_AGTIM events are received and toggled back, once the DMA configuration is finalized, at the end of `imx_dma_rxint`. Measurements: The measurements were done from the end of the last byte (RX line) until the GPIO was toggled back LOW. Note: The GPIO toggling was done using `gpiod_set_value` method. Tests performed: 1. Sending 9 bytes packets at 8ms interval. Having the 9 bytes packets will activate the RRDY threshold event and IMX serial interrupt called. Results: - DMA start latency (interrupt start latency + DMA configuration) consistently 70us when system not loaded. - DMA start latency up to 4400us when system loaded. 2. Sending 40 bytes packet at 8mS interval. Results with load: - Able to observe overruns by running: `watch -n1 cat /proc/tty/driver/IMX-uart` Tested-by: Peter Senna Tschudin <peter.senna@collabora.com> Acked-by: Peter Senna Tschudin <peter.senna@collabora.com> Signed-off-by: Nandor Han <nandor.han@ge.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2016-08-08 15:38:27 +03:00
if (status == DMA_ERROR) {
uart_port_lock(&sport->port);
imx_uart_clear_rx_errors(sport);
uart_port_unlock(&sport->port);
serial: imx-serial - update UART IMX driver to use cyclic DMA The IMX UART has a 32 bytes HW buffer which can be filled up in 2777us at 115200 baud or 80us at 4Mbaud (supported by IMX53). Taking this in consideration there is a good probability to lose data because of the DMA startup latency. Our tests (explained below) indicates a latency up to 4400us when creating interrupt load and ~70us without. When creating interrupt load I was able to see continuous overrun errors by checking serial driver statistics using the command: `cat /proc/tty/driver/IMX-uart`. Replace manual restart of DMA with cyclic DMA to eliminate data loss due to DMA engine startup latency (similar approch to atmel_serial.c driver). As result the DMA engine will start on the first serial data transfer and stops only when serial port is closed. Tests environment: Using the m53evk board I have used a GPIO for profiling the IMX serial driver. - The RX line and GPIO were connected to oscilloscope. - Run a small test program on the m53evk board that will only open and read data from ttymxc2 port. - Connect the ttymxc2 port to my laptop using a USB serial converter where another test program is running, able to send configurable packet lengths and intervals. - Serial ports configured at 115200 8N1. - Interrupts load created by disconnecting/connecting (3s interval) a USB hub, using a digital switch, with 4 USB devices (USB-Serial converter, USB SD card, etc) connected. (around 160 interrupts/second generated) - The GPIO was toggled HI in the `imx_int` when USR1_RRDY or USR1_AGTIM events are received and toggled back, once the DMA configuration is finalized, at the end of `imx_dma_rxint`. Measurements: The measurements were done from the end of the last byte (RX line) until the GPIO was toggled back LOW. Note: The GPIO toggling was done using `gpiod_set_value` method. Tests performed: 1. Sending 9 bytes packets at 8ms interval. Having the 9 bytes packets will activate the RRDY threshold event and IMX serial interrupt called. Results: - DMA start latency (interrupt start latency + DMA configuration) consistently 70us when system not loaded. - DMA start latency up to 4400us when system loaded. 2. Sending 40 bytes packet at 8mS interval. Results with load: - Able to observe overruns by running: `watch -n1 cat /proc/tty/driver/IMX-uart` Tested-by: Peter Senna Tschudin <peter.senna@collabora.com> Acked-by: Peter Senna Tschudin <peter.senna@collabora.com> Signed-off-by: Nandor Han <nandor.han@ge.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2016-08-08 15:38:27 +03:00
return;
}
/*
* The state-residue variable represents the empty space
* relative to the entire buffer. Taking this in consideration
* the head is always calculated base on the buffer total
* length - DMA transaction residue. The UART script from the
* SDMA firmware will jump to the next buffer descriptor,
* once a DMA transaction if finalized (IMX53 RM - A.4.1.2.4).
* Taking this in consideration the tail is always at the
* beginning of the buffer descriptor that contains the head.
*/
/* Calculate the head */
rx_ring->head = sg_dma_len(sgl) - state.residue;
/* Calculate the tail. */
bd_size = sg_dma_len(sgl) / sport->rx_periods;
rx_ring->tail = ((rx_ring->head-1) / bd_size) * bd_size;
serial: imx-serial - update UART IMX driver to use cyclic DMA The IMX UART has a 32 bytes HW buffer which can be filled up in 2777us at 115200 baud or 80us at 4Mbaud (supported by IMX53). Taking this in consideration there is a good probability to lose data because of the DMA startup latency. Our tests (explained below) indicates a latency up to 4400us when creating interrupt load and ~70us without. When creating interrupt load I was able to see continuous overrun errors by checking serial driver statistics using the command: `cat /proc/tty/driver/IMX-uart`. Replace manual restart of DMA with cyclic DMA to eliminate data loss due to DMA engine startup latency (similar approch to atmel_serial.c driver). As result the DMA engine will start on the first serial data transfer and stops only when serial port is closed. Tests environment: Using the m53evk board I have used a GPIO for profiling the IMX serial driver. - The RX line and GPIO were connected to oscilloscope. - Run a small test program on the m53evk board that will only open and read data from ttymxc2 port. - Connect the ttymxc2 port to my laptop using a USB serial converter where another test program is running, able to send configurable packet lengths and intervals. - Serial ports configured at 115200 8N1. - Interrupts load created by disconnecting/connecting (3s interval) a USB hub, using a digital switch, with 4 USB devices (USB-Serial converter, USB SD card, etc) connected. (around 160 interrupts/second generated) - The GPIO was toggled HI in the `imx_int` when USR1_RRDY or USR1_AGTIM events are received and toggled back, once the DMA configuration is finalized, at the end of `imx_dma_rxint`. Measurements: The measurements were done from the end of the last byte (RX line) until the GPIO was toggled back LOW. Note: The GPIO toggling was done using `gpiod_set_value` method. Tests performed: 1. Sending 9 bytes packets at 8ms interval. Having the 9 bytes packets will activate the RRDY threshold event and IMX serial interrupt called. Results: - DMA start latency (interrupt start latency + DMA configuration) consistently 70us when system not loaded. - DMA start latency up to 4400us when system loaded. 2. Sending 40 bytes packet at 8mS interval. Results with load: - Able to observe overruns by running: `watch -n1 cat /proc/tty/driver/IMX-uart` Tested-by: Peter Senna Tschudin <peter.senna@collabora.com> Acked-by: Peter Senna Tschudin <peter.senna@collabora.com> Signed-off-by: Nandor Han <nandor.han@ge.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2016-08-08 15:38:27 +03:00
if (rx_ring->head <= sg_dma_len(sgl) &&
rx_ring->head > rx_ring->tail) {
serial: imx-serial - update UART IMX driver to use cyclic DMA The IMX UART has a 32 bytes HW buffer which can be filled up in 2777us at 115200 baud or 80us at 4Mbaud (supported by IMX53). Taking this in consideration there is a good probability to lose data because of the DMA startup latency. Our tests (explained below) indicates a latency up to 4400us when creating interrupt load and ~70us without. When creating interrupt load I was able to see continuous overrun errors by checking serial driver statistics using the command: `cat /proc/tty/driver/IMX-uart`. Replace manual restart of DMA with cyclic DMA to eliminate data loss due to DMA engine startup latency (similar approch to atmel_serial.c driver). As result the DMA engine will start on the first serial data transfer and stops only when serial port is closed. Tests environment: Using the m53evk board I have used a GPIO for profiling the IMX serial driver. - The RX line and GPIO were connected to oscilloscope. - Run a small test program on the m53evk board that will only open and read data from ttymxc2 port. - Connect the ttymxc2 port to my laptop using a USB serial converter where another test program is running, able to send configurable packet lengths and intervals. - Serial ports configured at 115200 8N1. - Interrupts load created by disconnecting/connecting (3s interval) a USB hub, using a digital switch, with 4 USB devices (USB-Serial converter, USB SD card, etc) connected. (around 160 interrupts/second generated) - The GPIO was toggled HI in the `imx_int` when USR1_RRDY or USR1_AGTIM events are received and toggled back, once the DMA configuration is finalized, at the end of `imx_dma_rxint`. Measurements: The measurements were done from the end of the last byte (RX line) until the GPIO was toggled back LOW. Note: The GPIO toggling was done using `gpiod_set_value` method. Tests performed: 1. Sending 9 bytes packets at 8ms interval. Having the 9 bytes packets will activate the RRDY threshold event and IMX serial interrupt called. Results: - DMA start latency (interrupt start latency + DMA configuration) consistently 70us when system not loaded. - DMA start latency up to 4400us when system loaded. 2. Sending 40 bytes packet at 8mS interval. Results with load: - Able to observe overruns by running: `watch -n1 cat /proc/tty/driver/IMX-uart` Tested-by: Peter Senna Tschudin <peter.senna@collabora.com> Acked-by: Peter Senna Tschudin <peter.senna@collabora.com> Signed-off-by: Nandor Han <nandor.han@ge.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2016-08-08 15:38:27 +03:00
/* Move data from tail to head */
r_bytes = rx_ring->head - rx_ring->tail;
serial: imx-serial - update UART IMX driver to use cyclic DMA The IMX UART has a 32 bytes HW buffer which can be filled up in 2777us at 115200 baud or 80us at 4Mbaud (supported by IMX53). Taking this in consideration there is a good probability to lose data because of the DMA startup latency. Our tests (explained below) indicates a latency up to 4400us when creating interrupt load and ~70us without. When creating interrupt load I was able to see continuous overrun errors by checking serial driver statistics using the command: `cat /proc/tty/driver/IMX-uart`. Replace manual restart of DMA with cyclic DMA to eliminate data loss due to DMA engine startup latency (similar approch to atmel_serial.c driver). As result the DMA engine will start on the first serial data transfer and stops only when serial port is closed. Tests environment: Using the m53evk board I have used a GPIO for profiling the IMX serial driver. - The RX line and GPIO were connected to oscilloscope. - Run a small test program on the m53evk board that will only open and read data from ttymxc2 port. - Connect the ttymxc2 port to my laptop using a USB serial converter where another test program is running, able to send configurable packet lengths and intervals. - Serial ports configured at 115200 8N1. - Interrupts load created by disconnecting/connecting (3s interval) a USB hub, using a digital switch, with 4 USB devices (USB-Serial converter, USB SD card, etc) connected. (around 160 interrupts/second generated) - The GPIO was toggled HI in the `imx_int` when USR1_RRDY or USR1_AGTIM events are received and toggled back, once the DMA configuration is finalized, at the end of `imx_dma_rxint`. Measurements: The measurements were done from the end of the last byte (RX line) until the GPIO was toggled back LOW. Note: The GPIO toggling was done using `gpiod_set_value` method. Tests performed: 1. Sending 9 bytes packets at 8ms interval. Having the 9 bytes packets will activate the RRDY threshold event and IMX serial interrupt called. Results: - DMA start latency (interrupt start latency + DMA configuration) consistently 70us when system not loaded. - DMA start latency up to 4400us when system loaded. 2. Sending 40 bytes packet at 8mS interval. Results with load: - Able to observe overruns by running: `watch -n1 cat /proc/tty/driver/IMX-uart` Tested-by: Peter Senna Tschudin <peter.senna@collabora.com> Acked-by: Peter Senna Tschudin <peter.senna@collabora.com> Signed-off-by: Nandor Han <nandor.han@ge.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2016-08-08 15:38:27 +03:00
/* If we received something, check for 0xff flood */
uart_port_lock(&sport->port);
imx_uart_check_flood(sport, imx_uart_readl(sport, USR2));
uart_port_unlock(&sport->port);
if (!(sport->port.ignore_status_mask & URXD_DUMMY_READ)) {
serial: imx-serial - update UART IMX driver to use cyclic DMA The IMX UART has a 32 bytes HW buffer which can be filled up in 2777us at 115200 baud or 80us at 4Mbaud (supported by IMX53). Taking this in consideration there is a good probability to lose data because of the DMA startup latency. Our tests (explained below) indicates a latency up to 4400us when creating interrupt load and ~70us without. When creating interrupt load I was able to see continuous overrun errors by checking serial driver statistics using the command: `cat /proc/tty/driver/IMX-uart`. Replace manual restart of DMA with cyclic DMA to eliminate data loss due to DMA engine startup latency (similar approch to atmel_serial.c driver). As result the DMA engine will start on the first serial data transfer and stops only when serial port is closed. Tests environment: Using the m53evk board I have used a GPIO for profiling the IMX serial driver. - The RX line and GPIO were connected to oscilloscope. - Run a small test program on the m53evk board that will only open and read data from ttymxc2 port. - Connect the ttymxc2 port to my laptop using a USB serial converter where another test program is running, able to send configurable packet lengths and intervals. - Serial ports configured at 115200 8N1. - Interrupts load created by disconnecting/connecting (3s interval) a USB hub, using a digital switch, with 4 USB devices (USB-Serial converter, USB SD card, etc) connected. (around 160 interrupts/second generated) - The GPIO was toggled HI in the `imx_int` when USR1_RRDY or USR1_AGTIM events are received and toggled back, once the DMA configuration is finalized, at the end of `imx_dma_rxint`. Measurements: The measurements were done from the end of the last byte (RX line) until the GPIO was toggled back LOW. Note: The GPIO toggling was done using `gpiod_set_value` method. Tests performed: 1. Sending 9 bytes packets at 8ms interval. Having the 9 bytes packets will activate the RRDY threshold event and IMX serial interrupt called. Results: - DMA start latency (interrupt start latency + DMA configuration) consistently 70us when system not loaded. - DMA start latency up to 4400us when system loaded. 2. Sending 40 bytes packet at 8mS interval. Results with load: - Able to observe overruns by running: `watch -n1 cat /proc/tty/driver/IMX-uart` Tested-by: Peter Senna Tschudin <peter.senna@collabora.com> Acked-by: Peter Senna Tschudin <peter.senna@collabora.com> Signed-off-by: Nandor Han <nandor.han@ge.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2016-08-08 15:38:27 +03:00
/* CPU claims ownership of RX DMA buffer */
dma_sync_sg_for_cpu(sport->port.dev, sgl, 1,
DMA_FROM_DEVICE);
serial: imx-serial - update UART IMX driver to use cyclic DMA The IMX UART has a 32 bytes HW buffer which can be filled up in 2777us at 115200 baud or 80us at 4Mbaud (supported by IMX53). Taking this in consideration there is a good probability to lose data because of the DMA startup latency. Our tests (explained below) indicates a latency up to 4400us when creating interrupt load and ~70us without. When creating interrupt load I was able to see continuous overrun errors by checking serial driver statistics using the command: `cat /proc/tty/driver/IMX-uart`. Replace manual restart of DMA with cyclic DMA to eliminate data loss due to DMA engine startup latency (similar approch to atmel_serial.c driver). As result the DMA engine will start on the first serial data transfer and stops only when serial port is closed. Tests environment: Using the m53evk board I have used a GPIO for profiling the IMX serial driver. - The RX line and GPIO were connected to oscilloscope. - Run a small test program on the m53evk board that will only open and read data from ttymxc2 port. - Connect the ttymxc2 port to my laptop using a USB serial converter where another test program is running, able to send configurable packet lengths and intervals. - Serial ports configured at 115200 8N1. - Interrupts load created by disconnecting/connecting (3s interval) a USB hub, using a digital switch, with 4 USB devices (USB-Serial converter, USB SD card, etc) connected. (around 160 interrupts/second generated) - The GPIO was toggled HI in the `imx_int` when USR1_RRDY or USR1_AGTIM events are received and toggled back, once the DMA configuration is finalized, at the end of `imx_dma_rxint`. Measurements: The measurements were done from the end of the last byte (RX line) until the GPIO was toggled back LOW. Note: The GPIO toggling was done using `gpiod_set_value` method. Tests performed: 1. Sending 9 bytes packets at 8ms interval. Having the 9 bytes packets will activate the RRDY threshold event and IMX serial interrupt called. Results: - DMA start latency (interrupt start latency + DMA configuration) consistently 70us when system not loaded. - DMA start latency up to 4400us when system loaded. 2. Sending 40 bytes packet at 8mS interval. Results with load: - Able to observe overruns by running: `watch -n1 cat /proc/tty/driver/IMX-uart` Tested-by: Peter Senna Tschudin <peter.senna@collabora.com> Acked-by: Peter Senna Tschudin <peter.senna@collabora.com> Signed-off-by: Nandor Han <nandor.han@ge.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2016-08-08 15:38:27 +03:00
w_bytes = tty_insert_flip_string(port,
sport->rx_buf + rx_ring->tail, r_bytes);
serial: imx-serial - update UART IMX driver to use cyclic DMA The IMX UART has a 32 bytes HW buffer which can be filled up in 2777us at 115200 baud or 80us at 4Mbaud (supported by IMX53). Taking this in consideration there is a good probability to lose data because of the DMA startup latency. Our tests (explained below) indicates a latency up to 4400us when creating interrupt load and ~70us without. When creating interrupt load I was able to see continuous overrun errors by checking serial driver statistics using the command: `cat /proc/tty/driver/IMX-uart`. Replace manual restart of DMA with cyclic DMA to eliminate data loss due to DMA engine startup latency (similar approch to atmel_serial.c driver). As result the DMA engine will start on the first serial data transfer and stops only when serial port is closed. Tests environment: Using the m53evk board I have used a GPIO for profiling the IMX serial driver. - The RX line and GPIO were connected to oscilloscope. - Run a small test program on the m53evk board that will only open and read data from ttymxc2 port. - Connect the ttymxc2 port to my laptop using a USB serial converter where another test program is running, able to send configurable packet lengths and intervals. - Serial ports configured at 115200 8N1. - Interrupts load created by disconnecting/connecting (3s interval) a USB hub, using a digital switch, with 4 USB devices (USB-Serial converter, USB SD card, etc) connected. (around 160 interrupts/second generated) - The GPIO was toggled HI in the `imx_int` when USR1_RRDY or USR1_AGTIM events are received and toggled back, once the DMA configuration is finalized, at the end of `imx_dma_rxint`. Measurements: The measurements were done from the end of the last byte (RX line) until the GPIO was toggled back LOW. Note: The GPIO toggling was done using `gpiod_set_value` method. Tests performed: 1. Sending 9 bytes packets at 8ms interval. Having the 9 bytes packets will activate the RRDY threshold event and IMX serial interrupt called. Results: - DMA start latency (interrupt start latency + DMA configuration) consistently 70us when system not loaded. - DMA start latency up to 4400us when system loaded. 2. Sending 40 bytes packet at 8mS interval. Results with load: - Able to observe overruns by running: `watch -n1 cat /proc/tty/driver/IMX-uart` Tested-by: Peter Senna Tschudin <peter.senna@collabora.com> Acked-by: Peter Senna Tschudin <peter.senna@collabora.com> Signed-off-by: Nandor Han <nandor.han@ge.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2016-08-08 15:38:27 +03:00
/* UART retrieves ownership of RX DMA buffer */
dma_sync_sg_for_device(sport->port.dev, sgl, 1,
DMA_FROM_DEVICE);
serial: imx-serial - update UART IMX driver to use cyclic DMA The IMX UART has a 32 bytes HW buffer which can be filled up in 2777us at 115200 baud or 80us at 4Mbaud (supported by IMX53). Taking this in consideration there is a good probability to lose data because of the DMA startup latency. Our tests (explained below) indicates a latency up to 4400us when creating interrupt load and ~70us without. When creating interrupt load I was able to see continuous overrun errors by checking serial driver statistics using the command: `cat /proc/tty/driver/IMX-uart`. Replace manual restart of DMA with cyclic DMA to eliminate data loss due to DMA engine startup latency (similar approch to atmel_serial.c driver). As result the DMA engine will start on the first serial data transfer and stops only when serial port is closed. Tests environment: Using the m53evk board I have used a GPIO for profiling the IMX serial driver. - The RX line and GPIO were connected to oscilloscope. - Run a small test program on the m53evk board that will only open and read data from ttymxc2 port. - Connect the ttymxc2 port to my laptop using a USB serial converter where another test program is running, able to send configurable packet lengths and intervals. - Serial ports configured at 115200 8N1. - Interrupts load created by disconnecting/connecting (3s interval) a USB hub, using a digital switch, with 4 USB devices (USB-Serial converter, USB SD card, etc) connected. (around 160 interrupts/second generated) - The GPIO was toggled HI in the `imx_int` when USR1_RRDY or USR1_AGTIM events are received and toggled back, once the DMA configuration is finalized, at the end of `imx_dma_rxint`. Measurements: The measurements were done from the end of the last byte (RX line) until the GPIO was toggled back LOW. Note: The GPIO toggling was done using `gpiod_set_value` method. Tests performed: 1. Sending 9 bytes packets at 8ms interval. Having the 9 bytes packets will activate the RRDY threshold event and IMX serial interrupt called. Results: - DMA start latency (interrupt start latency + DMA configuration) consistently 70us when system not loaded. - DMA start latency up to 4400us when system loaded. 2. Sending 40 bytes packet at 8mS interval. Results with load: - Able to observe overruns by running: `watch -n1 cat /proc/tty/driver/IMX-uart` Tested-by: Peter Senna Tschudin <peter.senna@collabora.com> Acked-by: Peter Senna Tschudin <peter.senna@collabora.com> Signed-off-by: Nandor Han <nandor.han@ge.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2016-08-08 15:38:27 +03:00
if (w_bytes != r_bytes)
sport->port.icount.buf_overrun++;
serial: imx-serial - update UART IMX driver to use cyclic DMA The IMX UART has a 32 bytes HW buffer which can be filled up in 2777us at 115200 baud or 80us at 4Mbaud (supported by IMX53). Taking this in consideration there is a good probability to lose data because of the DMA startup latency. Our tests (explained below) indicates a latency up to 4400us when creating interrupt load and ~70us without. When creating interrupt load I was able to see continuous overrun errors by checking serial driver statistics using the command: `cat /proc/tty/driver/IMX-uart`. Replace manual restart of DMA with cyclic DMA to eliminate data loss due to DMA engine startup latency (similar approch to atmel_serial.c driver). As result the DMA engine will start on the first serial data transfer and stops only when serial port is closed. Tests environment: Using the m53evk board I have used a GPIO for profiling the IMX serial driver. - The RX line and GPIO were connected to oscilloscope. - Run a small test program on the m53evk board that will only open and read data from ttymxc2 port. - Connect the ttymxc2 port to my laptop using a USB serial converter where another test program is running, able to send configurable packet lengths and intervals. - Serial ports configured at 115200 8N1. - Interrupts load created by disconnecting/connecting (3s interval) a USB hub, using a digital switch, with 4 USB devices (USB-Serial converter, USB SD card, etc) connected. (around 160 interrupts/second generated) - The GPIO was toggled HI in the `imx_int` when USR1_RRDY or USR1_AGTIM events are received and toggled back, once the DMA configuration is finalized, at the end of `imx_dma_rxint`. Measurements: The measurements were done from the end of the last byte (RX line) until the GPIO was toggled back LOW. Note: The GPIO toggling was done using `gpiod_set_value` method. Tests performed: 1. Sending 9 bytes packets at 8ms interval. Having the 9 bytes packets will activate the RRDY threshold event and IMX serial interrupt called. Results: - DMA start latency (interrupt start latency + DMA configuration) consistently 70us when system not loaded. - DMA start latency up to 4400us when system loaded. 2. Sending 40 bytes packet at 8mS interval. Results with load: - Able to observe overruns by running: `watch -n1 cat /proc/tty/driver/IMX-uart` Tested-by: Peter Senna Tschudin <peter.senna@collabora.com> Acked-by: Peter Senna Tschudin <peter.senna@collabora.com> Signed-off-by: Nandor Han <nandor.han@ge.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2016-08-08 15:38:27 +03:00
sport->port.icount.rx += w_bytes;
}
} else {
WARN_ON(rx_ring->head > sg_dma_len(sgl));
WARN_ON(rx_ring->head <= rx_ring->tail);
}
serial: imx-serial - update UART IMX driver to use cyclic DMA The IMX UART has a 32 bytes HW buffer which can be filled up in 2777us at 115200 baud or 80us at 4Mbaud (supported by IMX53). Taking this in consideration there is a good probability to lose data because of the DMA startup latency. Our tests (explained below) indicates a latency up to 4400us when creating interrupt load and ~70us without. When creating interrupt load I was able to see continuous overrun errors by checking serial driver statistics using the command: `cat /proc/tty/driver/IMX-uart`. Replace manual restart of DMA with cyclic DMA to eliminate data loss due to DMA engine startup latency (similar approch to atmel_serial.c driver). As result the DMA engine will start on the first serial data transfer and stops only when serial port is closed. Tests environment: Using the m53evk board I have used a GPIO for profiling the IMX serial driver. - The RX line and GPIO were connected to oscilloscope. - Run a small test program on the m53evk board that will only open and read data from ttymxc2 port. - Connect the ttymxc2 port to my laptop using a USB serial converter where another test program is running, able to send configurable packet lengths and intervals. - Serial ports configured at 115200 8N1. - Interrupts load created by disconnecting/connecting (3s interval) a USB hub, using a digital switch, with 4 USB devices (USB-Serial converter, USB SD card, etc) connected. (around 160 interrupts/second generated) - The GPIO was toggled HI in the `imx_int` when USR1_RRDY or USR1_AGTIM events are received and toggled back, once the DMA configuration is finalized, at the end of `imx_dma_rxint`. Measurements: The measurements were done from the end of the last byte (RX line) until the GPIO was toggled back LOW. Note: The GPIO toggling was done using `gpiod_set_value` method. Tests performed: 1. Sending 9 bytes packets at 8ms interval. Having the 9 bytes packets will activate the RRDY threshold event and IMX serial interrupt called. Results: - DMA start latency (interrupt start latency + DMA configuration) consistently 70us when system not loaded. - DMA start latency up to 4400us when system loaded. 2. Sending 40 bytes packet at 8mS interval. Results with load: - Able to observe overruns by running: `watch -n1 cat /proc/tty/driver/IMX-uart` Tested-by: Peter Senna Tschudin <peter.senna@collabora.com> Acked-by: Peter Senna Tschudin <peter.senna@collabora.com> Signed-off-by: Nandor Han <nandor.han@ge.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2016-08-08 15:38:27 +03:00
if (w_bytes) {
tty_flip_buffer_push(port);
dev_dbg(sport->port.dev, "We get %d bytes.\n", w_bytes);
}
}
static int imx_uart_start_rx_dma(struct imx_port *sport)
{
struct scatterlist *sgl = &sport->rx_sgl;
struct dma_chan *chan = sport->dma_chan_rx;
struct device *dev = sport->port.dev;
struct dma_async_tx_descriptor *desc;
int ret;
serial: imx-serial - update UART IMX driver to use cyclic DMA The IMX UART has a 32 bytes HW buffer which can be filled up in 2777us at 115200 baud or 80us at 4Mbaud (supported by IMX53). Taking this in consideration there is a good probability to lose data because of the DMA startup latency. Our tests (explained below) indicates a latency up to 4400us when creating interrupt load and ~70us without. When creating interrupt load I was able to see continuous overrun errors by checking serial driver statistics using the command: `cat /proc/tty/driver/IMX-uart`. Replace manual restart of DMA with cyclic DMA to eliminate data loss due to DMA engine startup latency (similar approch to atmel_serial.c driver). As result the DMA engine will start on the first serial data transfer and stops only when serial port is closed. Tests environment: Using the m53evk board I have used a GPIO for profiling the IMX serial driver. - The RX line and GPIO were connected to oscilloscope. - Run a small test program on the m53evk board that will only open and read data from ttymxc2 port. - Connect the ttymxc2 port to my laptop using a USB serial converter where another test program is running, able to send configurable packet lengths and intervals. - Serial ports configured at 115200 8N1. - Interrupts load created by disconnecting/connecting (3s interval) a USB hub, using a digital switch, with 4 USB devices (USB-Serial converter, USB SD card, etc) connected. (around 160 interrupts/second generated) - The GPIO was toggled HI in the `imx_int` when USR1_RRDY or USR1_AGTIM events are received and toggled back, once the DMA configuration is finalized, at the end of `imx_dma_rxint`. Measurements: The measurements were done from the end of the last byte (RX line) until the GPIO was toggled back LOW. Note: The GPIO toggling was done using `gpiod_set_value` method. Tests performed: 1. Sending 9 bytes packets at 8ms interval. Having the 9 bytes packets will activate the RRDY threshold event and IMX serial interrupt called. Results: - DMA start latency (interrupt start latency + DMA configuration) consistently 70us when system not loaded. - DMA start latency up to 4400us when system loaded. 2. Sending 40 bytes packet at 8mS interval. Results with load: - Able to observe overruns by running: `watch -n1 cat /proc/tty/driver/IMX-uart` Tested-by: Peter Senna Tschudin <peter.senna@collabora.com> Acked-by: Peter Senna Tschudin <peter.senna@collabora.com> Signed-off-by: Nandor Han <nandor.han@ge.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2016-08-08 15:38:27 +03:00
sport->rx_ring.head = 0;
sport->rx_ring.tail = 0;
sg_init_one(sgl, sport->rx_buf, sport->rx_buf_size);
ret = dma_map_sg(dev, sgl, 1, DMA_FROM_DEVICE);
if (ret == 0) {
dev_err(dev, "DMA mapping error for RX.\n");
return -EINVAL;
}
serial: imx-serial - update UART IMX driver to use cyclic DMA The IMX UART has a 32 bytes HW buffer which can be filled up in 2777us at 115200 baud or 80us at 4Mbaud (supported by IMX53). Taking this in consideration there is a good probability to lose data because of the DMA startup latency. Our tests (explained below) indicates a latency up to 4400us when creating interrupt load and ~70us without. When creating interrupt load I was able to see continuous overrun errors by checking serial driver statistics using the command: `cat /proc/tty/driver/IMX-uart`. Replace manual restart of DMA with cyclic DMA to eliminate data loss due to DMA engine startup latency (similar approch to atmel_serial.c driver). As result the DMA engine will start on the first serial data transfer and stops only when serial port is closed. Tests environment: Using the m53evk board I have used a GPIO for profiling the IMX serial driver. - The RX line and GPIO were connected to oscilloscope. - Run a small test program on the m53evk board that will only open and read data from ttymxc2 port. - Connect the ttymxc2 port to my laptop using a USB serial converter where another test program is running, able to send configurable packet lengths and intervals. - Serial ports configured at 115200 8N1. - Interrupts load created by disconnecting/connecting (3s interval) a USB hub, using a digital switch, with 4 USB devices (USB-Serial converter, USB SD card, etc) connected. (around 160 interrupts/second generated) - The GPIO was toggled HI in the `imx_int` when USR1_RRDY or USR1_AGTIM events are received and toggled back, once the DMA configuration is finalized, at the end of `imx_dma_rxint`. Measurements: The measurements were done from the end of the last byte (RX line) until the GPIO was toggled back LOW. Note: The GPIO toggling was done using `gpiod_set_value` method. Tests performed: 1. Sending 9 bytes packets at 8ms interval. Having the 9 bytes packets will activate the RRDY threshold event and IMX serial interrupt called. Results: - DMA start latency (interrupt start latency + DMA configuration) consistently 70us when system not loaded. - DMA start latency up to 4400us when system loaded. 2. Sending 40 bytes packet at 8mS interval. Results with load: - Able to observe overruns by running: `watch -n1 cat /proc/tty/driver/IMX-uart` Tested-by: Peter Senna Tschudin <peter.senna@collabora.com> Acked-by: Peter Senna Tschudin <peter.senna@collabora.com> Signed-off-by: Nandor Han <nandor.han@ge.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2016-08-08 15:38:27 +03:00
desc = dmaengine_prep_dma_cyclic(chan, sg_dma_address(sgl),
sg_dma_len(sgl), sg_dma_len(sgl) / sport->rx_periods,
DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT);
if (!desc) {
dma_unmap_sg(dev, sgl, 1, DMA_FROM_DEVICE);
dev_err(dev, "We cannot prepare for the RX slave dma!\n");
return -EINVAL;
}
desc->callback = imx_uart_dma_rx_callback;
desc->callback_param = sport;
dev_dbg(dev, "RX: prepare for the DMA.\n");
sport->dma_is_rxing = 1;
serial: imx-serial - update UART IMX driver to use cyclic DMA The IMX UART has a 32 bytes HW buffer which can be filled up in 2777us at 115200 baud or 80us at 4Mbaud (supported by IMX53). Taking this in consideration there is a good probability to lose data because of the DMA startup latency. Our tests (explained below) indicates a latency up to 4400us when creating interrupt load and ~70us without. When creating interrupt load I was able to see continuous overrun errors by checking serial driver statistics using the command: `cat /proc/tty/driver/IMX-uart`. Replace manual restart of DMA with cyclic DMA to eliminate data loss due to DMA engine startup latency (similar approch to atmel_serial.c driver). As result the DMA engine will start on the first serial data transfer and stops only when serial port is closed. Tests environment: Using the m53evk board I have used a GPIO for profiling the IMX serial driver. - The RX line and GPIO were connected to oscilloscope. - Run a small test program on the m53evk board that will only open and read data from ttymxc2 port. - Connect the ttymxc2 port to my laptop using a USB serial converter where another test program is running, able to send configurable packet lengths and intervals. - Serial ports configured at 115200 8N1. - Interrupts load created by disconnecting/connecting (3s interval) a USB hub, using a digital switch, with 4 USB devices (USB-Serial converter, USB SD card, etc) connected. (around 160 interrupts/second generated) - The GPIO was toggled HI in the `imx_int` when USR1_RRDY or USR1_AGTIM events are received and toggled back, once the DMA configuration is finalized, at the end of `imx_dma_rxint`. Measurements: The measurements were done from the end of the last byte (RX line) until the GPIO was toggled back LOW. Note: The GPIO toggling was done using `gpiod_set_value` method. Tests performed: 1. Sending 9 bytes packets at 8ms interval. Having the 9 bytes packets will activate the RRDY threshold event and IMX serial interrupt called. Results: - DMA start latency (interrupt start latency + DMA configuration) consistently 70us when system not loaded. - DMA start latency up to 4400us when system loaded. 2. Sending 40 bytes packet at 8mS interval. Results with load: - Able to observe overruns by running: `watch -n1 cat /proc/tty/driver/IMX-uart` Tested-by: Peter Senna Tschudin <peter.senna@collabora.com> Acked-by: Peter Senna Tschudin <peter.senna@collabora.com> Signed-off-by: Nandor Han <nandor.han@ge.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2016-08-08 15:38:27 +03:00
sport->rx_cookie = dmaengine_submit(desc);
dma_async_issue_pending(chan);
return 0;
}
/* called with port.lock taken and irqs off */
static void imx_uart_clear_rx_errors(struct imx_port *sport)
{
struct tty_port *port = &sport->port.state->port;
u32 usr1, usr2;
usr1 = imx_uart_readl(sport, USR1);
usr2 = imx_uart_readl(sport, USR2);
if (usr2 & USR2_BRCD) {
sport->port.icount.brk++;
imx_uart_writel(sport, USR2_BRCD, USR2);
uart_handle_break(&sport->port);
if (tty_insert_flip_char(port, 0, TTY_BREAK) == 0)
sport->port.icount.buf_overrun++;
tty_flip_buffer_push(port);
} else {
if (usr1 & USR1_FRAMERR) {
sport->port.icount.frame++;
imx_uart_writel(sport, USR1_FRAMERR, USR1);
} else if (usr1 & USR1_PARITYERR) {
sport->port.icount.parity++;
imx_uart_writel(sport, USR1_PARITYERR, USR1);
}
}
if (usr2 & USR2_ORE) {
sport->port.icount.overrun++;
imx_uart_writel(sport, USR2_ORE, USR2);
}
sport->idle_counter = 0;
}
#define TXTL_DEFAULT 8
#define RXTL_DEFAULT 8 /* 8 characters or aging timer */
#define RXTL_CONSOLE_DEFAULT 1
#define TXTL_DMA 8 /* DMA burst setting */
#define RXTL_DMA 9 /* DMA burst setting */
static void imx_uart_setup_ufcr(struct imx_port *sport,
unsigned char txwl, unsigned char rxwl)
{
unsigned int val;
/* set receiver / transmitter trigger level */
val = imx_uart_readl(sport, UFCR) & (UFCR_RFDIV | UFCR_DCEDTE);
val |= txwl << UFCR_TXTL_SHF | rxwl;
imx_uart_writel(sport, val, UFCR);
}
static void imx_uart_dma_exit(struct imx_port *sport)
{
if (sport->dma_chan_rx) {
dmaengine_terminate_sync(sport->dma_chan_rx);
dma_release_channel(sport->dma_chan_rx);
sport->dma_chan_rx = NULL;
serial: imx-serial - update UART IMX driver to use cyclic DMA The IMX UART has a 32 bytes HW buffer which can be filled up in 2777us at 115200 baud or 80us at 4Mbaud (supported by IMX53). Taking this in consideration there is a good probability to lose data because of the DMA startup latency. Our tests (explained below) indicates a latency up to 4400us when creating interrupt load and ~70us without. When creating interrupt load I was able to see continuous overrun errors by checking serial driver statistics using the command: `cat /proc/tty/driver/IMX-uart`. Replace manual restart of DMA with cyclic DMA to eliminate data loss due to DMA engine startup latency (similar approch to atmel_serial.c driver). As result the DMA engine will start on the first serial data transfer and stops only when serial port is closed. Tests environment: Using the m53evk board I have used a GPIO for profiling the IMX serial driver. - The RX line and GPIO were connected to oscilloscope. - Run a small test program on the m53evk board that will only open and read data from ttymxc2 port. - Connect the ttymxc2 port to my laptop using a USB serial converter where another test program is running, able to send configurable packet lengths and intervals. - Serial ports configured at 115200 8N1. - Interrupts load created by disconnecting/connecting (3s interval) a USB hub, using a digital switch, with 4 USB devices (USB-Serial converter, USB SD card, etc) connected. (around 160 interrupts/second generated) - The GPIO was toggled HI in the `imx_int` when USR1_RRDY or USR1_AGTIM events are received and toggled back, once the DMA configuration is finalized, at the end of `imx_dma_rxint`. Measurements: The measurements were done from the end of the last byte (RX line) until the GPIO was toggled back LOW. Note: The GPIO toggling was done using `gpiod_set_value` method. Tests performed: 1. Sending 9 bytes packets at 8ms interval. Having the 9 bytes packets will activate the RRDY threshold event and IMX serial interrupt called. Results: - DMA start latency (interrupt start latency + DMA configuration) consistently 70us when system not loaded. - DMA start latency up to 4400us when system loaded. 2. Sending 40 bytes packet at 8mS interval. Results with load: - Able to observe overruns by running: `watch -n1 cat /proc/tty/driver/IMX-uart` Tested-by: Peter Senna Tschudin <peter.senna@collabora.com> Acked-by: Peter Senna Tschudin <peter.senna@collabora.com> Signed-off-by: Nandor Han <nandor.han@ge.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2016-08-08 15:38:27 +03:00
sport->rx_cookie = -EINVAL;
kfree(sport->rx_buf);
sport->rx_buf = NULL;
}
if (sport->dma_chan_tx) {
dmaengine_terminate_sync(sport->dma_chan_tx);
dma_release_channel(sport->dma_chan_tx);
sport->dma_chan_tx = NULL;
}
}
static int imx_uart_dma_init(struct imx_port *sport)
{
struct dma_slave_config slave_config = {};
struct device *dev = sport->port.dev;
struct dma_chan *chan;
int ret;
/* Prepare for RX : */
chan = dma_request_chan(dev, "rx");
if (IS_ERR(chan)) {
dev_dbg(dev, "cannot get the DMA channel.\n");
sport->dma_chan_rx = NULL;
ret = PTR_ERR(chan);
goto err;
}
sport->dma_chan_rx = chan;
slave_config.direction = DMA_DEV_TO_MEM;
slave_config.src_addr = sport->port.mapbase + URXD0;
slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
/* one byte less than the watermark level to enable the aging timer */
slave_config.src_maxburst = RXTL_DMA - 1;
ret = dmaengine_slave_config(sport->dma_chan_rx, &slave_config);
if (ret) {
dev_err(dev, "error in RX dma configuration.\n");
goto err;
}
sport->rx_buf_size = sport->rx_period_length * sport->rx_periods;
sport->rx_buf = kzalloc(sport->rx_buf_size, GFP_KERNEL);
if (!sport->rx_buf) {
ret = -ENOMEM;
goto err;
}
serial: imx-serial - update UART IMX driver to use cyclic DMA The IMX UART has a 32 bytes HW buffer which can be filled up in 2777us at 115200 baud or 80us at 4Mbaud (supported by IMX53). Taking this in consideration there is a good probability to lose data because of the DMA startup latency. Our tests (explained below) indicates a latency up to 4400us when creating interrupt load and ~70us without. When creating interrupt load I was able to see continuous overrun errors by checking serial driver statistics using the command: `cat /proc/tty/driver/IMX-uart`. Replace manual restart of DMA with cyclic DMA to eliminate data loss due to DMA engine startup latency (similar approch to atmel_serial.c driver). As result the DMA engine will start on the first serial data transfer and stops only when serial port is closed. Tests environment: Using the m53evk board I have used a GPIO for profiling the IMX serial driver. - The RX line and GPIO were connected to oscilloscope. - Run a small test program on the m53evk board that will only open and read data from ttymxc2 port. - Connect the ttymxc2 port to my laptop using a USB serial converter where another test program is running, able to send configurable packet lengths and intervals. - Serial ports configured at 115200 8N1. - Interrupts load created by disconnecting/connecting (3s interval) a USB hub, using a digital switch, with 4 USB devices (USB-Serial converter, USB SD card, etc) connected. (around 160 interrupts/second generated) - The GPIO was toggled HI in the `imx_int` when USR1_RRDY or USR1_AGTIM events are received and toggled back, once the DMA configuration is finalized, at the end of `imx_dma_rxint`. Measurements: The measurements were done from the end of the last byte (RX line) until the GPIO was toggled back LOW. Note: The GPIO toggling was done using `gpiod_set_value` method. Tests performed: 1. Sending 9 bytes packets at 8ms interval. Having the 9 bytes packets will activate the RRDY threshold event and IMX serial interrupt called. Results: - DMA start latency (interrupt start latency + DMA configuration) consistently 70us when system not loaded. - DMA start latency up to 4400us when system loaded. 2. Sending 40 bytes packet at 8mS interval. Results with load: - Able to observe overruns by running: `watch -n1 cat /proc/tty/driver/IMX-uart` Tested-by: Peter Senna Tschudin <peter.senna@collabora.com> Acked-by: Peter Senna Tschudin <peter.senna@collabora.com> Signed-off-by: Nandor Han <nandor.han@ge.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2016-08-08 15:38:27 +03:00
sport->rx_ring.buf = sport->rx_buf;
/* Prepare for TX : */
chan = dma_request_chan(dev, "tx");
if (IS_ERR(chan)) {
dev_err(dev, "cannot get the TX DMA channel!\n");
sport->dma_chan_tx = NULL;
ret = PTR_ERR(chan);
goto err;
}
sport->dma_chan_tx = chan;
slave_config.direction = DMA_MEM_TO_DEV;
slave_config.dst_addr = sport->port.mapbase + URTX0;
slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
slave_config.dst_maxburst = TXTL_DMA;
ret = dmaengine_slave_config(sport->dma_chan_tx, &slave_config);
if (ret) {
dev_err(dev, "error in TX dma configuration.");
goto err;
}
return 0;
err:
imx_uart_dma_exit(sport);
return ret;
}
/* called with port.lock taken and irqs off */
static void imx_uart_enable_dma(struct imx_port *sport)
{
u32 ucr1;
imx_uart_setup_ufcr(sport, TXTL_DMA, RXTL_DMA);
/* set UCR1 */
ucr1 = imx_uart_readl(sport, UCR1);
ucr1 |= UCR1_RXDMAEN | UCR1_TXDMAEN | UCR1_ATDMAEN;
imx_uart_writel(sport, ucr1, UCR1);
sport->dma_is_enabled = 1;
}
static void imx_uart_disable_dma(struct imx_port *sport)
{
u32 ucr1;
/* clear UCR1 */
ucr1 = imx_uart_readl(sport, UCR1);
ucr1 &= ~(UCR1_RXDMAEN | UCR1_TXDMAEN | UCR1_ATDMAEN);
imx_uart_writel(sport, ucr1, UCR1);
imx_uart_setup_ufcr(sport, TXTL_DEFAULT, sport->rxtl);
sport->dma_is_enabled = 0;
}
/* half the RX buffer size */
#define CTSTL 16
static int imx_uart_startup(struct uart_port *port)
{
struct imx_port *sport = to_imx_port(port);
int retval;
unsigned long flags;
int dma_is_inited = 0;
u32 ucr1, ucr2, ucr3, ucr4;
retval = clk_prepare_enable(sport->clk_per);
if (retval)
return retval;
retval = clk_prepare_enable(sport->clk_ipg);
if (retval) {
clk_disable_unprepare(sport->clk_per);
return retval;
}
if (uart_console(&sport->port))
sport->rxtl = RXTL_CONSOLE_DEFAULT;
else
sport->rxtl = RXTL_DEFAULT;
imx_uart_setup_ufcr(sport, TXTL_DEFAULT, sport->rxtl);
/* disable the DREN bit (Data Ready interrupt enable) before
* requesting IRQs
*/
ucr4 = imx_uart_readl(sport, UCR4);
/* set the trigger level for CTS */
ucr4 &= ~(UCR4_CTSTL_MASK << UCR4_CTSTL_SHF);
ucr4 |= CTSTL << UCR4_CTSTL_SHF;
imx_uart_writel(sport, ucr4 & ~UCR4_DREN, UCR4);
/* Can we enable the DMA support? */
serial: imx: Put DMA enabled UART in separate lock subclass Lockdep complains about possible circular locking dependencies when the i.MX SDMA driver issues console messages under its spinlock. While the SDMA driver calls back into the UART when issuing a message, the i.MX UART driver will never call back into the SDMA driver for this UART, because DMA is explicitly not used for UARTs providing the console. To avoid the lockdep warnings put the UART port lock for console devices into a separate subclass. This fixes possible deadlock warnings like the following which was provoked by adding a printk to the i.MX SDMA driver at a place where the driver holds its spinlock. ====================================================== WARNING: possible circular locking dependency detected 6.6.0-rc3-00045-g517852be693b-dirty #110 Not tainted ------------------------------------------------------ swapper/0/0 is trying to acquire lock: c1818e04 (console_owner){-...}-{0:0}, at: console_flush_all+0x1c4/0x634 but task is already holding lock: c44649e0 (&vc->lock){-...}-{3:3}, at: sdma_int_handler+0xc4/0x368 which lock already depends on the new lock. the existing dependency chain (in reverse order) is: -> #2 (&vc->lock){-...}-{3:3}: _raw_spin_lock_irqsave+0x4c/0x68 sdma_prep_dma_cyclic+0x1a8/0x21c imx_uart_startup+0x44c/0x5d4 uart_startup+0x120/0x2b0 uart_port_activate+0x44/0x98 tty_port_open+0x80/0xd0 uart_open+0x18/0x20 tty_open+0x120/0x664 chrdev_open+0xc0/0x214 do_dentry_open+0x1d0/0x544 path_openat+0xbb0/0xea0 do_filp_open+0x5c/0xd4 do_sys_openat2+0xb8/0xf0 sys_openat+0x8c/0xd8 ret_fast_syscall+0x0/0x1c -> #1 (&port_lock_key){-.-.}-{3:3}: _raw_spin_lock_irqsave+0x4c/0x68 imx_uart_console_write+0x164/0x1a0 console_flush_all+0x220/0x634 console_unlock+0x64/0x164 vprintk_emit+0xb0/0x390 vprintk_default+0x24/0x2c _printk+0x2c/0x5c register_console+0x244/0x478 serial_core_register_port+0x5c4/0x618 imx_uart_probe+0x4e0/0x7d4 platform_probe+0x58/0xb0 really_probe+0xc4/0x2e0 __driver_probe_device+0x84/0x1a0 driver_probe_device+0x2c/0x108 __driver_attach+0x94/0x17c bus_for_each_dev+0x7c/0xd0 bus_add_driver+0xc4/0x1cc driver_register+0x7c/0x114 imx_uart_init+0x20/0x40 do_one_initcall+0x7c/0x3c4 kernel_init_freeable+0x17c/0x228 kernel_init+0x14/0x140 ret_from_fork+0x14/0x24 -> #0 (console_owner){-...}-{0:0}: __lock_acquire+0x14b0/0x29a0 lock_acquire.part.0+0xb4/0x264 console_flush_all+0x20c/0x634 console_unlock+0x64/0x164 vprintk_emit+0xb0/0x390 vprintk_default+0x24/0x2c _printk+0x2c/0x5c sdma_int_handler+0xcc/0x368 __handle_irq_event_percpu+0x94/0x2d0 handle_irq_event+0x38/0xd0 handle_fasteoi_irq+0x98/0x248 handle_irq_desc+0x1c/0x2c gic_handle_irq+0x6c/0x90 generic_handle_arch_irq+0x2c/0x64 __irq_svc+0x90/0xbc cpuidle_enter_state+0x1a0/0x4f4 cpuidle_enter+0x30/0x40 do_idle+0x210/0x2b4 cpu_startup_entry+0x28/0x2c rest_init+0xd0/0x184 arch_post_acpi_subsys_init+0x0/0x8 other info that might help us debug this: Chain exists of: console_owner --> &port_lock_key --> &vc->lock Possible unsafe locking scenario: CPU0 CPU1 ---- ---- lock(&vc->lock); lock(&port_lock_key); lock(&vc->lock); lock(console_owner); *** DEADLOCK *** 3 locks held by swapper/0/0: #0: c44649e0 (&vc->lock){-...}-{3:3}, at: sdma_int_handler+0xc4/0x368 #1: c1818d50 (console_lock){+.+.}-{0:0}, at: vprintk_default+0x24/0x2c #2: c1818d08 (console_srcu){....}-{0:0}, at: console_flush_all+0x44/0x634 stack backtrace: CPU: 0 PID: 0 Comm: swapper/0 Not tainted 6.6.0-rc3-00045-g517852be693b-dirty #110 Hardware name: Freescale i.MX6 Quad/DualLite (Device Tree) unwind_backtrace from show_stack+0x10/0x14 show_stack from dump_stack_lvl+0x60/0x90 dump_stack_lvl from check_noncircular+0x184/0x1b8 check_noncircular from __lock_acquire+0x14b0/0x29a0 __lock_acquire from lock_acquire.part.0+0xb4/0x264 lock_acquire.part.0 from console_flush_all+0x20c/0x634 console_flush_all from console_unlock+0x64/0x164 console_unlock from vprintk_emit+0xb0/0x390 vprintk_emit from vprintk_default+0x24/0x2c vprintk_default from _printk+0x2c/0x5c _printk from sdma_int_handler+0xcc/0x368 sdma_int_handler from __handle_irq_event_percpu+0x94/0x2d0 __handle_irq_event_percpu from handle_irq_event+0x38/0xd0 handle_irq_event from handle_fasteoi_irq+0x98/0x248 handle_fasteoi_irq from handle_irq_desc+0x1c/0x2c handle_irq_desc from gic_handle_irq+0x6c/0x90 gic_handle_irq from generic_handle_arch_irq+0x2c/0x64 generic_handle_arch_irq from __irq_svc+0x90/0xbc Exception stack(0xc1801ee8 to 0xc1801f30) 1ee0: ffffffff ffffffff 00000001 00030349 00000000 00000012 1f00: 00000000 d7e45f4b 00000012 00000000 d7e16d63 c1810828 00000000 c1801f38 1f20: c108125c c1081260 60010013 ffffffff __irq_svc from cpuidle_enter_state+0x1a0/0x4f4 cpuidle_enter_state from cpuidle_enter+0x30/0x40 cpuidle_enter from do_idle+0x210/0x2b4 do_idle from cpu_startup_entry+0x28/0x2c cpu_startup_entry from rest_init+0xd0/0x184 rest_init from arch_post_acpi_subsys_init+0x0/0x8 Reported-by: Tim van der Staaij <Tim.vanderstaaij@zigngroup.com> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> Link: https://lore.kernel.org/r/20230928064320.711603-1-s.hauer@pengutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-09-28 08:43:20 +02:00
if (!uart_console(port) && imx_uart_dma_init(sport) == 0) {
lockdep_set_subclass(&port->lock, 1);
dma_is_inited = 1;
serial: imx: Put DMA enabled UART in separate lock subclass Lockdep complains about possible circular locking dependencies when the i.MX SDMA driver issues console messages under its spinlock. While the SDMA driver calls back into the UART when issuing a message, the i.MX UART driver will never call back into the SDMA driver for this UART, because DMA is explicitly not used for UARTs providing the console. To avoid the lockdep warnings put the UART port lock for console devices into a separate subclass. This fixes possible deadlock warnings like the following which was provoked by adding a printk to the i.MX SDMA driver at a place where the driver holds its spinlock. ====================================================== WARNING: possible circular locking dependency detected 6.6.0-rc3-00045-g517852be693b-dirty #110 Not tainted ------------------------------------------------------ swapper/0/0 is trying to acquire lock: c1818e04 (console_owner){-...}-{0:0}, at: console_flush_all+0x1c4/0x634 but task is already holding lock: c44649e0 (&vc->lock){-...}-{3:3}, at: sdma_int_handler+0xc4/0x368 which lock already depends on the new lock. the existing dependency chain (in reverse order) is: -> #2 (&vc->lock){-...}-{3:3}: _raw_spin_lock_irqsave+0x4c/0x68 sdma_prep_dma_cyclic+0x1a8/0x21c imx_uart_startup+0x44c/0x5d4 uart_startup+0x120/0x2b0 uart_port_activate+0x44/0x98 tty_port_open+0x80/0xd0 uart_open+0x18/0x20 tty_open+0x120/0x664 chrdev_open+0xc0/0x214 do_dentry_open+0x1d0/0x544 path_openat+0xbb0/0xea0 do_filp_open+0x5c/0xd4 do_sys_openat2+0xb8/0xf0 sys_openat+0x8c/0xd8 ret_fast_syscall+0x0/0x1c -> #1 (&port_lock_key){-.-.}-{3:3}: _raw_spin_lock_irqsave+0x4c/0x68 imx_uart_console_write+0x164/0x1a0 console_flush_all+0x220/0x634 console_unlock+0x64/0x164 vprintk_emit+0xb0/0x390 vprintk_default+0x24/0x2c _printk+0x2c/0x5c register_console+0x244/0x478 serial_core_register_port+0x5c4/0x618 imx_uart_probe+0x4e0/0x7d4 platform_probe+0x58/0xb0 really_probe+0xc4/0x2e0 __driver_probe_device+0x84/0x1a0 driver_probe_device+0x2c/0x108 __driver_attach+0x94/0x17c bus_for_each_dev+0x7c/0xd0 bus_add_driver+0xc4/0x1cc driver_register+0x7c/0x114 imx_uart_init+0x20/0x40 do_one_initcall+0x7c/0x3c4 kernel_init_freeable+0x17c/0x228 kernel_init+0x14/0x140 ret_from_fork+0x14/0x24 -> #0 (console_owner){-...}-{0:0}: __lock_acquire+0x14b0/0x29a0 lock_acquire.part.0+0xb4/0x264 console_flush_all+0x20c/0x634 console_unlock+0x64/0x164 vprintk_emit+0xb0/0x390 vprintk_default+0x24/0x2c _printk+0x2c/0x5c sdma_int_handler+0xcc/0x368 __handle_irq_event_percpu+0x94/0x2d0 handle_irq_event+0x38/0xd0 handle_fasteoi_irq+0x98/0x248 handle_irq_desc+0x1c/0x2c gic_handle_irq+0x6c/0x90 generic_handle_arch_irq+0x2c/0x64 __irq_svc+0x90/0xbc cpuidle_enter_state+0x1a0/0x4f4 cpuidle_enter+0x30/0x40 do_idle+0x210/0x2b4 cpu_startup_entry+0x28/0x2c rest_init+0xd0/0x184 arch_post_acpi_subsys_init+0x0/0x8 other info that might help us debug this: Chain exists of: console_owner --> &port_lock_key --> &vc->lock Possible unsafe locking scenario: CPU0 CPU1 ---- ---- lock(&vc->lock); lock(&port_lock_key); lock(&vc->lock); lock(console_owner); *** DEADLOCK *** 3 locks held by swapper/0/0: #0: c44649e0 (&vc->lock){-...}-{3:3}, at: sdma_int_handler+0xc4/0x368 #1: c1818d50 (console_lock){+.+.}-{0:0}, at: vprintk_default+0x24/0x2c #2: c1818d08 (console_srcu){....}-{0:0}, at: console_flush_all+0x44/0x634 stack backtrace: CPU: 0 PID: 0 Comm: swapper/0 Not tainted 6.6.0-rc3-00045-g517852be693b-dirty #110 Hardware name: Freescale i.MX6 Quad/DualLite (Device Tree) unwind_backtrace from show_stack+0x10/0x14 show_stack from dump_stack_lvl+0x60/0x90 dump_stack_lvl from check_noncircular+0x184/0x1b8 check_noncircular from __lock_acquire+0x14b0/0x29a0 __lock_acquire from lock_acquire.part.0+0xb4/0x264 lock_acquire.part.0 from console_flush_all+0x20c/0x634 console_flush_all from console_unlock+0x64/0x164 console_unlock from vprintk_emit+0xb0/0x390 vprintk_emit from vprintk_default+0x24/0x2c vprintk_default from _printk+0x2c/0x5c _printk from sdma_int_handler+0xcc/0x368 sdma_int_handler from __handle_irq_event_percpu+0x94/0x2d0 __handle_irq_event_percpu from handle_irq_event+0x38/0xd0 handle_irq_event from handle_fasteoi_irq+0x98/0x248 handle_fasteoi_irq from handle_irq_desc+0x1c/0x2c handle_irq_desc from gic_handle_irq+0x6c/0x90 gic_handle_irq from generic_handle_arch_irq+0x2c/0x64 generic_handle_arch_irq from __irq_svc+0x90/0xbc Exception stack(0xc1801ee8 to 0xc1801f30) 1ee0: ffffffff ffffffff 00000001 00030349 00000000 00000012 1f00: 00000000 d7e45f4b 00000012 00000000 d7e16d63 c1810828 00000000 c1801f38 1f20: c108125c c1081260 60010013 ffffffff __irq_svc from cpuidle_enter_state+0x1a0/0x4f4 cpuidle_enter_state from cpuidle_enter+0x30/0x40 cpuidle_enter from do_idle+0x210/0x2b4 do_idle from cpu_startup_entry+0x28/0x2c cpu_startup_entry from rest_init+0xd0/0x184 rest_init from arch_post_acpi_subsys_init+0x0/0x8 Reported-by: Tim van der Staaij <Tim.vanderstaaij@zigngroup.com> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> Link: https://lore.kernel.org/r/20230928064320.711603-1-s.hauer@pengutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-09-28 08:43:20 +02:00
}
uart_port_lock_irqsave(&sport->port, &flags);
/* Reset fifo's and state machines */
imx_uart_soft_reset(sport);
/*
* Finally, clear and enable interrupts
*/
imx_uart_writel(sport, USR1_RTSD | USR1_DTRD, USR1);
imx_uart_writel(sport, USR2_ORE, USR2);
ucr1 = imx_uart_readl(sport, UCR1) & ~UCR1_RRDYEN;
ucr1 |= UCR1_UARTEN;
if (sport->have_rtscts)
ucr1 |= UCR1_RTSDEN;
imx_uart_writel(sport, ucr1, UCR1);
ucr4 = imx_uart_readl(sport, UCR4) & ~(UCR4_OREN | UCR4_INVR);
if (!dma_is_inited)
ucr4 |= UCR4_OREN;
if (sport->inverted_rx)
ucr4 |= UCR4_INVR;
imx_uart_writel(sport, ucr4, UCR4);
ucr3 = imx_uart_readl(sport, UCR3) & ~UCR3_INVT;
/*
* configure tx polarity before enabling tx
*/
if (sport->inverted_tx)
ucr3 |= UCR3_INVT;
if (!imx_uart_is_imx1(sport)) {
ucr3 |= UCR3_DTRDEN | UCR3_RI | UCR3_DCD;
if (sport->dte_mode)
/* disable broken interrupts */
ucr3 &= ~(UCR3_RI | UCR3_DCD);
}
imx_uart_writel(sport, ucr3, UCR3);
ucr2 = imx_uart_readl(sport, UCR2) & ~UCR2_ATEN;
ucr2 |= (UCR2_RXEN | UCR2_TXEN);
if (!sport->have_rtscts)
ucr2 |= UCR2_IRTS;
/*
* make sure the edge sensitive RTS-irq is disabled,
* we're using RTSD instead.
*/
if (!imx_uart_is_imx1(sport))
ucr2 &= ~UCR2_RTSEN;
imx_uart_writel(sport, ucr2, UCR2);
/*
* Enable modem status interrupts
*/
imx_uart_enable_ms(&sport->port);
if (dma_is_inited) {
imx_uart_enable_dma(sport);
imx_uart_start_rx_dma(sport);
} else {
ucr1 = imx_uart_readl(sport, UCR1);
ucr1 |= UCR1_RRDYEN;
imx_uart_writel(sport, ucr1, UCR1);
ucr2 = imx_uart_readl(sport, UCR2);
ucr2 |= UCR2_ATEN;
imx_uart_writel(sport, ucr2, UCR2);
}
imx_uart_disable_loopback_rs485(sport);
tty: serial: imx: Handle RS485 DE signal active high The default polarity of RS485 DE signal is active high. This driver does not handle such case properly. Currently, when a pin is multiplexed as a UART CTS_B on boot, this pin is pulled HIGH by the i.MX UART CTS circuit, which activates DE signal on the RS485 transceiver and thus behave as if the RS485 was transmitting data, so the system blocks the RS485 bus when it starts and until user application takes over. This behavior is not OK. The problem consists of two separate parts. First, the i.MX UART IP requires UCR1 UARTEN and UCR2 RXEN to be set for UCR2 CTSC and CTS bits to have any effect. The UCR2 CTSC bit permits the driver to set CTS (RTS_B or RS485 DE signal) to either level sychronous to the internal UART IP clock. Compared to other options, like GPIO CTS control, this has the benefit of being synchronous to the UART IP clock and thus without glitches or bus delays. The reason for the CTS design is likely because when the Receiver is disabled, the UART IP can never indicate that it is ready to receive data by assering CTS signal, so the CTS is always pulled HIGH by default. When the port is closed by user space, imx_uart_stop_rx() clears UCR2 RXEN bit, and imx_uart_shutdown() clears UCR1 UARTEN bit. This disables UART Receiver and UART itself, and forces CTS signal HIGH, which leads to the RS485 bus being blocked because RS485 DE is incorrectly active. The proposed solution for this problem is to keep the Receiver running even after the port is closed, but in loopback mode. This disconnects the RX FIFO input from the RXD external signal, and since UCR2 TXEN is cleared, the UART Transmitter is disabled, so nothing can feed data in the RX FIFO. Because the Receiver is still enabled, the UCR2 CTSC and CTS bits still have effect and the CTS (RS485 DE) control is retained. Note that in case of RS485 DE signal active low, there is no problem and no special handling is necessary. The CTS signal defaults to HIGH, thus the RS485 is by default set to Receive and the bus is not blocked. Note that while there is the possibility to control CTS using GPIO with either CTS polarity, this has the downside of not being synchronous to the UART IP clock and thus glitchy and susceptible to slow DE switching. Second, on boot, before the UART driver probe callback is called, the driver core triggers pinctrl_init_done() and configures the IOMUXC to default state. At this point, UCR1 UARTEN and UCR2 RXEN are both still cleared, but UART CTS_B (RS485 DE) is configured as CTS function, thus the RTS signal is pulled HIGH by the UART IP CTS circuit. One part of the solution here is to enable UCR1 UARTEN and UCR2 RXEN and UTS loopback in this driver probe callback, thus unblocking the CTSC and CTS control early on. But this is still too late, since the pin control is already configured and CTS has been pulled HIGH for a short period of time. When Linux kernel boots and this driver is bound, the pin control is set to special "init" state if the state is available, and driver can switch the "default" state afterward when ready. This state can be used to set the CTS line as a GPIO in DT temporarily, and a GPIO hog can force such GPIO to LOW, thus keeping the RS485 DE line LOW early on boot. Once the driver takes over and UCR1 UARTEN and UCR2 RXEN and UTS loopback are all enabled, the driver can switch to "default" pin control state and control the CTS line as function instead. DT binding example is below: " &gpio6 { rts-init-hog { gpio-hog; gpios = <5 0>; output-low; line-name = "rs485-de"; }; }; &uart5 { /* DHCOM UART2 */ pinctrl-0 = <&pinctrl_uart5>; pinctrl-1 = <&pinctrl_uart5_init>; pinctrl-names = "default", "init"; ... }; pinctrl_uart5_init: uart5-init-grp { fsl,pins = < ... MX6QDL_PAD_CSI0_DAT19__GPIO6_IO05 0x30b1 >; }; pinctrl_uart5: uart5-grp { fsl,pins = < ... MX6QDL_PAD_CSI0_DAT19__UART5_CTS_B 0x30b1 >; }; " Tested-by: Christoph Niedermaier <cniedermaier@dh-electronics.com> Reviewed-by: Fabio Estevam <festevam@denx.de> Signed-off-by: Marek Vasut <marex@denx.de> Link: https://lore.kernel.org/r/20220929144400.13571-1-marex@denx.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2022-09-29 16:44:00 +02:00
uart_port_unlock_irqrestore(&sport->port, flags);
return 0;
}
static void imx_uart_shutdown(struct uart_port *port)
{
struct imx_port *sport = to_imx_port(port);
unsigned long flags;
tty: serial: imx: Handle RS485 DE signal active high The default polarity of RS485 DE signal is active high. This driver does not handle such case properly. Currently, when a pin is multiplexed as a UART CTS_B on boot, this pin is pulled HIGH by the i.MX UART CTS circuit, which activates DE signal on the RS485 transceiver and thus behave as if the RS485 was transmitting data, so the system blocks the RS485 bus when it starts and until user application takes over. This behavior is not OK. The problem consists of two separate parts. First, the i.MX UART IP requires UCR1 UARTEN and UCR2 RXEN to be set for UCR2 CTSC and CTS bits to have any effect. The UCR2 CTSC bit permits the driver to set CTS (RTS_B or RS485 DE signal) to either level sychronous to the internal UART IP clock. Compared to other options, like GPIO CTS control, this has the benefit of being synchronous to the UART IP clock and thus without glitches or bus delays. The reason for the CTS design is likely because when the Receiver is disabled, the UART IP can never indicate that it is ready to receive data by assering CTS signal, so the CTS is always pulled HIGH by default. When the port is closed by user space, imx_uart_stop_rx() clears UCR2 RXEN bit, and imx_uart_shutdown() clears UCR1 UARTEN bit. This disables UART Receiver and UART itself, and forces CTS signal HIGH, which leads to the RS485 bus being blocked because RS485 DE is incorrectly active. The proposed solution for this problem is to keep the Receiver running even after the port is closed, but in loopback mode. This disconnects the RX FIFO input from the RXD external signal, and since UCR2 TXEN is cleared, the UART Transmitter is disabled, so nothing can feed data in the RX FIFO. Because the Receiver is still enabled, the UCR2 CTSC and CTS bits still have effect and the CTS (RS485 DE) control is retained. Note that in case of RS485 DE signal active low, there is no problem and no special handling is necessary. The CTS signal defaults to HIGH, thus the RS485 is by default set to Receive and the bus is not blocked. Note that while there is the possibility to control CTS using GPIO with either CTS polarity, this has the downside of not being synchronous to the UART IP clock and thus glitchy and susceptible to slow DE switching. Second, on boot, before the UART driver probe callback is called, the driver core triggers pinctrl_init_done() and configures the IOMUXC to default state. At this point, UCR1 UARTEN and UCR2 RXEN are both still cleared, but UART CTS_B (RS485 DE) is configured as CTS function, thus the RTS signal is pulled HIGH by the UART IP CTS circuit. One part of the solution here is to enable UCR1 UARTEN and UCR2 RXEN and UTS loopback in this driver probe callback, thus unblocking the CTSC and CTS control early on. But this is still too late, since the pin control is already configured and CTS has been pulled HIGH for a short period of time. When Linux kernel boots and this driver is bound, the pin control is set to special "init" state if the state is available, and driver can switch the "default" state afterward when ready. This state can be used to set the CTS line as a GPIO in DT temporarily, and a GPIO hog can force such GPIO to LOW, thus keeping the RS485 DE line LOW early on boot. Once the driver takes over and UCR1 UARTEN and UCR2 RXEN and UTS loopback are all enabled, the driver can switch to "default" pin control state and control the CTS line as function instead. DT binding example is below: " &gpio6 { rts-init-hog { gpio-hog; gpios = <5 0>; output-low; line-name = "rs485-de"; }; }; &uart5 { /* DHCOM UART2 */ pinctrl-0 = <&pinctrl_uart5>; pinctrl-1 = <&pinctrl_uart5_init>; pinctrl-names = "default", "init"; ... }; pinctrl_uart5_init: uart5-init-grp { fsl,pins = < ... MX6QDL_PAD_CSI0_DAT19__GPIO6_IO05 0x30b1 >; }; pinctrl_uart5: uart5-grp { fsl,pins = < ... MX6QDL_PAD_CSI0_DAT19__UART5_CTS_B 0x30b1 >; }; " Tested-by: Christoph Niedermaier <cniedermaier@dh-electronics.com> Reviewed-by: Fabio Estevam <festevam@denx.de> Signed-off-by: Marek Vasut <marex@denx.de> Link: https://lore.kernel.org/r/20220929144400.13571-1-marex@denx.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2022-09-29 16:44:00 +02:00
u32 ucr1, ucr2, ucr4, uts;
int loops;
if (sport->dma_is_enabled) {
dmaengine_terminate_sync(sport->dma_chan_tx);
if (sport->dma_is_txing) {
dma_unmap_sg(sport->port.dev, &sport->tx_sgl[0],
sport->dma_tx_nents, DMA_TO_DEVICE);
sport->dma_is_txing = 0;
}
dmaengine_terminate_sync(sport->dma_chan_rx);
if (sport->dma_is_rxing) {
dma_unmap_sg(sport->port.dev, &sport->rx_sgl,
1, DMA_FROM_DEVICE);
sport->dma_is_rxing = 0;
}
uart_port_lock_irqsave(&sport->port, &flags);
imx_uart_stop_tx(port);
imx_uart_stop_rx(port);
imx_uart_disable_dma(sport);
uart_port_unlock_irqrestore(&sport->port, flags);
imx_uart_dma_exit(sport);
}
serial: mctrl_gpio: split disable_ms into sync and no_sync APIs The following splat has been observed on a SAMA5D27 platform using atmel_serial: BUG: sleeping function called from invalid context at kernel/irq/manage.c:738 in_atomic(): 1, irqs_disabled(): 128, non_block: 0, pid: 27, name: kworker/u5:0 preempt_count: 1, expected: 0 INFO: lockdep is turned off. irq event stamp: 0 hardirqs last enabled at (0): [<00000000>] 0x0 hardirqs last disabled at (0): [<c01588f0>] copy_process+0x1c4c/0x7bec softirqs last enabled at (0): [<c0158944>] copy_process+0x1ca0/0x7bec softirqs last disabled at (0): [<00000000>] 0x0 CPU: 0 UID: 0 PID: 27 Comm: kworker/u5:0 Not tainted 6.13.0-rc7+ #74 Hardware name: Atmel SAMA5 Workqueue: hci0 hci_power_on [bluetooth] Call trace: unwind_backtrace from show_stack+0x18/0x1c show_stack from dump_stack_lvl+0x44/0x70 dump_stack_lvl from __might_resched+0x38c/0x598 __might_resched from disable_irq+0x1c/0x48 disable_irq from mctrl_gpio_disable_ms+0x74/0xc0 mctrl_gpio_disable_ms from atmel_disable_ms.part.0+0x80/0x1f4 atmel_disable_ms.part.0 from atmel_set_termios+0x764/0x11e8 atmel_set_termios from uart_change_line_settings+0x15c/0x994 uart_change_line_settings from uart_set_termios+0x2b0/0x668 uart_set_termios from tty_set_termios+0x600/0x8ec tty_set_termios from ttyport_set_flow_control+0x188/0x1e0 ttyport_set_flow_control from wilc_setup+0xd0/0x524 [hci_wilc] wilc_setup [hci_wilc] from hci_dev_open_sync+0x330/0x203c [bluetooth] hci_dev_open_sync [bluetooth] from hci_dev_do_open+0x40/0xb0 [bluetooth] hci_dev_do_open [bluetooth] from hci_power_on+0x12c/0x664 [bluetooth] hci_power_on [bluetooth] from process_one_work+0x998/0x1a38 process_one_work from worker_thread+0x6e0/0xfb4 worker_thread from kthread+0x3d4/0x484 kthread from ret_from_fork+0x14/0x28 This warning is emitted when trying to toggle, at the highest level, some flow control (with serdev_device_set_flow_control) in a device driver. At the lowest level, the atmel_serial driver is using serial_mctrl_gpio lib to enable/disable the corresponding IRQs accordingly. The warning emitted by CONFIG_DEBUG_ATOMIC_SLEEP is due to disable_irq (called in mctrl_gpio_disable_ms) being possibly called in some atomic context (some tty drivers perform modem lines configuration in regions protected by port lock). Split mctrl_gpio_disable_ms into two differents APIs, a non-blocking one and a blocking one. Replace mctrl_gpio_disable_ms calls with the relevant version depending on whether the call is protected by some port lock. Suggested-by: Jiri Slaby <jirislaby@kernel.org> Signed-off-by: Alexis Lothoré <alexis.lothore@bootlin.com> Acked-by: Richard Genoud <richard.genoud@bootlin.com> Link: https://lore.kernel.org/r/20250217-atomic_sleep_mctrl_serial_gpio-v3-1-59324b313eef@bootlin.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2025-02-17 07:21:53 +01:00
mctrl_gpio_disable_ms_sync(sport->gpios);
uart_port_lock_irqsave(&sport->port, &flags);
ucr2 = imx_uart_readl(sport, UCR2);
ucr2 &= ~(UCR2_TXEN | UCR2_ATEN);
imx_uart_writel(sport, ucr2, UCR2);
uart_port_unlock_irqrestore(&sport->port, flags);
/*
* Stop our timer.
*/
timer_delete_sync(&sport->timer);
/*
* Disable all interrupts, port and break condition.
*/
uart_port_lock_irqsave(&sport->port, &flags);
ucr1 = imx_uart_readl(sport, UCR1);
ucr1 &= ~(UCR1_TRDYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_RXDMAEN |
UCR1_ATDMAEN | UCR1_SNDBRK);
tty: serial: imx: Handle RS485 DE signal active high The default polarity of RS485 DE signal is active high. This driver does not handle such case properly. Currently, when a pin is multiplexed as a UART CTS_B on boot, this pin is pulled HIGH by the i.MX UART CTS circuit, which activates DE signal on the RS485 transceiver and thus behave as if the RS485 was transmitting data, so the system blocks the RS485 bus when it starts and until user application takes over. This behavior is not OK. The problem consists of two separate parts. First, the i.MX UART IP requires UCR1 UARTEN and UCR2 RXEN to be set for UCR2 CTSC and CTS bits to have any effect. The UCR2 CTSC bit permits the driver to set CTS (RTS_B or RS485 DE signal) to either level sychronous to the internal UART IP clock. Compared to other options, like GPIO CTS control, this has the benefit of being synchronous to the UART IP clock and thus without glitches or bus delays. The reason for the CTS design is likely because when the Receiver is disabled, the UART IP can never indicate that it is ready to receive data by assering CTS signal, so the CTS is always pulled HIGH by default. When the port is closed by user space, imx_uart_stop_rx() clears UCR2 RXEN bit, and imx_uart_shutdown() clears UCR1 UARTEN bit. This disables UART Receiver and UART itself, and forces CTS signal HIGH, which leads to the RS485 bus being blocked because RS485 DE is incorrectly active. The proposed solution for this problem is to keep the Receiver running even after the port is closed, but in loopback mode. This disconnects the RX FIFO input from the RXD external signal, and since UCR2 TXEN is cleared, the UART Transmitter is disabled, so nothing can feed data in the RX FIFO. Because the Receiver is still enabled, the UCR2 CTSC and CTS bits still have effect and the CTS (RS485 DE) control is retained. Note that in case of RS485 DE signal active low, there is no problem and no special handling is necessary. The CTS signal defaults to HIGH, thus the RS485 is by default set to Receive and the bus is not blocked. Note that while there is the possibility to control CTS using GPIO with either CTS polarity, this has the downside of not being synchronous to the UART IP clock and thus glitchy and susceptible to slow DE switching. Second, on boot, before the UART driver probe callback is called, the driver core triggers pinctrl_init_done() and configures the IOMUXC to default state. At this point, UCR1 UARTEN and UCR2 RXEN are both still cleared, but UART CTS_B (RS485 DE) is configured as CTS function, thus the RTS signal is pulled HIGH by the UART IP CTS circuit. One part of the solution here is to enable UCR1 UARTEN and UCR2 RXEN and UTS loopback in this driver probe callback, thus unblocking the CTSC and CTS control early on. But this is still too late, since the pin control is already configured and CTS has been pulled HIGH for a short period of time. When Linux kernel boots and this driver is bound, the pin control is set to special "init" state if the state is available, and driver can switch the "default" state afterward when ready. This state can be used to set the CTS line as a GPIO in DT temporarily, and a GPIO hog can force such GPIO to LOW, thus keeping the RS485 DE line LOW early on boot. Once the driver takes over and UCR1 UARTEN and UCR2 RXEN and UTS loopback are all enabled, the driver can switch to "default" pin control state and control the CTS line as function instead. DT binding example is below: " &gpio6 { rts-init-hog { gpio-hog; gpios = <5 0>; output-low; line-name = "rs485-de"; }; }; &uart5 { /* DHCOM UART2 */ pinctrl-0 = <&pinctrl_uart5>; pinctrl-1 = <&pinctrl_uart5_init>; pinctrl-names = "default", "init"; ... }; pinctrl_uart5_init: uart5-init-grp { fsl,pins = < ... MX6QDL_PAD_CSI0_DAT19__GPIO6_IO05 0x30b1 >; }; pinctrl_uart5: uart5-grp { fsl,pins = < ... MX6QDL_PAD_CSI0_DAT19__UART5_CTS_B 0x30b1 >; }; " Tested-by: Christoph Niedermaier <cniedermaier@dh-electronics.com> Reviewed-by: Fabio Estevam <festevam@denx.de> Signed-off-by: Marek Vasut <marex@denx.de> Link: https://lore.kernel.org/r/20220929144400.13571-1-marex@denx.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2022-09-29 16:44:00 +02:00
/* See SER_RS485_ENABLED/UTS_LOOP comment in imx_uart_probe() */
if (port->rs485.flags & SER_RS485_ENABLED &&
port->rs485.flags & SER_RS485_RTS_ON_SEND &&
sport->have_rtscts && !sport->have_rtsgpio) {
uts = imx_uart_readl(sport, imx_uart_uts_reg(sport));
uts |= UTS_LOOP;
imx_uart_writel(sport, uts, imx_uart_uts_reg(sport));
ucr1 |= UCR1_UARTEN;
} else {
ucr1 &= ~UCR1_UARTEN;
}
imx_uart_writel(sport, ucr1, UCR1);
ucr4 = imx_uart_readl(sport, UCR4);
ucr4 &= ~UCR4_TCEN;
imx_uart_writel(sport, ucr4, UCR4);
/*
* We have to ensure the tx state machine ends up in OFF. This
* is especially important for rs485 where we must not leave
* the RTS signal high, blocking the bus indefinitely.
*
* All interrupts are now disabled, so imx_uart_stop_tx() will
* no longer be called from imx_uart_transmit_buffer(). It may
* still be called via the hrtimers, and if those are in play,
* we have to honour the delays.
*/
if (sport->tx_state == WAIT_AFTER_RTS || sport->tx_state == SEND)
imx_uart_stop_tx(port);
/*
* In many cases (rs232 mode, or if tx_state was
* WAIT_AFTER_RTS, or if tx_state was SEND and there is no
* delay_rts_after_send), this will have moved directly to
* OFF. In rs485 mode, tx_state might already have been
* WAIT_AFTER_SEND and the hrtimer thus already started, or
* the above imx_uart_stop_tx() call could have started it. In
* those cases, we have to wait for the hrtimer to fire and
* complete the transition to OFF.
*/
loops = port->rs485.flags & SER_RS485_ENABLED ?
port->rs485.delay_rts_after_send : 0;
while (sport->tx_state != OFF && loops--) {
uart_port_unlock_irqrestore(&sport->port, flags);
msleep(1);
uart_port_lock_irqsave(&sport->port, &flags);
}
if (sport->tx_state != OFF) {
dev_warn(sport->port.dev, "unexpected tx_state %d\n",
sport->tx_state);
/*
* This machine may be busted, but ensure the RTS
* signal is inactive in order not to block other
* devices.
*/
if (port->rs485.flags & SER_RS485_ENABLED) {
ucr2 = imx_uart_readl(sport, UCR2);
if (port->rs485.flags & SER_RS485_RTS_AFTER_SEND)
imx_uart_rts_active(sport, &ucr2);
else
imx_uart_rts_inactive(sport, &ucr2);
imx_uart_writel(sport, ucr2, UCR2);
}
sport->tx_state = OFF;
}
uart_port_unlock_irqrestore(&sport->port, flags);
clk_disable_unprepare(sport->clk_per);
clk_disable_unprepare(sport->clk_ipg);
}
/* called with port.lock taken and irqs off */
static void imx_uart_flush_buffer(struct uart_port *port)
{
struct imx_port *sport = to_imx_port(port);
struct scatterlist *sgl = &sport->tx_sgl[0];
if (!sport->dma_chan_tx)
return;
sport->tx_bytes = 0;
dmaengine_terminate_all(sport->dma_chan_tx);
if (sport->dma_is_txing) {
u32 ucr1;
dma_unmap_sg(sport->port.dev, sgl, sport->dma_tx_nents,
DMA_TO_DEVICE);
ucr1 = imx_uart_readl(sport, UCR1);
ucr1 &= ~UCR1_TXDMAEN;
imx_uart_writel(sport, ucr1, UCR1);
sport->dma_is_txing = 0;
}
imx_uart_soft_reset(sport);
}
static void
imx_uart_set_termios(struct uart_port *port, struct ktermios *termios,
const struct ktermios *old)
{
struct imx_port *sport = to_imx_port(port);
unsigned long flags;
u32 ucr2, old_ucr2, ufcr;
unsigned int baud, quot;
unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8;
unsigned long div;
unsigned long num, denom, old_ubir, old_ubmr;
uint64_t tdiv64;
/*
* We only support CS7 and CS8.
*/
while ((termios->c_cflag & CSIZE) != CS7 &&
(termios->c_cflag & CSIZE) != CS8) {
termios->c_cflag &= ~CSIZE;
termios->c_cflag |= old_csize;
old_csize = CS8;
}
timer_delete_sync(&sport->timer);
/*
* Ask the core to calculate the divisor for us.
*/
baud = uart_get_baud_rate(port, termios, old, 50, port->uartclk / 16);
quot = uart_get_divisor(port, baud);
uart_port_lock_irqsave(&sport->port, &flags);
/*
* Read current UCR2 and save it for future use, then clear all the bits
* except those we will or may need to preserve.
*/
old_ucr2 = imx_uart_readl(sport, UCR2);
ucr2 = old_ucr2 & (UCR2_TXEN | UCR2_RXEN | UCR2_ATEN | UCR2_CTS);
ucr2 |= UCR2_SRST | UCR2_IRTS;
if ((termios->c_cflag & CSIZE) == CS8)
ucr2 |= UCR2_WS;
if (!sport->have_rtscts)
termios->c_cflag &= ~CRTSCTS;
if (port->rs485.flags & SER_RS485_ENABLED) {
/*
* RTS is mandatory for rs485 operation, so keep
* it under manual control and keep transmitter
* disabled.
*/
if (port->rs485.flags & SER_RS485_RTS_AFTER_SEND)
imx_uart_rts_active(sport, &ucr2);
else
imx_uart_rts_inactive(sport, &ucr2);
} else if (termios->c_cflag & CRTSCTS) {
/*
* Only let receiver control RTS output if we were not requested
* to have RTS inactive (which then should take precedence).
*/
if (ucr2 & UCR2_CTS)
ucr2 |= UCR2_CTSC;
}
if (termios->c_cflag & CRTSCTS)
ucr2 &= ~UCR2_IRTS;
if (termios->c_cflag & CSTOPB)
ucr2 |= UCR2_STPB;
if (termios->c_cflag & PARENB) {
ucr2 |= UCR2_PREN;
if (termios->c_cflag & PARODD)
ucr2 |= UCR2_PROE;
}
sport->port.read_status_mask = 0;
if (termios->c_iflag & INPCK)
sport->port.read_status_mask |= (URXD_FRMERR | URXD_PRERR);
if (termios->c_iflag & (BRKINT | PARMRK))
sport->port.read_status_mask |= URXD_BRK;
/*
* Characters to ignore
*/
sport->port.ignore_status_mask = 0;
if (termios->c_iflag & IGNPAR)
sport->port.ignore_status_mask |= URXD_PRERR | URXD_FRMERR;
if (termios->c_iflag & IGNBRK) {
sport->port.ignore_status_mask |= URXD_BRK;
/*
* If we're ignoring parity and break indicators,
* ignore overruns too (for real raw support).
*/
if (termios->c_iflag & IGNPAR)
sport->port.ignore_status_mask |= URXD_OVRRUN;
}
if ((termios->c_cflag & CREAD) == 0)
sport->port.ignore_status_mask |= URXD_DUMMY_READ;
/*
* Update the per-port timeout.
*/
uart_update_timeout(port, termios->c_cflag, baud);
/* custom-baudrate handling */
div = sport->port.uartclk / (baud * 16);
if (baud == 38400 && quot != div)
baud = sport->port.uartclk / (quot * 16);
div = sport->port.uartclk / (baud * 16);
if (div > 7)
div = 7;
if (!div)
div = 1;
rational_best_approximation(16 * div * baud, sport->port.uartclk,
1 << 16, 1 << 16, &num, &denom);
tdiv64 = sport->port.uartclk;
tdiv64 *= num;
do_div(tdiv64, denom * 16 * div);
tty_termios_encode_baud_rate(termios,
(speed_t)tdiv64, (speed_t)tdiv64);
num -= 1;
denom -= 1;
ufcr = imx_uart_readl(sport, UFCR);
ufcr = (ufcr & (~UFCR_RFDIV)) | UFCR_RFDIV_REG(div);
imx_uart_writel(sport, ufcr, UFCR);
/*
* Two registers below should always be written both and in this
* particular order. One consequence is that we need to check if any of
* them changes and then update both. We do need the check for change
* as even writing the same values seem to "restart"
* transmission/receiving logic in the hardware, that leads to data
* breakage even when rate doesn't in fact change. E.g., user switches
* RTS/CTS handshake and suddenly gets broken bytes.
*/
old_ubir = imx_uart_readl(sport, UBIR);
old_ubmr = imx_uart_readl(sport, UBMR);
if (old_ubir != num || old_ubmr != denom) {
imx_uart_writel(sport, num, UBIR);
imx_uart_writel(sport, denom, UBMR);
}
if (!imx_uart_is_imx1(sport))
imx_uart_writel(sport, sport->port.uartclk / div / 1000,
IMX21_ONEMS);
imx_uart_writel(sport, ucr2, UCR2);
if (UART_ENABLE_MS(&sport->port, termios->c_cflag))
imx_uart_enable_ms(&sport->port);
uart_port_unlock_irqrestore(&sport->port, flags);
}
static const char *imx_uart_type(struct uart_port *port)
{
return port->type == PORT_IMX ? "IMX" : NULL;
}
/*
* Configure/autoconfigure the port.
*/
static void imx_uart_config_port(struct uart_port *port, int flags)
{
if (flags & UART_CONFIG_TYPE)
port->type = PORT_IMX;
}
/*
* Verify the new serial_struct (for TIOCSSERIAL).
* The only change we allow are to the flags and type, and
* even then only between PORT_IMX and PORT_UNKNOWN
*/
static int
imx_uart_verify_port(struct uart_port *port, struct serial_struct *ser)
{
int ret = 0;
if (ser->type != PORT_UNKNOWN && ser->type != PORT_IMX)
ret = -EINVAL;
if (port->irq != ser->irq)
ret = -EINVAL;
if (ser->io_type != UPIO_MEM)
ret = -EINVAL;
if (port->uartclk / 16 != ser->baud_base)
ret = -EINVAL;
if (port->mapbase != (unsigned long)ser->iomem_base)
ret = -EINVAL;
if (port->iobase != ser->port)
ret = -EINVAL;
if (ser->hub6 != 0)
ret = -EINVAL;
return ret;
}
#if defined(CONFIG_CONSOLE_POLL)
static int imx_uart_poll_init(struct uart_port *port)
{
struct imx_port *sport = to_imx_port(port);
unsigned long flags;
u32 ucr1, ucr2;
int retval;
retval = clk_prepare_enable(sport->clk_ipg);
if (retval)
return retval;
retval = clk_prepare_enable(sport->clk_per);
if (retval)
clk_disable_unprepare(sport->clk_ipg);
imx_uart_setup_ufcr(sport, TXTL_DEFAULT, sport->rxtl);
uart_port_lock_irqsave(&sport->port, &flags);
/*
* Be careful about the order of enabling bits here. First enable the
* receiver (UARTEN + RXEN) and only then the corresponding irqs.
* This prevents that a character that already sits in the RX fifo is
* triggering an irq but the try to fetch it from there results in an
* exception because UARTEN or RXEN is still off.
*/
ucr1 = imx_uart_readl(sport, UCR1);
ucr2 = imx_uart_readl(sport, UCR2);
if (imx_uart_is_imx1(sport))
ucr1 |= IMX1_UCR1_UARTCLKEN;
ucr1 |= UCR1_UARTEN;
ucr1 &= ~(UCR1_TRDYEN | UCR1_RTSDEN | UCR1_RRDYEN);
ucr2 |= UCR2_RXEN | UCR2_TXEN;
ucr2 &= ~UCR2_ATEN;
imx_uart_writel(sport, ucr1, UCR1);
imx_uart_writel(sport, ucr2, UCR2);
/* now enable irqs */
imx_uart_writel(sport, ucr1 | UCR1_RRDYEN, UCR1);
imx_uart_writel(sport, ucr2 | UCR2_ATEN, UCR2);
uart_port_unlock_irqrestore(&sport->port, flags);
return 0;
}
static int imx_uart_poll_get_char(struct uart_port *port)
{
struct imx_port *sport = to_imx_port(port);
if (!(imx_uart_readl(sport, USR2) & USR2_RDR))
return NO_POLL_CHAR;
return imx_uart_readl(sport, URXD0) & URXD_RX_DATA;
}
static void imx_uart_poll_put_char(struct uart_port *port, unsigned char c)
{
struct imx_port *sport = to_imx_port(port);
unsigned int status;
/* drain */
do {
status = imx_uart_readl(sport, USR1);
} while (~status & USR1_TRDY);
/* write */
imx_uart_writel(sport, c, URTX0);
/* flush */
do {
status = imx_uart_readl(sport, USR2);
} while (~status & USR2_TXDC);
}
#endif
/* called with port.lock taken and irqs off or from .probe without locking */
static int imx_uart_rs485_config(struct uart_port *port, struct ktermios *termios,
struct serial_rs485 *rs485conf)
{
struct imx_port *sport = to_imx_port(port);
u32 ucr2, ufcr;
if (rs485conf->flags & SER_RS485_ENABLED) {
/* Enable receiver if low-active RTS signal is requested */
if (sport->have_rtscts && !sport->have_rtsgpio &&
!(rs485conf->flags & SER_RS485_RTS_ON_SEND))
rs485conf->flags |= SER_RS485_RX_DURING_TX;
/* disable transmitter */
ucr2 = imx_uart_readl(sport, UCR2);
if (rs485conf->flags & SER_RS485_RTS_AFTER_SEND)
imx_uart_rts_active(sport, &ucr2);
else
imx_uart_rts_inactive(sport, &ucr2);
imx_uart_writel(sport, ucr2, UCR2);
}
/* Make sure Rx is enabled in case Tx is active with Rx disabled */
if (!(rs485conf->flags & SER_RS485_ENABLED) ||
rs485conf->flags & SER_RS485_RX_DURING_TX) {
/* If the receiver trigger is 0, set it to a default value */
ufcr = imx_uart_readl(sport, UFCR);
if ((ufcr & UFCR_RXTL_MASK) == 0)
imx_uart_setup_ufcr(sport, TXTL_DEFAULT, sport->rxtl);
imx_uart_start_rx(port);
}
return 0;
}
static const struct uart_ops imx_uart_pops = {
.tx_empty = imx_uart_tx_empty,
.set_mctrl = imx_uart_set_mctrl,
.get_mctrl = imx_uart_get_mctrl,
.stop_tx = imx_uart_stop_tx,
.start_tx = imx_uart_start_tx,
.stop_rx = imx_uart_stop_rx,
.enable_ms = imx_uart_enable_ms,
.break_ctl = imx_uart_break_ctl,
.startup = imx_uart_startup,
.shutdown = imx_uart_shutdown,
.flush_buffer = imx_uart_flush_buffer,
.set_termios = imx_uart_set_termios,
.type = imx_uart_type,
.config_port = imx_uart_config_port,
.verify_port = imx_uart_verify_port,
#if defined(CONFIG_CONSOLE_POLL)
.poll_init = imx_uart_poll_init,
.poll_get_char = imx_uart_poll_get_char,
.poll_put_char = imx_uart_poll_put_char,
#endif
};
static struct imx_port *imx_uart_ports[UART_NR];
#if IS_ENABLED(CONFIG_SERIAL_IMX_CONSOLE)
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 09:08:31 +01:00
static void imx_uart_console_putchar(struct uart_port *port, unsigned char ch)
{
struct imx_port *sport = to_imx_port(port);
while (imx_uart_readl(sport, imx_uart_uts_reg(sport)) & UTS_TXFULL)
barrier();
imx_uart_writel(sport, ch, URTX0);
sport->last_putchar_was_newline = (ch == '\n');
}
static void imx_uart_console_device_lock(struct console *co, unsigned long *flags)
{
struct uart_port *up = &imx_uart_ports[co->index]->port;
return __uart_port_lock_irqsave(up, flags);
}
static void imx_uart_console_device_unlock(struct console *co, unsigned long flags)
{
struct uart_port *up = &imx_uart_ports[co->index]->port;
return __uart_port_unlock_irqrestore(up, flags);
}
static void imx_uart_console_write_atomic(struct console *co,
struct nbcon_write_context *wctxt)
{
struct imx_port *sport = imx_uart_ports[co->index];
struct uart_port *port = &sport->port;
struct imx_port_ucrs old_ucr;
unsigned int ucr1, usr2;
if (!nbcon_enter_unsafe(wctxt))
return;
/*
* First, save UCR1/2/3 and then disable interrupts
*/
imx_uart_ucrs_save(sport, &old_ucr);
ucr1 = old_ucr.ucr1;
if (imx_uart_is_imx1(sport))
ucr1 |= IMX1_UCR1_UARTCLKEN;
ucr1 |= UCR1_UARTEN;
ucr1 &= ~(UCR1_TRDYEN | UCR1_RRDYEN | UCR1_RTSDEN);
imx_uart_writel(sport, ucr1, UCR1);
imx_uart_writel(sport, old_ucr.ucr2 | UCR2_TXEN, UCR2);
if (!sport->last_putchar_was_newline)
uart_console_write(port, "\n", 1, imx_uart_console_putchar);
uart_console_write(port, wctxt->outbuf, wctxt->len,
imx_uart_console_putchar);
/*
* Finally, wait for transmitter to become empty
* and restore UCR1/2/3
*/
read_poll_timeout_atomic(imx_uart_readl, usr2, usr2 & USR2_TXDC,
0, USEC_PER_SEC, false, sport, USR2);
imx_uart_ucrs_restore(sport, &old_ucr);
nbcon_exit_unsafe(wctxt);
}
static void imx_uart_console_write_thread(struct console *co,
struct nbcon_write_context *wctxt)
{
struct imx_port *sport = imx_uart_ports[co->index];
struct uart_port *port = &sport->port;
struct imx_port_ucrs old_ucr;
unsigned int ucr1, usr2;
if (!nbcon_enter_unsafe(wctxt))
return;
/*
* First, save UCR1/2/3 and then disable interrupts
*/
imx_uart_ucrs_save(sport, &old_ucr);
ucr1 = old_ucr.ucr1;
if (imx_uart_is_imx1(sport))
ucr1 |= IMX1_UCR1_UARTCLKEN;
ucr1 |= UCR1_UARTEN;
ucr1 &= ~(UCR1_TRDYEN | UCR1_RRDYEN | UCR1_RTSDEN);
imx_uart_writel(sport, ucr1, UCR1);
imx_uart_writel(sport, old_ucr.ucr2 | UCR2_TXEN, UCR2);
if (nbcon_exit_unsafe(wctxt)) {
int len = READ_ONCE(wctxt->len);
int i;
/*
* Write out the message. Toggle unsafe for each byte in order
* to give another (higher priority) context the opportunity
* for a friendly takeover. If such a takeover occurs, this
* context must reacquire ownership in order to perform final
* actions (such as re-enabling the interrupts).
*
* IMPORTANT: wctxt->outbuf and wctxt->len are no longer valid
* after a reacquire so writing the message must be
* aborted.
*/
for (i = 0; i < len; i++) {
if (!nbcon_enter_unsafe(wctxt))
break;
uart_console_write(port, wctxt->outbuf + i, 1,
imx_uart_console_putchar);
if (!nbcon_exit_unsafe(wctxt))
break;
}
}
while (!nbcon_enter_unsafe(wctxt))
nbcon_reacquire_nobuf(wctxt);
/*
* Finally, wait for transmitter to become empty
* and restore UCR1/2/3
*/
read_poll_timeout(imx_uart_readl, usr2, usr2 & USR2_TXDC,
0, USEC_PER_SEC, false, sport, USR2);
imx_uart_ucrs_restore(sport, &old_ucr);
nbcon_exit_unsafe(wctxt);
}
/*
* If the port was already initialised (eg, by a boot loader),
* try to determine the current setup.
*/
serial: imx: fix detach/attach of serial console If the device used as a serial console gets detached/attached at runtime, register_console() will try to call imx_uart_setup_console(), but this is not possible since it is marked as __init. For instance # cat /sys/devices/virtual/tty/console/active tty1 ttymxc0 # echo -n N > /sys/devices/virtual/tty/console/subsystem/ttymxc0/console # echo -n Y > /sys/devices/virtual/tty/console/subsystem/ttymxc0/console [ 73.166649] 8<--- cut here --- [ 73.167005] Unable to handle kernel paging request at virtual address c154d928 [ 73.167601] pgd = 55433e84 [ 73.167875] [c154d928] *pgd=8141941e(bad) [ 73.168304] Internal error: Oops: 8000000d [#1] SMP ARM [ 73.168429] Modules linked in: [ 73.168522] CPU: 0 PID: 536 Comm: sh Not tainted 5.15.0-rc6-00056-g3968ddcf05fb #3 [ 73.168675] Hardware name: Freescale i.MX6 Ultralite (Device Tree) [ 73.168791] PC is at imx_uart_console_setup+0x0/0x238 [ 73.168927] LR is at try_enable_new_console+0x98/0x124 [ 73.169056] pc : [<c154d928>] lr : [<c0196f44>] psr: a0000013 [ 73.169178] sp : c2ef5e70 ip : 00000000 fp : 00000000 [ 73.169281] r10: 00000000 r9 : c02cf970 r8 : 00000000 [ 73.169389] r7 : 00000001 r6 : 00000001 r5 : c1760164 r4 : c1e0fb08 [ 73.169512] r3 : c154d928 r2 : 00000000 r1 : efffcbd1 r0 : c1760164 [ 73.169641] Flags: NzCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment none [ 73.169782] Control: 10c5387d Table: 8345406a DAC: 00000051 [ 73.169895] Register r0 information: non-slab/vmalloc memory [ 73.170032] Register r1 information: non-slab/vmalloc memory [ 73.170158] Register r2 information: NULL pointer [ 73.170273] Register r3 information: non-slab/vmalloc memory [ 73.170397] Register r4 information: non-slab/vmalloc memory [ 73.170521] Register r5 information: non-slab/vmalloc memory [ 73.170647] Register r6 information: non-paged memory [ 73.170771] Register r7 information: non-paged memory [ 73.170892] Register r8 information: NULL pointer [ 73.171009] Register r9 information: non-slab/vmalloc memory [ 73.171142] Register r10 information: NULL pointer [ 73.171259] Register r11 information: NULL pointer [ 73.171375] Register r12 information: NULL pointer [ 73.171494] Process sh (pid: 536, stack limit = 0xcd1ba82f) [ 73.171621] Stack: (0xc2ef5e70 to 0xc2ef6000) [ 73.171731] 5e60: ???????? ???????? ???????? ???????? [ 73.171899] 5e80: ???????? ???????? ???????? ???????? ???????? ???????? ???????? ???????? [ 73.172059] 5ea0: ???????? ???????? ???????? ???????? ???????? ???????? ???????? ???????? [ 73.172217] 5ec0: ???????? ???????? ???????? ???????? ???????? ???????? ???????? ???????? [ 73.172377] 5ee0: ???????? ???????? ???????? ???????? ???????? ???????? ???????? ???????? [ 73.172537] 5f00: ???????? ???????? ???????? ???????? ???????? ???????? ???????? ???????? [ 73.172698] 5f20: ???????? ???????? ???????? ???????? ???????? ???????? ???????? ???????? [ 73.172856] 5f40: ???????? ???????? ???????? ???????? ???????? ???????? ???????? ???????? [ 73.173016] 5f60: ???????? ???????? ???????? ???????? ???????? ???????? ???????? ???????? [ 73.173177] 5f80: ???????? ???????? ???????? ???????? ???????? ???????? ???????? ???????? [ 73.173336] 5fa0: ???????? ???????? ???????? ???????? ???????? ???????? ???????? ???????? [ 73.173496] 5fc0: ???????? ???????? ???????? ???????? ???????? ???????? ???????? ???????? [ 73.173654] 5fe0: ???????? ???????? ???????? ???????? ???????? ???????? ???????? ???????? [ 73.173826] [<c0196f44>] (try_enable_new_console) from [<c01984a8>] (register_console+0x10c/0x2ec) [ 73.174053] [<c01984a8>] (register_console) from [<c06e2c90>] (console_store+0x14c/0x168) [ 73.174262] [<c06e2c90>] (console_store) from [<c0383718>] (kernfs_fop_write_iter+0x110/0x1cc) [ 73.174470] [<c0383718>] (kernfs_fop_write_iter) from [<c02cf5f4>] (vfs_write+0x31c/0x548) [ 73.174679] [<c02cf5f4>] (vfs_write) from [<c02cf970>] (ksys_write+0x60/0xec) [ 73.174863] [<c02cf970>] (ksys_write) from [<c0100080>] (ret_fast_syscall+0x0/0x1c) [ 73.175052] Exception stack(0xc2ef5fa8 to 0xc2ef5ff0) [ 73.175167] 5fa0: ???????? ???????? ???????? ???????? ???????? ???????? [ 73.175327] 5fc0: ???????? ???????? ???????? ???????? ???????? ???????? ???????? ???????? [ 73.175486] 5fe0: ???????? ???????? ???????? ???????? [ 73.175608] Code: 00000000 00000000 00000000 00000000 (00000000) [ 73.175744] ---[ end trace 9b75121265109bf1 ]--- A similar issue could be triggered by unbinding/binding the serial console device [*]. Drop __init so that imx_uart_setup_console() can be safely called at runtime. [*] https://lore.kernel.org/all/20181114174940.7865-3-stefan@agner.ch/ Fixes: a3cb39d258ef ("serial: core: Allow detach and attach serial device for console") Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com> Acked-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de> Signed-off-by: Stefan Agner <stefan@agner.ch> Signed-off-by: Francesco Dolcini <francesco.dolcini@toradex.com> Link: https://lore.kernel.org/r/20211020192643.476895-2-francesco.dolcini@toradex.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2021-10-20 21:26:42 +02:00
static void
imx_uart_console_get_options(struct imx_port *sport, int *baud,
int *parity, int *bits)
{
if (imx_uart_readl(sport, UCR1) & UCR1_UARTEN) {
/* ok, the port was enabled */
unsigned int ucr2, ubir, ubmr, uartclk;
unsigned int baud_raw;
unsigned int ucfr_rfdiv;
ucr2 = imx_uart_readl(sport, UCR2);
*parity = 'n';
if (ucr2 & UCR2_PREN) {
if (ucr2 & UCR2_PROE)
*parity = 'o';
else
*parity = 'e';
}
if (ucr2 & UCR2_WS)
*bits = 8;
else
*bits = 7;
ubir = imx_uart_readl(sport, UBIR) & 0xffff;
ubmr = imx_uart_readl(sport, UBMR) & 0xffff;
ucfr_rfdiv = (imx_uart_readl(sport, UFCR) & UFCR_RFDIV) >> 7;
if (ucfr_rfdiv == 6)
ucfr_rfdiv = 7;
else
ucfr_rfdiv = 6 - ucfr_rfdiv;
uartclk = clk_get_rate(sport->clk_per);
uartclk /= ucfr_rfdiv;
{ /*
* The next code provides exact computation of
* baud_raw = round(((uartclk/16) * (ubir + 1)) / (ubmr + 1))
* without need of float support or long long division,
* which would be required to prevent 32bit arithmetic overflow
*/
unsigned int mul = ubir + 1;
unsigned int div = 16 * (ubmr + 1);
unsigned int rem = uartclk % div;
baud_raw = (uartclk / div) * mul;
baud_raw += (rem * mul + div / 2) / div;
*baud = (baud_raw + 50) / 100 * 100;
}
if (*baud != baud_raw)
dev_info(sport->port.dev, "Console IMX rounded baud rate from %d to %d\n",
baud_raw, *baud);
}
}
serial: imx: fix detach/attach of serial console If the device used as a serial console gets detached/attached at runtime, register_console() will try to call imx_uart_setup_console(), but this is not possible since it is marked as __init. For instance # cat /sys/devices/virtual/tty/console/active tty1 ttymxc0 # echo -n N > /sys/devices/virtual/tty/console/subsystem/ttymxc0/console # echo -n Y > /sys/devices/virtual/tty/console/subsystem/ttymxc0/console [ 73.166649] 8<--- cut here --- [ 73.167005] Unable to handle kernel paging request at virtual address c154d928 [ 73.167601] pgd = 55433e84 [ 73.167875] [c154d928] *pgd=8141941e(bad) [ 73.168304] Internal error: Oops: 8000000d [#1] SMP ARM [ 73.168429] Modules linked in: [ 73.168522] CPU: 0 PID: 536 Comm: sh Not tainted 5.15.0-rc6-00056-g3968ddcf05fb #3 [ 73.168675] Hardware name: Freescale i.MX6 Ultralite (Device Tree) [ 73.168791] PC is at imx_uart_console_setup+0x0/0x238 [ 73.168927] LR is at try_enable_new_console+0x98/0x124 [ 73.169056] pc : [<c154d928>] lr : [<c0196f44>] psr: a0000013 [ 73.169178] sp : c2ef5e70 ip : 00000000 fp : 00000000 [ 73.169281] r10: 00000000 r9 : c02cf970 r8 : 00000000 [ 73.169389] r7 : 00000001 r6 : 00000001 r5 : c1760164 r4 : c1e0fb08 [ 73.169512] r3 : c154d928 r2 : 00000000 r1 : efffcbd1 r0 : c1760164 [ 73.169641] Flags: NzCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment none [ 73.169782] Control: 10c5387d Table: 8345406a DAC: 00000051 [ 73.169895] Register r0 information: non-slab/vmalloc memory [ 73.170032] Register r1 information: non-slab/vmalloc memory [ 73.170158] Register r2 information: NULL pointer [ 73.170273] Register r3 information: non-slab/vmalloc memory [ 73.170397] Register r4 information: non-slab/vmalloc memory [ 73.170521] Register r5 information: non-slab/vmalloc memory [ 73.170647] Register r6 information: non-paged memory [ 73.170771] Register r7 information: non-paged memory [ 73.170892] Register r8 information: NULL pointer [ 73.171009] Register r9 information: non-slab/vmalloc memory [ 73.171142] Register r10 information: NULL pointer [ 73.171259] Register r11 information: NULL pointer [ 73.171375] Register r12 information: NULL pointer [ 73.171494] Process sh (pid: 536, stack limit = 0xcd1ba82f) [ 73.171621] Stack: (0xc2ef5e70 to 0xc2ef6000) [ 73.171731] 5e60: ???????? ???????? ???????? ???????? [ 73.171899] 5e80: ???????? ???????? ???????? ???????? ???????? ???????? ???????? ???????? [ 73.172059] 5ea0: ???????? ???????? ???????? ???????? ???????? ???????? ???????? ???????? [ 73.172217] 5ec0: ???????? ???????? ???????? ???????? ???????? ???????? ???????? ???????? [ 73.172377] 5ee0: ???????? ???????? ???????? ???????? ???????? ???????? ???????? ???????? [ 73.172537] 5f00: ???????? ???????? ???????? ???????? ???????? ???????? ???????? ???????? [ 73.172698] 5f20: ???????? ???????? ???????? ???????? ???????? ???????? ???????? ???????? [ 73.172856] 5f40: ???????? ???????? ???????? ???????? ???????? ???????? ???????? ???????? [ 73.173016] 5f60: ???????? ???????? ???????? ???????? ???????? ???????? ???????? ???????? [ 73.173177] 5f80: ???????? ???????? ???????? ???????? ???????? ???????? ???????? ???????? [ 73.173336] 5fa0: ???????? ???????? ???????? ???????? ???????? ???????? ???????? ???????? [ 73.173496] 5fc0: ???????? ???????? ???????? ???????? ???????? ???????? ???????? ???????? [ 73.173654] 5fe0: ???????? ???????? ???????? ???????? ???????? ???????? ???????? ???????? [ 73.173826] [<c0196f44>] (try_enable_new_console) from [<c01984a8>] (register_console+0x10c/0x2ec) [ 73.174053] [<c01984a8>] (register_console) from [<c06e2c90>] (console_store+0x14c/0x168) [ 73.174262] [<c06e2c90>] (console_store) from [<c0383718>] (kernfs_fop_write_iter+0x110/0x1cc) [ 73.174470] [<c0383718>] (kernfs_fop_write_iter) from [<c02cf5f4>] (vfs_write+0x31c/0x548) [ 73.174679] [<c02cf5f4>] (vfs_write) from [<c02cf970>] (ksys_write+0x60/0xec) [ 73.174863] [<c02cf970>] (ksys_write) from [<c0100080>] (ret_fast_syscall+0x0/0x1c) [ 73.175052] Exception stack(0xc2ef5fa8 to 0xc2ef5ff0) [ 73.175167] 5fa0: ???????? ???????? ???????? ???????? ???????? ???????? [ 73.175327] 5fc0: ???????? ???????? ???????? ???????? ???????? ???????? ???????? ???????? [ 73.175486] 5fe0: ???????? ???????? ???????? ???????? [ 73.175608] Code: 00000000 00000000 00000000 00000000 (00000000) [ 73.175744] ---[ end trace 9b75121265109bf1 ]--- A similar issue could be triggered by unbinding/binding the serial console device [*]. Drop __init so that imx_uart_setup_console() can be safely called at runtime. [*] https://lore.kernel.org/all/20181114174940.7865-3-stefan@agner.ch/ Fixes: a3cb39d258ef ("serial: core: Allow detach and attach serial device for console") Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com> Acked-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de> Signed-off-by: Stefan Agner <stefan@agner.ch> Signed-off-by: Francesco Dolcini <francesco.dolcini@toradex.com> Link: https://lore.kernel.org/r/20211020192643.476895-2-francesco.dolcini@toradex.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2021-10-20 21:26:42 +02:00
static int
imx_uart_console_setup(struct console *co, char *options)
{
struct imx_port *sport;
int baud = 9600;
int bits = 8;
int parity = 'n';
int flow = 'n';
int retval;
/*
* Check whether an invalid uart number has been specified, and
* if so, search for the first available port that does have
* console support.
*/
if (co->index == -1 || co->index >= ARRAY_SIZE(imx_uart_ports))
co->index = 0;
sport = imx_uart_ports[co->index];
if (sport == NULL)
return -ENODEV;
/* For setting the registers, we only need to enable the ipg clock. */
retval = clk_prepare_enable(sport->clk_ipg);
if (retval)
goto error_console;
sport->last_putchar_was_newline = true;
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
else
imx_uart_console_get_options(sport, &baud, &parity, &bits);
imx_uart_setup_ufcr(sport, TXTL_DEFAULT, sport->rxtl);
retval = uart_set_options(&sport->port, co, baud, parity, bits, flow);
Revert "serial: imx: remove unbalanced clk_prepare" This reverts commit 9e7b399d6528eac33a6fbfceb2b92af209c3454d. Commit ("9e7b399d6528ea") causes the following warning and sometimes also hangs the system: ------------[ cut here ]------------ WARNING: CPU: 0 PID: 0 at kernel/locking/mutex.c:868 mutex_trylock+0x20c/0x22c() DEBUG_LOCKS_WARN_ON(in_interrupt()) Modules linked in: CPU: 0 PID: 0 Comm: swapper/0 Not tainted 4.2.0-rc7-next-20150818-00001-g14418a6 #4 Hardware name: Freescale i.MX6 Quad/DualLite (Device Tree) Backtrace: [<80012f08>] (dump_backtrace) from [<800130a4>] (show_stack+0x18/0x1c) r6:00000364 r5:00000000 r4:00000000 r3:00000000 [<8001308c>] (show_stack) from [<807902b8>] (dump_stack+0x88/0xa4) [<80790230>] (dump_stack) from [<8002a604>] (warn_slowpath_common+0x80/0xbc) r5:807945c4 r4:80ab3b50 [<8002a584>] (warn_slowpath_common) from [<8002a6e4>] (warn_slowpath_fmt+0x38/0x40) r8:00000000 r7:8131100c r6:8054c3cc r5:8131300c r4:80b0a570 [<8002a6b0>] (warn_slowpath_fmt) from [<807945c4>] (mutex_trylock+0x20c/0x22c) r3:8095d0d8 r2:8095ab28 [<807943b8>] (mutex_trylock) from [<8054c3cc>] (clk_prepare_lock+0x14/0xf4) r7:8131100c r6:be3f0c80 r5:00000037 r4:be3f0c80 [<8054c3b8>] (clk_prepare_lock) from [<8054dbfc>] (clk_prepare+0x18/0x30) r5:00000037 r4:be3f0c80 [<8054dbe4>] (clk_prepare) from [<8036a600>] (imx_console_write+0x30/0x244) r4:812d0bc8 r3:8132b9a4 To reproduce the problem we only need to let the board idle for something like 30 seconds. Tested on a imx6q-sabresd. Signed-off-by: Fabio Estevam <fabio.estevam@freescale.com> Reviewed-by: Eduardo Valentin <edubezval@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2015-08-18 12:43:12 -03:00
if (retval) {
clk_disable_unprepare(sport->clk_ipg);
Revert "serial: imx: remove unbalanced clk_prepare" This reverts commit 9e7b399d6528eac33a6fbfceb2b92af209c3454d. Commit ("9e7b399d6528ea") causes the following warning and sometimes also hangs the system: ------------[ cut here ]------------ WARNING: CPU: 0 PID: 0 at kernel/locking/mutex.c:868 mutex_trylock+0x20c/0x22c() DEBUG_LOCKS_WARN_ON(in_interrupt()) Modules linked in: CPU: 0 PID: 0 Comm: swapper/0 Not tainted 4.2.0-rc7-next-20150818-00001-g14418a6 #4 Hardware name: Freescale i.MX6 Quad/DualLite (Device Tree) Backtrace: [<80012f08>] (dump_backtrace) from [<800130a4>] (show_stack+0x18/0x1c) r6:00000364 r5:00000000 r4:00000000 r3:00000000 [<8001308c>] (show_stack) from [<807902b8>] (dump_stack+0x88/0xa4) [<80790230>] (dump_stack) from [<8002a604>] (warn_slowpath_common+0x80/0xbc) r5:807945c4 r4:80ab3b50 [<8002a584>] (warn_slowpath_common) from [<8002a6e4>] (warn_slowpath_fmt+0x38/0x40) r8:00000000 r7:8131100c r6:8054c3cc r5:8131300c r4:80b0a570 [<8002a6b0>] (warn_slowpath_fmt) from [<807945c4>] (mutex_trylock+0x20c/0x22c) r3:8095d0d8 r2:8095ab28 [<807943b8>] (mutex_trylock) from [<8054c3cc>] (clk_prepare_lock+0x14/0xf4) r7:8131100c r6:be3f0c80 r5:00000037 r4:be3f0c80 [<8054c3b8>] (clk_prepare_lock) from [<8054dbfc>] (clk_prepare+0x18/0x30) r5:00000037 r4:be3f0c80 [<8054dbe4>] (clk_prepare) from [<8036a600>] (imx_console_write+0x30/0x244) r4:812d0bc8 r3:8132b9a4 To reproduce the problem we only need to let the board idle for something like 30 seconds. Tested on a imx6q-sabresd. Signed-off-by: Fabio Estevam <fabio.estevam@freescale.com> Reviewed-by: Eduardo Valentin <edubezval@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2015-08-18 12:43:12 -03:00
goto error_console;
}
retval = clk_prepare_enable(sport->clk_per);
Revert "serial: imx: remove unbalanced clk_prepare" This reverts commit 9e7b399d6528eac33a6fbfceb2b92af209c3454d. Commit ("9e7b399d6528ea") causes the following warning and sometimes also hangs the system: ------------[ cut here ]------------ WARNING: CPU: 0 PID: 0 at kernel/locking/mutex.c:868 mutex_trylock+0x20c/0x22c() DEBUG_LOCKS_WARN_ON(in_interrupt()) Modules linked in: CPU: 0 PID: 0 Comm: swapper/0 Not tainted 4.2.0-rc7-next-20150818-00001-g14418a6 #4 Hardware name: Freescale i.MX6 Quad/DualLite (Device Tree) Backtrace: [<80012f08>] (dump_backtrace) from [<800130a4>] (show_stack+0x18/0x1c) r6:00000364 r5:00000000 r4:00000000 r3:00000000 [<8001308c>] (show_stack) from [<807902b8>] (dump_stack+0x88/0xa4) [<80790230>] (dump_stack) from [<8002a604>] (warn_slowpath_common+0x80/0xbc) r5:807945c4 r4:80ab3b50 [<8002a584>] (warn_slowpath_common) from [<8002a6e4>] (warn_slowpath_fmt+0x38/0x40) r8:00000000 r7:8131100c r6:8054c3cc r5:8131300c r4:80b0a570 [<8002a6b0>] (warn_slowpath_fmt) from [<807945c4>] (mutex_trylock+0x20c/0x22c) r3:8095d0d8 r2:8095ab28 [<807943b8>] (mutex_trylock) from [<8054c3cc>] (clk_prepare_lock+0x14/0xf4) r7:8131100c r6:be3f0c80 r5:00000037 r4:be3f0c80 [<8054c3b8>] (clk_prepare_lock) from [<8054dbfc>] (clk_prepare+0x18/0x30) r5:00000037 r4:be3f0c80 [<8054dbe4>] (clk_prepare) from [<8036a600>] (imx_console_write+0x30/0x244) r4:812d0bc8 r3:8132b9a4 To reproduce the problem we only need to let the board idle for something like 30 seconds. Tested on a imx6q-sabresd. Signed-off-by: Fabio Estevam <fabio.estevam@freescale.com> Reviewed-by: Eduardo Valentin <edubezval@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2015-08-18 12:43:12 -03:00
if (retval)
clk_disable_unprepare(sport->clk_ipg);
error_console:
return retval;
}
static int
imx_uart_console_exit(struct console *co)
{
struct imx_port *sport = imx_uart_ports[co->index];
clk_disable_unprepare(sport->clk_per);
clk_disable_unprepare(sport->clk_ipg);
return 0;
}
static struct uart_driver imx_uart_uart_driver;
static struct console imx_uart_console = {
.name = DEV_NAME,
.write_atomic = imx_uart_console_write_atomic,
.write_thread = imx_uart_console_write_thread,
.device_lock = imx_uart_console_device_lock,
.device_unlock = imx_uart_console_device_unlock,
.flags = CON_PRINTBUFFER | CON_NBCON,
.device = uart_console_device,
.setup = imx_uart_console_setup,
.exit = imx_uart_console_exit,
.index = -1,
.data = &imx_uart_uart_driver,
};
#define IMX_CONSOLE &imx_uart_console
#else
#define IMX_CONSOLE NULL
#endif
static struct uart_driver imx_uart_uart_driver = {
.owner = THIS_MODULE,
.driver_name = DRIVER_NAME,
.dev_name = DEV_NAME,
.major = SERIAL_IMX_MAJOR,
.minor = MINOR_START,
.nr = ARRAY_SIZE(imx_uart_ports),
.cons = IMX_CONSOLE,
};
static enum hrtimer_restart imx_trigger_start_tx(struct hrtimer *t)
{
struct imx_port *sport = container_of(t, struct imx_port, trigger_start_tx);
unsigned long flags;
uart_port_lock_irqsave(&sport->port, &flags);
if (sport->tx_state == WAIT_AFTER_RTS)
imx_uart_start_tx(&sport->port);
uart_port_unlock_irqrestore(&sport->port, flags);
return HRTIMER_NORESTART;
}
static enum hrtimer_restart imx_trigger_stop_tx(struct hrtimer *t)
{
struct imx_port *sport = container_of(t, struct imx_port, trigger_stop_tx);
unsigned long flags;
uart_port_lock_irqsave(&sport->port, &flags);
if (sport->tx_state == WAIT_AFTER_SEND)
imx_uart_stop_tx(&sport->port);
uart_port_unlock_irqrestore(&sport->port, flags);
return HRTIMER_NORESTART;
}
static const struct serial_rs485 imx_rs485_supported = {
.flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | SER_RS485_RTS_AFTER_SEND |
SER_RS485_RX_DURING_TX,
.delay_rts_before_send = 1,
.delay_rts_after_send = 1,
};
/* Default RX DMA buffer configuration */
#define RX_DMA_PERIODS 16
#define RX_DMA_PERIOD_LEN (PAGE_SIZE / 4)
static int imx_uart_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct imx_port *sport;
void __iomem *base;
u32 dma_buf_conf[2];
int ret = 0;
tty: serial: imx: Handle RS485 DE signal active high The default polarity of RS485 DE signal is active high. This driver does not handle such case properly. Currently, when a pin is multiplexed as a UART CTS_B on boot, this pin is pulled HIGH by the i.MX UART CTS circuit, which activates DE signal on the RS485 transceiver and thus behave as if the RS485 was transmitting data, so the system blocks the RS485 bus when it starts and until user application takes over. This behavior is not OK. The problem consists of two separate parts. First, the i.MX UART IP requires UCR1 UARTEN and UCR2 RXEN to be set for UCR2 CTSC and CTS bits to have any effect. The UCR2 CTSC bit permits the driver to set CTS (RTS_B or RS485 DE signal) to either level sychronous to the internal UART IP clock. Compared to other options, like GPIO CTS control, this has the benefit of being synchronous to the UART IP clock and thus without glitches or bus delays. The reason for the CTS design is likely because when the Receiver is disabled, the UART IP can never indicate that it is ready to receive data by assering CTS signal, so the CTS is always pulled HIGH by default. When the port is closed by user space, imx_uart_stop_rx() clears UCR2 RXEN bit, and imx_uart_shutdown() clears UCR1 UARTEN bit. This disables UART Receiver and UART itself, and forces CTS signal HIGH, which leads to the RS485 bus being blocked because RS485 DE is incorrectly active. The proposed solution for this problem is to keep the Receiver running even after the port is closed, but in loopback mode. This disconnects the RX FIFO input from the RXD external signal, and since UCR2 TXEN is cleared, the UART Transmitter is disabled, so nothing can feed data in the RX FIFO. Because the Receiver is still enabled, the UCR2 CTSC and CTS bits still have effect and the CTS (RS485 DE) control is retained. Note that in case of RS485 DE signal active low, there is no problem and no special handling is necessary. The CTS signal defaults to HIGH, thus the RS485 is by default set to Receive and the bus is not blocked. Note that while there is the possibility to control CTS using GPIO with either CTS polarity, this has the downside of not being synchronous to the UART IP clock and thus glitchy and susceptible to slow DE switching. Second, on boot, before the UART driver probe callback is called, the driver core triggers pinctrl_init_done() and configures the IOMUXC to default state. At this point, UCR1 UARTEN and UCR2 RXEN are both still cleared, but UART CTS_B (RS485 DE) is configured as CTS function, thus the RTS signal is pulled HIGH by the UART IP CTS circuit. One part of the solution here is to enable UCR1 UARTEN and UCR2 RXEN and UTS loopback in this driver probe callback, thus unblocking the CTSC and CTS control early on. But this is still too late, since the pin control is already configured and CTS has been pulled HIGH for a short period of time. When Linux kernel boots and this driver is bound, the pin control is set to special "init" state if the state is available, and driver can switch the "default" state afterward when ready. This state can be used to set the CTS line as a GPIO in DT temporarily, and a GPIO hog can force such GPIO to LOW, thus keeping the RS485 DE line LOW early on boot. Once the driver takes over and UCR1 UARTEN and UCR2 RXEN and UTS loopback are all enabled, the driver can switch to "default" pin control state and control the CTS line as function instead. DT binding example is below: " &gpio6 { rts-init-hog { gpio-hog; gpios = <5 0>; output-low; line-name = "rs485-de"; }; }; &uart5 { /* DHCOM UART2 */ pinctrl-0 = <&pinctrl_uart5>; pinctrl-1 = <&pinctrl_uart5_init>; pinctrl-names = "default", "init"; ... }; pinctrl_uart5_init: uart5-init-grp { fsl,pins = < ... MX6QDL_PAD_CSI0_DAT19__GPIO6_IO05 0x30b1 >; }; pinctrl_uart5: uart5-grp { fsl,pins = < ... MX6QDL_PAD_CSI0_DAT19__UART5_CTS_B 0x30b1 >; }; " Tested-by: Christoph Niedermaier <cniedermaier@dh-electronics.com> Reviewed-by: Fabio Estevam <festevam@denx.de> Signed-off-by: Marek Vasut <marex@denx.de> Link: https://lore.kernel.org/r/20220929144400.13571-1-marex@denx.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2022-09-29 16:44:00 +02:00
u32 ucr1, ucr2, uts;
struct resource *res;
int txirq, rxirq, rtsirq;
sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL);
if (!sport)
return -ENOMEM;
sport->devdata = of_device_get_match_data(&pdev->dev);
ret = of_alias_get_id(np, "serial");
if (ret < 0) {
dev_err(&pdev->dev, "failed to get alias id, errno %d\n", ret);
return ret;
}
sport->port.line = ret;
sport->have_rtscts = of_property_read_bool(np, "uart-has-rtscts") ||
of_property_read_bool(np, "fsl,uart-has-rtscts"); /* deprecated */
sport->dte_mode = of_property_read_bool(np, "fsl,dte-mode");
sport->have_rtsgpio = of_property_present(np, "rts-gpios");
sport->inverted_tx = of_property_read_bool(np, "fsl,inverted-tx");
sport->inverted_rx = of_property_read_bool(np, "fsl,inverted-rx");
if (!of_property_read_u32_array(np, "fsl,dma-info", dma_buf_conf, 2)) {
sport->rx_period_length = dma_buf_conf[0];
sport->rx_periods = dma_buf_conf[1];
} else {
sport->rx_period_length = RX_DMA_PERIOD_LEN;
sport->rx_periods = RX_DMA_PERIODS;
}
if (sport->port.line >= ARRAY_SIZE(imx_uart_ports)) {
dev_err(&pdev->dev, "serial%d out of range\n",
sport->port.line);
return -EINVAL;
}
base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(base))
return PTR_ERR(base);
rxirq = platform_get_irq(pdev, 0);
if (rxirq < 0)
return rxirq;
txirq = platform_get_irq_optional(pdev, 1);
rtsirq = platform_get_irq_optional(pdev, 2);
sport->port.dev = &pdev->dev;
sport->port.mapbase = res->start;
sport->port.membase = base;
sport->port.type = PORT_IMX;
sport->port.iotype = UPIO_MEM;
sport->port.irq = rxirq;
sport->port.fifosize = 32;
sport->port.has_sysrq = IS_ENABLED(CONFIG_SERIAL_IMX_CONSOLE);
sport->port.ops = &imx_uart_pops;
sport->port.rs485_config = imx_uart_rs485_config;
/* RTS is required to control the RS485 transmitter */
if (sport->have_rtscts || sport->have_rtsgpio)
sport->port.rs485_supported = imx_rs485_supported;
sport->port.flags = UPF_BOOT_AUTOCONF;
timer_setup(&sport->timer, imx_uart_timeout, 0);
sport->gpios = mctrl_gpio_init(&sport->port, 0);
if (IS_ERR(sport->gpios))
return PTR_ERR(sport->gpios);
sport->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
if (IS_ERR(sport->clk_ipg)) {
ret = PTR_ERR(sport->clk_ipg);
dev_err(&pdev->dev, "failed to get ipg clk: %d\n", ret);
return ret;
}
sport->clk_per = devm_clk_get(&pdev->dev, "per");
if (IS_ERR(sport->clk_per)) {
ret = PTR_ERR(sport->clk_per);
dev_err(&pdev->dev, "failed to get per clk: %d\n", ret);
return ret;
}
sport->port.uartclk = clk_get_rate(sport->clk_per);
/* For register access, we only need to enable the ipg clock. */
ret = clk_prepare_enable(sport->clk_ipg);
if (ret) {
dev_err(&pdev->dev, "failed to enable ipg clk: %d\n", ret);
return ret;
}
ret = uart_get_rs485_mode(&sport->port);
if (ret)
goto err_clk;
serial: Make retrieval of rs485 properties platform-agnostic Commit ef838a81dd4d ("serial: Add common rs485 device tree parsing function") consolidated retrieval of rs485 OF properties in a common helper function but did not #ifdef it to CONFIG_OF. The function is therefore included on ACPI platforms as well even though it's not used. On the other hand ACPI platforms with rs485 do exist (e.g. Siemens IOT2040) and they may leverage _DSD to store rs485 properties. Likewise, UART platform devices instantiated from an MFD should be able to specify rs485 properties. In fact, the tty subsystem maintainer had asked for a "generic" function during review of commit ef838a81dd4d: https://marc.info/?l=linux-serial&m=150143441725194&w=4 Thus, instead of constraining the helper to OF platforms, make it platform-agnostic by converting it to device_property_*() functions and renaming it accordingly. In imx.c, move the invocation of uart_get_rs485_mode() from serial_imx_probe_dt() to serial_imx_probe() so that it also gets called for non-OF devices. In omap-serial.c, move its invocation further up within serial_omap_probe_rs485() so that the RTS polarity can be overridden with the driver-specific "rs485-rts-active-high" property once we introduce a generic "rs485-rts-active-low" property. Cc: Jan Kiszka <jan.kiszka@siemens.com> Cc: Richard Genoud <richard.genoud@gmail.com> Cc: Sascha Hauer <s.hauer@pengutronix.de> Signed-off-by: Lukas Wunner <lukas@wunner.de> Acked-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2017-11-24 23:26:40 +01:00
/*
* If using the i.MX UART RTS/CTS control then the RTS (CTS_B)
* signal cannot be set low during transmission in case the
* receiver is off (limitation of the i.MX UART IP).
*/
if (sport->port.rs485.flags & SER_RS485_ENABLED &&
sport->have_rtscts && !sport->have_rtsgpio &&
(!(sport->port.rs485.flags & SER_RS485_RTS_ON_SEND) &&
!(sport->port.rs485.flags & SER_RS485_RX_DURING_TX)))
dev_err(&pdev->dev,
"low-active RTS not possible when receiver is off, enabling receiver\n");
/* Disable interrupts before requesting them */
ucr1 = imx_uart_readl(sport, UCR1);
ucr1 &= ~(UCR1_ADEN | UCR1_TRDYEN | UCR1_IDEN | UCR1_RRDYEN | UCR1_RTSDEN);
imx_uart_writel(sport, ucr1, UCR1);
tty: serial: imx: disable Ageing Timer interrupt request irq There maybe pending USR interrupt before requesting irq, however uart_add_one_port has not executed, so there will be kernel panic: [ 0.795668] Unable to handle kernel NULL pointer dereference at virtual addre ss 0000000000000080 [ 0.802701] Mem abort info: [ 0.805367] ESR = 0x0000000096000004 [ 0.808950] EC = 0x25: DABT (current EL), IL = 32 bits [ 0.814033] SET = 0, FnV = 0 [ 0.816950] EA = 0, S1PTW = 0 [ 0.819950] FSC = 0x04: level 0 translation fault [ 0.824617] Data abort info: [ 0.827367] ISV = 0, ISS = 0x00000004 [ 0.831033] CM = 0, WnR = 0 [ 0.833866] [0000000000000080] user address but active_mm is swapper [ 0.839951] Internal error: Oops: 0000000096000004 [#1] PREEMPT SMP [ 0.845953] Modules linked in: [ 0.848869] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 6.1.1+g56321e101aca #1 [ 0.855617] Hardware name: Freescale i.MX8MP EVK (DT) [ 0.860452] pstate: 000000c5 (nzcv daIF -PAN -UAO -TCO -DIT -SSBS BTYPE=--) [ 0.867117] pc : __imx_uart_rxint.constprop.0+0x11c/0x2c0 [ 0.872283] lr : imx_uart_int+0xf8/0x1ec The issue only happends in the inmate linux when Jailhouse hypervisor enabled. The test procedure is: while true; do jailhouse enable imx8mp.cell jailhouse cell linux xxxx sleep 10 jailhouse cell destroy 1 jailhouse disable sleep 5 done And during the upper test, press keys to the 2nd linux console. When `jailhouse cell destroy 1`, the 2nd linux has no chance to put the uart to a quiese state, so USR1/2 may has pending interrupts. Then when `jailhosue cell linux xx` to start 2nd linux again, the issue trigger. In order to disable irqs before requesting them, both UCR1 and UCR2 irqs should be disabled, so here fix that, disable the Ageing Timer interrupt in UCR2 as UCR1 does. Fixes: 8a61f0c70ae6 ("serial: imx: Disable irqs before requesting them") Suggested-by: Sherry Sun <sherry.sun@nxp.com> Reviewed-by: Sherry Sun <sherry.sun@nxp.com> Signed-off-by: Peng Fan <peng.fan@nxp.com> Acked-by: Jason Liu <jason.hui.liu@nxp.com> Link: https://lore.kernel.org/r/20230206013016.29352-1-sherry.sun@nxp.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-02-06 09:30:16 +08:00
/* Disable Ageing Timer interrupt */
ucr2 = imx_uart_readl(sport, UCR2);
ucr2 &= ~UCR2_ATEN;
imx_uart_writel(sport, ucr2, UCR2);
tty: serial: imx: Handle RS485 DE signal active high The default polarity of RS485 DE signal is active high. This driver does not handle such case properly. Currently, when a pin is multiplexed as a UART CTS_B on boot, this pin is pulled HIGH by the i.MX UART CTS circuit, which activates DE signal on the RS485 transceiver and thus behave as if the RS485 was transmitting data, so the system blocks the RS485 bus when it starts and until user application takes over. This behavior is not OK. The problem consists of two separate parts. First, the i.MX UART IP requires UCR1 UARTEN and UCR2 RXEN to be set for UCR2 CTSC and CTS bits to have any effect. The UCR2 CTSC bit permits the driver to set CTS (RTS_B or RS485 DE signal) to either level sychronous to the internal UART IP clock. Compared to other options, like GPIO CTS control, this has the benefit of being synchronous to the UART IP clock and thus without glitches or bus delays. The reason for the CTS design is likely because when the Receiver is disabled, the UART IP can never indicate that it is ready to receive data by assering CTS signal, so the CTS is always pulled HIGH by default. When the port is closed by user space, imx_uart_stop_rx() clears UCR2 RXEN bit, and imx_uart_shutdown() clears UCR1 UARTEN bit. This disables UART Receiver and UART itself, and forces CTS signal HIGH, which leads to the RS485 bus being blocked because RS485 DE is incorrectly active. The proposed solution for this problem is to keep the Receiver running even after the port is closed, but in loopback mode. This disconnects the RX FIFO input from the RXD external signal, and since UCR2 TXEN is cleared, the UART Transmitter is disabled, so nothing can feed data in the RX FIFO. Because the Receiver is still enabled, the UCR2 CTSC and CTS bits still have effect and the CTS (RS485 DE) control is retained. Note that in case of RS485 DE signal active low, there is no problem and no special handling is necessary. The CTS signal defaults to HIGH, thus the RS485 is by default set to Receive and the bus is not blocked. Note that while there is the possibility to control CTS using GPIO with either CTS polarity, this has the downside of not being synchronous to the UART IP clock and thus glitchy and susceptible to slow DE switching. Second, on boot, before the UART driver probe callback is called, the driver core triggers pinctrl_init_done() and configures the IOMUXC to default state. At this point, UCR1 UARTEN and UCR2 RXEN are both still cleared, but UART CTS_B (RS485 DE) is configured as CTS function, thus the RTS signal is pulled HIGH by the UART IP CTS circuit. One part of the solution here is to enable UCR1 UARTEN and UCR2 RXEN and UTS loopback in this driver probe callback, thus unblocking the CTSC and CTS control early on. But this is still too late, since the pin control is already configured and CTS has been pulled HIGH for a short period of time. When Linux kernel boots and this driver is bound, the pin control is set to special "init" state if the state is available, and driver can switch the "default" state afterward when ready. This state can be used to set the CTS line as a GPIO in DT temporarily, and a GPIO hog can force such GPIO to LOW, thus keeping the RS485 DE line LOW early on boot. Once the driver takes over and UCR1 UARTEN and UCR2 RXEN and UTS loopback are all enabled, the driver can switch to "default" pin control state and control the CTS line as function instead. DT binding example is below: " &gpio6 { rts-init-hog { gpio-hog; gpios = <5 0>; output-low; line-name = "rs485-de"; }; }; &uart5 { /* DHCOM UART2 */ pinctrl-0 = <&pinctrl_uart5>; pinctrl-1 = <&pinctrl_uart5_init>; pinctrl-names = "default", "init"; ... }; pinctrl_uart5_init: uart5-init-grp { fsl,pins = < ... MX6QDL_PAD_CSI0_DAT19__GPIO6_IO05 0x30b1 >; }; pinctrl_uart5: uart5-grp { fsl,pins = < ... MX6QDL_PAD_CSI0_DAT19__UART5_CTS_B 0x30b1 >; }; " Tested-by: Christoph Niedermaier <cniedermaier@dh-electronics.com> Reviewed-by: Fabio Estevam <festevam@denx.de> Signed-off-by: Marek Vasut <marex@denx.de> Link: https://lore.kernel.org/r/20220929144400.13571-1-marex@denx.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2022-09-29 16:44:00 +02:00
/*
* In case RS485 is enabled without GPIO RTS control, the UART IP
* is used to control CTS signal. Keep both the UART and Receiver
* enabled, otherwise the UART IP pulls CTS signal always HIGH no
* matter how the UCR2 CTSC and CTS bits are set. To prevent any
* data from being fed into the RX FIFO, enable loopback mode in
* UTS register, which disconnects the RX path from external RXD
* pin and connects it to the Transceiver, which is disabled, so
* no data can be fed to the RX FIFO that way.
*/
if (sport->port.rs485.flags & SER_RS485_ENABLED &&
sport->have_rtscts && !sport->have_rtsgpio) {
uts = imx_uart_readl(sport, imx_uart_uts_reg(sport));
uts |= UTS_LOOP;
imx_uart_writel(sport, uts, imx_uart_uts_reg(sport));
ucr1 = imx_uart_readl(sport, UCR1);
ucr1 |= UCR1_UARTEN;
imx_uart_writel(sport, ucr1, UCR1);
ucr2 = imx_uart_readl(sport, UCR2);
ucr2 |= UCR2_RXEN;
imx_uart_writel(sport, ucr2, UCR2);
}
if (!imx_uart_is_imx1(sport) && sport->dte_mode) {
/*
* The DCEDTE bit changes the direction of DSR, DCD, DTR and RI
* and influences if UCR3_RI and UCR3_DCD changes the level of RI
* and DCD (when they are outputs) or enables the respective
* irqs. So set this bit early, i.e. before requesting irqs.
*/
u32 ufcr = imx_uart_readl(sport, UFCR);
if (!(ufcr & UFCR_DCEDTE))
imx_uart_writel(sport, ufcr | UFCR_DCEDTE, UFCR);
/*
* Disable UCR3_RI and UCR3_DCD irqs. They are also not
* enabled later because they cannot be cleared
* (confirmed on i.MX25) which makes them unusable.
*/
imx_uart_writel(sport,
IMX21_UCR3_RXDMUXSEL | UCR3_ADNIMP | UCR3_DSR,
UCR3);
} else {
u32 ucr3 = UCR3_DSR;
u32 ufcr = imx_uart_readl(sport, UFCR);
if (ufcr & UFCR_DCEDTE)
imx_uart_writel(sport, ufcr & ~UFCR_DCEDTE, UFCR);
if (!imx_uart_is_imx1(sport))
ucr3 |= IMX21_UCR3_RXDMUXSEL | UCR3_ADNIMP;
imx_uart_writel(sport, ucr3, UCR3);
}
hrtimer_setup(&sport->trigger_start_tx, imx_trigger_start_tx, CLOCK_MONOTONIC,
HRTIMER_MODE_REL);
hrtimer_setup(&sport->trigger_stop_tx, imx_trigger_stop_tx, CLOCK_MONOTONIC,
HRTIMER_MODE_REL);
/*
* Allocate the IRQ(s) i.MX1 has three interrupts whereas later
* chips only have one interrupt.
*/
if (txirq > 0) {
ret = devm_request_irq(&pdev->dev, rxirq, imx_uart_rxint, 0,
dev_name(&pdev->dev), sport);
if (ret) {
dev_err(&pdev->dev, "failed to request rx irq: %d\n",
ret);
goto err_clk;
}
ret = devm_request_irq(&pdev->dev, txirq, imx_uart_txint, 0,
dev_name(&pdev->dev), sport);
if (ret) {
dev_err(&pdev->dev, "failed to request tx irq: %d\n",
ret);
goto err_clk;
}
ret = devm_request_irq(&pdev->dev, rtsirq, imx_uart_rtsint, 0,
dev_name(&pdev->dev), sport);
if (ret) {
dev_err(&pdev->dev, "failed to request rts irq: %d\n",
ret);
goto err_clk;
}
} else {
ret = devm_request_irq(&pdev->dev, rxirq, imx_uart_int, 0,
dev_name(&pdev->dev), sport);
if (ret) {
dev_err(&pdev->dev, "failed to request irq: %d\n", ret);
goto err_clk;
}
}
imx_uart_ports[sport->port.line] = sport;
platform_set_drvdata(pdev, sport);
ret = uart_add_one_port(&imx_uart_uart_driver, &sport->port);
err_clk:
clk_disable_unprepare(sport->clk_ipg);
return ret;
}
static void imx_uart_remove(struct platform_device *pdev)
{
struct imx_port *sport = platform_get_drvdata(pdev);
uart_remove_one_port(&imx_uart_uart_driver, &sport->port);
}
static void imx_uart_restore_context(struct imx_port *sport)
{
unsigned long flags;
uart_port_lock_irqsave(&sport->port, &flags);
if (!sport->context_saved) {
uart_port_unlock_irqrestore(&sport->port, flags);
return;
}
imx_uart_writel(sport, sport->saved_reg[4], UFCR);
imx_uart_writel(sport, sport->saved_reg[5], UESC);
imx_uart_writel(sport, sport->saved_reg[6], UTIM);
imx_uart_writel(sport, sport->saved_reg[7], UBIR);
imx_uart_writel(sport, sport->saved_reg[8], UBMR);
imx_uart_writel(sport, sport->saved_reg[9], IMX21_UTS);
imx_uart_writel(sport, sport->saved_reg[0], UCR1);
imx_uart_writel(sport, sport->saved_reg[1] | UCR2_SRST, UCR2);
imx_uart_writel(sport, sport->saved_reg[2], UCR3);
imx_uart_writel(sport, sport->saved_reg[3], UCR4);
sport->context_saved = false;
uart_port_unlock_irqrestore(&sport->port, flags);
}
static void imx_uart_save_context(struct imx_port *sport)
{
unsigned long flags;
/* Save necessary regs */
uart_port_lock_irqsave(&sport->port, &flags);
sport->saved_reg[0] = imx_uart_readl(sport, UCR1);
sport->saved_reg[1] = imx_uart_readl(sport, UCR2);
sport->saved_reg[2] = imx_uart_readl(sport, UCR3);
sport->saved_reg[3] = imx_uart_readl(sport, UCR4);
sport->saved_reg[4] = imx_uart_readl(sport, UFCR);
sport->saved_reg[5] = imx_uart_readl(sport, UESC);
sport->saved_reg[6] = imx_uart_readl(sport, UTIM);
sport->saved_reg[7] = imx_uart_readl(sport, UBIR);
sport->saved_reg[8] = imx_uart_readl(sport, UBMR);
sport->saved_reg[9] = imx_uart_readl(sport, IMX21_UTS);
sport->context_saved = true;
uart_port_unlock_irqrestore(&sport->port, flags);
}
/* called with irq off */
static void imx_uart_enable_wakeup(struct imx_port *sport, bool on)
{
u32 ucr3;
serial: imx: Use uart_port_lock_irq() instead of uart_port_lock() When executing 'echo mem > /sys/power/state', the following deadlock occurs. Since there is output during the serial port entering the suspend process, the suspend will be interrupted, resulting in the nesting of locks. Therefore, use uart_port_lock_irq() instead of uart_port_unlock(). WARNING: inconsistent lock state 6.12.0-rc2-00002-g3c199ed5bd64-dirty #23 Not tainted -------------------------------- inconsistent {IN-HARDIRQ-W} -> {HARDIRQ-ON-W} usage. sh/494 [HC0[0]:SC0[0]:HE1:SE1] takes: c4db5850 (&port_lock_key){?.-.}-{3:3}, at: imx_uart_enable_wakeup+0x14/0x254 {IN-HARDIRQ-W} state was registered at: lock_acquire+0x104/0x348 _raw_spin_lock+0x48/0x84 imx_uart_int+0x14/0x4dc __handle_irq_event_percpu+0xac/0x2fc handle_irq_event_percpu+0xc/0x40 handle_irq_event+0x38/0x8c handle_fasteoi_irq+0xb4/0x1b8 handle_irq_desc+0x1c/0x2c gic_handle_irq+0x6c/0xa0 generic_handle_arch_irq+0x2c/0x64 call_with_stack+0x18/0x20 __irq_svc+0x9c/0xbc _raw_spin_unlock_irqrestore+0x2c/0x48 uart_write+0xd8/0x3a0 do_output_char+0x1a8/0x1e4 n_tty_write+0x224/0x440 file_tty_write.constprop.0+0x124/0x250 do_iter_readv_writev+0x100/0x1e0 vfs_writev+0xc4/0x448 do_writev+0x68/0xf8 ret_fast_syscall+0x0/0x1c irq event stamp: 31593 hardirqs last enabled at (31593): [<c1150e48>] _raw_spin_unlock_irqrestore+0x44/0x48 hardirqs last disabled at (31592): [<c07f32f0>] clk_enable_lock+0x60/0x120 softirqs last enabled at (30334): [<c012d1d4>] handle_softirqs+0x2cc/0x478 softirqs last disabled at (30325): [<c012d510>] __irq_exit_rcu+0x120/0x15c other info that might help us debug this: Possible unsafe locking scenario: CPU0 ---- lock(&port_lock_key); <Interrupt> lock(&port_lock_key); Fixes: 3c199ed5bd64 ("serial: imx: Grab port lock in imx_uart_enable_wakeup()") Signed-off-by: Xiaolei Wang <xiaolei.wang@windriver.com> Reviewed-by: Lucas Stach <l.stach@pengutronix.de> Reviewed-by: Fabio Estevam <festevam@gmail.com> Link: https://lore.kernel.org/r/20241210233613.2881264-1-xiaolei.wang@windriver.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2024-12-11 07:36:13 +08:00
uart_port_lock_irq(&sport->port);
ucr3 = imx_uart_readl(sport, UCR3);
if (on) {
imx_uart_writel(sport, USR1_AWAKE, USR1);
ucr3 |= UCR3_AWAKEN;
} else {
ucr3 &= ~UCR3_AWAKEN;
}
imx_uart_writel(sport, ucr3, UCR3);
if (sport->have_rtscts) {
u32 ucr1 = imx_uart_readl(sport, UCR1);
if (on) {
imx_uart_writel(sport, USR1_RTSD, USR1);
ucr1 |= UCR1_RTSDEN;
} else {
ucr1 &= ~UCR1_RTSDEN;
}
imx_uart_writel(sport, ucr1, UCR1);
}
serial: imx: Use uart_port_lock_irq() instead of uart_port_lock() When executing 'echo mem > /sys/power/state', the following deadlock occurs. Since there is output during the serial port entering the suspend process, the suspend will be interrupted, resulting in the nesting of locks. Therefore, use uart_port_lock_irq() instead of uart_port_unlock(). WARNING: inconsistent lock state 6.12.0-rc2-00002-g3c199ed5bd64-dirty #23 Not tainted -------------------------------- inconsistent {IN-HARDIRQ-W} -> {HARDIRQ-ON-W} usage. sh/494 [HC0[0]:SC0[0]:HE1:SE1] takes: c4db5850 (&port_lock_key){?.-.}-{3:3}, at: imx_uart_enable_wakeup+0x14/0x254 {IN-HARDIRQ-W} state was registered at: lock_acquire+0x104/0x348 _raw_spin_lock+0x48/0x84 imx_uart_int+0x14/0x4dc __handle_irq_event_percpu+0xac/0x2fc handle_irq_event_percpu+0xc/0x40 handle_irq_event+0x38/0x8c handle_fasteoi_irq+0xb4/0x1b8 handle_irq_desc+0x1c/0x2c gic_handle_irq+0x6c/0xa0 generic_handle_arch_irq+0x2c/0x64 call_with_stack+0x18/0x20 __irq_svc+0x9c/0xbc _raw_spin_unlock_irqrestore+0x2c/0x48 uart_write+0xd8/0x3a0 do_output_char+0x1a8/0x1e4 n_tty_write+0x224/0x440 file_tty_write.constprop.0+0x124/0x250 do_iter_readv_writev+0x100/0x1e0 vfs_writev+0xc4/0x448 do_writev+0x68/0xf8 ret_fast_syscall+0x0/0x1c irq event stamp: 31593 hardirqs last enabled at (31593): [<c1150e48>] _raw_spin_unlock_irqrestore+0x44/0x48 hardirqs last disabled at (31592): [<c07f32f0>] clk_enable_lock+0x60/0x120 softirqs last enabled at (30334): [<c012d1d4>] handle_softirqs+0x2cc/0x478 softirqs last disabled at (30325): [<c012d510>] __irq_exit_rcu+0x120/0x15c other info that might help us debug this: Possible unsafe locking scenario: CPU0 ---- lock(&port_lock_key); <Interrupt> lock(&port_lock_key); Fixes: 3c199ed5bd64 ("serial: imx: Grab port lock in imx_uart_enable_wakeup()") Signed-off-by: Xiaolei Wang <xiaolei.wang@windriver.com> Reviewed-by: Lucas Stach <l.stach@pengutronix.de> Reviewed-by: Fabio Estevam <festevam@gmail.com> Link: https://lore.kernel.org/r/20241210233613.2881264-1-xiaolei.wang@windriver.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2024-12-11 07:36:13 +08:00
uart_port_unlock_irq(&sport->port);
}
static int imx_uart_suspend_noirq(struct device *dev)
{
struct imx_port *sport = dev_get_drvdata(dev);
imx_uart_save_context(sport);
clk_disable(sport->clk_ipg);
pinctrl_pm_select_sleep_state(dev);
return 0;
}
static int imx_uart_resume_noirq(struct device *dev)
{
struct imx_port *sport = dev_get_drvdata(dev);
int ret;
pinctrl_pm_select_default_state(dev);
ret = clk_enable(sport->clk_ipg);
if (ret)
return ret;
imx_uart_restore_context(sport);
return 0;
}
static int imx_uart_suspend(struct device *dev)
{
struct imx_port *sport = dev_get_drvdata(dev);
int ret;
uart_suspend_port(&imx_uart_uart_driver, &sport->port);
disable_irq(sport->port.irq);
ret = clk_prepare_enable(sport->clk_ipg);
if (ret)
return ret;
/* enable wakeup from i.MX UART */
imx_uart_enable_wakeup(sport, true);
return 0;
}
static int imx_uart_resume(struct device *dev)
{
struct imx_port *sport = dev_get_drvdata(dev);
/* disable wakeup from i.MX UART */
imx_uart_enable_wakeup(sport, false);
uart_resume_port(&imx_uart_uart_driver, &sport->port);
enable_irq(sport->port.irq);
clk_disable_unprepare(sport->clk_ipg);
serial: imx: Fix suspend / resume. When a non console i.MX UART is enabled in the device tree, system suspend fails due to an unprepared clock: [ 638.794563] PM: Syncing filesystems ... done. [ 638.878902] Freezing user space processes ... (elapsed 0.002 seconds) done. [ 638.888454] Freezing remaining freezable tasks ... (elapsed 0.001 seconds) done. [ 638.996697] PM: suspend of devices complete after 97.200 msecs [ 639.002611] PM: suspend devices took 0.100 seconds [ 639.013020] PM: late suspend of devices complete after 2.288 msecs [ 639.021486] ------------[ cut here ]------------ [ 639.026147] WARNING: CPU: 0 PID: 488 at drivers/clk/clk.c:732 clk_core_enable+0xc0/0x12c() [ 639.034413] Modules linked in: [ 639.037490] CPU: 0 PID: 488 Comm: system_server Tainted: G W 4.4.0-rc5-pknbsp-svn2214-atag-v4.4-rc5-121-gebfd9cb #1304 [ 639.049312] Hardware name: Freescale i.MX53 (Device Tree Support) [ 639.055444] [<c0016d54>] (unwind_backtrace) from [<c00140f8>] (show_stack+0x20/0x24) [ 639.063199] [<c00140f8>] (show_stack) from [<c02c99a0>] (dump_stack+0x20/0x28) [ 639.070442] [<c02c99a0>] (dump_stack) from [<c0024ca8>] (warn_slowpath_common+0x88/0xc0) [ 639.078541] [<c0024ca8>] (warn_slowpath_common) from [<c0024d0c>] (warn_slowpath_null+0x2c/0x34) [ 639.087332] [<c0024d0c>] (warn_slowpath_null) from [<c05171e8>] (clk_core_enable+0xc0/0x12c) [ 639.095777] [<c05171e8>] (clk_core_enable) from [<c05172f8>] (clk_enable+0x2c/0x40) [ 639.103441] [<c05172f8>] (clk_enable) from [<c0349880>] (imx_serial_port_suspend_noirq+0x20/0xe0) [ 639.112336] [<c0349880>] (imx_serial_port_suspend_noirq) from [<c03a26a0>] (dpm_run_callback+0x68/0x16c) [ 639.121825] [<c03a26a0>] (dpm_run_callback) from [<c03a2898>] (__device_suspend_noirq+0xf4/0x22c) [ 639.130705] [<c03a2898>] (__device_suspend_noirq) from [<c03a4b0c>] (dpm_suspend_noirq+0x148/0x30c) [ 639.139764] [<c03a4b0c>] (dpm_suspend_noirq) from [<c00511d4>] (suspend_devices_and_enter+0x2e8/0x6a4) [ 639.149078] [<c00511d4>] (suspend_devices_and_enter) from [<c00518a0>] (pm_suspend+0x310/0x4b8) [ 639.157782] [<c00518a0>] (pm_suspend) from [<c00500ec>] (state_store+0x7c/0xcc) [ 639.165099] [<c00500ec>] (state_store) from [<c02cb6dc>] (kobj_attr_store+0x1c/0x28) [ 639.172858] [<c02cb6dc>] (kobj_attr_store) from [<c01633d4>] (sysfs_kf_write+0x54/0x58) [ 639.180871] [<c01633d4>] (sysfs_kf_write) from [<c01629b4>] (kernfs_fop_write+0x100/0x1c8) [ 639.189152] [<c01629b4>] (kernfs_fop_write) from [<c00fb8b8>] (__vfs_write+0x3c/0xe8) [ 639.196991] [<c00fb8b8>] (__vfs_write) from [<c00fc810>] (vfs_write+0xa4/0x160) [ 639.204307] [<c00fc810>] (vfs_write) from [<c00fcac4>] (SyS_write+0x4c/0x98) [ 639.211363] [<c00fcac4>] (SyS_write) from [<c0010760>] (ret_fast_syscall+0x0/0x3c) This does not happen for the common case of a single UART used as a console (since imx_console_setup() already does a prepare) Signed-off-by: Martin Fuzzey <mfuzzey@parkeon.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2016-01-05 16:53:31 +01:00
return 0;
}
static int imx_uart_freeze(struct device *dev)
{
struct imx_port *sport = dev_get_drvdata(dev);
uart_suspend_port(&imx_uart_uart_driver, &sport->port);
return clk_prepare_enable(sport->clk_ipg);
}
static int imx_uart_thaw(struct device *dev)
{
struct imx_port *sport = dev_get_drvdata(dev);
uart_resume_port(&imx_uart_uart_driver, &sport->port);
clk_disable_unprepare(sport->clk_ipg);
return 0;
}
static const struct dev_pm_ops imx_uart_pm_ops = {
.suspend_noirq = imx_uart_suspend_noirq,
.resume_noirq = imx_uart_resume_noirq,
.freeze_noirq = imx_uart_suspend_noirq,
serial: imx: Add missing .thaw_noirq hook The following warning is seen with non-console UART instance when system hibernates. [ 37.371969] ------------[ cut here ]------------ [ 37.376599] uart3_root_clk already disabled [ 37.380810] WARNING: CPU: 0 PID: 296 at drivers/clk/clk.c:952 clk_core_disable+0xa4/0xb0 ... [ 37.506986] Call trace: [ 37.509432] clk_core_disable+0xa4/0xb0 [ 37.513270] clk_disable+0x34/0x50 [ 37.516672] imx_uart_thaw+0x38/0x5c [ 37.520250] platform_pm_thaw+0x30/0x6c [ 37.524089] dpm_run_callback.constprop.0+0x3c/0xd4 [ 37.528972] device_resume+0x7c/0x160 [ 37.532633] dpm_resume+0xe8/0x230 [ 37.536036] hibernation_snapshot+0x288/0x430 [ 37.540397] hibernate+0x10c/0x2e0 [ 37.543798] state_store+0xc4/0xd0 [ 37.547203] kobj_attr_store+0x1c/0x30 [ 37.550953] sysfs_kf_write+0x48/0x60 [ 37.554619] kernfs_fop_write_iter+0x118/0x1ac [ 37.559063] new_sync_write+0xe8/0x184 [ 37.562812] vfs_write+0x230/0x290 [ 37.566214] ksys_write+0x68/0xf4 [ 37.569529] __arm64_sys_write+0x20/0x2c [ 37.573452] invoke_syscall.constprop.0+0x50/0xf0 [ 37.578156] do_el0_svc+0x11c/0x150 [ 37.581648] el0_svc+0x30/0x140 [ 37.584792] el0t_64_sync_handler+0xe8/0xf0 [ 37.588976] el0t_64_sync+0x1a0/0x1a4 [ 37.592639] ---[ end trace 56e22eec54676d75 ]--- On hibernating, pm core calls into related hooks in sequence like: .freeze .freeze_noirq .thaw_noirq .thaw With .thaw_noirq hook being absent, the clock will be disabled in a unbalanced call which results the warning above. imx_uart_freeze() clk_prepare_enable() imx_uart_suspend_noirq() clk_disable() imx_uart_thaw clk_disable_unprepare() Adding the missing .thaw_noirq hook as imx_uart_resume_noirq() will have the call sequence corrected as below and thus fix the warning. imx_uart_freeze() clk_prepare_enable() imx_uart_suspend_noirq() clk_disable() imx_uart_resume_noirq() clk_enable() imx_uart_thaw clk_disable_unprepare() Fixes: 09df0b3464e5 ("serial: imx: fix endless loop during suspend") Reviewed-by: Martin Kaiser <martin@kaiser.cx> Signed-off-by: Shawn Guo <shawn.guo@linaro.org> Link: https://lore.kernel.org/r/20221012121353.2346280-1-shawn.guo@linaro.org Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2022-10-12 20:13:53 +08:00
.thaw_noirq = imx_uart_resume_noirq,
.restore_noirq = imx_uart_resume_noirq,
.suspend = imx_uart_suspend,
.resume = imx_uart_resume,
.freeze = imx_uart_freeze,
.thaw = imx_uart_thaw,
.restore = imx_uart_thaw,
};
static struct platform_driver imx_uart_platform_driver = {
.probe = imx_uart_probe,
.remove = imx_uart_remove,
.driver = {
.name = "imx-uart",
.of_match_table = imx_uart_dt_ids,
.pm = &imx_uart_pm_ops,
},
};
static int __init imx_uart_init(void)
{
int ret = uart_register_driver(&imx_uart_uart_driver);
if (ret)
return ret;
ret = platform_driver_register(&imx_uart_platform_driver);
if (ret != 0)
uart_unregister_driver(&imx_uart_uart_driver);
return ret;
}
static void __exit imx_uart_exit(void)
{
platform_driver_unregister(&imx_uart_platform_driver);
uart_unregister_driver(&imx_uart_uart_driver);
}
module_init(imx_uart_init);
module_exit(imx_uart_exit);
MODULE_AUTHOR("Sascha Hauer");
MODULE_DESCRIPTION("IMX generic serial port driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:imx-uart");