xref: /openbmc/linux/drivers/tty/serial/mxs-auart.c (revision c845428b7a9157523103100806bc8130d64769c8)
1e3b3d0f5SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+
2f19693a1SSascha Hauer /*
3254da0d7SOleksij Rempel  * Application UART driver for:
4254da0d7SOleksij Rempel  *	Freescale STMP37XX/STMP378X
5254da0d7SOleksij Rempel  *	Alphascale ASM9260
6f19693a1SSascha Hauer  *
7f19693a1SSascha Hauer  * Author: dmitry pervushin <dimka@embeddedalley.com>
8f19693a1SSascha Hauer  *
9254da0d7SOleksij Rempel  * Copyright 2014 Oleksij Rempel <linux@rempel-privat.de>
10254da0d7SOleksij Rempel  *	Provide Alphascale ASM9260 support.
11f19693a1SSascha Hauer  * Copyright 2008-2010 Freescale Semiconductor, Inc.
12f19693a1SSascha Hauer  * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
13f19693a1SSascha Hauer  */
14f19693a1SSascha Hauer 
15f19693a1SSascha Hauer #include <linux/kernel.h>
16f19693a1SSascha Hauer #include <linux/errno.h>
17f19693a1SSascha Hauer #include <linux/init.h>
18f19693a1SSascha Hauer #include <linux/console.h>
19f19693a1SSascha Hauer #include <linux/interrupt.h>
20f19693a1SSascha Hauer #include <linux/module.h>
21f19693a1SSascha Hauer #include <linux/slab.h>
22f19693a1SSascha Hauer #include <linux/wait.h>
23f19693a1SSascha Hauer #include <linux/tty.h>
24f19693a1SSascha Hauer #include <linux/tty_driver.h>
25f19693a1SSascha Hauer #include <linux/tty_flip.h>
26f19693a1SSascha Hauer #include <linux/serial.h>
27f19693a1SSascha Hauer #include <linux/serial_core.h>
28f19693a1SSascha Hauer #include <linux/platform_device.h>
29f19693a1SSascha Hauer #include <linux/device.h>
30f19693a1SSascha Hauer #include <linux/clk.h>
31f19693a1SSascha Hauer #include <linux/delay.h>
32f19693a1SSascha Hauer #include <linux/io.h>
3329e5c442SRob Herring #include <linux/of.h>
34e8001632SHuang Shijie #include <linux/dma-mapping.h>
35bcc20f9eSShawn Guo #include <linux/dmaengine.h>
36f19693a1SSascha Hauer 
37f9e42397SJanusz Uzycki #include <linux/gpio/consumer.h>
387c573d7eSJanusz Uzycki #include <linux/err.h>
39f9e42397SJanusz Uzycki #include <linux/irq.h>
407c573d7eSJanusz Uzycki #include "serial_mctrl_gpio.h"
417c573d7eSJanusz Uzycki 
42f19693a1SSascha Hauer #define MXS_AUART_PORTS 5
439987f76aSHector Palacios #define MXS_AUART_FIFO_SIZE		16
44f19693a1SSascha Hauer 
45254da0d7SOleksij Rempel #define SET_REG				0x4
46254da0d7SOleksij Rempel #define CLR_REG				0x8
47254da0d7SOleksij Rempel #define TOG_REG				0xc
48254da0d7SOleksij Rempel 
49f19693a1SSascha Hauer #define AUART_CTRL0			0x00000000
50f19693a1SSascha Hauer #define AUART_CTRL1			0x00000010
51f19693a1SSascha Hauer #define AUART_CTRL2			0x00000020
52f19693a1SSascha Hauer #define AUART_LINECTRL			0x00000030
53f19693a1SSascha Hauer #define AUART_LINECTRL2			0x00000040
54f19693a1SSascha Hauer #define AUART_INTR			0x00000050
55f19693a1SSascha Hauer #define AUART_DATA			0x00000060
56f19693a1SSascha Hauer #define AUART_STAT			0x00000070
57f19693a1SSascha Hauer #define AUART_DEBUG			0x00000080
58f19693a1SSascha Hauer #define AUART_VERSION			0x00000090
59f19693a1SSascha Hauer #define AUART_AUTOBAUD			0x000000a0
60f19693a1SSascha Hauer 
61f19693a1SSascha Hauer #define AUART_CTRL0_SFTRST			(1 << 31)
62f19693a1SSascha Hauer #define AUART_CTRL0_CLKGATE			(1 << 30)
63e8001632SHuang Shijie #define AUART_CTRL0_RXTO_ENABLE			(1 << 27)
64e8001632SHuang Shijie #define AUART_CTRL0_RXTIMEOUT(v)		(((v) & 0x7ff) << 16)
65e8001632SHuang Shijie #define AUART_CTRL0_XFER_COUNT(v)		((v) & 0xffff)
66e8001632SHuang Shijie 
67e8001632SHuang Shijie #define AUART_CTRL1_XFER_COUNT(v)		((v) & 0xffff)
68e8001632SHuang Shijie 
69e8001632SHuang Shijie #define AUART_CTRL2_DMAONERR			(1 << 26)
70e8001632SHuang Shijie #define AUART_CTRL2_TXDMAE			(1 << 25)
71e8001632SHuang Shijie #define AUART_CTRL2_RXDMAE			(1 << 24)
72f19693a1SSascha Hauer 
73f19693a1SSascha Hauer #define AUART_CTRL2_CTSEN			(1 << 15)
7400592021SHuang Shijie #define AUART_CTRL2_RTSEN			(1 << 14)
75f19693a1SSascha Hauer #define AUART_CTRL2_RTS				(1 << 11)
76f19693a1SSascha Hauer #define AUART_CTRL2_RXE				(1 << 9)
77f19693a1SSascha Hauer #define AUART_CTRL2_TXE				(1 << 8)
78f19693a1SSascha Hauer #define AUART_CTRL2_UARTEN			(1 << 0)
79f19693a1SSascha Hauer 
80df57cf6aSStefan Wahren #define AUART_LINECTRL_BAUD_DIV_MAX		0x003fffc0
81df57cf6aSStefan Wahren #define AUART_LINECTRL_BAUD_DIV_MIN		0x000000ec
82f19693a1SSascha Hauer #define AUART_LINECTRL_BAUD_DIVINT_SHIFT	16
83f19693a1SSascha Hauer #define AUART_LINECTRL_BAUD_DIVINT_MASK		0xffff0000
84f19693a1SSascha Hauer #define AUART_LINECTRL_BAUD_DIVINT(v)		(((v) & 0xffff) << 16)
85f19693a1SSascha Hauer #define AUART_LINECTRL_BAUD_DIVFRAC_SHIFT	8
86f19693a1SSascha Hauer #define AUART_LINECTRL_BAUD_DIVFRAC_MASK	0x00003f00
87f19693a1SSascha Hauer #define AUART_LINECTRL_BAUD_DIVFRAC(v)		(((v) & 0x3f) << 8)
88f87fa71eSWolfgang Ocker #define AUART_LINECTRL_SPS			(1 << 7)
89f19693a1SSascha Hauer #define AUART_LINECTRL_WLEN_MASK		0x00000060
908ea43accSJiri Slaby #define AUART_LINECTRL_WLEN(v)			((((v) - 5) & 0x3) << 5)
91f19693a1SSascha Hauer #define AUART_LINECTRL_FEN			(1 << 4)
92f19693a1SSascha Hauer #define AUART_LINECTRL_STP2			(1 << 3)
93f19693a1SSascha Hauer #define AUART_LINECTRL_EPS			(1 << 2)
94f19693a1SSascha Hauer #define AUART_LINECTRL_PEN			(1 << 1)
95f19693a1SSascha Hauer #define AUART_LINECTRL_BRK			(1 << 0)
96f19693a1SSascha Hauer 
97f19693a1SSascha Hauer #define AUART_INTR_RTIEN			(1 << 22)
98f19693a1SSascha Hauer #define AUART_INTR_TXIEN			(1 << 21)
99f19693a1SSascha Hauer #define AUART_INTR_RXIEN			(1 << 20)
100f19693a1SSascha Hauer #define AUART_INTR_CTSMIEN			(1 << 17)
101f19693a1SSascha Hauer #define AUART_INTR_RTIS				(1 << 6)
102f19693a1SSascha Hauer #define AUART_INTR_TXIS				(1 << 5)
103f19693a1SSascha Hauer #define AUART_INTR_RXIS				(1 << 4)
104f19693a1SSascha Hauer #define AUART_INTR_CTSMIS			(1 << 1)
105f19693a1SSascha Hauer 
106f19693a1SSascha Hauer #define AUART_STAT_BUSY				(1 << 29)
107f19693a1SSascha Hauer #define AUART_STAT_CTS				(1 << 28)
108f19693a1SSascha Hauer #define AUART_STAT_TXFE				(1 << 27)
109f19693a1SSascha Hauer #define AUART_STAT_TXFF				(1 << 25)
110f19693a1SSascha Hauer #define AUART_STAT_RXFE				(1 << 24)
111f19693a1SSascha Hauer #define AUART_STAT_OERR				(1 << 19)
112f19693a1SSascha Hauer #define AUART_STAT_BERR				(1 << 18)
113f19693a1SSascha Hauer #define AUART_STAT_PERR				(1 << 17)
114f19693a1SSascha Hauer #define AUART_STAT_FERR				(1 << 16)
115e8001632SHuang Shijie #define AUART_STAT_RXCOUNT_MASK			0xffff
116f19693a1SSascha Hauer 
117254da0d7SOleksij Rempel /*
118254da0d7SOleksij Rempel  * Start of Alphascale asm9260 defines
119254da0d7SOleksij Rempel  * This list contains only differences of existing bits
120254da0d7SOleksij Rempel  * between imx2x and asm9260
121254da0d7SOleksij Rempel  */
122254da0d7SOleksij Rempel #define ASM9260_HW_CTRL0			0x0000
123254da0d7SOleksij Rempel /*
124254da0d7SOleksij Rempel  * RW. Tell the UART to execute the RX DMA Command. The
125254da0d7SOleksij Rempel  * UART will clear this bit at the end of receive execution.
126254da0d7SOleksij Rempel  */
127254da0d7SOleksij Rempel #define ASM9260_BM_CTRL0_RXDMA_RUN		BIT(28)
128254da0d7SOleksij Rempel /* RW. 0 use FIFO for status register; 1 use DMA */
129254da0d7SOleksij Rempel #define ASM9260_BM_CTRL0_RXTO_SOURCE_STATUS	BIT(25)
130254da0d7SOleksij Rempel /*
131254da0d7SOleksij Rempel  * RW. RX TIMEOUT Enable. Valid for FIFO and DMA.
132254da0d7SOleksij Rempel  * Warning: If this bit is set to 0, the RX timeout will not affect receive DMA
133254da0d7SOleksij Rempel  * operation. If this bit is set to 1, a receive timeout will cause the receive
134254da0d7SOleksij Rempel  * DMA logic to terminate by filling the remaining DMA bytes with garbage data.
135254da0d7SOleksij Rempel  */
136254da0d7SOleksij Rempel #define ASM9260_BM_CTRL0_RXTO_ENABLE		BIT(24)
137254da0d7SOleksij Rempel /*
138254da0d7SOleksij Rempel  * RW. Receive Timeout Counter Value: number of 8-bit-time to wait before
139254da0d7SOleksij Rempel  * asserting timeout on the RX input. If the RXFIFO is not empty and the RX
140254da0d7SOleksij Rempel  * input is idle, then the watchdog counter will decrement each bit-time. Note
141254da0d7SOleksij Rempel  * 7-bit-time is added to the programmed value, so a value of zero will set
142254da0d7SOleksij Rempel  * the counter to 7-bit-time, a value of 0x1 gives 15-bit-time and so on. Also
143254da0d7SOleksij Rempel  * note that the counter is reloaded at the end of each frame, so if the frame
144254da0d7SOleksij Rempel  * is 10 bits long and the timeout counter value is zero, then timeout will
145254da0d7SOleksij Rempel  * occur (when FIFO is not empty) even if the RX input is not idle. The default
146254da0d7SOleksij Rempel  * value is 0x3 (31 bit-time).
147254da0d7SOleksij Rempel  */
148254da0d7SOleksij Rempel #define ASM9260_BM_CTRL0_RXTO_MASK		(0xff << 16)
149254da0d7SOleksij Rempel /* TIMEOUT = (100*7+1)*(1/BAUD) */
150254da0d7SOleksij Rempel #define ASM9260_BM_CTRL0_DEFAULT_RXTIMEOUT	(20 << 16)
151254da0d7SOleksij Rempel 
152254da0d7SOleksij Rempel /* TX ctrl register */
153254da0d7SOleksij Rempel #define ASM9260_HW_CTRL1			0x0010
154254da0d7SOleksij Rempel /*
155254da0d7SOleksij Rempel  * RW. Tell the UART to execute the TX DMA Command. The
156254da0d7SOleksij Rempel  * UART will clear this bit at the end of transmit execution.
157254da0d7SOleksij Rempel  */
158254da0d7SOleksij Rempel #define ASM9260_BM_CTRL1_TXDMA_RUN		BIT(28)
159254da0d7SOleksij Rempel 
160254da0d7SOleksij Rempel #define ASM9260_HW_CTRL2			0x0020
161254da0d7SOleksij Rempel /*
162254da0d7SOleksij Rempel  * RW. Receive Interrupt FIFO Level Select.
163254da0d7SOleksij Rempel  * The trigger points for the receive interrupt are as follows:
164254da0d7SOleksij Rempel  * ONE_EIGHTHS = 0x0 Trigger on FIFO full to at least 2 of 16 entries.
165254da0d7SOleksij Rempel  * ONE_QUARTER = 0x1 Trigger on FIFO full to at least 4 of 16 entries.
166254da0d7SOleksij Rempel  * ONE_HALF = 0x2 Trigger on FIFO full to at least 8 of 16 entries.
167254da0d7SOleksij Rempel  * THREE_QUARTERS = 0x3 Trigger on FIFO full to at least 12 of 16 entries.
168254da0d7SOleksij Rempel  * SEVEN_EIGHTHS = 0x4 Trigger on FIFO full to at least 14 of 16 entries.
169254da0d7SOleksij Rempel  */
170254da0d7SOleksij Rempel #define ASM9260_BM_CTRL2_RXIFLSEL		(7 << 20)
171254da0d7SOleksij Rempel #define ASM9260_BM_CTRL2_DEFAULT_RXIFLSEL	(3 << 20)
172254da0d7SOleksij Rempel /* RW. Same as RXIFLSEL */
173254da0d7SOleksij Rempel #define ASM9260_BM_CTRL2_TXIFLSEL		(7 << 16)
174254da0d7SOleksij Rempel #define ASM9260_BM_CTRL2_DEFAULT_TXIFLSEL	(2 << 16)
175254da0d7SOleksij Rempel /* RW. Set DTR. When this bit is 1, the output is 0. */
176254da0d7SOleksij Rempel #define ASM9260_BM_CTRL2_DTR			BIT(10)
177254da0d7SOleksij Rempel /* RW. Loop Back Enable */
178254da0d7SOleksij Rempel #define ASM9260_BM_CTRL2_LBE			BIT(7)
179254da0d7SOleksij Rempel #define ASM9260_BM_CTRL2_PORT_ENABLE		BIT(0)
180254da0d7SOleksij Rempel 
181254da0d7SOleksij Rempel #define ASM9260_HW_LINECTRL			0x0030
182254da0d7SOleksij Rempel /*
183254da0d7SOleksij Rempel  * RW. Stick Parity Select. When bits 1, 2, and 7 of this register are set, the
184254da0d7SOleksij Rempel  * parity bit is transmitted and checked as a 0. When bits 1 and 7 are set,
185254da0d7SOleksij Rempel  * and bit 2 is 0, the parity bit is transmitted and checked as a 1. When this
186254da0d7SOleksij Rempel  * bit is cleared stick parity is disabled.
187254da0d7SOleksij Rempel  */
188254da0d7SOleksij Rempel #define ASM9260_BM_LCTRL_SPS			BIT(7)
189254da0d7SOleksij Rempel /* RW. Word length */
190254da0d7SOleksij Rempel #define ASM9260_BM_LCTRL_WLEN			(3 << 5)
191254da0d7SOleksij Rempel #define ASM9260_BM_LCTRL_CHRL_5			(0 << 5)
192254da0d7SOleksij Rempel #define ASM9260_BM_LCTRL_CHRL_6			(1 << 5)
193254da0d7SOleksij Rempel #define ASM9260_BM_LCTRL_CHRL_7			(2 << 5)
194254da0d7SOleksij Rempel #define ASM9260_BM_LCTRL_CHRL_8			(3 << 5)
195254da0d7SOleksij Rempel 
196254da0d7SOleksij Rempel /*
197254da0d7SOleksij Rempel  * Interrupt register.
198254da0d7SOleksij Rempel  * contains the interrupt enables and the interrupt status bits
199254da0d7SOleksij Rempel  */
200254da0d7SOleksij Rempel #define ASM9260_HW_INTR				0x0040
201254da0d7SOleksij Rempel /* Tx FIFO EMPTY Raw Interrupt enable */
202254da0d7SOleksij Rempel #define ASM9260_BM_INTR_TFEIEN			BIT(27)
203254da0d7SOleksij Rempel /* Overrun Error Interrupt Enable. */
204254da0d7SOleksij Rempel #define ASM9260_BM_INTR_OEIEN			BIT(26)
205254da0d7SOleksij Rempel /* Break Error Interrupt Enable. */
206254da0d7SOleksij Rempel #define ASM9260_BM_INTR_BEIEN			BIT(25)
207254da0d7SOleksij Rempel /* Parity Error Interrupt Enable. */
208254da0d7SOleksij Rempel #define ASM9260_BM_INTR_PEIEN			BIT(24)
209254da0d7SOleksij Rempel /* Framing Error Interrupt Enable. */
210254da0d7SOleksij Rempel #define ASM9260_BM_INTR_FEIEN			BIT(23)
211254da0d7SOleksij Rempel 
212254da0d7SOleksij Rempel /* nUARTDSR Modem Interrupt Enable. */
213254da0d7SOleksij Rempel #define ASM9260_BM_INTR_DSRMIEN			BIT(19)
214254da0d7SOleksij Rempel /* nUARTDCD Modem Interrupt Enable. */
215254da0d7SOleksij Rempel #define ASM9260_BM_INTR_DCDMIEN			BIT(18)
216254da0d7SOleksij Rempel /* nUARTRI Modem Interrupt Enable. */
217254da0d7SOleksij Rempel #define ASM9260_BM_INTR_RIMIEN			BIT(16)
218254da0d7SOleksij Rempel /* Auto-Boud Timeout */
219254da0d7SOleksij Rempel #define ASM9260_BM_INTR_ABTO			BIT(13)
220254da0d7SOleksij Rempel #define ASM9260_BM_INTR_ABEO			BIT(12)
221254da0d7SOleksij Rempel /* Tx FIFO EMPTY Raw Interrupt state */
222254da0d7SOleksij Rempel #define ASM9260_BM_INTR_TFEIS			BIT(11)
223254da0d7SOleksij Rempel /* Overrun Error */
224254da0d7SOleksij Rempel #define ASM9260_BM_INTR_OEIS			BIT(10)
225254da0d7SOleksij Rempel /* Break Error */
226254da0d7SOleksij Rempel #define ASM9260_BM_INTR_BEIS			BIT(9)
227254da0d7SOleksij Rempel /* Parity Error */
228254da0d7SOleksij Rempel #define ASM9260_BM_INTR_PEIS			BIT(8)
229254da0d7SOleksij Rempel /* Framing Error */
230254da0d7SOleksij Rempel #define ASM9260_BM_INTR_FEIS			BIT(7)
231254da0d7SOleksij Rempel #define ASM9260_BM_INTR_DSRMIS			BIT(3)
232254da0d7SOleksij Rempel #define ASM9260_BM_INTR_DCDMIS			BIT(2)
233254da0d7SOleksij Rempel #define ASM9260_BM_INTR_RIMIS			BIT(0)
234254da0d7SOleksij Rempel 
235254da0d7SOleksij Rempel /*
236254da0d7SOleksij Rempel  * RW. In DMA mode, up to 4 Received/Transmit characters can be accessed at a
237254da0d7SOleksij Rempel  * time. In PIO mode, only one character can be accessed at a time. The status
238254da0d7SOleksij Rempel  * register contains the receive data flags and valid bits.
239254da0d7SOleksij Rempel  */
240254da0d7SOleksij Rempel #define ASM9260_HW_DATA				0x0050
241254da0d7SOleksij Rempel 
242254da0d7SOleksij Rempel #define ASM9260_HW_STAT				0x0060
243254da0d7SOleksij Rempel /* RO. If 1, UARTAPP is present in this product. */
244254da0d7SOleksij Rempel #define ASM9260_BM_STAT_PRESENT			BIT(31)
245254da0d7SOleksij Rempel /* RO. If 1, HISPEED is present in this product. */
246254da0d7SOleksij Rempel #define ASM9260_BM_STAT_HISPEED			BIT(30)
247254da0d7SOleksij Rempel /* RO. Receive FIFO Full. */
248254da0d7SOleksij Rempel #define ASM9260_BM_STAT_RXFULL			BIT(26)
249254da0d7SOleksij Rempel 
250254da0d7SOleksij Rempel /* RO. The UART Debug Register contains the state of the DMA signals. */
251254da0d7SOleksij Rempel #define ASM9260_HW_DEBUG			0x0070
252254da0d7SOleksij Rempel /* DMA Command Run Status */
253254da0d7SOleksij Rempel #define ASM9260_BM_DEBUG_TXDMARUN		BIT(5)
254254da0d7SOleksij Rempel #define ASM9260_BM_DEBUG_RXDMARUN		BIT(4)
255254da0d7SOleksij Rempel /* DMA Command End Status */
256254da0d7SOleksij Rempel #define ASM9260_BM_DEBUG_TXCMDEND		BIT(3)
257254da0d7SOleksij Rempel #define ASM9260_BM_DEBUG_RXCMDEND		BIT(2)
258254da0d7SOleksij Rempel /* DMA Request Status */
259254da0d7SOleksij Rempel #define ASM9260_BM_DEBUG_TXDMARQ		BIT(1)
260254da0d7SOleksij Rempel #define ASM9260_BM_DEBUG_RXDMARQ		BIT(0)
261254da0d7SOleksij Rempel 
262254da0d7SOleksij Rempel #define ASM9260_HW_ILPR				0x0080
263254da0d7SOleksij Rempel 
264254da0d7SOleksij Rempel #define ASM9260_HW_RS485CTRL			0x0090
265254da0d7SOleksij Rempel /*
266254da0d7SOleksij Rempel  * RW. This bit reverses the polarity of the direction control signal on the RTS
267254da0d7SOleksij Rempel  * (or DTR) pin.
268254da0d7SOleksij Rempel  * If 0, The direction control pin will be driven to logic ‘0’ when the
269254da0d7SOleksij Rempel  * transmitter has data to be sent. It will be driven to logic ‘1’ after the
270254da0d7SOleksij Rempel  * last bit of data has been transmitted.
271254da0d7SOleksij Rempel  */
272254da0d7SOleksij Rempel #define ASM9260_BM_RS485CTRL_ONIV		BIT(5)
273254da0d7SOleksij Rempel /* RW. Enable Auto Direction Control. */
274254da0d7SOleksij Rempel #define ASM9260_BM_RS485CTRL_DIR_CTRL		BIT(4)
275254da0d7SOleksij Rempel /*
276254da0d7SOleksij Rempel  * RW. If 0 and DIR_CTRL = 1, pin RTS is used for direction control.
277254da0d7SOleksij Rempel  * If 1 and DIR_CTRL = 1, pin DTR is used for direction control.
278254da0d7SOleksij Rempel  */
279254da0d7SOleksij Rempel #define ASM9260_BM_RS485CTRL_PINSEL		BIT(3)
280254da0d7SOleksij Rempel /* RW. Enable Auto Address Detect (AAD). */
281254da0d7SOleksij Rempel #define ASM9260_BM_RS485CTRL_AADEN		BIT(2)
282254da0d7SOleksij Rempel /* RW. Disable receiver. */
283254da0d7SOleksij Rempel #define ASM9260_BM_RS485CTRL_RXDIS		BIT(1)
284254da0d7SOleksij Rempel /* RW. Enable RS-485/EIA-485 Normal Multidrop Mode (NMM) */
285254da0d7SOleksij Rempel #define ASM9260_BM_RS485CTRL_RS485EN		BIT(0)
286254da0d7SOleksij Rempel 
287254da0d7SOleksij Rempel #define ASM9260_HW_RS485ADRMATCH		0x00a0
288254da0d7SOleksij Rempel /* Contains the address match value. */
289254da0d7SOleksij Rempel #define ASM9260_BM_RS485ADRMATCH_MASK		(0xff << 0)
290254da0d7SOleksij Rempel 
291254da0d7SOleksij Rempel #define ASM9260_HW_RS485DLY			0x00b0
292254da0d7SOleksij Rempel /*
293254da0d7SOleksij Rempel  * RW. Contains the direction control (RTS or DTR) delay value. This delay time
294254da0d7SOleksij Rempel  * is in periods of the baud clock.
295254da0d7SOleksij Rempel  */
296254da0d7SOleksij Rempel #define ASM9260_BM_RS485DLY_MASK		(0xff << 0)
297254da0d7SOleksij Rempel 
298254da0d7SOleksij Rempel #define ASM9260_HW_AUTOBAUD			0x00c0
299254da0d7SOleksij Rempel /* WO. Auto-baud time-out interrupt clear bit. */
300254da0d7SOleksij Rempel #define ASM9260_BM_AUTOBAUD_TO_INT_CLR		BIT(9)
301254da0d7SOleksij Rempel /* WO. End of auto-baud interrupt clear bit. */
302254da0d7SOleksij Rempel #define ASM9260_BM_AUTOBAUD_EO_INT_CLR		BIT(8)
303254da0d7SOleksij Rempel /* Restart in case of timeout (counter restarts at next UART Rx falling edge) */
304254da0d7SOleksij Rempel #define ASM9260_BM_AUTOBAUD_AUTORESTART		BIT(2)
305254da0d7SOleksij Rempel /* Auto-baud mode select bit. 0 - Mode 0, 1 - Mode 1. */
306254da0d7SOleksij Rempel #define ASM9260_BM_AUTOBAUD_MODE		BIT(1)
307254da0d7SOleksij Rempel /*
308254da0d7SOleksij Rempel  * Auto-baud start (auto-baud is running). Auto-baud run bit. This bit is
309254da0d7SOleksij Rempel  * automatically cleared after auto-baud completion.
310254da0d7SOleksij Rempel  */
311254da0d7SOleksij Rempel #define ASM9260_BM_AUTOBAUD_START		BIT(0)
312254da0d7SOleksij Rempel 
313254da0d7SOleksij Rempel #define ASM9260_HW_CTRL3			0x00d0
314254da0d7SOleksij Rempel #define ASM9260_BM_CTRL3_OUTCLK_DIV_MASK	(0xffff << 16)
315254da0d7SOleksij Rempel /*
316254da0d7SOleksij Rempel  * RW. Provide clk over OUTCLK pin. In case of asm9260 it can be configured on
317254da0d7SOleksij Rempel  * pins 137 and 144.
318254da0d7SOleksij Rempel  */
319254da0d7SOleksij Rempel #define ASM9260_BM_CTRL3_MASTERMODE		BIT(6)
320254da0d7SOleksij Rempel /* RW. Baud Rate Mode: 1 - Enable sync mode. 0 - async mode. */
321254da0d7SOleksij Rempel #define ASM9260_BM_CTRL3_SYNCMODE		BIT(4)
322254da0d7SOleksij Rempel /* RW. 1 - MSB bit send frist; 0 - LSB bit frist. */
323254da0d7SOleksij Rempel #define ASM9260_BM_CTRL3_MSBF			BIT(2)
324254da0d7SOleksij Rempel /* RW. 1 - sample rate = 8 x Baudrate; 0 - sample rate = 16 x Baudrate. */
325254da0d7SOleksij Rempel #define ASM9260_BM_CTRL3_BAUD8			BIT(1)
326254da0d7SOleksij Rempel /* RW. 1 - Set word length to 9bit. 0 - use ASM9260_BM_LCTRL_WLEN */
327254da0d7SOleksij Rempel #define ASM9260_BM_CTRL3_9BIT			BIT(0)
328254da0d7SOleksij Rempel 
329254da0d7SOleksij Rempel #define ASM9260_HW_ISO7816_CTRL			0x00e0
330254da0d7SOleksij Rempel /* RW. Enable High Speed mode. */
331254da0d7SOleksij Rempel #define ASM9260_BM_ISO7816CTRL_HS		BIT(12)
332254da0d7SOleksij Rempel /* Disable Successive Receive NACK */
333254da0d7SOleksij Rempel #define ASM9260_BM_ISO7816CTRL_DS_NACK		BIT(8)
334254da0d7SOleksij Rempel #define ASM9260_BM_ISO7816CTRL_MAX_ITER_MASK	(0xff << 4)
335254da0d7SOleksij Rempel /* Receive NACK Inhibit */
336254da0d7SOleksij Rempel #define ASM9260_BM_ISO7816CTRL_INACK		BIT(3)
337254da0d7SOleksij Rempel #define ASM9260_BM_ISO7816CTRL_NEG_DATA		BIT(2)
338254da0d7SOleksij Rempel /* RW. 1 - ISO7816 mode; 0 - USART mode */
339254da0d7SOleksij Rempel #define ASM9260_BM_ISO7816CTRL_ENABLE		BIT(0)
340254da0d7SOleksij Rempel 
341254da0d7SOleksij Rempel #define ASM9260_HW_ISO7816_ERRCNT		0x00f0
342254da0d7SOleksij Rempel /* Parity error counter. Will be cleared after reading */
343254da0d7SOleksij Rempel #define ASM9260_BM_ISO7816_NB_ERRORS_MASK	(0xff << 0)
344254da0d7SOleksij Rempel 
345254da0d7SOleksij Rempel #define ASM9260_HW_ISO7816_STATUS		0x0100
346254da0d7SOleksij Rempel /* Max number of Repetitions Reached */
347254da0d7SOleksij Rempel #define ASM9260_BM_ISO7816_STAT_ITERATION	BIT(0)
348254da0d7SOleksij Rempel 
349254da0d7SOleksij Rempel /* End of Alphascale asm9260 defines */
350254da0d7SOleksij Rempel 
351f19693a1SSascha Hauer static struct uart_driver auart_driver;
352f19693a1SSascha Hauer 
353f4b1f03bSHuang Shijie enum mxs_auart_type {
354f4b1f03bSHuang Shijie 	IMX23_AUART,
355f4b1f03bSHuang Shijie 	IMX28_AUART,
356254da0d7SOleksij Rempel 	ASM9260_AUART,
357254da0d7SOleksij Rempel };
358254da0d7SOleksij Rempel 
359254da0d7SOleksij Rempel struct vendor_data {
360254da0d7SOleksij Rempel 	const u16	*reg_offset;
361254da0d7SOleksij Rempel };
362254da0d7SOleksij Rempel 
363254da0d7SOleksij Rempel enum {
364254da0d7SOleksij Rempel 	REG_CTRL0,
365254da0d7SOleksij Rempel 	REG_CTRL1,
366254da0d7SOleksij Rempel 	REG_CTRL2,
367254da0d7SOleksij Rempel 	REG_LINECTRL,
368254da0d7SOleksij Rempel 	REG_LINECTRL2,
369254da0d7SOleksij Rempel 	REG_INTR,
370254da0d7SOleksij Rempel 	REG_DATA,
371254da0d7SOleksij Rempel 	REG_STAT,
372254da0d7SOleksij Rempel 	REG_DEBUG,
373254da0d7SOleksij Rempel 	REG_VERSION,
374254da0d7SOleksij Rempel 	REG_AUTOBAUD,
375254da0d7SOleksij Rempel 
376254da0d7SOleksij Rempel 	/* The size of the array - must be last */
377254da0d7SOleksij Rempel 	REG_ARRAY_SIZE,
378254da0d7SOleksij Rempel };
379254da0d7SOleksij Rempel 
380254da0d7SOleksij Rempel static const u16 mxs_asm9260_offsets[REG_ARRAY_SIZE] = {
381254da0d7SOleksij Rempel 	[REG_CTRL0] = ASM9260_HW_CTRL0,
382254da0d7SOleksij Rempel 	[REG_CTRL1] = ASM9260_HW_CTRL1,
383254da0d7SOleksij Rempel 	[REG_CTRL2] = ASM9260_HW_CTRL2,
384254da0d7SOleksij Rempel 	[REG_LINECTRL] = ASM9260_HW_LINECTRL,
385254da0d7SOleksij Rempel 	[REG_INTR] = ASM9260_HW_INTR,
386254da0d7SOleksij Rempel 	[REG_DATA] = ASM9260_HW_DATA,
387254da0d7SOleksij Rempel 	[REG_STAT] = ASM9260_HW_STAT,
388254da0d7SOleksij Rempel 	[REG_DEBUG] = ASM9260_HW_DEBUG,
389254da0d7SOleksij Rempel 	[REG_AUTOBAUD] = ASM9260_HW_AUTOBAUD,
390254da0d7SOleksij Rempel };
391254da0d7SOleksij Rempel 
392254da0d7SOleksij Rempel static const u16 mxs_stmp37xx_offsets[REG_ARRAY_SIZE] = {
393254da0d7SOleksij Rempel 	[REG_CTRL0] = AUART_CTRL0,
394254da0d7SOleksij Rempel 	[REG_CTRL1] = AUART_CTRL1,
395254da0d7SOleksij Rempel 	[REG_CTRL2] = AUART_CTRL2,
396254da0d7SOleksij Rempel 	[REG_LINECTRL] = AUART_LINECTRL,
397254da0d7SOleksij Rempel 	[REG_LINECTRL2] = AUART_LINECTRL2,
398254da0d7SOleksij Rempel 	[REG_INTR] = AUART_INTR,
399254da0d7SOleksij Rempel 	[REG_DATA] = AUART_DATA,
400254da0d7SOleksij Rempel 	[REG_STAT] = AUART_STAT,
401254da0d7SOleksij Rempel 	[REG_DEBUG] = AUART_DEBUG,
402254da0d7SOleksij Rempel 	[REG_VERSION] = AUART_VERSION,
403254da0d7SOleksij Rempel 	[REG_AUTOBAUD] = AUART_AUTOBAUD,
404254da0d7SOleksij Rempel };
405254da0d7SOleksij Rempel 
406254da0d7SOleksij Rempel static const struct vendor_data vendor_alphascale_asm9260 = {
407254da0d7SOleksij Rempel 	.reg_offset = mxs_asm9260_offsets,
408254da0d7SOleksij Rempel };
409254da0d7SOleksij Rempel 
410254da0d7SOleksij Rempel static const struct vendor_data vendor_freescale_stmp37xx = {
411254da0d7SOleksij Rempel 	.reg_offset = mxs_stmp37xx_offsets,
412f4b1f03bSHuang Shijie };
413f4b1f03bSHuang Shijie 
414f19693a1SSascha Hauer struct mxs_auart_port {
415f19693a1SSascha Hauer 	struct uart_port port;
416f19693a1SSascha Hauer 
417e8001632SHuang Shijie #define MXS_AUART_DMA_ENABLED	0x2
418e8001632SHuang Shijie #define MXS_AUART_DMA_TX_SYNC	2  /* bit 2 */
419e8001632SHuang Shijie #define MXS_AUART_DMA_RX_READY	3  /* bit 3 */
4208418e67dSHuang Shijie #define MXS_AUART_RTSCTS	4  /* bit 4 */
421e8001632SHuang Shijie 	unsigned long flags;
422f9e42397SJanusz Uzycki 	unsigned int mctrl_prev;
423f4b1f03bSHuang Shijie 	enum mxs_auart_type devtype;
424254da0d7SOleksij Rempel 	const struct vendor_data *vendor;
425f19693a1SSascha Hauer 
426f19693a1SSascha Hauer 	struct clk *clk;
427254da0d7SOleksij Rempel 	struct clk *clk_ahb;
428f19693a1SSascha Hauer 	struct device *dev;
429e8001632SHuang Shijie 
430e8001632SHuang Shijie 	/* for DMA */
431e8001632SHuang Shijie 	struct scatterlist tx_sgl;
432e8001632SHuang Shijie 	struct dma_chan	*tx_dma_chan;
433e8001632SHuang Shijie 	void *tx_dma_buf;
434e8001632SHuang Shijie 
435e8001632SHuang Shijie 	struct scatterlist rx_sgl;
436e8001632SHuang Shijie 	struct dma_chan	*rx_dma_chan;
437e8001632SHuang Shijie 	void *rx_dma_buf;
4387c573d7eSJanusz Uzycki 
4397c573d7eSJanusz Uzycki 	struct mctrl_gpios	*gpios;
440f9e42397SJanusz Uzycki 	int			gpio_irq[UART_GPIO_MAX];
441f9e42397SJanusz Uzycki 	bool			ms_irq_enabled;
442f19693a1SSascha Hauer };
443f19693a1SSascha Hauer 
444ed0bb232SFabian Frederick static const struct of_device_id mxs_auart_dt_ids[] = {
445f4b1f03bSHuang Shijie 	{
446f4b1f03bSHuang Shijie 		.compatible = "fsl,imx28-auart",
4475f1697feSFabio Estevam 		.data = (const void *)IMX28_AUART
448f4b1f03bSHuang Shijie 	}, {
449f4b1f03bSHuang Shijie 		.compatible = "fsl,imx23-auart",
4505f1697feSFabio Estevam 		.data = (const void *)IMX23_AUART
451254da0d7SOleksij Rempel 	}, {
452254da0d7SOleksij Rempel 		.compatible = "alphascale,asm9260-auart",
4535f1697feSFabio Estevam 		.data = (const void *)ASM9260_AUART
454f4b1f03bSHuang Shijie 	}, { /* sentinel */ }
455f4b1f03bSHuang Shijie };
456f4b1f03bSHuang Shijie MODULE_DEVICE_TABLE(of, mxs_auart_dt_ids);
457f4b1f03bSHuang Shijie 
is_imx28_auart(struct mxs_auart_port * s)458f4b1f03bSHuang Shijie static inline int is_imx28_auart(struct mxs_auart_port *s)
459f4b1f03bSHuang Shijie {
460f4b1f03bSHuang Shijie 	return s->devtype == IMX28_AUART;
461f4b1f03bSHuang Shijie }
462f4b1f03bSHuang Shijie 
is_asm9260_auart(struct mxs_auart_port * s)463254da0d7SOleksij Rempel static inline int is_asm9260_auart(struct mxs_auart_port *s)
464254da0d7SOleksij Rempel {
465254da0d7SOleksij Rempel 	return s->devtype == ASM9260_AUART;
466254da0d7SOleksij Rempel }
467254da0d7SOleksij Rempel 
auart_dma_enabled(struct mxs_auart_port * s)468e8001632SHuang Shijie static inline bool auart_dma_enabled(struct mxs_auart_port *s)
469e8001632SHuang Shijie {
470e8001632SHuang Shijie 	return s->flags & MXS_AUART_DMA_ENABLED;
471e8001632SHuang Shijie }
472e8001632SHuang Shijie 
mxs_reg_to_offset(const struct mxs_auart_port * uap,unsigned int reg)473254da0d7SOleksij Rempel static unsigned int mxs_reg_to_offset(const struct mxs_auart_port *uap,
474254da0d7SOleksij Rempel 				      unsigned int reg)
475254da0d7SOleksij Rempel {
476254da0d7SOleksij Rempel 	return uap->vendor->reg_offset[reg];
477254da0d7SOleksij Rempel }
478254da0d7SOleksij Rempel 
mxs_read(const struct mxs_auart_port * uap,unsigned int reg)479254da0d7SOleksij Rempel static unsigned int mxs_read(const struct mxs_auart_port *uap,
480254da0d7SOleksij Rempel 			     unsigned int reg)
481254da0d7SOleksij Rempel {
482254da0d7SOleksij Rempel 	void __iomem *addr = uap->port.membase + mxs_reg_to_offset(uap, reg);
483254da0d7SOleksij Rempel 
484254da0d7SOleksij Rempel 	return readl_relaxed(addr);
485254da0d7SOleksij Rempel }
486254da0d7SOleksij Rempel 
mxs_write(unsigned int val,struct mxs_auart_port * uap,unsigned int reg)487254da0d7SOleksij Rempel static void mxs_write(unsigned int val, struct mxs_auart_port *uap,
488254da0d7SOleksij Rempel 		      unsigned int reg)
489254da0d7SOleksij Rempel {
490254da0d7SOleksij Rempel 	void __iomem *addr = uap->port.membase + mxs_reg_to_offset(uap, reg);
491254da0d7SOleksij Rempel 
492254da0d7SOleksij Rempel 	writel_relaxed(val, addr);
493254da0d7SOleksij Rempel }
494254da0d7SOleksij Rempel 
mxs_set(unsigned int val,struct mxs_auart_port * uap,unsigned int reg)495254da0d7SOleksij Rempel static void mxs_set(unsigned int val, struct mxs_auart_port *uap,
496254da0d7SOleksij Rempel 		    unsigned int reg)
497254da0d7SOleksij Rempel {
498254da0d7SOleksij Rempel 	void __iomem *addr = uap->port.membase + mxs_reg_to_offset(uap, reg);
499254da0d7SOleksij Rempel 
500254da0d7SOleksij Rempel 	writel_relaxed(val, addr + SET_REG);
501254da0d7SOleksij Rempel }
502254da0d7SOleksij Rempel 
mxs_clr(unsigned int val,struct mxs_auart_port * uap,unsigned int reg)503254da0d7SOleksij Rempel static void mxs_clr(unsigned int val, struct mxs_auart_port *uap,
504254da0d7SOleksij Rempel 		    unsigned int reg)
505254da0d7SOleksij Rempel {
506254da0d7SOleksij Rempel 	void __iomem *addr = uap->port.membase + mxs_reg_to_offset(uap, reg);
507254da0d7SOleksij Rempel 
508254da0d7SOleksij Rempel 	writel_relaxed(val, addr + CLR_REG);
509254da0d7SOleksij Rempel }
510254da0d7SOleksij Rempel 
511f19693a1SSascha Hauer static void mxs_auart_stop_tx(struct uart_port *u);
512f19693a1SSascha Hauer 
513f19693a1SSascha Hauer #define to_auart_port(u) container_of(u, struct mxs_auart_port, port)
514f19693a1SSascha Hauer 
515e8001632SHuang Shijie static void mxs_auart_tx_chars(struct mxs_auart_port *s);
516e8001632SHuang Shijie 
dma_tx_callback(void * param)517e8001632SHuang Shijie static void dma_tx_callback(void *param)
518e8001632SHuang Shijie {
519e8001632SHuang Shijie 	struct mxs_auart_port *s = param;
520e8001632SHuang Shijie 	struct circ_buf *xmit = &s->port.state->xmit;
521e8001632SHuang Shijie 
522e8001632SHuang Shijie 	dma_unmap_sg(s->dev, &s->tx_sgl, 1, DMA_TO_DEVICE);
523e8001632SHuang Shijie 
524e8001632SHuang Shijie 	/* clear the bit used to serialize the DMA tx. */
525e8001632SHuang Shijie 	clear_bit(MXS_AUART_DMA_TX_SYNC, &s->flags);
5264e857c58SPeter Zijlstra 	smp_mb__after_atomic();
527e8001632SHuang Shijie 
528e8001632SHuang Shijie 	/* wake up the possible processes. */
529e8001632SHuang Shijie 	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
530e8001632SHuang Shijie 		uart_write_wakeup(&s->port);
531e8001632SHuang Shijie 
532e8001632SHuang Shijie 	mxs_auart_tx_chars(s);
533e8001632SHuang Shijie }
534e8001632SHuang Shijie 
mxs_auart_dma_tx(struct mxs_auart_port * s,int size)535e8001632SHuang Shijie static int mxs_auart_dma_tx(struct mxs_auart_port *s, int size)
536e8001632SHuang Shijie {
537e8001632SHuang Shijie 	struct dma_async_tx_descriptor *desc;
538e8001632SHuang Shijie 	struct scatterlist *sgl = &s->tx_sgl;
539e8001632SHuang Shijie 	struct dma_chan *channel = s->tx_dma_chan;
540e8001632SHuang Shijie 	u32 pio;
541e8001632SHuang Shijie 
542e8001632SHuang Shijie 	/* [1] : send PIO. Note, the first pio word is CTRL1. */
543e8001632SHuang Shijie 	pio = AUART_CTRL1_XFER_COUNT(size);
544e8001632SHuang Shijie 	desc = dmaengine_prep_slave_sg(channel, (struct scatterlist *)&pio,
545e8001632SHuang Shijie 					1, DMA_TRANS_NONE, 0);
546e8001632SHuang Shijie 	if (!desc) {
547e8001632SHuang Shijie 		dev_err(s->dev, "step 1 error\n");
548e8001632SHuang Shijie 		return -EINVAL;
549e8001632SHuang Shijie 	}
550e8001632SHuang Shijie 
551e8001632SHuang Shijie 	/* [2] : set DMA buffer. */
552e8001632SHuang Shijie 	sg_init_one(sgl, s->tx_dma_buf, size);
553e8001632SHuang Shijie 	dma_map_sg(s->dev, sgl, 1, DMA_TO_DEVICE);
554e8001632SHuang Shijie 	desc = dmaengine_prep_slave_sg(channel, sgl,
555e8001632SHuang Shijie 			1, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
556e8001632SHuang Shijie 	if (!desc) {
557e8001632SHuang Shijie 		dev_err(s->dev, "step 2 error\n");
558e8001632SHuang Shijie 		return -EINVAL;
559e8001632SHuang Shijie 	}
560e8001632SHuang Shijie 
561e8001632SHuang Shijie 	/* [3] : submit the DMA */
562e8001632SHuang Shijie 	desc->callback = dma_tx_callback;
563e8001632SHuang Shijie 	desc->callback_param = s;
564e8001632SHuang Shijie 	dmaengine_submit(desc);
565e8001632SHuang Shijie 	dma_async_issue_pending(channel);
566e8001632SHuang Shijie 	return 0;
567e8001632SHuang Shijie }
568e8001632SHuang Shijie 
mxs_auart_tx_chars(struct mxs_auart_port * s)569e8001632SHuang Shijie static void mxs_auart_tx_chars(struct mxs_auart_port *s)
570f19693a1SSascha Hauer {
571f19693a1SSascha Hauer 	struct circ_buf *xmit = &s->port.state->xmit;
5722d141e68SJiri Slaby (SUSE) 	bool pending;
5732d141e68SJiri Slaby (SUSE) 	u8 ch;
574f19693a1SSascha Hauer 
575e8001632SHuang Shijie 	if (auart_dma_enabled(s)) {
57687b8bed2Sfabio.estevam@freescale.com 		u32 i = 0;
577e8001632SHuang Shijie 		int size;
578e8001632SHuang Shijie 		void *buffer = s->tx_dma_buf;
579e8001632SHuang Shijie 
580e8001632SHuang Shijie 		if (test_and_set_bit(MXS_AUART_DMA_TX_SYNC, &s->flags))
581e8001632SHuang Shijie 			return;
582e8001632SHuang Shijie 
583e8001632SHuang Shijie 		while (!uart_circ_empty(xmit) && !uart_tx_stopped(&s->port)) {
584e8001632SHuang Shijie 			size = min_t(u32, UART_XMIT_SIZE - i,
585e8001632SHuang Shijie 				     CIRC_CNT_TO_END(xmit->head,
586e8001632SHuang Shijie 						     xmit->tail,
587e8001632SHuang Shijie 						     UART_XMIT_SIZE));
588e8001632SHuang Shijie 			memcpy(buffer + i, xmit->buf + xmit->tail, size);
589e8001632SHuang Shijie 			xmit->tail = (xmit->tail + size) & (UART_XMIT_SIZE - 1);
590e8001632SHuang Shijie 
591e8001632SHuang Shijie 			i += size;
592e8001632SHuang Shijie 			if (i >= UART_XMIT_SIZE)
593e8001632SHuang Shijie 				break;
594e8001632SHuang Shijie 		}
595e8001632SHuang Shijie 
596e8001632SHuang Shijie 		if (uart_tx_stopped(&s->port))
597e8001632SHuang Shijie 			mxs_auart_stop_tx(&s->port);
598e8001632SHuang Shijie 
599e8001632SHuang Shijie 		if (i) {
600e8001632SHuang Shijie 			mxs_auart_dma_tx(s, i);
601e8001632SHuang Shijie 		} else {
602e8001632SHuang Shijie 			clear_bit(MXS_AUART_DMA_TX_SYNC, &s->flags);
6034e857c58SPeter Zijlstra 			smp_mb__after_atomic();
604e8001632SHuang Shijie 		}
605e8001632SHuang Shijie 		return;
606e8001632SHuang Shijie 	}
607e8001632SHuang Shijie 
60853600696SJiri Slaby (SUSE) 	pending = uart_port_tx_flags(&s->port, ch, UART_TX_NOSTOP,
6092d141e68SJiri Slaby (SUSE) 		!(mxs_read(s, REG_STAT) & AUART_STAT_TXFF),
6102d141e68SJiri Slaby (SUSE) 		mxs_write(ch, s, REG_DATA));
6112d141e68SJiri Slaby (SUSE) 	if (pending)
612254da0d7SOleksij Rempel 		mxs_set(AUART_INTR_TXIEN, s, REG_INTR);
6132d141e68SJiri Slaby (SUSE) 	else
6142d141e68SJiri Slaby (SUSE) 		mxs_clr(AUART_INTR_TXIEN, s, REG_INTR);
61553600696SJiri Slaby (SUSE) 
61653600696SJiri Slaby (SUSE) 	if (uart_tx_stopped(&s->port))
61753600696SJiri Slaby (SUSE)                mxs_auart_stop_tx(&s->port);
618f19693a1SSascha Hauer }
619f19693a1SSascha Hauer 
mxs_auart_rx_char(struct mxs_auart_port * s)620f19693a1SSascha Hauer static void mxs_auart_rx_char(struct mxs_auart_port *s)
621f19693a1SSascha Hauer {
622f19693a1SSascha Hauer 	u32 stat;
623fd2b55f8SJiri Slaby 	u8 c, flag;
624f19693a1SSascha Hauer 
625254da0d7SOleksij Rempel 	c = mxs_read(s, REG_DATA);
626254da0d7SOleksij Rempel 	stat = mxs_read(s, REG_STAT);
627f19693a1SSascha Hauer 
628f19693a1SSascha Hauer 	flag = TTY_NORMAL;
629f19693a1SSascha Hauer 	s->port.icount.rx++;
630f19693a1SSascha Hauer 
631f19693a1SSascha Hauer 	if (stat & AUART_STAT_BERR) {
632f19693a1SSascha Hauer 		s->port.icount.brk++;
633f19693a1SSascha Hauer 		if (uart_handle_break(&s->port))
634f19693a1SSascha Hauer 			goto out;
635f19693a1SSascha Hauer 	} else if (stat & AUART_STAT_PERR) {
636f19693a1SSascha Hauer 		s->port.icount.parity++;
637f19693a1SSascha Hauer 	} else if (stat & AUART_STAT_FERR) {
638f19693a1SSascha Hauer 		s->port.icount.frame++;
639f19693a1SSascha Hauer 	}
640f19693a1SSascha Hauer 
641f19693a1SSascha Hauer 	/*
642f19693a1SSascha Hauer 	 * Mask off conditions which should be ingored.
643f19693a1SSascha Hauer 	 */
644f19693a1SSascha Hauer 	stat &= s->port.read_status_mask;
645f19693a1SSascha Hauer 
646f19693a1SSascha Hauer 	if (stat & AUART_STAT_BERR) {
647f19693a1SSascha Hauer 		flag = TTY_BREAK;
648f19693a1SSascha Hauer 	} else if (stat & AUART_STAT_PERR)
649f19693a1SSascha Hauer 		flag = TTY_PARITY;
650f19693a1SSascha Hauer 	else if (stat & AUART_STAT_FERR)
651f19693a1SSascha Hauer 		flag = TTY_FRAME;
652f19693a1SSascha Hauer 
653f19693a1SSascha Hauer 	if (stat & AUART_STAT_OERR)
654f19693a1SSascha Hauer 		s->port.icount.overrun++;
655f19693a1SSascha Hauer 
656f19693a1SSascha Hauer 	if (uart_handle_sysrq_char(&s->port, c))
657f19693a1SSascha Hauer 		goto out;
658f19693a1SSascha Hauer 
659f19693a1SSascha Hauer 	uart_insert_char(&s->port, stat, AUART_STAT_OERR, c, flag);
660f19693a1SSascha Hauer out:
661254da0d7SOleksij Rempel 	mxs_write(stat, s, REG_STAT);
662f19693a1SSascha Hauer }
663f19693a1SSascha Hauer 
mxs_auart_rx_chars(struct mxs_auart_port * s)664f19693a1SSascha Hauer static void mxs_auart_rx_chars(struct mxs_auart_port *s)
665f19693a1SSascha Hauer {
666f19693a1SSascha Hauer 	u32 stat = 0;
667f19693a1SSascha Hauer 
668f19693a1SSascha Hauer 	for (;;) {
669254da0d7SOleksij Rempel 		stat = mxs_read(s, REG_STAT);
670f19693a1SSascha Hauer 		if (stat & AUART_STAT_RXFE)
671f19693a1SSascha Hauer 			break;
672f19693a1SSascha Hauer 		mxs_auart_rx_char(s);
673f19693a1SSascha Hauer 	}
674f19693a1SSascha Hauer 
675254da0d7SOleksij Rempel 	mxs_write(stat, s, REG_STAT);
6762e124b4aSJiri Slaby 	tty_flip_buffer_push(&s->port.state->port);
677f19693a1SSascha Hauer }
678f19693a1SSascha Hauer 
mxs_auart_request_port(struct uart_port * u)679f19693a1SSascha Hauer static int mxs_auart_request_port(struct uart_port *u)
680f19693a1SSascha Hauer {
681f19693a1SSascha Hauer 	return 0;
682f19693a1SSascha Hauer }
683f19693a1SSascha Hauer 
mxs_auart_verify_port(struct uart_port * u,struct serial_struct * ser)684f19693a1SSascha Hauer static int mxs_auart_verify_port(struct uart_port *u,
685f19693a1SSascha Hauer 				    struct serial_struct *ser)
686f19693a1SSascha Hauer {
687f19693a1SSascha Hauer 	if (u->type != PORT_UNKNOWN && u->type != PORT_IMX)
688f19693a1SSascha Hauer 		return -EINVAL;
689f19693a1SSascha Hauer 	return 0;
690f19693a1SSascha Hauer }
691f19693a1SSascha Hauer 
mxs_auart_config_port(struct uart_port * u,int flags)692f19693a1SSascha Hauer static void mxs_auart_config_port(struct uart_port *u, int flags)
693f19693a1SSascha Hauer {
694f19693a1SSascha Hauer }
695f19693a1SSascha Hauer 
mxs_auart_type(struct uart_port * u)696f19693a1SSascha Hauer static const char *mxs_auart_type(struct uart_port *u)
697f19693a1SSascha Hauer {
698f19693a1SSascha Hauer 	struct mxs_auart_port *s = to_auart_port(u);
699f19693a1SSascha Hauer 
700f19693a1SSascha Hauer 	return dev_name(s->dev);
701f19693a1SSascha Hauer }
702f19693a1SSascha Hauer 
mxs_auart_release_port(struct uart_port * u)703f19693a1SSascha Hauer static void mxs_auart_release_port(struct uart_port *u)
704f19693a1SSascha Hauer {
705f19693a1SSascha Hauer }
706f19693a1SSascha Hauer 
mxs_auart_set_mctrl(struct uart_port * u,unsigned mctrl)707f19693a1SSascha Hauer static void mxs_auart_set_mctrl(struct uart_port *u, unsigned mctrl)
708f19693a1SSascha Hauer {
7097c573d7eSJanusz Uzycki 	struct mxs_auart_port *s = to_auart_port(u);
7107c573d7eSJanusz Uzycki 
711254da0d7SOleksij Rempel 	u32 ctrl = mxs_read(s, REG_CTRL2);
712f19693a1SSascha Hauer 
713a6833214SSteffen Trumtrar 	ctrl &= ~(AUART_CTRL2_RTSEN | AUART_CTRL2_RTS);
71400592021SHuang Shijie 	if (mctrl & TIOCM_RTS) {
715299245a1SPeter Hurley 		if (uart_cts_enabled(u))
71600592021SHuang Shijie 			ctrl |= AUART_CTRL2_RTSEN;
717a6833214SSteffen Trumtrar 		else
718a6833214SSteffen Trumtrar 			ctrl |= AUART_CTRL2_RTS;
71900592021SHuang Shijie 	}
72000592021SHuang Shijie 
721254da0d7SOleksij Rempel 	mxs_write(ctrl, s, REG_CTRL2);
7227c573d7eSJanusz Uzycki 
7237c573d7eSJanusz Uzycki 	mctrl_gpio_set(s->gpios, mctrl);
724f19693a1SSascha Hauer }
725f19693a1SSascha Hauer 
726f9e42397SJanusz Uzycki #define MCTRL_ANY_DELTA        (TIOCM_RI | TIOCM_DSR | TIOCM_CD | TIOCM_CTS)
mxs_auart_modem_status(struct mxs_auart_port * s,u32 mctrl)727f9e42397SJanusz Uzycki static u32 mxs_auart_modem_status(struct mxs_auart_port *s, u32 mctrl)
728f9e42397SJanusz Uzycki {
729f9e42397SJanusz Uzycki 	u32 mctrl_diff;
730f9e42397SJanusz Uzycki 
731f9e42397SJanusz Uzycki 	mctrl_diff = mctrl ^ s->mctrl_prev;
732f9e42397SJanusz Uzycki 	s->mctrl_prev = mctrl;
733f9e42397SJanusz Uzycki 	if (mctrl_diff & MCTRL_ANY_DELTA && s->ms_irq_enabled &&
734f9e42397SJanusz Uzycki 						s->port.state != NULL) {
735f9e42397SJanusz Uzycki 		if (mctrl_diff & TIOCM_RI)
736f9e42397SJanusz Uzycki 			s->port.icount.rng++;
737f9e42397SJanusz Uzycki 		if (mctrl_diff & TIOCM_DSR)
738f9e42397SJanusz Uzycki 			s->port.icount.dsr++;
739f9e42397SJanusz Uzycki 		if (mctrl_diff & TIOCM_CD)
740f9e42397SJanusz Uzycki 			uart_handle_dcd_change(&s->port, mctrl & TIOCM_CD);
741f9e42397SJanusz Uzycki 		if (mctrl_diff & TIOCM_CTS)
742f9e42397SJanusz Uzycki 			uart_handle_cts_change(&s->port, mctrl & TIOCM_CTS);
743f9e42397SJanusz Uzycki 
744f9e42397SJanusz Uzycki 		wake_up_interruptible(&s->port.state->port.delta_msr_wait);
745f9e42397SJanusz Uzycki 	}
746f9e42397SJanusz Uzycki 	return mctrl;
747f9e42397SJanusz Uzycki }
748f9e42397SJanusz Uzycki 
mxs_auart_get_mctrl(struct uart_port * u)749f19693a1SSascha Hauer static u32 mxs_auart_get_mctrl(struct uart_port *u)
750f19693a1SSascha Hauer {
7517c573d7eSJanusz Uzycki 	struct mxs_auart_port *s = to_auart_port(u);
752254da0d7SOleksij Rempel 	u32 stat = mxs_read(s, REG_STAT);
75342b4eba0SJanusz Uzycki 	u32 mctrl = 0;
754f19693a1SSascha Hauer 
755f19693a1SSascha Hauer 	if (stat & AUART_STAT_CTS)
756f19693a1SSascha Hauer 		mctrl |= TIOCM_CTS;
757f19693a1SSascha Hauer 
7587c573d7eSJanusz Uzycki 	return mctrl_gpio_get(s->gpios, &mctrl);
759f19693a1SSascha Hauer }
760f19693a1SSascha Hauer 
761f9e42397SJanusz Uzycki /*
762f9e42397SJanusz Uzycki  * Enable modem status interrupts
763f9e42397SJanusz Uzycki  */
mxs_auart_enable_ms(struct uart_port * port)764f9e42397SJanusz Uzycki static void mxs_auart_enable_ms(struct uart_port *port)
765f9e42397SJanusz Uzycki {
766f9e42397SJanusz Uzycki 	struct mxs_auart_port *s = to_auart_port(port);
767f9e42397SJanusz Uzycki 
768f9e42397SJanusz Uzycki 	/*
769f9e42397SJanusz Uzycki 	 * Interrupt should not be enabled twice
770f9e42397SJanusz Uzycki 	 */
771f9e42397SJanusz Uzycki 	if (s->ms_irq_enabled)
772f9e42397SJanusz Uzycki 		return;
773f9e42397SJanusz Uzycki 
774f9e42397SJanusz Uzycki 	s->ms_irq_enabled = true;
775f9e42397SJanusz Uzycki 
776f9e42397SJanusz Uzycki 	if (s->gpio_irq[UART_GPIO_CTS] >= 0)
777f9e42397SJanusz Uzycki 		enable_irq(s->gpio_irq[UART_GPIO_CTS]);
778f9e42397SJanusz Uzycki 	/* TODO: enable AUART_INTR_CTSMIEN otherwise */
779f9e42397SJanusz Uzycki 
780f9e42397SJanusz Uzycki 	if (s->gpio_irq[UART_GPIO_DSR] >= 0)
781f9e42397SJanusz Uzycki 		enable_irq(s->gpio_irq[UART_GPIO_DSR]);
782f9e42397SJanusz Uzycki 
783f9e42397SJanusz Uzycki 	if (s->gpio_irq[UART_GPIO_RI] >= 0)
784f9e42397SJanusz Uzycki 		enable_irq(s->gpio_irq[UART_GPIO_RI]);
785f9e42397SJanusz Uzycki 
786f9e42397SJanusz Uzycki 	if (s->gpio_irq[UART_GPIO_DCD] >= 0)
787f9e42397SJanusz Uzycki 		enable_irq(s->gpio_irq[UART_GPIO_DCD]);
788f9e42397SJanusz Uzycki }
789f9e42397SJanusz Uzycki 
790f9e42397SJanusz Uzycki /*
791f9e42397SJanusz Uzycki  * Disable modem status interrupts
792f9e42397SJanusz Uzycki  */
mxs_auart_disable_ms(struct uart_port * port)793f9e42397SJanusz Uzycki static void mxs_auart_disable_ms(struct uart_port *port)
794f9e42397SJanusz Uzycki {
795f9e42397SJanusz Uzycki 	struct mxs_auart_port *s = to_auart_port(port);
796f9e42397SJanusz Uzycki 
797f9e42397SJanusz Uzycki 	/*
798f9e42397SJanusz Uzycki 	 * Interrupt should not be disabled twice
799f9e42397SJanusz Uzycki 	 */
800f9e42397SJanusz Uzycki 	if (!s->ms_irq_enabled)
801f9e42397SJanusz Uzycki 		return;
802f9e42397SJanusz Uzycki 
803f9e42397SJanusz Uzycki 	s->ms_irq_enabled = false;
804f9e42397SJanusz Uzycki 
805f9e42397SJanusz Uzycki 	if (s->gpio_irq[UART_GPIO_CTS] >= 0)
806f9e42397SJanusz Uzycki 		disable_irq(s->gpio_irq[UART_GPIO_CTS]);
807f9e42397SJanusz Uzycki 	/* TODO: disable AUART_INTR_CTSMIEN otherwise */
808f9e42397SJanusz Uzycki 
809f9e42397SJanusz Uzycki 	if (s->gpio_irq[UART_GPIO_DSR] >= 0)
810f9e42397SJanusz Uzycki 		disable_irq(s->gpio_irq[UART_GPIO_DSR]);
811f9e42397SJanusz Uzycki 
812f9e42397SJanusz Uzycki 	if (s->gpio_irq[UART_GPIO_RI] >= 0)
813f9e42397SJanusz Uzycki 		disable_irq(s->gpio_irq[UART_GPIO_RI]);
814f9e42397SJanusz Uzycki 
815f9e42397SJanusz Uzycki 	if (s->gpio_irq[UART_GPIO_DCD] >= 0)
816f9e42397SJanusz Uzycki 		disable_irq(s->gpio_irq[UART_GPIO_DCD]);
817f9e42397SJanusz Uzycki }
818f9e42397SJanusz Uzycki 
819e8001632SHuang Shijie static int mxs_auart_dma_prep_rx(struct mxs_auart_port *s);
dma_rx_callback(void * arg)820e8001632SHuang Shijie static void dma_rx_callback(void *arg)
821e8001632SHuang Shijie {
822e8001632SHuang Shijie 	struct mxs_auart_port *s = (struct mxs_auart_port *) arg;
82305c7cd39SJiri Slaby 	struct tty_port *port = &s->port.state->port;
824e8001632SHuang Shijie 	int count;
825e8001632SHuang Shijie 	u32 stat;
826e8001632SHuang Shijie 
827d7ffb932SHuang Shijie 	dma_unmap_sg(s->dev, &s->rx_sgl, 1, DMA_FROM_DEVICE);
828d7ffb932SHuang Shijie 
829254da0d7SOleksij Rempel 	stat = mxs_read(s, REG_STAT);
830e8001632SHuang Shijie 	stat &= ~(AUART_STAT_OERR | AUART_STAT_BERR |
831e8001632SHuang Shijie 			AUART_STAT_PERR | AUART_STAT_FERR);
832e8001632SHuang Shijie 
833e8001632SHuang Shijie 	count = stat & AUART_STAT_RXCOUNT_MASK;
83405c7cd39SJiri Slaby 	tty_insert_flip_string(port, s->rx_dma_buf, count);
835e8001632SHuang Shijie 
836254da0d7SOleksij Rempel 	mxs_write(stat, s, REG_STAT);
8372e124b4aSJiri Slaby 	tty_flip_buffer_push(port);
838e8001632SHuang Shijie 
839e8001632SHuang Shijie 	/* start the next DMA for RX. */
840e8001632SHuang Shijie 	mxs_auart_dma_prep_rx(s);
841e8001632SHuang Shijie }
842e8001632SHuang Shijie 
mxs_auart_dma_prep_rx(struct mxs_auart_port * s)843e8001632SHuang Shijie static int mxs_auart_dma_prep_rx(struct mxs_auart_port *s)
844e8001632SHuang Shijie {
845e8001632SHuang Shijie 	struct dma_async_tx_descriptor *desc;
846e8001632SHuang Shijie 	struct scatterlist *sgl = &s->rx_sgl;
847e8001632SHuang Shijie 	struct dma_chan *channel = s->rx_dma_chan;
848e8001632SHuang Shijie 	u32 pio[1];
849e8001632SHuang Shijie 
850e8001632SHuang Shijie 	/* [1] : send PIO */
851e8001632SHuang Shijie 	pio[0] = AUART_CTRL0_RXTO_ENABLE
852e8001632SHuang Shijie 		| AUART_CTRL0_RXTIMEOUT(0x80)
853e8001632SHuang Shijie 		| AUART_CTRL0_XFER_COUNT(UART_XMIT_SIZE);
854e8001632SHuang Shijie 	desc = dmaengine_prep_slave_sg(channel, (struct scatterlist *)pio,
855e8001632SHuang Shijie 					1, DMA_TRANS_NONE, 0);
856e8001632SHuang Shijie 	if (!desc) {
857e8001632SHuang Shijie 		dev_err(s->dev, "step 1 error\n");
858e8001632SHuang Shijie 		return -EINVAL;
859e8001632SHuang Shijie 	}
860e8001632SHuang Shijie 
861e8001632SHuang Shijie 	/* [2] : send DMA request */
862e8001632SHuang Shijie 	sg_init_one(sgl, s->rx_dma_buf, UART_XMIT_SIZE);
863e8001632SHuang Shijie 	dma_map_sg(s->dev, sgl, 1, DMA_FROM_DEVICE);
864e8001632SHuang Shijie 	desc = dmaengine_prep_slave_sg(channel, sgl, 1, DMA_DEV_TO_MEM,
865e8001632SHuang Shijie 					DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
866e8001632SHuang Shijie 	if (!desc) {
867e8001632SHuang Shijie 		dev_err(s->dev, "step 2 error\n");
868e8001632SHuang Shijie 		return -1;
869e8001632SHuang Shijie 	}
870e8001632SHuang Shijie 
871e8001632SHuang Shijie 	/* [3] : submit the DMA, but do not issue it. */
872e8001632SHuang Shijie 	desc->callback = dma_rx_callback;
873e8001632SHuang Shijie 	desc->callback_param = s;
874e8001632SHuang Shijie 	dmaengine_submit(desc);
875e8001632SHuang Shijie 	dma_async_issue_pending(channel);
876e8001632SHuang Shijie 	return 0;
877e8001632SHuang Shijie }
878e8001632SHuang Shijie 
mxs_auart_dma_exit_channel(struct mxs_auart_port * s)879e8001632SHuang Shijie static void mxs_auart_dma_exit_channel(struct mxs_auart_port *s)
880e8001632SHuang Shijie {
881e8001632SHuang Shijie 	if (s->tx_dma_chan) {
882e8001632SHuang Shijie 		dma_release_channel(s->tx_dma_chan);
883e8001632SHuang Shijie 		s->tx_dma_chan = NULL;
884e8001632SHuang Shijie 	}
885e8001632SHuang Shijie 	if (s->rx_dma_chan) {
886e8001632SHuang Shijie 		dma_release_channel(s->rx_dma_chan);
887e8001632SHuang Shijie 		s->rx_dma_chan = NULL;
888e8001632SHuang Shijie 	}
889e8001632SHuang Shijie 
890e8001632SHuang Shijie 	kfree(s->tx_dma_buf);
891e8001632SHuang Shijie 	kfree(s->rx_dma_buf);
892e8001632SHuang Shijie 	s->tx_dma_buf = NULL;
893e8001632SHuang Shijie 	s->rx_dma_buf = NULL;
894e8001632SHuang Shijie }
895e8001632SHuang Shijie 
mxs_auart_dma_exit(struct mxs_auart_port * s)896e8001632SHuang Shijie static void mxs_auart_dma_exit(struct mxs_auart_port *s)
897e8001632SHuang Shijie {
898e8001632SHuang Shijie 
899254da0d7SOleksij Rempel 	mxs_clr(AUART_CTRL2_TXDMAE | AUART_CTRL2_RXDMAE | AUART_CTRL2_DMAONERR,
900254da0d7SOleksij Rempel 		s, REG_CTRL2);
901e8001632SHuang Shijie 
902e8001632SHuang Shijie 	mxs_auart_dma_exit_channel(s);
903e8001632SHuang Shijie 	s->flags &= ~MXS_AUART_DMA_ENABLED;
904e8001632SHuang Shijie 	clear_bit(MXS_AUART_DMA_TX_SYNC, &s->flags);
905e8001632SHuang Shijie 	clear_bit(MXS_AUART_DMA_RX_READY, &s->flags);
906e8001632SHuang Shijie }
907e8001632SHuang Shijie 
mxs_auart_dma_init(struct mxs_auart_port * s)908e8001632SHuang Shijie static int mxs_auart_dma_init(struct mxs_auart_port *s)
909e8001632SHuang Shijie {
910e8001632SHuang Shijie 	if (auart_dma_enabled(s))
911e8001632SHuang Shijie 		return 0;
912e8001632SHuang Shijie 
913e8001632SHuang Shijie 	/* init for RX */
914bcc20f9eSShawn Guo 	s->rx_dma_chan = dma_request_slave_channel(s->dev, "rx");
915e8001632SHuang Shijie 	if (!s->rx_dma_chan)
916e8001632SHuang Shijie 		goto err_out;
917e8001632SHuang Shijie 	s->rx_dma_buf = kzalloc(UART_XMIT_SIZE, GFP_KERNEL | GFP_DMA);
918e8001632SHuang Shijie 	if (!s->rx_dma_buf)
919e8001632SHuang Shijie 		goto err_out;
920e8001632SHuang Shijie 
921e8001632SHuang Shijie 	/* init for TX */
922bcc20f9eSShawn Guo 	s->tx_dma_chan = dma_request_slave_channel(s->dev, "tx");
923e8001632SHuang Shijie 	if (!s->tx_dma_chan)
924e8001632SHuang Shijie 		goto err_out;
925e8001632SHuang Shijie 	s->tx_dma_buf = kzalloc(UART_XMIT_SIZE, GFP_KERNEL | GFP_DMA);
926e8001632SHuang Shijie 	if (!s->tx_dma_buf)
927e8001632SHuang Shijie 		goto err_out;
928e8001632SHuang Shijie 
929e8001632SHuang Shijie 	/* set the flags */
930e8001632SHuang Shijie 	s->flags |= MXS_AUART_DMA_ENABLED;
931e8001632SHuang Shijie 	dev_dbg(s->dev, "enabled the DMA support.");
932e8001632SHuang Shijie 
9339987f76aSHector Palacios 	/* The DMA buffer is now the FIFO the TTY subsystem can use */
9349987f76aSHector Palacios 	s->port.fifosize = UART_XMIT_SIZE;
9359987f76aSHector Palacios 
936e8001632SHuang Shijie 	return 0;
937e8001632SHuang Shijie 
938e8001632SHuang Shijie err_out:
939e8001632SHuang Shijie 	mxs_auart_dma_exit_channel(s);
940e8001632SHuang Shijie 	return -EINVAL;
941e8001632SHuang Shijie 
942e8001632SHuang Shijie }
943e8001632SHuang Shijie 
9446cbdf5c6SGeert Uytterhoeven #define RTS_AT_AUART()	!mctrl_gpio_to_gpiod(s->gpios, UART_GPIO_RTS)
9456cbdf5c6SGeert Uytterhoeven #define CTS_AT_AUART()	!mctrl_gpio_to_gpiod(s->gpios, UART_GPIO_CTS)
mxs_auart_settermios(struct uart_port * u,struct ktermios * termios,const struct ktermios * old)946f19693a1SSascha Hauer static void mxs_auart_settermios(struct uart_port *u,
947f19693a1SSascha Hauer 				 struct ktermios *termios,
948bec5b814SIlpo Järvinen 				 const struct ktermios *old)
949f19693a1SSascha Hauer {
950e8001632SHuang Shijie 	struct mxs_auart_port *s = to_auart_port(u);
9513ec2ff37SJiri Slaby 	u32 ctrl, ctrl2, div;
952df57cf6aSStefan Wahren 	unsigned int cflag, baud, baud_min, baud_max;
953f19693a1SSascha Hauer 
954f19693a1SSascha Hauer 	cflag = termios->c_cflag;
955f19693a1SSascha Hauer 
956f19693a1SSascha Hauer 	ctrl = AUART_LINECTRL_FEN;
957254da0d7SOleksij Rempel 	ctrl2 = mxs_read(s, REG_CTRL2);
958f19693a1SSascha Hauer 
9593ec2ff37SJiri Slaby 	ctrl |= AUART_LINECTRL_WLEN(tty_get_char_size(cflag));
960f19693a1SSascha Hauer 
961f19693a1SSascha Hauer 	/* parity */
962f19693a1SSascha Hauer 	if (cflag & PARENB) {
963f19693a1SSascha Hauer 		ctrl |= AUART_LINECTRL_PEN;
964f19693a1SSascha Hauer 		if ((cflag & PARODD) == 0)
965f19693a1SSascha Hauer 			ctrl |= AUART_LINECTRL_EPS;
966f87fa71eSWolfgang Ocker 		if (cflag & CMSPAR)
967f87fa71eSWolfgang Ocker 			ctrl |= AUART_LINECTRL_SPS;
968f19693a1SSascha Hauer 	}
969f19693a1SSascha Hauer 
970b8106454SWolfgang Ocker 	u->read_status_mask = AUART_STAT_OERR;
971f19693a1SSascha Hauer 
972f19693a1SSascha Hauer 	if (termios->c_iflag & INPCK)
973f19693a1SSascha Hauer 		u->read_status_mask |= AUART_STAT_PERR;
974ef8b9ddcSPeter Hurley 	if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
975f19693a1SSascha Hauer 		u->read_status_mask |= AUART_STAT_BERR;
976f19693a1SSascha Hauer 
977f19693a1SSascha Hauer 	/*
978f19693a1SSascha Hauer 	 * Characters to ignore
979f19693a1SSascha Hauer 	 */
980f19693a1SSascha Hauer 	u->ignore_status_mask = 0;
981f19693a1SSascha Hauer 	if (termios->c_iflag & IGNPAR)
982f19693a1SSascha Hauer 		u->ignore_status_mask |= AUART_STAT_PERR;
983f19693a1SSascha Hauer 	if (termios->c_iflag & IGNBRK) {
984f19693a1SSascha Hauer 		u->ignore_status_mask |= AUART_STAT_BERR;
985f19693a1SSascha Hauer 		/*
986f19693a1SSascha Hauer 		 * If we're ignoring parity and break indicators,
987f19693a1SSascha Hauer 		 * ignore overruns too (for real raw support).
988f19693a1SSascha Hauer 		 */
989f19693a1SSascha Hauer 		if (termios->c_iflag & IGNPAR)
990f19693a1SSascha Hauer 			u->ignore_status_mask |= AUART_STAT_OERR;
991f19693a1SSascha Hauer 	}
992f19693a1SSascha Hauer 
993f19693a1SSascha Hauer 	/*
994f19693a1SSascha Hauer 	 * ignore all characters if CREAD is not set
995f19693a1SSascha Hauer 	 */
996f19693a1SSascha Hauer 	if (cflag & CREAD)
997f19693a1SSascha Hauer 		ctrl2 |= AUART_CTRL2_RXE;
998f19693a1SSascha Hauer 	else
999f19693a1SSascha Hauer 		ctrl2 &= ~AUART_CTRL2_RXE;
1000f19693a1SSascha Hauer 
1001f19693a1SSascha Hauer 	/* figure out the stop bits requested */
1002f19693a1SSascha Hauer 	if (cflag & CSTOPB)
1003f19693a1SSascha Hauer 		ctrl |= AUART_LINECTRL_STP2;
1004f19693a1SSascha Hauer 
1005f19693a1SSascha Hauer 	/* figure out the hardware flow control settings */
10067c573d7eSJanusz Uzycki 	ctrl2 &= ~(AUART_CTRL2_CTSEN | AUART_CTRL2_RTSEN);
1007e8001632SHuang Shijie 	if (cflag & CRTSCTS) {
1008e8001632SHuang Shijie 		/*
1009e8001632SHuang Shijie 		 * The DMA has a bug(see errata:2836) in mx23.
1010e8001632SHuang Shijie 		 * So we can not implement the DMA for auart in mx23,
1011e8001632SHuang Shijie 		 * we can only implement the DMA support for auart
1012e8001632SHuang Shijie 		 * in mx28.
1013e8001632SHuang Shijie 		 */
1014afab2203SHuang Shijie 		if (is_imx28_auart(s)
10158418e67dSHuang Shijie 				&& test_bit(MXS_AUART_RTSCTS, &s->flags)) {
1016e8001632SHuang Shijie 			if (!mxs_auart_dma_init(s))
1017e8001632SHuang Shijie 				/* enable DMA tranfer */
1018e8001632SHuang Shijie 				ctrl2 |= AUART_CTRL2_TXDMAE | AUART_CTRL2_RXDMAE
1019e8001632SHuang Shijie 				       | AUART_CTRL2_DMAONERR;
1020e8001632SHuang Shijie 		}
10217c573d7eSJanusz Uzycki 		/* Even if RTS is GPIO line RTSEN can be enabled because
10227c573d7eSJanusz Uzycki 		 * the pinctrl configuration decides about RTS pin function */
10237c573d7eSJanusz Uzycki 		ctrl2 |= AUART_CTRL2_RTSEN;
10247c573d7eSJanusz Uzycki 		if (CTS_AT_AUART())
10257c573d7eSJanusz Uzycki 			ctrl2 |= AUART_CTRL2_CTSEN;
1026e8001632SHuang Shijie 	}
1027f19693a1SSascha Hauer 
1028f19693a1SSascha Hauer 	/* set baud rate */
1029254da0d7SOleksij Rempel 	if (is_asm9260_auart(s)) {
1030254da0d7SOleksij Rempel 		baud = uart_get_baud_rate(u, termios, old,
1031254da0d7SOleksij Rempel 					  u->uartclk * 4 / 0x3FFFFF,
1032254da0d7SOleksij Rempel 					  u->uartclk / 16);
1033254da0d7SOleksij Rempel 		div = u->uartclk * 4 / baud;
1034254da0d7SOleksij Rempel 	} else {
1035254da0d7SOleksij Rempel 		baud_min = DIV_ROUND_UP(u->uartclk * 32,
1036254da0d7SOleksij Rempel 					AUART_LINECTRL_BAUD_DIV_MAX);
1037df57cf6aSStefan Wahren 		baud_max = u->uartclk * 32 / AUART_LINECTRL_BAUD_DIV_MIN;
1038df57cf6aSStefan Wahren 		baud = uart_get_baud_rate(u, termios, old, baud_min, baud_max);
1039a6040bc6SUwe Kleine-König 		div = DIV_ROUND_CLOSEST(u->uartclk * 32, baud);
1040254da0d7SOleksij Rempel 	}
1041254da0d7SOleksij Rempel 
1042f19693a1SSascha Hauer 	ctrl |= AUART_LINECTRL_BAUD_DIVFRAC(div & 0x3F);
1043f19693a1SSascha Hauer 	ctrl |= AUART_LINECTRL_BAUD_DIVINT(div >> 6);
1044254da0d7SOleksij Rempel 	mxs_write(ctrl, s, REG_LINECTRL);
1045f19693a1SSascha Hauer 
1046254da0d7SOleksij Rempel 	mxs_write(ctrl2, s, REG_CTRL2);
10478b979f7cSLothar Waßmann 
10488b979f7cSLothar Waßmann 	uart_update_timeout(u, termios->c_cflag, baud);
1049e8001632SHuang Shijie 
1050e8001632SHuang Shijie 	/* prepare for the DMA RX. */
1051e8001632SHuang Shijie 	if (auart_dma_enabled(s) &&
1052e8001632SHuang Shijie 		!test_and_set_bit(MXS_AUART_DMA_RX_READY, &s->flags)) {
1053e8001632SHuang Shijie 		if (!mxs_auart_dma_prep_rx(s)) {
1054e8001632SHuang Shijie 			/* Disable the normal RX interrupt. */
1055254da0d7SOleksij Rempel 			mxs_clr(AUART_INTR_RXIEN | AUART_INTR_RTIEN,
1056254da0d7SOleksij Rempel 				s, REG_INTR);
1057e8001632SHuang Shijie 		} else {
1058e8001632SHuang Shijie 			mxs_auart_dma_exit(s);
1059e8001632SHuang Shijie 			dev_err(s->dev, "We can not start up the DMA.\n");
1060e8001632SHuang Shijie 		}
1061e8001632SHuang Shijie 	}
1062f9e42397SJanusz Uzycki 
1063f9e42397SJanusz Uzycki 	/* CTS flow-control and modem-status interrupts */
1064f9e42397SJanusz Uzycki 	if (UART_ENABLE_MS(u, termios->c_cflag))
1065f9e42397SJanusz Uzycki 		mxs_auart_enable_ms(u);
1066f9e42397SJanusz Uzycki 	else
1067f9e42397SJanusz Uzycki 		mxs_auart_disable_ms(u);
1068f19693a1SSascha Hauer }
1069f19693a1SSascha Hauer 
mxs_auart_set_ldisc(struct uart_port * port,struct ktermios * termios)1070f3006e44SFabio Estevam static void mxs_auart_set_ldisc(struct uart_port *port,
1071f3006e44SFabio Estevam 				struct ktermios *termios)
107236a26278SJanusz Uzycki {
1073f3006e44SFabio Estevam 	if (termios->c_line == N_PPS) {
107436a26278SJanusz Uzycki 		port->flags |= UPF_HARDPPS_CD;
107536a26278SJanusz Uzycki 		mxs_auart_enable_ms(port);
107636a26278SJanusz Uzycki 	} else {
107736a26278SJanusz Uzycki 		port->flags &= ~UPF_HARDPPS_CD;
107836a26278SJanusz Uzycki 	}
107936a26278SJanusz Uzycki }
108036a26278SJanusz Uzycki 
mxs_auart_irq_handle(int irq,void * context)1081f19693a1SSascha Hauer static irqreturn_t mxs_auart_irq_handle(int irq, void *context)
1082f19693a1SSascha Hauer {
1083*5f40fd6cSEmil Kronborg 	u32 istat, stat;
1084f19693a1SSascha Hauer 	struct mxs_auart_port *s = context;
108508f937f4SJanusz Uzycki 	u32 mctrl_temp = s->mctrl_prev;
1086f19693a1SSascha Hauer 
1087*5f40fd6cSEmil Kronborg 	uart_port_lock(&s->port);
1088*5f40fd6cSEmil Kronborg 
1089*5f40fd6cSEmil Kronborg 	stat = mxs_read(s, REG_STAT);
1090254da0d7SOleksij Rempel 	istat = mxs_read(s, REG_INTR);
1091d970d7feSUwe Kleine-König 
1092d970d7feSUwe Kleine-König 	/* ack irq */
1093254da0d7SOleksij Rempel 	mxs_clr(istat & (AUART_INTR_RTIS | AUART_INTR_TXIS | AUART_INTR_RXIS
1094254da0d7SOleksij Rempel 		| AUART_INTR_CTSMIS), s, REG_INTR);
1095f19693a1SSascha Hauer 
1096f9e42397SJanusz Uzycki 	/*
1097f9e42397SJanusz Uzycki 	 * Dealing with GPIO interrupt
1098f9e42397SJanusz Uzycki 	 */
1099f9e42397SJanusz Uzycki 	if (irq == s->gpio_irq[UART_GPIO_CTS] ||
1100f9e42397SJanusz Uzycki 	    irq == s->gpio_irq[UART_GPIO_DCD] ||
1101f9e42397SJanusz Uzycki 	    irq == s->gpio_irq[UART_GPIO_DSR] ||
1102f9e42397SJanusz Uzycki 	    irq == s->gpio_irq[UART_GPIO_RI])
1103f9e42397SJanusz Uzycki 		mxs_auart_modem_status(s,
110408f937f4SJanusz Uzycki 				mctrl_gpio_get(s->gpios, &mctrl_temp));
1105f9e42397SJanusz Uzycki 
1106f19693a1SSascha Hauer 	if (istat & AUART_INTR_CTSMIS) {
1107f9e42397SJanusz Uzycki 		if (CTS_AT_AUART() && s->ms_irq_enabled)
11087c573d7eSJanusz Uzycki 			uart_handle_cts_change(&s->port,
11097c573d7eSJanusz Uzycki 					stat & AUART_STAT_CTS);
1110254da0d7SOleksij Rempel 		mxs_clr(AUART_INTR_CTSMIS, s, REG_INTR);
1111f19693a1SSascha Hauer 		istat &= ~AUART_INTR_CTSMIS;
1112f19693a1SSascha Hauer 	}
1113f19693a1SSascha Hauer 
1114f19693a1SSascha Hauer 	if (istat & (AUART_INTR_RTIS | AUART_INTR_RXIS)) {
1115a5919442SHuang Shijie 		if (!auart_dma_enabled(s))
1116f19693a1SSascha Hauer 			mxs_auart_rx_chars(s);
1117f19693a1SSascha Hauer 		istat &= ~(AUART_INTR_RTIS | AUART_INTR_RXIS);
1118f19693a1SSascha Hauer 	}
1119f19693a1SSascha Hauer 
1120f19693a1SSascha Hauer 	if (istat & AUART_INTR_TXIS) {
1121f19693a1SSascha Hauer 		mxs_auart_tx_chars(s);
1122f19693a1SSascha Hauer 		istat &= ~AUART_INTR_TXIS;
1123f19693a1SSascha Hauer 	}
1124f19693a1SSascha Hauer 
1125*5f40fd6cSEmil Kronborg 	uart_port_unlock(&s->port);
1126*5f40fd6cSEmil Kronborg 
1127f19693a1SSascha Hauer 	return IRQ_HANDLED;
1128f19693a1SSascha Hauer }
1129f19693a1SSascha Hauer 
mxs_auart_reset_deassert(struct mxs_auart_port * s)1130254da0d7SOleksij Rempel static void mxs_auart_reset_deassert(struct mxs_auart_port *s)
1131f19693a1SSascha Hauer {
1132f19693a1SSascha Hauer 	int i;
1133f19693a1SSascha Hauer 	unsigned int reg;
1134f19693a1SSascha Hauer 
1135254da0d7SOleksij Rempel 	mxs_clr(AUART_CTRL0_SFTRST, s, REG_CTRL0);
1136f19693a1SSascha Hauer 
1137f19693a1SSascha Hauer 	for (i = 0; i < 10000; i++) {
1138254da0d7SOleksij Rempel 		reg = mxs_read(s, REG_CTRL0);
1139f19693a1SSascha Hauer 		if (!(reg & AUART_CTRL0_SFTRST))
1140f19693a1SSascha Hauer 			break;
1141f19693a1SSascha Hauer 		udelay(3);
1142f19693a1SSascha Hauer 	}
1143254da0d7SOleksij Rempel 	mxs_clr(AUART_CTRL0_CLKGATE, s, REG_CTRL0);
1144f19693a1SSascha Hauer }
1145f19693a1SSascha Hauer 
mxs_auart_reset_assert(struct mxs_auart_port * s)1146254da0d7SOleksij Rempel static void mxs_auart_reset_assert(struct mxs_auart_port *s)
114717dc72cfSJuergen Borleis {
114817dc72cfSJuergen Borleis 	int i;
114917dc72cfSJuergen Borleis 	u32 reg;
115017dc72cfSJuergen Borleis 
1151254da0d7SOleksij Rempel 	reg = mxs_read(s, REG_CTRL0);
115217dc72cfSJuergen Borleis 	/* if already in reset state, keep it untouched */
115317dc72cfSJuergen Borleis 	if (reg & AUART_CTRL0_SFTRST)
115417dc72cfSJuergen Borleis 		return;
115517dc72cfSJuergen Borleis 
1156254da0d7SOleksij Rempel 	mxs_clr(AUART_CTRL0_CLKGATE, s, REG_CTRL0);
1157254da0d7SOleksij Rempel 	mxs_set(AUART_CTRL0_SFTRST, s, REG_CTRL0);
115817dc72cfSJuergen Borleis 
115917dc72cfSJuergen Borleis 	for (i = 0; i < 1000; i++) {
1160254da0d7SOleksij Rempel 		reg = mxs_read(s, REG_CTRL0);
116117dc72cfSJuergen Borleis 		/* reset is finished when the clock is gated */
116217dc72cfSJuergen Borleis 		if (reg & AUART_CTRL0_CLKGATE)
116317dc72cfSJuergen Borleis 			return;
116417dc72cfSJuergen Borleis 		udelay(10);
116517dc72cfSJuergen Borleis 	}
116617dc72cfSJuergen Borleis 
1167254da0d7SOleksij Rempel 	dev_err(s->dev, "Failed to reset the unit.");
116817dc72cfSJuergen Borleis }
116917dc72cfSJuergen Borleis 
mxs_auart_startup(struct uart_port * u)1170f19693a1SSascha Hauer static int mxs_auart_startup(struct uart_port *u)
1171f19693a1SSascha Hauer {
11729bbc3dcaSFabio Estevam 	int ret;
1173f19693a1SSascha Hauer 	struct mxs_auart_port *s = to_auart_port(u);
1174f19693a1SSascha Hauer 
11759bbc3dcaSFabio Estevam 	ret = clk_prepare_enable(s->clk);
11769bbc3dcaSFabio Estevam 	if (ret)
11779bbc3dcaSFabio Estevam 		return ret;
1178f19693a1SSascha Hauer 
117917dc72cfSJuergen Borleis 	if (uart_console(u)) {
1180254da0d7SOleksij Rempel 		mxs_clr(AUART_CTRL0_CLKGATE, s, REG_CTRL0);
118117dc72cfSJuergen Borleis 	} else {
118217dc72cfSJuergen Borleis 		/* reset the unit to a well known state */
1183254da0d7SOleksij Rempel 		mxs_auart_reset_assert(s);
1184254da0d7SOleksij Rempel 		mxs_auart_reset_deassert(s);
118517dc72cfSJuergen Borleis 	}
1186f19693a1SSascha Hauer 
1187254da0d7SOleksij Rempel 	mxs_set(AUART_CTRL2_UARTEN, s, REG_CTRL2);
1188f19693a1SSascha Hauer 
1189254da0d7SOleksij Rempel 	mxs_write(AUART_INTR_RXIEN | AUART_INTR_RTIEN | AUART_INTR_CTSMIEN,
1190254da0d7SOleksij Rempel 		  s, REG_INTR);
1191f19693a1SSascha Hauer 
11929987f76aSHector Palacios 	/* Reset FIFO size (it could have changed if DMA was enabled) */
11939987f76aSHector Palacios 	u->fifosize = MXS_AUART_FIFO_SIZE;
11949987f76aSHector Palacios 
1195f19693a1SSascha Hauer 	/*
1196f19693a1SSascha Hauer 	 * Enable fifo so all four bytes of a DMA word are written to
1197f19693a1SSascha Hauer 	 * output (otherwise, only the LSB is written, ie. 1 in 4 bytes)
1198f19693a1SSascha Hauer 	 */
1199254da0d7SOleksij Rempel 	mxs_set(AUART_LINECTRL_FEN, s, REG_LINECTRL);
1200f19693a1SSascha Hauer 
1201f9e42397SJanusz Uzycki 	/* get initial status of modem lines */
1202f9e42397SJanusz Uzycki 	mctrl_gpio_get(s->gpios, &s->mctrl_prev);
1203f9e42397SJanusz Uzycki 
1204f9e42397SJanusz Uzycki 	s->ms_irq_enabled = false;
1205f19693a1SSascha Hauer 	return 0;
1206f19693a1SSascha Hauer }
1207f19693a1SSascha Hauer 
mxs_auart_shutdown(struct uart_port * u)1208f19693a1SSascha Hauer static void mxs_auart_shutdown(struct uart_port *u)
1209f19693a1SSascha Hauer {
1210f19693a1SSascha Hauer 	struct mxs_auart_port *s = to_auart_port(u);
1211f19693a1SSascha Hauer 
1212f9e42397SJanusz Uzycki 	mxs_auart_disable_ms(u);
1213f9e42397SJanusz Uzycki 
1214e8001632SHuang Shijie 	if (auart_dma_enabled(s))
1215e8001632SHuang Shijie 		mxs_auart_dma_exit(s);
1216e8001632SHuang Shijie 
121717dc72cfSJuergen Borleis 	if (uart_console(u)) {
1218254da0d7SOleksij Rempel 		mxs_clr(AUART_CTRL2_UARTEN, s, REG_CTRL2);
1219254da0d7SOleksij Rempel 
1220254da0d7SOleksij Rempel 		mxs_clr(AUART_INTR_RXIEN | AUART_INTR_RTIEN |
1221254da0d7SOleksij Rempel 			AUART_INTR_CTSMIEN, s, REG_INTR);
1222254da0d7SOleksij Rempel 		mxs_set(AUART_CTRL0_CLKGATE, s, REG_CTRL0);
122317dc72cfSJuergen Borleis 	} else {
1224254da0d7SOleksij Rempel 		mxs_auart_reset_assert(s);
122517dc72cfSJuergen Borleis 	}
1226851b714bSHuang Shijie 
1227a4813770SShawn Guo 	clk_disable_unprepare(s->clk);
1228f19693a1SSascha Hauer }
1229f19693a1SSascha Hauer 
mxs_auart_tx_empty(struct uart_port * u)1230f19693a1SSascha Hauer static unsigned int mxs_auart_tx_empty(struct uart_port *u)
1231f19693a1SSascha Hauer {
1232254da0d7SOleksij Rempel 	struct mxs_auart_port *s = to_auart_port(u);
1233254da0d7SOleksij Rempel 
1234254da0d7SOleksij Rempel 	if ((mxs_read(s, REG_STAT) &
12352b310ec7SJanusz Uzycki 		 (AUART_STAT_TXFE | AUART_STAT_BUSY)) == AUART_STAT_TXFE)
1236f19693a1SSascha Hauer 		return TIOCSER_TEMT;
12372b310ec7SJanusz Uzycki 
1238f19693a1SSascha Hauer 	return 0;
1239f19693a1SSascha Hauer }
1240f19693a1SSascha Hauer 
mxs_auart_start_tx(struct uart_port * u)1241f19693a1SSascha Hauer static void mxs_auart_start_tx(struct uart_port *u)
1242f19693a1SSascha Hauer {
1243f19693a1SSascha Hauer 	struct mxs_auart_port *s = to_auart_port(u);
1244f19693a1SSascha Hauer 
1245f19693a1SSascha Hauer 	/* enable transmitter */
1246254da0d7SOleksij Rempel 	mxs_set(AUART_CTRL2_TXE, s, REG_CTRL2);
1247f19693a1SSascha Hauer 
1248f19693a1SSascha Hauer 	mxs_auart_tx_chars(s);
1249f19693a1SSascha Hauer }
1250f19693a1SSascha Hauer 
mxs_auart_stop_tx(struct uart_port * u)1251f19693a1SSascha Hauer static void mxs_auart_stop_tx(struct uart_port *u)
1252f19693a1SSascha Hauer {
1253254da0d7SOleksij Rempel 	struct mxs_auart_port *s = to_auart_port(u);
1254254da0d7SOleksij Rempel 
1255254da0d7SOleksij Rempel 	mxs_clr(AUART_CTRL2_TXE, s, REG_CTRL2);
1256f19693a1SSascha Hauer }
1257f19693a1SSascha Hauer 
mxs_auart_stop_rx(struct uart_port * u)1258f19693a1SSascha Hauer static void mxs_auart_stop_rx(struct uart_port *u)
1259f19693a1SSascha Hauer {
1260254da0d7SOleksij Rempel 	struct mxs_auart_port *s = to_auart_port(u);
1261254da0d7SOleksij Rempel 
1262254da0d7SOleksij Rempel 	mxs_clr(AUART_CTRL2_RXE, s, REG_CTRL2);
1263f19693a1SSascha Hauer }
1264f19693a1SSascha Hauer 
mxs_auart_break_ctl(struct uart_port * u,int ctl)1265f19693a1SSascha Hauer static void mxs_auart_break_ctl(struct uart_port *u, int ctl)
1266f19693a1SSascha Hauer {
1267254da0d7SOleksij Rempel 	struct mxs_auart_port *s = to_auart_port(u);
1268254da0d7SOleksij Rempel 
1269f19693a1SSascha Hauer 	if (ctl)
1270254da0d7SOleksij Rempel 		mxs_set(AUART_LINECTRL_BRK, s, REG_LINECTRL);
1271f19693a1SSascha Hauer 	else
1272254da0d7SOleksij Rempel 		mxs_clr(AUART_LINECTRL_BRK, s, REG_LINECTRL);
1273f19693a1SSascha Hauer }
1274f19693a1SSascha Hauer 
1275069a47e5SJulia Lawall static const struct uart_ops mxs_auart_ops = {
1276f19693a1SSascha Hauer 	.tx_empty       = mxs_auart_tx_empty,
1277f19693a1SSascha Hauer 	.start_tx       = mxs_auart_start_tx,
1278f19693a1SSascha Hauer 	.stop_tx	= mxs_auart_stop_tx,
1279f19693a1SSascha Hauer 	.stop_rx	= mxs_auart_stop_rx,
1280f9e42397SJanusz Uzycki 	.enable_ms      = mxs_auart_enable_ms,
1281f19693a1SSascha Hauer 	.break_ctl      = mxs_auart_break_ctl,
1282f19693a1SSascha Hauer 	.set_mctrl	= mxs_auart_set_mctrl,
1283f19693a1SSascha Hauer 	.get_mctrl      = mxs_auart_get_mctrl,
1284f19693a1SSascha Hauer 	.startup	= mxs_auart_startup,
1285f19693a1SSascha Hauer 	.shutdown       = mxs_auart_shutdown,
1286f19693a1SSascha Hauer 	.set_termios    = mxs_auart_settermios,
128736a26278SJanusz Uzycki 	.set_ldisc      = mxs_auart_set_ldisc,
1288f19693a1SSascha Hauer 	.type	   	= mxs_auart_type,
1289f19693a1SSascha Hauer 	.release_port   = mxs_auart_release_port,
1290f19693a1SSascha Hauer 	.request_port   = mxs_auart_request_port,
1291f19693a1SSascha Hauer 	.config_port    = mxs_auart_config_port,
1292f19693a1SSascha Hauer 	.verify_port    = mxs_auart_verify_port,
1293f19693a1SSascha Hauer };
1294f19693a1SSascha Hauer 
1295f19693a1SSascha Hauer static struct mxs_auart_port *auart_port[MXS_AUART_PORTS];
1296f19693a1SSascha Hauer 
1297f19693a1SSascha Hauer #ifdef CONFIG_SERIAL_MXS_AUART_CONSOLE
mxs_auart_console_putchar(struct uart_port * port,unsigned char ch)12983f8bab17SJiri Slaby static void mxs_auart_console_putchar(struct uart_port *port, unsigned char ch)
1299f19693a1SSascha Hauer {
1300254da0d7SOleksij Rempel 	struct mxs_auart_port *s = to_auart_port(port);
1301f19693a1SSascha Hauer 	unsigned int to = 1000;
1302f19693a1SSascha Hauer 
1303254da0d7SOleksij Rempel 	while (mxs_read(s, REG_STAT) & AUART_STAT_TXFF) {
1304f19693a1SSascha Hauer 		if (!to--)
1305f19693a1SSascha Hauer 			break;
1306f19693a1SSascha Hauer 		udelay(1);
1307f19693a1SSascha Hauer 	}
1308f19693a1SSascha Hauer 
1309254da0d7SOleksij Rempel 	mxs_write(ch, s, REG_DATA);
1310f19693a1SSascha Hauer }
1311f19693a1SSascha Hauer 
1312f19693a1SSascha Hauer static void
auart_console_write(struct console * co,const char * str,unsigned int count)1313f19693a1SSascha Hauer auart_console_write(struct console *co, const char *str, unsigned int count)
1314f19693a1SSascha Hauer {
1315f19693a1SSascha Hauer 	struct mxs_auart_port *s;
1316f19693a1SSascha Hauer 	struct uart_port *port;
1317f19693a1SSascha Hauer 	unsigned int old_ctrl0, old_ctrl2;
1318079a036fSUwe Kleine-König 	unsigned int to = 20000;
1319f19693a1SSascha Hauer 
13204829e765SWolfram Sang 	if (co->index >= MXS_AUART_PORTS || co->index < 0)
1321f19693a1SSascha Hauer 		return;
1322f19693a1SSascha Hauer 
1323f19693a1SSascha Hauer 	s = auart_port[co->index];
1324f19693a1SSascha Hauer 	port = &s->port;
1325f19693a1SSascha Hauer 
1326f19693a1SSascha Hauer 	clk_enable(s->clk);
1327f19693a1SSascha Hauer 
1328f19693a1SSascha Hauer 	/* First save the CR then disable the interrupts */
1329254da0d7SOleksij Rempel 	old_ctrl2 = mxs_read(s, REG_CTRL2);
1330254da0d7SOleksij Rempel 	old_ctrl0 = mxs_read(s, REG_CTRL0);
1331f19693a1SSascha Hauer 
1332254da0d7SOleksij Rempel 	mxs_clr(AUART_CTRL0_CLKGATE, s, REG_CTRL0);
1333254da0d7SOleksij Rempel 	mxs_set(AUART_CTRL2_UARTEN | AUART_CTRL2_TXE, s, REG_CTRL2);
1334f19693a1SSascha Hauer 
1335f19693a1SSascha Hauer 	uart_console_write(port, str, count, mxs_auart_console_putchar);
1336f19693a1SSascha Hauer 
1337079a036fSUwe Kleine-König 	/* Finally, wait for transmitter to become empty ... */
1338254da0d7SOleksij Rempel 	while (mxs_read(s, REG_STAT) & AUART_STAT_BUSY) {
1339079a036fSUwe Kleine-König 		udelay(1);
1340f19693a1SSascha Hauer 		if (!to--)
1341f19693a1SSascha Hauer 			break;
1342f19693a1SSascha Hauer 	}
1343f19693a1SSascha Hauer 
1344079a036fSUwe Kleine-König 	/*
1345079a036fSUwe Kleine-König 	 * ... and restore the TCR if we waited long enough for the transmitter
1346079a036fSUwe Kleine-König 	 * to be idle. This might keep the transmitter enabled although it is
1347079a036fSUwe Kleine-König 	 * unused, but that is better than to disable it while it is still
1348079a036fSUwe Kleine-König 	 * transmitting.
1349079a036fSUwe Kleine-König 	 */
1350254da0d7SOleksij Rempel 	if (!(mxs_read(s, REG_STAT) & AUART_STAT_BUSY)) {
1351254da0d7SOleksij Rempel 		mxs_write(old_ctrl0, s, REG_CTRL0);
1352254da0d7SOleksij Rempel 		mxs_write(old_ctrl2, s, REG_CTRL2);
1353079a036fSUwe Kleine-König 	}
1354f19693a1SSascha Hauer 
1355f19693a1SSascha Hauer 	clk_disable(s->clk);
1356f19693a1SSascha Hauer }
1357f19693a1SSascha Hauer 
1358f19693a1SSascha Hauer static void __init
auart_console_get_options(struct mxs_auart_port * s,int * baud,int * parity,int * bits)1359254da0d7SOleksij Rempel auart_console_get_options(struct mxs_auart_port *s, int *baud,
1360f19693a1SSascha Hauer 			  int *parity, int *bits)
1361f19693a1SSascha Hauer {
1362254da0d7SOleksij Rempel 	struct uart_port *port = &s->port;
1363f19693a1SSascha Hauer 	unsigned int lcr_h, quot;
1364f19693a1SSascha Hauer 
1365254da0d7SOleksij Rempel 	if (!(mxs_read(s, REG_CTRL2) & AUART_CTRL2_UARTEN))
1366f19693a1SSascha Hauer 		return;
1367f19693a1SSascha Hauer 
1368254da0d7SOleksij Rempel 	lcr_h = mxs_read(s, REG_LINECTRL);
1369f19693a1SSascha Hauer 
1370f19693a1SSascha Hauer 	*parity = 'n';
1371f19693a1SSascha Hauer 	if (lcr_h & AUART_LINECTRL_PEN) {
1372f19693a1SSascha Hauer 		if (lcr_h & AUART_LINECTRL_EPS)
1373f19693a1SSascha Hauer 			*parity = 'e';
1374f19693a1SSascha Hauer 		else
1375f19693a1SSascha Hauer 			*parity = 'o';
1376f19693a1SSascha Hauer 	}
1377f19693a1SSascha Hauer 
13788ea43accSJiri Slaby 	if ((lcr_h & AUART_LINECTRL_WLEN_MASK) == AUART_LINECTRL_WLEN(7))
1379f19693a1SSascha Hauer 		*bits = 7;
1380f19693a1SSascha Hauer 	else
1381f19693a1SSascha Hauer 		*bits = 8;
1382f19693a1SSascha Hauer 
1383254da0d7SOleksij Rempel 	quot = ((mxs_read(s, REG_LINECTRL) & AUART_LINECTRL_BAUD_DIVINT_MASK))
1384f19693a1SSascha Hauer 		>> (AUART_LINECTRL_BAUD_DIVINT_SHIFT - 6);
1385254da0d7SOleksij Rempel 	quot |= ((mxs_read(s, REG_LINECTRL) & AUART_LINECTRL_BAUD_DIVFRAC_MASK))
1386f19693a1SSascha Hauer 		>> AUART_LINECTRL_BAUD_DIVFRAC_SHIFT;
1387f19693a1SSascha Hauer 	if (quot == 0)
1388f19693a1SSascha Hauer 		quot = 1;
1389f19693a1SSascha Hauer 
1390f19693a1SSascha Hauer 	*baud = (port->uartclk << 2) / quot;
1391f19693a1SSascha Hauer }
1392f19693a1SSascha Hauer 
1393f19693a1SSascha Hauer static int __init
auart_console_setup(struct console * co,char * options)1394f19693a1SSascha Hauer auart_console_setup(struct console *co, char *options)
1395f19693a1SSascha Hauer {
1396f19693a1SSascha Hauer 	struct mxs_auart_port *s;
1397f19693a1SSascha Hauer 	int baud = 9600;
1398f19693a1SSascha Hauer 	int bits = 8;
1399f19693a1SSascha Hauer 	int parity = 'n';
1400f19693a1SSascha Hauer 	int flow = 'n';
1401f19693a1SSascha Hauer 	int ret;
1402f19693a1SSascha Hauer 
1403f19693a1SSascha Hauer 	/*
1404f19693a1SSascha Hauer 	 * Check whether an invalid uart number has been specified, and
1405f19693a1SSascha Hauer 	 * if so, search for the first available port that does have
1406f19693a1SSascha Hauer 	 * console support.
1407f19693a1SSascha Hauer 	 */
1408f19693a1SSascha Hauer 	if (co->index == -1 || co->index >= ARRAY_SIZE(auart_port))
1409f19693a1SSascha Hauer 		co->index = 0;
1410f19693a1SSascha Hauer 	s = auart_port[co->index];
1411f19693a1SSascha Hauer 	if (!s)
1412f19693a1SSascha Hauer 		return -ENODEV;
1413f19693a1SSascha Hauer 
14149bbc3dcaSFabio Estevam 	ret = clk_prepare_enable(s->clk);
14159bbc3dcaSFabio Estevam 	if (ret)
14169bbc3dcaSFabio Estevam 		return ret;
1417f19693a1SSascha Hauer 
1418f19693a1SSascha Hauer 	if (options)
1419f19693a1SSascha Hauer 		uart_parse_options(options, &baud, &parity, &bits, &flow);
1420f19693a1SSascha Hauer 	else
1421254da0d7SOleksij Rempel 		auart_console_get_options(s, &baud, &parity, &bits);
1422f19693a1SSascha Hauer 
1423f19693a1SSascha Hauer 	ret = uart_set_options(&s->port, co, baud, parity, bits, flow);
1424f19693a1SSascha Hauer 
1425a4813770SShawn Guo 	clk_disable_unprepare(s->clk);
1426f19693a1SSascha Hauer 
1427f19693a1SSascha Hauer 	return ret;
1428f19693a1SSascha Hauer }
1429f19693a1SSascha Hauer 
1430f19693a1SSascha Hauer static struct console auart_console = {
1431f19693a1SSascha Hauer 	.name		= "ttyAPP",
1432f19693a1SSascha Hauer 	.write		= auart_console_write,
1433f19693a1SSascha Hauer 	.device		= uart_console_device,
1434f19693a1SSascha Hauer 	.setup		= auart_console_setup,
1435f19693a1SSascha Hauer 	.flags		= CON_PRINTBUFFER,
1436f19693a1SSascha Hauer 	.index		= -1,
1437f19693a1SSascha Hauer 	.data		= &auart_driver,
1438f19693a1SSascha Hauer };
1439f19693a1SSascha Hauer #endif
1440f19693a1SSascha Hauer 
1441f19693a1SSascha Hauer static struct uart_driver auart_driver = {
1442f19693a1SSascha Hauer 	.owner		= THIS_MODULE,
1443f19693a1SSascha Hauer 	.driver_name	= "ttyAPP",
1444f19693a1SSascha Hauer 	.dev_name	= "ttyAPP",
1445f19693a1SSascha Hauer 	.major		= 0,
1446f19693a1SSascha Hauer 	.minor		= 0,
1447f19693a1SSascha Hauer 	.nr		= MXS_AUART_PORTS,
1448f19693a1SSascha Hauer #ifdef CONFIG_SERIAL_MXS_AUART_CONSOLE
1449f19693a1SSascha Hauer 	.cons =		&auart_console,
1450f19693a1SSascha Hauer #endif
1451f19693a1SSascha Hauer };
1452f19693a1SSascha Hauer 
mxs_init_regs(struct mxs_auart_port * s)1453254da0d7SOleksij Rempel static void mxs_init_regs(struct mxs_auart_port *s)
1454254da0d7SOleksij Rempel {
1455254da0d7SOleksij Rempel 	if (is_asm9260_auart(s))
1456254da0d7SOleksij Rempel 		s->vendor = &vendor_alphascale_asm9260;
1457254da0d7SOleksij Rempel 	else
1458254da0d7SOleksij Rempel 		s->vendor = &vendor_freescale_stmp37xx;
1459254da0d7SOleksij Rempel }
1460254da0d7SOleksij Rempel 
mxs_get_clks(struct mxs_auart_port * s,struct platform_device * pdev)1461254da0d7SOleksij Rempel static int mxs_get_clks(struct mxs_auart_port *s,
1462254da0d7SOleksij Rempel 			struct platform_device *pdev)
1463254da0d7SOleksij Rempel {
1464254da0d7SOleksij Rempel 	int err;
1465254da0d7SOleksij Rempel 
1466254da0d7SOleksij Rempel 	if (!is_asm9260_auart(s)) {
1467254da0d7SOleksij Rempel 		s->clk = devm_clk_get(&pdev->dev, NULL);
14680d2665b6SWei Yongjun 		return PTR_ERR_OR_ZERO(s->clk);
1469254da0d7SOleksij Rempel 	}
1470254da0d7SOleksij Rempel 
1471254da0d7SOleksij Rempel 	s->clk = devm_clk_get(s->dev, "mod");
1472254da0d7SOleksij Rempel 	if (IS_ERR(s->clk)) {
1473254da0d7SOleksij Rempel 		dev_err(s->dev, "Failed to get \"mod\" clk\n");
1474254da0d7SOleksij Rempel 		return PTR_ERR(s->clk);
1475254da0d7SOleksij Rempel 	}
1476254da0d7SOleksij Rempel 
1477254da0d7SOleksij Rempel 	s->clk_ahb = devm_clk_get(s->dev, "ahb");
1478254da0d7SOleksij Rempel 	if (IS_ERR(s->clk_ahb)) {
1479254da0d7SOleksij Rempel 		dev_err(s->dev, "Failed to get \"ahb\" clk\n");
1480254da0d7SOleksij Rempel 		return PTR_ERR(s->clk_ahb);
1481254da0d7SOleksij Rempel 	}
1482254da0d7SOleksij Rempel 
1483254da0d7SOleksij Rempel 	err = clk_prepare_enable(s->clk_ahb);
1484254da0d7SOleksij Rempel 	if (err) {
1485254da0d7SOleksij Rempel 		dev_err(s->dev, "Failed to enable ahb_clk!\n");
1486254da0d7SOleksij Rempel 		return err;
1487254da0d7SOleksij Rempel 	}
1488254da0d7SOleksij Rempel 
1489254da0d7SOleksij Rempel 	err = clk_set_rate(s->clk, clk_get_rate(s->clk_ahb));
1490254da0d7SOleksij Rempel 	if (err) {
1491254da0d7SOleksij Rempel 		dev_err(s->dev, "Failed to set rate!\n");
14921664bc40SWei Yongjun 		goto disable_clk_ahb;
1493254da0d7SOleksij Rempel 	}
1494254da0d7SOleksij Rempel 
1495254da0d7SOleksij Rempel 	err = clk_prepare_enable(s->clk);
1496254da0d7SOleksij Rempel 	if (err) {
1497254da0d7SOleksij Rempel 		dev_err(s->dev, "Failed to enable clk!\n");
14985d7519dfSFabio Estevam 		goto disable_clk_ahb;
1499254da0d7SOleksij Rempel 	}
1500254da0d7SOleksij Rempel 
1501254da0d7SOleksij Rempel 	return 0;
15025d7519dfSFabio Estevam 
15035d7519dfSFabio Estevam disable_clk_ahb:
15045d7519dfSFabio Estevam 	clk_disable_unprepare(s->clk_ahb);
15055d7519dfSFabio Estevam 	return err;
1506254da0d7SOleksij Rempel }
1507254da0d7SOleksij Rempel 
mxs_auart_init_gpios(struct mxs_auart_port * s,struct device * dev)1508343fda95SUwe Kleine-König static int mxs_auart_init_gpios(struct mxs_auart_port *s, struct device *dev)
15097c573d7eSJanusz Uzycki {
1510f9e42397SJanusz Uzycki 	enum mctrl_gpio_idx i;
1511f9e42397SJanusz Uzycki 	struct gpio_desc *gpiod;
1512f9e42397SJanusz Uzycki 
15137d8c70d8SUwe Kleine-König 	s->gpios = mctrl_gpio_init_noauto(dev, 0);
1514343fda95SUwe Kleine-König 	if (IS_ERR(s->gpios))
1515343fda95SUwe Kleine-König 		return PTR_ERR(s->gpios);
15167c573d7eSJanusz Uzycki 
15177c573d7eSJanusz Uzycki 	/* Block (enabled before) DMA option if RTS or CTS is GPIO line */
15187c573d7eSJanusz Uzycki 	if (!RTS_AT_AUART() || !CTS_AT_AUART()) {
15197c573d7eSJanusz Uzycki 		if (test_bit(MXS_AUART_RTSCTS, &s->flags))
15207c573d7eSJanusz Uzycki 			dev_warn(dev,
15217c573d7eSJanusz Uzycki 				 "DMA and flow control via gpio may cause some problems. DMA disabled!\n");
15227c573d7eSJanusz Uzycki 		clear_bit(MXS_AUART_RTSCTS, &s->flags);
15237c573d7eSJanusz Uzycki 	}
15247c573d7eSJanusz Uzycki 
1525f9e42397SJanusz Uzycki 	for (i = 0; i < UART_GPIO_MAX; i++) {
1526f9e42397SJanusz Uzycki 		gpiod = mctrl_gpio_to_gpiod(s->gpios, i);
1527f8bdfe9dSWolfram Sang 		if (gpiod && (gpiod_get_direction(gpiod) == 1))
1528f9e42397SJanusz Uzycki 			s->gpio_irq[i] = gpiod_to_irq(gpiod);
1529f9e42397SJanusz Uzycki 		else
1530f9e42397SJanusz Uzycki 			s->gpio_irq[i] = -EINVAL;
1531f9e42397SJanusz Uzycki 	}
1532f9e42397SJanusz Uzycki 
1533343fda95SUwe Kleine-König 	return 0;
15347c573d7eSJanusz Uzycki }
15357c573d7eSJanusz Uzycki 
mxs_auart_free_gpio_irq(struct mxs_auart_port * s)1536f9e42397SJanusz Uzycki static void mxs_auart_free_gpio_irq(struct mxs_auart_port *s)
1537f9e42397SJanusz Uzycki {
1538f9e42397SJanusz Uzycki 	enum mctrl_gpio_idx i;
1539f9e42397SJanusz Uzycki 
1540f9e42397SJanusz Uzycki 	for (i = 0; i < UART_GPIO_MAX; i++)
1541f9e42397SJanusz Uzycki 		if (s->gpio_irq[i] >= 0)
1542f9e42397SJanusz Uzycki 			free_irq(s->gpio_irq[i], s);
1543f9e42397SJanusz Uzycki }
1544f9e42397SJanusz Uzycki 
mxs_auart_request_gpio_irq(struct mxs_auart_port * s)1545f9e42397SJanusz Uzycki static int mxs_auart_request_gpio_irq(struct mxs_auart_port *s)
1546f9e42397SJanusz Uzycki {
1547f9e42397SJanusz Uzycki 	int *irq = s->gpio_irq;
1548f9e42397SJanusz Uzycki 	enum mctrl_gpio_idx i;
1549f9e42397SJanusz Uzycki 	int err = 0;
1550f9e42397SJanusz Uzycki 
1551f9e42397SJanusz Uzycki 	for (i = 0; (i < UART_GPIO_MAX) && !err; i++) {
1552f9e42397SJanusz Uzycki 		if (irq[i] < 0)
1553f9e42397SJanusz Uzycki 			continue;
1554f9e42397SJanusz Uzycki 
1555f9e42397SJanusz Uzycki 		irq_set_status_flags(irq[i], IRQ_NOAUTOEN);
1556f9e42397SJanusz Uzycki 		err = request_irq(irq[i], mxs_auart_irq_handle,
1557f9e42397SJanusz Uzycki 				IRQ_TYPE_EDGE_BOTH, dev_name(s->dev), s);
1558f9e42397SJanusz Uzycki 		if (err)
1559f9e42397SJanusz Uzycki 			dev_err(s->dev, "%s - Can't get %d irq\n",
1560f9e42397SJanusz Uzycki 				__func__, irq[i]);
1561f9e42397SJanusz Uzycki 	}
1562f9e42397SJanusz Uzycki 
1563f9e42397SJanusz Uzycki 	/*
1564f9e42397SJanusz Uzycki 	 * If something went wrong, rollback.
15655963e8a3SAnton Vasilyev 	 * Be careful: i may be unsigned.
1566f9e42397SJanusz Uzycki 	 */
15675963e8a3SAnton Vasilyev 	while (err && (i-- > 0))
1568f9e42397SJanusz Uzycki 		if (irq[i] >= 0)
1569f9e42397SJanusz Uzycki 			free_irq(irq[i], s);
1570f9e42397SJanusz Uzycki 
1571f9e42397SJanusz Uzycki 	return err;
1572f9e42397SJanusz Uzycki }
1573f9e42397SJanusz Uzycki 
mxs_auart_probe(struct platform_device * pdev)15749671f099SBill Pemberton static int mxs_auart_probe(struct platform_device *pdev)
1575f19693a1SSascha Hauer {
157608fdc699SFabio Estevam 	struct device_node *np = pdev->dev.of_node;
1577f19693a1SSascha Hauer 	struct mxs_auart_port *s;
1578f19693a1SSascha Hauer 	u32 version;
15795f9ba5b6SFabio Estevam 	int ret, irq;
1580f19693a1SSascha Hauer 	struct resource *r;
1581f19693a1SSascha Hauer 
158246778bcaSFabio Estevam 	s = devm_kzalloc(&pdev->dev, sizeof(*s), GFP_KERNEL);
158311387b0aSFabio Estevam 	if (!s)
158411387b0aSFabio Estevam 		return -ENOMEM;
1585f19693a1SSascha Hauer 
1586254da0d7SOleksij Rempel 	s->port.dev = &pdev->dev;
1587254da0d7SOleksij Rempel 	s->dev = &pdev->dev;
1588254da0d7SOleksij Rempel 
158908fdc699SFabio Estevam 	ret = of_alias_get_id(np, "serial");
159008fdc699SFabio Estevam 	if (ret < 0) {
159108fdc699SFabio Estevam 		dev_err(&pdev->dev, "failed to get alias id: %d\n", ret);
159246778bcaSFabio Estevam 		return ret;
159308fdc699SFabio Estevam 	}
159408fdc699SFabio Estevam 	s->port.line = ret;
159508fdc699SFabio Estevam 
1596822a729aSRob Herring 	if (of_property_read_bool(np, "uart-has-rtscts") ||
1597822a729aSRob Herring 	    of_property_read_bool(np, "fsl,uart-has-rtscts") /* deprecated */)
159808fdc699SFabio Estevam 		set_bit(MXS_AUART_RTSCTS, &s->flags);
159908fdc699SFabio Estevam 
1600dd345a31SGeert Uytterhoeven 	if (s->port.line >= ARRAY_SIZE(auart_port)) {
1601dd345a31SGeert Uytterhoeven 		dev_err(&pdev->dev, "serial%d out of range\n", s->port.line);
1602dd345a31SGeert Uytterhoeven 		return -EINVAL;
1603dd345a31SGeert Uytterhoeven 	}
16041ea6607dSFabio Estevam 
16055f1697feSFabio Estevam 	s->devtype = (enum mxs_auart_type)of_device_get_match_data(&pdev->dev);
1606f4b1f03bSHuang Shijie 
1607254da0d7SOleksij Rempel 	ret = mxs_get_clks(s, pdev);
1608254da0d7SOleksij Rempel 	if (ret)
1609254da0d7SOleksij Rempel 		return ret;
1610f19693a1SSascha Hauer 
1611f19693a1SSascha Hauer 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1612ca7c22fcSAlexey Khoroshilov 	if (!r) {
1613ca7c22fcSAlexey Khoroshilov 		ret = -ENXIO;
1614ca7c22fcSAlexey Khoroshilov 		goto out_disable_clks;
1615ca7c22fcSAlexey Khoroshilov 	}
161675beb268SFabio Estevam 
1617f19693a1SSascha Hauer 	s->port.mapbase = r->start;
1618f19693a1SSascha Hauer 	s->port.membase = ioremap(r->start, resource_size(r));
161967343306SKangjie Lu 	if (!s->port.membase) {
162067343306SKangjie Lu 		ret = -ENOMEM;
162167343306SKangjie Lu 		goto out_disable_clks;
162267343306SKangjie Lu 	}
1623f19693a1SSascha Hauer 	s->port.ops = &mxs_auart_ops;
1624f19693a1SSascha Hauer 	s->port.iotype = UPIO_MEM;
16259987f76aSHector Palacios 	s->port.fifosize = MXS_AUART_FIFO_SIZE;
1626f19693a1SSascha Hauer 	s->port.uartclk = clk_get_rate(s->clk);
1627f19693a1SSascha Hauer 	s->port.type = PORT_IMX;
16282deed958SDmitry Safonov 	s->port.has_sysrq = IS_ENABLED(CONFIG_SERIAL_MXS_AUART_CONSOLE);
1629254da0d7SOleksij Rempel 
1630254da0d7SOleksij Rempel 	mxs_init_regs(s);
1631f19693a1SSascha Hauer 
1632f9e42397SJanusz Uzycki 	s->mctrl_prev = 0;
1633f9e42397SJanusz Uzycki 
16346960cd46SFabio Estevam 	irq = platform_get_irq(pdev, 0);
1635ca7c22fcSAlexey Khoroshilov 	if (irq < 0) {
1636ca7c22fcSAlexey Khoroshilov 		ret = irq;
1637d8edf8ebSChuhong Yuan 		goto out_iounmap;
1638ca7c22fcSAlexey Khoroshilov 	}
163999c932c2SFabio Estevam 
16406960cd46SFabio Estevam 	s->port.irq = irq;
16416960cd46SFabio Estevam 	ret = devm_request_irq(&pdev->dev, irq, mxs_auart_irq_handle, 0,
16429e5df9f8SFabio Estevam 			       dev_name(&pdev->dev), s);
1643f19693a1SSascha Hauer 	if (ret)
1644d8edf8ebSChuhong Yuan 		goto out_iounmap;
1645f19693a1SSascha Hauer 
1646f19693a1SSascha Hauer 	platform_set_drvdata(pdev, s);
1647f19693a1SSascha Hauer 
1648343fda95SUwe Kleine-König 	ret = mxs_auart_init_gpios(s, &pdev->dev);
1649343fda95SUwe Kleine-König 	if (ret) {
1650343fda95SUwe Kleine-König 		dev_err(&pdev->dev, "Failed to initialize GPIOs.\n");
1651d8edf8ebSChuhong Yuan 		goto out_iounmap;
1652343fda95SUwe Kleine-König 	}
16537c573d7eSJanusz Uzycki 
1654f9e42397SJanusz Uzycki 	/*
1655f9e42397SJanusz Uzycki 	 * Get the GPIO lines IRQ
1656f9e42397SJanusz Uzycki 	 */
1657f9e42397SJanusz Uzycki 	ret = mxs_auart_request_gpio_irq(s);
1658f9e42397SJanusz Uzycki 	if (ret)
1659d8edf8ebSChuhong Yuan 		goto out_iounmap;
1660f9e42397SJanusz Uzycki 
16611ea6607dSFabio Estevam 	auart_port[s->port.line] = s;
1662f19693a1SSascha Hauer 
1663254da0d7SOleksij Rempel 	mxs_auart_reset_deassert(s);
1664f19693a1SSascha Hauer 
1665f19693a1SSascha Hauer 	ret = uart_add_one_port(&auart_driver, &s->port);
1666f19693a1SSascha Hauer 	if (ret)
1667ca7c22fcSAlexey Khoroshilov 		goto out_free_qpio_irq;
1668f19693a1SSascha Hauer 
1669254da0d7SOleksij Rempel 	/* ASM9260 don't have version reg */
1670254da0d7SOleksij Rempel 	if (is_asm9260_auart(s)) {
1671254da0d7SOleksij Rempel 		dev_info(&pdev->dev, "Found APPUART ASM9260\n");
1672254da0d7SOleksij Rempel 	} else {
1673254da0d7SOleksij Rempel 		version = mxs_read(s, REG_VERSION);
1674f19693a1SSascha Hauer 		dev_info(&pdev->dev, "Found APPUART %d.%d.%d\n",
1675f19693a1SSascha Hauer 			 (version >> 24) & 0xff,
1676f19693a1SSascha Hauer 			 (version >> 16) & 0xff, version & 0xffff);
1677254da0d7SOleksij Rempel 	}
1678f19693a1SSascha Hauer 
1679f19693a1SSascha Hauer 	return 0;
1680f19693a1SSascha Hauer 
1681ca7c22fcSAlexey Khoroshilov out_free_qpio_irq:
1682f9e42397SJanusz Uzycki 	mxs_auart_free_gpio_irq(s);
1683f19693a1SSascha Hauer 	auart_port[pdev->id] = NULL;
1684ca7c22fcSAlexey Khoroshilov 
1685d8edf8ebSChuhong Yuan out_iounmap:
1686d8edf8ebSChuhong Yuan 	iounmap(s->port.membase);
1687d8edf8ebSChuhong Yuan 
1688ca7c22fcSAlexey Khoroshilov out_disable_clks:
1689ca7c22fcSAlexey Khoroshilov 	if (is_asm9260_auart(s)) {
1690ca7c22fcSAlexey Khoroshilov 		clk_disable_unprepare(s->clk);
1691ca7c22fcSAlexey Khoroshilov 		clk_disable_unprepare(s->clk_ahb);
1692ca7c22fcSAlexey Khoroshilov 	}
1693f19693a1SSascha Hauer 	return ret;
1694f19693a1SSascha Hauer }
1695f19693a1SSascha Hauer 
mxs_auart_remove(struct platform_device * pdev)1696ae8d8a14SBill Pemberton static int mxs_auart_remove(struct platform_device *pdev)
1697f19693a1SSascha Hauer {
1698f19693a1SSascha Hauer 	struct mxs_auart_port *s = platform_get_drvdata(pdev);
1699f19693a1SSascha Hauer 
1700f19693a1SSascha Hauer 	uart_remove_one_port(&auart_driver, &s->port);
1701f19693a1SSascha Hauer 	auart_port[pdev->id] = NULL;
1702f9e42397SJanusz Uzycki 	mxs_auart_free_gpio_irq(s);
1703d8edf8ebSChuhong Yuan 	iounmap(s->port.membase);
1704ca7c22fcSAlexey Khoroshilov 	if (is_asm9260_auart(s)) {
1705ca7c22fcSAlexey Khoroshilov 		clk_disable_unprepare(s->clk);
1706ca7c22fcSAlexey Khoroshilov 		clk_disable_unprepare(s->clk_ahb);
1707ca7c22fcSAlexey Khoroshilov 	}
1708f19693a1SSascha Hauer 
1709f19693a1SSascha Hauer 	return 0;
1710f19693a1SSascha Hauer }
1711f19693a1SSascha Hauer 
1712f19693a1SSascha Hauer static struct platform_driver mxs_auart_driver = {
1713f19693a1SSascha Hauer 	.probe = mxs_auart_probe,
17142d47b716SBill Pemberton 	.remove = mxs_auart_remove,
1715f19693a1SSascha Hauer 	.driver = {
1716f19693a1SSascha Hauer 		.name = "mxs-auart",
17171ea6607dSFabio Estevam 		.of_match_table = mxs_auart_dt_ids,
1718f19693a1SSascha Hauer 	},
1719f19693a1SSascha Hauer };
1720f19693a1SSascha Hauer 
mxs_auart_init(void)1721f19693a1SSascha Hauer static int __init mxs_auart_init(void)
1722f19693a1SSascha Hauer {
1723f19693a1SSascha Hauer 	int r;
1724f19693a1SSascha Hauer 
1725f19693a1SSascha Hauer 	r = uart_register_driver(&auart_driver);
1726f19693a1SSascha Hauer 	if (r)
1727f19693a1SSascha Hauer 		goto out;
1728f19693a1SSascha Hauer 
1729f19693a1SSascha Hauer 	r = platform_driver_register(&mxs_auart_driver);
1730f19693a1SSascha Hauer 	if (r)
1731f19693a1SSascha Hauer 		goto out_err;
1732f19693a1SSascha Hauer 
1733f19693a1SSascha Hauer 	return 0;
1734f19693a1SSascha Hauer out_err:
1735f19693a1SSascha Hauer 	uart_unregister_driver(&auart_driver);
1736f19693a1SSascha Hauer out:
1737f19693a1SSascha Hauer 	return r;
1738f19693a1SSascha Hauer }
1739f19693a1SSascha Hauer 
mxs_auart_exit(void)1740f19693a1SSascha Hauer static void __exit mxs_auart_exit(void)
1741f19693a1SSascha Hauer {
1742f19693a1SSascha Hauer 	platform_driver_unregister(&mxs_auart_driver);
1743f19693a1SSascha Hauer 	uart_unregister_driver(&auart_driver);
1744f19693a1SSascha Hauer }
1745f19693a1SSascha Hauer 
1746f19693a1SSascha Hauer module_init(mxs_auart_init);
1747f19693a1SSascha Hauer module_exit(mxs_auart_exit);
1748f19693a1SSascha Hauer MODULE_LICENSE("GPL");
1749f19693a1SSascha Hauer MODULE_DESCRIPTION("Freescale MXS application uart driver");
17501ea6607dSFabio Estevam MODULE_ALIAS("platform:mxs-auart");
1751