xref: /openbmc/linux/drivers/tty/serial/sccnxp.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1e3b3d0f5SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+
21d65c0b1SAlexander Shiyan /*
31d65c0b1SAlexander Shiyan  *  NXP (Philips) SCC+++(SCN+++) serial driver
41d65c0b1SAlexander Shiyan  *
51d65c0b1SAlexander Shiyan  *  Copyright (C) 2012 Alexander Shiyan <shc_work@mail.ru>
61d65c0b1SAlexander Shiyan  *
71d65c0b1SAlexander Shiyan  *  Based on sc26xx.c, by Thomas Bogendörfer (tsbogend@alpha.franken.de)
81d65c0b1SAlexander Shiyan  */
91d65c0b1SAlexander Shiyan 
1090efa75fSAlexander Shiyan #include <linux/clk.h>
114ce193fdSAlexander Shiyan #include <linux/delay.h>
12eb612fa0SThierry Reding #include <linux/err.h>
131d65c0b1SAlexander Shiyan #include <linux/module.h>
14ac316725SRandy Dunlap #include <linux/mod_devicetable.h>
151d65c0b1SAlexander Shiyan #include <linux/device.h>
16d83b5425SStephen Rothwell #include <linux/console.h>
171d65c0b1SAlexander Shiyan #include <linux/serial_core.h>
181d65c0b1SAlexander Shiyan #include <linux/serial.h>
191d65c0b1SAlexander Shiyan #include <linux/io.h>
201d65c0b1SAlexander Shiyan #include <linux/tty.h>
211d65c0b1SAlexander Shiyan #include <linux/tty_flip.h>
22ec063899SAlexander Shiyan #include <linux/spinlock.h>
231d65c0b1SAlexander Shiyan #include <linux/platform_device.h>
24463dcc42SAlexander Shiyan #include <linux/platform_data/serial-sccnxp.h>
2531815c08SAlexander Shiyan #include <linux/regulator/consumer.h>
261d65c0b1SAlexander Shiyan 
271d65c0b1SAlexander Shiyan #define SCCNXP_NAME			"uart-sccnxp"
281d65c0b1SAlexander Shiyan #define SCCNXP_MAJOR			204
291d65c0b1SAlexander Shiyan #define SCCNXP_MINOR			205
301d65c0b1SAlexander Shiyan 
311d65c0b1SAlexander Shiyan #define SCCNXP_MR_REG			(0x00)
321d65c0b1SAlexander Shiyan #	define MR0_BAUD_NORMAL		(0 << 0)
331d65c0b1SAlexander Shiyan #	define MR0_BAUD_EXT1		(1 << 0)
341d65c0b1SAlexander Shiyan #	define MR0_BAUD_EXT2		(5 << 0)
351d65c0b1SAlexander Shiyan #	define MR0_FIFO			(1 << 3)
361d65c0b1SAlexander Shiyan #	define MR0_TXLVL		(1 << 4)
371d65c0b1SAlexander Shiyan #	define MR1_BITS_5		(0 << 0)
381d65c0b1SAlexander Shiyan #	define MR1_BITS_6		(1 << 0)
391d65c0b1SAlexander Shiyan #	define MR1_BITS_7		(2 << 0)
401d65c0b1SAlexander Shiyan #	define MR1_BITS_8		(3 << 0)
411d65c0b1SAlexander Shiyan #	define MR1_PAR_EVN		(0 << 2)
421d65c0b1SAlexander Shiyan #	define MR1_PAR_ODD		(1 << 2)
431d65c0b1SAlexander Shiyan #	define MR1_PAR_NO		(4 << 2)
441d65c0b1SAlexander Shiyan #	define MR2_STOP1		(7 << 0)
451d65c0b1SAlexander Shiyan #	define MR2_STOP2		(0xf << 0)
461d65c0b1SAlexander Shiyan #define SCCNXP_SR_REG			(0x01)
471d65c0b1SAlexander Shiyan #	define SR_RXRDY			(1 << 0)
481d65c0b1SAlexander Shiyan #	define SR_FULL			(1 << 1)
491d65c0b1SAlexander Shiyan #	define SR_TXRDY			(1 << 2)
501d65c0b1SAlexander Shiyan #	define SR_TXEMT			(1 << 3)
511d65c0b1SAlexander Shiyan #	define SR_OVR			(1 << 4)
521d65c0b1SAlexander Shiyan #	define SR_PE			(1 << 5)
531d65c0b1SAlexander Shiyan #	define SR_FE			(1 << 6)
541d65c0b1SAlexander Shiyan #	define SR_BRK			(1 << 7)
55efa0f494SAlexander Shiyan #define SCCNXP_CSR_REG			(SCCNXP_SR_REG)
56efa0f494SAlexander Shiyan #	define CSR_TIMER_MODE		(0x0d)
571d65c0b1SAlexander Shiyan #define SCCNXP_CR_REG			(0x02)
581d65c0b1SAlexander Shiyan #	define CR_RX_ENABLE		(1 << 0)
591d65c0b1SAlexander Shiyan #	define CR_RX_DISABLE		(1 << 1)
601d65c0b1SAlexander Shiyan #	define CR_TX_ENABLE		(1 << 2)
611d65c0b1SAlexander Shiyan #	define CR_TX_DISABLE		(1 << 3)
621d65c0b1SAlexander Shiyan #	define CR_CMD_MRPTR1		(0x01 << 4)
631d65c0b1SAlexander Shiyan #	define CR_CMD_RX_RESET		(0x02 << 4)
641d65c0b1SAlexander Shiyan #	define CR_CMD_TX_RESET		(0x03 << 4)
651d65c0b1SAlexander Shiyan #	define CR_CMD_STATUS_RESET	(0x04 << 4)
661d65c0b1SAlexander Shiyan #	define CR_CMD_BREAK_RESET	(0x05 << 4)
671d65c0b1SAlexander Shiyan #	define CR_CMD_START_BREAK	(0x06 << 4)
681d65c0b1SAlexander Shiyan #	define CR_CMD_STOP_BREAK	(0x07 << 4)
691d65c0b1SAlexander Shiyan #	define CR_CMD_MRPTR0		(0x0b << 4)
701d65c0b1SAlexander Shiyan #define SCCNXP_RHR_REG			(0x03)
711d65c0b1SAlexander Shiyan #define SCCNXP_THR_REG			SCCNXP_RHR_REG
721d65c0b1SAlexander Shiyan #define SCCNXP_IPCR_REG			(0x04)
731d65c0b1SAlexander Shiyan #define SCCNXP_ACR_REG			SCCNXP_IPCR_REG
741d65c0b1SAlexander Shiyan #	define ACR_BAUD0		(0 << 7)
751d65c0b1SAlexander Shiyan #	define ACR_BAUD1		(1 << 7)
761d65c0b1SAlexander Shiyan #	define ACR_TIMER_MODE		(6 << 4)
771d65c0b1SAlexander Shiyan #define SCCNXP_ISR_REG			(0x05)
781d65c0b1SAlexander Shiyan #define SCCNXP_IMR_REG			SCCNXP_ISR_REG
791d65c0b1SAlexander Shiyan #	define IMR_TXRDY		(1 << 0)
801d65c0b1SAlexander Shiyan #	define IMR_RXRDY		(1 << 1)
811d65c0b1SAlexander Shiyan #	define ISR_TXRDY(x)		(1 << ((x * 4) + 0))
821d65c0b1SAlexander Shiyan #	define ISR_RXRDY(x)		(1 << ((x * 4) + 1))
83efa0f494SAlexander Shiyan #define SCCNXP_CTPU_REG			(0x06)
84efa0f494SAlexander Shiyan #define SCCNXP_CTPL_REG			(0x07)
851d65c0b1SAlexander Shiyan #define SCCNXP_IPR_REG			(0x0d)
861d65c0b1SAlexander Shiyan #define SCCNXP_OPCR_REG			SCCNXP_IPR_REG
871d65c0b1SAlexander Shiyan #define SCCNXP_SOP_REG			(0x0e)
88efa0f494SAlexander Shiyan #define SCCNXP_START_COUNTER_REG	SCCNXP_SOP_REG
891d65c0b1SAlexander Shiyan #define SCCNXP_ROP_REG			(0x0f)
901d65c0b1SAlexander Shiyan 
911d65c0b1SAlexander Shiyan /* Route helpers */
921d65c0b1SAlexander Shiyan #define MCTRL_MASK(sig)			(0xf << (sig))
931d65c0b1SAlexander Shiyan #define MCTRL_IBIT(cfg, sig)		((((cfg) >> (sig)) & 0xf) - LINE_IP0)
941d65c0b1SAlexander Shiyan #define MCTRL_OBIT(cfg, sig)		((((cfg) >> (sig)) & 0xf) - LINE_OP0)
951d65c0b1SAlexander Shiyan 
96ea4c39beSAlexander Shiyan #define SCCNXP_HAVE_IO		0x00000001
97ea4c39beSAlexander Shiyan #define SCCNXP_HAVE_MR0		0x00000002
98ea4c39beSAlexander Shiyan 
99ea4c39beSAlexander Shiyan struct sccnxp_chip {
100ea4c39beSAlexander Shiyan 	const char		*name;
101ea4c39beSAlexander Shiyan 	unsigned int		nr;
102ea4c39beSAlexander Shiyan 	unsigned long		freq_min;
103ea4c39beSAlexander Shiyan 	unsigned long		freq_std;
104ea4c39beSAlexander Shiyan 	unsigned long		freq_max;
105ea4c39beSAlexander Shiyan 	unsigned int		flags;
106ea4c39beSAlexander Shiyan 	unsigned int		fifosize;
1074ce193fdSAlexander Shiyan 	/* Time between read/write cycles */
1084ce193fdSAlexander Shiyan 	unsigned int		trwd;
1091d65c0b1SAlexander Shiyan };
1101d65c0b1SAlexander Shiyan 
1111d65c0b1SAlexander Shiyan struct sccnxp_port {
1121d65c0b1SAlexander Shiyan 	struct uart_driver	uart;
1131d65c0b1SAlexander Shiyan 	struct uart_port	port[SCCNXP_MAX_UARTS];
114ec063899SAlexander Shiyan 	bool			opened[SCCNXP_MAX_UARTS];
1151d65c0b1SAlexander Shiyan 
1161d65c0b1SAlexander Shiyan 	int			irq;
1171d65c0b1SAlexander Shiyan 	u8			imr;
1181d65c0b1SAlexander Shiyan 
119ea4c39beSAlexander Shiyan 	struct sccnxp_chip	*chip;
1201d65c0b1SAlexander Shiyan 
1211d65c0b1SAlexander Shiyan #ifdef CONFIG_SERIAL_SCCNXP_CONSOLE
1221d65c0b1SAlexander Shiyan 	struct console		console;
1231d65c0b1SAlexander Shiyan #endif
1241d65c0b1SAlexander Shiyan 
125ec063899SAlexander Shiyan 	spinlock_t		lock;
126ec063899SAlexander Shiyan 
127ec063899SAlexander Shiyan 	bool			poll;
128ec063899SAlexander Shiyan 	struct timer_list	timer;
1291d65c0b1SAlexander Shiyan 
1301d65c0b1SAlexander Shiyan 	struct sccnxp_pdata	pdata;
13131815c08SAlexander Shiyan 
13231815c08SAlexander Shiyan 	struct regulator	*regulator;
1331d65c0b1SAlexander Shiyan };
1341d65c0b1SAlexander Shiyan 
135ea4c39beSAlexander Shiyan static const struct sccnxp_chip sc2681 = {
136ea4c39beSAlexander Shiyan 	.name		= "SC2681",
137ea4c39beSAlexander Shiyan 	.nr		= 2,
138ea4c39beSAlexander Shiyan 	.freq_min	= 1000000,
139ea4c39beSAlexander Shiyan 	.freq_std	= 3686400,
140ea4c39beSAlexander Shiyan 	.freq_max	= 4000000,
141ea4c39beSAlexander Shiyan 	.flags		= SCCNXP_HAVE_IO,
142ea4c39beSAlexander Shiyan 	.fifosize	= 3,
1434ce193fdSAlexander Shiyan 	.trwd		= 200,
144ea4c39beSAlexander Shiyan };
1451d65c0b1SAlexander Shiyan 
146ea4c39beSAlexander Shiyan static const struct sccnxp_chip sc2691 = {
147ea4c39beSAlexander Shiyan 	.name		= "SC2691",
148ea4c39beSAlexander Shiyan 	.nr		= 1,
149ea4c39beSAlexander Shiyan 	.freq_min	= 1000000,
150ea4c39beSAlexander Shiyan 	.freq_std	= 3686400,
151ea4c39beSAlexander Shiyan 	.freq_max	= 4000000,
152ea4c39beSAlexander Shiyan 	.flags		= 0,
153ea4c39beSAlexander Shiyan 	.fifosize	= 3,
1544ce193fdSAlexander Shiyan 	.trwd		= 150,
155ea4c39beSAlexander Shiyan };
156ea4c39beSAlexander Shiyan 
157ea4c39beSAlexander Shiyan static const struct sccnxp_chip sc2692 = {
158ea4c39beSAlexander Shiyan 	.name		= "SC2692",
159ea4c39beSAlexander Shiyan 	.nr		= 2,
160ea4c39beSAlexander Shiyan 	.freq_min	= 1000000,
161ea4c39beSAlexander Shiyan 	.freq_std	= 3686400,
162ea4c39beSAlexander Shiyan 	.freq_max	= 4000000,
163ea4c39beSAlexander Shiyan 	.flags		= SCCNXP_HAVE_IO,
164ea4c39beSAlexander Shiyan 	.fifosize	= 3,
1654ce193fdSAlexander Shiyan 	.trwd		= 30,
166ea4c39beSAlexander Shiyan };
167ea4c39beSAlexander Shiyan 
168ea4c39beSAlexander Shiyan static const struct sccnxp_chip sc2891 = {
169ea4c39beSAlexander Shiyan 	.name		= "SC2891",
170ea4c39beSAlexander Shiyan 	.nr		= 1,
171ea4c39beSAlexander Shiyan 	.freq_min	= 100000,
172ea4c39beSAlexander Shiyan 	.freq_std	= 3686400,
173ea4c39beSAlexander Shiyan 	.freq_max	= 8000000,
174ea4c39beSAlexander Shiyan 	.flags		= SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0,
175ea4c39beSAlexander Shiyan 	.fifosize	= 16,
1764ce193fdSAlexander Shiyan 	.trwd		= 27,
177ea4c39beSAlexander Shiyan };
178ea4c39beSAlexander Shiyan 
179ea4c39beSAlexander Shiyan static const struct sccnxp_chip sc2892 = {
180ea4c39beSAlexander Shiyan 	.name		= "SC2892",
181ea4c39beSAlexander Shiyan 	.nr		= 2,
182ea4c39beSAlexander Shiyan 	.freq_min	= 100000,
183ea4c39beSAlexander Shiyan 	.freq_std	= 3686400,
184ea4c39beSAlexander Shiyan 	.freq_max	= 8000000,
185ea4c39beSAlexander Shiyan 	.flags		= SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0,
186ea4c39beSAlexander Shiyan 	.fifosize	= 16,
1874ce193fdSAlexander Shiyan 	.trwd		= 17,
188ea4c39beSAlexander Shiyan };
189ea4c39beSAlexander Shiyan 
190ea4c39beSAlexander Shiyan static const struct sccnxp_chip sc28202 = {
191ea4c39beSAlexander Shiyan 	.name		= "SC28202",
192ea4c39beSAlexander Shiyan 	.nr		= 2,
193ea4c39beSAlexander Shiyan 	.freq_min	= 1000000,
194ea4c39beSAlexander Shiyan 	.freq_std	= 14745600,
195ea4c39beSAlexander Shiyan 	.freq_max	= 50000000,
196ea4c39beSAlexander Shiyan 	.flags		= SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0,
197ea4c39beSAlexander Shiyan 	.fifosize	= 256,
1984ce193fdSAlexander Shiyan 	.trwd		= 10,
199ea4c39beSAlexander Shiyan };
200ea4c39beSAlexander Shiyan 
201ea4c39beSAlexander Shiyan static const struct sccnxp_chip sc68681 = {
202ea4c39beSAlexander Shiyan 	.name		= "SC68681",
203ea4c39beSAlexander Shiyan 	.nr		= 2,
204ea4c39beSAlexander Shiyan 	.freq_min	= 1000000,
205ea4c39beSAlexander Shiyan 	.freq_std	= 3686400,
206ea4c39beSAlexander Shiyan 	.freq_max	= 4000000,
207ea4c39beSAlexander Shiyan 	.flags		= SCCNXP_HAVE_IO,
208ea4c39beSAlexander Shiyan 	.fifosize	= 3,
2094ce193fdSAlexander Shiyan 	.trwd		= 200,
210ea4c39beSAlexander Shiyan };
211ea4c39beSAlexander Shiyan 
212ea4c39beSAlexander Shiyan static const struct sccnxp_chip sc68692 = {
213ea4c39beSAlexander Shiyan 	.name		= "SC68692",
214ea4c39beSAlexander Shiyan 	.nr		= 2,
215ea4c39beSAlexander Shiyan 	.freq_min	= 1000000,
216ea4c39beSAlexander Shiyan 	.freq_std	= 3686400,
217ea4c39beSAlexander Shiyan 	.freq_max	= 4000000,
218ea4c39beSAlexander Shiyan 	.flags		= SCCNXP_HAVE_IO,
219ea4c39beSAlexander Shiyan 	.fifosize	= 3,
2204ce193fdSAlexander Shiyan 	.trwd		= 200,
221ea4c39beSAlexander Shiyan };
2221d65c0b1SAlexander Shiyan 
sccnxp_read(struct uart_port * port,u8 reg)2234ce193fdSAlexander Shiyan static u8 sccnxp_read(struct uart_port *port, u8 reg)
2241d65c0b1SAlexander Shiyan {
2254ce193fdSAlexander Shiyan 	struct sccnxp_port *s = dev_get_drvdata(port->dev);
2264ce193fdSAlexander Shiyan 	u8 ret;
2274ce193fdSAlexander Shiyan 
2284ce193fdSAlexander Shiyan 	ret = readb(port->membase + (reg << port->regshift));
2294ce193fdSAlexander Shiyan 
2304ce193fdSAlexander Shiyan 	ndelay(s->chip->trwd);
2314ce193fdSAlexander Shiyan 
2324ce193fdSAlexander Shiyan 	return ret;
2331d65c0b1SAlexander Shiyan }
2341d65c0b1SAlexander Shiyan 
sccnxp_write(struct uart_port * port,u8 reg,u8 v)2354ce193fdSAlexander Shiyan static void sccnxp_write(struct uart_port *port, u8 reg, u8 v)
2361d65c0b1SAlexander Shiyan {
2374ce193fdSAlexander Shiyan 	struct sccnxp_port *s = dev_get_drvdata(port->dev);
2384ce193fdSAlexander Shiyan 
239ea4c39beSAlexander Shiyan 	writeb(v, port->membase + (reg << port->regshift));
2404ce193fdSAlexander Shiyan 
2414ce193fdSAlexander Shiyan 	ndelay(s->chip->trwd);
2421d65c0b1SAlexander Shiyan }
2431d65c0b1SAlexander Shiyan 
sccnxp_port_read(struct uart_port * port,u8 reg)2444ce193fdSAlexander Shiyan static u8 sccnxp_port_read(struct uart_port *port, u8 reg)
2451d65c0b1SAlexander Shiyan {
2461d65c0b1SAlexander Shiyan 	return sccnxp_read(port, (port->line << 3) + reg);
2471d65c0b1SAlexander Shiyan }
2481d65c0b1SAlexander Shiyan 
sccnxp_port_write(struct uart_port * port,u8 reg,u8 v)2494ce193fdSAlexander Shiyan static void sccnxp_port_write(struct uart_port *port, u8 reg, u8 v)
2501d65c0b1SAlexander Shiyan {
2511d65c0b1SAlexander Shiyan 	sccnxp_write(port, (port->line << 3) + reg, v);
2521d65c0b1SAlexander Shiyan }
2531d65c0b1SAlexander Shiyan 
sccnxp_update_best_err(int a,int b,int * besterr)2541d65c0b1SAlexander Shiyan static int sccnxp_update_best_err(int a, int b, int *besterr)
2551d65c0b1SAlexander Shiyan {
2561d65c0b1SAlexander Shiyan 	int err = abs(a - b);
2571d65c0b1SAlexander Shiyan 
258efa0f494SAlexander Shiyan 	if (*besterr > err) {
2591d65c0b1SAlexander Shiyan 		*besterr = err;
2601d65c0b1SAlexander Shiyan 		return 0;
2611d65c0b1SAlexander Shiyan 	}
2621d65c0b1SAlexander Shiyan 
2631d65c0b1SAlexander Shiyan 	return 1;
2641d65c0b1SAlexander Shiyan }
2651d65c0b1SAlexander Shiyan 
2664bbed6bcSAlexander Shiyan static const struct {
2671d65c0b1SAlexander Shiyan 	u8	csr;
2681d65c0b1SAlexander Shiyan 	u8	acr;
2691d65c0b1SAlexander Shiyan 	u8	mr0;
2701d65c0b1SAlexander Shiyan 	int	baud;
2714bbed6bcSAlexander Shiyan } baud_std[] = {
2721d65c0b1SAlexander Shiyan 	{ 0,	ACR_BAUD0,	MR0_BAUD_NORMAL,	50, },
2731d65c0b1SAlexander Shiyan 	{ 0,	ACR_BAUD1,	MR0_BAUD_NORMAL,	75, },
2741d65c0b1SAlexander Shiyan 	{ 1,	ACR_BAUD0,	MR0_BAUD_NORMAL,	110, },
2751d65c0b1SAlexander Shiyan 	{ 2,	ACR_BAUD0,	MR0_BAUD_NORMAL,	134, },
2761d65c0b1SAlexander Shiyan 	{ 3,	ACR_BAUD1,	MR0_BAUD_NORMAL,	150, },
2771d65c0b1SAlexander Shiyan 	{ 3,	ACR_BAUD0,	MR0_BAUD_NORMAL,	200, },
2781d65c0b1SAlexander Shiyan 	{ 4,	ACR_BAUD0,	MR0_BAUD_NORMAL,	300, },
2791d65c0b1SAlexander Shiyan 	{ 0,	ACR_BAUD1,	MR0_BAUD_EXT1,		450, },
2801d65c0b1SAlexander Shiyan 	{ 1,	ACR_BAUD0,	MR0_BAUD_EXT2,		880, },
2811d65c0b1SAlexander Shiyan 	{ 3,	ACR_BAUD1,	MR0_BAUD_EXT1,		900, },
2821d65c0b1SAlexander Shiyan 	{ 5,	ACR_BAUD0,	MR0_BAUD_NORMAL,	600, },
2831d65c0b1SAlexander Shiyan 	{ 7,	ACR_BAUD0,	MR0_BAUD_NORMAL,	1050, },
2841d65c0b1SAlexander Shiyan 	{ 2,	ACR_BAUD0,	MR0_BAUD_EXT2,		1076, },
2851d65c0b1SAlexander Shiyan 	{ 6,	ACR_BAUD0,	MR0_BAUD_NORMAL,	1200, },
2861d65c0b1SAlexander Shiyan 	{ 10,	ACR_BAUD1,	MR0_BAUD_NORMAL,	1800, },
2871d65c0b1SAlexander Shiyan 	{ 7,	ACR_BAUD1,	MR0_BAUD_NORMAL,	2000, },
2881d65c0b1SAlexander Shiyan 	{ 8,	ACR_BAUD0,	MR0_BAUD_NORMAL,	2400, },
2891d65c0b1SAlexander Shiyan 	{ 5,	ACR_BAUD1,	MR0_BAUD_EXT1,		3600, },
2901d65c0b1SAlexander Shiyan 	{ 9,	ACR_BAUD0,	MR0_BAUD_NORMAL,	4800, },
2911d65c0b1SAlexander Shiyan 	{ 10,	ACR_BAUD0,	MR0_BAUD_NORMAL,	7200, },
2921d65c0b1SAlexander Shiyan 	{ 11,	ACR_BAUD0,	MR0_BAUD_NORMAL,	9600, },
2931d65c0b1SAlexander Shiyan 	{ 8,	ACR_BAUD0,	MR0_BAUD_EXT1,		14400, },
2941d65c0b1SAlexander Shiyan 	{ 12,	ACR_BAUD1,	MR0_BAUD_NORMAL,	19200, },
2951d65c0b1SAlexander Shiyan 	{ 9,	ACR_BAUD0,	MR0_BAUD_EXT1,		28800, },
2961d65c0b1SAlexander Shiyan 	{ 12,	ACR_BAUD0,	MR0_BAUD_NORMAL,	38400, },
2971d65c0b1SAlexander Shiyan 	{ 11,	ACR_BAUD0,	MR0_BAUD_EXT1,		57600, },
2981d65c0b1SAlexander Shiyan 	{ 12,	ACR_BAUD1,	MR0_BAUD_EXT1,		115200, },
2991d65c0b1SAlexander Shiyan 	{ 12,	ACR_BAUD0,	MR0_BAUD_EXT1,		230400, },
3001d65c0b1SAlexander Shiyan 	{ 0, 0, 0, 0 }
3011d65c0b1SAlexander Shiyan };
3021d65c0b1SAlexander Shiyan 
sccnxp_set_baud(struct uart_port * port,int baud)3031685118fSAlexander Shiyan static int sccnxp_set_baud(struct uart_port *port, int baud)
3041d65c0b1SAlexander Shiyan {
3051d65c0b1SAlexander Shiyan 	struct sccnxp_port *s = dev_get_drvdata(port->dev);
306efa0f494SAlexander Shiyan 	int div_std, tmp_baud, bestbaud = INT_MAX, besterr = INT_MAX;
307ea4c39beSAlexander Shiyan 	struct sccnxp_chip *chip = s->chip;
3081d65c0b1SAlexander Shiyan 	u8 i, acr = 0, csr = 0, mr0 = 0;
3091d65c0b1SAlexander Shiyan 
310efa0f494SAlexander Shiyan 	/* Find divisor to load to the timer preset registers */
311efa0f494SAlexander Shiyan 	div_std = DIV_ROUND_CLOSEST(port->uartclk, 2 * 16 * baud);
312efa0f494SAlexander Shiyan 	if ((div_std >= 2) && (div_std <= 0xffff)) {
313efa0f494SAlexander Shiyan 		bestbaud = DIV_ROUND_CLOSEST(port->uartclk, 2 * 16 * div_std);
314efa0f494SAlexander Shiyan 		sccnxp_update_best_err(baud, bestbaud, &besterr);
315efa0f494SAlexander Shiyan 		csr = CSR_TIMER_MODE;
316efa0f494SAlexander Shiyan 		sccnxp_port_write(port, SCCNXP_CTPU_REG, div_std >> 8);
317efa0f494SAlexander Shiyan 		sccnxp_port_write(port, SCCNXP_CTPL_REG, div_std);
318efa0f494SAlexander Shiyan 		/* Issue start timer/counter command */
319efa0f494SAlexander Shiyan 		sccnxp_port_read(port, SCCNXP_START_COUNTER_REG);
320efa0f494SAlexander Shiyan 	}
321efa0f494SAlexander Shiyan 
3221d65c0b1SAlexander Shiyan 	/* Find best baud from table */
3231d65c0b1SAlexander Shiyan 	for (i = 0; baud_std[i].baud && besterr; i++) {
324ea4c39beSAlexander Shiyan 		if (baud_std[i].mr0 && !(chip->flags & SCCNXP_HAVE_MR0))
3251d65c0b1SAlexander Shiyan 			continue;
326ea4c39beSAlexander Shiyan 		div_std = DIV_ROUND_CLOSEST(chip->freq_std, baud_std[i].baud);
3271d65c0b1SAlexander Shiyan 		tmp_baud = DIV_ROUND_CLOSEST(port->uartclk, div_std);
3281d65c0b1SAlexander Shiyan 		if (!sccnxp_update_best_err(baud, tmp_baud, &besterr)) {
3291d65c0b1SAlexander Shiyan 			acr = baud_std[i].acr;
3301d65c0b1SAlexander Shiyan 			csr = baud_std[i].csr;
3311d65c0b1SAlexander Shiyan 			mr0 = baud_std[i].mr0;
3321d65c0b1SAlexander Shiyan 			bestbaud = tmp_baud;
3331d65c0b1SAlexander Shiyan 		}
3341d65c0b1SAlexander Shiyan 	}
3351d65c0b1SAlexander Shiyan 
336ea4c39beSAlexander Shiyan 	if (chip->flags & SCCNXP_HAVE_MR0) {
3371d65c0b1SAlexander Shiyan 		/* Enable FIFO, set half level for TX */
3381d65c0b1SAlexander Shiyan 		mr0 |= MR0_FIFO | MR0_TXLVL;
3391d65c0b1SAlexander Shiyan 		/* Update MR0 */
3401d65c0b1SAlexander Shiyan 		sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_MRPTR0);
3411d65c0b1SAlexander Shiyan 		sccnxp_port_write(port, SCCNXP_MR_REG, mr0);
3421d65c0b1SAlexander Shiyan 	}
3431d65c0b1SAlexander Shiyan 
3441d65c0b1SAlexander Shiyan 	sccnxp_port_write(port, SCCNXP_ACR_REG, acr | ACR_TIMER_MODE);
3451d65c0b1SAlexander Shiyan 	sccnxp_port_write(port, SCCNXP_CSR_REG, (csr << 4) | csr);
3461d65c0b1SAlexander Shiyan 
3471685118fSAlexander Shiyan 	if (baud != bestbaud)
3481d65c0b1SAlexander Shiyan 		dev_dbg(port->dev, "Baudrate desired: %i, calculated: %i\n",
3491d65c0b1SAlexander Shiyan 			baud, bestbaud);
3501685118fSAlexander Shiyan 
3511685118fSAlexander Shiyan 	return bestbaud;
3521d65c0b1SAlexander Shiyan }
3531d65c0b1SAlexander Shiyan 
sccnxp_enable_irq(struct uart_port * port,int mask)3541d65c0b1SAlexander Shiyan static void sccnxp_enable_irq(struct uart_port *port, int mask)
3551d65c0b1SAlexander Shiyan {
3561d65c0b1SAlexander Shiyan 	struct sccnxp_port *s = dev_get_drvdata(port->dev);
3571d65c0b1SAlexander Shiyan 
3581d65c0b1SAlexander Shiyan 	s->imr |= mask << (port->line * 4);
3591d65c0b1SAlexander Shiyan 	sccnxp_write(port, SCCNXP_IMR_REG, s->imr);
3601d65c0b1SAlexander Shiyan }
3611d65c0b1SAlexander Shiyan 
sccnxp_disable_irq(struct uart_port * port,int mask)3621d65c0b1SAlexander Shiyan static void sccnxp_disable_irq(struct uart_port *port, int mask)
3631d65c0b1SAlexander Shiyan {
3641d65c0b1SAlexander Shiyan 	struct sccnxp_port *s = dev_get_drvdata(port->dev);
3651d65c0b1SAlexander Shiyan 
3661d65c0b1SAlexander Shiyan 	s->imr &= ~(mask << (port->line * 4));
3671d65c0b1SAlexander Shiyan 	sccnxp_write(port, SCCNXP_IMR_REG, s->imr);
3681d65c0b1SAlexander Shiyan }
3691d65c0b1SAlexander Shiyan 
sccnxp_set_bit(struct uart_port * port,int sig,int state)3701d65c0b1SAlexander Shiyan static void sccnxp_set_bit(struct uart_port *port, int sig, int state)
3711d65c0b1SAlexander Shiyan {
3721d65c0b1SAlexander Shiyan 	u8 bitmask;
3731d65c0b1SAlexander Shiyan 	struct sccnxp_port *s = dev_get_drvdata(port->dev);
3741d65c0b1SAlexander Shiyan 
3751d65c0b1SAlexander Shiyan 	if (s->pdata.mctrl_cfg[port->line] & MCTRL_MASK(sig)) {
3761d65c0b1SAlexander Shiyan 		bitmask = 1 << MCTRL_OBIT(s->pdata.mctrl_cfg[port->line], sig);
3771d65c0b1SAlexander Shiyan 		if (state)
3781d65c0b1SAlexander Shiyan 			sccnxp_write(port, SCCNXP_SOP_REG, bitmask);
3791d65c0b1SAlexander Shiyan 		else
3801d65c0b1SAlexander Shiyan 			sccnxp_write(port, SCCNXP_ROP_REG, bitmask);
3811d65c0b1SAlexander Shiyan 	}
3821d65c0b1SAlexander Shiyan }
3831d65c0b1SAlexander Shiyan 
sccnxp_handle_rx(struct uart_port * port)3841d65c0b1SAlexander Shiyan static void sccnxp_handle_rx(struct uart_port *port)
3851d65c0b1SAlexander Shiyan {
386fd2b55f8SJiri Slaby 	u8 sr, ch, flag;
3871d65c0b1SAlexander Shiyan 
3881d65c0b1SAlexander Shiyan 	for (;;) {
3891d65c0b1SAlexander Shiyan 		sr = sccnxp_port_read(port, SCCNXP_SR_REG);
3901d65c0b1SAlexander Shiyan 		if (!(sr & SR_RXRDY))
3911d65c0b1SAlexander Shiyan 			break;
3921d65c0b1SAlexander Shiyan 		sr &= SR_PE | SR_FE | SR_OVR | SR_BRK;
3931d65c0b1SAlexander Shiyan 
3941d65c0b1SAlexander Shiyan 		ch = sccnxp_port_read(port, SCCNXP_RHR_REG);
3951d65c0b1SAlexander Shiyan 
3961d65c0b1SAlexander Shiyan 		port->icount.rx++;
3971d65c0b1SAlexander Shiyan 		flag = TTY_NORMAL;
3981d65c0b1SAlexander Shiyan 
3991d65c0b1SAlexander Shiyan 		if (unlikely(sr)) {
4001d65c0b1SAlexander Shiyan 			if (sr & SR_BRK) {
4011d65c0b1SAlexander Shiyan 				port->icount.brk++;
402f548b96dSAlexander Shiyan 				sccnxp_port_write(port, SCCNXP_CR_REG,
403f548b96dSAlexander Shiyan 						  CR_CMD_BREAK_RESET);
4041d65c0b1SAlexander Shiyan 				if (uart_handle_break(port))
4051d65c0b1SAlexander Shiyan 					continue;
4061d65c0b1SAlexander Shiyan 			} else if (sr & SR_PE)
4071d65c0b1SAlexander Shiyan 				port->icount.parity++;
4081d65c0b1SAlexander Shiyan 			else if (sr & SR_FE)
4091d65c0b1SAlexander Shiyan 				port->icount.frame++;
410f548b96dSAlexander Shiyan 			else if (sr & SR_OVR) {
4111d65c0b1SAlexander Shiyan 				port->icount.overrun++;
412f548b96dSAlexander Shiyan 				sccnxp_port_write(port, SCCNXP_CR_REG,
413f548b96dSAlexander Shiyan 						  CR_CMD_STATUS_RESET);
414f548b96dSAlexander Shiyan 			}
4151d65c0b1SAlexander Shiyan 
4161d65c0b1SAlexander Shiyan 			sr &= port->read_status_mask;
4171d65c0b1SAlexander Shiyan 			if (sr & SR_BRK)
4181d65c0b1SAlexander Shiyan 				flag = TTY_BREAK;
4191d65c0b1SAlexander Shiyan 			else if (sr & SR_PE)
4201d65c0b1SAlexander Shiyan 				flag = TTY_PARITY;
4211d65c0b1SAlexander Shiyan 			else if (sr & SR_FE)
4221d65c0b1SAlexander Shiyan 				flag = TTY_FRAME;
4231d65c0b1SAlexander Shiyan 			else if (sr & SR_OVR)
4241d65c0b1SAlexander Shiyan 				flag = TTY_OVERRUN;
4251d65c0b1SAlexander Shiyan 		}
4261d65c0b1SAlexander Shiyan 
4271d65c0b1SAlexander Shiyan 		if (uart_handle_sysrq_char(port, ch))
4281d65c0b1SAlexander Shiyan 			continue;
4291d65c0b1SAlexander Shiyan 
4301d65c0b1SAlexander Shiyan 		if (sr & port->ignore_status_mask)
4311d65c0b1SAlexander Shiyan 			continue;
4321d65c0b1SAlexander Shiyan 
4331d65c0b1SAlexander Shiyan 		uart_insert_char(port, sr, SR_OVR, ch, flag);
4341d65c0b1SAlexander Shiyan 	}
4351d65c0b1SAlexander Shiyan 
4362e124b4aSJiri Slaby 	tty_flip_buffer_push(&port->state->port);
4371d65c0b1SAlexander Shiyan }
4381d65c0b1SAlexander Shiyan 
sccnxp_handle_tx(struct uart_port * port)4391d65c0b1SAlexander Shiyan static void sccnxp_handle_tx(struct uart_port *port)
4401d65c0b1SAlexander Shiyan {
4411d65c0b1SAlexander Shiyan 	u8 sr;
4421d65c0b1SAlexander Shiyan 	struct circ_buf *xmit = &port->state->xmit;
4431d65c0b1SAlexander Shiyan 	struct sccnxp_port *s = dev_get_drvdata(port->dev);
4441d65c0b1SAlexander Shiyan 
4451d65c0b1SAlexander Shiyan 	if (unlikely(port->x_char)) {
4461d65c0b1SAlexander Shiyan 		sccnxp_port_write(port, SCCNXP_THR_REG, port->x_char);
4471d65c0b1SAlexander Shiyan 		port->icount.tx++;
4481d65c0b1SAlexander Shiyan 		port->x_char = 0;
4491d65c0b1SAlexander Shiyan 		return;
4501d65c0b1SAlexander Shiyan 	}
4511d65c0b1SAlexander Shiyan 
4521d65c0b1SAlexander Shiyan 	if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
4531d65c0b1SAlexander Shiyan 		/* Disable TX if FIFO is empty */
4541d65c0b1SAlexander Shiyan 		if (sccnxp_port_read(port, SCCNXP_SR_REG) & SR_TXEMT) {
4551d65c0b1SAlexander Shiyan 			sccnxp_disable_irq(port, IMR_TXRDY);
4561d65c0b1SAlexander Shiyan 
4571d65c0b1SAlexander Shiyan 			/* Set direction to input */
458ea4c39beSAlexander Shiyan 			if (s->chip->flags & SCCNXP_HAVE_IO)
4591d65c0b1SAlexander Shiyan 				sccnxp_set_bit(port, DIR_OP, 0);
4601d65c0b1SAlexander Shiyan 		}
4611d65c0b1SAlexander Shiyan 		return;
4621d65c0b1SAlexander Shiyan 	}
4631d65c0b1SAlexander Shiyan 
4641d65c0b1SAlexander Shiyan 	while (!uart_circ_empty(xmit)) {
4651d65c0b1SAlexander Shiyan 		sr = sccnxp_port_read(port, SCCNXP_SR_REG);
4661d65c0b1SAlexander Shiyan 		if (!(sr & SR_TXRDY))
4671d65c0b1SAlexander Shiyan 			break;
4681d65c0b1SAlexander Shiyan 
4691d65c0b1SAlexander Shiyan 		sccnxp_port_write(port, SCCNXP_THR_REG, xmit->buf[xmit->tail]);
4703ea03c02SIlpo Järvinen 		uart_xmit_advance(port, 1);
4711d65c0b1SAlexander Shiyan 	}
4721d65c0b1SAlexander Shiyan 
4731d65c0b1SAlexander Shiyan 	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
4741d65c0b1SAlexander Shiyan 		uart_write_wakeup(port);
4751d65c0b1SAlexander Shiyan }
4761d65c0b1SAlexander Shiyan 
sccnxp_handle_events(struct sccnxp_port * s)477ec063899SAlexander Shiyan static void sccnxp_handle_events(struct sccnxp_port *s)
4781d65c0b1SAlexander Shiyan {
4791d65c0b1SAlexander Shiyan 	int i;
4801d65c0b1SAlexander Shiyan 	u8 isr;
4811d65c0b1SAlexander Shiyan 
482ec063899SAlexander Shiyan 	do {
4831d65c0b1SAlexander Shiyan 		isr = sccnxp_read(&s->port[0], SCCNXP_ISR_REG);
4841d65c0b1SAlexander Shiyan 		isr &= s->imr;
4851d65c0b1SAlexander Shiyan 		if (!isr)
4861d65c0b1SAlexander Shiyan 			break;
4871d65c0b1SAlexander Shiyan 
4881d65c0b1SAlexander Shiyan 		for (i = 0; i < s->uart.nr; i++) {
489ec063899SAlexander Shiyan 			if (s->opened[i] && (isr & ISR_RXRDY(i)))
4901d65c0b1SAlexander Shiyan 				sccnxp_handle_rx(&s->port[i]);
491ec063899SAlexander Shiyan 			if (s->opened[i] && (isr & ISR_TXRDY(i)))
4921d65c0b1SAlexander Shiyan 				sccnxp_handle_tx(&s->port[i]);
4931d65c0b1SAlexander Shiyan 		}
494ec063899SAlexander Shiyan 	} while (1);
4951d65c0b1SAlexander Shiyan }
4961d65c0b1SAlexander Shiyan 
sccnxp_timer(struct timer_list * t)497fc3b00d7SKees Cook static void sccnxp_timer(struct timer_list *t)
498ec063899SAlexander Shiyan {
499fc3b00d7SKees Cook 	struct sccnxp_port *s = from_timer(s, t, timer);
500ec063899SAlexander Shiyan 	unsigned long flags;
501ec063899SAlexander Shiyan 
502ec063899SAlexander Shiyan 	spin_lock_irqsave(&s->lock, flags);
503ec063899SAlexander Shiyan 	sccnxp_handle_events(s);
504ec063899SAlexander Shiyan 	spin_unlock_irqrestore(&s->lock, flags);
505ec063899SAlexander Shiyan 
506bee18bdcSAlexander Shiyan 	mod_timer(&s->timer, jiffies + usecs_to_jiffies(s->pdata.poll_time_us));
507ec063899SAlexander Shiyan }
508ec063899SAlexander Shiyan 
sccnxp_ist(int irq,void * dev_id)509ec063899SAlexander Shiyan static irqreturn_t sccnxp_ist(int irq, void *dev_id)
510ec063899SAlexander Shiyan {
511ec063899SAlexander Shiyan 	struct sccnxp_port *s = (struct sccnxp_port *)dev_id;
512ec063899SAlexander Shiyan 	unsigned long flags;
513ec063899SAlexander Shiyan 
514ec063899SAlexander Shiyan 	spin_lock_irqsave(&s->lock, flags);
515ec063899SAlexander Shiyan 	sccnxp_handle_events(s);
516ec063899SAlexander Shiyan 	spin_unlock_irqrestore(&s->lock, flags);
5171d65c0b1SAlexander Shiyan 
5181d65c0b1SAlexander Shiyan 	return IRQ_HANDLED;
5191d65c0b1SAlexander Shiyan }
5201d65c0b1SAlexander Shiyan 
sccnxp_start_tx(struct uart_port * port)5211d65c0b1SAlexander Shiyan static void sccnxp_start_tx(struct uart_port *port)
5221d65c0b1SAlexander Shiyan {
5231d65c0b1SAlexander Shiyan 	struct sccnxp_port *s = dev_get_drvdata(port->dev);
524ec063899SAlexander Shiyan 	unsigned long flags;
5251d65c0b1SAlexander Shiyan 
526ec063899SAlexander Shiyan 	spin_lock_irqsave(&s->lock, flags);
5271d65c0b1SAlexander Shiyan 
5281d65c0b1SAlexander Shiyan 	/* Set direction to output */
529ea4c39beSAlexander Shiyan 	if (s->chip->flags & SCCNXP_HAVE_IO)
5301d65c0b1SAlexander Shiyan 		sccnxp_set_bit(port, DIR_OP, 1);
5311d65c0b1SAlexander Shiyan 
5321d65c0b1SAlexander Shiyan 	sccnxp_enable_irq(port, IMR_TXRDY);
5331d65c0b1SAlexander Shiyan 
534ec063899SAlexander Shiyan 	spin_unlock_irqrestore(&s->lock, flags);
5351d65c0b1SAlexander Shiyan }
5361d65c0b1SAlexander Shiyan 
sccnxp_stop_tx(struct uart_port * port)5371d65c0b1SAlexander Shiyan static void sccnxp_stop_tx(struct uart_port *port)
5381d65c0b1SAlexander Shiyan {
5391d65c0b1SAlexander Shiyan 	/* Do nothing */
5401d65c0b1SAlexander Shiyan }
5411d65c0b1SAlexander Shiyan 
sccnxp_stop_rx(struct uart_port * port)5421d65c0b1SAlexander Shiyan static void sccnxp_stop_rx(struct uart_port *port)
5431d65c0b1SAlexander Shiyan {
5441d65c0b1SAlexander Shiyan 	struct sccnxp_port *s = dev_get_drvdata(port->dev);
545ec063899SAlexander Shiyan 	unsigned long flags;
5461d65c0b1SAlexander Shiyan 
547ec063899SAlexander Shiyan 	spin_lock_irqsave(&s->lock, flags);
5481d65c0b1SAlexander Shiyan 	sccnxp_port_write(port, SCCNXP_CR_REG, CR_RX_DISABLE);
549ec063899SAlexander Shiyan 	spin_unlock_irqrestore(&s->lock, flags);
5501d65c0b1SAlexander Shiyan }
5511d65c0b1SAlexander Shiyan 
sccnxp_tx_empty(struct uart_port * port)5521d65c0b1SAlexander Shiyan static unsigned int sccnxp_tx_empty(struct uart_port *port)
5531d65c0b1SAlexander Shiyan {
5541d65c0b1SAlexander Shiyan 	u8 val;
555ec063899SAlexander Shiyan 	unsigned long flags;
5561d65c0b1SAlexander Shiyan 	struct sccnxp_port *s = dev_get_drvdata(port->dev);
5571d65c0b1SAlexander Shiyan 
558ec063899SAlexander Shiyan 	spin_lock_irqsave(&s->lock, flags);
5591d65c0b1SAlexander Shiyan 	val = sccnxp_port_read(port, SCCNXP_SR_REG);
560ec063899SAlexander Shiyan 	spin_unlock_irqrestore(&s->lock, flags);
5611d65c0b1SAlexander Shiyan 
5621d65c0b1SAlexander Shiyan 	return (val & SR_TXEMT) ? TIOCSER_TEMT : 0;
5631d65c0b1SAlexander Shiyan }
5641d65c0b1SAlexander Shiyan 
sccnxp_set_mctrl(struct uart_port * port,unsigned int mctrl)5651d65c0b1SAlexander Shiyan static void sccnxp_set_mctrl(struct uart_port *port, unsigned int mctrl)
5661d65c0b1SAlexander Shiyan {
5671d65c0b1SAlexander Shiyan 	struct sccnxp_port *s = dev_get_drvdata(port->dev);
568ec063899SAlexander Shiyan 	unsigned long flags;
5691d65c0b1SAlexander Shiyan 
570ea4c39beSAlexander Shiyan 	if (!(s->chip->flags & SCCNXP_HAVE_IO))
5711d65c0b1SAlexander Shiyan 		return;
5721d65c0b1SAlexander Shiyan 
573ec063899SAlexander Shiyan 	spin_lock_irqsave(&s->lock, flags);
5741d65c0b1SAlexander Shiyan 
5751d65c0b1SAlexander Shiyan 	sccnxp_set_bit(port, DTR_OP, mctrl & TIOCM_DTR);
5761d65c0b1SAlexander Shiyan 	sccnxp_set_bit(port, RTS_OP, mctrl & TIOCM_RTS);
5771d65c0b1SAlexander Shiyan 
578ec063899SAlexander Shiyan 	spin_unlock_irqrestore(&s->lock, flags);
5791d65c0b1SAlexander Shiyan }
5801d65c0b1SAlexander Shiyan 
sccnxp_get_mctrl(struct uart_port * port)5811d65c0b1SAlexander Shiyan static unsigned int sccnxp_get_mctrl(struct uart_port *port)
5821d65c0b1SAlexander Shiyan {
5831d65c0b1SAlexander Shiyan 	u8 bitmask, ipr;
584ec063899SAlexander Shiyan 	unsigned long flags;
5851d65c0b1SAlexander Shiyan 	struct sccnxp_port *s = dev_get_drvdata(port->dev);
5861d65c0b1SAlexander Shiyan 	unsigned int mctrl = TIOCM_DSR | TIOCM_CTS | TIOCM_CAR;
5871d65c0b1SAlexander Shiyan 
588ea4c39beSAlexander Shiyan 	if (!(s->chip->flags & SCCNXP_HAVE_IO))
5891d65c0b1SAlexander Shiyan 		return mctrl;
5901d65c0b1SAlexander Shiyan 
591ec063899SAlexander Shiyan 	spin_lock_irqsave(&s->lock, flags);
5921d65c0b1SAlexander Shiyan 
5931d65c0b1SAlexander Shiyan 	ipr = ~sccnxp_read(port, SCCNXP_IPCR_REG);
5941d65c0b1SAlexander Shiyan 
5951d65c0b1SAlexander Shiyan 	if (s->pdata.mctrl_cfg[port->line] & MCTRL_MASK(DSR_IP)) {
5961d65c0b1SAlexander Shiyan 		bitmask = 1 << MCTRL_IBIT(s->pdata.mctrl_cfg[port->line],
5971d65c0b1SAlexander Shiyan 					  DSR_IP);
5981d65c0b1SAlexander Shiyan 		mctrl &= ~TIOCM_DSR;
5991d65c0b1SAlexander Shiyan 		mctrl |= (ipr & bitmask) ? TIOCM_DSR : 0;
6001d65c0b1SAlexander Shiyan 	}
6011d65c0b1SAlexander Shiyan 	if (s->pdata.mctrl_cfg[port->line] & MCTRL_MASK(CTS_IP)) {
6021d65c0b1SAlexander Shiyan 		bitmask = 1 << MCTRL_IBIT(s->pdata.mctrl_cfg[port->line],
6031d65c0b1SAlexander Shiyan 					  CTS_IP);
6041d65c0b1SAlexander Shiyan 		mctrl &= ~TIOCM_CTS;
6051d65c0b1SAlexander Shiyan 		mctrl |= (ipr & bitmask) ? TIOCM_CTS : 0;
6061d65c0b1SAlexander Shiyan 	}
6071d65c0b1SAlexander Shiyan 	if (s->pdata.mctrl_cfg[port->line] & MCTRL_MASK(DCD_IP)) {
6081d65c0b1SAlexander Shiyan 		bitmask = 1 << MCTRL_IBIT(s->pdata.mctrl_cfg[port->line],
6091d65c0b1SAlexander Shiyan 					  DCD_IP);
6101d65c0b1SAlexander Shiyan 		mctrl &= ~TIOCM_CAR;
6111d65c0b1SAlexander Shiyan 		mctrl |= (ipr & bitmask) ? TIOCM_CAR : 0;
6121d65c0b1SAlexander Shiyan 	}
6131d65c0b1SAlexander Shiyan 	if (s->pdata.mctrl_cfg[port->line] & MCTRL_MASK(RNG_IP)) {
6141d65c0b1SAlexander Shiyan 		bitmask = 1 << MCTRL_IBIT(s->pdata.mctrl_cfg[port->line],
6151d65c0b1SAlexander Shiyan 					  RNG_IP);
6161d65c0b1SAlexander Shiyan 		mctrl &= ~TIOCM_RNG;
6171d65c0b1SAlexander Shiyan 		mctrl |= (ipr & bitmask) ? TIOCM_RNG : 0;
6181d65c0b1SAlexander Shiyan 	}
6191d65c0b1SAlexander Shiyan 
620ec063899SAlexander Shiyan 	spin_unlock_irqrestore(&s->lock, flags);
6211d65c0b1SAlexander Shiyan 
6221d65c0b1SAlexander Shiyan 	return mctrl;
6231d65c0b1SAlexander Shiyan }
6241d65c0b1SAlexander Shiyan 
sccnxp_break_ctl(struct uart_port * port,int break_state)6251d65c0b1SAlexander Shiyan static void sccnxp_break_ctl(struct uart_port *port, int break_state)
6261d65c0b1SAlexander Shiyan {
6271d65c0b1SAlexander Shiyan 	struct sccnxp_port *s = dev_get_drvdata(port->dev);
628ec063899SAlexander Shiyan 	unsigned long flags;
6291d65c0b1SAlexander Shiyan 
630ec063899SAlexander Shiyan 	spin_lock_irqsave(&s->lock, flags);
6311d65c0b1SAlexander Shiyan 	sccnxp_port_write(port, SCCNXP_CR_REG, break_state ?
6321d65c0b1SAlexander Shiyan 			  CR_CMD_START_BREAK : CR_CMD_STOP_BREAK);
633ec063899SAlexander Shiyan 	spin_unlock_irqrestore(&s->lock, flags);
6341d65c0b1SAlexander Shiyan }
6351d65c0b1SAlexander Shiyan 
sccnxp_set_termios(struct uart_port * port,struct ktermios * termios,const struct ktermios * old)6361d65c0b1SAlexander Shiyan static void sccnxp_set_termios(struct uart_port *port,
637bec5b814SIlpo Järvinen 			       struct ktermios *termios,
638bec5b814SIlpo Järvinen 			       const struct ktermios *old)
6391d65c0b1SAlexander Shiyan {
6401d65c0b1SAlexander Shiyan 	struct sccnxp_port *s = dev_get_drvdata(port->dev);
641ec063899SAlexander Shiyan 	unsigned long flags;
6421d65c0b1SAlexander Shiyan 	u8 mr1, mr2;
6431d65c0b1SAlexander Shiyan 	int baud;
6441d65c0b1SAlexander Shiyan 
645ec063899SAlexander Shiyan 	spin_lock_irqsave(&s->lock, flags);
6461d65c0b1SAlexander Shiyan 
6471d65c0b1SAlexander Shiyan 	/* Mask termios capabilities we don't support */
6481d65c0b1SAlexander Shiyan 	termios->c_cflag &= ~CMSPAR;
6491d65c0b1SAlexander Shiyan 
6501d65c0b1SAlexander Shiyan 	/* Disable RX & TX, reset break condition, status and FIFOs */
6511d65c0b1SAlexander Shiyan 	sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_RX_RESET |
6521d65c0b1SAlexander Shiyan 					       CR_RX_DISABLE | CR_TX_DISABLE);
6531d65c0b1SAlexander Shiyan 	sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_TX_RESET);
6541d65c0b1SAlexander Shiyan 	sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_STATUS_RESET);
6551d65c0b1SAlexander Shiyan 	sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_BREAK_RESET);
6561d65c0b1SAlexander Shiyan 
6571d65c0b1SAlexander Shiyan 	/* Word size */
6581d65c0b1SAlexander Shiyan 	switch (termios->c_cflag & CSIZE) {
6591d65c0b1SAlexander Shiyan 	case CS5:
6601d65c0b1SAlexander Shiyan 		mr1 = MR1_BITS_5;
6611d65c0b1SAlexander Shiyan 		break;
6621d65c0b1SAlexander Shiyan 	case CS6:
6631d65c0b1SAlexander Shiyan 		mr1 = MR1_BITS_6;
6641d65c0b1SAlexander Shiyan 		break;
6651d65c0b1SAlexander Shiyan 	case CS7:
6661d65c0b1SAlexander Shiyan 		mr1 = MR1_BITS_7;
6671d65c0b1SAlexander Shiyan 		break;
6681d65c0b1SAlexander Shiyan 	case CS8:
66991f61ce2SAlexander Shiyan 	default:
6701d65c0b1SAlexander Shiyan 		mr1 = MR1_BITS_8;
6711d65c0b1SAlexander Shiyan 		break;
6721d65c0b1SAlexander Shiyan 	}
6731d65c0b1SAlexander Shiyan 
6741d65c0b1SAlexander Shiyan 	/* Parity */
6751d65c0b1SAlexander Shiyan 	if (termios->c_cflag & PARENB) {
6761d65c0b1SAlexander Shiyan 		if (termios->c_cflag & PARODD)
6771d65c0b1SAlexander Shiyan 			mr1 |= MR1_PAR_ODD;
6781d65c0b1SAlexander Shiyan 	} else
6791d65c0b1SAlexander Shiyan 		mr1 |= MR1_PAR_NO;
6801d65c0b1SAlexander Shiyan 
6811d65c0b1SAlexander Shiyan 	/* Stop bits */
6821d65c0b1SAlexander Shiyan 	mr2 = (termios->c_cflag & CSTOPB) ? MR2_STOP2 : MR2_STOP1;
6831d65c0b1SAlexander Shiyan 
6841d65c0b1SAlexander Shiyan 	/* Update desired format */
6851d65c0b1SAlexander Shiyan 	sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_MRPTR1);
6861d65c0b1SAlexander Shiyan 	sccnxp_port_write(port, SCCNXP_MR_REG, mr1);
6871d65c0b1SAlexander Shiyan 	sccnxp_port_write(port, SCCNXP_MR_REG, mr2);
6881d65c0b1SAlexander Shiyan 
6891d65c0b1SAlexander Shiyan 	/* Set read status mask */
6901d65c0b1SAlexander Shiyan 	port->read_status_mask = SR_OVR;
6911d65c0b1SAlexander Shiyan 	if (termios->c_iflag & INPCK)
6921d65c0b1SAlexander Shiyan 		port->read_status_mask |= SR_PE | SR_FE;
693ef8b9ddcSPeter Hurley 	if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
6941d65c0b1SAlexander Shiyan 		port->read_status_mask |= SR_BRK;
6951d65c0b1SAlexander Shiyan 
6961d65c0b1SAlexander Shiyan 	/* Set status ignore mask */
6971d65c0b1SAlexander Shiyan 	port->ignore_status_mask = 0;
6981d65c0b1SAlexander Shiyan 	if (termios->c_iflag & IGNBRK)
6991d65c0b1SAlexander Shiyan 		port->ignore_status_mask |= SR_BRK;
7002ce7c148SAlexander Shiyan 	if (termios->c_iflag & IGNPAR)
7012ce7c148SAlexander Shiyan 		port->ignore_status_mask |= SR_PE;
7021d65c0b1SAlexander Shiyan 	if (!(termios->c_cflag & CREAD))
7031d65c0b1SAlexander Shiyan 		port->ignore_status_mask |= SR_PE | SR_OVR | SR_FE | SR_BRK;
7041d65c0b1SAlexander Shiyan 
7051d65c0b1SAlexander Shiyan 	/* Setup baudrate */
7061d65c0b1SAlexander Shiyan 	baud = uart_get_baud_rate(port, termios, old, 50,
707ea4c39beSAlexander Shiyan 				  (s->chip->flags & SCCNXP_HAVE_MR0) ?
7081d65c0b1SAlexander Shiyan 				  230400 : 38400);
7091685118fSAlexander Shiyan 	baud = sccnxp_set_baud(port, baud);
7101d65c0b1SAlexander Shiyan 
7111d65c0b1SAlexander Shiyan 	/* Update timeout according to new baud rate */
7121d65c0b1SAlexander Shiyan 	uart_update_timeout(port, termios->c_cflag, baud);
7131d65c0b1SAlexander Shiyan 
714ec063899SAlexander Shiyan 	/* Report actual baudrate back to core */
7151685118fSAlexander Shiyan 	if (tty_termios_baud_rate(termios))
7161685118fSAlexander Shiyan 		tty_termios_encode_baud_rate(termios, baud, baud);
7171685118fSAlexander Shiyan 
7181d65c0b1SAlexander Shiyan 	/* Enable RX & TX */
7191d65c0b1SAlexander Shiyan 	sccnxp_port_write(port, SCCNXP_CR_REG, CR_RX_ENABLE | CR_TX_ENABLE);
7201d65c0b1SAlexander Shiyan 
721ec063899SAlexander Shiyan 	spin_unlock_irqrestore(&s->lock, flags);
7221d65c0b1SAlexander Shiyan }
7231d65c0b1SAlexander Shiyan 
sccnxp_startup(struct uart_port * port)7241d65c0b1SAlexander Shiyan static int sccnxp_startup(struct uart_port *port)
7251d65c0b1SAlexander Shiyan {
7261d65c0b1SAlexander Shiyan 	struct sccnxp_port *s = dev_get_drvdata(port->dev);
727ec063899SAlexander Shiyan 	unsigned long flags;
7281d65c0b1SAlexander Shiyan 
729ec063899SAlexander Shiyan 	spin_lock_irqsave(&s->lock, flags);
7301d65c0b1SAlexander Shiyan 
731ea4c39beSAlexander Shiyan 	if (s->chip->flags & SCCNXP_HAVE_IO) {
7321d65c0b1SAlexander Shiyan 		/* Outputs are controlled manually */
7331d65c0b1SAlexander Shiyan 		sccnxp_write(port, SCCNXP_OPCR_REG, 0);
7341d65c0b1SAlexander Shiyan 	}
7351d65c0b1SAlexander Shiyan 
7361d65c0b1SAlexander Shiyan 	/* Reset break condition, status and FIFOs */
7371d65c0b1SAlexander Shiyan 	sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_RX_RESET);
7381d65c0b1SAlexander Shiyan 	sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_TX_RESET);
7391d65c0b1SAlexander Shiyan 	sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_STATUS_RESET);
7401d65c0b1SAlexander Shiyan 	sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_BREAK_RESET);
7411d65c0b1SAlexander Shiyan 
7421d65c0b1SAlexander Shiyan 	/* Enable RX & TX */
7431d65c0b1SAlexander Shiyan 	sccnxp_port_write(port, SCCNXP_CR_REG, CR_RX_ENABLE | CR_TX_ENABLE);
7441d65c0b1SAlexander Shiyan 
7451d65c0b1SAlexander Shiyan 	/* Enable RX interrupt */
7461d65c0b1SAlexander Shiyan 	sccnxp_enable_irq(port, IMR_RXRDY);
7471d65c0b1SAlexander Shiyan 
748ec063899SAlexander Shiyan 	s->opened[port->line] = 1;
749ec063899SAlexander Shiyan 
750ec063899SAlexander Shiyan 	spin_unlock_irqrestore(&s->lock, flags);
7511d65c0b1SAlexander Shiyan 
7521d65c0b1SAlexander Shiyan 	return 0;
7531d65c0b1SAlexander Shiyan }
7541d65c0b1SAlexander Shiyan 
sccnxp_shutdown(struct uart_port * port)7551d65c0b1SAlexander Shiyan static void sccnxp_shutdown(struct uart_port *port)
7561d65c0b1SAlexander Shiyan {
7571d65c0b1SAlexander Shiyan 	struct sccnxp_port *s = dev_get_drvdata(port->dev);
758ec063899SAlexander Shiyan 	unsigned long flags;
7591d65c0b1SAlexander Shiyan 
760ec063899SAlexander Shiyan 	spin_lock_irqsave(&s->lock, flags);
761ec063899SAlexander Shiyan 
762ec063899SAlexander Shiyan 	s->opened[port->line] = 0;
7631d65c0b1SAlexander Shiyan 
7641d65c0b1SAlexander Shiyan 	/* Disable interrupts */
7651d65c0b1SAlexander Shiyan 	sccnxp_disable_irq(port, IMR_TXRDY | IMR_RXRDY);
7661d65c0b1SAlexander Shiyan 
7671d65c0b1SAlexander Shiyan 	/* Disable TX & RX */
7681d65c0b1SAlexander Shiyan 	sccnxp_port_write(port, SCCNXP_CR_REG, CR_RX_DISABLE | CR_TX_DISABLE);
7691d65c0b1SAlexander Shiyan 
7701d65c0b1SAlexander Shiyan 	/* Leave direction to input */
771ea4c39beSAlexander Shiyan 	if (s->chip->flags & SCCNXP_HAVE_IO)
7721d65c0b1SAlexander Shiyan 		sccnxp_set_bit(port, DIR_OP, 0);
7731d65c0b1SAlexander Shiyan 
774ec063899SAlexander Shiyan 	spin_unlock_irqrestore(&s->lock, flags);
7751d65c0b1SAlexander Shiyan }
7761d65c0b1SAlexander Shiyan 
sccnxp_type(struct uart_port * port)7771d65c0b1SAlexander Shiyan static const char *sccnxp_type(struct uart_port *port)
7781d65c0b1SAlexander Shiyan {
7791d65c0b1SAlexander Shiyan 	struct sccnxp_port *s = dev_get_drvdata(port->dev);
7801d65c0b1SAlexander Shiyan 
781ea4c39beSAlexander Shiyan 	return (port->type == PORT_SC26XX) ? s->chip->name : NULL;
7821d65c0b1SAlexander Shiyan }
7831d65c0b1SAlexander Shiyan 
sccnxp_release_port(struct uart_port * port)7841d65c0b1SAlexander Shiyan static void sccnxp_release_port(struct uart_port *port)
7851d65c0b1SAlexander Shiyan {
7861d65c0b1SAlexander Shiyan 	/* Do nothing */
7871d65c0b1SAlexander Shiyan }
7881d65c0b1SAlexander Shiyan 
sccnxp_request_port(struct uart_port * port)7891d65c0b1SAlexander Shiyan static int sccnxp_request_port(struct uart_port *port)
7901d65c0b1SAlexander Shiyan {
7911d65c0b1SAlexander Shiyan 	/* Do nothing */
7921d65c0b1SAlexander Shiyan 	return 0;
7931d65c0b1SAlexander Shiyan }
7941d65c0b1SAlexander Shiyan 
sccnxp_config_port(struct uart_port * port,int flags)7951d65c0b1SAlexander Shiyan static void sccnxp_config_port(struct uart_port *port, int flags)
7961d65c0b1SAlexander Shiyan {
7971d65c0b1SAlexander Shiyan 	if (flags & UART_CONFIG_TYPE)
7981d65c0b1SAlexander Shiyan 		port->type = PORT_SC26XX;
7991d65c0b1SAlexander Shiyan }
8001d65c0b1SAlexander Shiyan 
sccnxp_verify_port(struct uart_port * port,struct serial_struct * s)8011d65c0b1SAlexander Shiyan static int sccnxp_verify_port(struct uart_port *port, struct serial_struct *s)
8021d65c0b1SAlexander Shiyan {
8031d65c0b1SAlexander Shiyan 	if ((s->type == PORT_UNKNOWN) || (s->type == PORT_SC26XX))
8041d65c0b1SAlexander Shiyan 		return 0;
8051d65c0b1SAlexander Shiyan 	if (s->irq == port->irq)
8061d65c0b1SAlexander Shiyan 		return 0;
8071d65c0b1SAlexander Shiyan 
8081d65c0b1SAlexander Shiyan 	return -EINVAL;
8091d65c0b1SAlexander Shiyan }
8101d65c0b1SAlexander Shiyan 
8111d65c0b1SAlexander Shiyan static const struct uart_ops sccnxp_ops = {
8121d65c0b1SAlexander Shiyan 	.tx_empty	= sccnxp_tx_empty,
8131d65c0b1SAlexander Shiyan 	.set_mctrl	= sccnxp_set_mctrl,
8141d65c0b1SAlexander Shiyan 	.get_mctrl	= sccnxp_get_mctrl,
8151d65c0b1SAlexander Shiyan 	.stop_tx	= sccnxp_stop_tx,
8161d65c0b1SAlexander Shiyan 	.start_tx	= sccnxp_start_tx,
8171d65c0b1SAlexander Shiyan 	.stop_rx	= sccnxp_stop_rx,
8181d65c0b1SAlexander Shiyan 	.break_ctl	= sccnxp_break_ctl,
8191d65c0b1SAlexander Shiyan 	.startup	= sccnxp_startup,
8201d65c0b1SAlexander Shiyan 	.shutdown	= sccnxp_shutdown,
8211d65c0b1SAlexander Shiyan 	.set_termios	= sccnxp_set_termios,
8221d65c0b1SAlexander Shiyan 	.type		= sccnxp_type,
8231d65c0b1SAlexander Shiyan 	.release_port	= sccnxp_release_port,
8241d65c0b1SAlexander Shiyan 	.request_port	= sccnxp_request_port,
8251d65c0b1SAlexander Shiyan 	.config_port	= sccnxp_config_port,
8261d65c0b1SAlexander Shiyan 	.verify_port	= sccnxp_verify_port,
8271d65c0b1SAlexander Shiyan };
8281d65c0b1SAlexander Shiyan 
8291d65c0b1SAlexander Shiyan #ifdef CONFIG_SERIAL_SCCNXP_CONSOLE
sccnxp_console_putchar(struct uart_port * port,unsigned char c)8303f8bab17SJiri Slaby static void sccnxp_console_putchar(struct uart_port *port, unsigned char c)
8311d65c0b1SAlexander Shiyan {
8321d65c0b1SAlexander Shiyan 	int tryes = 100000;
8331d65c0b1SAlexander Shiyan 
8341d65c0b1SAlexander Shiyan 	while (tryes--) {
8351d65c0b1SAlexander Shiyan 		if (sccnxp_port_read(port, SCCNXP_SR_REG) & SR_TXRDY) {
8361d65c0b1SAlexander Shiyan 			sccnxp_port_write(port, SCCNXP_THR_REG, c);
8371d65c0b1SAlexander Shiyan 			break;
8381d65c0b1SAlexander Shiyan 		}
8391d65c0b1SAlexander Shiyan 		barrier();
8401d65c0b1SAlexander Shiyan 	}
8411d65c0b1SAlexander Shiyan }
8421d65c0b1SAlexander Shiyan 
sccnxp_console_write(struct console * co,const char * c,unsigned n)8431d65c0b1SAlexander Shiyan static void sccnxp_console_write(struct console *co, const char *c, unsigned n)
8441d65c0b1SAlexander Shiyan {
8451d65c0b1SAlexander Shiyan 	struct sccnxp_port *s = (struct sccnxp_port *)co->data;
8461d65c0b1SAlexander Shiyan 	struct uart_port *port = &s->port[co->index];
847ec063899SAlexander Shiyan 	unsigned long flags;
8481d65c0b1SAlexander Shiyan 
849ec063899SAlexander Shiyan 	spin_lock_irqsave(&s->lock, flags);
8501d65c0b1SAlexander Shiyan 	uart_console_write(port, c, n, sccnxp_console_putchar);
851ec063899SAlexander Shiyan 	spin_unlock_irqrestore(&s->lock, flags);
8521d65c0b1SAlexander Shiyan }
8531d65c0b1SAlexander Shiyan 
sccnxp_console_setup(struct console * co,char * options)8541d65c0b1SAlexander Shiyan static int sccnxp_console_setup(struct console *co, char *options)
8551d65c0b1SAlexander Shiyan {
8561d65c0b1SAlexander Shiyan 	struct sccnxp_port *s = (struct sccnxp_port *)co->data;
8571d65c0b1SAlexander Shiyan 	struct uart_port *port = &s->port[(co->index > 0) ? co->index : 0];
8581d65c0b1SAlexander Shiyan 	int baud = 9600, bits = 8, parity = 'n', flow = 'n';
8591d65c0b1SAlexander Shiyan 
8601d65c0b1SAlexander Shiyan 	if (options)
8611d65c0b1SAlexander Shiyan 		uart_parse_options(options, &baud, &parity, &bits, &flow);
8621d65c0b1SAlexander Shiyan 
8631d65c0b1SAlexander Shiyan 	return uart_set_options(port, co, baud, parity, bits, flow);
8641d65c0b1SAlexander Shiyan }
8651d65c0b1SAlexander Shiyan #endif
8661d65c0b1SAlexander Shiyan 
867ea4c39beSAlexander Shiyan static const struct platform_device_id sccnxp_id_table[] = {
868ea4c39beSAlexander Shiyan 	{ .name = "sc2681",	.driver_data = (kernel_ulong_t)&sc2681, },
869ea4c39beSAlexander Shiyan 	{ .name = "sc2691",	.driver_data = (kernel_ulong_t)&sc2691, },
870ea4c39beSAlexander Shiyan 	{ .name = "sc2692",	.driver_data = (kernel_ulong_t)&sc2692, },
871ea4c39beSAlexander Shiyan 	{ .name = "sc2891",	.driver_data = (kernel_ulong_t)&sc2891, },
872ea4c39beSAlexander Shiyan 	{ .name = "sc2892",	.driver_data = (kernel_ulong_t)&sc2892, },
873ea4c39beSAlexander Shiyan 	{ .name = "sc28202",	.driver_data = (kernel_ulong_t)&sc28202, },
874ea4c39beSAlexander Shiyan 	{ .name = "sc68681",	.driver_data = (kernel_ulong_t)&sc68681, },
875ea4c39beSAlexander Shiyan 	{ .name = "sc68692",	.driver_data = (kernel_ulong_t)&sc68692, },
876ea4c39beSAlexander Shiyan 	{ }
877ea4c39beSAlexander Shiyan };
878ea4c39beSAlexander Shiyan MODULE_DEVICE_TABLE(platform, sccnxp_id_table);
879ea4c39beSAlexander Shiyan 
sccnxp_probe(struct platform_device * pdev)8809671f099SBill Pemberton static int sccnxp_probe(struct platform_device *pdev)
8811d65c0b1SAlexander Shiyan {
8821d65c0b1SAlexander Shiyan 	struct sccnxp_pdata *pdata = dev_get_platdata(&pdev->dev);
883*0851efafSYangtao Li 	struct resource *res;
884ea4c39beSAlexander Shiyan 	int i, ret, uartclk;
8851d65c0b1SAlexander Shiyan 	struct sccnxp_port *s;
8861d65c0b1SAlexander Shiyan 	void __iomem *membase;
88790efa75fSAlexander Shiyan 	struct clk *clk;
8881d65c0b1SAlexander Shiyan 
889*0851efafSYangtao Li 	membase = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
890e087ab74SAlexander Shiyan 	if (IS_ERR(membase))
891e087ab74SAlexander Shiyan 		return PTR_ERR(membase);
8921d65c0b1SAlexander Shiyan 
8931d65c0b1SAlexander Shiyan 	s = devm_kzalloc(&pdev->dev, sizeof(struct sccnxp_port), GFP_KERNEL);
8941d65c0b1SAlexander Shiyan 	if (!s) {
8951d65c0b1SAlexander Shiyan 		dev_err(&pdev->dev, "Error allocating port structure\n");
8961d65c0b1SAlexander Shiyan 		return -ENOMEM;
8971d65c0b1SAlexander Shiyan 	}
8981d65c0b1SAlexander Shiyan 	platform_set_drvdata(pdev, s);
8991d65c0b1SAlexander Shiyan 
900ec063899SAlexander Shiyan 	spin_lock_init(&s->lock);
9011d65c0b1SAlexander Shiyan 
902ea4c39beSAlexander Shiyan 	s->chip = (struct sccnxp_chip *)pdev->id_entry->driver_data;
9031d65c0b1SAlexander Shiyan 
904e087ab74SAlexander Shiyan 	s->regulator = devm_regulator_get(&pdev->dev, "vcc");
905e087ab74SAlexander Shiyan 	if (!IS_ERR(s->regulator)) {
906e087ab74SAlexander Shiyan 		ret = regulator_enable(s->regulator);
907e087ab74SAlexander Shiyan 		if (ret) {
908e087ab74SAlexander Shiyan 			dev_err(&pdev->dev,
909e087ab74SAlexander Shiyan 				"Failed to enable regulator: %i\n", ret);
910e087ab74SAlexander Shiyan 			return ret;
911e087ab74SAlexander Shiyan 		}
912e087ab74SAlexander Shiyan 	} else if (PTR_ERR(s->regulator) == -EPROBE_DEFER)
913e087ab74SAlexander Shiyan 		return -EPROBE_DEFER;
914e087ab74SAlexander Shiyan 
91503e30f06SChristophe JAILLET 	clk = devm_clk_get_enabled(&pdev->dev, NULL);
91690efa75fSAlexander Shiyan 	if (IS_ERR(clk)) {
917e279e6d9SThomas Bogendoerfer 		ret = PTR_ERR(clk);
918e279e6d9SThomas Bogendoerfer 		if (ret == -EPROBE_DEFER)
91990efa75fSAlexander Shiyan 			goto err_out;
920e279e6d9SThomas Bogendoerfer 		uartclk = 0;
921e279e6d9SThomas Bogendoerfer 	} else {
922e279e6d9SThomas Bogendoerfer 		uartclk = clk_get_rate(clk);
92390efa75fSAlexander Shiyan 	}
924e279e6d9SThomas Bogendoerfer 
925e279e6d9SThomas Bogendoerfer 	if (!uartclk) {
92690efa75fSAlexander Shiyan 		dev_notice(&pdev->dev, "Using default clock frequency\n");
927ea4c39beSAlexander Shiyan 		uartclk = s->chip->freq_std;
928e279e6d9SThomas Bogendoerfer 	}
92990efa75fSAlexander Shiyan 
93090efa75fSAlexander Shiyan 	/* Check input frequency */
931ea4c39beSAlexander Shiyan 	if ((uartclk < s->chip->freq_min) || (uartclk > s->chip->freq_max)) {
93290efa75fSAlexander Shiyan 		dev_err(&pdev->dev, "Frequency out of bounds\n");
93390efa75fSAlexander Shiyan 		ret = -EINVAL;
93490efa75fSAlexander Shiyan 		goto err_out;
93590efa75fSAlexander Shiyan 	}
93690efa75fSAlexander Shiyan 
937461a8ecbSGreg Kroah-Hartman 	if (pdata)
938461a8ecbSGreg Kroah-Hartman 		memcpy(&s->pdata, pdata, sizeof(struct sccnxp_pdata));
939461a8ecbSGreg Kroah-Hartman 
940b786337dSAlexander Shiyan 	if (s->pdata.poll_time_us) {
941ec063899SAlexander Shiyan 		dev_info(&pdev->dev, "Using poll mode, resolution %u usecs\n",
942b786337dSAlexander Shiyan 			 s->pdata.poll_time_us);
943ec063899SAlexander Shiyan 		s->poll = 1;
944461a8ecbSGreg Kroah-Hartman 	}
945461a8ecbSGreg Kroah-Hartman 
946461a8ecbSGreg Kroah-Hartman 	if (!s->poll) {
9471d65c0b1SAlexander Shiyan 		s->irq = platform_get_irq(pdev, 0);
948ec063899SAlexander Shiyan 		if (s->irq < 0) {
9491d65c0b1SAlexander Shiyan 			ret = -ENXIO;
9501d65c0b1SAlexander Shiyan 			goto err_out;
9511d65c0b1SAlexander Shiyan 		}
952ec063899SAlexander Shiyan 	}
9531d65c0b1SAlexander Shiyan 
9541d65c0b1SAlexander Shiyan 	s->uart.owner		= THIS_MODULE;
9551d65c0b1SAlexander Shiyan 	s->uart.dev_name	= "ttySC";
9561d65c0b1SAlexander Shiyan 	s->uart.major		= SCCNXP_MAJOR;
9571d65c0b1SAlexander Shiyan 	s->uart.minor		= SCCNXP_MINOR;
958ea4c39beSAlexander Shiyan 	s->uart.nr		= s->chip->nr;
9591d65c0b1SAlexander Shiyan #ifdef CONFIG_SERIAL_SCCNXP_CONSOLE
9601d65c0b1SAlexander Shiyan 	s->uart.cons		= &s->console;
9611d65c0b1SAlexander Shiyan 	s->uart.cons->device	= uart_console_device;
9621d65c0b1SAlexander Shiyan 	s->uart.cons->write	= sccnxp_console_write;
9631d65c0b1SAlexander Shiyan 	s->uart.cons->setup	= sccnxp_console_setup;
9641d65c0b1SAlexander Shiyan 	s->uart.cons->flags	= CON_PRINTBUFFER;
9651d65c0b1SAlexander Shiyan 	s->uart.cons->index	= -1;
9661d65c0b1SAlexander Shiyan 	s->uart.cons->data	= s;
9671d65c0b1SAlexander Shiyan 	strcpy(s->uart.cons->name, "ttySC");
9681d65c0b1SAlexander Shiyan #endif
9691d65c0b1SAlexander Shiyan 	ret = uart_register_driver(&s->uart);
9701d65c0b1SAlexander Shiyan 	if (ret) {
9711d65c0b1SAlexander Shiyan 		dev_err(&pdev->dev, "Registering UART driver failed\n");
9721d65c0b1SAlexander Shiyan 		goto err_out;
9731d65c0b1SAlexander Shiyan 	}
9741d65c0b1SAlexander Shiyan 
9751d65c0b1SAlexander Shiyan 	for (i = 0; i < s->uart.nr; i++) {
9761d65c0b1SAlexander Shiyan 		s->port[i].line		= i;
9771d65c0b1SAlexander Shiyan 		s->port[i].dev		= &pdev->dev;
9781d65c0b1SAlexander Shiyan 		s->port[i].irq		= s->irq;
9791d65c0b1SAlexander Shiyan 		s->port[i].type		= PORT_SC26XX;
980ea4c39beSAlexander Shiyan 		s->port[i].fifosize	= s->chip->fifosize;
9811d65c0b1SAlexander Shiyan 		s->port[i].flags	= UPF_SKIP_TEST | UPF_FIXED_TYPE;
9821d65c0b1SAlexander Shiyan 		s->port[i].iotype	= UPIO_MEM;
9831d65c0b1SAlexander Shiyan 		s->port[i].mapbase	= res->start;
9841d65c0b1SAlexander Shiyan 		s->port[i].membase	= membase;
9851d65c0b1SAlexander Shiyan 		s->port[i].regshift	= s->pdata.reg_shift;
98690efa75fSAlexander Shiyan 		s->port[i].uartclk	= uartclk;
9871d65c0b1SAlexander Shiyan 		s->port[i].ops		= &sccnxp_ops;
988212d9371SDmitry Safonov 		s->port[i].has_sysrq = IS_ENABLED(CONFIG_SERIAL_SCCNXP_CONSOLE);
9891d65c0b1SAlexander Shiyan 		uart_add_one_port(&s->uart, &s->port[i]);
9901d65c0b1SAlexander Shiyan 		/* Set direction to input */
991ea4c39beSAlexander Shiyan 		if (s->chip->flags & SCCNXP_HAVE_IO)
9921d65c0b1SAlexander Shiyan 			sccnxp_set_bit(&s->port[i], DIR_OP, 0);
9931d65c0b1SAlexander Shiyan 	}
9941d65c0b1SAlexander Shiyan 
9951d65c0b1SAlexander Shiyan 	/* Disable interrupts */
9961d65c0b1SAlexander Shiyan 	s->imr = 0;
9971d65c0b1SAlexander Shiyan 	sccnxp_write(&s->port[0], SCCNXP_IMR_REG, 0);
9981d65c0b1SAlexander Shiyan 
999ec063899SAlexander Shiyan 	if (!s->poll) {
1000ec063899SAlexander Shiyan 		ret = devm_request_threaded_irq(&pdev->dev, s->irq, NULL,
1001ec063899SAlexander Shiyan 						sccnxp_ist,
1002ec063899SAlexander Shiyan 						IRQF_TRIGGER_FALLING |
1003ec063899SAlexander Shiyan 						IRQF_ONESHOT,
10041d65c0b1SAlexander Shiyan 						dev_name(&pdev->dev), s);
10051d65c0b1SAlexander Shiyan 		if (!ret)
10061d65c0b1SAlexander Shiyan 			return 0;
10071d65c0b1SAlexander Shiyan 
10081d65c0b1SAlexander Shiyan 		dev_err(&pdev->dev, "Unable to reguest IRQ %i\n", s->irq);
1009ec063899SAlexander Shiyan 	} else {
1010fc3b00d7SKees Cook 		timer_setup(&s->timer, sccnxp_timer, 0);
1011ec063899SAlexander Shiyan 		mod_timer(&s->timer, jiffies +
1012ec063899SAlexander Shiyan 			  usecs_to_jiffies(s->pdata.poll_time_us));
1013ec063899SAlexander Shiyan 		return 0;
1014ec063899SAlexander Shiyan 	}
10151d65c0b1SAlexander Shiyan 
10167a95b815SWei Yongjun 	uart_unregister_driver(&s->uart);
10171d65c0b1SAlexander Shiyan err_out:
1018e087ab74SAlexander Shiyan 	if (!IS_ERR(s->regulator))
1019c9126143SAlexey Khoroshilov 		regulator_disable(s->regulator);
1020e087ab74SAlexander Shiyan 
10211d65c0b1SAlexander Shiyan 	return ret;
10221d65c0b1SAlexander Shiyan }
10231d65c0b1SAlexander Shiyan 
sccnxp_remove(struct platform_device * pdev)1024ae8d8a14SBill Pemberton static int sccnxp_remove(struct platform_device *pdev)
10251d65c0b1SAlexander Shiyan {
10261d65c0b1SAlexander Shiyan 	int i;
10271d65c0b1SAlexander Shiyan 	struct sccnxp_port *s = platform_get_drvdata(pdev);
10281d65c0b1SAlexander Shiyan 
1029ec063899SAlexander Shiyan 	if (!s->poll)
10301d65c0b1SAlexander Shiyan 		devm_free_irq(&pdev->dev, s->irq, s);
1031ec063899SAlexander Shiyan 	else
1032ec063899SAlexander Shiyan 		del_timer_sync(&s->timer);
10331d65c0b1SAlexander Shiyan 
10341d65c0b1SAlexander Shiyan 	for (i = 0; i < s->uart.nr; i++)
10351d65c0b1SAlexander Shiyan 		uart_remove_one_port(&s->uart, &s->port[i]);
10361d65c0b1SAlexander Shiyan 
10371d65c0b1SAlexander Shiyan 	uart_unregister_driver(&s->uart);
10381d65c0b1SAlexander Shiyan 
103931815c08SAlexander Shiyan 	if (!IS_ERR(s->regulator))
104031815c08SAlexander Shiyan 		return regulator_disable(s->regulator);
10411d65c0b1SAlexander Shiyan 
10421d65c0b1SAlexander Shiyan 	return 0;
10431d65c0b1SAlexander Shiyan }
10441d65c0b1SAlexander Shiyan 
10451d65c0b1SAlexander Shiyan static struct platform_driver sccnxp_uart_driver = {
10461d65c0b1SAlexander Shiyan 	.driver = {
10471d65c0b1SAlexander Shiyan 		.name	= SCCNXP_NAME,
10481d65c0b1SAlexander Shiyan 	},
10491d65c0b1SAlexander Shiyan 	.probe		= sccnxp_probe,
10502d47b716SBill Pemberton 	.remove		= sccnxp_remove,
10511d65c0b1SAlexander Shiyan 	.id_table	= sccnxp_id_table,
10521d65c0b1SAlexander Shiyan };
10531d65c0b1SAlexander Shiyan module_platform_driver(sccnxp_uart_driver);
10541d65c0b1SAlexander Shiyan 
10551d65c0b1SAlexander Shiyan MODULE_LICENSE("GPL v2");
10561d65c0b1SAlexander Shiyan MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
10571d65c0b1SAlexander Shiyan MODULE_DESCRIPTION("SCCNXP serial driver");
1058