xref: /linux-6.15/drivers/tty/serial/sh-sci.c (revision 8fa7292f)
1e3b3d0f5SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2ab4382d2SGreg Kroah-Hartman /*
3ab4382d2SGreg Kroah-Hartman  * SuperH on-chip serial module support.  (SCI with no FIFO / with FIFO)
4ab4382d2SGreg Kroah-Hartman  *
5fc887b15SLinus Torvalds  *  Copyright (C) 2002 - 2011  Paul Mundt
6f4998e55SGeert Uytterhoeven  *  Copyright (C) 2015 Glider bvba
7ab4382d2SGreg Kroah-Hartman  *  Modified to support SH7720 SCIF. Markus Brunner, Mark Jonas (Jul 2007).
8ab4382d2SGreg Kroah-Hartman  *
9ab4382d2SGreg Kroah-Hartman  * based off of the old drivers/char/sh-sci.c by:
10ab4382d2SGreg Kroah-Hartman  *
11ab4382d2SGreg Kroah-Hartman  *   Copyright (C) 1999, 2000  Niibe Yutaka
12ab4382d2SGreg Kroah-Hartman  *   Copyright (C) 2000  Sugioka Toshinobu
13ab4382d2SGreg Kroah-Hartman  *   Modified to support multiple serial ports. Stuart Menefy (May 2000).
14ab4382d2SGreg Kroah-Hartman  *   Modified to support SecureEdge. David McCullough (2002)
15ab4382d2SGreg Kroah-Hartman  *   Modified to support SH7300 SCIF. Takashi Kusuda (Jun 2003).
16ab4382d2SGreg Kroah-Hartman  *   Removed SH7300 support (Jul 2007).
17ab4382d2SGreg Kroah-Hartman  */
18ab4382d2SGreg Kroah-Hartman #undef DEBUG
19ab4382d2SGreg Kroah-Hartman 
20ab4382d2SGreg Kroah-Hartman #include <linux/clk.h>
218fb9631cSLaurent Pinchart #include <linux/console.h>
22ab4382d2SGreg Kroah-Hartman #include <linux/ctype.h>
238fb9631cSLaurent Pinchart #include <linux/cpufreq.h>
248fb9631cSLaurent Pinchart #include <linux/delay.h>
25ab4382d2SGreg Kroah-Hartman #include <linux/dmaengine.h>
265beabc7fSMagnus Damm #include <linux/dma-mapping.h>
278fb9631cSLaurent Pinchart #include <linux/err.h>
288fb9631cSLaurent Pinchart #include <linux/errno.h>
298fb9631cSLaurent Pinchart #include <linux/init.h>
308fb9631cSLaurent Pinchart #include <linux/interrupt.h>
318fb9631cSLaurent Pinchart #include <linux/ioport.h>
32b96408b4SUlrich Hecht #include <linux/ktime.h>
338fb9631cSLaurent Pinchart #include <linux/major.h>
34b43a1864SBiju Das #include <linux/minmax.h>
358fb9631cSLaurent Pinchart #include <linux/module.h>
368fb9631cSLaurent Pinchart #include <linux/mm.h>
3720bdcab8SBastian Hecht #include <linux/of.h>
388fb9631cSLaurent Pinchart #include <linux/platform_device.h>
398fb9631cSLaurent Pinchart #include <linux/pm_runtime.h>
40862f7218SLad Prabhakar #include <linux/reset.h>
418fb9631cSLaurent Pinchart #include <linux/scatterlist.h>
428fb9631cSLaurent Pinchart #include <linux/serial.h>
438fb9631cSLaurent Pinchart #include <linux/serial_sci.h>
448fb9631cSLaurent Pinchart #include <linux/sh_dma.h>
458fb9631cSLaurent Pinchart #include <linux/slab.h>
468fb9631cSLaurent Pinchart #include <linux/string.h>
478fb9631cSLaurent Pinchart #include <linux/sysrq.h>
488fb9631cSLaurent Pinchart #include <linux/timer.h>
498fb9631cSLaurent Pinchart #include <linux/tty.h>
508fb9631cSLaurent Pinchart #include <linux/tty_flip.h>
51ab4382d2SGreg Kroah-Hartman 
52ab4382d2SGreg Kroah-Hartman #ifdef CONFIG_SUPERH
53ab4382d2SGreg Kroah-Hartman #include <asm/sh_bios.h>
54507fd01dSBartosz Golaszewski #include <asm/platform_early.h>
55ab4382d2SGreg Kroah-Hartman #endif
56ab4382d2SGreg Kroah-Hartman 
57f907c9eaSGeert Uytterhoeven #include "serial_mctrl_gpio.h"
58ab4382d2SGreg Kroah-Hartman #include "sh-sci.h"
59ab4382d2SGreg Kroah-Hartman 
6089b5c1abSLaurent Pinchart /* Offsets into the sci_port->irqs array */
6189b5c1abSLaurent Pinchart enum {
6289b5c1abSLaurent Pinchart 	SCIx_ERI_IRQ,
6389b5c1abSLaurent Pinchart 	SCIx_RXI_IRQ,
6489b5c1abSLaurent Pinchart 	SCIx_TXI_IRQ,
6589b5c1abSLaurent Pinchart 	SCIx_BRI_IRQ,
66628c534aSChris Brandt 	SCIx_DRI_IRQ,
67628c534aSChris Brandt 	SCIx_TEI_IRQ,
6889b5c1abSLaurent Pinchart 	SCIx_NR_IRQS,
6989b5c1abSLaurent Pinchart 
7089b5c1abSLaurent Pinchart 	SCIx_MUX_IRQ = SCIx_NR_IRQS,	/* special case */
7189b5c1abSLaurent Pinchart };
7289b5c1abSLaurent Pinchart 
7389b5c1abSLaurent Pinchart #define SCIx_IRQ_IS_MUXED(port)			\
7489b5c1abSLaurent Pinchart 	((port)->irqs[SCIx_ERI_IRQ] ==	\
7589b5c1abSLaurent Pinchart 	 (port)->irqs[SCIx_RXI_IRQ]) ||	\
7689b5c1abSLaurent Pinchart 	((port)->irqs[SCIx_ERI_IRQ] &&	\
7789b5c1abSLaurent Pinchart 	 ((port)->irqs[SCIx_RXI_IRQ] < 0))
7889b5c1abSLaurent Pinchart 
79f4998e55SGeert Uytterhoeven enum SCI_CLKS {
80f4998e55SGeert Uytterhoeven 	SCI_FCK,		/* Functional Clock */
816af27bf2SGeert Uytterhoeven 	SCI_SCK,		/* Optional External Clock */
821270f865SGeert Uytterhoeven 	SCI_BRG_INT,		/* Optional BRG Internal Clock Source */
831270f865SGeert Uytterhoeven 	SCI_SCIF_CLK,		/* Optional BRG External Clock Source */
84f4998e55SGeert Uytterhoeven 	SCI_NUM_CLKS
85f4998e55SGeert Uytterhoeven };
86f4998e55SGeert Uytterhoeven 
8769eee8e9SGeert Uytterhoeven /* Bit x set means sampling rate x + 1 is supported */
8869eee8e9SGeert Uytterhoeven #define SCI_SR(x)		BIT((x) - 1)
8969eee8e9SGeert Uytterhoeven #define SCI_SR_RANGE(x, y)	GENMASK((y) - 1, (x) - 1)
9069eee8e9SGeert Uytterhoeven 
9192a05748SGeert Uytterhoeven #define SCI_SR_SCIFAB		SCI_SR(5) | SCI_SR(7) | SCI_SR(11) | \
9292a05748SGeert Uytterhoeven 				SCI_SR(13) | SCI_SR(16) | SCI_SR(17) | \
9392a05748SGeert Uytterhoeven 				SCI_SR(19) | SCI_SR(27)
9492a05748SGeert Uytterhoeven 
9569eee8e9SGeert Uytterhoeven #define min_sr(_port)		ffs((_port)->sampling_rate_mask)
9669eee8e9SGeert Uytterhoeven #define max_sr(_port)		fls((_port)->sampling_rate_mask)
9769eee8e9SGeert Uytterhoeven 
9869eee8e9SGeert Uytterhoeven /* Iterate over all supported sampling rates, from high to low */
9969eee8e9SGeert Uytterhoeven #define for_each_sr(_sr, _port)						\
10069eee8e9SGeert Uytterhoeven 	for ((_sr) = max_sr(_port); (_sr) >= min_sr(_port); (_sr)--)	\
10169eee8e9SGeert Uytterhoeven 		if ((_port)->sampling_rate_mask & SCI_SR((_sr)))
10269eee8e9SGeert Uytterhoeven 
103e095ee6bSLaurent Pinchart struct plat_sci_reg {
104e095ee6bSLaurent Pinchart 	u8 offset, size;
105e095ee6bSLaurent Pinchart };
106e095ee6bSLaurent Pinchart 
10722a6984cSClaudiu Beznea struct sci_suspend_regs {
10881100b9aSGeert Uytterhoeven 	u16 scdl;
10981100b9aSGeert Uytterhoeven 	u16 sccks;
11022a6984cSClaudiu Beznea 	u16 scsmr;
11122a6984cSClaudiu Beznea 	u16 scscr;
11222a6984cSClaudiu Beznea 	u16 scfcr;
11322a6984cSClaudiu Beznea 	u16 scsptr;
11481100b9aSGeert Uytterhoeven 	u16 hssrr;
11581100b9aSGeert Uytterhoeven 	u16 scpcr;
11681100b9aSGeert Uytterhoeven 	u16 scpdr;
11722a6984cSClaudiu Beznea 	u8 scbrr;
11822a6984cSClaudiu Beznea 	u8 semr;
11922a6984cSClaudiu Beznea };
12022a6984cSClaudiu Beznea 
121e095ee6bSLaurent Pinchart struct sci_port_params {
122e095ee6bSLaurent Pinchart 	const struct plat_sci_reg regs[SCIx_NR_REGS];
123b2f20ed9SLaurent Pinchart 	unsigned int fifosize;
124b2f20ed9SLaurent Pinchart 	unsigned int overrun_reg;
125b2f20ed9SLaurent Pinchart 	unsigned int overrun_mask;
126b2f20ed9SLaurent Pinchart 	unsigned int sampling_rate_mask;
127b2f20ed9SLaurent Pinchart 	unsigned int error_mask;
128b2f20ed9SLaurent Pinchart 	unsigned int error_clear;
129e095ee6bSLaurent Pinchart };
130e095ee6bSLaurent Pinchart 
131ab4382d2SGreg Kroah-Hartman struct sci_port {
132ab4382d2SGreg Kroah-Hartman 	struct uart_port	port;
133ab4382d2SGreg Kroah-Hartman 
1346b620478SPaul Mundt 	/* Platform configuration */
135e095ee6bSLaurent Pinchart 	const struct sci_port_params *params;
136daf5a895SLaurent Pinchart 	const struct plat_sci_port *cfg;
13769eee8e9SGeert Uytterhoeven 	unsigned int		sampling_rate_mask;
138e4d6f911SYoshinori Sato 	resource_size_t		reg_size;
139f907c9eaSGeert Uytterhoeven 	struct mctrl_gpios	*gpios;
140ab4382d2SGreg Kroah-Hartman 
141f4998e55SGeert Uytterhoeven 	/* Clocks */
142f4998e55SGeert Uytterhoeven 	struct clk		*clks[SCI_NUM_CLKS];
143f4998e55SGeert Uytterhoeven 	unsigned long		clk_rates[SCI_NUM_CLKS];
144ab4382d2SGreg Kroah-Hartman 
1451fcc91a6SLaurent Pinchart 	int			irqs[SCIx_NR_IRQS];
1469174fc8fSPaul Mundt 	char			*irqstr[SCIx_NR_IRQS];
1479174fc8fSPaul Mundt 
148ab4382d2SGreg Kroah-Hartman 	struct dma_chan			*chan_tx;
149ab4382d2SGreg Kroah-Hartman 	struct dma_chan			*chan_rx;
150fc887b15SLinus Torvalds 
15122a6984cSClaudiu Beznea 	struct reset_control		*rstc;
15222a6984cSClaudiu Beznea 
153ab4382d2SGreg Kroah-Hartman #ifdef CONFIG_SERIAL_SH_SCI_DMA
1542c4ee235SGeert Uytterhoeven 	struct dma_chan			*chan_tx_saved;
1552c4ee235SGeert Uytterhoeven 	struct dma_chan			*chan_rx_saved;
156ab4382d2SGreg Kroah-Hartman 	dma_cookie_t			cookie_tx;
157ab4382d2SGreg Kroah-Hartman 	dma_cookie_t			cookie_rx[2];
158ab4382d2SGreg Kroah-Hartman 	dma_cookie_t			active_rx;
15979904420SGeert Uytterhoeven 	dma_addr_t			tx_dma_addr;
16079904420SGeert Uytterhoeven 	unsigned int			tx_dma_len;
161ab4382d2SGreg Kroah-Hartman 	struct scatterlist		sg_rx[2];
1627b39d901SYoshihiro Shimoda 	void				*rx_buf[2];
163ab4382d2SGreg Kroah-Hartman 	size_t				buf_len_rx;
164ab4382d2SGreg Kroah-Hartman 	struct work_struct		work_tx;
165b96408b4SUlrich Hecht 	struct hrtimer			rx_timer;
166b96408b4SUlrich Hecht 	unsigned int			rx_timeout;	/* microseconds */
167ab4382d2SGreg Kroah-Hartman #endif
16803940376SUlrich Hecht 	unsigned int			rx_frame;
16918e8cf15SUlrich Hecht 	int				rx_trigger;
17003940376SUlrich Hecht 	struct timer_list		rx_fifo_timer;
17103940376SUlrich Hecht 	int				rx_fifo_timeout;
17222a6984cSClaudiu Beznea 	struct sci_suspend_regs		suspend_regs;
173fa2abb03SUlrich Hecht 	u16				hscif_tot;
17433f50ffcSGeert Uytterhoeven 
17597ed9790SLaurent Pinchart 	bool has_rtscts;
17633f50ffcSGeert Uytterhoeven 	bool autorts;
1777cc0e0a4SClaudiu Beznea 	bool tx_occurred;
178ab4382d2SGreg Kroah-Hartman };
179ab4382d2SGreg Kroah-Hartman 
180ab4382d2SGreg Kroah-Hartman #define SCI_NPORTS CONFIG_SERIAL_SH_SCI_NR_UARTS
181ab4382d2SGreg Kroah-Hartman 
182ab4382d2SGreg Kroah-Hartman static struct sci_port sci_ports[SCI_NPORTS];
1837678f4c2SGeert Uytterhoeven static unsigned long sci_ports_in_use;
184ab4382d2SGreg Kroah-Hartman static struct uart_driver sci_uart_driver;
1859f7dea87SClaudiu Beznea static bool sci_uart_earlycon;
1865f101706SClaudiu Beznea static bool sci_uart_earlycon_dev_probing;
187ab4382d2SGreg Kroah-Hartman 
188ab4382d2SGreg Kroah-Hartman static inline struct sci_port *
to_sci_port(struct uart_port * uart)189ab4382d2SGreg Kroah-Hartman to_sci_port(struct uart_port *uart)
190ab4382d2SGreg Kroah-Hartman {
191ab4382d2SGreg Kroah-Hartman 	return container_of(uart, struct sci_port, port);
192ab4382d2SGreg Kroah-Hartman }
193ab4382d2SGreg Kroah-Hartman 
194e095ee6bSLaurent Pinchart static const struct sci_port_params sci_port_params[SCIx_NR_REGTYPES] = {
19561a6976bSPaul Mundt 	/*
19661a6976bSPaul Mundt 	 * Common SCI definitions, dependent on the port's regshift
19761a6976bSPaul Mundt 	 * value.
19861a6976bSPaul Mundt 	 */
19961a6976bSPaul Mundt 	[SCIx_SCI_REGTYPE] = {
200e095ee6bSLaurent Pinchart 		.regs = {
20161a6976bSPaul Mundt 			[SCSMR]		= { 0x00,  8 },
20261a6976bSPaul Mundt 			[SCBRR]		= { 0x01,  8 },
20361a6976bSPaul Mundt 			[SCSCR]		= { 0x02,  8 },
20461a6976bSPaul Mundt 			[SCxTDR]	= { 0x03,  8 },
20561a6976bSPaul Mundt 			[SCxSR]		= { 0x04,  8 },
20661a6976bSPaul Mundt 			[SCxRDR]	= { 0x05,  8 },
20761a6976bSPaul Mundt 		},
208b2f20ed9SLaurent Pinchart 		.fifosize = 1,
209b2f20ed9SLaurent Pinchart 		.overrun_reg = SCxSR,
210b2f20ed9SLaurent Pinchart 		.overrun_mask = SCI_ORER,
211b2f20ed9SLaurent Pinchart 		.sampling_rate_mask = SCI_SR(32),
212b2f20ed9SLaurent Pinchart 		.error_mask = SCI_DEFAULT_ERROR_MASK | SCI_ORER,
213b2f20ed9SLaurent Pinchart 		.error_clear = SCI_ERROR_CLEAR & ~SCI_ORER,
214e095ee6bSLaurent Pinchart 	},
21561a6976bSPaul Mundt 
21661a6976bSPaul Mundt 	/*
217a752ba18SLaurent Pinchart 	 * Common definitions for legacy IrDA ports.
21861a6976bSPaul Mundt 	 */
21961a6976bSPaul Mundt 	[SCIx_IRDA_REGTYPE] = {
220e095ee6bSLaurent Pinchart 		.regs = {
22161a6976bSPaul Mundt 			[SCSMR]		= { 0x00,  8 },
222a752ba18SLaurent Pinchart 			[SCBRR]		= { 0x02,  8 },
223a752ba18SLaurent Pinchart 			[SCSCR]		= { 0x04,  8 },
224a752ba18SLaurent Pinchart 			[SCxTDR]	= { 0x06,  8 },
225a752ba18SLaurent Pinchart 			[SCxSR]		= { 0x08, 16 },
226a752ba18SLaurent Pinchart 			[SCxRDR]	= { 0x0a,  8 },
227a752ba18SLaurent Pinchart 			[SCFCR]		= { 0x0c,  8 },
228a752ba18SLaurent Pinchart 			[SCFDR]		= { 0x0e, 16 },
22961a6976bSPaul Mundt 		},
230b2f20ed9SLaurent Pinchart 		.fifosize = 1,
231b2f20ed9SLaurent Pinchart 		.overrun_reg = SCxSR,
232b2f20ed9SLaurent Pinchart 		.overrun_mask = SCI_ORER,
233b2f20ed9SLaurent Pinchart 		.sampling_rate_mask = SCI_SR(32),
234b2f20ed9SLaurent Pinchart 		.error_mask = SCI_DEFAULT_ERROR_MASK | SCI_ORER,
235b2f20ed9SLaurent Pinchart 		.error_clear = SCI_ERROR_CLEAR & ~SCI_ORER,
236e095ee6bSLaurent Pinchart 	},
23761a6976bSPaul Mundt 
23861a6976bSPaul Mundt 	/*
23961a6976bSPaul Mundt 	 * Common SCIFA definitions.
24061a6976bSPaul Mundt 	 */
24161a6976bSPaul Mundt 	[SCIx_SCIFA_REGTYPE] = {
242e095ee6bSLaurent Pinchart 		.regs = {
24361a6976bSPaul Mundt 			[SCSMR]		= { 0x00, 16 },
24461a6976bSPaul Mundt 			[SCBRR]		= { 0x04,  8 },
24561a6976bSPaul Mundt 			[SCSCR]		= { 0x08, 16 },
24661a6976bSPaul Mundt 			[SCxTDR]	= { 0x20,  8 },
24761a6976bSPaul Mundt 			[SCxSR]		= { 0x14, 16 },
24861a6976bSPaul Mundt 			[SCxRDR]	= { 0x24,  8 },
24961a6976bSPaul Mundt 			[SCFCR]		= { 0x18, 16 },
25061a6976bSPaul Mundt 			[SCFDR]		= { 0x1c, 16 },
251c097abc3SGeert Uytterhoeven 			[SCPCR]		= { 0x30, 16 },
252c097abc3SGeert Uytterhoeven 			[SCPDR]		= { 0x34, 16 },
25361a6976bSPaul Mundt 		},
254b2f20ed9SLaurent Pinchart 		.fifosize = 64,
255b2f20ed9SLaurent Pinchart 		.overrun_reg = SCxSR,
256b2f20ed9SLaurent Pinchart 		.overrun_mask = SCIFA_ORER,
257b2f20ed9SLaurent Pinchart 		.sampling_rate_mask = SCI_SR_SCIFAB,
258b2f20ed9SLaurent Pinchart 		.error_mask = SCIF_DEFAULT_ERROR_MASK | SCIFA_ORER,
259b2f20ed9SLaurent Pinchart 		.error_clear = SCIF_ERROR_CLEAR & ~SCIFA_ORER,
260e095ee6bSLaurent Pinchart 	},
26161a6976bSPaul Mundt 
26261a6976bSPaul Mundt 	/*
26361a6976bSPaul Mundt 	 * Common SCIFB definitions.
26461a6976bSPaul Mundt 	 */
26561a6976bSPaul Mundt 	[SCIx_SCIFB_REGTYPE] = {
266e095ee6bSLaurent Pinchart 		.regs = {
26761a6976bSPaul Mundt 			[SCSMR]		= { 0x00, 16 },
26861a6976bSPaul Mundt 			[SCBRR]		= { 0x04,  8 },
26961a6976bSPaul Mundt 			[SCSCR]		= { 0x08, 16 },
27061a6976bSPaul Mundt 			[SCxTDR]	= { 0x40,  8 },
27161a6976bSPaul Mundt 			[SCxSR]		= { 0x14, 16 },
27261a6976bSPaul Mundt 			[SCxRDR]	= { 0x60,  8 },
27361a6976bSPaul Mundt 			[SCFCR]		= { 0x18, 16 },
2748c66d6d2STakashi Yoshii 			[SCTFDR]	= { 0x38, 16 },
2758c66d6d2STakashi Yoshii 			[SCRFDR]	= { 0x3c, 16 },
276c097abc3SGeert Uytterhoeven 			[SCPCR]		= { 0x30, 16 },
277c097abc3SGeert Uytterhoeven 			[SCPDR]		= { 0x34, 16 },
27861a6976bSPaul Mundt 		},
279b2f20ed9SLaurent Pinchart 		.fifosize = 256,
280b2f20ed9SLaurent Pinchart 		.overrun_reg = SCxSR,
281b2f20ed9SLaurent Pinchart 		.overrun_mask = SCIFA_ORER,
282b2f20ed9SLaurent Pinchart 		.sampling_rate_mask = SCI_SR_SCIFAB,
283b2f20ed9SLaurent Pinchart 		.error_mask = SCIF_DEFAULT_ERROR_MASK | SCIFA_ORER,
284b2f20ed9SLaurent Pinchart 		.error_clear = SCIF_ERROR_CLEAR & ~SCIFA_ORER,
285e095ee6bSLaurent Pinchart 	},
28661a6976bSPaul Mundt 
28761a6976bSPaul Mundt 	/*
2883af1f8a4SPhil Edworthy 	 * Common SH-2(A) SCIF definitions for ports with FIFO data
2893af1f8a4SPhil Edworthy 	 * count registers.
2903af1f8a4SPhil Edworthy 	 */
2913af1f8a4SPhil Edworthy 	[SCIx_SH2_SCIF_FIFODATA_REGTYPE] = {
292e095ee6bSLaurent Pinchart 		.regs = {
2933af1f8a4SPhil Edworthy 			[SCSMR]		= { 0x00, 16 },
2943af1f8a4SPhil Edworthy 			[SCBRR]		= { 0x04,  8 },
2953af1f8a4SPhil Edworthy 			[SCSCR]		= { 0x08, 16 },
2963af1f8a4SPhil Edworthy 			[SCxTDR]	= { 0x0c,  8 },
2973af1f8a4SPhil Edworthy 			[SCxSR]		= { 0x10, 16 },
2983af1f8a4SPhil Edworthy 			[SCxRDR]	= { 0x14,  8 },
2993af1f8a4SPhil Edworthy 			[SCFCR]		= { 0x18, 16 },
3003af1f8a4SPhil Edworthy 			[SCFDR]		= { 0x1c, 16 },
3013af1f8a4SPhil Edworthy 			[SCSPTR]	= { 0x20, 16 },
3023af1f8a4SPhil Edworthy 			[SCLSR]		= { 0x24, 16 },
3033af1f8a4SPhil Edworthy 		},
304b2f20ed9SLaurent Pinchart 		.fifosize = 16,
305b2f20ed9SLaurent Pinchart 		.overrun_reg = SCLSR,
306b2f20ed9SLaurent Pinchart 		.overrun_mask = SCLSR_ORER,
307b2f20ed9SLaurent Pinchart 		.sampling_rate_mask = SCI_SR(32),
308b2f20ed9SLaurent Pinchart 		.error_mask = SCIF_DEFAULT_ERROR_MASK,
309b2f20ed9SLaurent Pinchart 		.error_clear = SCIF_ERROR_CLEAR,
310e095ee6bSLaurent Pinchart 	},
3113af1f8a4SPhil Edworthy 
3123af1f8a4SPhil Edworthy 	/*
3133b2cd606SBiju Das 	 * The "SCIFA" that is in RZ/A2, RZ/G2L and RZ/T.
31410c63443SGeert Uytterhoeven 	 * It looks like a normal SCIF with FIFO data, but with a
31510c63443SGeert Uytterhoeven 	 * compressed address space. Also, the break out of interrupts
31610c63443SGeert Uytterhoeven 	 * are different: ERI/BRI, RXI, TXI, TEI, DRI.
31710c63443SGeert Uytterhoeven 	 */
31810c63443SGeert Uytterhoeven 	[SCIx_RZ_SCIFA_REGTYPE] = {
31910c63443SGeert Uytterhoeven 		.regs = {
32010c63443SGeert Uytterhoeven 			[SCSMR]		= { 0x00, 16 },
32110c63443SGeert Uytterhoeven 			[SCBRR]		= { 0x02,  8 },
32210c63443SGeert Uytterhoeven 			[SCSCR]		= { 0x04, 16 },
32310c63443SGeert Uytterhoeven 			[SCxTDR]	= { 0x06,  8 },
32410c63443SGeert Uytterhoeven 			[SCxSR]		= { 0x08, 16 },
32510c63443SGeert Uytterhoeven 			[SCxRDR]	= { 0x0A,  8 },
32610c63443SGeert Uytterhoeven 			[SCFCR]		= { 0x0C, 16 },
32710c63443SGeert Uytterhoeven 			[SCFDR]		= { 0x0E, 16 },
32810c63443SGeert Uytterhoeven 			[SCSPTR]	= { 0x10, 16 },
32910c63443SGeert Uytterhoeven 			[SCLSR]		= { 0x12, 16 },
3303b2cd606SBiju Das 			[SEMR]		= { 0x14, 8 },
33110c63443SGeert Uytterhoeven 		},
33210c63443SGeert Uytterhoeven 		.fifosize = 16,
33310c63443SGeert Uytterhoeven 		.overrun_reg = SCLSR,
33410c63443SGeert Uytterhoeven 		.overrun_mask = SCLSR_ORER,
33510c63443SGeert Uytterhoeven 		.sampling_rate_mask = SCI_SR(32),
33610c63443SGeert Uytterhoeven 		.error_mask = SCIF_DEFAULT_ERROR_MASK,
33710c63443SGeert Uytterhoeven 		.error_clear = SCIF_ERROR_CLEAR,
33810c63443SGeert Uytterhoeven 	},
33910c63443SGeert Uytterhoeven 
34010c63443SGeert Uytterhoeven 	/*
3412f50304eSLad Prabhakar 	 * The "SCIF" that is in RZ/V2H(P) SoC is similar to one found on RZ/G2L SoC
3422f50304eSLad Prabhakar 	 * with below differences,
3432f50304eSLad Prabhakar 	 * - Break out of interrupts are different: ERI, BRI, RXI, TXI, TEI, DRI,
3442f50304eSLad Prabhakar 	 *   TEI-DRI, RXI-EDGE and TXI-EDGE.
3452f50304eSLad Prabhakar 	 * - SCSMR register does not have CM bit (BIT(7)) ie it does not support synchronous mode.
3462f50304eSLad Prabhakar 	 * - SCFCR register does not have SCFCR_MCE bit.
3472f50304eSLad Prabhakar 	 * - SCSPTR register has only bits SCSPTR_SPB2DT and SCSPTR_SPB2IO.
3482f50304eSLad Prabhakar 	 */
3492f50304eSLad Prabhakar 	[SCIx_RZV2H_SCIF_REGTYPE] = {
3502f50304eSLad Prabhakar 		.regs = {
3512f50304eSLad Prabhakar 			[SCSMR]		= { 0x00, 16 },
3522f50304eSLad Prabhakar 			[SCBRR]		= { 0x02,  8 },
3532f50304eSLad Prabhakar 			[SCSCR]		= { 0x04, 16 },
3542f50304eSLad Prabhakar 			[SCxTDR]	= { 0x06,  8 },
3552f50304eSLad Prabhakar 			[SCxSR]		= { 0x08, 16 },
3562f50304eSLad Prabhakar 			[SCxRDR]	= { 0x0a,  8 },
3572f50304eSLad Prabhakar 			[SCFCR]		= { 0x0c, 16 },
3582f50304eSLad Prabhakar 			[SCFDR]		= { 0x0e, 16 },
3592f50304eSLad Prabhakar 			[SCSPTR]	= { 0x10, 16 },
3602f50304eSLad Prabhakar 			[SCLSR]		= { 0x12, 16 },
3612f50304eSLad Prabhakar 			[SEMR]		= { 0x14, 8 },
3622f50304eSLad Prabhakar 		},
3632f50304eSLad Prabhakar 		.fifosize = 16,
3642f50304eSLad Prabhakar 		.overrun_reg = SCLSR,
3652f50304eSLad Prabhakar 		.overrun_mask = SCLSR_ORER,
3662f50304eSLad Prabhakar 		.sampling_rate_mask = SCI_SR(32),
3672f50304eSLad Prabhakar 		.error_mask = SCIF_DEFAULT_ERROR_MASK,
3682f50304eSLad Prabhakar 		.error_clear = SCIF_ERROR_CLEAR,
3692f50304eSLad Prabhakar 	},
3702f50304eSLad Prabhakar 
3712f50304eSLad Prabhakar 	/*
37261a6976bSPaul Mundt 	 * Common SH-3 SCIF definitions.
37361a6976bSPaul Mundt 	 */
37461a6976bSPaul Mundt 	[SCIx_SH3_SCIF_REGTYPE] = {
375e095ee6bSLaurent Pinchart 		.regs = {
37661a6976bSPaul Mundt 			[SCSMR]		= { 0x00,  8 },
37761a6976bSPaul Mundt 			[SCBRR]		= { 0x02,  8 },
37861a6976bSPaul Mundt 			[SCSCR]		= { 0x04,  8 },
37961a6976bSPaul Mundt 			[SCxTDR]	= { 0x06,  8 },
38061a6976bSPaul Mundt 			[SCxSR]		= { 0x08, 16 },
38161a6976bSPaul Mundt 			[SCxRDR]	= { 0x0a,  8 },
38261a6976bSPaul Mundt 			[SCFCR]		= { 0x0c,  8 },
38361a6976bSPaul Mundt 			[SCFDR]		= { 0x0e, 16 },
38461a6976bSPaul Mundt 		},
385b2f20ed9SLaurent Pinchart 		.fifosize = 16,
386b2f20ed9SLaurent Pinchart 		.overrun_reg = SCLSR,
387b2f20ed9SLaurent Pinchart 		.overrun_mask = SCLSR_ORER,
388b2f20ed9SLaurent Pinchart 		.sampling_rate_mask = SCI_SR(32),
389b2f20ed9SLaurent Pinchart 		.error_mask = SCIF_DEFAULT_ERROR_MASK,
390b2f20ed9SLaurent Pinchart 		.error_clear = SCIF_ERROR_CLEAR,
391e095ee6bSLaurent Pinchart 	},
39261a6976bSPaul Mundt 
39361a6976bSPaul Mundt 	/*
39461a6976bSPaul Mundt 	 * Common SH-4(A) SCIF(B) definitions.
39561a6976bSPaul Mundt 	 */
39661a6976bSPaul Mundt 	[SCIx_SH4_SCIF_REGTYPE] = {
397e095ee6bSLaurent Pinchart 		.regs = {
39861a6976bSPaul Mundt 			[SCSMR]		= { 0x00, 16 },
399a1c2fd7eSGeert Uytterhoeven 			[SCBRR]		= { 0x04,  8 },
400a1c2fd7eSGeert Uytterhoeven 			[SCSCR]		= { 0x08, 16 },
401a1c2fd7eSGeert Uytterhoeven 			[SCxTDR]	= { 0x0c,  8 },
402a1c2fd7eSGeert Uytterhoeven 			[SCxSR]		= { 0x10, 16 },
403a1c2fd7eSGeert Uytterhoeven 			[SCxRDR]	= { 0x14,  8 },
404a1c2fd7eSGeert Uytterhoeven 			[SCFCR]		= { 0x18, 16 },
405a1c2fd7eSGeert Uytterhoeven 			[SCFDR]		= { 0x1c, 16 },
406a1c2fd7eSGeert Uytterhoeven 			[SCSPTR]	= { 0x20, 16 },
407a1c2fd7eSGeert Uytterhoeven 			[SCLSR]		= { 0x24, 16 },
408b8bbd6b2SGeert Uytterhoeven 		},
409b2f20ed9SLaurent Pinchart 		.fifosize = 16,
410b2f20ed9SLaurent Pinchart 		.overrun_reg = SCLSR,
411b2f20ed9SLaurent Pinchart 		.overrun_mask = SCLSR_ORER,
412b2f20ed9SLaurent Pinchart 		.sampling_rate_mask = SCI_SR(32),
413b2f20ed9SLaurent Pinchart 		.error_mask = SCIF_DEFAULT_ERROR_MASK,
414b2f20ed9SLaurent Pinchart 		.error_clear = SCIF_ERROR_CLEAR,
415e095ee6bSLaurent Pinchart 	},
416b8bbd6b2SGeert Uytterhoeven 
417b8bbd6b2SGeert Uytterhoeven 	/*
418b8bbd6b2SGeert Uytterhoeven 	 * Common SCIF definitions for ports with a Baud Rate Generator for
419b8bbd6b2SGeert Uytterhoeven 	 * External Clock (BRG).
420b8bbd6b2SGeert Uytterhoeven 	 */
421b8bbd6b2SGeert Uytterhoeven 	[SCIx_SH4_SCIF_BRG_REGTYPE] = {
422e095ee6bSLaurent Pinchart 		.regs = {
423b8bbd6b2SGeert Uytterhoeven 			[SCSMR]		= { 0x00, 16 },
424b8bbd6b2SGeert Uytterhoeven 			[SCBRR]		= { 0x04,  8 },
425b8bbd6b2SGeert Uytterhoeven 			[SCSCR]		= { 0x08, 16 },
426b8bbd6b2SGeert Uytterhoeven 			[SCxTDR]	= { 0x0c,  8 },
427b8bbd6b2SGeert Uytterhoeven 			[SCxSR]		= { 0x10, 16 },
428b8bbd6b2SGeert Uytterhoeven 			[SCxRDR]	= { 0x14,  8 },
429b8bbd6b2SGeert Uytterhoeven 			[SCFCR]		= { 0x18, 16 },
430b8bbd6b2SGeert Uytterhoeven 			[SCFDR]		= { 0x1c, 16 },
431b8bbd6b2SGeert Uytterhoeven 			[SCSPTR]	= { 0x20, 16 },
432b8bbd6b2SGeert Uytterhoeven 			[SCLSR]		= { 0x24, 16 },
433b8bbd6b2SGeert Uytterhoeven 			[SCDL]		= { 0x30, 16 },
434b8bbd6b2SGeert Uytterhoeven 			[SCCKS]		= { 0x34, 16 },
435f303b364SUlrich Hecht 		},
436b2f20ed9SLaurent Pinchart 		.fifosize = 16,
437b2f20ed9SLaurent Pinchart 		.overrun_reg = SCLSR,
438b2f20ed9SLaurent Pinchart 		.overrun_mask = SCLSR_ORER,
439b2f20ed9SLaurent Pinchart 		.sampling_rate_mask = SCI_SR(32),
440b2f20ed9SLaurent Pinchart 		.error_mask = SCIF_DEFAULT_ERROR_MASK,
441b2f20ed9SLaurent Pinchart 		.error_clear = SCIF_ERROR_CLEAR,
442e095ee6bSLaurent Pinchart 	},
443f303b364SUlrich Hecht 
444f303b364SUlrich Hecht 	/*
445f303b364SUlrich Hecht 	 * Common HSCIF definitions.
446f303b364SUlrich Hecht 	 */
447f303b364SUlrich Hecht 	[SCIx_HSCIF_REGTYPE] = {
448e095ee6bSLaurent Pinchart 		.regs = {
449f303b364SUlrich Hecht 			[SCSMR]		= { 0x00, 16 },
450f303b364SUlrich Hecht 			[SCBRR]		= { 0x04,  8 },
451f303b364SUlrich Hecht 			[SCSCR]		= { 0x08, 16 },
452f303b364SUlrich Hecht 			[SCxTDR]	= { 0x0c,  8 },
453f303b364SUlrich Hecht 			[SCxSR]		= { 0x10, 16 },
454f303b364SUlrich Hecht 			[SCxRDR]	= { 0x14,  8 },
455f303b364SUlrich Hecht 			[SCFCR]		= { 0x18, 16 },
456f303b364SUlrich Hecht 			[SCFDR]		= { 0x1c, 16 },
457f303b364SUlrich Hecht 			[SCSPTR]	= { 0x20, 16 },
458f303b364SUlrich Hecht 			[SCLSR]		= { 0x24, 16 },
459f303b364SUlrich Hecht 			[HSSRR]		= { 0x40, 16 },
460b8bbd6b2SGeert Uytterhoeven 			[SCDL]		= { 0x30, 16 },
461b8bbd6b2SGeert Uytterhoeven 			[SCCKS]		= { 0x34, 16 },
46254e14ae2SUlrich Hecht 			[HSRTRGR]	= { 0x54, 16 },
46354e14ae2SUlrich Hecht 			[HSTTRGR]	= { 0x58, 16 },
46461a6976bSPaul Mundt 		},
465b2f20ed9SLaurent Pinchart 		.fifosize = 128,
466b2f20ed9SLaurent Pinchart 		.overrun_reg = SCLSR,
467b2f20ed9SLaurent Pinchart 		.overrun_mask = SCLSR_ORER,
468b2f20ed9SLaurent Pinchart 		.sampling_rate_mask = SCI_SR_RANGE(8, 32),
469b2f20ed9SLaurent Pinchart 		.error_mask = SCIF_DEFAULT_ERROR_MASK,
470b2f20ed9SLaurent Pinchart 		.error_clear = SCIF_ERROR_CLEAR,
471e095ee6bSLaurent Pinchart 	},
47261a6976bSPaul Mundt 
47361a6976bSPaul Mundt 	/*
47461a6976bSPaul Mundt 	 * Common SH-4(A) SCIF(B) definitions for ports without an SCSPTR
47561a6976bSPaul Mundt 	 * register.
47661a6976bSPaul Mundt 	 */
47761a6976bSPaul Mundt 	[SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE] = {
478e095ee6bSLaurent Pinchart 		.regs = {
47961a6976bSPaul Mundt 			[SCSMR]		= { 0x00, 16 },
48061a6976bSPaul Mundt 			[SCBRR]		= { 0x04,  8 },
48161a6976bSPaul Mundt 			[SCSCR]		= { 0x08, 16 },
48261a6976bSPaul Mundt 			[SCxTDR]	= { 0x0c,  8 },
48361a6976bSPaul Mundt 			[SCxSR]		= { 0x10, 16 },
48461a6976bSPaul Mundt 			[SCxRDR]	= { 0x14,  8 },
48561a6976bSPaul Mundt 			[SCFCR]		= { 0x18, 16 },
48661a6976bSPaul Mundt 			[SCFDR]		= { 0x1c, 16 },
48761a6976bSPaul Mundt 			[SCLSR]		= { 0x24, 16 },
48861a6976bSPaul Mundt 		},
489b2f20ed9SLaurent Pinchart 		.fifosize = 16,
490b2f20ed9SLaurent Pinchart 		.overrun_reg = SCLSR,
491b2f20ed9SLaurent Pinchart 		.overrun_mask = SCLSR_ORER,
492b2f20ed9SLaurent Pinchart 		.sampling_rate_mask = SCI_SR(32),
493b2f20ed9SLaurent Pinchart 		.error_mask = SCIF_DEFAULT_ERROR_MASK,
494b2f20ed9SLaurent Pinchart 		.error_clear = SCIF_ERROR_CLEAR,
495e095ee6bSLaurent Pinchart 	},
49661a6976bSPaul Mundt 
49761a6976bSPaul Mundt 	/*
49861a6976bSPaul Mundt 	 * Common SH-4(A) SCIF(B) definitions for ports with FIFO data
49961a6976bSPaul Mundt 	 * count registers.
50061a6976bSPaul Mundt 	 */
50161a6976bSPaul Mundt 	[SCIx_SH4_SCIF_FIFODATA_REGTYPE] = {
502e095ee6bSLaurent Pinchart 		.regs = {
50361a6976bSPaul Mundt 			[SCSMR]		= { 0x00, 16 },
50461a6976bSPaul Mundt 			[SCBRR]		= { 0x04,  8 },
50561a6976bSPaul Mundt 			[SCSCR]		= { 0x08, 16 },
50661a6976bSPaul Mundt 			[SCxTDR]	= { 0x0c,  8 },
50761a6976bSPaul Mundt 			[SCxSR]		= { 0x10, 16 },
50861a6976bSPaul Mundt 			[SCxRDR]	= { 0x14,  8 },
50961a6976bSPaul Mundt 			[SCFCR]		= { 0x18, 16 },
51061a6976bSPaul Mundt 			[SCFDR]		= { 0x1c, 16 },
51161a6976bSPaul Mundt 			[SCTFDR]	= { 0x1c, 16 },	/* aliased to SCFDR */
51261a6976bSPaul Mundt 			[SCRFDR]	= { 0x20, 16 },
51361a6976bSPaul Mundt 			[SCSPTR]	= { 0x24, 16 },
51461a6976bSPaul Mundt 			[SCLSR]		= { 0x28, 16 },
51561a6976bSPaul Mundt 		},
516b2f20ed9SLaurent Pinchart 		.fifosize = 16,
517b2f20ed9SLaurent Pinchart 		.overrun_reg = SCLSR,
518b2f20ed9SLaurent Pinchart 		.overrun_mask = SCLSR_ORER,
519b2f20ed9SLaurent Pinchart 		.sampling_rate_mask = SCI_SR(32),
520b2f20ed9SLaurent Pinchart 		.error_mask = SCIF_DEFAULT_ERROR_MASK,
521b2f20ed9SLaurent Pinchart 		.error_clear = SCIF_ERROR_CLEAR,
522e095ee6bSLaurent Pinchart 	},
52361a6976bSPaul Mundt 
52461a6976bSPaul Mundt 	/*
52561a6976bSPaul Mundt 	 * SH7705-style SCIF(B) ports, lacking both SCSPTR and SCLSR
52661a6976bSPaul Mundt 	 * registers.
52761a6976bSPaul Mundt 	 */
52861a6976bSPaul Mundt 	[SCIx_SH7705_SCIF_REGTYPE] = {
529e095ee6bSLaurent Pinchart 		.regs = {
53061a6976bSPaul Mundt 			[SCSMR]		= { 0x00, 16 },
53161a6976bSPaul Mundt 			[SCBRR]		= { 0x04,  8 },
53261a6976bSPaul Mundt 			[SCSCR]		= { 0x08, 16 },
53361a6976bSPaul Mundt 			[SCxTDR]	= { 0x20,  8 },
53461a6976bSPaul Mundt 			[SCxSR]		= { 0x14, 16 },
53561a6976bSPaul Mundt 			[SCxRDR]	= { 0x24,  8 },
53661a6976bSPaul Mundt 			[SCFCR]		= { 0x18, 16 },
53761a6976bSPaul Mundt 			[SCFDR]		= { 0x1c, 16 },
53861a6976bSPaul Mundt 		},
53918e8cf15SUlrich Hecht 		.fifosize = 64,
540b2f20ed9SLaurent Pinchart 		.overrun_reg = SCxSR,
541b2f20ed9SLaurent Pinchart 		.overrun_mask = SCIFA_ORER,
542b2f20ed9SLaurent Pinchart 		.sampling_rate_mask = SCI_SR(16),
543b2f20ed9SLaurent Pinchart 		.error_mask = SCIF_DEFAULT_ERROR_MASK | SCIFA_ORER,
544b2f20ed9SLaurent Pinchart 		.error_clear = SCIF_ERROR_CLEAR & ~SCIFA_ORER,
545e095ee6bSLaurent Pinchart 	},
54661a6976bSPaul Mundt };
54761a6976bSPaul Mundt 
548e095ee6bSLaurent Pinchart #define sci_getreg(up, offset)		(&to_sci_port(up)->params->regs[offset])
54972b294cfSPaul Mundt 
55061a6976bSPaul Mundt /*
55161a6976bSPaul Mundt  * The "offset" here is rather misleading, in that it refers to an enum
55261a6976bSPaul Mundt  * value relative to the port mapping rather than the fixed offset
55361a6976bSPaul Mundt  * itself, which needs to be manually retrieved from the platform's
55461a6976bSPaul Mundt  * register map for the given port.
55561a6976bSPaul Mundt  */
sci_serial_in(struct uart_port * p,int offset)55661a6976bSPaul Mundt static unsigned int sci_serial_in(struct uart_port *p, int offset)
55761a6976bSPaul Mundt {
558d3184e68SGeert Uytterhoeven 	const struct plat_sci_reg *reg = sci_getreg(p, offset);
55961a6976bSPaul Mundt 
56061a6976bSPaul Mundt 	if (reg->size == 8)
56161a6976bSPaul Mundt 		return ioread8(p->membase + (reg->offset << p->regshift));
56261a6976bSPaul Mundt 	else if (reg->size == 16)
56361a6976bSPaul Mundt 		return ioread16(p->membase + (reg->offset << p->regshift));
56461a6976bSPaul Mundt 	else
56561a6976bSPaul Mundt 		WARN(1, "Invalid register access\n");
56661a6976bSPaul Mundt 
56761a6976bSPaul Mundt 	return 0;
56861a6976bSPaul Mundt }
56961a6976bSPaul Mundt 
sci_serial_out(struct uart_port * p,int offset,int value)57061a6976bSPaul Mundt static void sci_serial_out(struct uart_port *p, int offset, int value)
57161a6976bSPaul Mundt {
572d3184e68SGeert Uytterhoeven 	const struct plat_sci_reg *reg = sci_getreg(p, offset);
57361a6976bSPaul Mundt 
57461a6976bSPaul Mundt 	if (reg->size == 8)
57561a6976bSPaul Mundt 		iowrite8(value, p->membase + (reg->offset << p->regshift));
57661a6976bSPaul Mundt 	else if (reg->size == 16)
57761a6976bSPaul Mundt 		iowrite16(value, p->membase + (reg->offset << p->regshift));
57861a6976bSPaul Mundt 	else
57961a6976bSPaul Mundt 		WARN(1, "Invalid register access\n");
58061a6976bSPaul Mundt }
58161a6976bSPaul Mundt 
sci_port_enable(struct sci_port * sci_port)58223241d43SPaul Mundt static void sci_port_enable(struct sci_port *sci_port)
58323241d43SPaul Mundt {
584f4998e55SGeert Uytterhoeven 	unsigned int i;
585f4998e55SGeert Uytterhoeven 
58623241d43SPaul Mundt 	if (!sci_port->port.dev)
58723241d43SPaul Mundt 		return;
58823241d43SPaul Mundt 
58923241d43SPaul Mundt 	pm_runtime_get_sync(sci_port->port.dev);
59023241d43SPaul Mundt 
591f4998e55SGeert Uytterhoeven 	for (i = 0; i < SCI_NUM_CLKS; i++) {
592f4998e55SGeert Uytterhoeven 		clk_prepare_enable(sci_port->clks[i]);
593f4998e55SGeert Uytterhoeven 		sci_port->clk_rates[i] = clk_get_rate(sci_port->clks[i]);
594f4998e55SGeert Uytterhoeven 	}
595f4998e55SGeert Uytterhoeven 	sci_port->port.uartclk = sci_port->clk_rates[SCI_FCK];
59623241d43SPaul Mundt }
59723241d43SPaul Mundt 
sci_port_disable(struct sci_port * sci_port)59823241d43SPaul Mundt static void sci_port_disable(struct sci_port *sci_port)
59923241d43SPaul Mundt {
600f4998e55SGeert Uytterhoeven 	unsigned int i;
601f4998e55SGeert Uytterhoeven 
60223241d43SPaul Mundt 	if (!sci_port->port.dev)
60323241d43SPaul Mundt 		return;
60423241d43SPaul Mundt 
605f4998e55SGeert Uytterhoeven 	for (i = SCI_NUM_CLKS; i-- > 0; )
606f4998e55SGeert Uytterhoeven 		clk_disable_unprepare(sci_port->clks[i]);
60723241d43SPaul Mundt 
60823241d43SPaul Mundt 	pm_runtime_put_sync(sci_port->port.dev);
60923241d43SPaul Mundt }
61023241d43SPaul Mundt 
port_rx_irq_mask(struct uart_port * port)611e1910fcdSGeert Uytterhoeven static inline unsigned long port_rx_irq_mask(struct uart_port *port)
612e1910fcdSGeert Uytterhoeven {
613e1910fcdSGeert Uytterhoeven 	/*
614e1910fcdSGeert Uytterhoeven 	 * Not all ports (such as SCIFA) will support REIE. Rather than
615e1910fcdSGeert Uytterhoeven 	 * special-casing the port type, we check the port initialization
616e1910fcdSGeert Uytterhoeven 	 * IRQ enable mask to see whether the IRQ is desired at all. If
617e1910fcdSGeert Uytterhoeven 	 * it's unset, it's logically inferred that there's no point in
618e1910fcdSGeert Uytterhoeven 	 * testing for it.
619e1910fcdSGeert Uytterhoeven 	 */
620e1910fcdSGeert Uytterhoeven 	return SCSCR_RIE | (to_sci_port(port)->cfg->scscr & SCSCR_REIE);
621e1910fcdSGeert Uytterhoeven }
622e1910fcdSGeert Uytterhoeven 
sci_start_tx(struct uart_port * port)623e1910fcdSGeert Uytterhoeven static void sci_start_tx(struct uart_port *port)
624e1910fcdSGeert Uytterhoeven {
625e1910fcdSGeert Uytterhoeven 	struct sci_port *s = to_sci_port(port);
626e1910fcdSGeert Uytterhoeven 	unsigned short ctrl;
627e1910fcdSGeert Uytterhoeven 
628e1910fcdSGeert Uytterhoeven #ifdef CONFIG_SERIAL_SH_SCI_DMA
629e1910fcdSGeert Uytterhoeven 	if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
6306deab514SGeert Uytterhoeven 		u16 new, scr = sci_serial_in(port, SCSCR);
631e1910fcdSGeert Uytterhoeven 		if (s->chan_tx)
632e1910fcdSGeert Uytterhoeven 			new = scr | SCSCR_TDRQE;
633e1910fcdSGeert Uytterhoeven 		else
634e1910fcdSGeert Uytterhoeven 			new = scr & ~SCSCR_TDRQE;
635e1910fcdSGeert Uytterhoeven 		if (new != scr)
6366deab514SGeert Uytterhoeven 			sci_serial_out(port, SCSCR, new);
637e1910fcdSGeert Uytterhoeven 	}
638e1910fcdSGeert Uytterhoeven 
6391788cf6aSJiri Slaby (SUSE) 	if (s->chan_tx && !kfifo_is_empty(&port->state->port.xmit_fifo) &&
640e1910fcdSGeert Uytterhoeven 	    dma_submit_error(s->cookie_tx)) {
6418749061bSBiju Das 		if (s->cfg->regtype == SCIx_RZ_SCIFA_REGTYPE)
6428749061bSBiju Das 			/* Switch irq from SCIF to DMA */
64357c984f6SBiju Das 			disable_irq_nosync(s->irqs[SCIx_TXI_IRQ]);
6448749061bSBiju Das 
645e1910fcdSGeert Uytterhoeven 		s->cookie_tx = 0;
646e1910fcdSGeert Uytterhoeven 		schedule_work(&s->work_tx);
647e1910fcdSGeert Uytterhoeven 	}
648e1910fcdSGeert Uytterhoeven #endif
649e1910fcdSGeert Uytterhoeven 
6508749061bSBiju Das 	if (!s->chan_tx || s->cfg->regtype == SCIx_RZ_SCIFA_REGTYPE ||
6518749061bSBiju Das 	    port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
652e1910fcdSGeert Uytterhoeven 		/* Set TIE (Transmit Interrupt Enable) bit in SCSCR */
6536deab514SGeert Uytterhoeven 		ctrl = sci_serial_in(port, SCSCR);
6541707ce2dSBiju Das 
6551707ce2dSBiju Das 		/*
6561707ce2dSBiju Das 		 * For SCI, TE (transmit enable) must be set after setting TIE
6571707ce2dSBiju Das 		 * (transmit interrupt enable) or in the same instruction to start
6581707ce2dSBiju Das 		 * the transmit process.
6591707ce2dSBiju Das 		 */
6601707ce2dSBiju Das 		if (port->type == PORT_SCI)
6611707ce2dSBiju Das 			ctrl |= SCSCR_TE;
6621707ce2dSBiju Das 
6636deab514SGeert Uytterhoeven 		sci_serial_out(port, SCSCR, ctrl | SCSCR_TIE);
664e1910fcdSGeert Uytterhoeven 	}
665e1910fcdSGeert Uytterhoeven }
666e1910fcdSGeert Uytterhoeven 
sci_stop_tx(struct uart_port * port)667e1910fcdSGeert Uytterhoeven static void sci_stop_tx(struct uart_port *port)
668e1910fcdSGeert Uytterhoeven {
669e1910fcdSGeert Uytterhoeven 	unsigned short ctrl;
670e1910fcdSGeert Uytterhoeven 
671e1910fcdSGeert Uytterhoeven 	/* Clear TIE (Transmit Interrupt Enable) bit in SCSCR */
6726deab514SGeert Uytterhoeven 	ctrl = sci_serial_in(port, SCSCR);
673e1910fcdSGeert Uytterhoeven 
674e1910fcdSGeert Uytterhoeven 	if (port->type == PORT_SCIFA || port->type == PORT_SCIFB)
675e1910fcdSGeert Uytterhoeven 		ctrl &= ~SCSCR_TDRQE;
676e1910fcdSGeert Uytterhoeven 
677e1910fcdSGeert Uytterhoeven 	ctrl &= ~SCSCR_TIE;
678e1910fcdSGeert Uytterhoeven 
6796deab514SGeert Uytterhoeven 	sci_serial_out(port, SCSCR, ctrl);
68008a84410SYoshihiro Shimoda 
68108a84410SYoshihiro Shimoda #ifdef CONFIG_SERIAL_SH_SCI_DMA
68208a84410SYoshihiro Shimoda 	if (to_sci_port(port)->chan_tx &&
68308a84410SYoshihiro Shimoda 	    !dma_submit_error(to_sci_port(port)->cookie_tx)) {
68408a84410SYoshihiro Shimoda 		dmaengine_terminate_async(to_sci_port(port)->chan_tx);
68508a84410SYoshihiro Shimoda 		to_sci_port(port)->cookie_tx = -EINVAL;
68608a84410SYoshihiro Shimoda 	}
68708a84410SYoshihiro Shimoda #endif
688e1910fcdSGeert Uytterhoeven }
689e1910fcdSGeert Uytterhoeven 
sci_start_rx(struct uart_port * port)690e1910fcdSGeert Uytterhoeven static void sci_start_rx(struct uart_port *port)
691e1910fcdSGeert Uytterhoeven {
692e1910fcdSGeert Uytterhoeven 	unsigned short ctrl;
693e1910fcdSGeert Uytterhoeven 
6946deab514SGeert Uytterhoeven 	ctrl = sci_serial_in(port, SCSCR) | port_rx_irq_mask(port);
695e1910fcdSGeert Uytterhoeven 
696e1910fcdSGeert Uytterhoeven 	if (port->type == PORT_SCIFA || port->type == PORT_SCIFB)
697e1910fcdSGeert Uytterhoeven 		ctrl &= ~SCSCR_RDRQE;
698e1910fcdSGeert Uytterhoeven 
6996deab514SGeert Uytterhoeven 	sci_serial_out(port, SCSCR, ctrl);
700e1910fcdSGeert Uytterhoeven }
701e1910fcdSGeert Uytterhoeven 
sci_stop_rx(struct uart_port * port)702e1910fcdSGeert Uytterhoeven static void sci_stop_rx(struct uart_port *port)
703e1910fcdSGeert Uytterhoeven {
704e1910fcdSGeert Uytterhoeven 	unsigned short ctrl;
705e1910fcdSGeert Uytterhoeven 
7066deab514SGeert Uytterhoeven 	ctrl = sci_serial_in(port, SCSCR);
707e1910fcdSGeert Uytterhoeven 
708e1910fcdSGeert Uytterhoeven 	if (port->type == PORT_SCIFA || port->type == PORT_SCIFB)
709e1910fcdSGeert Uytterhoeven 		ctrl &= ~SCSCR_RDRQE;
710e1910fcdSGeert Uytterhoeven 
711e1910fcdSGeert Uytterhoeven 	ctrl &= ~port_rx_irq_mask(port);
712e1910fcdSGeert Uytterhoeven 
7136deab514SGeert Uytterhoeven 	sci_serial_out(port, SCSCR, ctrl);
714e1910fcdSGeert Uytterhoeven }
715e1910fcdSGeert Uytterhoeven 
sci_clear_SCxSR(struct uart_port * port,unsigned int mask)716a1b5b43fSGeert Uytterhoeven static void sci_clear_SCxSR(struct uart_port *port, unsigned int mask)
717a1b5b43fSGeert Uytterhoeven {
718a1b5b43fSGeert Uytterhoeven 	if (port->type == PORT_SCI) {
719a1b5b43fSGeert Uytterhoeven 		/* Just store the mask */
7206deab514SGeert Uytterhoeven 		sci_serial_out(port, SCxSR, mask);
721b2f20ed9SLaurent Pinchart 	} else if (to_sci_port(port)->params->overrun_mask == SCIFA_ORER) {
722a1b5b43fSGeert Uytterhoeven 		/* SCIFA/SCIFB and SCIF on SH7705/SH7720/SH7721 */
723a1b5b43fSGeert Uytterhoeven 		/* Only clear the status bits we want to clear */
7246deab514SGeert Uytterhoeven 		sci_serial_out(port, SCxSR, sci_serial_in(port, SCxSR) & mask);
725a1b5b43fSGeert Uytterhoeven 	} else {
726a1b5b43fSGeert Uytterhoeven 		/* Store the mask, clear parity/framing errors */
7276deab514SGeert Uytterhoeven 		sci_serial_out(port, SCxSR, mask & ~(SCIF_FERC | SCIF_PERC));
728a1b5b43fSGeert Uytterhoeven 	}
729a1b5b43fSGeert Uytterhoeven }
730a1b5b43fSGeert Uytterhoeven 
7310b0cced1SYoshinori Sato #if defined(CONFIG_CONSOLE_POLL) || defined(CONFIG_SERIAL_SH_SCI_CONSOLE) || \
7320b0cced1SYoshinori Sato     defined(CONFIG_SERIAL_SH_SCI_EARLYCON)
733ab4382d2SGreg Kroah-Hartman 
734ab4382d2SGreg Kroah-Hartman #ifdef CONFIG_CONSOLE_POLL
sci_poll_get_char(struct uart_port * port)735ab4382d2SGreg Kroah-Hartman static int sci_poll_get_char(struct uart_port *port)
736ab4382d2SGreg Kroah-Hartman {
737ab4382d2SGreg Kroah-Hartman 	unsigned short status;
738ab4382d2SGreg Kroah-Hartman 	int c;
739ab4382d2SGreg Kroah-Hartman 
740ab4382d2SGreg Kroah-Hartman 	do {
7416deab514SGeert Uytterhoeven 		status = sci_serial_in(port, SCxSR);
742ab4382d2SGreg Kroah-Hartman 		if (status & SCxSR_ERRORS(port)) {
743a1b5b43fSGeert Uytterhoeven 			sci_clear_SCxSR(port, SCxSR_ERROR_CLEAR(port));
744ab4382d2SGreg Kroah-Hartman 			continue;
745ab4382d2SGreg Kroah-Hartman 		}
746ab4382d2SGreg Kroah-Hartman 		break;
747ab4382d2SGreg Kroah-Hartman 	} while (1);
748ab4382d2SGreg Kroah-Hartman 
749ab4382d2SGreg Kroah-Hartman 	if (!(status & SCxSR_RDxF(port)))
750ab4382d2SGreg Kroah-Hartman 		return NO_POLL_CHAR;
751ab4382d2SGreg Kroah-Hartman 
7526deab514SGeert Uytterhoeven 	c = sci_serial_in(port, SCxRDR);
753ab4382d2SGreg Kroah-Hartman 
754ab4382d2SGreg Kroah-Hartman 	/* Dummy read */
7556deab514SGeert Uytterhoeven 	sci_serial_in(port, SCxSR);
756a1b5b43fSGeert Uytterhoeven 	sci_clear_SCxSR(port, SCxSR_RDxF_CLEAR(port));
757ab4382d2SGreg Kroah-Hartman 
758ab4382d2SGreg Kroah-Hartman 	return c;
759ab4382d2SGreg Kroah-Hartman }
760ab4382d2SGreg Kroah-Hartman #endif
761ab4382d2SGreg Kroah-Hartman 
sci_poll_put_char(struct uart_port * port,unsigned char c)762ab4382d2SGreg Kroah-Hartman static void sci_poll_put_char(struct uart_port *port, unsigned char c)
763ab4382d2SGreg Kroah-Hartman {
764ab4382d2SGreg Kroah-Hartman 	unsigned short status;
765ab4382d2SGreg Kroah-Hartman 
766ab4382d2SGreg Kroah-Hartman 	do {
7676deab514SGeert Uytterhoeven 		status = sci_serial_in(port, SCxSR);
768ab4382d2SGreg Kroah-Hartman 	} while (!(status & SCxSR_TDxE(port)));
769ab4382d2SGreg Kroah-Hartman 
7706deab514SGeert Uytterhoeven 	sci_serial_out(port, SCxTDR, c);
771a1b5b43fSGeert Uytterhoeven 	sci_clear_SCxSR(port, SCxSR_TDxE_CLEAR(port) & ~SCxSR_TEND(port));
772ab4382d2SGreg Kroah-Hartman }
7730b0cced1SYoshinori Sato #endif /* CONFIG_CONSOLE_POLL || CONFIG_SERIAL_SH_SCI_CONSOLE ||
7740b0cced1SYoshinori Sato 	  CONFIG_SERIAL_SH_SCI_EARLYCON */
775ab4382d2SGreg Kroah-Hartman 
sci_init_pins(struct uart_port * port,unsigned int cflag)77661a6976bSPaul Mundt static void sci_init_pins(struct uart_port *port, unsigned int cflag)
777ab4382d2SGreg Kroah-Hartman {
77861a6976bSPaul Mundt 	struct sci_port *s = to_sci_port(port);
779ab4382d2SGreg Kroah-Hartman 
78061a6976bSPaul Mundt 	/*
78161a6976bSPaul Mundt 	 * Use port-specific handler if provided.
78261a6976bSPaul Mundt 	 */
78361a6976bSPaul Mundt 	if (s->cfg->ops && s->cfg->ops->init_pins) {
78461a6976bSPaul Mundt 		s->cfg->ops->init_pins(port, cflag);
78561a6976bSPaul Mundt 		return;
786ab4382d2SGreg Kroah-Hartman 	}
787ab4382d2SGreg Kroah-Hartman 
788e9d7a45aSGeert Uytterhoeven 	if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
7896deab514SGeert Uytterhoeven 		u16 data = sci_serial_in(port, SCPDR);
7906deab514SGeert Uytterhoeven 		u16 ctrl = sci_serial_in(port, SCPCR);
791e9d7a45aSGeert Uytterhoeven 
792e9d7a45aSGeert Uytterhoeven 		/* Enable RXD and TXD pin functions */
793e9d7a45aSGeert Uytterhoeven 		ctrl &= ~(SCPCR_RXDC | SCPCR_TXDC);
79497ed9790SLaurent Pinchart 		if (to_sci_port(port)->has_rtscts) {
795cfa6eb23SGeert Uytterhoeven 			/* RTS# is output, active low, unless autorts */
796cfa6eb23SGeert Uytterhoeven 			if (!(port->mctrl & TIOCM_RTS)) {
797e9d7a45aSGeert Uytterhoeven 				ctrl |= SCPCR_RTSC;
798cfa6eb23SGeert Uytterhoeven 				data |= SCPDR_RTSD;
799cfa6eb23SGeert Uytterhoeven 			} else if (!s->autorts) {
800cfa6eb23SGeert Uytterhoeven 				ctrl |= SCPCR_RTSC;
801cfa6eb23SGeert Uytterhoeven 				data &= ~SCPDR_RTSD;
802cfa6eb23SGeert Uytterhoeven 			} else {
803cfa6eb23SGeert Uytterhoeven 				/* Enable RTS# pin function */
804cfa6eb23SGeert Uytterhoeven 				ctrl &= ~SCPCR_RTSC;
805cfa6eb23SGeert Uytterhoeven 			}
806e9d7a45aSGeert Uytterhoeven 			/* Enable CTS# pin function */
807e9d7a45aSGeert Uytterhoeven 			ctrl &= ~SCPCR_CTSC;
808e9d7a45aSGeert Uytterhoeven 		}
8096deab514SGeert Uytterhoeven 		sci_serial_out(port, SCPDR, data);
8106deab514SGeert Uytterhoeven 		sci_serial_out(port, SCPCR, ctrl);
8112f50304eSLad Prabhakar 	} else if (sci_getreg(port, SCSPTR)->size && s->cfg->regtype != SCIx_RZV2H_SCIF_REGTYPE) {
8126deab514SGeert Uytterhoeven 		u16 status = sci_serial_in(port, SCSPTR);
813ab4382d2SGreg Kroah-Hartman 
814cfa6eb23SGeert Uytterhoeven 		/* RTS# is always output; and active low, unless autorts */
815cfa6eb23SGeert Uytterhoeven 		status |= SCSPTR_RTSIO;
816cfa6eb23SGeert Uytterhoeven 		if (!(port->mctrl & TIOCM_RTS))
817cfa6eb23SGeert Uytterhoeven 			status |= SCSPTR_RTSDT;
818cfa6eb23SGeert Uytterhoeven 		else if (!s->autorts)
819cfa6eb23SGeert Uytterhoeven 			status &= ~SCSPTR_RTSDT;
820d2b9775dSGeert Uytterhoeven 		/* CTS# and SCK are inputs */
821d2b9775dSGeert Uytterhoeven 		status &= ~(SCSPTR_CTSIO | SCSPTR_SCKIO);
8226deab514SGeert Uytterhoeven 		sci_serial_out(port, SCSPTR, status);
823faf02f8fSPaul Mundt 	}
824ab4382d2SGreg Kroah-Hartman }
825ab4382d2SGreg Kroah-Hartman 
sci_txfill(struct uart_port * port)826ab4382d2SGreg Kroah-Hartman static int sci_txfill(struct uart_port *port)
827ab4382d2SGreg Kroah-Hartman {
828b2f20ed9SLaurent Pinchart 	struct sci_port *s = to_sci_port(port);
829b2f20ed9SLaurent Pinchart 	unsigned int fifo_mask = (s->params->fifosize << 1) - 1;
830d3184e68SGeert Uytterhoeven 	const struct plat_sci_reg *reg;
83172b294cfSPaul Mundt 
83272b294cfSPaul Mundt 	reg = sci_getreg(port, SCTFDR);
83372b294cfSPaul Mundt 	if (reg->size)
8346deab514SGeert Uytterhoeven 		return sci_serial_in(port, SCTFDR) & fifo_mask;
83572b294cfSPaul Mundt 
83672b294cfSPaul Mundt 	reg = sci_getreg(port, SCFDR);
83772b294cfSPaul Mundt 	if (reg->size)
8386deab514SGeert Uytterhoeven 		return sci_serial_in(port, SCFDR) >> 8;
83972b294cfSPaul Mundt 
8406deab514SGeert Uytterhoeven 	return !(sci_serial_in(port, SCxSR) & SCI_TDRE);
841ab4382d2SGreg Kroah-Hartman }
842ab4382d2SGreg Kroah-Hartman 
sci_txroom(struct uart_port * port)843ab4382d2SGreg Kroah-Hartman static int sci_txroom(struct uart_port *port)
844ab4382d2SGreg Kroah-Hartman {
84572b294cfSPaul Mundt 	return port->fifosize - sci_txfill(port);
846ab4382d2SGreg Kroah-Hartman }
847ab4382d2SGreg Kroah-Hartman 
sci_rxfill(struct uart_port * port)848ab4382d2SGreg Kroah-Hartman static int sci_rxfill(struct uart_port *port)
849ab4382d2SGreg Kroah-Hartman {
850b2f20ed9SLaurent Pinchart 	struct sci_port *s = to_sci_port(port);
851b2f20ed9SLaurent Pinchart 	unsigned int fifo_mask = (s->params->fifosize << 1) - 1;
852d3184e68SGeert Uytterhoeven 	const struct plat_sci_reg *reg;
85372b294cfSPaul Mundt 
85472b294cfSPaul Mundt 	reg = sci_getreg(port, SCRFDR);
85572b294cfSPaul Mundt 	if (reg->size)
8566deab514SGeert Uytterhoeven 		return sci_serial_in(port, SCRFDR) & fifo_mask;
85772b294cfSPaul Mundt 
85872b294cfSPaul Mundt 	reg = sci_getreg(port, SCFDR);
85972b294cfSPaul Mundt 	if (reg->size)
8606deab514SGeert Uytterhoeven 		return sci_serial_in(port, SCFDR) & fifo_mask;
86172b294cfSPaul Mundt 
8626deab514SGeert Uytterhoeven 	return (sci_serial_in(port, SCxSR) & SCxSR_RDxF(port)) != 0;
863ab4382d2SGreg Kroah-Hartman }
864ab4382d2SGreg Kroah-Hartman 
865ab4382d2SGreg Kroah-Hartman /* ********************************************************************** *
866ab4382d2SGreg Kroah-Hartman  *                   the interrupt related routines                       *
867ab4382d2SGreg Kroah-Hartman  * ********************************************************************** */
868ab4382d2SGreg Kroah-Hartman 
sci_transmit_chars(struct uart_port * port)869ab4382d2SGreg Kroah-Hartman static void sci_transmit_chars(struct uart_port *port)
870ab4382d2SGreg Kroah-Hartman {
8711788cf6aSJiri Slaby (SUSE) 	struct tty_port *tport = &port->state->port;
872ab4382d2SGreg Kroah-Hartman 	unsigned int stopped = uart_tx_stopped(port);
8737cc0e0a4SClaudiu Beznea 	struct sci_port *s = to_sci_port(port);
874ab4382d2SGreg Kroah-Hartman 	unsigned short status;
875ab4382d2SGreg Kroah-Hartman 	unsigned short ctrl;
876ab4382d2SGreg Kroah-Hartman 	int count;
877ab4382d2SGreg Kroah-Hartman 
8786deab514SGeert Uytterhoeven 	status = sci_serial_in(port, SCxSR);
879ab4382d2SGreg Kroah-Hartman 	if (!(status & SCxSR_TDxE(port))) {
8806deab514SGeert Uytterhoeven 		ctrl = sci_serial_in(port, SCSCR);
8811788cf6aSJiri Slaby (SUSE) 		if (kfifo_is_empty(&tport->xmit_fifo))
882fc887b15SLinus Torvalds 			ctrl &= ~SCSCR_TIE;
883ab4382d2SGreg Kroah-Hartman 		else
884fc887b15SLinus Torvalds 			ctrl |= SCSCR_TIE;
8856deab514SGeert Uytterhoeven 		sci_serial_out(port, SCSCR, ctrl);
886ab4382d2SGreg Kroah-Hartman 		return;
887ab4382d2SGreg Kroah-Hartman 	}
888ab4382d2SGreg Kroah-Hartman 
889ab4382d2SGreg Kroah-Hartman 	count = sci_txroom(port);
890ab4382d2SGreg Kroah-Hartman 
891ab4382d2SGreg Kroah-Hartman 	do {
892ab4382d2SGreg Kroah-Hartman 		unsigned char c;
893ab4382d2SGreg Kroah-Hartman 
894ab4382d2SGreg Kroah-Hartman 		if (port->x_char) {
895ab4382d2SGreg Kroah-Hartman 			c = port->x_char;
896ab4382d2SGreg Kroah-Hartman 			port->x_char = 0;
8971788cf6aSJiri Slaby (SUSE) 		} else if (stopped || !kfifo_get(&tport->xmit_fifo, &c)) {
8981788cf6aSJiri Slaby (SUSE) 			if (port->type == PORT_SCI &&
8991788cf6aSJiri Slaby (SUSE) 				   kfifo_is_empty(&tport->xmit_fifo)) {
9006deab514SGeert Uytterhoeven 				ctrl = sci_serial_in(port, SCSCR);
901f06c2a90SBiju Das 				ctrl &= ~SCSCR_TE;
9026deab514SGeert Uytterhoeven 				sci_serial_out(port, SCSCR, ctrl);
903f06c2a90SBiju Das 				return;
9041788cf6aSJiri Slaby (SUSE) 			}
905ab4382d2SGreg Kroah-Hartman 			break;
906ab4382d2SGreg Kroah-Hartman 		}
907ab4382d2SGreg Kroah-Hartman 
9086deab514SGeert Uytterhoeven 		sci_serial_out(port, SCxTDR, c);
9097cc0e0a4SClaudiu Beznea 		s->tx_occurred = true;
910ab4382d2SGreg Kroah-Hartman 
911ab4382d2SGreg Kroah-Hartman 		port->icount.tx++;
912ab4382d2SGreg Kroah-Hartman 	} while (--count > 0);
913ab4382d2SGreg Kroah-Hartman 
914a1b5b43fSGeert Uytterhoeven 	sci_clear_SCxSR(port, SCxSR_TDxE_CLEAR(port));
915ab4382d2SGreg Kroah-Hartman 
9161788cf6aSJiri Slaby (SUSE) 	if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
917ab4382d2SGreg Kroah-Hartman 		uart_write_wakeup(port);
9181788cf6aSJiri Slaby (SUSE) 	if (kfifo_is_empty(&tport->xmit_fifo)) {
919d61ae331SBiju Das 		if (port->type == PORT_SCI) {
9206deab514SGeert Uytterhoeven 			ctrl = sci_serial_in(port, SCSCR);
921d61ae331SBiju Das 			ctrl &= ~SCSCR_TIE;
922d61ae331SBiju Das 			ctrl |= SCSCR_TEIE;
9236deab514SGeert Uytterhoeven 			sci_serial_out(port, SCSCR, ctrl);
924d61ae331SBiju Das 		}
925ab4382d2SGreg Kroah-Hartman 
926d61ae331SBiju Das 		sci_stop_tx(port);
927d61ae331SBiju Das 	}
928ab4382d2SGreg Kroah-Hartman }
929ab4382d2SGreg Kroah-Hartman 
sci_receive_chars(struct uart_port * port)9306b620478SPaul Mundt static void sci_receive_chars(struct uart_port *port)
931ab4382d2SGreg Kroah-Hartman {
932227434f8SJiri Slaby 	struct tty_port *tport = &port->state->port;
933ab4382d2SGreg Kroah-Hartman 	int i, count, copied = 0;
934ab4382d2SGreg Kroah-Hartman 	unsigned short status;
935ab4382d2SGreg Kroah-Hartman 	unsigned char flag;
936ab4382d2SGreg Kroah-Hartman 
9376deab514SGeert Uytterhoeven 	status = sci_serial_in(port, SCxSR);
938ab4382d2SGreg Kroah-Hartman 	if (!(status & SCxSR_RDxF(port)))
939ab4382d2SGreg Kroah-Hartman 		return;
940ab4382d2SGreg Kroah-Hartman 
941ab4382d2SGreg Kroah-Hartman 	while (1) {
942ab4382d2SGreg Kroah-Hartman 		/* Don't copy more bytes than there is room for in the buffer */
943227434f8SJiri Slaby 		count = tty_buffer_request_room(tport, sci_rxfill(port));
944ab4382d2SGreg Kroah-Hartman 
945ab4382d2SGreg Kroah-Hartman 		/* If for any reason we can't copy more data, we're done! */
946ab4382d2SGreg Kroah-Hartman 		if (count == 0)
947ab4382d2SGreg Kroah-Hartman 			break;
948ab4382d2SGreg Kroah-Hartman 
949ab4382d2SGreg Kroah-Hartman 		if (port->type == PORT_SCI) {
9506deab514SGeert Uytterhoeven 			char c = sci_serial_in(port, SCxRDR);
951d5cb1319SLaurent Pinchart 			if (uart_handle_sysrq_char(port, c))
952ab4382d2SGreg Kroah-Hartman 				count = 0;
953ab4382d2SGreg Kroah-Hartman 			else
95492a19f9cSJiri Slaby 				tty_insert_flip_char(tport, c, TTY_NORMAL);
955ab4382d2SGreg Kroah-Hartman 		} else {
956ab4382d2SGreg Kroah-Hartman 			for (i = 0; i < count; i++) {
9573dc4db36SKazuhiro Fujita 				char c;
958d97fbbedSPaul Mundt 
9593dc4db36SKazuhiro Fujita 				if (port->type == PORT_SCIF ||
9603dc4db36SKazuhiro Fujita 				    port->type == PORT_HSCIF) {
9616deab514SGeert Uytterhoeven 					status = sci_serial_in(port, SCxSR);
9626deab514SGeert Uytterhoeven 					c = sci_serial_in(port, SCxRDR);
9633dc4db36SKazuhiro Fujita 				} else {
9646deab514SGeert Uytterhoeven 					c = sci_serial_in(port, SCxRDR);
9656deab514SGeert Uytterhoeven 					status = sci_serial_in(port, SCxSR);
9663dc4db36SKazuhiro Fujita 				}
967ab4382d2SGreg Kroah-Hartman 				if (uart_handle_sysrq_char(port, c)) {
968ab4382d2SGreg Kroah-Hartman 					count--; i--;
969ab4382d2SGreg Kroah-Hartman 					continue;
970ab4382d2SGreg Kroah-Hartman 				}
971ab4382d2SGreg Kroah-Hartman 
972ab4382d2SGreg Kroah-Hartman 				/* Store data and status */
973ab4382d2SGreg Kroah-Hartman 				if (status & SCxSR_FER(port)) {
974ab4382d2SGreg Kroah-Hartman 					flag = TTY_FRAME;
975d97fbbedSPaul Mundt 					port->icount.frame++;
976ab4382d2SGreg Kroah-Hartman 				} else if (status & SCxSR_PER(port)) {
977ab4382d2SGreg Kroah-Hartman 					flag = TTY_PARITY;
978d97fbbedSPaul Mundt 					port->icount.parity++;
979ab4382d2SGreg Kroah-Hartman 				} else
980ab4382d2SGreg Kroah-Hartman 					flag = TTY_NORMAL;
981ab4382d2SGreg Kroah-Hartman 
98292a19f9cSJiri Slaby 				tty_insert_flip_char(tport, c, flag);
983ab4382d2SGreg Kroah-Hartman 			}
984ab4382d2SGreg Kroah-Hartman 		}
985ab4382d2SGreg Kroah-Hartman 
9866deab514SGeert Uytterhoeven 		sci_serial_in(port, SCxSR); /* dummy read */
987a1b5b43fSGeert Uytterhoeven 		sci_clear_SCxSR(port, SCxSR_RDxF_CLEAR(port));
988ab4382d2SGreg Kroah-Hartman 
989ab4382d2SGreg Kroah-Hartman 		copied += count;
990ab4382d2SGreg Kroah-Hartman 		port->icount.rx += count;
991ab4382d2SGreg Kroah-Hartman 	}
992ab4382d2SGreg Kroah-Hartman 
993ab4382d2SGreg Kroah-Hartman 	if (copied) {
994ab4382d2SGreg Kroah-Hartman 		/* Tell the rest of the system the news. New characters! */
9952e124b4aSJiri Slaby 		tty_flip_buffer_push(tport);
996ab4382d2SGreg Kroah-Hartman 	} else {
9977842055bSUlrich Hecht 		/* TTY buffers full; read from RX reg to prevent lockup */
9986deab514SGeert Uytterhoeven 		sci_serial_in(port, SCxRDR);
9996deab514SGeert Uytterhoeven 		sci_serial_in(port, SCxSR); /* dummy read */
1000a1b5b43fSGeert Uytterhoeven 		sci_clear_SCxSR(port, SCxSR_RDxF_CLEAR(port));
1001ab4382d2SGreg Kroah-Hartman 	}
1002ab4382d2SGreg Kroah-Hartman }
1003ab4382d2SGreg Kroah-Hartman 
sci_handle_errors(struct uart_port * port)10046b620478SPaul Mundt static int sci_handle_errors(struct uart_port *port)
1005ab4382d2SGreg Kroah-Hartman {
1006ab4382d2SGreg Kroah-Hartman 	int copied = 0;
10076deab514SGeert Uytterhoeven 	unsigned short status = sci_serial_in(port, SCxSR);
100892a19f9cSJiri Slaby 	struct tty_port *tport = &port->state->port;
1009debf9507SPaul Mundt 	struct sci_port *s = to_sci_port(port);
1010ab4382d2SGreg Kroah-Hartman 
10113ae988d9SLaurent Pinchart 	/* Handle overruns */
1012b2f20ed9SLaurent Pinchart 	if (status & s->params->overrun_mask) {
1013d97fbbedSPaul Mundt 		port->icount.overrun++;
1014d97fbbedSPaul Mundt 
1015ab4382d2SGreg Kroah-Hartman 		/* overrun error */
101692a19f9cSJiri Slaby 		if (tty_insert_flip_char(tport, 0, TTY_OVERRUN))
1017ab4382d2SGreg Kroah-Hartman 			copied++;
1018ab4382d2SGreg Kroah-Hartman 	}
1019ab4382d2SGreg Kroah-Hartman 
1020ab4382d2SGreg Kroah-Hartman 	if (status & SCxSR_FER(port)) {
1021ab4382d2SGreg Kroah-Hartman 		/* frame error */
1022d97fbbedSPaul Mundt 		port->icount.frame++;
1023d97fbbedSPaul Mundt 
102492a19f9cSJiri Slaby 		if (tty_insert_flip_char(tport, 0, TTY_FRAME))
1025ab4382d2SGreg Kroah-Hartman 			copied++;
1026ab4382d2SGreg Kroah-Hartman 	}
1027ab4382d2SGreg Kroah-Hartman 
1028ab4382d2SGreg Kroah-Hartman 	if (status & SCxSR_PER(port)) {
1029ab4382d2SGreg Kroah-Hartman 		/* parity error */
1030d97fbbedSPaul Mundt 		port->icount.parity++;
1031d97fbbedSPaul Mundt 
103292a19f9cSJiri Slaby 		if (tty_insert_flip_char(tport, 0, TTY_PARITY))
1033ab4382d2SGreg Kroah-Hartman 			copied++;
1034ab4382d2SGreg Kroah-Hartman 	}
1035ab4382d2SGreg Kroah-Hartman 
1036ab4382d2SGreg Kroah-Hartman 	if (copied)
10372e124b4aSJiri Slaby 		tty_flip_buffer_push(tport);
1038ab4382d2SGreg Kroah-Hartman 
1039ab4382d2SGreg Kroah-Hartman 	return copied;
1040ab4382d2SGreg Kroah-Hartman }
1041ab4382d2SGreg Kroah-Hartman 
sci_handle_fifo_overrun(struct uart_port * port)10426b620478SPaul Mundt static int sci_handle_fifo_overrun(struct uart_port *port)
1043ab4382d2SGreg Kroah-Hartman {
104492a19f9cSJiri Slaby 	struct tty_port *tport = &port->state->port;
1045debf9507SPaul Mundt 	struct sci_port *s = to_sci_port(port);
1046d3184e68SGeert Uytterhoeven 	const struct plat_sci_reg *reg;
10472e0842a1SGeert Uytterhoeven 	int copied = 0;
104875c249fdSGeert Uytterhoeven 	u16 status;
1049ab4382d2SGreg Kroah-Hartman 
1050b2f20ed9SLaurent Pinchart 	reg = sci_getreg(port, s->params->overrun_reg);
10514b8c59a3SPaul Mundt 	if (!reg->size)
1052ab4382d2SGreg Kroah-Hartman 		return 0;
1053ab4382d2SGreg Kroah-Hartman 
10546deab514SGeert Uytterhoeven 	status = sci_serial_in(port, s->params->overrun_reg);
1055b2f20ed9SLaurent Pinchart 	if (status & s->params->overrun_mask) {
1056b2f20ed9SLaurent Pinchart 		status &= ~s->params->overrun_mask;
10576deab514SGeert Uytterhoeven 		sci_serial_out(port, s->params->overrun_reg, status);
1058ab4382d2SGreg Kroah-Hartman 
1059d97fbbedSPaul Mundt 		port->icount.overrun++;
1060d97fbbedSPaul Mundt 
106192a19f9cSJiri Slaby 		tty_insert_flip_char(tport, 0, TTY_OVERRUN);
10622e124b4aSJiri Slaby 		tty_flip_buffer_push(tport);
1063ab4382d2SGreg Kroah-Hartman 		copied++;
1064ab4382d2SGreg Kroah-Hartman 	}
1065ab4382d2SGreg Kroah-Hartman 
1066ab4382d2SGreg Kroah-Hartman 	return copied;
1067ab4382d2SGreg Kroah-Hartman }
1068ab4382d2SGreg Kroah-Hartman 
sci_handle_breaks(struct uart_port * port)10696b620478SPaul Mundt static int sci_handle_breaks(struct uart_port *port)
1070ab4382d2SGreg Kroah-Hartman {
1071ab4382d2SGreg Kroah-Hartman 	int copied = 0;
10726deab514SGeert Uytterhoeven 	unsigned short status = sci_serial_in(port, SCxSR);
107392a19f9cSJiri Slaby 	struct tty_port *tport = &port->state->port;
1074ab4382d2SGreg Kroah-Hartman 
1075ab4382d2SGreg Kroah-Hartman 	if (uart_handle_break(port))
1076ab4382d2SGreg Kroah-Hartman 		return 0;
1077ab4382d2SGreg Kroah-Hartman 
1078d5cb1319SLaurent Pinchart 	if (status & SCxSR_BRK(port)) {
1079d97fbbedSPaul Mundt 		port->icount.brk++;
1080d97fbbedSPaul Mundt 
1081ab4382d2SGreg Kroah-Hartman 		/* Notify of BREAK */
108292a19f9cSJiri Slaby 		if (tty_insert_flip_char(tport, 0, TTY_BREAK))
1083ab4382d2SGreg Kroah-Hartman 			copied++;
1084ab4382d2SGreg Kroah-Hartman 	}
1085ab4382d2SGreg Kroah-Hartman 
1086ab4382d2SGreg Kroah-Hartman 	if (copied)
10872e124b4aSJiri Slaby 		tty_flip_buffer_push(tport);
1088ab4382d2SGreg Kroah-Hartman 
1089ab4382d2SGreg Kroah-Hartman 	copied += sci_handle_fifo_overrun(port);
1090ab4382d2SGreg Kroah-Hartman 
1091ab4382d2SGreg Kroah-Hartman 	return copied;
1092ab4382d2SGreg Kroah-Hartman }
1093ab4382d2SGreg Kroah-Hartman 
scif_set_rtrg(struct uart_port * port,int rx_trig)1094a380ed46SUlrich Hecht static int scif_set_rtrg(struct uart_port *port, int rx_trig)
1095a380ed46SUlrich Hecht {
1096a380ed46SUlrich Hecht 	unsigned int bits;
1097a380ed46SUlrich Hecht 
10982ea2e019SGeert Uytterhoeven 	if (rx_trig >= port->fifosize)
10992ea2e019SGeert Uytterhoeven 		rx_trig = port->fifosize - 1;
1100a380ed46SUlrich Hecht 	if (rx_trig < 1)
1101a380ed46SUlrich Hecht 		rx_trig = 1;
1102a380ed46SUlrich Hecht 
1103a380ed46SUlrich Hecht 	/* HSCIF can be set to an arbitrary level. */
1104a380ed46SUlrich Hecht 	if (sci_getreg(port, HSRTRGR)->size) {
11056deab514SGeert Uytterhoeven 		sci_serial_out(port, HSRTRGR, rx_trig);
1106a380ed46SUlrich Hecht 		return rx_trig;
1107a380ed46SUlrich Hecht 	}
1108a380ed46SUlrich Hecht 
1109a380ed46SUlrich Hecht 	switch (port->type) {
1110a380ed46SUlrich Hecht 	case PORT_SCIF:
1111a380ed46SUlrich Hecht 		if (rx_trig < 4) {
1112a380ed46SUlrich Hecht 			bits = 0;
1113a380ed46SUlrich Hecht 			rx_trig = 1;
1114a380ed46SUlrich Hecht 		} else if (rx_trig < 8) {
1115a380ed46SUlrich Hecht 			bits = SCFCR_RTRG0;
1116a380ed46SUlrich Hecht 			rx_trig = 4;
1117a380ed46SUlrich Hecht 		} else if (rx_trig < 14) {
1118a380ed46SUlrich Hecht 			bits = SCFCR_RTRG1;
1119a380ed46SUlrich Hecht 			rx_trig = 8;
1120a380ed46SUlrich Hecht 		} else {
1121a380ed46SUlrich Hecht 			bits = SCFCR_RTRG0 | SCFCR_RTRG1;
1122a380ed46SUlrich Hecht 			rx_trig = 14;
1123a380ed46SUlrich Hecht 		}
1124a380ed46SUlrich Hecht 		break;
1125a380ed46SUlrich Hecht 	case PORT_SCIFA:
1126a380ed46SUlrich Hecht 	case PORT_SCIFB:
1127a380ed46SUlrich Hecht 		if (rx_trig < 16) {
1128a380ed46SUlrich Hecht 			bits = 0;
1129a380ed46SUlrich Hecht 			rx_trig = 1;
1130a380ed46SUlrich Hecht 		} else if (rx_trig < 32) {
1131a380ed46SUlrich Hecht 			bits = SCFCR_RTRG0;
1132a380ed46SUlrich Hecht 			rx_trig = 16;
1133a380ed46SUlrich Hecht 		} else if (rx_trig < 48) {
1134a380ed46SUlrich Hecht 			bits = SCFCR_RTRG1;
1135a380ed46SUlrich Hecht 			rx_trig = 32;
1136a380ed46SUlrich Hecht 		} else {
1137a380ed46SUlrich Hecht 			bits = SCFCR_RTRG0 | SCFCR_RTRG1;
1138a380ed46SUlrich Hecht 			rx_trig = 48;
1139a380ed46SUlrich Hecht 		}
1140a380ed46SUlrich Hecht 		break;
1141a380ed46SUlrich Hecht 	default:
1142a380ed46SUlrich Hecht 		WARN(1, "unknown FIFO configuration");
1143a380ed46SUlrich Hecht 		return 1;
1144a380ed46SUlrich Hecht 	}
1145a380ed46SUlrich Hecht 
11466deab514SGeert Uytterhoeven 	sci_serial_out(port, SCFCR,
11476deab514SGeert Uytterhoeven 		       (sci_serial_in(port, SCFCR) &
1148a380ed46SUlrich Hecht 			~(SCFCR_RTRG1 | SCFCR_RTRG0)) | bits);
1149a380ed46SUlrich Hecht 
1150a380ed46SUlrich Hecht 	return rx_trig;
1151a380ed46SUlrich Hecht }
1152a380ed46SUlrich Hecht 
scif_rtrg_enabled(struct uart_port * port)115303940376SUlrich Hecht static int scif_rtrg_enabled(struct uart_port *port)
115403940376SUlrich Hecht {
115503940376SUlrich Hecht 	if (sci_getreg(port, HSRTRGR)->size)
11566deab514SGeert Uytterhoeven 		return sci_serial_in(port, HSRTRGR) != 0;
115703940376SUlrich Hecht 	else
11586deab514SGeert Uytterhoeven 		return (sci_serial_in(port, SCFCR) &
115903940376SUlrich Hecht 			(SCFCR_RTRG0 | SCFCR_RTRG1)) != 0;
116003940376SUlrich Hecht }
116103940376SUlrich Hecht 
rx_fifo_timer_fn(struct timer_list * t)1162e99e88a9SKees Cook static void rx_fifo_timer_fn(struct timer_list *t)
116303940376SUlrich Hecht {
1164e99e88a9SKees Cook 	struct sci_port *s = from_timer(s, t, rx_fifo_timer);
116503940376SUlrich Hecht 	struct uart_port *port = &s->port;
116603940376SUlrich Hecht 
116703940376SUlrich Hecht 	dev_dbg(port->dev, "Rx timed out\n");
116803940376SUlrich Hecht 	scif_set_rtrg(port, 1);
116903940376SUlrich Hecht }
117003940376SUlrich Hecht 
rx_fifo_trigger_show(struct device * dev,struct device_attribute * attr,char * buf)11717027e62aSGeert Uytterhoeven static ssize_t rx_fifo_trigger_show(struct device *dev,
11727027e62aSGeert Uytterhoeven 				    struct device_attribute *attr, char *buf)
11735d23188aSUlrich Hecht {
11745d23188aSUlrich Hecht 	struct uart_port *port = dev_get_drvdata(dev);
11755d23188aSUlrich Hecht 	struct sci_port *sci = to_sci_port(port);
11765d23188aSUlrich Hecht 
11775d23188aSUlrich Hecht 	return sprintf(buf, "%d\n", sci->rx_trigger);
11785d23188aSUlrich Hecht }
11795d23188aSUlrich Hecht 
rx_fifo_trigger_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)11807027e62aSGeert Uytterhoeven static ssize_t rx_fifo_trigger_store(struct device *dev,
11815d23188aSUlrich Hecht 				     struct device_attribute *attr,
11827027e62aSGeert Uytterhoeven 				     const char *buf, size_t count)
11835d23188aSUlrich Hecht {
11845d23188aSUlrich Hecht 	struct uart_port *port = dev_get_drvdata(dev);
11855d23188aSUlrich Hecht 	struct sci_port *sci = to_sci_port(port);
11864ab3c51eSDan Carpenter 	int ret;
11875d23188aSUlrich Hecht 	long r;
11885d23188aSUlrich Hecht 
11894ab3c51eSDan Carpenter 	ret = kstrtol(buf, 0, &r);
11904ab3c51eSDan Carpenter 	if (ret)
11914ab3c51eSDan Carpenter 		return ret;
119290afa525SUlrich Hecht 
11935d23188aSUlrich Hecht 	sci->rx_trigger = scif_set_rtrg(port, r);
119490afa525SUlrich Hecht 	if (port->type == PORT_SCIFA || port->type == PORT_SCIFB)
11955d23188aSUlrich Hecht 		scif_set_rtrg(port, 1);
119690afa525SUlrich Hecht 
11975d23188aSUlrich Hecht 	return count;
11985d23188aSUlrich Hecht }
11995d23188aSUlrich Hecht 
12007027e62aSGeert Uytterhoeven static DEVICE_ATTR_RW(rx_fifo_trigger);
12015d23188aSUlrich Hecht 
rx_fifo_timeout_show(struct device * dev,struct device_attribute * attr,char * buf)12025d23188aSUlrich Hecht static ssize_t rx_fifo_timeout_show(struct device *dev,
12035d23188aSUlrich Hecht 			       struct device_attribute *attr,
12045d23188aSUlrich Hecht 			       char *buf)
12055d23188aSUlrich Hecht {
12065d23188aSUlrich Hecht 	struct uart_port *port = dev_get_drvdata(dev);
12075d23188aSUlrich Hecht 	struct sci_port *sci = to_sci_port(port);
1208fa2abb03SUlrich Hecht 	int v;
12095d23188aSUlrich Hecht 
1210fa2abb03SUlrich Hecht 	if (port->type == PORT_HSCIF)
1211fa2abb03SUlrich Hecht 		v = sci->hscif_tot >> HSSCR_TOT_SHIFT;
1212fa2abb03SUlrich Hecht 	else
1213fa2abb03SUlrich Hecht 		v = sci->rx_fifo_timeout;
1214fa2abb03SUlrich Hecht 
1215fa2abb03SUlrich Hecht 	return sprintf(buf, "%d\n", v);
12165d23188aSUlrich Hecht }
12175d23188aSUlrich Hecht 
rx_fifo_timeout_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)12185d23188aSUlrich Hecht static ssize_t rx_fifo_timeout_store(struct device *dev,
12195d23188aSUlrich Hecht 				struct device_attribute *attr,
12205d23188aSUlrich Hecht 				const char *buf,
12215d23188aSUlrich Hecht 				size_t count)
12225d23188aSUlrich Hecht {
12235d23188aSUlrich Hecht 	struct uart_port *port = dev_get_drvdata(dev);
12245d23188aSUlrich Hecht 	struct sci_port *sci = to_sci_port(port);
12254ab3c51eSDan Carpenter 	int ret;
12265d23188aSUlrich Hecht 	long r;
12275d23188aSUlrich Hecht 
12284ab3c51eSDan Carpenter 	ret = kstrtol(buf, 0, &r);
12294ab3c51eSDan Carpenter 	if (ret)
12304ab3c51eSDan Carpenter 		return ret;
1231fa2abb03SUlrich Hecht 
1232fa2abb03SUlrich Hecht 	if (port->type == PORT_HSCIF) {
1233fa2abb03SUlrich Hecht 		if (r < 0 || r > 3)
1234fa2abb03SUlrich Hecht 			return -EINVAL;
1235fa2abb03SUlrich Hecht 		sci->hscif_tot = r << HSSCR_TOT_SHIFT;
1236fa2abb03SUlrich Hecht 	} else {
12375d23188aSUlrich Hecht 		sci->rx_fifo_timeout = r;
12385d23188aSUlrich Hecht 		scif_set_rtrg(port, 1);
12395d23188aSUlrich Hecht 		if (r > 0)
1240e99e88a9SKees Cook 			timer_setup(&sci->rx_fifo_timer, rx_fifo_timer_fn, 0);
1241fa2abb03SUlrich Hecht 	}
1242fa2abb03SUlrich Hecht 
12435d23188aSUlrich Hecht 	return count;
12445d23188aSUlrich Hecht }
12455d23188aSUlrich Hecht 
1246b6b996b6SJoe Perches static DEVICE_ATTR_RW(rx_fifo_timeout);
12475d23188aSUlrich Hecht 
12485d23188aSUlrich Hecht 
1249e1910fcdSGeert Uytterhoeven #ifdef CONFIG_SERIAL_SH_SCI_DMA
sci_dma_tx_complete(void * arg)1250e1910fcdSGeert Uytterhoeven static void sci_dma_tx_complete(void *arg)
1251e1910fcdSGeert Uytterhoeven {
1252e1910fcdSGeert Uytterhoeven 	struct sci_port *s = arg;
1253e1910fcdSGeert Uytterhoeven 	struct uart_port *port = &s->port;
12541788cf6aSJiri Slaby (SUSE) 	struct tty_port *tport = &port->state->port;
1255e1910fcdSGeert Uytterhoeven 	unsigned long flags;
1256e1910fcdSGeert Uytterhoeven 
1257e1910fcdSGeert Uytterhoeven 	dev_dbg(port->dev, "%s(%d)\n", __func__, port->line);
1258e1910fcdSGeert Uytterhoeven 
125994c53770SThomas Gleixner 	uart_port_lock_irqsave(port, &flags);
1260e1910fcdSGeert Uytterhoeven 
1261e234ef0eSIlpo Järvinen 	uart_xmit_advance(port, s->tx_dma_len);
1262e1910fcdSGeert Uytterhoeven 
12631788cf6aSJiri Slaby (SUSE) 	if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
1264e1910fcdSGeert Uytterhoeven 		uart_write_wakeup(port);
1265e1910fcdSGeert Uytterhoeven 
12667cc0e0a4SClaudiu Beznea 	s->tx_occurred = true;
12677cc0e0a4SClaudiu Beznea 
12681788cf6aSJiri Slaby (SUSE) 	if (!kfifo_is_empty(&tport->xmit_fifo)) {
1269e1910fcdSGeert Uytterhoeven 		s->cookie_tx = 0;
1270e1910fcdSGeert Uytterhoeven 		schedule_work(&s->work_tx);
1271e1910fcdSGeert Uytterhoeven 	} else {
1272e1910fcdSGeert Uytterhoeven 		s->cookie_tx = -EINVAL;
12738749061bSBiju Das 		if (port->type == PORT_SCIFA || port->type == PORT_SCIFB ||
12748749061bSBiju Das 		    s->cfg->regtype == SCIx_RZ_SCIFA_REGTYPE) {
12756deab514SGeert Uytterhoeven 			u16 ctrl = sci_serial_in(port, SCSCR);
12766deab514SGeert Uytterhoeven 			sci_serial_out(port, SCSCR, ctrl & ~SCSCR_TIE);
12778749061bSBiju Das 			if (s->cfg->regtype == SCIx_RZ_SCIFA_REGTYPE) {
12788749061bSBiju Das 				/* Switch irq from DMA to SCIF */
12798749061bSBiju Das 				dmaengine_pause(s->chan_tx_saved);
12808749061bSBiju Das 				enable_irq(s->irqs[SCIx_TXI_IRQ]);
12818749061bSBiju Das 			}
1282e1910fcdSGeert Uytterhoeven 		}
1283e1910fcdSGeert Uytterhoeven 	}
1284e1910fcdSGeert Uytterhoeven 
128594c53770SThomas Gleixner 	uart_port_unlock_irqrestore(port, flags);
1286e1910fcdSGeert Uytterhoeven }
1287e1910fcdSGeert Uytterhoeven 
1288e1910fcdSGeert Uytterhoeven /* Locking: called with port lock held */
sci_dma_rx_push(struct sci_port * s,void * buf,size_t count)1289e1910fcdSGeert Uytterhoeven static int sci_dma_rx_push(struct sci_port *s, void *buf, size_t count)
1290e1910fcdSGeert Uytterhoeven {
1291e1910fcdSGeert Uytterhoeven 	struct uart_port *port = &s->port;
1292e1910fcdSGeert Uytterhoeven 	struct tty_port *tport = &port->state->port;
1293e1910fcdSGeert Uytterhoeven 	int copied;
1294e1910fcdSGeert Uytterhoeven 
1295e1910fcdSGeert Uytterhoeven 	copied = tty_insert_flip_string(tport, buf, count);
12966fc5a520STakatoshi Akiyama 	if (copied < count)
1297e1910fcdSGeert Uytterhoeven 		port->icount.buf_overrun++;
1298e1910fcdSGeert Uytterhoeven 
1299e1910fcdSGeert Uytterhoeven 	port->icount.rx += copied;
1300e1910fcdSGeert Uytterhoeven 
1301e1910fcdSGeert Uytterhoeven 	return copied;
1302e1910fcdSGeert Uytterhoeven }
1303e1910fcdSGeert Uytterhoeven 
sci_dma_rx_find_active(struct sci_port * s)1304e1910fcdSGeert Uytterhoeven static int sci_dma_rx_find_active(struct sci_port *s)
1305e1910fcdSGeert Uytterhoeven {
1306e1910fcdSGeert Uytterhoeven 	unsigned int i;
1307e1910fcdSGeert Uytterhoeven 
1308e1910fcdSGeert Uytterhoeven 	for (i = 0; i < ARRAY_SIZE(s->cookie_rx); i++)
1309e1910fcdSGeert Uytterhoeven 		if (s->active_rx == s->cookie_rx[i])
1310e1910fcdSGeert Uytterhoeven 			return i;
1311e1910fcdSGeert Uytterhoeven 
1312e1910fcdSGeert Uytterhoeven 	return -1;
1313e1910fcdSGeert Uytterhoeven }
1314e1910fcdSGeert Uytterhoeven 
13158efc4405SWolfram Sang /* Must only be called with uart_port_lock taken */
sci_dma_rx_chan_invalidate(struct sci_port * s)131611b3770dSGeert Uytterhoeven static void sci_dma_rx_chan_invalidate(struct sci_port *s)
131711b3770dSGeert Uytterhoeven {
131811b3770dSGeert Uytterhoeven 	unsigned int i;
131911b3770dSGeert Uytterhoeven 
132011b3770dSGeert Uytterhoeven 	s->chan_rx = NULL;
132111b3770dSGeert Uytterhoeven 	for (i = 0; i < ARRAY_SIZE(s->cookie_rx); i++)
132211b3770dSGeert Uytterhoeven 		s->cookie_rx[i] = -EINVAL;
132311b3770dSGeert Uytterhoeven 	s->active_rx = 0;
132411b3770dSGeert Uytterhoeven }
132511b3770dSGeert Uytterhoeven 
sci_dma_rx_release(struct sci_port * s)13268fcf7a65SGeert Uytterhoeven static void sci_dma_rx_release(struct sci_port *s)
1327e1910fcdSGeert Uytterhoeven {
13282c4ee235SGeert Uytterhoeven 	struct dma_chan *chan = s->chan_rx_saved;
1329aae20f6eSWolfram Sang 	struct uart_port *port = &s->port;
1330aae20f6eSWolfram Sang 	unsigned long flags;
1331e1910fcdSGeert Uytterhoeven 
1332aae20f6eSWolfram Sang 	uart_port_lock_irqsave(port, &flags);
133311b3770dSGeert Uytterhoeven 	s->chan_rx_saved = NULL;
133411b3770dSGeert Uytterhoeven 	sci_dma_rx_chan_invalidate(s);
1335aae20f6eSWolfram Sang 	uart_port_unlock_irqrestore(port, flags);
1336aae20f6eSWolfram Sang 
13376eefc68dSGeert Uytterhoeven 	dmaengine_terminate_sync(chan);
1338e1910fcdSGeert Uytterhoeven 	dma_free_coherent(chan->device->dev, s->buf_len_rx * 2, s->rx_buf[0],
1339e1910fcdSGeert Uytterhoeven 			  sg_dma_address(&s->sg_rx[0]));
1340e1910fcdSGeert Uytterhoeven 	dma_release_channel(chan);
1341e1910fcdSGeert Uytterhoeven }
1342e1910fcdSGeert Uytterhoeven 
start_hrtimer_us(struct hrtimer * hrt,unsigned long usec)1343b96408b4SUlrich Hecht static void start_hrtimer_us(struct hrtimer *hrt, unsigned long usec)
1344b96408b4SUlrich Hecht {
1345b96408b4SUlrich Hecht 	long sec = usec / 1000000;
1346b96408b4SUlrich Hecht 	long nsec = (usec % 1000000) * 1000;
1347b96408b4SUlrich Hecht 	ktime_t t = ktime_set(sec, nsec);
1348b96408b4SUlrich Hecht 
1349b96408b4SUlrich Hecht 	hrtimer_start(hrt, t, HRTIMER_MODE_REL);
1350b96408b4SUlrich Hecht }
1351b96408b4SUlrich Hecht 
sci_dma_rx_reenable_irq(struct sci_port * s)135238766e4bSGeert Uytterhoeven static void sci_dma_rx_reenable_irq(struct sci_port *s)
135338766e4bSGeert Uytterhoeven {
135438766e4bSGeert Uytterhoeven 	struct uart_port *port = &s->port;
135538766e4bSGeert Uytterhoeven 	u16 scr;
135638766e4bSGeert Uytterhoeven 
135738766e4bSGeert Uytterhoeven 	/* Direct new serial port interrupts back to CPU */
13586deab514SGeert Uytterhoeven 	scr = sci_serial_in(port, SCSCR);
1359cf383d12SBiju Das 	if (port->type == PORT_SCIFA || port->type == PORT_SCIFB ||
1360cf383d12SBiju Das 	    s->cfg->regtype == SCIx_RZ_SCIFA_REGTYPE) {
136138766e4bSGeert Uytterhoeven 		enable_irq(s->irqs[SCIx_RXI_IRQ]);
1362cf383d12SBiju Das 		if (s->cfg->regtype == SCIx_RZ_SCIFA_REGTYPE)
1363cf383d12SBiju Das 			scif_set_rtrg(port, s->rx_trigger);
1364cf383d12SBiju Das 		else
1365cf383d12SBiju Das 			scr &= ~SCSCR_RDRQE;
136638766e4bSGeert Uytterhoeven 	}
13676deab514SGeert Uytterhoeven 	sci_serial_out(port, SCSCR, scr | SCSCR_RIE);
136838766e4bSGeert Uytterhoeven }
136938766e4bSGeert Uytterhoeven 
sci_dma_rx_complete(void * arg)1370e1910fcdSGeert Uytterhoeven static void sci_dma_rx_complete(void *arg)
1371e1910fcdSGeert Uytterhoeven {
1372e1910fcdSGeert Uytterhoeven 	struct sci_port *s = arg;
13731d3db608SMuhammad Hamza Farooq 	struct dma_chan *chan = s->chan_rx;
1374e1910fcdSGeert Uytterhoeven 	struct uart_port *port = &s->port;
137567f462b0SGeert Uytterhoeven 	struct dma_async_tx_descriptor *desc;
1376e1910fcdSGeert Uytterhoeven 	unsigned long flags;
1377e1910fcdSGeert Uytterhoeven 	int active, count = 0;
1378e1910fcdSGeert Uytterhoeven 
1379e1910fcdSGeert Uytterhoeven 	dev_dbg(port->dev, "%s(%d) active cookie %d\n", __func__, port->line,
1380e1910fcdSGeert Uytterhoeven 		s->active_rx);
1381e1910fcdSGeert Uytterhoeven 
13820c9c1ea5SWolfram Sang 	hrtimer_cancel(&s->rx_timer);
13830c9c1ea5SWolfram Sang 
138494c53770SThomas Gleixner 	uart_port_lock_irqsave(port, &flags);
1385e1910fcdSGeert Uytterhoeven 
1386e1910fcdSGeert Uytterhoeven 	active = sci_dma_rx_find_active(s);
1387e1910fcdSGeert Uytterhoeven 	if (active >= 0)
1388e1910fcdSGeert Uytterhoeven 		count = sci_dma_rx_push(s, s->rx_buf[active], s->buf_len_rx);
1389e1910fcdSGeert Uytterhoeven 
1390e1910fcdSGeert Uytterhoeven 	if (count)
1391e1910fcdSGeert Uytterhoeven 		tty_flip_buffer_push(&port->state->port);
1392e1910fcdSGeert Uytterhoeven 
139367f462b0SGeert Uytterhoeven 	desc = dmaengine_prep_slave_sg(s->chan_rx, &s->sg_rx[active], 1,
139467f462b0SGeert Uytterhoeven 				       DMA_DEV_TO_MEM,
139567f462b0SGeert Uytterhoeven 				       DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
139667f462b0SGeert Uytterhoeven 	if (!desc)
139767f462b0SGeert Uytterhoeven 		goto fail;
139867f462b0SGeert Uytterhoeven 
139967f462b0SGeert Uytterhoeven 	desc->callback = sci_dma_rx_complete;
140067f462b0SGeert Uytterhoeven 	desc->callback_param = s;
140167f462b0SGeert Uytterhoeven 	s->cookie_rx[active] = dmaengine_submit(desc);
140267f462b0SGeert Uytterhoeven 	if (dma_submit_error(s->cookie_rx[active]))
140367f462b0SGeert Uytterhoeven 		goto fail;
140467f462b0SGeert Uytterhoeven 
140567f462b0SGeert Uytterhoeven 	s->active_rx = s->cookie_rx[!active];
140667f462b0SGeert Uytterhoeven 
14071d3db608SMuhammad Hamza Farooq 	dma_async_issue_pending(chan);
14081d3db608SMuhammad Hamza Farooq 
140994c53770SThomas Gleixner 	uart_port_unlock_irqrestore(port, flags);
141067f462b0SGeert Uytterhoeven 	dev_dbg(port->dev, "%s: cookie %d #%d, new active cookie %d\n",
141167f462b0SGeert Uytterhoeven 		__func__, s->cookie_rx[active], active, s->active_rx);
14120c9c1ea5SWolfram Sang 
14130c9c1ea5SWolfram Sang 	start_hrtimer_us(&s->rx_timer, s->rx_timeout);
14140c9c1ea5SWolfram Sang 
141567f462b0SGeert Uytterhoeven 	return;
141667f462b0SGeert Uytterhoeven 
141767f462b0SGeert Uytterhoeven fail:
14182c4ee235SGeert Uytterhoeven 	/* Switch to PIO */
141926f07399SGeert Uytterhoeven 	dmaengine_terminate_async(chan);
142026f07399SGeert Uytterhoeven 	sci_dma_rx_chan_invalidate(s);
142126f07399SGeert Uytterhoeven 	sci_dma_rx_reenable_irq(s);
142294c53770SThomas Gleixner 	uart_port_unlock_irqrestore(port, flags);
1423ae1b6b4cSWolfram Sang 	dev_warn(port->dev, "Failed submitting Rx DMA descriptor\n");
1424e1910fcdSGeert Uytterhoeven }
1425e1910fcdSGeert Uytterhoeven 
sci_dma_tx_release(struct sci_port * s)14268fcf7a65SGeert Uytterhoeven static void sci_dma_tx_release(struct sci_port *s)
1427e1910fcdSGeert Uytterhoeven {
14282c4ee235SGeert Uytterhoeven 	struct dma_chan *chan = s->chan_tx_saved;
1429e1910fcdSGeert Uytterhoeven 
1430f6611317SGeert Uytterhoeven 	cancel_work_sync(&s->work_tx);
14312c4ee235SGeert Uytterhoeven 	s->chan_tx_saved = s->chan_tx = NULL;
1432e1910fcdSGeert Uytterhoeven 	s->cookie_tx = -EINVAL;
14336eefc68dSGeert Uytterhoeven 	dmaengine_terminate_sync(chan);
1434e1910fcdSGeert Uytterhoeven 	dma_unmap_single(chan->device->dev, s->tx_dma_addr, UART_XMIT_SIZE,
1435e1910fcdSGeert Uytterhoeven 			 DMA_TO_DEVICE);
1436e1910fcdSGeert Uytterhoeven 	dma_release_channel(chan);
1437e1910fcdSGeert Uytterhoeven }
1438e1910fcdSGeert Uytterhoeven 
sci_dma_rx_submit(struct sci_port * s,bool port_lock_held)14398fcf7a65SGeert Uytterhoeven static int sci_dma_rx_submit(struct sci_port *s, bool port_lock_held)
1440e1910fcdSGeert Uytterhoeven {
1441e1910fcdSGeert Uytterhoeven 	struct dma_chan *chan = s->chan_rx;
14422c4ee235SGeert Uytterhoeven 	struct uart_port *port = &s->port;
14432c4ee235SGeert Uytterhoeven 	unsigned long flags;
1444e1910fcdSGeert Uytterhoeven 	int i;
1445e1910fcdSGeert Uytterhoeven 
1446e1910fcdSGeert Uytterhoeven 	for (i = 0; i < 2; i++) {
1447e1910fcdSGeert Uytterhoeven 		struct scatterlist *sg = &s->sg_rx[i];
1448e1910fcdSGeert Uytterhoeven 		struct dma_async_tx_descriptor *desc;
1449e1910fcdSGeert Uytterhoeven 
1450e1910fcdSGeert Uytterhoeven 		desc = dmaengine_prep_slave_sg(chan,
1451e1910fcdSGeert Uytterhoeven 			sg, 1, DMA_DEV_TO_MEM,
1452e1910fcdSGeert Uytterhoeven 			DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
1453e1910fcdSGeert Uytterhoeven 		if (!desc)
1454e1910fcdSGeert Uytterhoeven 			goto fail;
1455e1910fcdSGeert Uytterhoeven 
1456e1910fcdSGeert Uytterhoeven 		desc->callback = sci_dma_rx_complete;
1457e1910fcdSGeert Uytterhoeven 		desc->callback_param = s;
1458e1910fcdSGeert Uytterhoeven 		s->cookie_rx[i] = dmaengine_submit(desc);
1459e1910fcdSGeert Uytterhoeven 		if (dma_submit_error(s->cookie_rx[i]))
1460e1910fcdSGeert Uytterhoeven 			goto fail;
1461e1910fcdSGeert Uytterhoeven 
1462e1910fcdSGeert Uytterhoeven 	}
1463e1910fcdSGeert Uytterhoeven 
1464e1910fcdSGeert Uytterhoeven 	s->active_rx = s->cookie_rx[0];
1465e1910fcdSGeert Uytterhoeven 
1466e1910fcdSGeert Uytterhoeven 	dma_async_issue_pending(chan);
146771ab1c03SGeert Uytterhoeven 	return 0;
1468e1910fcdSGeert Uytterhoeven 
1469e1910fcdSGeert Uytterhoeven fail:
1470dd1f2250SGeert Uytterhoeven 	/* Switch to PIO */
1471dd1f2250SGeert Uytterhoeven 	if (!port_lock_held)
147294c53770SThomas Gleixner 		uart_port_lock_irqsave(port, &flags);
1473e1910fcdSGeert Uytterhoeven 	if (i)
14746eefc68dSGeert Uytterhoeven 		dmaengine_terminate_async(chan);
147511b3770dSGeert Uytterhoeven 	sci_dma_rx_chan_invalidate(s);
14762c4ee235SGeert Uytterhoeven 	sci_start_rx(port);
1477dd1f2250SGeert Uytterhoeven 	if (!port_lock_held)
147894c53770SThomas Gleixner 		uart_port_unlock_irqrestore(port, flags);
147971ab1c03SGeert Uytterhoeven 	return -EAGAIN;
1480e1910fcdSGeert Uytterhoeven }
1481e1910fcdSGeert Uytterhoeven 
sci_dma_tx_work_fn(struct work_struct * work)14828fcf7a65SGeert Uytterhoeven static void sci_dma_tx_work_fn(struct work_struct *work)
1483e1910fcdSGeert Uytterhoeven {
1484e1910fcdSGeert Uytterhoeven 	struct sci_port *s = container_of(work, struct sci_port, work_tx);
1485e1910fcdSGeert Uytterhoeven 	struct dma_async_tx_descriptor *desc;
1486e1910fcdSGeert Uytterhoeven 	struct dma_chan *chan = s->chan_tx;
1487e1910fcdSGeert Uytterhoeven 	struct uart_port *port = &s->port;
14881788cf6aSJiri Slaby (SUSE) 	struct tty_port *tport = &port->state->port;
14892c4ee235SGeert Uytterhoeven 	unsigned long flags;
14901788cf6aSJiri Slaby (SUSE) 	unsigned int tail;
1491e1910fcdSGeert Uytterhoeven 	dma_addr_t buf;
1492e1910fcdSGeert Uytterhoeven 
1493e1910fcdSGeert Uytterhoeven 	/*
1494e1910fcdSGeert Uytterhoeven 	 * DMA is idle now.
1495e1910fcdSGeert Uytterhoeven 	 * Port xmit buffer is already mapped, and it is one page... Just adjust
1496e1910fcdSGeert Uytterhoeven 	 * offsets and lengths. Since it is a circular buffer, we have to
1497e1910fcdSGeert Uytterhoeven 	 * transmit till the end, and then the rest. Take the port lock to get a
1498e1910fcdSGeert Uytterhoeven 	 * consistent xmit buffer state.
1499e1910fcdSGeert Uytterhoeven 	 */
150094c53770SThomas Gleixner 	uart_port_lock_irq(port);
15011788cf6aSJiri Slaby (SUSE) 	s->tx_dma_len = kfifo_out_linear(&tport->xmit_fifo, &tail,
15021788cf6aSJiri Slaby (SUSE) 			UART_XMIT_SIZE);
1503575ca2cbSIlpo Järvinen 	buf = s->tx_dma_addr + tail;
15048493eab0SGeert Uytterhoeven 	if (!s->tx_dma_len) {
15058493eab0SGeert Uytterhoeven 		/* Transmit buffer has been flushed */
150694c53770SThomas Gleixner 		uart_port_unlock_irq(port);
15078493eab0SGeert Uytterhoeven 		return;
15088493eab0SGeert Uytterhoeven 	}
1509e1910fcdSGeert Uytterhoeven 
1510e1910fcdSGeert Uytterhoeven 	desc = dmaengine_prep_slave_single(chan, buf, s->tx_dma_len,
1511e1910fcdSGeert Uytterhoeven 					   DMA_MEM_TO_DEV,
1512e1910fcdSGeert Uytterhoeven 					   DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
1513e1910fcdSGeert Uytterhoeven 	if (!desc) {
151494c53770SThomas Gleixner 		uart_port_unlock_irq(port);
1515e1910fcdSGeert Uytterhoeven 		dev_warn(port->dev, "Failed preparing Tx DMA descriptor\n");
15162c4ee235SGeert Uytterhoeven 		goto switch_to_pio;
1517e1910fcdSGeert Uytterhoeven 	}
1518e1910fcdSGeert Uytterhoeven 
1519e1910fcdSGeert Uytterhoeven 	dma_sync_single_for_device(chan->device->dev, buf, s->tx_dma_len,
1520e1910fcdSGeert Uytterhoeven 				   DMA_TO_DEVICE);
1521e1910fcdSGeert Uytterhoeven 
1522e1910fcdSGeert Uytterhoeven 	desc->callback = sci_dma_tx_complete;
1523e1910fcdSGeert Uytterhoeven 	desc->callback_param = s;
1524e1910fcdSGeert Uytterhoeven 	s->cookie_tx = dmaengine_submit(desc);
1525e1910fcdSGeert Uytterhoeven 	if (dma_submit_error(s->cookie_tx)) {
152694c53770SThomas Gleixner 		uart_port_unlock_irq(port);
1527e1910fcdSGeert Uytterhoeven 		dev_warn(port->dev, "Failed submitting Tx DMA descriptor\n");
15282c4ee235SGeert Uytterhoeven 		goto switch_to_pio;
1529e1910fcdSGeert Uytterhoeven 	}
1530e1910fcdSGeert Uytterhoeven 
153194c53770SThomas Gleixner 	uart_port_unlock_irq(port);
15321788cf6aSJiri Slaby (SUSE) 	dev_dbg(port->dev, "%s: %p: %u, cookie %d\n",
15331788cf6aSJiri Slaby (SUSE) 		__func__, tport->xmit_buf, tail, s->cookie_tx);
1534e1910fcdSGeert Uytterhoeven 
1535e1910fcdSGeert Uytterhoeven 	dma_async_issue_pending(chan);
15362c4ee235SGeert Uytterhoeven 	return;
15372c4ee235SGeert Uytterhoeven 
15382c4ee235SGeert Uytterhoeven switch_to_pio:
153994c53770SThomas Gleixner 	uart_port_lock_irqsave(port, &flags);
15402c4ee235SGeert Uytterhoeven 	s->chan_tx = NULL;
15412c4ee235SGeert Uytterhoeven 	sci_start_tx(port);
154294c53770SThomas Gleixner 	uart_port_unlock_irqrestore(port, flags);
15432c4ee235SGeert Uytterhoeven 	return;
1544e1910fcdSGeert Uytterhoeven }
1545e1910fcdSGeert Uytterhoeven 
sci_dma_rx_timer_fn(struct hrtimer * t)15468fcf7a65SGeert Uytterhoeven static enum hrtimer_restart sci_dma_rx_timer_fn(struct hrtimer *t)
1547e1910fcdSGeert Uytterhoeven {
1548b96408b4SUlrich Hecht 	struct sci_port *s = container_of(t, struct sci_port, rx_timer);
1549e7327c09SMuhammad Hamza Farooq 	struct dma_chan *chan = s->chan_rx;
1550e1910fcdSGeert Uytterhoeven 	struct uart_port *port = &s->port;
155167f462b0SGeert Uytterhoeven 	struct dma_tx_state state;
155267f462b0SGeert Uytterhoeven 	enum dma_status status;
155367f462b0SGeert Uytterhoeven 	unsigned long flags;
155467f462b0SGeert Uytterhoeven 	unsigned int read;
155567f462b0SGeert Uytterhoeven 	int active, count;
1556e1910fcdSGeert Uytterhoeven 
155767f462b0SGeert Uytterhoeven 	dev_dbg(port->dev, "DMA Rx timed out\n");
155867f462b0SGeert Uytterhoeven 
155994c53770SThomas Gleixner 	uart_port_lock_irqsave(port, &flags);
15606fc5a520STakatoshi Akiyama 
156167f462b0SGeert Uytterhoeven 	active = sci_dma_rx_find_active(s);
156267f462b0SGeert Uytterhoeven 	if (active < 0) {
156394c53770SThomas Gleixner 		uart_port_unlock_irqrestore(port, flags);
1564b96408b4SUlrich Hecht 		return HRTIMER_NORESTART;
156567f462b0SGeert Uytterhoeven 	}
156667f462b0SGeert Uytterhoeven 
156767f462b0SGeert Uytterhoeven 	status = dmaengine_tx_status(s->chan_rx, s->active_rx, &state);
15683b963042SMuhammad Hamza Farooq 	if (status == DMA_COMPLETE) {
156994c53770SThomas Gleixner 		uart_port_unlock_irqrestore(port, flags);
157067f462b0SGeert Uytterhoeven 		dev_dbg(port->dev, "Cookie %d #%d has already completed\n",
157167f462b0SGeert Uytterhoeven 			s->active_rx, active);
15723b963042SMuhammad Hamza Farooq 
15733b963042SMuhammad Hamza Farooq 		/* Let packet complete handler take care of the packet */
1574b96408b4SUlrich Hecht 		return HRTIMER_NORESTART;
15753b963042SMuhammad Hamza Farooq 	}
157667f462b0SGeert Uytterhoeven 
1577e7327c09SMuhammad Hamza Farooq 	dmaengine_pause(chan);
1578e7327c09SMuhammad Hamza Farooq 
1579e7327c09SMuhammad Hamza Farooq 	/*
1580e7327c09SMuhammad Hamza Farooq 	 * sometimes DMA transfer doesn't stop even if it is stopped and
1581e7327c09SMuhammad Hamza Farooq 	 * data keeps on coming until transaction is complete so check
1582e7327c09SMuhammad Hamza Farooq 	 * for DMA_COMPLETE again
1583e7327c09SMuhammad Hamza Farooq 	 * Let packet complete handler take care of the packet
1584e7327c09SMuhammad Hamza Farooq 	 */
1585e7327c09SMuhammad Hamza Farooq 	status = dmaengine_tx_status(s->chan_rx, s->active_rx, &state);
1586e7327c09SMuhammad Hamza Farooq 	if (status == DMA_COMPLETE) {
158794c53770SThomas Gleixner 		uart_port_unlock_irqrestore(port, flags);
1588e7327c09SMuhammad Hamza Farooq 		dev_dbg(port->dev, "Transaction complete after DMA engine was stopped");
1589b96408b4SUlrich Hecht 		return HRTIMER_NORESTART;
1590e7327c09SMuhammad Hamza Farooq 	}
1591e7327c09SMuhammad Hamza Farooq 
159267f462b0SGeert Uytterhoeven 	/* Handle incomplete DMA receive */
15936eefc68dSGeert Uytterhoeven 	dmaengine_terminate_async(s->chan_rx);
159467f462b0SGeert Uytterhoeven 	read = sg_dma_len(&s->sg_rx[active]) - state.residue;
159567f462b0SGeert Uytterhoeven 
159667f462b0SGeert Uytterhoeven 	if (read) {
159767f462b0SGeert Uytterhoeven 		count = sci_dma_rx_push(s, s->rx_buf[active], read);
159867f462b0SGeert Uytterhoeven 		if (count)
159967f462b0SGeert Uytterhoeven 			tty_flip_buffer_push(&port->state->port);
160067f462b0SGeert Uytterhoeven 	}
160167f462b0SGeert Uytterhoeven 
1602cf383d12SBiju Das 	if (port->type == PORT_SCIFA || port->type == PORT_SCIFB ||
1603cf383d12SBiju Das 	    s->cfg->regtype == SCIx_RZ_SCIFA_REGTYPE)
16048fcf7a65SGeert Uytterhoeven 		sci_dma_rx_submit(s, true);
1605371cfed3SMuhammad Hamza Farooq 
160638766e4bSGeert Uytterhoeven 	sci_dma_rx_reenable_irq(s);
1607371cfed3SMuhammad Hamza Farooq 
160894c53770SThomas Gleixner 	uart_port_unlock_irqrestore(port, flags);
1609b96408b4SUlrich Hecht 
1610b96408b4SUlrich Hecht 	return HRTIMER_NORESTART;
1611e1910fcdSGeert Uytterhoeven }
1612e1910fcdSGeert Uytterhoeven 
sci_request_dma_chan(struct uart_port * port,enum dma_transfer_direction dir)1613ff441129SGeert Uytterhoeven static struct dma_chan *sci_request_dma_chan(struct uart_port *port,
1614219fb0c1SLaurent Pinchart 					     enum dma_transfer_direction dir)
1615e1910fcdSGeert Uytterhoeven {
1616ff441129SGeert Uytterhoeven 	struct dma_chan *chan;
1617ff441129SGeert Uytterhoeven 	struct dma_slave_config cfg;
1618ff441129SGeert Uytterhoeven 	int ret;
1619e1910fcdSGeert Uytterhoeven 
1620f1c7f92eSChristophe JAILLET 	chan = dma_request_chan(port->dev, dir == DMA_MEM_TO_DEV ? "tx" : "rx");
1621f1c7f92eSChristophe JAILLET 	if (IS_ERR(chan)) {
1622f1c7f92eSChristophe JAILLET 		dev_dbg(port->dev, "dma_request_chan failed\n");
1623ff441129SGeert Uytterhoeven 		return NULL;
1624ff441129SGeert Uytterhoeven 	}
1625e1910fcdSGeert Uytterhoeven 
1626ff441129SGeert Uytterhoeven 	memset(&cfg, 0, sizeof(cfg));
1627ff441129SGeert Uytterhoeven 	cfg.direction = dir;
1628ff441129SGeert Uytterhoeven 	cfg.dst_addr = port->mapbase +
1629ff441129SGeert Uytterhoeven 		(sci_getreg(port, SCxTDR)->offset << port->regshift);
1630ff441129SGeert Uytterhoeven 	cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
1631ff441129SGeert Uytterhoeven 	cfg.src_addr = port->mapbase +
1632ff441129SGeert Uytterhoeven 		(sci_getreg(port, SCxRDR)->offset << port->regshift);
1633ff441129SGeert Uytterhoeven 	cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
1634ff441129SGeert Uytterhoeven 
1635ff441129SGeert Uytterhoeven 	ret = dmaengine_slave_config(chan, &cfg);
1636ff441129SGeert Uytterhoeven 	if (ret) {
1637ff441129SGeert Uytterhoeven 		dev_warn(port->dev, "dmaengine_slave_config failed %d\n", ret);
1638ff441129SGeert Uytterhoeven 		dma_release_channel(chan);
1639ff441129SGeert Uytterhoeven 		return NULL;
1640ff441129SGeert Uytterhoeven 	}
1641ff441129SGeert Uytterhoeven 
1642ff441129SGeert Uytterhoeven 	return chan;
1643ff441129SGeert Uytterhoeven }
1644ff441129SGeert Uytterhoeven 
sci_request_dma(struct uart_port * port)1645ff441129SGeert Uytterhoeven static void sci_request_dma(struct uart_port *port)
1646ff441129SGeert Uytterhoeven {
1647ff441129SGeert Uytterhoeven 	struct sci_port *s = to_sci_port(port);
16481788cf6aSJiri Slaby (SUSE) 	struct tty_port *tport = &port->state->port;
1649ff441129SGeert Uytterhoeven 	struct dma_chan *chan;
1650ff441129SGeert Uytterhoeven 
1651ff441129SGeert Uytterhoeven 	dev_dbg(port->dev, "%s: port %d\n", __func__, port->line);
1652ff441129SGeert Uytterhoeven 
1653099506cbSGeorge G. Davis 	/*
1654099506cbSGeorge G. Davis 	 * DMA on console may interfere with Kernel log messages which use
1655099506cbSGeorge G. Davis 	 * plain putchar(). So, simply don't use it with a console.
1656099506cbSGeorge G. Davis 	 */
1657099506cbSGeorge G. Davis 	if (uart_console(port))
1658099506cbSGeorge G. Davis 		return;
1659099506cbSGeorge G. Davis 
1660219fb0c1SLaurent Pinchart 	if (!port->dev->of_node)
1661ff441129SGeert Uytterhoeven 		return;
1662e1910fcdSGeert Uytterhoeven 
1663e1910fcdSGeert Uytterhoeven 	s->cookie_tx = -EINVAL;
16647464779fSAndy Lowe 
16657464779fSAndy Lowe 	/*
16667464779fSAndy Lowe 	 * Don't request a dma channel if no channel was specified
16677464779fSAndy Lowe 	 * in the device tree.
16687464779fSAndy Lowe 	 */
1669ef194140SRob Herring 	if (!of_property_present(port->dev->of_node, "dmas"))
16707464779fSAndy Lowe 		return;
16717464779fSAndy Lowe 
1672219fb0c1SLaurent Pinchart 	chan = sci_request_dma_chan(port, DMA_MEM_TO_DEV);
1673e1910fcdSGeert Uytterhoeven 	dev_dbg(port->dev, "%s: TX: got channel %p\n", __func__, chan);
1674e1910fcdSGeert Uytterhoeven 	if (chan) {
1675e1910fcdSGeert Uytterhoeven 		/* UART circular tx buffer is an aligned page. */
1676e1910fcdSGeert Uytterhoeven 		s->tx_dma_addr = dma_map_single(chan->device->dev,
16771788cf6aSJiri Slaby (SUSE) 						tport->xmit_buf,
1678e1910fcdSGeert Uytterhoeven 						UART_XMIT_SIZE,
1679e1910fcdSGeert Uytterhoeven 						DMA_TO_DEVICE);
1680e1910fcdSGeert Uytterhoeven 		if (dma_mapping_error(chan->device->dev, s->tx_dma_addr)) {
1681e1910fcdSGeert Uytterhoeven 			dev_warn(port->dev, "Failed mapping Tx DMA descriptor\n");
1682e1910fcdSGeert Uytterhoeven 			dma_release_channel(chan);
1683e1910fcdSGeert Uytterhoeven 		} else {
1684e1910fcdSGeert Uytterhoeven 			dev_dbg(port->dev, "%s: mapped %lu@%p to %pad\n",
1685e1910fcdSGeert Uytterhoeven 				__func__, UART_XMIT_SIZE,
16861788cf6aSJiri Slaby (SUSE) 				tport->xmit_buf, &s->tx_dma_addr);
16872c4ee235SGeert Uytterhoeven 
16888fcf7a65SGeert Uytterhoeven 			INIT_WORK(&s->work_tx, sci_dma_tx_work_fn);
16892c4ee235SGeert Uytterhoeven 			s->chan_tx_saved = s->chan_tx = chan;
1690e1910fcdSGeert Uytterhoeven 		}
1691e1910fcdSGeert Uytterhoeven 	}
1692e1910fcdSGeert Uytterhoeven 
1693219fb0c1SLaurent Pinchart 	chan = sci_request_dma_chan(port, DMA_DEV_TO_MEM);
1694e1910fcdSGeert Uytterhoeven 	dev_dbg(port->dev, "%s: RX: got channel %p\n", __func__, chan);
1695e1910fcdSGeert Uytterhoeven 	if (chan) {
1696e1910fcdSGeert Uytterhoeven 		unsigned int i;
1697e1910fcdSGeert Uytterhoeven 		dma_addr_t dma;
1698e1910fcdSGeert Uytterhoeven 		void *buf;
1699e1910fcdSGeert Uytterhoeven 
1700e1910fcdSGeert Uytterhoeven 		s->buf_len_rx = 2 * max_t(size_t, 16, port->fifosize);
1701e1910fcdSGeert Uytterhoeven 		buf = dma_alloc_coherent(chan->device->dev, s->buf_len_rx * 2,
1702e1910fcdSGeert Uytterhoeven 					 &dma, GFP_KERNEL);
1703e1910fcdSGeert Uytterhoeven 		if (!buf) {
1704e1910fcdSGeert Uytterhoeven 			dev_warn(port->dev,
1705e1910fcdSGeert Uytterhoeven 				 "Failed to allocate Rx dma buffer, using PIO\n");
1706e1910fcdSGeert Uytterhoeven 			dma_release_channel(chan);
1707e1910fcdSGeert Uytterhoeven 			return;
1708e1910fcdSGeert Uytterhoeven 		}
1709e1910fcdSGeert Uytterhoeven 
1710e1910fcdSGeert Uytterhoeven 		for (i = 0; i < 2; i++) {
1711e1910fcdSGeert Uytterhoeven 			struct scatterlist *sg = &s->sg_rx[i];
1712e1910fcdSGeert Uytterhoeven 
1713e1910fcdSGeert Uytterhoeven 			sg_init_table(sg, 1);
1714e1910fcdSGeert Uytterhoeven 			s->rx_buf[i] = buf;
1715e1910fcdSGeert Uytterhoeven 			sg_dma_address(sg) = dma;
1716d09959e7SYoshihiro Shimoda 			sg_dma_len(sg) = s->buf_len_rx;
1717e1910fcdSGeert Uytterhoeven 
1718e1910fcdSGeert Uytterhoeven 			buf += s->buf_len_rx;
1719e1910fcdSGeert Uytterhoeven 			dma += s->buf_len_rx;
1720e1910fcdSGeert Uytterhoeven 		}
1721e1910fcdSGeert Uytterhoeven 
17227ba2faccSNam Cao 		hrtimer_setup(&s->rx_timer, sci_dma_rx_timer_fn, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
1723e1910fcdSGeert Uytterhoeven 
1724202dc3ccSGeert Uytterhoeven 		s->chan_rx_saved = s->chan_rx = chan;
1725202dc3ccSGeert Uytterhoeven 
1726cf383d12SBiju Das 		if (port->type == PORT_SCIFA || port->type == PORT_SCIFB ||
1727cf383d12SBiju Das 		    s->cfg->regtype == SCIx_RZ_SCIFA_REGTYPE)
17288fcf7a65SGeert Uytterhoeven 			sci_dma_rx_submit(s, false);
1729e1910fcdSGeert Uytterhoeven 	}
1730e1910fcdSGeert Uytterhoeven }
1731e1910fcdSGeert Uytterhoeven 
sci_free_dma(struct uart_port * port)1732e1910fcdSGeert Uytterhoeven static void sci_free_dma(struct uart_port *port)
1733e1910fcdSGeert Uytterhoeven {
1734e1910fcdSGeert Uytterhoeven 	struct sci_port *s = to_sci_port(port);
1735e1910fcdSGeert Uytterhoeven 
17362c4ee235SGeert Uytterhoeven 	if (s->chan_tx_saved)
17378fcf7a65SGeert Uytterhoeven 		sci_dma_tx_release(s);
17382c4ee235SGeert Uytterhoeven 	if (s->chan_rx_saved)
17398fcf7a65SGeert Uytterhoeven 		sci_dma_rx_release(s);
1740e1910fcdSGeert Uytterhoeven }
17411cf4a7efSGeert Uytterhoeven 
sci_flush_buffer(struct uart_port * port)17421cf4a7efSGeert Uytterhoeven static void sci_flush_buffer(struct uart_port *port)
17431cf4a7efSGeert Uytterhoeven {
1744775b7ffdSGeert Uytterhoeven 	struct sci_port *s = to_sci_port(port);
1745775b7ffdSGeert Uytterhoeven 
17461cf4a7efSGeert Uytterhoeven 	/*
17471cf4a7efSGeert Uytterhoeven 	 * In uart_flush_buffer(), the xmit circular buffer has just been
1748775b7ffdSGeert Uytterhoeven 	 * cleared, so we have to reset tx_dma_len accordingly, and stop any
1749775b7ffdSGeert Uytterhoeven 	 * pending transfers
17501cf4a7efSGeert Uytterhoeven 	 */
1751775b7ffdSGeert Uytterhoeven 	s->tx_dma_len = 0;
1752775b7ffdSGeert Uytterhoeven 	if (s->chan_tx) {
1753775b7ffdSGeert Uytterhoeven 		dmaengine_terminate_async(s->chan_tx);
1754775b7ffdSGeert Uytterhoeven 		s->cookie_tx = -EINVAL;
1755775b7ffdSGeert Uytterhoeven 	}
17561cf4a7efSGeert Uytterhoeven }
17577cc0e0a4SClaudiu Beznea 
sci_dma_check_tx_occurred(struct sci_port * s)17587cc0e0a4SClaudiu Beznea static void sci_dma_check_tx_occurred(struct sci_port *s)
17597cc0e0a4SClaudiu Beznea {
17607cc0e0a4SClaudiu Beznea 	struct dma_tx_state state;
17617cc0e0a4SClaudiu Beznea 	enum dma_status status;
17627cc0e0a4SClaudiu Beznea 
17637cc0e0a4SClaudiu Beznea 	if (!s->chan_tx)
17647cc0e0a4SClaudiu Beznea 		return;
17657cc0e0a4SClaudiu Beznea 
17667cc0e0a4SClaudiu Beznea 	status = dmaengine_tx_status(s->chan_tx, s->cookie_tx, &state);
17677cc0e0a4SClaudiu Beznea 	if (status == DMA_COMPLETE || status == DMA_IN_PROGRESS)
17687cc0e0a4SClaudiu Beznea 		s->tx_occurred = true;
17697cc0e0a4SClaudiu Beznea }
17701cf4a7efSGeert Uytterhoeven #else /* !CONFIG_SERIAL_SH_SCI_DMA */
sci_request_dma(struct uart_port * port)1771e1910fcdSGeert Uytterhoeven static inline void sci_request_dma(struct uart_port *port)
1772e1910fcdSGeert Uytterhoeven {
1773e1910fcdSGeert Uytterhoeven }
1774e1910fcdSGeert Uytterhoeven 
sci_free_dma(struct uart_port * port)1775e1910fcdSGeert Uytterhoeven static inline void sci_free_dma(struct uart_port *port)
1776e1910fcdSGeert Uytterhoeven {
1777e1910fcdSGeert Uytterhoeven }
17781cf4a7efSGeert Uytterhoeven 
sci_dma_check_tx_occurred(struct sci_port * s)17797cc0e0a4SClaudiu Beznea static void sci_dma_check_tx_occurred(struct sci_port *s)
17807cc0e0a4SClaudiu Beznea {
17817cc0e0a4SClaudiu Beznea }
17827cc0e0a4SClaudiu Beznea 
17831cf4a7efSGeert Uytterhoeven #define sci_flush_buffer	NULL
17841cf4a7efSGeert Uytterhoeven #endif /* !CONFIG_SERIAL_SH_SCI_DMA */
1785e1910fcdSGeert Uytterhoeven 
sci_rx_interrupt(int irq,void * ptr)1786ab4382d2SGreg Kroah-Hartman static irqreturn_t sci_rx_interrupt(int irq, void *ptr)
1787ab4382d2SGreg Kroah-Hartman {
1788ab4382d2SGreg Kroah-Hartman 	struct uart_port *port = ptr;
1789ab4382d2SGreg Kroah-Hartman 	struct sci_port *s = to_sci_port(port);
1790ab4382d2SGreg Kroah-Hartman 
179103940376SUlrich Hecht #ifdef CONFIG_SERIAL_SH_SCI_DMA
1792ab4382d2SGreg Kroah-Hartman 	if (s->chan_rx) {
17936deab514SGeert Uytterhoeven 		u16 scr = sci_serial_in(port, SCSCR);
17946deab514SGeert Uytterhoeven 		u16 ssr = sci_serial_in(port, SCxSR);
1795ab4382d2SGreg Kroah-Hartman 
1796ab4382d2SGreg Kroah-Hartman 		/* Disable future Rx interrupts */
1797cf383d12SBiju Das 		if (port->type == PORT_SCIFA || port->type == PORT_SCIFB ||
1798cf383d12SBiju Das 		    s->cfg->regtype == SCIx_RZ_SCIFA_REGTYPE) {
1799cf383d12SBiju Das 			disable_irq_nosync(s->irqs[SCIx_RXI_IRQ]);
1800cf383d12SBiju Das 			if (s->cfg->regtype == SCIx_RZ_SCIFA_REGTYPE) {
1801cf383d12SBiju Das 				scif_set_rtrg(port, 1);
1802cf383d12SBiju Das 				scr |= SCSCR_RIE;
1803cf383d12SBiju Das 			} else {
180426de4f1bSGeert Uytterhoeven 				scr |= SCSCR_RDRQE;
1805cf383d12SBiju Das 			}
1806ab4382d2SGreg Kroah-Hartman 		} else {
18078fcf7a65SGeert Uytterhoeven 			if (sci_dma_rx_submit(s, false) < 0)
180871ab1c03SGeert Uytterhoeven 				goto handle_pio;
180971ab1c03SGeert Uytterhoeven 
1810fc887b15SLinus Torvalds 			scr &= ~SCSCR_RIE;
1811ab4382d2SGreg Kroah-Hartman 		}
18126deab514SGeert Uytterhoeven 		sci_serial_out(port, SCSCR, scr);
1813ab4382d2SGreg Kroah-Hartman 		/* Clear current interrupt */
18146deab514SGeert Uytterhoeven 		sci_serial_out(port, SCxSR,
181554af5001SGeert Uytterhoeven 			       ssr & ~(SCIF_DR | SCxSR_RDxF(port)));
1816b96408b4SUlrich Hecht 		dev_dbg(port->dev, "Rx IRQ %lu: setup t-out in %u us\n",
1817ab4382d2SGreg Kroah-Hartman 			jiffies, s->rx_timeout);
1818b96408b4SUlrich Hecht 		start_hrtimer_us(&s->rx_timer, s->rx_timeout);
1819ab4382d2SGreg Kroah-Hartman 
1820ab4382d2SGreg Kroah-Hartman 		return IRQ_HANDLED;
1821ab4382d2SGreg Kroah-Hartman 	}
182271ab1c03SGeert Uytterhoeven 
182371ab1c03SGeert Uytterhoeven handle_pio:
1824ab4382d2SGreg Kroah-Hartman #endif
1825ab4382d2SGreg Kroah-Hartman 
182603940376SUlrich Hecht 	if (s->rx_trigger > 1 && s->rx_fifo_timeout > 0) {
182703940376SUlrich Hecht 		if (!scif_rtrg_enabled(port))
182803940376SUlrich Hecht 			scif_set_rtrg(port, s->rx_trigger);
182903940376SUlrich Hecht 
183003940376SUlrich Hecht 		mod_timer(&s->rx_fifo_timer, jiffies + DIV_ROUND_UP(
1831b96408b4SUlrich Hecht 			  s->rx_frame * HZ * s->rx_fifo_timeout, 1000000));
183203940376SUlrich Hecht 	}
183303940376SUlrich Hecht 
1834ab4382d2SGreg Kroah-Hartman 	/* I think sci_receive_chars has to be called irrespective
1835ab4382d2SGreg Kroah-Hartman 	 * of whether the I_IXOFF is set, otherwise, how is the interrupt
1836ab4382d2SGreg Kroah-Hartman 	 * to be disabled?
1837ab4382d2SGreg Kroah-Hartman 	 */
1838ed8c8e1eSGeert Uytterhoeven 	sci_receive_chars(port);
1839ab4382d2SGreg Kroah-Hartman 
1840ab4382d2SGreg Kroah-Hartman 	return IRQ_HANDLED;
1841ab4382d2SGreg Kroah-Hartman }
1842ab4382d2SGreg Kroah-Hartman 
sci_tx_interrupt(int irq,void * ptr)1843ab4382d2SGreg Kroah-Hartman static irqreturn_t sci_tx_interrupt(int irq, void *ptr)
1844ab4382d2SGreg Kroah-Hartman {
1845ab4382d2SGreg Kroah-Hartman 	struct uart_port *port = ptr;
1846ab4382d2SGreg Kroah-Hartman 	unsigned long flags;
1847ab4382d2SGreg Kroah-Hartman 
184894c53770SThomas Gleixner 	uart_port_lock_irqsave(port, &flags);
1849ab4382d2SGreg Kroah-Hartman 	sci_transmit_chars(port);
185094c53770SThomas Gleixner 	uart_port_unlock_irqrestore(port, flags);
1851ab4382d2SGreg Kroah-Hartman 
1852ab4382d2SGreg Kroah-Hartman 	return IRQ_HANDLED;
1853ab4382d2SGreg Kroah-Hartman }
1854ab4382d2SGreg Kroah-Hartman 
sci_tx_end_interrupt(int irq,void * ptr)1855d61ae331SBiju Das static irqreturn_t sci_tx_end_interrupt(int irq, void *ptr)
1856d61ae331SBiju Das {
1857d61ae331SBiju Das 	struct uart_port *port = ptr;
1858d61ae331SBiju Das 	unsigned long flags;
1859d61ae331SBiju Das 	unsigned short ctrl;
1860d61ae331SBiju Das 
1861d61ae331SBiju Das 	if (port->type != PORT_SCI)
1862d61ae331SBiju Das 		return sci_tx_interrupt(irq, ptr);
1863d61ae331SBiju Das 
186494c53770SThomas Gleixner 	uart_port_lock_irqsave(port, &flags);
18656deab514SGeert Uytterhoeven 	ctrl = sci_serial_in(port, SCSCR);
1866d61ae331SBiju Das 	ctrl &= ~(SCSCR_TE | SCSCR_TEIE);
18676deab514SGeert Uytterhoeven 	sci_serial_out(port, SCSCR, ctrl);
186894c53770SThomas Gleixner 	uart_port_unlock_irqrestore(port, flags);
1869d61ae331SBiju Das 
1870d61ae331SBiju Das 	return IRQ_HANDLED;
1871d61ae331SBiju Das }
1872d61ae331SBiju Das 
sci_br_interrupt(int irq,void * ptr)1873628c534aSChris Brandt static irqreturn_t sci_br_interrupt(int irq, void *ptr)
1874628c534aSChris Brandt {
1875628c534aSChris Brandt 	struct uart_port *port = ptr;
1876628c534aSChris Brandt 
1877628c534aSChris Brandt 	/* Handle BREAKs */
1878628c534aSChris Brandt 	sci_handle_breaks(port);
187987b8061bSUlrich Hecht 
188087b8061bSUlrich Hecht 	/* drop invalid character received before break was detected */
18816deab514SGeert Uytterhoeven 	sci_serial_in(port, SCxRDR);
188287b8061bSUlrich Hecht 
1883628c534aSChris Brandt 	sci_clear_SCxSR(port, SCxSR_BREAK_CLEAR(port));
1884628c534aSChris Brandt 
1885628c534aSChris Brandt 	return IRQ_HANDLED;
1886628c534aSChris Brandt }
18878b0bbd95SChris Brandt 
sci_er_interrupt(int irq,void * ptr)1888ab4382d2SGreg Kroah-Hartman static irqreturn_t sci_er_interrupt(int irq, void *ptr)
1889ab4382d2SGreg Kroah-Hartman {
1890ab4382d2SGreg Kroah-Hartman 	struct uart_port *port = ptr;
1891e6403c11SGeert Uytterhoeven 	struct sci_port *s = to_sci_port(port);
1892ab4382d2SGreg Kroah-Hartman 
1893628c534aSChris Brandt 	if (s->irqs[SCIx_ERI_IRQ] == s->irqs[SCIx_BRI_IRQ]) {
18948b0bbd95SChris Brandt 		/* Break and Error interrupts are muxed */
18956deab514SGeert Uytterhoeven 		unsigned short ssr_status = sci_serial_in(port, SCxSR);
18968b0bbd95SChris Brandt 
18978b0bbd95SChris Brandt 		/* Break Interrupt */
18988b0bbd95SChris Brandt 		if (ssr_status & SCxSR_BRK(port))
18998b0bbd95SChris Brandt 			sci_br_interrupt(irq, ptr);
19008b0bbd95SChris Brandt 
19018b0bbd95SChris Brandt 		/* Break only? */
19028b0bbd95SChris Brandt 		if (!(ssr_status & SCxSR_ERRORS(port)))
19038b0bbd95SChris Brandt 			return IRQ_HANDLED;
19048b0bbd95SChris Brandt 	}
19058b0bbd95SChris Brandt 
1906ab4382d2SGreg Kroah-Hartman 	/* Handle errors */
1907ab4382d2SGreg Kroah-Hartman 	if (port->type == PORT_SCI) {
1908ab4382d2SGreg Kroah-Hartman 		if (sci_handle_errors(port)) {
1909ab4382d2SGreg Kroah-Hartman 			/* discard character in rx buffer */
19106deab514SGeert Uytterhoeven 			sci_serial_in(port, SCxSR);
1911a1b5b43fSGeert Uytterhoeven 			sci_clear_SCxSR(port, SCxSR_RDxF_CLEAR(port));
1912ab4382d2SGreg Kroah-Hartman 		}
1913ab4382d2SGreg Kroah-Hartman 	} else {
1914ab4382d2SGreg Kroah-Hartman 		sci_handle_fifo_overrun(port);
1915e6403c11SGeert Uytterhoeven 		if (!s->chan_rx)
1916ed8c8e1eSGeert Uytterhoeven 			sci_receive_chars(port);
1917ab4382d2SGreg Kroah-Hartman 	}
1918ab4382d2SGreg Kroah-Hartman 
1919a1b5b43fSGeert Uytterhoeven 	sci_clear_SCxSR(port, SCxSR_ERROR_CLEAR(port));
1920ab4382d2SGreg Kroah-Hartman 
1921ab4382d2SGreg Kroah-Hartman 	/* Kick the transmission */
19228eadb56dSYoshihiro Shimoda 	if (!s->chan_tx)
1923ab4382d2SGreg Kroah-Hartman 		sci_tx_interrupt(irq, ptr);
1924ab4382d2SGreg Kroah-Hartman 
1925ab4382d2SGreg Kroah-Hartman 	return IRQ_HANDLED;
1926ab4382d2SGreg Kroah-Hartman }
1927ab4382d2SGreg Kroah-Hartman 
sci_mpxed_interrupt(int irq,void * ptr)1928ab4382d2SGreg Kroah-Hartman static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr)
1929ab4382d2SGreg Kroah-Hartman {
1930cb772fe7SNobuhiro Iwamatsu 	unsigned short ssr_status, scr_status, err_enabled, orer_status = 0;
1931ab4382d2SGreg Kroah-Hartman 	struct uart_port *port = ptr;
1932ab4382d2SGreg Kroah-Hartman 	struct sci_port *s = to_sci_port(port);
1933ab4382d2SGreg Kroah-Hartman 	irqreturn_t ret = IRQ_NONE;
1934ab4382d2SGreg Kroah-Hartman 
19356deab514SGeert Uytterhoeven 	ssr_status = sci_serial_in(port, SCxSR);
19366deab514SGeert Uytterhoeven 	scr_status = sci_serial_in(port, SCSCR);
1937b2f20ed9SLaurent Pinchart 	if (s->params->overrun_reg == SCxSR)
1938cb772fe7SNobuhiro Iwamatsu 		orer_status = ssr_status;
1939b2f20ed9SLaurent Pinchart 	else if (sci_getreg(port, s->params->overrun_reg)->size)
19406deab514SGeert Uytterhoeven 		orer_status = sci_serial_in(port, s->params->overrun_reg);
1941cb772fe7SNobuhiro Iwamatsu 
1942fc887b15SLinus Torvalds 	err_enabled = scr_status & port_rx_irq_mask(port);
1943ab4382d2SGreg Kroah-Hartman 
1944ab4382d2SGreg Kroah-Hartman 	/* Tx Interrupt */
1945fc887b15SLinus Torvalds 	if ((ssr_status & SCxSR_TDxE(port)) && (scr_status & SCSCR_TIE) &&
1946ab4382d2SGreg Kroah-Hartman 	    !s->chan_tx)
1947ab4382d2SGreg Kroah-Hartman 		ret = sci_tx_interrupt(irq, ptr);
1948fc887b15SLinus Torvalds 
1949ab4382d2SGreg Kroah-Hartman 	/*
1950ab4382d2SGreg Kroah-Hartman 	 * Rx Interrupt: if we're using DMA, the DMA controller clears RDF /
1951ab4382d2SGreg Kroah-Hartman 	 * DR flags
1952ab4382d2SGreg Kroah-Hartman 	 */
1953ab4382d2SGreg Kroah-Hartman 	if (((ssr_status & SCxSR_RDxF(port)) || s->chan_rx) &&
1954e0a12a27SGeert Uytterhoeven 	    (scr_status & SCSCR_RIE))
1955ab4382d2SGreg Kroah-Hartman 		ret = sci_rx_interrupt(irq, ptr);
1956fc887b15SLinus Torvalds 
1957ab4382d2SGreg Kroah-Hartman 	/* Error Interrupt */
1958ab4382d2SGreg Kroah-Hartman 	if ((ssr_status & SCxSR_ERRORS(port)) && err_enabled)
1959ab4382d2SGreg Kroah-Hartman 		ret = sci_er_interrupt(irq, ptr);
1960fc887b15SLinus Torvalds 
1961ab4382d2SGreg Kroah-Hartman 	/* Break Interrupt */
196287b8061bSUlrich Hecht 	if (s->irqs[SCIx_ERI_IRQ] != s->irqs[SCIx_BRI_IRQ] &&
196387b8061bSUlrich Hecht 	    (ssr_status & SCxSR_BRK(port)) && err_enabled)
1964ab4382d2SGreg Kroah-Hartman 		ret = sci_br_interrupt(irq, ptr);
1965ab4382d2SGreg Kroah-Hartman 
19668b6ff84cSHisashi Nakamura 	/* Overrun Interrupt */
1967b2f20ed9SLaurent Pinchart 	if (orer_status & s->params->overrun_mask) {
19688b6ff84cSHisashi Nakamura 		sci_handle_fifo_overrun(port);
196990803072SYoshihiro Shimoda 		ret = IRQ_HANDLED;
197090803072SYoshihiro Shimoda 	}
19718b6ff84cSHisashi Nakamura 
1972ab4382d2SGreg Kroah-Hartman 	return ret;
1973ab4382d2SGreg Kroah-Hartman }
1974ab4382d2SGreg Kroah-Hartman 
1975d56a91e8SGeert Uytterhoeven static const struct sci_irq_desc {
19769174fc8fSPaul Mundt 	const char	*desc;
19779174fc8fSPaul Mundt 	irq_handler_t	handler;
19789174fc8fSPaul Mundt } sci_irq_desc[] = {
19799174fc8fSPaul Mundt 	/*
19809174fc8fSPaul Mundt 	 * Split out handlers, the default case.
19819174fc8fSPaul Mundt 	 */
19829174fc8fSPaul Mundt 	[SCIx_ERI_IRQ] = {
19839174fc8fSPaul Mundt 		.desc = "rx err",
19849174fc8fSPaul Mundt 		.handler = sci_er_interrupt,
19859174fc8fSPaul Mundt 	},
19869174fc8fSPaul Mundt 
19879174fc8fSPaul Mundt 	[SCIx_RXI_IRQ] = {
19889174fc8fSPaul Mundt 		.desc = "rx full",
19899174fc8fSPaul Mundt 		.handler = sci_rx_interrupt,
19909174fc8fSPaul Mundt 	},
19919174fc8fSPaul Mundt 
19929174fc8fSPaul Mundt 	[SCIx_TXI_IRQ] = {
19939174fc8fSPaul Mundt 		.desc = "tx empty",
19949174fc8fSPaul Mundt 		.handler = sci_tx_interrupt,
19959174fc8fSPaul Mundt 	},
19969174fc8fSPaul Mundt 
19979174fc8fSPaul Mundt 	[SCIx_BRI_IRQ] = {
19989174fc8fSPaul Mundt 		.desc = "break",
19999174fc8fSPaul Mundt 		.handler = sci_br_interrupt,
20009174fc8fSPaul Mundt 	},
20019174fc8fSPaul Mundt 
2002628c534aSChris Brandt 	[SCIx_DRI_IRQ] = {
2003628c534aSChris Brandt 		.desc = "rx ready",
2004628c534aSChris Brandt 		.handler = sci_rx_interrupt,
2005628c534aSChris Brandt 	},
2006628c534aSChris Brandt 
2007628c534aSChris Brandt 	[SCIx_TEI_IRQ] = {
2008628c534aSChris Brandt 		.desc = "tx end",
2009d61ae331SBiju Das 		.handler = sci_tx_end_interrupt,
2010628c534aSChris Brandt 	},
2011628c534aSChris Brandt 
20129174fc8fSPaul Mundt 	/*
20139174fc8fSPaul Mundt 	 * Special muxed handler.
20149174fc8fSPaul Mundt 	 */
20159174fc8fSPaul Mundt 	[SCIx_MUX_IRQ] = {
20169174fc8fSPaul Mundt 		.desc = "mux",
20179174fc8fSPaul Mundt 		.handler = sci_mpxed_interrupt,
20189174fc8fSPaul Mundt 	},
20199174fc8fSPaul Mundt };
20209174fc8fSPaul Mundt 
sci_request_irq(struct sci_port * port)2021ab4382d2SGreg Kroah-Hartman static int sci_request_irq(struct sci_port *port)
2022ab4382d2SGreg Kroah-Hartman {
20239174fc8fSPaul Mundt 	struct uart_port *up = &port->port;
2024628c534aSChris Brandt 	int i, j, w, ret = 0;
2025ab4382d2SGreg Kroah-Hartman 
20269174fc8fSPaul Mundt 	for (i = j = 0; i < SCIx_NR_IRQS; i++, j++) {
2027d56a91e8SGeert Uytterhoeven 		const struct sci_irq_desc *desc;
20281fcc91a6SLaurent Pinchart 		int irq;
2029ab4382d2SGreg Kroah-Hartman 
2030628c534aSChris Brandt 		/* Check if already registered (muxed) */
2031628c534aSChris Brandt 		for (w = 0; w < i; w++)
2032628c534aSChris Brandt 			if (port->irqs[w] == port->irqs[i])
2033628c534aSChris Brandt 				w = i + 1;
2034628c534aSChris Brandt 		if (w > i)
2035628c534aSChris Brandt 			continue;
2036628c534aSChris Brandt 
20379174fc8fSPaul Mundt 		if (SCIx_IRQ_IS_MUXED(port)) {
20389174fc8fSPaul Mundt 			i = SCIx_MUX_IRQ;
20399174fc8fSPaul Mundt 			irq = up->irq;
20400e8963deSPaul Mundt 		} else {
20411fcc91a6SLaurent Pinchart 			irq = port->irqs[i];
20429174fc8fSPaul Mundt 
20430e8963deSPaul Mundt 			/*
20440e8963deSPaul Mundt 			 * Certain port types won't support all of the
20450e8963deSPaul Mundt 			 * available interrupt sources.
20460e8963deSPaul Mundt 			 */
20471fcc91a6SLaurent Pinchart 			if (unlikely(irq < 0))
20480e8963deSPaul Mundt 				continue;
20490e8963deSPaul Mundt 		}
20500e8963deSPaul Mundt 
20519174fc8fSPaul Mundt 		desc = sci_irq_desc + i;
20529174fc8fSPaul Mundt 		port->irqstr[j] = kasprintf(GFP_KERNEL, "%s:%s",
2053628c534aSChris Brandt 					    dev_name(up->dev), desc->desc);
2054623ac1d4SPan Bian 		if (!port->irqstr[j]) {
2055623ac1d4SPan Bian 			ret = -ENOMEM;
20569174fc8fSPaul Mundt 			goto out_nomem;
2057623ac1d4SPan Bian 		}
2058ab4382d2SGreg Kroah-Hartman 
20599174fc8fSPaul Mundt 		ret = request_irq(irq, desc->handler, up->irqflags,
20609174fc8fSPaul Mundt 				  port->irqstr[j], port);
20619174fc8fSPaul Mundt 		if (unlikely(ret)) {
20629174fc8fSPaul Mundt 			dev_err(up->dev, "Can't allocate %s IRQ\n", desc->desc);
20639174fc8fSPaul Mundt 			goto out_noirq;
2064ab4382d2SGreg Kroah-Hartman 		}
2065ab4382d2SGreg Kroah-Hartman 	}
2066ab4382d2SGreg Kroah-Hartman 
2067ab4382d2SGreg Kroah-Hartman 	return 0;
20689174fc8fSPaul Mundt 
20699174fc8fSPaul Mundt out_noirq:
20709174fc8fSPaul Mundt 	while (--i >= 0)
20711fcc91a6SLaurent Pinchart 		free_irq(port->irqs[i], port);
20729174fc8fSPaul Mundt 
20739174fc8fSPaul Mundt out_nomem:
20749174fc8fSPaul Mundt 	while (--j >= 0)
20759174fc8fSPaul Mundt 		kfree(port->irqstr[j]);
20769174fc8fSPaul Mundt 
20779174fc8fSPaul Mundt 	return ret;
2078ab4382d2SGreg Kroah-Hartman }
2079ab4382d2SGreg Kroah-Hartman 
sci_free_irq(struct sci_port * port)2080ab4382d2SGreg Kroah-Hartman static void sci_free_irq(struct sci_port *port)
2081ab4382d2SGreg Kroah-Hartman {
20824d95987aSChris Brandt 	int i, j;
2083ab4382d2SGreg Kroah-Hartman 
20849174fc8fSPaul Mundt 	/*
20859174fc8fSPaul Mundt 	 * Intentionally in reverse order so we iterate over the muxed
20869174fc8fSPaul Mundt 	 * IRQ first.
20879174fc8fSPaul Mundt 	 */
20889174fc8fSPaul Mundt 	for (i = 0; i < SCIx_NR_IRQS; i++) {
20891fcc91a6SLaurent Pinchart 		int irq = port->irqs[i];
20900e8963deSPaul Mundt 
20910e8963deSPaul Mundt 		/*
20920e8963deSPaul Mundt 		 * Certain port types won't support all of the available
20930e8963deSPaul Mundt 		 * interrupt sources.
20940e8963deSPaul Mundt 		 */
20951fcc91a6SLaurent Pinchart 		if (unlikely(irq < 0))
20960e8963deSPaul Mundt 			continue;
20970e8963deSPaul Mundt 
20984d95987aSChris Brandt 		/* Check if already freed (irq was muxed) */
20994d95987aSChris Brandt 		for (j = 0; j < i; j++)
21004d95987aSChris Brandt 			if (port->irqs[j] == irq)
21014d95987aSChris Brandt 				j = i + 1;
21024d95987aSChris Brandt 		if (j > i)
21034d95987aSChris Brandt 			continue;
21044d95987aSChris Brandt 
21051fcc91a6SLaurent Pinchart 		free_irq(port->irqs[i], port);
21069174fc8fSPaul Mundt 		kfree(port->irqstr[i]);
21079174fc8fSPaul Mundt 
21089174fc8fSPaul Mundt 		if (SCIx_IRQ_IS_MUXED(port)) {
21099174fc8fSPaul Mundt 			/* If there's only one IRQ, we're done. */
21109174fc8fSPaul Mundt 			return;
2111ab4382d2SGreg Kroah-Hartman 		}
2112ab4382d2SGreg Kroah-Hartman 	}
2113ab4382d2SGreg Kroah-Hartman }
2114ab4382d2SGreg Kroah-Hartman 
sci_tx_empty(struct uart_port * port)2115ab4382d2SGreg Kroah-Hartman static unsigned int sci_tx_empty(struct uart_port *port)
2116ab4382d2SGreg Kroah-Hartman {
21176deab514SGeert Uytterhoeven 	unsigned short status = sci_serial_in(port, SCxSR);
211872b294cfSPaul Mundt 	unsigned short in_tx_fifo = sci_txfill(port);
21197cc0e0a4SClaudiu Beznea 	struct sci_port *s = to_sci_port(port);
21207cc0e0a4SClaudiu Beznea 
21217cc0e0a4SClaudiu Beznea 	sci_dma_check_tx_occurred(s);
21227cc0e0a4SClaudiu Beznea 
21237cc0e0a4SClaudiu Beznea 	if (!s->tx_occurred)
21247cc0e0a4SClaudiu Beznea 		return TIOCSER_TEMT;
2125ab4382d2SGreg Kroah-Hartman 
2126ab4382d2SGreg Kroah-Hartman 	return (status & SCxSR_TEND(port)) && !in_tx_fifo ? TIOCSER_TEMT : 0;
2127ab4382d2SGreg Kroah-Hartman }
2128ab4382d2SGreg Kroah-Hartman 
sci_set_rts(struct uart_port * port,bool state)212933f50ffcSGeert Uytterhoeven static void sci_set_rts(struct uart_port *port, bool state)
213033f50ffcSGeert Uytterhoeven {
213133f50ffcSGeert Uytterhoeven 	if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
21326deab514SGeert Uytterhoeven 		u16 data = sci_serial_in(port, SCPDR);
213333f50ffcSGeert Uytterhoeven 
213433f50ffcSGeert Uytterhoeven 		/* Active low */
213533f50ffcSGeert Uytterhoeven 		if (state)
213633f50ffcSGeert Uytterhoeven 			data &= ~SCPDR_RTSD;
213733f50ffcSGeert Uytterhoeven 		else
213833f50ffcSGeert Uytterhoeven 			data |= SCPDR_RTSD;
21396deab514SGeert Uytterhoeven 		sci_serial_out(port, SCPDR, data);
214033f50ffcSGeert Uytterhoeven 
214133f50ffcSGeert Uytterhoeven 		/* RTS# is output */
21426deab514SGeert Uytterhoeven 		sci_serial_out(port, SCPCR,
21436deab514SGeert Uytterhoeven 			       sci_serial_in(port, SCPCR) | SCPCR_RTSC);
214433f50ffcSGeert Uytterhoeven 	} else if (sci_getreg(port, SCSPTR)->size) {
21456deab514SGeert Uytterhoeven 		u16 ctrl = sci_serial_in(port, SCSPTR);
214633f50ffcSGeert Uytterhoeven 
214733f50ffcSGeert Uytterhoeven 		/* Active low */
214833f50ffcSGeert Uytterhoeven 		if (state)
214933f50ffcSGeert Uytterhoeven 			ctrl &= ~SCSPTR_RTSDT;
215033f50ffcSGeert Uytterhoeven 		else
215133f50ffcSGeert Uytterhoeven 			ctrl |= SCSPTR_RTSDT;
21526deab514SGeert Uytterhoeven 		sci_serial_out(port, SCSPTR, ctrl);
215333f50ffcSGeert Uytterhoeven 	}
215433f50ffcSGeert Uytterhoeven }
215533f50ffcSGeert Uytterhoeven 
sci_get_cts(struct uart_port * port)215633f50ffcSGeert Uytterhoeven static bool sci_get_cts(struct uart_port *port)
215733f50ffcSGeert Uytterhoeven {
215833f50ffcSGeert Uytterhoeven 	if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
215933f50ffcSGeert Uytterhoeven 		/* Active low */
21606deab514SGeert Uytterhoeven 		return !(sci_serial_in(port, SCPDR) & SCPDR_CTSD);
216133f50ffcSGeert Uytterhoeven 	} else if (sci_getreg(port, SCSPTR)->size) {
216233f50ffcSGeert Uytterhoeven 		/* Active low */
21636deab514SGeert Uytterhoeven 		return !(sci_serial_in(port, SCSPTR) & SCSPTR_CTSDT);
216433f50ffcSGeert Uytterhoeven 	}
216533f50ffcSGeert Uytterhoeven 
216633f50ffcSGeert Uytterhoeven 	return true;
216733f50ffcSGeert Uytterhoeven }
216833f50ffcSGeert Uytterhoeven 
2169cdf7c42fSPaul Mundt /*
2170cdf7c42fSPaul Mundt  * Modem control is a bit of a mixed bag for SCI(F) ports. Generally
2171cdf7c42fSPaul Mundt  * CTS/RTS is supported in hardware by at least one port and controlled
2172cdf7c42fSPaul Mundt  * via SCSPTR (SCxPCR for SCIFA/B parts), or external pins (presently
2173cdf7c42fSPaul Mundt  * handled via the ->init_pins() op, which is a bit of a one-way street,
2174cdf7c42fSPaul Mundt  * lacking any ability to defer pin control -- this will later be
2175cdf7c42fSPaul Mundt  * converted over to the GPIO framework).
2176dc7e3ef7SPaul Mundt  *
2177dc7e3ef7SPaul Mundt  * Other modes (such as loopback) are supported generically on certain
2178dc7e3ef7SPaul Mundt  * port types, but not others. For these it's sufficient to test for the
2179dc7e3ef7SPaul Mundt  * existence of the support register and simply ignore the port type.
2180cdf7c42fSPaul Mundt  */
sci_set_mctrl(struct uart_port * port,unsigned int mctrl)2181ab4382d2SGreg Kroah-Hartman static void sci_set_mctrl(struct uart_port *port, unsigned int mctrl)
2182ab4382d2SGreg Kroah-Hartman {
2183f907c9eaSGeert Uytterhoeven 	struct sci_port *s = to_sci_port(port);
2184f907c9eaSGeert Uytterhoeven 
2185dc7e3ef7SPaul Mundt 	if (mctrl & TIOCM_LOOP) {
2186d3184e68SGeert Uytterhoeven 		const struct plat_sci_reg *reg;
2187dc7e3ef7SPaul Mundt 
2188dc7e3ef7SPaul Mundt 		/*
2189dc7e3ef7SPaul Mundt 		 * Standard loopback mode for SCFCR ports.
2190dc7e3ef7SPaul Mundt 		 */
2191dc7e3ef7SPaul Mundt 		reg = sci_getreg(port, SCFCR);
2192dc7e3ef7SPaul Mundt 		if (reg->size)
21936deab514SGeert Uytterhoeven 			sci_serial_out(port, SCFCR,
21946deab514SGeert Uytterhoeven 				       sci_serial_in(port, SCFCR) | SCFCR_LOOP);
2195dc7e3ef7SPaul Mundt 	}
2196f907c9eaSGeert Uytterhoeven 
2197f907c9eaSGeert Uytterhoeven 	mctrl_gpio_set(s->gpios, mctrl);
219833f50ffcSGeert Uytterhoeven 
219997ed9790SLaurent Pinchart 	if (!s->has_rtscts)
220033f50ffcSGeert Uytterhoeven 		return;
220133f50ffcSGeert Uytterhoeven 
220233f50ffcSGeert Uytterhoeven 	if (!(mctrl & TIOCM_RTS)) {
220333f50ffcSGeert Uytterhoeven 		/* Disable Auto RTS */
22042f50304eSLad Prabhakar 		if (s->cfg->regtype != SCIx_RZV2H_SCIF_REGTYPE)
22056deab514SGeert Uytterhoeven 			sci_serial_out(port, SCFCR,
22066deab514SGeert Uytterhoeven 				       sci_serial_in(port, SCFCR) & ~SCFCR_MCE);
220733f50ffcSGeert Uytterhoeven 
220833f50ffcSGeert Uytterhoeven 		/* Clear RTS */
220933f50ffcSGeert Uytterhoeven 		sci_set_rts(port, 0);
221033f50ffcSGeert Uytterhoeven 	} else if (s->autorts) {
221133f50ffcSGeert Uytterhoeven 		if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
221233f50ffcSGeert Uytterhoeven 			/* Enable RTS# pin function */
22136deab514SGeert Uytterhoeven 			sci_serial_out(port, SCPCR,
22146deab514SGeert Uytterhoeven 				sci_serial_in(port, SCPCR) & ~SCPCR_RTSC);
221533f50ffcSGeert Uytterhoeven 		}
221633f50ffcSGeert Uytterhoeven 
221733f50ffcSGeert Uytterhoeven 		/* Enable Auto RTS */
22182f50304eSLad Prabhakar 		if (s->cfg->regtype != SCIx_RZV2H_SCIF_REGTYPE)
22196deab514SGeert Uytterhoeven 			sci_serial_out(port, SCFCR,
22206deab514SGeert Uytterhoeven 				       sci_serial_in(port, SCFCR) | SCFCR_MCE);
222133f50ffcSGeert Uytterhoeven 	} else {
222233f50ffcSGeert Uytterhoeven 		/* Set RTS */
222333f50ffcSGeert Uytterhoeven 		sci_set_rts(port, 1);
222433f50ffcSGeert Uytterhoeven 	}
2225ab4382d2SGreg Kroah-Hartman }
2226ab4382d2SGreg Kroah-Hartman 
sci_get_mctrl(struct uart_port * port)2227ab4382d2SGreg Kroah-Hartman static unsigned int sci_get_mctrl(struct uart_port *port)
2228ab4382d2SGreg Kroah-Hartman {
2229f907c9eaSGeert Uytterhoeven 	struct sci_port *s = to_sci_port(port);
2230f907c9eaSGeert Uytterhoeven 	struct mctrl_gpios *gpios = s->gpios;
2231f907c9eaSGeert Uytterhoeven 	unsigned int mctrl = 0;
2232f907c9eaSGeert Uytterhoeven 
2233f907c9eaSGeert Uytterhoeven 	mctrl_gpio_get(gpios, &mctrl);
2234f907c9eaSGeert Uytterhoeven 
2235cdf7c42fSPaul Mundt 	/*
2236cdf7c42fSPaul Mundt 	 * CTS/RTS is handled in hardware when supported, while nothing
223733f50ffcSGeert Uytterhoeven 	 * else is wired up.
2238cdf7c42fSPaul Mundt 	 */
223933f50ffcSGeert Uytterhoeven 	if (s->autorts) {
224033f50ffcSGeert Uytterhoeven 		if (sci_get_cts(port))
2241f907c9eaSGeert Uytterhoeven 			mctrl |= TIOCM_CTS;
2242a16c4c5aSGeert Uytterhoeven 	} else if (!mctrl_gpio_to_gpiod(gpios, UART_GPIO_CTS)) {
224333f50ffcSGeert Uytterhoeven 		mctrl |= TIOCM_CTS;
224433f50ffcSGeert Uytterhoeven 	}
2245a16c4c5aSGeert Uytterhoeven 	if (!mctrl_gpio_to_gpiod(gpios, UART_GPIO_DSR))
2246f907c9eaSGeert Uytterhoeven 		mctrl |= TIOCM_DSR;
2247a16c4c5aSGeert Uytterhoeven 	if (!mctrl_gpio_to_gpiod(gpios, UART_GPIO_DCD))
2248f907c9eaSGeert Uytterhoeven 		mctrl |= TIOCM_CAR;
2249f907c9eaSGeert Uytterhoeven 
2250f907c9eaSGeert Uytterhoeven 	return mctrl;
2251f907c9eaSGeert Uytterhoeven }
2252f907c9eaSGeert Uytterhoeven 
sci_enable_ms(struct uart_port * port)2253f907c9eaSGeert Uytterhoeven static void sci_enable_ms(struct uart_port *port)
2254f907c9eaSGeert Uytterhoeven {
2255f907c9eaSGeert Uytterhoeven 	mctrl_gpio_enable_ms(to_sci_port(port)->gpios);
2256ab4382d2SGreg Kroah-Hartman }
2257ab4382d2SGreg Kroah-Hartman 
sci_break_ctl(struct uart_port * port,int break_state)2258ab4382d2SGreg Kroah-Hartman static void sci_break_ctl(struct uart_port *port, int break_state)
2259ab4382d2SGreg Kroah-Hartman {
2260bbb4ce50SShimoda, Yoshihiro 	unsigned short scscr, scsptr;
22611be22663STakatoshi Akiyama 	unsigned long flags;
2262bbb4ce50SShimoda, Yoshihiro 
226377124a42SWang Qing 	/* check whether the port has SCSPTR */
2264abbf121fSGeert Uytterhoeven 	if (!sci_getreg(port, SCSPTR)->size) {
2265a4e02f6dSShimoda, Yoshihiro 		/*
2266a4e02f6dSShimoda, Yoshihiro 		 * Not supported by hardware. Most parts couple break and rx
2267a4e02f6dSShimoda, Yoshihiro 		 * interrupts together, with break detection always enabled.
2268a4e02f6dSShimoda, Yoshihiro 		 */
2269a4e02f6dSShimoda, Yoshihiro 		return;
2270a4e02f6dSShimoda, Yoshihiro 	}
2271a4e02f6dSShimoda, Yoshihiro 
227294c53770SThomas Gleixner 	uart_port_lock_irqsave(port, &flags);
22736deab514SGeert Uytterhoeven 	scsptr = sci_serial_in(port, SCSPTR);
22746deab514SGeert Uytterhoeven 	scscr = sci_serial_in(port, SCSCR);
2275bbb4ce50SShimoda, Yoshihiro 
2276bbb4ce50SShimoda, Yoshihiro 	if (break_state == -1) {
2277bbb4ce50SShimoda, Yoshihiro 		scsptr = (scsptr | SCSPTR_SPB2IO) & ~SCSPTR_SPB2DT;
2278bbb4ce50SShimoda, Yoshihiro 		scscr &= ~SCSCR_TE;
2279bbb4ce50SShimoda, Yoshihiro 	} else {
2280bbb4ce50SShimoda, Yoshihiro 		scsptr = (scsptr | SCSPTR_SPB2DT) & ~SCSPTR_SPB2IO;
2281bbb4ce50SShimoda, Yoshihiro 		scscr |= SCSCR_TE;
2282bbb4ce50SShimoda, Yoshihiro 	}
2283bbb4ce50SShimoda, Yoshihiro 
22846deab514SGeert Uytterhoeven 	sci_serial_out(port, SCSPTR, scsptr);
22856deab514SGeert Uytterhoeven 	sci_serial_out(port, SCSCR, scscr);
228694c53770SThomas Gleixner 	uart_port_unlock_irqrestore(port, flags);
2287ab4382d2SGreg Kroah-Hartman }
2288ab4382d2SGreg Kroah-Hartman 
sci_startup(struct uart_port * port)2289ab4382d2SGreg Kroah-Hartman static int sci_startup(struct uart_port *port)
2290ab4382d2SGreg Kroah-Hartman {
2291ab4382d2SGreg Kroah-Hartman 	struct sci_port *s = to_sci_port(port);
22926b620478SPaul Mundt 	int ret;
2293ab4382d2SGreg Kroah-Hartman 
2294ab4382d2SGreg Kroah-Hartman 	dev_dbg(port->dev, "%s(%d)\n", __func__, port->line);
2295ab4382d2SGreg Kroah-Hartman 
22967cc0e0a4SClaudiu Beznea 	s->tx_occurred = false;
2297ab4382d2SGreg Kroah-Hartman 	sci_request_dma(port);
22986b620478SPaul Mundt 
22993c910176STakatoshi Akiyama 	ret = sci_request_irq(s);
23003c910176STakatoshi Akiyama 	if (unlikely(ret < 0)) {
23013c910176STakatoshi Akiyama 		sci_free_dma(port);
23023c910176STakatoshi Akiyama 		return ret;
23033c910176STakatoshi Akiyama 	}
23043c910176STakatoshi Akiyama 
2305ab4382d2SGreg Kroah-Hartman 	return 0;
2306ab4382d2SGreg Kroah-Hartman }
2307ab4382d2SGreg Kroah-Hartman 
sci_shutdown(struct uart_port * port)2308ab4382d2SGreg Kroah-Hartman static void sci_shutdown(struct uart_port *port)
2309ab4382d2SGreg Kroah-Hartman {
2310ab4382d2SGreg Kroah-Hartman 	struct sci_port *s = to_sci_port(port);
231133b48e16SShinya Kuribayashi 	unsigned long flags;
23125fd2b6eeSGeert Uytterhoeven 	u16 scr;
2313ab4382d2SGreg Kroah-Hartman 
2314ab4382d2SGreg Kroah-Hartman 	dev_dbg(port->dev, "%s(%d)\n", __func__, port->line);
2315ab4382d2SGreg Kroah-Hartman 
231633f50ffcSGeert Uytterhoeven 	s->autorts = false;
23171bd2aad5SAlexis Lothoré 	mctrl_gpio_disable_ms_sync(to_sci_port(port)->gpios);
2318f907c9eaSGeert Uytterhoeven 
231994c53770SThomas Gleixner 	uart_port_lock_irqsave(port, &flags);
2320ab4382d2SGreg Kroah-Hartman 	sci_stop_rx(port);
2321ab4382d2SGreg Kroah-Hartman 	sci_stop_tx(port);
2322fa2abb03SUlrich Hecht 	/*
2323fa2abb03SUlrich Hecht 	 * Stop RX and TX, disable related interrupts, keep clock source
2324fa2abb03SUlrich Hecht 	 * and HSCIF TOT bits
2325fa2abb03SUlrich Hecht 	 */
23266deab514SGeert Uytterhoeven 	scr = sci_serial_in(port, SCSCR);
23276deab514SGeert Uytterhoeven 	sci_serial_out(port, SCSCR,
23286deab514SGeert Uytterhoeven 		       scr & (SCSCR_CKE1 | SCSCR_CKE0 | s->hscif_tot));
232994c53770SThomas Gleixner 	uart_port_unlock_irqrestore(port, flags);
23306b620478SPaul Mundt 
23319ab76556SAleksandar Mitev #ifdef CONFIG_SERIAL_SH_SCI_DMA
23322c4ee235SGeert Uytterhoeven 	if (s->chan_rx_saved) {
23339ab76556SAleksandar Mitev 		dev_dbg(port->dev, "%s(%d) deleting rx_timer\n", __func__,
23349ab76556SAleksandar Mitev 			port->line);
2335b96408b4SUlrich Hecht 		hrtimer_cancel(&s->rx_timer);
23369ab76556SAleksandar Mitev 	}
23379ab76556SAleksandar Mitev #endif
23389ab76556SAleksandar Mitev 
2339c5a9262fSGeert Uytterhoeven 	if (s->rx_trigger > 1 && s->rx_fifo_timeout > 0)
2340*8fa7292fSThomas Gleixner 		timer_delete_sync(&s->rx_fifo_timer);
2341ab4382d2SGreg Kroah-Hartman 	sci_free_irq(s);
23423c910176STakatoshi Akiyama 	sci_free_dma(port);
2343ab4382d2SGreg Kroah-Hartman }
2344ab4382d2SGreg Kroah-Hartman 
sci_sck_calc(struct sci_port * s,unsigned int bps,unsigned int * srr)23456af27bf2SGeert Uytterhoeven static int sci_sck_calc(struct sci_port *s, unsigned int bps,
23466af27bf2SGeert Uytterhoeven 			unsigned int *srr)
2347fc887b15SLinus Torvalds {
23486af27bf2SGeert Uytterhoeven 	unsigned long freq = s->clk_rates[SCI_SCK];
23496af27bf2SGeert Uytterhoeven 	int err, min_err = INT_MAX;
235069eee8e9SGeert Uytterhoeven 	unsigned int sr;
2351ec09c5ebSLaurent Pinchart 
23527b5c0c08SGeert Uytterhoeven 	if (s->port.type != PORT_HSCIF)
23537b5c0c08SGeert Uytterhoeven 		freq *= 2;
2354fc887b15SLinus Torvalds 
235569eee8e9SGeert Uytterhoeven 	for_each_sr(sr, s) {
23566af27bf2SGeert Uytterhoeven 		err = DIV_ROUND_CLOSEST(freq, sr) - bps;
23576af27bf2SGeert Uytterhoeven 		if (abs(err) >= abs(min_err))
23586af27bf2SGeert Uytterhoeven 			continue;
2359730c4e78SNobuhiro Iwamatsu 
23606af27bf2SGeert Uytterhoeven 		min_err = err;
23616af27bf2SGeert Uytterhoeven 		*srr = sr - 1;
2362730c4e78SNobuhiro Iwamatsu 
23636af27bf2SGeert Uytterhoeven 		if (!err)
23646af27bf2SGeert Uytterhoeven 			break;
2365730c4e78SNobuhiro Iwamatsu 	}
2366730c4e78SNobuhiro Iwamatsu 
23676af27bf2SGeert Uytterhoeven 	dev_dbg(s->port.dev, "SCK: %u%+d bps using SR %u\n", bps, min_err,
23686af27bf2SGeert Uytterhoeven 		*srr + 1);
23696af27bf2SGeert Uytterhoeven 	return min_err;
23706af27bf2SGeert Uytterhoeven }
2371730c4e78SNobuhiro Iwamatsu 
sci_brg_calc(struct sci_port * s,unsigned int bps,unsigned long freq,unsigned int * dlr,unsigned int * srr)23721270f865SGeert Uytterhoeven static int sci_brg_calc(struct sci_port *s, unsigned int bps,
23731270f865SGeert Uytterhoeven 			unsigned long freq, unsigned int *dlr,
23741270f865SGeert Uytterhoeven 			unsigned int *srr)
2375f303b364SUlrich Hecht {
23761270f865SGeert Uytterhoeven 	int err, min_err = INT_MAX;
237769eee8e9SGeert Uytterhoeven 	unsigned int sr, dl;
2378f303b364SUlrich Hecht 
23797b5c0c08SGeert Uytterhoeven 	if (s->port.type != PORT_HSCIF)
23807b5c0c08SGeert Uytterhoeven 		freq *= 2;
23811270f865SGeert Uytterhoeven 
238269eee8e9SGeert Uytterhoeven 	for_each_sr(sr, s) {
23831270f865SGeert Uytterhoeven 		dl = DIV_ROUND_CLOSEST(freq, sr * bps);
23841270f865SGeert Uytterhoeven 		dl = clamp(dl, 1U, 65535U);
23851270f865SGeert Uytterhoeven 
23861270f865SGeert Uytterhoeven 		err = DIV_ROUND_CLOSEST(freq, sr * dl) - bps;
23871270f865SGeert Uytterhoeven 		if (abs(err) >= abs(min_err))
23881270f865SGeert Uytterhoeven 			continue;
23891270f865SGeert Uytterhoeven 
23901270f865SGeert Uytterhoeven 		min_err = err;
23911270f865SGeert Uytterhoeven 		*dlr = dl;
23921270f865SGeert Uytterhoeven 		*srr = sr - 1;
23931270f865SGeert Uytterhoeven 
23941270f865SGeert Uytterhoeven 		if (!err)
23951270f865SGeert Uytterhoeven 			break;
23961270f865SGeert Uytterhoeven 	}
23971270f865SGeert Uytterhoeven 
23981270f865SGeert Uytterhoeven 	dev_dbg(s->port.dev, "BRG: %u%+d bps using DL %u SR %u\n", bps,
23991270f865SGeert Uytterhoeven 		min_err, *dlr, *srr + 1);
24001270f865SGeert Uytterhoeven 	return min_err;
24011270f865SGeert Uytterhoeven }
24021270f865SGeert Uytterhoeven 
2403b4a5c459SGeert Uytterhoeven /* calculate sample rate, BRR, and clock select */
sci_scbrr_calc(struct sci_port * s,unsigned int bps,unsigned int * brr,unsigned int * srr,unsigned int * cks)2404f4998e55SGeert Uytterhoeven static int sci_scbrr_calc(struct sci_port *s, unsigned int bps,
2405f4998e55SGeert Uytterhoeven 			  unsigned int *brr, unsigned int *srr,
2406b4a5c459SGeert Uytterhoeven 			  unsigned int *cks)
2407ab4382d2SGreg Kroah-Hartman {
2408f4998e55SGeert Uytterhoeven 	unsigned long freq = s->clk_rates[SCI_FCK];
240969eee8e9SGeert Uytterhoeven 	unsigned int sr, br, prediv, scrate, c;
24106c51332dSGeert Uytterhoeven 	int err, min_err = INT_MAX;
2411f303b364SUlrich Hecht 
24127b5c0c08SGeert Uytterhoeven 	if (s->port.type != PORT_HSCIF)
24137b5c0c08SGeert Uytterhoeven 		freq *= 2;
2414b4a5c459SGeert Uytterhoeven 
24156c51332dSGeert Uytterhoeven 	/*
24166c51332dSGeert Uytterhoeven 	 * Find the combination of sample rate and clock select with the
24176c51332dSGeert Uytterhoeven 	 * smallest deviation from the desired baud rate.
24186c51332dSGeert Uytterhoeven 	 * Prefer high sample rates to maximise the receive margin.
24196c51332dSGeert Uytterhoeven 	 *
2420730c4e78SNobuhiro Iwamatsu 	 * M: Receive margin (%)
2421730c4e78SNobuhiro Iwamatsu 	 * N: Ratio of bit rate to clock (N = sampling rate)
2422730c4e78SNobuhiro Iwamatsu 	 * D: Clock duty (D = 0 to 1.0)
2423730c4e78SNobuhiro Iwamatsu 	 * L: Frame length (L = 9 to 12)
2424730c4e78SNobuhiro Iwamatsu 	 * F: Absolute value of clock frequency deviation
2425730c4e78SNobuhiro Iwamatsu 	 *
2426730c4e78SNobuhiro Iwamatsu 	 *  M = |(0.5 - 1 / 2 * N) - ((L - 0.5) * F) -
2427730c4e78SNobuhiro Iwamatsu 	 *      (|D - 0.5| / N * (1 + F))|
24286c51332dSGeert Uytterhoeven 	 *  NOTE: Usually, treat D for 0.5, F is 0 by this calculation.
2429730c4e78SNobuhiro Iwamatsu 	 */
243069eee8e9SGeert Uytterhoeven 	for_each_sr(sr, s) {
2431b7d66397SNobuhiro Iwamatsu 		for (c = 0; c <= 3; c++) {
2432bcb9973aSNobuhiro Iwamatsu 			/* integerized formulas from HSCIF documentation */
243381ddb200SGeert Uytterhoeven 			prediv = sr << (2 * c + 1);
2434de01e6cdSGeert Uytterhoeven 
2435de01e6cdSGeert Uytterhoeven 			/*
2436de01e6cdSGeert Uytterhoeven 			 * We need to calculate:
2437de01e6cdSGeert Uytterhoeven 			 *
2438de01e6cdSGeert Uytterhoeven 			 *     br = freq / (prediv * bps) clamped to [1..256]
2439881a7489SGeert Uytterhoeven 			 *     err = freq / (br * prediv) - bps
2440de01e6cdSGeert Uytterhoeven 			 *
2441de01e6cdSGeert Uytterhoeven 			 * Watch out for overflow when calculating the desired
2442de01e6cdSGeert Uytterhoeven 			 * sampling clock rate!
2443de01e6cdSGeert Uytterhoeven 			 */
2444de01e6cdSGeert Uytterhoeven 			if (bps > UINT_MAX / prediv)
2445de01e6cdSGeert Uytterhoeven 				break;
2446de01e6cdSGeert Uytterhoeven 
2447de01e6cdSGeert Uytterhoeven 			scrate = prediv * bps;
2448de01e6cdSGeert Uytterhoeven 			br = DIV_ROUND_CLOSEST(freq, scrate);
244995a2703eSGeert Uytterhoeven 			br = clamp(br, 1U, 256U);
24506c51332dSGeert Uytterhoeven 
2451881a7489SGeert Uytterhoeven 			err = DIV_ROUND_CLOSEST(freq, br * prediv) - bps;
24526c51332dSGeert Uytterhoeven 			if (abs(err) >= abs(min_err))
2453730c4e78SNobuhiro Iwamatsu 				continue;
2454730c4e78SNobuhiro Iwamatsu 
24556c51332dSGeert Uytterhoeven 			min_err = err;
245695a2703eSGeert Uytterhoeven 			*brr = br - 1;
2457f303b364SUlrich Hecht 			*srr = sr - 1;
2458f303b364SUlrich Hecht 			*cks = c;
24596c51332dSGeert Uytterhoeven 
24606c51332dSGeert Uytterhoeven 			if (!err)
24616c51332dSGeert Uytterhoeven 				goto found;
2462f303b364SUlrich Hecht 		}
2463f303b364SUlrich Hecht 	}
2464f303b364SUlrich Hecht 
24656c51332dSGeert Uytterhoeven found:
2466881a7489SGeert Uytterhoeven 	dev_dbg(s->port.dev, "BRR: %u%+d bps using N %u SR %u cks %u\n", bps,
2467881a7489SGeert Uytterhoeven 		min_err, *brr, *srr + 1, *cks);
2468f4998e55SGeert Uytterhoeven 	return min_err;
2469f303b364SUlrich Hecht }
2470f303b364SUlrich Hecht 
sci_reset(struct uart_port * port)24711ba76220SMagnus Damm static void sci_reset(struct uart_port *port)
24721ba76220SMagnus Damm {
2473d3184e68SGeert Uytterhoeven 	const struct plat_sci_reg *reg;
24741ba76220SMagnus Damm 	unsigned int status;
247518e8cf15SUlrich Hecht 	struct sci_port *s = to_sci_port(port);
24761ba76220SMagnus Damm 
24776deab514SGeert Uytterhoeven 	sci_serial_out(port, SCSCR, s->hscif_tot);	/* TE=0, RE=0, CKE1=0 */
24781ba76220SMagnus Damm 
24790979e0e6SPaul Mundt 	reg = sci_getreg(port, SCFCR);
24800979e0e6SPaul Mundt 	if (reg->size)
24816deab514SGeert Uytterhoeven 		sci_serial_out(port, SCFCR, SCFCR_RFRST | SCFCR_TFRST);
24822768cf42SGeert Uytterhoeven 
24832768cf42SGeert Uytterhoeven 	sci_clear_SCxSR(port,
24842768cf42SGeert Uytterhoeven 			SCxSR_RDxF_CLEAR(port) & SCxSR_ERROR_CLEAR(port) &
24852768cf42SGeert Uytterhoeven 			SCxSR_BREAK_CLEAR(port));
2486fc2af334SGeert Uytterhoeven 	if (sci_getreg(port, SCLSR)->size) {
24876deab514SGeert Uytterhoeven 		status = sci_serial_in(port, SCLSR);
2488fc2af334SGeert Uytterhoeven 		status &= ~(SCLSR_TO | SCLSR_ORER);
24896deab514SGeert Uytterhoeven 		sci_serial_out(port, SCLSR, status);
2490fc2af334SGeert Uytterhoeven 	}
249118e8cf15SUlrich Hecht 
249203940376SUlrich Hecht 	if (s->rx_trigger > 1) {
249303940376SUlrich Hecht 		if (s->rx_fifo_timeout) {
249403940376SUlrich Hecht 			scif_set_rtrg(port, 1);
2495e99e88a9SKees Cook 			timer_setup(&s->rx_fifo_timer, rx_fifo_timer_fn, 0);
249603940376SUlrich Hecht 		} else {
249790afa525SUlrich Hecht 			if (port->type == PORT_SCIFA ||
249890afa525SUlrich Hecht 			    port->type == PORT_SCIFB)
249990afa525SUlrich Hecht 				scif_set_rtrg(port, 1);
250090afa525SUlrich Hecht 			else
250118e8cf15SUlrich Hecht 				scif_set_rtrg(port, s->rx_trigger);
25021ba76220SMagnus Damm 		}
250303940376SUlrich Hecht 	}
250403940376SUlrich Hecht }
25051ba76220SMagnus Damm 
sci_set_termios(struct uart_port * port,struct ktermios * termios,const struct ktermios * old)2506ab4382d2SGreg Kroah-Hartman static void sci_set_termios(struct uart_port *port, struct ktermios *termios,
2507bec5b814SIlpo Järvinen 		            const struct ktermios *old)
2508ab4382d2SGreg Kroah-Hartman {
250903940376SUlrich Hecht 	unsigned int baud, smr_val = SCSMR_ASYNC, scr_val = 0, i, bits;
25101270f865SGeert Uytterhoeven 	unsigned int brr = 255, cks = 0, srr = 15, dl = 0, sccks = 0;
25111270f865SGeert Uytterhoeven 	unsigned int brr1 = 255, cks1 = 0, srr1 = 15, dl1 = 0;
2512ab4382d2SGreg Kroah-Hartman 	struct sci_port *s = to_sci_port(port);
2513d3184e68SGeert Uytterhoeven 	const struct plat_sci_reg *reg;
2514f4998e55SGeert Uytterhoeven 	int min_err = INT_MAX, err;
2515f4998e55SGeert Uytterhoeven 	unsigned long max_freq = 0;
2516f4998e55SGeert Uytterhoeven 	int best_clk = -1;
25171be22663STakatoshi Akiyama 	unsigned long flags;
2518ab4382d2SGreg Kroah-Hartman 
25199b87162dSIlpo Järvinen 	if ((termios->c_cflag & CSIZE) == CS7) {
2520730c4e78SNobuhiro Iwamatsu 		smr_val |= SCSMR_CHR;
25219b87162dSIlpo Järvinen 	} else {
25229b87162dSIlpo Järvinen 		termios->c_cflag &= ~CSIZE;
25239b87162dSIlpo Järvinen 		termios->c_cflag |= CS8;
25249b87162dSIlpo Järvinen 	}
2525730c4e78SNobuhiro Iwamatsu 	if (termios->c_cflag & PARENB)
2526730c4e78SNobuhiro Iwamatsu 		smr_val |= SCSMR_PE;
2527730c4e78SNobuhiro Iwamatsu 	if (termios->c_cflag & PARODD)
2528730c4e78SNobuhiro Iwamatsu 		smr_val |= SCSMR_PE | SCSMR_ODD;
2529730c4e78SNobuhiro Iwamatsu 	if (termios->c_cflag & CSTOPB)
2530730c4e78SNobuhiro Iwamatsu 		smr_val |= SCSMR_STOP;
2531730c4e78SNobuhiro Iwamatsu 
2532ab4382d2SGreg Kroah-Hartman 	/*
2533ab4382d2SGreg Kroah-Hartman 	 * earlyprintk comes here early on with port->uartclk set to zero.
2534ab4382d2SGreg Kroah-Hartman 	 * the clock framework is not up and running at this point so here
2535ab4382d2SGreg Kroah-Hartman 	 * we assume that 115200 is the maximum baud rate. please note that
2536ab4382d2SGreg Kroah-Hartman 	 * the baud rate is not programmed during earlyprintk - it is assumed
2537ab4382d2SGreg Kroah-Hartman 	 * that the previous boot loader has enabled required clocks and
2538ab4382d2SGreg Kroah-Hartman 	 * setup the baud rate generator hardware for us already.
2539ab4382d2SGreg Kroah-Hartman 	 */
2540f4998e55SGeert Uytterhoeven 	if (!port->uartclk) {
2541f4998e55SGeert Uytterhoeven 		baud = uart_get_baud_rate(port, termios, old, 0, 115200);
2542f4998e55SGeert Uytterhoeven 		goto done;
2543f4998e55SGeert Uytterhoeven 	}
2544ab4382d2SGreg Kroah-Hartman 
2545f4998e55SGeert Uytterhoeven 	for (i = 0; i < SCI_NUM_CLKS; i++)
2546f4998e55SGeert Uytterhoeven 		max_freq = max(max_freq, s->clk_rates[i]);
2547f4998e55SGeert Uytterhoeven 
254869eee8e9SGeert Uytterhoeven 	baud = uart_get_baud_rate(port, termios, old, 0, max_freq / min_sr(s));
2549f4998e55SGeert Uytterhoeven 	if (!baud)
2550f4998e55SGeert Uytterhoeven 		goto done;
2551f4998e55SGeert Uytterhoeven 
2552f4998e55SGeert Uytterhoeven 	/*
2553f4998e55SGeert Uytterhoeven 	 * There can be multiple sources for the sampling clock.  Find the one
2554f4998e55SGeert Uytterhoeven 	 * that gives us the smallest deviation from the desired baud rate.
2555f4998e55SGeert Uytterhoeven 	 */
2556f4998e55SGeert Uytterhoeven 
25576af27bf2SGeert Uytterhoeven 	/* Optional Undivided External Clock */
25586af27bf2SGeert Uytterhoeven 	if (s->clk_rates[SCI_SCK] && port->type != PORT_SCIFA &&
25596af27bf2SGeert Uytterhoeven 	    port->type != PORT_SCIFB) {
25606af27bf2SGeert Uytterhoeven 		err = sci_sck_calc(s, baud, &srr1);
25616af27bf2SGeert Uytterhoeven 		if (abs(err) < abs(min_err)) {
25626af27bf2SGeert Uytterhoeven 			best_clk = SCI_SCK;
25636af27bf2SGeert Uytterhoeven 			scr_val = SCSCR_CKE1;
25646af27bf2SGeert Uytterhoeven 			sccks = SCCKS_CKS;
25656af27bf2SGeert Uytterhoeven 			min_err = err;
25666af27bf2SGeert Uytterhoeven 			srr = srr1;
25676af27bf2SGeert Uytterhoeven 			if (!err)
25686af27bf2SGeert Uytterhoeven 				goto done;
2569f303b364SUlrich Hecht 		}
2570f303b364SUlrich Hecht 	}
2571ab4382d2SGreg Kroah-Hartman 
25721270f865SGeert Uytterhoeven 	/* Optional BRG Frequency Divided External Clock */
25731270f865SGeert Uytterhoeven 	if (s->clk_rates[SCI_SCIF_CLK] && sci_getreg(port, SCDL)->size) {
25741270f865SGeert Uytterhoeven 		err = sci_brg_calc(s, baud, s->clk_rates[SCI_SCIF_CLK], &dl1,
25751270f865SGeert Uytterhoeven 				   &srr1);
25761270f865SGeert Uytterhoeven 		if (abs(err) < abs(min_err)) {
25771270f865SGeert Uytterhoeven 			best_clk = SCI_SCIF_CLK;
25781270f865SGeert Uytterhoeven 			scr_val = SCSCR_CKE1;
25791270f865SGeert Uytterhoeven 			sccks = 0;
25801270f865SGeert Uytterhoeven 			min_err = err;
25811270f865SGeert Uytterhoeven 			dl = dl1;
25821270f865SGeert Uytterhoeven 			srr = srr1;
25831270f865SGeert Uytterhoeven 			if (!err)
25841270f865SGeert Uytterhoeven 				goto done;
25851270f865SGeert Uytterhoeven 		}
25861270f865SGeert Uytterhoeven 	}
25871270f865SGeert Uytterhoeven 
25881270f865SGeert Uytterhoeven 	/* Optional BRG Frequency Divided Internal Clock */
25891270f865SGeert Uytterhoeven 	if (s->clk_rates[SCI_BRG_INT] && sci_getreg(port, SCDL)->size) {
25901270f865SGeert Uytterhoeven 		err = sci_brg_calc(s, baud, s->clk_rates[SCI_BRG_INT], &dl1,
25911270f865SGeert Uytterhoeven 				   &srr1);
25921270f865SGeert Uytterhoeven 		if (abs(err) < abs(min_err)) {
25931270f865SGeert Uytterhoeven 			best_clk = SCI_BRG_INT;
25941270f865SGeert Uytterhoeven 			scr_val = SCSCR_CKE1;
25951270f865SGeert Uytterhoeven 			sccks = SCCKS_XIN;
25961270f865SGeert Uytterhoeven 			min_err = err;
25971270f865SGeert Uytterhoeven 			dl = dl1;
25981270f865SGeert Uytterhoeven 			srr = srr1;
25991270f865SGeert Uytterhoeven 			if (!min_err)
26001270f865SGeert Uytterhoeven 				goto done;
26011270f865SGeert Uytterhoeven 		}
26021270f865SGeert Uytterhoeven 	}
26031270f865SGeert Uytterhoeven 
2604f4998e55SGeert Uytterhoeven 	/* Divided Functional Clock using standard Bit Rate Register */
2605f4998e55SGeert Uytterhoeven 	err = sci_scbrr_calc(s, baud, &brr1, &srr1, &cks1);
2606f4998e55SGeert Uytterhoeven 	if (abs(err) < abs(min_err)) {
2607f4998e55SGeert Uytterhoeven 		best_clk = SCI_FCK;
26086af27bf2SGeert Uytterhoeven 		scr_val = 0;
2609f4998e55SGeert Uytterhoeven 		min_err = err;
2610f4998e55SGeert Uytterhoeven 		brr = brr1;
2611f4998e55SGeert Uytterhoeven 		srr = srr1;
2612f4998e55SGeert Uytterhoeven 		cks = cks1;
2613f4998e55SGeert Uytterhoeven 	}
2614f4998e55SGeert Uytterhoeven 
2615f4998e55SGeert Uytterhoeven done:
2616f4998e55SGeert Uytterhoeven 	if (best_clk >= 0)
2617f4998e55SGeert Uytterhoeven 		dev_dbg(port->dev, "Using clk %pC for %u%+d bps\n",
2618f4998e55SGeert Uytterhoeven 			s->clks[best_clk], baud, min_err);
2619ab4382d2SGreg Kroah-Hartman 
262023241d43SPaul Mundt 	sci_port_enable(s);
262136003386SAlexandre Courbot 
26226af27bf2SGeert Uytterhoeven 	/*
26236af27bf2SGeert Uytterhoeven 	 * Program the optional External Baud Rate Generator (BRG) first.
26246af27bf2SGeert Uytterhoeven 	 * It controls the mux to select (H)SCK or frequency divided clock.
26256af27bf2SGeert Uytterhoeven 	 */
26261270f865SGeert Uytterhoeven 	if (best_clk >= 0 && sci_getreg(port, SCCKS)->size) {
26276deab514SGeert Uytterhoeven 		sci_serial_out(port, SCDL, dl);
26286deab514SGeert Uytterhoeven 		sci_serial_out(port, SCCKS, sccks);
26291270f865SGeert Uytterhoeven 	}
2630ab4382d2SGreg Kroah-Hartman 
263194c53770SThomas Gleixner 	uart_port_lock_irqsave(port, &flags);
26321be22663STakatoshi Akiyama 
2633ab4382d2SGreg Kroah-Hartman 	sci_reset(port);
2634ab4382d2SGreg Kroah-Hartman 
2635ab4382d2SGreg Kroah-Hartman 	uart_update_timeout(port, termios->c_cflag, baud);
2636ab4382d2SGreg Kroah-Hartman 
263763ba1e00SUlrich Hecht 	/* byte size and parity */
26383ec2ff37SJiri Slaby 	bits = tty_get_frame_size(termios->c_cflag);
263963ba1e00SUlrich Hecht 
26403b2cd606SBiju Das 	if (sci_getreg(port, SEMR)->size)
26416deab514SGeert Uytterhoeven 		sci_serial_out(port, SEMR, 0);
26423b2cd606SBiju Das 
2643f4998e55SGeert Uytterhoeven 	if (best_clk >= 0) {
264492a05748SGeert Uytterhoeven 		if (port->type == PORT_SCIFA || port->type == PORT_SCIFB)
264592a05748SGeert Uytterhoeven 			switch (srr + 1) {
264692a05748SGeert Uytterhoeven 			case 5:  smr_val |= SCSMR_SRC_5;  break;
264792a05748SGeert Uytterhoeven 			case 7:  smr_val |= SCSMR_SRC_7;  break;
264892a05748SGeert Uytterhoeven 			case 11: smr_val |= SCSMR_SRC_11; break;
264992a05748SGeert Uytterhoeven 			case 13: smr_val |= SCSMR_SRC_13; break;
265092a05748SGeert Uytterhoeven 			case 16: smr_val |= SCSMR_SRC_16; break;
265192a05748SGeert Uytterhoeven 			case 17: smr_val |= SCSMR_SRC_17; break;
265292a05748SGeert Uytterhoeven 			case 19: smr_val |= SCSMR_SRC_19; break;
265392a05748SGeert Uytterhoeven 			case 27: smr_val |= SCSMR_SRC_27; break;
265492a05748SGeert Uytterhoeven 			}
2655f4998e55SGeert Uytterhoeven 		smr_val |= cks;
26566deab514SGeert Uytterhoeven 		sci_serial_out(port, SCSCR, scr_val | s->hscif_tot);
26576deab514SGeert Uytterhoeven 		sci_serial_out(port, SCSMR, smr_val);
26586deab514SGeert Uytterhoeven 		sci_serial_out(port, SCBRR, brr);
265963ba1e00SUlrich Hecht 		if (sci_getreg(port, HSSRR)->size) {
266063ba1e00SUlrich Hecht 			unsigned int hssrr = srr | HSCIF_SRE;
266163ba1e00SUlrich Hecht 			/* Calculate deviation from intended rate at the
266263ba1e00SUlrich Hecht 			 * center of the last stop bit in sampling clocks.
266363ba1e00SUlrich Hecht 			 */
266463ba1e00SUlrich Hecht 			int last_stop = bits * 2 - 1;
2665ace96569SGeert Uytterhoeven 			int deviation = DIV_ROUND_CLOSEST(min_err * last_stop *
2666ace96569SGeert Uytterhoeven 							  (int)(srr + 1),
2667ace96569SGeert Uytterhoeven 							  2 * (int)baud);
266863ba1e00SUlrich Hecht 
266963ba1e00SUlrich Hecht 			if (abs(deviation) >= 2) {
267063ba1e00SUlrich Hecht 				/* At least two sampling clocks off at the
267163ba1e00SUlrich Hecht 				 * last stop bit; we can increase the error
267263ba1e00SUlrich Hecht 				 * margin by shifting the sampling point.
267363ba1e00SUlrich Hecht 				 */
26746b87784bSGeert Uytterhoeven 				int shift = clamp(deviation / 2, -8, 7);
267563ba1e00SUlrich Hecht 
267663ba1e00SUlrich Hecht 				hssrr |= (shift << HSCIF_SRHP_SHIFT) &
267763ba1e00SUlrich Hecht 					 HSCIF_SRHP_MASK;
267863ba1e00SUlrich Hecht 				hssrr |= HSCIF_SRDE;
267963ba1e00SUlrich Hecht 			}
26806deab514SGeert Uytterhoeven 			sci_serial_out(port, HSSRR, hssrr);
268163ba1e00SUlrich Hecht 		}
2682f4998e55SGeert Uytterhoeven 
2683f4998e55SGeert Uytterhoeven 		/* Wait one bit interval */
2684f4998e55SGeert Uytterhoeven 		udelay((1000000 + (baud - 1)) / baud);
2685f4998e55SGeert Uytterhoeven 	} else {
2686f4998e55SGeert Uytterhoeven 		/* Don't touch the bit rate configuration */
2687f4998e55SGeert Uytterhoeven 		scr_val = s->cfg->scscr & (SCSCR_CKE1 | SCSCR_CKE0);
26886deab514SGeert Uytterhoeven 		smr_val |= sci_serial_in(port, SCSMR) &
26893a964abeSGeert Uytterhoeven 			   (SCSMR_CKEDG | SCSMR_SRC_MASK | SCSMR_CKS);
26906deab514SGeert Uytterhoeven 		sci_serial_out(port, SCSCR, scr_val | s->hscif_tot);
26916deab514SGeert Uytterhoeven 		sci_serial_out(port, SCSMR, smr_val);
2692f4998e55SGeert Uytterhoeven 	}
2693ab4382d2SGreg Kroah-Hartman 
2694ab4382d2SGreg Kroah-Hartman 	sci_init_pins(port, termios->c_cflag);
26950979e0e6SPaul Mundt 
269633f50ffcSGeert Uytterhoeven 	port->status &= ~UPSTAT_AUTOCTS;
269733f50ffcSGeert Uytterhoeven 	s->autorts = false;
26980979e0e6SPaul Mundt 	reg = sci_getreg(port, SCFCR);
26990979e0e6SPaul Mundt 	if (reg->size) {
27006deab514SGeert Uytterhoeven 		unsigned short ctrl = sci_serial_in(port, SCFCR);
27010979e0e6SPaul Mundt 
270233f50ffcSGeert Uytterhoeven 		if ((port->flags & UPF_HARD_FLOW) &&
270333f50ffcSGeert Uytterhoeven 		    (termios->c_cflag & CRTSCTS)) {
270433f50ffcSGeert Uytterhoeven 			/* There is no CTS interrupt to restart the hardware */
270533f50ffcSGeert Uytterhoeven 			port->status |= UPSTAT_AUTOCTS;
270633f50ffcSGeert Uytterhoeven 			/* MCE is enabled when RTS is raised */
270733f50ffcSGeert Uytterhoeven 			s->autorts = true;
27080979e0e6SPaul Mundt 		}
270973c3d53fSPaul Mundt 
271073c3d53fSPaul Mundt 		/*
271173c3d53fSPaul Mundt 		 * As we've done a sci_reset() above, ensure we don't
271273c3d53fSPaul Mundt 		 * interfere with the FIFOs while toggling MCE. As the
271373c3d53fSPaul Mundt 		 * reset values could still be set, simply mask them out.
271473c3d53fSPaul Mundt 		 */
271573c3d53fSPaul Mundt 		ctrl &= ~(SCFCR_RFRST | SCFCR_TFRST);
271673c3d53fSPaul Mundt 
27176deab514SGeert Uytterhoeven 		sci_serial_out(port, SCFCR, ctrl);
2718faf02f8fSPaul Mundt 	}
27195f76895eSGeert Uytterhoeven 	if (port->flags & UPF_HARD_FLOW) {
27205f76895eSGeert Uytterhoeven 		/* Refresh (Auto) RTS */
27215f76895eSGeert Uytterhoeven 		sci_set_mctrl(port, port->mctrl);
27225f76895eSGeert Uytterhoeven 	}
2723ab4382d2SGreg Kroah-Hartman 
27241707ce2dSBiju Das 	/*
27251707ce2dSBiju Das 	 * For SCI, TE (transmit enable) must be set after setting TIE
27261707ce2dSBiju Das 	 * (transmit interrupt enable) or in the same instruction to
27271707ce2dSBiju Das 	 * start the transmitting process. So skip setting TE here for SCI.
27281707ce2dSBiju Das 	 */
27291707ce2dSBiju Das 	if (port->type != PORT_SCI)
27301707ce2dSBiju Das 		scr_val |= SCSCR_TE;
27311707ce2dSBiju Das 	scr_val |= SCSCR_RE | (s->cfg->scscr & ~(SCSCR_CKE1 | SCSCR_CKE0));
27326deab514SGeert Uytterhoeven 	sci_serial_out(port, SCSCR, scr_val | s->hscif_tot);
273392a05748SGeert Uytterhoeven 	if ((srr + 1 == 5) &&
273492a05748SGeert Uytterhoeven 	    (port->type == PORT_SCIFA || port->type == PORT_SCIFB)) {
273592a05748SGeert Uytterhoeven 		/*
273692a05748SGeert Uytterhoeven 		 * In asynchronous mode, when the sampling rate is 1/5, first
273792a05748SGeert Uytterhoeven 		 * received data may become invalid on some SCIFA and SCIFB.
273892a05748SGeert Uytterhoeven 		 * To avoid this problem wait more than 1 serial data time (1
273992a05748SGeert Uytterhoeven 		 * bit time x serial data number) after setting SCSCR.RE = 1.
274092a05748SGeert Uytterhoeven 		 */
274192a05748SGeert Uytterhoeven 		udelay(DIV_ROUND_UP(10 * 1000000, baud));
274292a05748SGeert Uytterhoeven 	}
2743ab4382d2SGreg Kroah-Hartman 
2744f9f54983SUlrich Hecht 	/* Calculate delay for 2 DMA buffers (4 FIFO). */
2745b96408b4SUlrich Hecht 	s->rx_frame = (10000 * bits) / (baud / 100);
274603940376SUlrich Hecht #ifdef CONFIG_SERIAL_SH_SCI_DMA
2747b96408b4SUlrich Hecht 	s->rx_timeout = s->buf_len_rx * 2 * s->rx_frame;
2748ab4382d2SGreg Kroah-Hartman #endif
2749ab4382d2SGreg Kroah-Hartman 
2750ab4382d2SGreg Kroah-Hartman 	if ((termios->c_cflag & CREAD) != 0)
2751ab4382d2SGreg Kroah-Hartman 		sci_start_rx(port);
275236003386SAlexandre Courbot 
275394c53770SThomas Gleixner 	uart_port_unlock_irqrestore(port, flags);
27541be22663STakatoshi Akiyama 
275523241d43SPaul Mundt 	sci_port_disable(s);
2756f907c9eaSGeert Uytterhoeven 
2757f907c9eaSGeert Uytterhoeven 	if (UART_ENABLE_MS(port, termios->c_cflag))
2758f907c9eaSGeert Uytterhoeven 		sci_enable_ms(port);
2759ab4382d2SGreg Kroah-Hartman }
2760ab4382d2SGreg Kroah-Hartman 
sci_pm(struct uart_port * port,unsigned int state,unsigned int oldstate)27610174e5caSTeppei Kamijou static void sci_pm(struct uart_port *port, unsigned int state,
27620174e5caSTeppei Kamijou 		   unsigned int oldstate)
27630174e5caSTeppei Kamijou {
27640174e5caSTeppei Kamijou 	struct sci_port *sci_port = to_sci_port(port);
27650174e5caSTeppei Kamijou 
27660174e5caSTeppei Kamijou 	switch (state) {
2767d3dfe5d9SGeert Uytterhoeven 	case UART_PM_STATE_OFF:
27680174e5caSTeppei Kamijou 		sci_port_disable(sci_port);
27690174e5caSTeppei Kamijou 		break;
27700174e5caSTeppei Kamijou 	default:
27710174e5caSTeppei Kamijou 		sci_port_enable(sci_port);
27720174e5caSTeppei Kamijou 		break;
27730174e5caSTeppei Kamijou 	}
27740174e5caSTeppei Kamijou }
27750174e5caSTeppei Kamijou 
sci_type(struct uart_port * port)2776ab4382d2SGreg Kroah-Hartman static const char *sci_type(struct uart_port *port)
2777ab4382d2SGreg Kroah-Hartman {
2778ab4382d2SGreg Kroah-Hartman 	switch (port->type) {
2779ab4382d2SGreg Kroah-Hartman 	case PORT_IRDA:
2780ab4382d2SGreg Kroah-Hartman 		return "irda";
2781ab4382d2SGreg Kroah-Hartman 	case PORT_SCI:
2782ab4382d2SGreg Kroah-Hartman 		return "sci";
2783ab4382d2SGreg Kroah-Hartman 	case PORT_SCIF:
2784ab4382d2SGreg Kroah-Hartman 		return "scif";
2785ab4382d2SGreg Kroah-Hartman 	case PORT_SCIFA:
2786ab4382d2SGreg Kroah-Hartman 		return "scifa";
2787ab4382d2SGreg Kroah-Hartman 	case PORT_SCIFB:
2788ab4382d2SGreg Kroah-Hartman 		return "scifb";
2789f303b364SUlrich Hecht 	case PORT_HSCIF:
2790f303b364SUlrich Hecht 		return "hscif";
2791ab4382d2SGreg Kroah-Hartman 	}
2792ab4382d2SGreg Kroah-Hartman 
2793ab4382d2SGreg Kroah-Hartman 	return NULL;
2794ab4382d2SGreg Kroah-Hartman }
2795ab4382d2SGreg Kroah-Hartman 
sci_remap_port(struct uart_port * port)27966b620478SPaul Mundt static int sci_remap_port(struct uart_port *port)
2797ab4382d2SGreg Kroah-Hartman {
2798e4d6f911SYoshinori Sato 	struct sci_port *sport = to_sci_port(port);
2799ab4382d2SGreg Kroah-Hartman 
28006b620478SPaul Mundt 	/*
28016b620478SPaul Mundt 	 * Nothing to do if there's already an established membase.
28026b620478SPaul Mundt 	 */
2803ab4382d2SGreg Kroah-Hartman 	if (port->membase)
28046b620478SPaul Mundt 		return 0;
2805ab4382d2SGreg Kroah-Hartman 
28063d73f32bSLaurent Pinchart 	if (port->dev->of_node || (port->flags & UPF_IOREMAP)) {
28074bdc0d67SChristoph Hellwig 		port->membase = ioremap(port->mapbase, sport->reg_size);
28086b620478SPaul Mundt 		if (unlikely(!port->membase)) {
2809ab4382d2SGreg Kroah-Hartman 			dev_err(port->dev, "can't remap port#%d\n", port->line);
28106b620478SPaul Mundt 			return -ENXIO;
28116b620478SPaul Mundt 		}
2812ab4382d2SGreg Kroah-Hartman 	} else {
2813ab4382d2SGreg Kroah-Hartman 		/*
2814ab4382d2SGreg Kroah-Hartman 		 * For the simple (and majority of) cases where we don't
2815ab4382d2SGreg Kroah-Hartman 		 * need to do any remapping, just cast the cookie
2816ab4382d2SGreg Kroah-Hartman 		 * directly.
2817ab4382d2SGreg Kroah-Hartman 		 */
28183af4e960SJingoo Han 		port->membase = (void __iomem *)(uintptr_t)port->mapbase;
2819ab4382d2SGreg Kroah-Hartman 	}
28206b620478SPaul Mundt 
28216b620478SPaul Mundt 	return 0;
28226b620478SPaul Mundt }
28236b620478SPaul Mundt 
sci_release_port(struct uart_port * port)28246b620478SPaul Mundt static void sci_release_port(struct uart_port *port)
28256b620478SPaul Mundt {
2826e4d6f911SYoshinori Sato 	struct sci_port *sport = to_sci_port(port);
2827e4d6f911SYoshinori Sato 
28283d73f32bSLaurent Pinchart 	if (port->dev->of_node || (port->flags & UPF_IOREMAP)) {
28296b620478SPaul Mundt 		iounmap(port->membase);
28306b620478SPaul Mundt 		port->membase = NULL;
28316b620478SPaul Mundt 	}
28326b620478SPaul Mundt 
2833e4d6f911SYoshinori Sato 	release_mem_region(port->mapbase, sport->reg_size);
28346b620478SPaul Mundt }
28356b620478SPaul Mundt 
sci_request_port(struct uart_port * port)28366b620478SPaul Mundt static int sci_request_port(struct uart_port *port)
28376b620478SPaul Mundt {
28386b620478SPaul Mundt 	struct resource *res;
2839e4d6f911SYoshinori Sato 	struct sci_port *sport = to_sci_port(port);
28406b620478SPaul Mundt 	int ret;
28416b620478SPaul Mundt 
2842e4d6f911SYoshinori Sato 	res = request_mem_region(port->mapbase, sport->reg_size,
2843e4d6f911SYoshinori Sato 				 dev_name(port->dev));
2844e4d6f911SYoshinori Sato 	if (unlikely(res == NULL)) {
2845e4d6f911SYoshinori Sato 		dev_err(port->dev, "request_mem_region failed.");
28466b620478SPaul Mundt 		return -EBUSY;
2847e4d6f911SYoshinori Sato 	}
28486b620478SPaul Mundt 
28496b620478SPaul Mundt 	ret = sci_remap_port(port);
28506b620478SPaul Mundt 	if (unlikely(ret != 0)) {
28516b620478SPaul Mundt 		release_resource(res);
28526b620478SPaul Mundt 		return ret;
28536b620478SPaul Mundt 	}
28546b620478SPaul Mundt 
28556b620478SPaul Mundt 	return 0;
28566b620478SPaul Mundt }
28576b620478SPaul Mundt 
sci_config_port(struct uart_port * port,int flags)28586b620478SPaul Mundt static void sci_config_port(struct uart_port *port, int flags)
28596b620478SPaul Mundt {
28606b620478SPaul Mundt 	if (flags & UART_CONFIG_TYPE) {
28616b620478SPaul Mundt 		struct sci_port *sport = to_sci_port(port);
28626b620478SPaul Mundt 
28636b620478SPaul Mundt 		port->type = sport->cfg->type;
28646b620478SPaul Mundt 		sci_request_port(port);
28656b620478SPaul Mundt 	}
2866ab4382d2SGreg Kroah-Hartman }
2867ab4382d2SGreg Kroah-Hartman 
sci_verify_port(struct uart_port * port,struct serial_struct * ser)2868ab4382d2SGreg Kroah-Hartman static int sci_verify_port(struct uart_port *port, struct serial_struct *ser)
2869ab4382d2SGreg Kroah-Hartman {
2870ab4382d2SGreg Kroah-Hartman 	if (ser->baud_base < 2400)
2871ab4382d2SGreg Kroah-Hartman 		/* No paper tape reader for Mitch.. */
2872ab4382d2SGreg Kroah-Hartman 		return -EINVAL;
2873ab4382d2SGreg Kroah-Hartman 
2874ab4382d2SGreg Kroah-Hartman 	return 0;
2875ab4382d2SGreg Kroah-Hartman }
2876ab4382d2SGreg Kroah-Hartman 
2877069a47e5SJulia Lawall static const struct uart_ops sci_uart_ops = {
2878ab4382d2SGreg Kroah-Hartman 	.tx_empty	= sci_tx_empty,
2879ab4382d2SGreg Kroah-Hartman 	.set_mctrl	= sci_set_mctrl,
2880ab4382d2SGreg Kroah-Hartman 	.get_mctrl	= sci_get_mctrl,
2881ab4382d2SGreg Kroah-Hartman 	.start_tx	= sci_start_tx,
2882ab4382d2SGreg Kroah-Hartman 	.stop_tx	= sci_stop_tx,
2883ab4382d2SGreg Kroah-Hartman 	.stop_rx	= sci_stop_rx,
2884f907c9eaSGeert Uytterhoeven 	.enable_ms	= sci_enable_ms,
2885ab4382d2SGreg Kroah-Hartman 	.break_ctl	= sci_break_ctl,
2886ab4382d2SGreg Kroah-Hartman 	.startup	= sci_startup,
2887ab4382d2SGreg Kroah-Hartman 	.shutdown	= sci_shutdown,
28881cf4a7efSGeert Uytterhoeven 	.flush_buffer	= sci_flush_buffer,
2889ab4382d2SGreg Kroah-Hartman 	.set_termios	= sci_set_termios,
28900174e5caSTeppei Kamijou 	.pm		= sci_pm,
2891ab4382d2SGreg Kroah-Hartman 	.type		= sci_type,
2892ab4382d2SGreg Kroah-Hartman 	.release_port	= sci_release_port,
2893ab4382d2SGreg Kroah-Hartman 	.request_port	= sci_request_port,
2894ab4382d2SGreg Kroah-Hartman 	.config_port	= sci_config_port,
2895ab4382d2SGreg Kroah-Hartman 	.verify_port	= sci_verify_port,
2896ab4382d2SGreg Kroah-Hartman #ifdef CONFIG_CONSOLE_POLL
2897ab4382d2SGreg Kroah-Hartman 	.poll_get_char	= sci_poll_get_char,
2898ab4382d2SGreg Kroah-Hartman 	.poll_put_char	= sci_poll_put_char,
2899ab4382d2SGreg Kroah-Hartman #endif
2900ab4382d2SGreg Kroah-Hartman };
2901ab4382d2SGreg Kroah-Hartman 
sci_init_clocks(struct sci_port * sci_port,struct device * dev)2902a9ec81f4SLaurent Pinchart static int sci_init_clocks(struct sci_port *sci_port, struct device *dev)
2903a9ec81f4SLaurent Pinchart {
2904f4998e55SGeert Uytterhoeven 	const char *clk_names[] = {
2905f4998e55SGeert Uytterhoeven 		[SCI_FCK] = "fck",
29066af27bf2SGeert Uytterhoeven 		[SCI_SCK] = "sck",
29071270f865SGeert Uytterhoeven 		[SCI_BRG_INT] = "brg_int",
29081270f865SGeert Uytterhoeven 		[SCI_SCIF_CLK] = "scif_clk",
2909f4998e55SGeert Uytterhoeven 	};
2910f4998e55SGeert Uytterhoeven 	struct clk *clk;
2911f4998e55SGeert Uytterhoeven 	unsigned int i;
2912a9ec81f4SLaurent Pinchart 
29136af27bf2SGeert Uytterhoeven 	if (sci_port->cfg->type == PORT_HSCIF)
29146af27bf2SGeert Uytterhoeven 		clk_names[SCI_SCK] = "hsck";
29156af27bf2SGeert Uytterhoeven 
2916f4998e55SGeert Uytterhoeven 	for (i = 0; i < SCI_NUM_CLKS; i++) {
29178a1dcae9SGeert Uytterhoeven 		clk = devm_clk_get_optional(dev, clk_names[i]);
29188a1dcae9SGeert Uytterhoeven 		if (IS_ERR(clk))
29198a1dcae9SGeert Uytterhoeven 			return PTR_ERR(clk);
2920f4998e55SGeert Uytterhoeven 
29218a1dcae9SGeert Uytterhoeven 		if (!clk && i == SCI_FCK) {
2922f4998e55SGeert Uytterhoeven 			/*
2923f4998e55SGeert Uytterhoeven 			 * Not all SH platforms declare a clock lookup entry
2924f4998e55SGeert Uytterhoeven 			 * for SCI devices, in which case we need to get the
2925f4998e55SGeert Uytterhoeven 			 * global "peripheral_clk" clock.
2926a9ec81f4SLaurent Pinchart 			 */
2927f4998e55SGeert Uytterhoeven 			clk = devm_clk_get(dev, "peripheral_clk");
29280d1bc829SGeert Uytterhoeven 			if (IS_ERR(clk))
29290d1bc829SGeert Uytterhoeven 				return dev_err_probe(dev, PTR_ERR(clk),
29300d1bc829SGeert Uytterhoeven 						     "failed to get %s\n",
29310d1bc829SGeert Uytterhoeven 						     clk_names[i]);
2932f4998e55SGeert Uytterhoeven 		}
2933f4998e55SGeert Uytterhoeven 
29348a1dcae9SGeert Uytterhoeven 		if (!clk)
29358a1dcae9SGeert Uytterhoeven 			dev_dbg(dev, "failed to get %s\n", clk_names[i]);
2936f4998e55SGeert Uytterhoeven 		else
2937d63c16f8SGeert Uytterhoeven 			dev_dbg(dev, "clk %s is %pC rate %lu\n", clk_names[i],
2938d63c16f8SGeert Uytterhoeven 				clk, clk_get_rate(clk));
29398a1dcae9SGeert Uytterhoeven 		sci_port->clks[i] = clk;
2940f4998e55SGeert Uytterhoeven 	}
2941f4998e55SGeert Uytterhoeven 	return 0;
2942a9ec81f4SLaurent Pinchart }
2943a9ec81f4SLaurent Pinchart 
2944daf5a895SLaurent Pinchart static const struct sci_port_params *
sci_probe_regmap(const struct plat_sci_port * cfg)2945daf5a895SLaurent Pinchart sci_probe_regmap(const struct plat_sci_port *cfg)
2946daf5a895SLaurent Pinchart {
2947daf5a895SLaurent Pinchart 	unsigned int regtype;
2948daf5a895SLaurent Pinchart 
2949daf5a895SLaurent Pinchart 	if (cfg->regtype != SCIx_PROBE_REGTYPE)
2950daf5a895SLaurent Pinchart 		return &sci_port_params[cfg->regtype];
2951daf5a895SLaurent Pinchart 
2952daf5a895SLaurent Pinchart 	switch (cfg->type) {
2953daf5a895SLaurent Pinchart 	case PORT_SCI:
2954daf5a895SLaurent Pinchart 		regtype = SCIx_SCI_REGTYPE;
2955daf5a895SLaurent Pinchart 		break;
2956daf5a895SLaurent Pinchart 	case PORT_IRDA:
2957daf5a895SLaurent Pinchart 		regtype = SCIx_IRDA_REGTYPE;
2958daf5a895SLaurent Pinchart 		break;
2959daf5a895SLaurent Pinchart 	case PORT_SCIFA:
2960daf5a895SLaurent Pinchart 		regtype = SCIx_SCIFA_REGTYPE;
2961daf5a895SLaurent Pinchart 		break;
2962daf5a895SLaurent Pinchart 	case PORT_SCIFB:
2963daf5a895SLaurent Pinchart 		regtype = SCIx_SCIFB_REGTYPE;
2964daf5a895SLaurent Pinchart 		break;
2965daf5a895SLaurent Pinchart 	case PORT_SCIF:
2966daf5a895SLaurent Pinchart 		/*
2967daf5a895SLaurent Pinchart 		 * The SH-4 is a bit of a misnomer here, although that's
2968daf5a895SLaurent Pinchart 		 * where this particular port layout originated. This
2969daf5a895SLaurent Pinchart 		 * configuration (or some slight variation thereof)
2970daf5a895SLaurent Pinchart 		 * remains the dominant model for all SCIFs.
2971daf5a895SLaurent Pinchart 		 */
2972daf5a895SLaurent Pinchart 		regtype = SCIx_SH4_SCIF_REGTYPE;
2973daf5a895SLaurent Pinchart 		break;
2974daf5a895SLaurent Pinchart 	case PORT_HSCIF:
2975daf5a895SLaurent Pinchart 		regtype = SCIx_HSCIF_REGTYPE;
2976daf5a895SLaurent Pinchart 		break;
2977daf5a895SLaurent Pinchart 	default:
2978daf5a895SLaurent Pinchart 		pr_err("Can't probe register map for given port\n");
2979daf5a895SLaurent Pinchart 		return NULL;
2980daf5a895SLaurent Pinchart 	}
2981daf5a895SLaurent Pinchart 
2982daf5a895SLaurent Pinchart 	return &sci_port_params[regtype];
2983daf5a895SLaurent Pinchart }
2984daf5a895SLaurent Pinchart 
sci_init_single(struct platform_device * dev,struct sci_port * sci_port,unsigned int index,const struct plat_sci_port * p,bool early)29859671f099SBill Pemberton static int sci_init_single(struct platform_device *dev,
29861fcc91a6SLaurent Pinchart 			   struct sci_port *sci_port, unsigned int index,
2987daf5a895SLaurent Pinchart 			   const struct plat_sci_port *p, bool early)
2988ab4382d2SGreg Kroah-Hartman {
2989ab4382d2SGreg Kroah-Hartman 	struct uart_port *port = &sci_port->port;
29901fcc91a6SLaurent Pinchart 	const struct resource *res;
2991a1c2fd7eSGeert Uytterhoeven 	unsigned int i;
29923127c6b2SPaul Mundt 	int ret;
2993ab4382d2SGreg Kroah-Hartman 
299450f0959aSPaul Mundt 	sci_port->cfg	= p;
299550f0959aSPaul Mundt 
2996ab4382d2SGreg Kroah-Hartman 	port->ops	= &sci_uart_ops;
2997ab4382d2SGreg Kroah-Hartman 	port->iotype	= UPIO_MEM;
2998ab4382d2SGreg Kroah-Hartman 	port->line	= index;
2999dc9a3254SDmitry Safonov 	port->has_sysrq = IS_ENABLED(CONFIG_SERIAL_SH_SCI_CONSOLE);
3000ab4382d2SGreg Kroah-Hartman 
30011fcc91a6SLaurent Pinchart 	res = platform_get_resource(dev, IORESOURCE_MEM, 0);
30021fcc91a6SLaurent Pinchart 	if (res == NULL)
30031fcc91a6SLaurent Pinchart 		return -ENOMEM;
30041fcc91a6SLaurent Pinchart 
30051fcc91a6SLaurent Pinchart 	port->mapbase = res->start;
3006e4d6f911SYoshinori Sato 	sci_port->reg_size = resource_size(res);
30071fcc91a6SLaurent Pinchart 
3008392fb8dfSGeert Uytterhoeven 	for (i = 0; i < ARRAY_SIZE(sci_port->irqs); ++i) {
3009392fb8dfSGeert Uytterhoeven 		if (i)
3010392fb8dfSGeert Uytterhoeven 			sci_port->irqs[i] = platform_get_irq_optional(dev, i);
3011392fb8dfSGeert Uytterhoeven 		else
30121fcc91a6SLaurent Pinchart 			sci_port->irqs[i] = platform_get_irq(dev, i);
3013392fb8dfSGeert Uytterhoeven 	}
30141fcc91a6SLaurent Pinchart 
3015b43a1864SBiju Das 	/*
3016b43a1864SBiju Das 	 * The fourth interrupt on SCI port is transmit end interrupt, so
3017b43a1864SBiju Das 	 * shuffle the interrupts.
3018b43a1864SBiju Das 	 */
3019b43a1864SBiju Das 	if (p->type == PORT_SCI)
3020b43a1864SBiju Das 		swap(sci_port->irqs[SCIx_BRI_IRQ], sci_port->irqs[SCIx_TEI_IRQ]);
3021b43a1864SBiju Das 
302289b5c1abSLaurent Pinchart 	/* The SCI generates several interrupts. They can be muxed together or
302389b5c1abSLaurent Pinchart 	 * connected to different interrupt lines. In the muxed case only one
3024628c534aSChris Brandt 	 * interrupt resource is specified as there is only one interrupt ID.
3025628c534aSChris Brandt 	 * In the non-muxed case, up to 6 interrupt signals might be generated
3026628c534aSChris Brandt 	 * from the SCI, however those signals might have their own individual
3027628c534aSChris Brandt 	 * interrupt ID numbers, or muxed together with another interrupt.
30281fcc91a6SLaurent Pinchart 	 */
30291fcc91a6SLaurent Pinchart 	if (sci_port->irqs[0] < 0)
30301fcc91a6SLaurent Pinchart 		return -ENXIO;
30311fcc91a6SLaurent Pinchart 
3032628c534aSChris Brandt 	if (sci_port->irqs[1] < 0)
3033628c534aSChris Brandt 		for (i = 1; i < ARRAY_SIZE(sci_port->irqs); i++)
3034628c534aSChris Brandt 			sci_port->irqs[i] = sci_port->irqs[0];
30351fcc91a6SLaurent Pinchart 
3036daf5a895SLaurent Pinchart 	sci_port->params = sci_probe_regmap(p);
3037daf5a895SLaurent Pinchart 	if (unlikely(sci_port->params == NULL))
3038daf5a895SLaurent Pinchart 		return -EINVAL;
3039e095ee6bSLaurent Pinchart 
304018e8cf15SUlrich Hecht 	switch (p->type) {
304118e8cf15SUlrich Hecht 	case PORT_SCIFB:
304218e8cf15SUlrich Hecht 		sci_port->rx_trigger = 48;
304318e8cf15SUlrich Hecht 		break;
304418e8cf15SUlrich Hecht 	case PORT_HSCIF:
304518e8cf15SUlrich Hecht 		sci_port->rx_trigger = 64;
304618e8cf15SUlrich Hecht 		break;
304718e8cf15SUlrich Hecht 	case PORT_SCIFA:
304818e8cf15SUlrich Hecht 		sci_port->rx_trigger = 32;
304918e8cf15SUlrich Hecht 		break;
305018e8cf15SUlrich Hecht 	case PORT_SCIF:
305118e8cf15SUlrich Hecht 		if (p->regtype == SCIx_SH7705_SCIF_REGTYPE)
305218e8cf15SUlrich Hecht 			/* RX triggering not implemented for this IP */
305318e8cf15SUlrich Hecht 			sci_port->rx_trigger = 1;
305418e8cf15SUlrich Hecht 		else
305518e8cf15SUlrich Hecht 			sci_port->rx_trigger = 8;
305618e8cf15SUlrich Hecht 		break;
305718e8cf15SUlrich Hecht 	default:
305818e8cf15SUlrich Hecht 		sci_port->rx_trigger = 1;
305918e8cf15SUlrich Hecht 		break;
306018e8cf15SUlrich Hecht 	}
306118e8cf15SUlrich Hecht 
306203940376SUlrich Hecht 	sci_port->rx_fifo_timeout = 0;
3063fa2abb03SUlrich Hecht 	sci_port->hscif_tot = 0;
306403940376SUlrich Hecht 
3065878fbb91SLaurent Pinchart 	/* SCIFA on sh7723 and sh7724 need a custom sampling rate that doesn't
3066878fbb91SLaurent Pinchart 	 * match the SoC datasheet, this should be investigated. Let platform
3067878fbb91SLaurent Pinchart 	 * data override the sampling rate for now.
3068ec09c5ebSLaurent Pinchart 	 */
3069b2f20ed9SLaurent Pinchart 	sci_port->sampling_rate_mask = p->sampling_rate
3070b2f20ed9SLaurent Pinchart 				     ? SCI_SR(p->sampling_rate)
3071b2f20ed9SLaurent Pinchart 				     : sci_port->params->sampling_rate_mask;
3072ec09c5ebSLaurent Pinchart 
30731fcc91a6SLaurent Pinchart 	if (!early) {
3074a9ec81f4SLaurent Pinchart 		ret = sci_init_clocks(sci_port, &dev->dev);
3075a9ec81f4SLaurent Pinchart 		if (ret < 0)
3076a9ec81f4SLaurent Pinchart 			return ret;
3077ab4382d2SGreg Kroah-Hartman 	}
3078ab4382d2SGreg Kroah-Hartman 
30796b620478SPaul Mundt 	port->type		= p->type;
30803d73f32bSLaurent Pinchart 	port->flags		= UPF_FIXED_PORT | UPF_BOOT_AUTOCONF | p->flags;
3081b2f20ed9SLaurent Pinchart 	port->fifosize		= sci_port->params->fifosize;
3082ab4382d2SGreg Kroah-Hartman 
3083f92ed0cdSBiju Das 	if (port->type == PORT_SCI && !dev->dev.of_node) {
3084dfc80387SLaurent Pinchart 		if (sci_port->reg_size >= 0x20)
3085dfc80387SLaurent Pinchart 			port->regshift = 2;
3086dfc80387SLaurent Pinchart 		else
3087dfc80387SLaurent Pinchart 			port->regshift = 1;
3088dfc80387SLaurent Pinchart 	}
3089dfc80387SLaurent Pinchart 
30906b620478SPaul Mundt 	/*
309161a6976bSPaul Mundt 	 * The UART port needs an IRQ value, so we peg this to the RX IRQ
30926b620478SPaul Mundt 	 * for the multi-IRQ ports, which is where we are primarily
30936b620478SPaul Mundt 	 * concerned with the shutdown path synchronization.
30946b620478SPaul Mundt 	 *
30956b620478SPaul Mundt 	 * For the muxed case there's nothing more to do.
30966b620478SPaul Mundt 	 */
30971fcc91a6SLaurent Pinchart 	port->irq		= sci_port->irqs[SCIx_RXI_IRQ];
30989cfb5c05SYong Zhang 	port->irqflags		= 0;
3099ab4382d2SGreg Kroah-Hartman 
3100ab4382d2SGreg Kroah-Hartman 	return 0;
3101ab4382d2SGreg Kroah-Hartman }
3102ab4382d2SGreg Kroah-Hartman 
31030b0cced1SYoshinori Sato #if defined(CONFIG_SERIAL_SH_SCI_CONSOLE) || \
31040b0cced1SYoshinori Sato     defined(CONFIG_SERIAL_SH_SCI_EARLYCON)
serial_console_putchar(struct uart_port * port,unsigned char ch)31053f8bab17SJiri Slaby static void serial_console_putchar(struct uart_port *port, unsigned char ch)
3106ab4382d2SGreg Kroah-Hartman {
3107ab4382d2SGreg Kroah-Hartman 	sci_poll_put_char(port, ch);
3108ab4382d2SGreg Kroah-Hartman }
3109ab4382d2SGreg Kroah-Hartman 
3110ab4382d2SGreg Kroah-Hartman /*
3111ab4382d2SGreg Kroah-Hartman  *	Print a string to the serial port trying not to disturb
3112ab4382d2SGreg Kroah-Hartman  *	any possible real use of the port...
3113ab4382d2SGreg Kroah-Hartman  */
serial_console_write(struct console * co,const char * s,unsigned count)3114ab4382d2SGreg Kroah-Hartman static void serial_console_write(struct console *co, const char *s,
3115ab4382d2SGreg Kroah-Hartman 				 unsigned count)
3116ab4382d2SGreg Kroah-Hartman {
31176b620478SPaul Mundt 	struct sci_port *sci_port = &sci_ports[co->index];
31186b620478SPaul Mundt 	struct uart_port *port = &sci_port->port;
3119a67969b5SGeert Uytterhoeven 	unsigned short bits, ctrl, ctrl_temp;
312040f70c03SShinya Kuribayashi 	unsigned long flags;
312140f70c03SShinya Kuribayashi 	int locked = 1;
312240f70c03SShinya Kuribayashi 
312340f70c03SShinya Kuribayashi 	if (port->sysrq)
312440f70c03SShinya Kuribayashi 		locked = 0;
3125dc9a3254SDmitry Safonov 	else if (oops_in_progress)
312694c53770SThomas Gleixner 		locked = uart_port_trylock_irqsave(port, &flags);
312740f70c03SShinya Kuribayashi 	else
312894c53770SThomas Gleixner 		uart_port_lock_irqsave(port, &flags);
312940f70c03SShinya Kuribayashi 
3130a67969b5SGeert Uytterhoeven 	/* first save SCSCR then disable interrupts, keep clock source */
31316deab514SGeert Uytterhoeven 	ctrl = sci_serial_in(port, SCSCR);
31329f8325b3SLaurent Pinchart 	ctrl_temp = SCSCR_RE | SCSCR_TE |
31339f8325b3SLaurent Pinchart 		    (sci_port->cfg->scscr & ~(SCSCR_CKE1 | SCSCR_CKE0)) |
3134a67969b5SGeert Uytterhoeven 		    (ctrl & (SCSCR_CKE1 | SCSCR_CKE0));
31356deab514SGeert Uytterhoeven 	sci_serial_out(port, SCSCR, ctrl_temp | sci_port->hscif_tot);
3136ab4382d2SGreg Kroah-Hartman 
3137ab4382d2SGreg Kroah-Hartman 	uart_console_write(port, s, count, serial_console_putchar);
3138ab4382d2SGreg Kroah-Hartman 
3139ab4382d2SGreg Kroah-Hartman 	/* wait until fifo is empty and last bit has been transmitted */
3140ab4382d2SGreg Kroah-Hartman 	bits = SCxSR_TDxE(port) | SCxSR_TEND(port);
31416deab514SGeert Uytterhoeven 	while ((sci_serial_in(port, SCxSR) & bits) != bits)
3142ab4382d2SGreg Kroah-Hartman 		cpu_relax();
314340f70c03SShinya Kuribayashi 
314440f70c03SShinya Kuribayashi 	/* restore the SCSCR */
31456deab514SGeert Uytterhoeven 	sci_serial_out(port, SCSCR, ctrl);
314640f70c03SShinya Kuribayashi 
314740f70c03SShinya Kuribayashi 	if (locked)
314894c53770SThomas Gleixner 		uart_port_unlock_irqrestore(port, flags);
3149ab4382d2SGreg Kroah-Hartman }
3150ab4382d2SGreg Kroah-Hartman 
serial_console_setup(struct console * co,char * options)31519671f099SBill Pemberton static int serial_console_setup(struct console *co, char *options)
3152ab4382d2SGreg Kroah-Hartman {
3153ab4382d2SGreg Kroah-Hartman 	struct sci_port *sci_port;
3154ab4382d2SGreg Kroah-Hartman 	struct uart_port *port;
3155ab4382d2SGreg Kroah-Hartman 	int baud = 115200;
3156ab4382d2SGreg Kroah-Hartman 	int bits = 8;
3157ab4382d2SGreg Kroah-Hartman 	int parity = 'n';
3158ab4382d2SGreg Kroah-Hartman 	int flow = 'n';
3159ab4382d2SGreg Kroah-Hartman 	int ret;
3160ab4382d2SGreg Kroah-Hartman 
3161ab4382d2SGreg Kroah-Hartman 	/*
31626b620478SPaul Mundt 	 * Refuse to handle any bogus ports.
3163ab4382d2SGreg Kroah-Hartman 	 */
31646b620478SPaul Mundt 	if (co->index < 0 || co->index >= SCI_NPORTS)
3165ab4382d2SGreg Kroah-Hartman 		return -ENODEV;
3166ab4382d2SGreg Kroah-Hartman 
31676b620478SPaul Mundt 	sci_port = &sci_ports[co->index];
31686b620478SPaul Mundt 	port = &sci_port->port;
31696b620478SPaul Mundt 
3170b2267a6bSAlexandre Courbot 	/*
3171b2267a6bSAlexandre Courbot 	 * Refuse to handle uninitialized ports.
3172b2267a6bSAlexandre Courbot 	 */
3173b2267a6bSAlexandre Courbot 	if (!port->ops)
3174b2267a6bSAlexandre Courbot 		return -ENODEV;
3175b2267a6bSAlexandre Courbot 
31766b620478SPaul Mundt 	ret = sci_remap_port(port);
31776b620478SPaul Mundt 	if (unlikely(ret != 0))
31786b620478SPaul Mundt 		return ret;
3179ab4382d2SGreg Kroah-Hartman 
3180ab4382d2SGreg Kroah-Hartman 	if (options)
3181ab4382d2SGreg Kroah-Hartman 		uart_parse_options(options, &baud, &parity, &bits, &flow);
3182ab4382d2SGreg Kroah-Hartman 
3183ab7cfb55SPaul Mundt 	return uart_set_options(port, co, baud, parity, bits, flow);
3184ab4382d2SGreg Kroah-Hartman }
3185ab4382d2SGreg Kroah-Hartman 
3186ab4382d2SGreg Kroah-Hartman static struct console serial_console = {
3187ab4382d2SGreg Kroah-Hartman 	.name		= "ttySC",
31886b620478SPaul Mundt 	.device		= uart_console_device,
3189ab4382d2SGreg Kroah-Hartman 	.write		= serial_console_write,
3190ab4382d2SGreg Kroah-Hartman 	.setup		= serial_console_setup,
3191ab4382d2SGreg Kroah-Hartman 	.flags		= CON_PRINTBUFFER,
3192ab4382d2SGreg Kroah-Hartman 	.index		= -1,
31936b620478SPaul Mundt 	.data		= &sci_uart_driver,
3194ab4382d2SGreg Kroah-Hartman };
3195ab4382d2SGreg Kroah-Hartman 
3196507fd01dSBartosz Golaszewski #ifdef CONFIG_SUPERH
3197ff707dfdSJohn Ogness static char early_serial_buf[32];
3198ff707dfdSJohn Ogness 
early_serial_console_setup(struct console * co,char * options)3199ff707dfdSJohn Ogness static int early_serial_console_setup(struct console *co, char *options)
3200ff707dfdSJohn Ogness {
3201ff707dfdSJohn Ogness 	/*
3202ff707dfdSJohn Ogness 	 * This early console is always registered using the earlyprintk=
3203ff707dfdSJohn Ogness 	 * parameter, which does not call add_preferred_console(). Thus
3204ff707dfdSJohn Ogness 	 * @options is always NULL and the options for this early console
3205ff707dfdSJohn Ogness 	 * are passed using a custom buffer.
3206ff707dfdSJohn Ogness 	 */
3207ff707dfdSJohn Ogness 	WARN_ON(options);
3208ff707dfdSJohn Ogness 
3209ff707dfdSJohn Ogness 	return serial_console_setup(co, early_serial_buf);
3210ff707dfdSJohn Ogness }
3211ff707dfdSJohn Ogness 
3212ab4382d2SGreg Kroah-Hartman static struct console early_serial_console = {
3213ab4382d2SGreg Kroah-Hartman 	.name           = "early_ttySC",
3214ab4382d2SGreg Kroah-Hartman 	.write          = serial_console_write,
3215ff707dfdSJohn Ogness 	.setup		= early_serial_console_setup,
3216ab4382d2SGreg Kroah-Hartman 	.flags          = CON_PRINTBUFFER,
32176b620478SPaul Mundt 	.index		= -1,
3218ab4382d2SGreg Kroah-Hartman };
32196b620478SPaul Mundt 
sci_probe_earlyprintk(struct platform_device * pdev)32209671f099SBill Pemberton static int sci_probe_earlyprintk(struct platform_device *pdev)
32216b620478SPaul Mundt {
3222daf5a895SLaurent Pinchart 	const struct plat_sci_port *cfg = dev_get_platdata(&pdev->dev);
32236b620478SPaul Mundt 
32246b620478SPaul Mundt 	if (early_serial_console.data)
32256b620478SPaul Mundt 		return -EEXIST;
32266b620478SPaul Mundt 
32276b620478SPaul Mundt 	early_serial_console.index = pdev->id;
32286b620478SPaul Mundt 
32291fcc91a6SLaurent Pinchart 	sci_init_single(pdev, &sci_ports[pdev->id], pdev->id, cfg, true);
32306b620478SPaul Mundt 
32316b620478SPaul Mundt 	if (!strstr(early_serial_buf, "keep"))
32326b620478SPaul Mundt 		early_serial_console.flags |= CON_BOOT;
32336b620478SPaul Mundt 
32346b620478SPaul Mundt 	register_console(&early_serial_console);
32356b620478SPaul Mundt 	return 0;
32366b620478SPaul Mundt }
3237507fd01dSBartosz Golaszewski #endif
32386a8c9799SNobuhiro Iwamatsu 
32396a8c9799SNobuhiro Iwamatsu #define SCI_CONSOLE	(&serial_console)
32406a8c9799SNobuhiro Iwamatsu 
32416b620478SPaul Mundt #else
sci_probe_earlyprintk(struct platform_device * pdev)32429671f099SBill Pemberton static inline int sci_probe_earlyprintk(struct platform_device *pdev)
32436b620478SPaul Mundt {
32446b620478SPaul Mundt 	return -EINVAL;
32456b620478SPaul Mundt }
3246ab4382d2SGreg Kroah-Hartman 
32476a8c9799SNobuhiro Iwamatsu #define SCI_CONSOLE	NULL
32486a8c9799SNobuhiro Iwamatsu 
32490b0cced1SYoshinori Sato #endif /* CONFIG_SERIAL_SH_SCI_CONSOLE || CONFIG_SERIAL_SH_SCI_EARLYCON */
3250ab4382d2SGreg Kroah-Hartman 
32516c13d5d2SGeert Uytterhoeven static const char banner[] __initconst = "SuperH (H)SCI(F) driver initialized";
3252ab4382d2SGreg Kroah-Hartman 
3253352b9266SSjoerd Simons static DEFINE_MUTEX(sci_uart_registration_lock);
3254ab4382d2SGreg Kroah-Hartman static struct uart_driver sci_uart_driver = {
3255ab4382d2SGreg Kroah-Hartman 	.owner		= THIS_MODULE,
3256ab4382d2SGreg Kroah-Hartman 	.driver_name	= "sci",
3257ab4382d2SGreg Kroah-Hartman 	.dev_name	= "ttySC",
3258ab4382d2SGreg Kroah-Hartman 	.major		= SCI_MAJOR,
3259ab4382d2SGreg Kroah-Hartman 	.minor		= SCI_MINOR_START,
3260ab4382d2SGreg Kroah-Hartman 	.nr		= SCI_NPORTS,
3261ab4382d2SGreg Kroah-Hartman 	.cons		= SCI_CONSOLE,
3262ab4382d2SGreg Kroah-Hartman };
3263ab4382d2SGreg Kroah-Hartman 
sci_remove(struct platform_device * dev)32645fc247bfSUwe Kleine-König static void sci_remove(struct platform_device *dev)
3265ab4382d2SGreg Kroah-Hartman {
32666b620478SPaul Mundt 	struct sci_port *port = platform_get_drvdata(dev);
3267641a41dbSYoshihiro Shimoda 	unsigned int type = port->port.type;	/* uart_remove_... clears it */
3268ab4382d2SGreg Kroah-Hartman 
32697678f4c2SGeert Uytterhoeven 	sci_ports_in_use &= ~BIT(port->port.line);
32706b620478SPaul Mundt 	uart_remove_one_port(&sci_uart_driver, &port->port);
3271ab4382d2SGreg Kroah-Hartman 
32726aa57f16SGreg Kroah-Hartman 	if (port->port.fifosize > 1)
32736aa57f16SGreg Kroah-Hartman 		device_remove_file(&dev->dev, &dev_attr_rx_fifo_trigger);
32746aa57f16SGreg Kroah-Hartman 	if (type == PORT_SCIFA || type == PORT_SCIFB || type == PORT_HSCIF)
32756aa57f16SGreg Kroah-Hartman 		device_remove_file(&dev->dev, &dev_attr_rx_fifo_timeout);
3276ab4382d2SGreg Kroah-Hartman }
3277ab4382d2SGreg Kroah-Hartman 
3278bd2238fbSGeert Uytterhoeven 
3279bd2238fbSGeert Uytterhoeven #define SCI_OF_DATA(type, regtype)	(void *)((type) << 16 | (regtype))
3280bd2238fbSGeert Uytterhoeven #define SCI_OF_TYPE(data)		((unsigned long)(data) >> 16)
3281bd2238fbSGeert Uytterhoeven #define SCI_OF_REGTYPE(data)		((unsigned long)(data) & 0xffff)
328220bdcab8SBastian Hecht 
328388dcd07dSKrzysztof Kozlowski static const struct of_device_id of_sci_match[] __maybe_unused = {
3284f443ff80SGeert Uytterhoeven 	/* SoC-specific types */
3285f443ff80SGeert Uytterhoeven 	{
3286f443ff80SGeert Uytterhoeven 		.compatible = "renesas,scif-r7s72100",
3287f443ff80SGeert Uytterhoeven 		.data = SCI_OF_DATA(PORT_SCIF, SCIx_SH2_SCIF_FIFODATA_REGTYPE),
3288f443ff80SGeert Uytterhoeven 	},
328910c63443SGeert Uytterhoeven 	{
329010c63443SGeert Uytterhoeven 		.compatible = "renesas,scif-r7s9210",
329110c63443SGeert Uytterhoeven 		.data = SCI_OF_DATA(PORT_SCIF, SCIx_RZ_SCIFA_REGTYPE),
329210c63443SGeert Uytterhoeven 	},
32933b2cd606SBiju Das 	{
32943b2cd606SBiju Das 		.compatible = "renesas,scif-r9a07g044",
32953b2cd606SBiju Das 		.data = SCI_OF_DATA(PORT_SCIF, SCIx_RZ_SCIFA_REGTYPE),
32963b2cd606SBiju Das 	},
32972f50304eSLad Prabhakar 	{
32982f50304eSLad Prabhakar 		.compatible = "renesas,scif-r9a09g057",
32992f50304eSLad Prabhakar 		.data = SCI_OF_DATA(PORT_SCIF, SCIx_RZV2H_SCIF_REGTYPE),
33002f50304eSLad Prabhakar 	},
33019ed44bb2SGeert Uytterhoeven 	/* Family-specific types */
33029ed44bb2SGeert Uytterhoeven 	{
33039ed44bb2SGeert Uytterhoeven 		.compatible = "renesas,rcar-gen1-scif",
33049ed44bb2SGeert Uytterhoeven 		.data = SCI_OF_DATA(PORT_SCIF, SCIx_SH4_SCIF_BRG_REGTYPE),
33059ed44bb2SGeert Uytterhoeven 	}, {
33069ed44bb2SGeert Uytterhoeven 		.compatible = "renesas,rcar-gen2-scif",
33079ed44bb2SGeert Uytterhoeven 		.data = SCI_OF_DATA(PORT_SCIF, SCIx_SH4_SCIF_BRG_REGTYPE),
33089ed44bb2SGeert Uytterhoeven 	}, {
33099ed44bb2SGeert Uytterhoeven 		.compatible = "renesas,rcar-gen3-scif",
33109ed44bb2SGeert Uytterhoeven 		.data = SCI_OF_DATA(PORT_SCIF, SCIx_SH4_SCIF_BRG_REGTYPE),
331126baf4b6SYoshihiro Shimoda 	}, {
331226baf4b6SYoshihiro Shimoda 		.compatible = "renesas,rcar-gen4-scif",
331326baf4b6SYoshihiro Shimoda 		.data = SCI_OF_DATA(PORT_SCIF, SCIx_SH4_SCIF_BRG_REGTYPE),
33149ed44bb2SGeert Uytterhoeven 	},
3315f443ff80SGeert Uytterhoeven 	/* Generic types */
331620bdcab8SBastian Hecht 	{
331720bdcab8SBastian Hecht 		.compatible = "renesas,scif",
3318bd2238fbSGeert Uytterhoeven 		.data = SCI_OF_DATA(PORT_SCIF, SCIx_SH4_SCIF_REGTYPE),
331920bdcab8SBastian Hecht 	}, {
332020bdcab8SBastian Hecht 		.compatible = "renesas,scifa",
3321bd2238fbSGeert Uytterhoeven 		.data = SCI_OF_DATA(PORT_SCIFA, SCIx_SCIFA_REGTYPE),
332220bdcab8SBastian Hecht 	}, {
332320bdcab8SBastian Hecht 		.compatible = "renesas,scifb",
3324bd2238fbSGeert Uytterhoeven 		.data = SCI_OF_DATA(PORT_SCIFB, SCIx_SCIFB_REGTYPE),
332520bdcab8SBastian Hecht 	}, {
332620bdcab8SBastian Hecht 		.compatible = "renesas,hscif",
3327bd2238fbSGeert Uytterhoeven 		.data = SCI_OF_DATA(PORT_HSCIF, SCIx_HSCIF_REGTYPE),
332820bdcab8SBastian Hecht 	}, {
3329e1d0be61SYoshinori Sato 		.compatible = "renesas,sci",
3330bd2238fbSGeert Uytterhoeven 		.data = SCI_OF_DATA(PORT_SCI, SCIx_SCI_REGTYPE),
3331e1d0be61SYoshinori Sato 	}, {
333220bdcab8SBastian Hecht 		/* Terminator */
333320bdcab8SBastian Hecht 	},
333420bdcab8SBastian Hecht };
333520bdcab8SBastian Hecht MODULE_DEVICE_TABLE(of, of_sci_match);
333620bdcab8SBastian Hecht 
sci_reset_control_assert(void * data)3337862f7218SLad Prabhakar static void sci_reset_control_assert(void *data)
3338862f7218SLad Prabhakar {
3339862f7218SLad Prabhakar 	reset_control_assert(data);
3340862f7218SLad Prabhakar }
3341862f7218SLad Prabhakar 
sci_parse_dt(struct platform_device * pdev,unsigned int * dev_id)334254b12c48SGeert Uytterhoeven static struct plat_sci_port *sci_parse_dt(struct platform_device *pdev,
334354b12c48SGeert Uytterhoeven 					  unsigned int *dev_id)
334420bdcab8SBastian Hecht {
334520bdcab8SBastian Hecht 	struct device_node *np = pdev->dev.of_node;
3346862f7218SLad Prabhakar 	struct reset_control *rstc;
334720bdcab8SBastian Hecht 	struct plat_sci_port *p;
334897ed9790SLaurent Pinchart 	struct sci_port *sp;
33496e605a01SGeert Uytterhoeven 	const void *data;
3350862f7218SLad Prabhakar 	int id, ret;
335120bdcab8SBastian Hecht 
335220bdcab8SBastian Hecht 	if (!IS_ENABLED(CONFIG_OF) || !np)
3353862f7218SLad Prabhakar 		return ERR_PTR(-EINVAL);
335420bdcab8SBastian Hecht 
33556e605a01SGeert Uytterhoeven 	data = of_device_get_match_data(&pdev->dev);
335620bdcab8SBastian Hecht 
3357862f7218SLad Prabhakar 	rstc = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL);
3358862f7218SLad Prabhakar 	if (IS_ERR(rstc))
3359862f7218SLad Prabhakar 		return ERR_PTR(dev_err_probe(&pdev->dev, PTR_ERR(rstc),
3360862f7218SLad Prabhakar 					     "failed to get reset ctrl\n"));
3361862f7218SLad Prabhakar 
3362862f7218SLad Prabhakar 	ret = reset_control_deassert(rstc);
3363862f7218SLad Prabhakar 	if (ret) {
3364862f7218SLad Prabhakar 		dev_err(&pdev->dev, "failed to deassert reset %d\n", ret);
3365862f7218SLad Prabhakar 		return ERR_PTR(ret);
3366862f7218SLad Prabhakar 	}
3367862f7218SLad Prabhakar 
3368862f7218SLad Prabhakar 	ret = devm_add_action_or_reset(&pdev->dev, sci_reset_control_assert, rstc);
3369862f7218SLad Prabhakar 	if (ret) {
3370862f7218SLad Prabhakar 		dev_err(&pdev->dev, "failed to register assert devm action, %d\n",
3371862f7218SLad Prabhakar 			ret);
3372862f7218SLad Prabhakar 		return ERR_PTR(ret);
3373862f7218SLad Prabhakar 	}
3374862f7218SLad Prabhakar 
337520bdcab8SBastian Hecht 	p = devm_kzalloc(&pdev->dev, sizeof(struct plat_sci_port), GFP_KERNEL);
33764205463cSGeert Uytterhoeven 	if (!p)
3377862f7218SLad Prabhakar 		return ERR_PTR(-ENOMEM);
337820bdcab8SBastian Hecht 
33792095fc76SGeert Uytterhoeven 	/* Get the line number from the aliases node. */
338020bdcab8SBastian Hecht 	id = of_alias_get_id(np, "serial");
33817678f4c2SGeert Uytterhoeven 	if (id < 0 && ~sci_ports_in_use)
33827678f4c2SGeert Uytterhoeven 		id = ffz(sci_ports_in_use);
338320bdcab8SBastian Hecht 	if (id < 0) {
338420bdcab8SBastian Hecht 		dev_err(&pdev->dev, "failed to get alias id (%d)\n", id);
3385862f7218SLad Prabhakar 		return ERR_PTR(-EINVAL);
338620bdcab8SBastian Hecht 	}
3387090fa4b0SGeert Uytterhoeven 	if (id >= ARRAY_SIZE(sci_ports)) {
3388090fa4b0SGeert Uytterhoeven 		dev_err(&pdev->dev, "serial%d out of range\n", id);
3389862f7218SLad Prabhakar 		return ERR_PTR(-EINVAL);
3390090fa4b0SGeert Uytterhoeven 	}
339120bdcab8SBastian Hecht 
339297ed9790SLaurent Pinchart 	sp = &sci_ports[id];
339322a6984cSClaudiu Beznea 	sp->rstc = rstc;
339420bdcab8SBastian Hecht 	*dev_id = id;
339520bdcab8SBastian Hecht 
33966e605a01SGeert Uytterhoeven 	p->type = SCI_OF_TYPE(data);
33976e605a01SGeert Uytterhoeven 	p->regtype = SCI_OF_REGTYPE(data);
339820bdcab8SBastian Hecht 
339943c61286SSergei Shtylyov 	sp->has_rtscts = of_property_read_bool(np, "uart-has-rtscts");
3400861a70abSGeert Uytterhoeven 
340120bdcab8SBastian Hecht 	return p;
340220bdcab8SBastian Hecht }
340320bdcab8SBastian Hecht 
sci_probe_single(struct platform_device * dev,unsigned int index,struct plat_sci_port * p,struct sci_port * sciport,struct resource * sci_res)34049671f099SBill Pemberton static int sci_probe_single(struct platform_device *dev,
3405ab4382d2SGreg Kroah-Hartman 				      unsigned int index,
3406ab4382d2SGreg Kroah-Hartman 				      struct plat_sci_port *p,
34075f101706SClaudiu Beznea 				      struct sci_port *sciport,
34085f101706SClaudiu Beznea 				      struct resource *sci_res)
3409ab4382d2SGreg Kroah-Hartman {
3410ab4382d2SGreg Kroah-Hartman 	int ret;
3411ab4382d2SGreg Kroah-Hartman 
3412ab4382d2SGreg Kroah-Hartman 	/* Sanity check */
3413ab4382d2SGreg Kroah-Hartman 	if (unlikely(index >= SCI_NPORTS)) {
34149b971cd2SJoe Perches 		dev_notice(&dev->dev, "Attempting to register port %d when only %d are available\n",
3415ab4382d2SGreg Kroah-Hartman 			   index+1, SCI_NPORTS);
34169b971cd2SJoe Perches 		dev_notice(&dev->dev, "Consider bumping CONFIG_SERIAL_SH_SCI_NR_UARTS!\n");
3417b6c5ef6fSLaurent Pinchart 		return -EINVAL;
3418ab4382d2SGreg Kroah-Hartman 	}
34197678f4c2SGeert Uytterhoeven 	BUILD_BUG_ON(SCI_NPORTS > sizeof(sci_ports_in_use) * 8);
34207678f4c2SGeert Uytterhoeven 	if (sci_ports_in_use & BIT(index))
34217678f4c2SGeert Uytterhoeven 		return -EBUSY;
3422ab4382d2SGreg Kroah-Hartman 
3423352b9266SSjoerd Simons 	mutex_lock(&sci_uart_registration_lock);
3424352b9266SSjoerd Simons 	if (!sci_uart_driver.state) {
3425352b9266SSjoerd Simons 		ret = uart_register_driver(&sci_uart_driver);
3426352b9266SSjoerd Simons 		if (ret) {
3427352b9266SSjoerd Simons 			mutex_unlock(&sci_uart_registration_lock);
3428352b9266SSjoerd Simons 			return ret;
3429352b9266SSjoerd Simons 		}
3430352b9266SSjoerd Simons 	}
3431352b9266SSjoerd Simons 	mutex_unlock(&sci_uart_registration_lock);
3432352b9266SSjoerd Simons 
34331fcc91a6SLaurent Pinchart 	ret = sci_init_single(dev, sciport, index, p, false);
3434ab4382d2SGreg Kroah-Hartman 	if (ret)
3435ab4382d2SGreg Kroah-Hartman 		return ret;
3436ab4382d2SGreg Kroah-Hartman 
3437239f1120SClaudiu Beznea 	sciport->port.dev = &dev->dev;
3438239f1120SClaudiu Beznea 	ret = devm_pm_runtime_enable(&dev->dev);
3439239f1120SClaudiu Beznea 	if (ret)
3440239f1120SClaudiu Beznea 		return ret;
3441239f1120SClaudiu Beznea 
3442f907c9eaSGeert Uytterhoeven 	sciport->gpios = mctrl_gpio_init(&sciport->port, 0);
3443e55a0973SFrieder Schrempf 	if (IS_ERR(sciport->gpios))
3444f907c9eaSGeert Uytterhoeven 		return PTR_ERR(sciport->gpios);
3445f907c9eaSGeert Uytterhoeven 
344697ed9790SLaurent Pinchart 	if (sciport->has_rtscts) {
3447a16c4c5aSGeert Uytterhoeven 		if (mctrl_gpio_to_gpiod(sciport->gpios, UART_GPIO_CTS) ||
3448a16c4c5aSGeert Uytterhoeven 		    mctrl_gpio_to_gpiod(sciport->gpios, UART_GPIO_RTS)) {
3449f907c9eaSGeert Uytterhoeven 			dev_err(&dev->dev, "Conflicting RTS/CTS config\n");
3450f907c9eaSGeert Uytterhoeven 			return -EINVAL;
3451f907c9eaSGeert Uytterhoeven 		}
345233f50ffcSGeert Uytterhoeven 		sciport->port.flags |= UPF_HARD_FLOW;
3453f907c9eaSGeert Uytterhoeven 	}
3454f907c9eaSGeert Uytterhoeven 
34555f101706SClaudiu Beznea 	if (sci_uart_earlycon && sci_ports[0].port.mapbase == sci_res->start) {
34565f101706SClaudiu Beznea 		/*
3457651dee03SClaudiu Beznea 		 * In case:
3458651dee03SClaudiu Beznea 		 * - this is the earlycon port (mapped on index 0 in sci_ports[]) and
3459651dee03SClaudiu Beznea 		 * - it now maps to an alias other than zero and
3460651dee03SClaudiu Beznea 		 * - the earlycon is still alive (e.g., "earlycon keep_bootcon" is
3461651dee03SClaudiu Beznea 		 *   available in bootargs)
3462651dee03SClaudiu Beznea 		 *
3463651dee03SClaudiu Beznea 		 * we need to avoid disabling clocks and PM domains through the runtime
3464651dee03SClaudiu Beznea 		 * PM APIs called in __device_attach(). For this, increment the runtime
3465651dee03SClaudiu Beznea 		 * PM reference counter (the clocks and PM domains were already enabled
3466651dee03SClaudiu Beznea 		 * by the bootloader). Otherwise the earlycon may access the HW when it
3467651dee03SClaudiu Beznea 		 * has no clocks enabled leading to failures (infinite loop in
3468651dee03SClaudiu Beznea 		 * sci_poll_put_char()).
3469651dee03SClaudiu Beznea 		 */
3470651dee03SClaudiu Beznea 		pm_runtime_get_noresume(&dev->dev);
3471651dee03SClaudiu Beznea 
3472651dee03SClaudiu Beznea 		/*
34735f101706SClaudiu Beznea 		 * Skip cleanup the sci_port[0] in early_console_exit(), this
34745f101706SClaudiu Beznea 		 * port is the same as the earlycon one.
34755f101706SClaudiu Beznea 		 */
34765f101706SClaudiu Beznea 		sci_uart_earlycon_dev_probing = true;
34775f101706SClaudiu Beznea 	}
34785f101706SClaudiu Beznea 
3479239f1120SClaudiu Beznea 	return uart_add_one_port(&sci_uart_driver, &sciport->port);
3480ab4382d2SGreg Kroah-Hartman }
3481ab4382d2SGreg Kroah-Hartman 
sci_probe(struct platform_device * dev)34829671f099SBill Pemberton static int sci_probe(struct platform_device *dev)
3483ab4382d2SGreg Kroah-Hartman {
348420bdcab8SBastian Hecht 	struct plat_sci_port *p;
34859f7dea87SClaudiu Beznea 	struct resource *res;
348620bdcab8SBastian Hecht 	struct sci_port *sp;
348720bdcab8SBastian Hecht 	unsigned int dev_id;
34886b620478SPaul Mundt 	int ret;
3489ab4382d2SGreg Kroah-Hartman 
34906b620478SPaul Mundt 	/*
34916b620478SPaul Mundt 	 * If we've come here via earlyprintk initialization, head off to
34926b620478SPaul Mundt 	 * the special early probe. We don't have sufficient device state
34936b620478SPaul Mundt 	 * to make it beyond this yet.
34946b620478SPaul Mundt 	 */
3495507fd01dSBartosz Golaszewski #ifdef CONFIG_SUPERH
3496201e9109SBartosz Golaszewski 	if (is_sh_early_platform_device(dev))
34976b620478SPaul Mundt 		return sci_probe_earlyprintk(dev);
3498507fd01dSBartosz Golaszewski #endif
3499ab4382d2SGreg Kroah-Hartman 
350020bdcab8SBastian Hecht 	if (dev->dev.of_node) {
350120bdcab8SBastian Hecht 		p = sci_parse_dt(dev, &dev_id);
3502862f7218SLad Prabhakar 		if (IS_ERR(p))
3503862f7218SLad Prabhakar 			return PTR_ERR(p);
350420bdcab8SBastian Hecht 	} else {
350520bdcab8SBastian Hecht 		p = dev->dev.platform_data;
350620bdcab8SBastian Hecht 		if (p == NULL) {
350720bdcab8SBastian Hecht 			dev_err(&dev->dev, "no platform data supplied\n");
350820bdcab8SBastian Hecht 			return -EINVAL;
350920bdcab8SBastian Hecht 		}
351020bdcab8SBastian Hecht 
351120bdcab8SBastian Hecht 		dev_id = dev->id;
351220bdcab8SBastian Hecht 	}
351320bdcab8SBastian Hecht 
351420bdcab8SBastian Hecht 	sp = &sci_ports[dev_id];
35159f7dea87SClaudiu Beznea 
35169f7dea87SClaudiu Beznea 	/*
35179f7dea87SClaudiu Beznea 	 * In case:
35189f7dea87SClaudiu Beznea 	 * - the probed port alias is zero (as the one used by earlycon), and
35199f7dea87SClaudiu Beznea 	 * - the earlycon is still active (e.g., "earlycon keep_bootcon" in
35209f7dea87SClaudiu Beznea 	 *   bootargs)
35219f7dea87SClaudiu Beznea 	 *
35229f7dea87SClaudiu Beznea 	 * defer the probe of this serial. This is a debug scenario and the user
35239f7dea87SClaudiu Beznea 	 * must be aware of it.
35249f7dea87SClaudiu Beznea 	 *
35259f7dea87SClaudiu Beznea 	 * Except when the probed port is the same as the earlycon port.
35269f7dea87SClaudiu Beznea 	 */
35279f7dea87SClaudiu Beznea 
35289f7dea87SClaudiu Beznea 	res = platform_get_resource(dev, IORESOURCE_MEM, 0);
35299f7dea87SClaudiu Beznea 	if (!res)
35309f7dea87SClaudiu Beznea 		return -ENODEV;
35319f7dea87SClaudiu Beznea 
35329f7dea87SClaudiu Beznea 	if (sci_uart_earlycon && sp == &sci_ports[0] && sp->port.mapbase != res->start)
35339f7dea87SClaudiu Beznea 		return dev_err_probe(&dev->dev, -EBUSY, "sci_port[0] is used by earlycon!\n");
35349f7dea87SClaudiu Beznea 
35356b620478SPaul Mundt 	platform_set_drvdata(dev, sp);
3536ab4382d2SGreg Kroah-Hartman 
35375f101706SClaudiu Beznea 	ret = sci_probe_single(dev, dev_id, p, sp, res);
3538ab4382d2SGreg Kroah-Hartman 	if (ret)
35396dae1421SLaurent Pinchart 		return ret;
35406b620478SPaul Mundt 
35415d23188aSUlrich Hecht 	if (sp->port.fifosize > 1) {
35426aa57f16SGreg Kroah-Hartman 		ret = device_create_file(&dev->dev, &dev_attr_rx_fifo_trigger);
35435d23188aSUlrich Hecht 		if (ret)
35445d23188aSUlrich Hecht 			return ret;
35455d23188aSUlrich Hecht 	}
3546fa2abb03SUlrich Hecht 	if (sp->port.type == PORT_SCIFA || sp->port.type == PORT_SCIFB ||
3547fa2abb03SUlrich Hecht 	    sp->port.type == PORT_HSCIF) {
35486aa57f16SGreg Kroah-Hartman 		ret = device_create_file(&dev->dev, &dev_attr_rx_fifo_timeout);
35495d23188aSUlrich Hecht 		if (ret) {
35505d23188aSUlrich Hecht 			if (sp->port.fifosize > 1) {
35516aa57f16SGreg Kroah-Hartman 				device_remove_file(&dev->dev,
35526aa57f16SGreg Kroah-Hartman 						   &dev_attr_rx_fifo_trigger);
35535d23188aSUlrich Hecht 			}
35545d23188aSUlrich Hecht 			return ret;
35555d23188aSUlrich Hecht 		}
35565d23188aSUlrich Hecht 	}
35575d23188aSUlrich Hecht 
3558ab4382d2SGreg Kroah-Hartman #ifdef CONFIG_SH_STANDARD_BIOS
3559ab4382d2SGreg Kroah-Hartman 	sh_bios_gdb_detach();
3560ab4382d2SGreg Kroah-Hartman #endif
3561ab4382d2SGreg Kroah-Hartman 
35627678f4c2SGeert Uytterhoeven 	sci_ports_in_use |= BIT(dev_id);
3563ab4382d2SGreg Kroah-Hartman 	return 0;
3564ab4382d2SGreg Kroah-Hartman }
3565ab4382d2SGreg Kroah-Hartman 
sci_console_save(struct sci_port * s)356622a6984cSClaudiu Beznea static void sci_console_save(struct sci_port *s)
356722a6984cSClaudiu Beznea {
356822a6984cSClaudiu Beznea 	struct sci_suspend_regs *regs = &s->suspend_regs;
356922a6984cSClaudiu Beznea 	struct uart_port *port = &s->port;
357022a6984cSClaudiu Beznea 
357181100b9aSGeert Uytterhoeven 	if (sci_getreg(port, SCDL)->size)
357281100b9aSGeert Uytterhoeven 		regs->scdl = sci_serial_in(port, SCDL);
357381100b9aSGeert Uytterhoeven 	if (sci_getreg(port, SCCKS)->size)
357481100b9aSGeert Uytterhoeven 		regs->sccks = sci_serial_in(port, SCCKS);
357522a6984cSClaudiu Beznea 	if (sci_getreg(port, SCSMR)->size)
357622a6984cSClaudiu Beznea 		regs->scsmr = sci_serial_in(port, SCSMR);
357722a6984cSClaudiu Beznea 	if (sci_getreg(port, SCSCR)->size)
357822a6984cSClaudiu Beznea 		regs->scscr = sci_serial_in(port, SCSCR);
357922a6984cSClaudiu Beznea 	if (sci_getreg(port, SCFCR)->size)
358022a6984cSClaudiu Beznea 		regs->scfcr = sci_serial_in(port, SCFCR);
358122a6984cSClaudiu Beznea 	if (sci_getreg(port, SCSPTR)->size)
358222a6984cSClaudiu Beznea 		regs->scsptr = sci_serial_in(port, SCSPTR);
358322a6984cSClaudiu Beznea 	if (sci_getreg(port, SCBRR)->size)
358422a6984cSClaudiu Beznea 		regs->scbrr = sci_serial_in(port, SCBRR);
358581100b9aSGeert Uytterhoeven 	if (sci_getreg(port, HSSRR)->size)
358681100b9aSGeert Uytterhoeven 		regs->hssrr = sci_serial_in(port, HSSRR);
358781100b9aSGeert Uytterhoeven 	if (sci_getreg(port, SCPCR)->size)
358881100b9aSGeert Uytterhoeven 		regs->scpcr = sci_serial_in(port, SCPCR);
358981100b9aSGeert Uytterhoeven 	if (sci_getreg(port, SCPDR)->size)
359081100b9aSGeert Uytterhoeven 		regs->scpdr = sci_serial_in(port, SCPDR);
359122a6984cSClaudiu Beznea 	if (sci_getreg(port, SEMR)->size)
359222a6984cSClaudiu Beznea 		regs->semr = sci_serial_in(port, SEMR);
359322a6984cSClaudiu Beznea }
359422a6984cSClaudiu Beznea 
sci_console_restore(struct sci_port * s)359522a6984cSClaudiu Beznea static void sci_console_restore(struct sci_port *s)
359622a6984cSClaudiu Beznea {
359722a6984cSClaudiu Beznea 	struct sci_suspend_regs *regs = &s->suspend_regs;
359822a6984cSClaudiu Beznea 	struct uart_port *port = &s->port;
359922a6984cSClaudiu Beznea 
360081100b9aSGeert Uytterhoeven 	if (sci_getreg(port, SCDL)->size)
360181100b9aSGeert Uytterhoeven 		sci_serial_out(port, SCDL, regs->scdl);
360281100b9aSGeert Uytterhoeven 	if (sci_getreg(port, SCCKS)->size)
360381100b9aSGeert Uytterhoeven 		sci_serial_out(port, SCCKS, regs->sccks);
360422a6984cSClaudiu Beznea 	if (sci_getreg(port, SCSMR)->size)
360522a6984cSClaudiu Beznea 		sci_serial_out(port, SCSMR, regs->scsmr);
360622a6984cSClaudiu Beznea 	if (sci_getreg(port, SCSCR)->size)
360722a6984cSClaudiu Beznea 		sci_serial_out(port, SCSCR, regs->scscr);
360822a6984cSClaudiu Beznea 	if (sci_getreg(port, SCFCR)->size)
360922a6984cSClaudiu Beznea 		sci_serial_out(port, SCFCR, regs->scfcr);
361022a6984cSClaudiu Beznea 	if (sci_getreg(port, SCSPTR)->size)
361122a6984cSClaudiu Beznea 		sci_serial_out(port, SCSPTR, regs->scsptr);
361222a6984cSClaudiu Beznea 	if (sci_getreg(port, SCBRR)->size)
361322a6984cSClaudiu Beznea 		sci_serial_out(port, SCBRR, regs->scbrr);
361481100b9aSGeert Uytterhoeven 	if (sci_getreg(port, HSSRR)->size)
361581100b9aSGeert Uytterhoeven 		sci_serial_out(port, HSSRR, regs->hssrr);
361681100b9aSGeert Uytterhoeven 	if (sci_getreg(port, SCPCR)->size)
361781100b9aSGeert Uytterhoeven 		sci_serial_out(port, SCPCR, regs->scpcr);
361881100b9aSGeert Uytterhoeven 	if (sci_getreg(port, SCPDR)->size)
361981100b9aSGeert Uytterhoeven 		sci_serial_out(port, SCPDR, regs->scpdr);
362022a6984cSClaudiu Beznea 	if (sci_getreg(port, SEMR)->size)
362122a6984cSClaudiu Beznea 		sci_serial_out(port, SEMR, regs->semr);
362222a6984cSClaudiu Beznea }
362322a6984cSClaudiu Beznea 
sci_suspend(struct device * dev)3624cb876341SSergei Shtylyov static __maybe_unused int sci_suspend(struct device *dev)
3625ab4382d2SGreg Kroah-Hartman {
36266b620478SPaul Mundt 	struct sci_port *sport = dev_get_drvdata(dev);
3627ab4382d2SGreg Kroah-Hartman 
362822a6984cSClaudiu Beznea 	if (sport) {
36296b620478SPaul Mundt 		uart_suspend_port(&sci_uart_driver, &sport->port);
3630ab4382d2SGreg Kroah-Hartman 
363122a6984cSClaudiu Beznea 		if (!console_suspend_enabled && uart_console(&sport->port))
363222a6984cSClaudiu Beznea 			sci_console_save(sport);
363322a6984cSClaudiu Beznea 		else
363422a6984cSClaudiu Beznea 			return reset_control_assert(sport->rstc);
363522a6984cSClaudiu Beznea 	}
363622a6984cSClaudiu Beznea 
3637ab4382d2SGreg Kroah-Hartman 	return 0;
3638ab4382d2SGreg Kroah-Hartman }
3639ab4382d2SGreg Kroah-Hartman 
sci_resume(struct device * dev)3640cb876341SSergei Shtylyov static __maybe_unused int sci_resume(struct device *dev)
3641ab4382d2SGreg Kroah-Hartman {
36426b620478SPaul Mundt 	struct sci_port *sport = dev_get_drvdata(dev);
3643ab4382d2SGreg Kroah-Hartman 
364422a6984cSClaudiu Beznea 	if (sport) {
364522a6984cSClaudiu Beznea 		if (!console_suspend_enabled && uart_console(&sport->port)) {
364622a6984cSClaudiu Beznea 			sci_console_restore(sport);
364722a6984cSClaudiu Beznea 		} else {
364822a6984cSClaudiu Beznea 			int ret = reset_control_deassert(sport->rstc);
364922a6984cSClaudiu Beznea 
365022a6984cSClaudiu Beznea 			if (ret)
365122a6984cSClaudiu Beznea 				return ret;
365222a6984cSClaudiu Beznea 		}
365322a6984cSClaudiu Beznea 
36546b620478SPaul Mundt 		uart_resume_port(&sci_uart_driver, &sport->port);
365522a6984cSClaudiu Beznea 	}
3656ab4382d2SGreg Kroah-Hartman 
3657ab4382d2SGreg Kroah-Hartman 	return 0;
3658ab4382d2SGreg Kroah-Hartman }
3659ab4382d2SGreg Kroah-Hartman 
3660cb876341SSergei Shtylyov static SIMPLE_DEV_PM_OPS(sci_dev_pm_ops, sci_suspend, sci_resume);
3661ab4382d2SGreg Kroah-Hartman 
3662ab4382d2SGreg Kroah-Hartman static struct platform_driver sci_driver = {
3663ab4382d2SGreg Kroah-Hartman 	.probe		= sci_probe,
36645cbb9b17SUwe Kleine-König 	.remove		= sci_remove,
3665ab4382d2SGreg Kroah-Hartman 	.driver		= {
3666ab4382d2SGreg Kroah-Hartman 		.name	= "sh-sci",
3667ab4382d2SGreg Kroah-Hartman 		.pm	= &sci_dev_pm_ops,
366820bdcab8SBastian Hecht 		.of_match_table = of_match_ptr(of_sci_match),
3669ab4382d2SGreg Kroah-Hartman 	},
3670ab4382d2SGreg Kroah-Hartman };
3671ab4382d2SGreg Kroah-Hartman 
sci_init(void)3672ab4382d2SGreg Kroah-Hartman static int __init sci_init(void)
3673ab4382d2SGreg Kroah-Hartman {
36746c13d5d2SGeert Uytterhoeven 	pr_info("%s\n", banner);
3675ab4382d2SGreg Kroah-Hartman 
3676352b9266SSjoerd Simons 	return platform_driver_register(&sci_driver);
3677ab4382d2SGreg Kroah-Hartman }
3678ab4382d2SGreg Kroah-Hartman 
sci_exit(void)3679ab4382d2SGreg Kroah-Hartman static void __exit sci_exit(void)
3680ab4382d2SGreg Kroah-Hartman {
3681ab4382d2SGreg Kroah-Hartman 	platform_driver_unregister(&sci_driver);
3682352b9266SSjoerd Simons 
3683352b9266SSjoerd Simons 	if (sci_uart_driver.state)
3684ab4382d2SGreg Kroah-Hartman 		uart_unregister_driver(&sci_uart_driver);
3685ab4382d2SGreg Kroah-Hartman }
3686ab4382d2SGreg Kroah-Hartman 
3687507fd01dSBartosz Golaszewski #if defined(CONFIG_SUPERH) && defined(CONFIG_SERIAL_SH_SCI_CONSOLE)
3688201e9109SBartosz Golaszewski sh_early_platform_init_buffer("earlyprintk", &sci_driver,
3689ab4382d2SGreg Kroah-Hartman 			   early_serial_buf, ARRAY_SIZE(early_serial_buf));
3690ab4382d2SGreg Kroah-Hartman #endif
36910b0cced1SYoshinori Sato #ifdef CONFIG_SERIAL_SH_SCI_EARLYCON
3692eaeee422SClaudiu Beznea static struct plat_sci_port port_cfg;
36930b0cced1SYoshinori Sato 
early_console_exit(struct console * co)36945f101706SClaudiu Beznea static int early_console_exit(struct console *co)
36955f101706SClaudiu Beznea {
36965f101706SClaudiu Beznea 	struct sci_port *sci_port = &sci_ports[0];
36975f101706SClaudiu Beznea 
36985f101706SClaudiu Beznea 	/*
36995f101706SClaudiu Beznea 	 * Clean the slot used by earlycon. A new SCI device might
37005f101706SClaudiu Beznea 	 * map to this slot.
37015f101706SClaudiu Beznea 	 */
37025f101706SClaudiu Beznea 	if (!sci_uart_earlycon_dev_probing) {
37035f101706SClaudiu Beznea 		memset(sci_port, 0, sizeof(*sci_port));
37045f101706SClaudiu Beznea 		sci_uart_earlycon = false;
37055f101706SClaudiu Beznea 	}
37065f101706SClaudiu Beznea 
37075f101706SClaudiu Beznea 	return 0;
37085f101706SClaudiu Beznea }
37095f101706SClaudiu Beznea 
early_console_setup(struct earlycon_device * device,int type)37100b0cced1SYoshinori Sato static int __init early_console_setup(struct earlycon_device *device,
37110b0cced1SYoshinori Sato 				      int type)
37120b0cced1SYoshinori Sato {
37130b0cced1SYoshinori Sato 	if (!device->port.membase)
37140b0cced1SYoshinori Sato 		return -ENODEV;
37150b0cced1SYoshinori Sato 
37160b0cced1SYoshinori Sato 	device->port.type = type;
3717c1117a2fSGeert Uytterhoeven 	sci_ports[0].port = device->port;
3718daf5a895SLaurent Pinchart 	port_cfg.type = type;
37190b0cced1SYoshinori Sato 	sci_ports[0].cfg = &port_cfg;
3720daf5a895SLaurent Pinchart 	sci_ports[0].params = sci_probe_regmap(&port_cfg);
37219f7dea87SClaudiu Beznea 	sci_uart_earlycon = true;
37229f8325b3SLaurent Pinchart 	port_cfg.scscr = sci_serial_in(&sci_ports[0].port, SCSCR);
37239f8325b3SLaurent Pinchart 	sci_serial_out(&sci_ports[0].port, SCSCR,
37249f8325b3SLaurent Pinchart 		       SCSCR_RE | SCSCR_TE | port_cfg.scscr);
37250b0cced1SYoshinori Sato 
37260b0cced1SYoshinori Sato 	device->con->write = serial_console_write;
37275f101706SClaudiu Beznea 	device->con->exit = early_console_exit;
37285f101706SClaudiu Beznea 
37290b0cced1SYoshinori Sato 	return 0;
37300b0cced1SYoshinori Sato }
sci_early_console_setup(struct earlycon_device * device,const char * opt)37310b0cced1SYoshinori Sato static int __init sci_early_console_setup(struct earlycon_device *device,
37320b0cced1SYoshinori Sato 					  const char *opt)
37330b0cced1SYoshinori Sato {
37340b0cced1SYoshinori Sato 	return early_console_setup(device, PORT_SCI);
37350b0cced1SYoshinori Sato }
scif_early_console_setup(struct earlycon_device * device,const char * opt)37360b0cced1SYoshinori Sato static int __init scif_early_console_setup(struct earlycon_device *device,
37370b0cced1SYoshinori Sato 					  const char *opt)
37380b0cced1SYoshinori Sato {
37390b0cced1SYoshinori Sato 	return early_console_setup(device, PORT_SCIF);
37400b0cced1SYoshinori Sato }
rzscifa_early_console_setup(struct earlycon_device * device,const char * opt)37413d8b43adSChris Brandt static int __init rzscifa_early_console_setup(struct earlycon_device *device,
37423d8b43adSChris Brandt 					  const char *opt)
37433d8b43adSChris Brandt {
37443d8b43adSChris Brandt 	port_cfg.regtype = SCIx_RZ_SCIFA_REGTYPE;
37453d8b43adSChris Brandt 	return early_console_setup(device, PORT_SCIF);
37463d8b43adSChris Brandt }
37473b2cd606SBiju Das 
rzv2hscif_early_console_setup(struct earlycon_device * device,const char * opt)37482f50304eSLad Prabhakar static int __init rzv2hscif_early_console_setup(struct earlycon_device *device,
37492f50304eSLad Prabhakar 						const char *opt)
37502f50304eSLad Prabhakar {
37512f50304eSLad Prabhakar 	port_cfg.regtype = SCIx_RZV2H_SCIF_REGTYPE;
37522f50304eSLad Prabhakar 	return early_console_setup(device, PORT_SCIF);
37532f50304eSLad Prabhakar }
37542f50304eSLad Prabhakar 
scifa_early_console_setup(struct earlycon_device * device,const char * opt)37550b0cced1SYoshinori Sato static int __init scifa_early_console_setup(struct earlycon_device *device,
37560b0cced1SYoshinori Sato 					  const char *opt)
37570b0cced1SYoshinori Sato {
37580b0cced1SYoshinori Sato 	return early_console_setup(device, PORT_SCIFA);
37590b0cced1SYoshinori Sato }
scifb_early_console_setup(struct earlycon_device * device,const char * opt)37600b0cced1SYoshinori Sato static int __init scifb_early_console_setup(struct earlycon_device *device,
37610b0cced1SYoshinori Sato 					  const char *opt)
37620b0cced1SYoshinori Sato {
37630b0cced1SYoshinori Sato 	return early_console_setup(device, PORT_SCIFB);
37640b0cced1SYoshinori Sato }
hscif_early_console_setup(struct earlycon_device * device,const char * opt)37650b0cced1SYoshinori Sato static int __init hscif_early_console_setup(struct earlycon_device *device,
37660b0cced1SYoshinori Sato 					  const char *opt)
37670b0cced1SYoshinori Sato {
37680b0cced1SYoshinori Sato 	return early_console_setup(device, PORT_HSCIF);
37690b0cced1SYoshinori Sato }
37700b0cced1SYoshinori Sato 
37710b0cced1SYoshinori Sato OF_EARLYCON_DECLARE(sci, "renesas,sci", sci_early_console_setup);
37720b0cced1SYoshinori Sato OF_EARLYCON_DECLARE(scif, "renesas,scif", scif_early_console_setup);
37733d8b43adSChris Brandt OF_EARLYCON_DECLARE(scif, "renesas,scif-r7s9210", rzscifa_early_console_setup);
37743b2cd606SBiju Das OF_EARLYCON_DECLARE(scif, "renesas,scif-r9a07g044", rzscifa_early_console_setup);
37752f50304eSLad Prabhakar OF_EARLYCON_DECLARE(scif, "renesas,scif-r9a09g057", rzv2hscif_early_console_setup);
37760b0cced1SYoshinori Sato OF_EARLYCON_DECLARE(scifa, "renesas,scifa", scifa_early_console_setup);
37770b0cced1SYoshinori Sato OF_EARLYCON_DECLARE(scifb, "renesas,scifb", scifb_early_console_setup);
37780b0cced1SYoshinori Sato OF_EARLYCON_DECLARE(hscif, "renesas,hscif", hscif_early_console_setup);
37790b0cced1SYoshinori Sato #endif /* CONFIG_SERIAL_SH_SCI_EARLYCON */
37800b0cced1SYoshinori Sato 
3781ab4382d2SGreg Kroah-Hartman module_init(sci_init);
3782ab4382d2SGreg Kroah-Hartman module_exit(sci_exit);
3783ab4382d2SGreg Kroah-Hartman 
3784ab4382d2SGreg Kroah-Hartman MODULE_LICENSE("GPL");
3785ab4382d2SGreg Kroah-Hartman MODULE_ALIAS("platform:sh-sci");
37867f405f9cSPaul Mundt MODULE_AUTHOR("Paul Mundt");
3787f303b364SUlrich Hecht MODULE_DESCRIPTION("SuperH (H)SCI(F) serial driver");
3788