xref: /openbmc/linux/drivers/tty/serial/lantiq.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1e3b3d0f5SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
22f0fc415SJohn Crispin /*
32f0fc415SJohn Crispin  *  Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
42f0fc415SJohn Crispin  *
52f0fc415SJohn Crispin  * Copyright (C) 2004 Infineon IFAP DC COM CPE
62f0fc415SJohn Crispin  * Copyright (C) 2007 Felix Fietkau <nbd@openwrt.org>
7552df698SJohn Crispin  * Copyright (C) 2007 John Crispin <john@phrozen.org>
82f0fc415SJohn Crispin  * Copyright (C) 2010 Thomas Langer, <thomas.langer@lantiq.com>
92f0fc415SJohn Crispin  */
102f0fc415SJohn Crispin 
11ed9bf4aeSJiri Slaby #include <linux/bitfield.h>
12a77bbe5eSSongjun Wu #include <linux/clk.h>
132f0fc415SJohn Crispin #include <linux/console.h>
142f0fc415SJohn Crispin #include <linux/device.h>
15a77bbe5eSSongjun Wu #include <linux/init.h>
16a77bbe5eSSongjun Wu #include <linux/io.h>
17a77bbe5eSSongjun Wu #include <linux/ioport.h>
183c8c2a9eSSongjun Wu #include <linux/lantiq.h>
19ad406341SRahul Tanwar #include <linux/module.h>
20*29e5c442SRob Herring #include <linux/of.h>
21*29e5c442SRob Herring #include <linux/platform_device.h>
22a77bbe5eSSongjun Wu #include <linux/serial.h>
23a77bbe5eSSongjun Wu #include <linux/serial_core.h>
24a77bbe5eSSongjun Wu #include <linux/slab.h>
25a77bbe5eSSongjun Wu #include <linux/sysrq.h>
26a77bbe5eSSongjun Wu #include <linux/tty.h>
27a77bbe5eSSongjun Wu #include <linux/tty_flip.h>
282f0fc415SJohn Crispin 
292f0fc415SJohn Crispin #define PORT_LTQ_ASC		111
302f0fc415SJohn Crispin #define MAXPORTS		2
312f0fc415SJohn Crispin #define UART_DUMMY_UER_RX	1
32ceff2676SJohn Crispin #define DRVNAME			"lantiq,asc"
332f0fc415SJohn Crispin #ifdef __BIG_ENDIAN
342f0fc415SJohn Crispin #define LTQ_ASC_TBUF		(0x0020 + 3)
352f0fc415SJohn Crispin #define LTQ_ASC_RBUF		(0x0024 + 3)
362f0fc415SJohn Crispin #else
372f0fc415SJohn Crispin #define LTQ_ASC_TBUF		0x0020
382f0fc415SJohn Crispin #define LTQ_ASC_RBUF		0x0024
392f0fc415SJohn Crispin #endif
402f0fc415SJohn Crispin #define LTQ_ASC_FSTAT		0x0048
412f0fc415SJohn Crispin #define LTQ_ASC_WHBSTATE	0x0018
422f0fc415SJohn Crispin #define LTQ_ASC_STATE		0x0014
432f0fc415SJohn Crispin #define LTQ_ASC_IRNCR		0x00F8
442f0fc415SJohn Crispin #define LTQ_ASC_CLC		0x0000
452f0fc415SJohn Crispin #define LTQ_ASC_ID		0x0008
462f0fc415SJohn Crispin #define LTQ_ASC_PISEL		0x0004
472f0fc415SJohn Crispin #define LTQ_ASC_TXFCON		0x0044
482f0fc415SJohn Crispin #define LTQ_ASC_RXFCON		0x0040
492f0fc415SJohn Crispin #define LTQ_ASC_CON		0x0010
502f0fc415SJohn Crispin #define LTQ_ASC_BG		0x0050
512f0fc415SJohn Crispin #define LTQ_ASC_IRNREN		0x00F4
522f0fc415SJohn Crispin 
532f0fc415SJohn Crispin #define ASC_IRNREN_TX		0x1
542f0fc415SJohn Crispin #define ASC_IRNREN_RX		0x2
552f0fc415SJohn Crispin #define ASC_IRNREN_ERR		0x4
562f0fc415SJohn Crispin #define ASC_IRNREN_TX_BUF	0x8
572f0fc415SJohn Crispin #define ASC_IRNCR_TIR		0x1
582f0fc415SJohn Crispin #define ASC_IRNCR_RIR		0x2
592f0fc415SJohn Crispin #define ASC_IRNCR_EIR		0x4
60b832776bSRahul Tanwar #define ASC_IRNCR_MASK		GENMASK(2, 0)
612f0fc415SJohn Crispin 
622f0fc415SJohn Crispin #define ASCOPT_CSIZE		0x3
632f0fc415SJohn Crispin #define TXFIFO_FL		1
642f0fc415SJohn Crispin #define RXFIFO_FL		1
652f0fc415SJohn Crispin #define ASCCLC_DISS		0x2
662f0fc415SJohn Crispin #define ASCCLC_RMCMASK		0x0000FF00
672f0fc415SJohn Crispin #define ASCCLC_RMCOFFSET	8
682f0fc415SJohn Crispin #define ASCCON_M_8ASYNC		0x0
692f0fc415SJohn Crispin #define ASCCON_M_7ASYNC		0x2
702f0fc415SJohn Crispin #define ASCCON_ODD		0x00000020
712f0fc415SJohn Crispin #define ASCCON_STP		0x00000080
722f0fc415SJohn Crispin #define ASCCON_BRS		0x00000100
732f0fc415SJohn Crispin #define ASCCON_FDE		0x00000200
742f0fc415SJohn Crispin #define ASCCON_R		0x00008000
752f0fc415SJohn Crispin #define ASCCON_FEN		0x00020000
762f0fc415SJohn Crispin #define ASCCON_ROEN		0x00080000
772f0fc415SJohn Crispin #define ASCCON_TOEN		0x00100000
782f0fc415SJohn Crispin #define ASCSTATE_PE		0x00010000
792f0fc415SJohn Crispin #define ASCSTATE_FE		0x00020000
802f0fc415SJohn Crispin #define ASCSTATE_ROE		0x00080000
812f0fc415SJohn Crispin #define ASCSTATE_ANY		(ASCSTATE_ROE|ASCSTATE_PE|ASCSTATE_FE)
822f0fc415SJohn Crispin #define ASCWHBSTATE_CLRREN	0x00000001
832f0fc415SJohn Crispin #define ASCWHBSTATE_SETREN	0x00000002
842f0fc415SJohn Crispin #define ASCWHBSTATE_CLRPE	0x00000004
852f0fc415SJohn Crispin #define ASCWHBSTATE_CLRFE	0x00000008
862f0fc415SJohn Crispin #define ASCWHBSTATE_CLRROE	0x00000020
872f0fc415SJohn Crispin #define ASCTXFCON_TXFEN		0x0001
882f0fc415SJohn Crispin #define ASCTXFCON_TXFFLU	0x0002
892f0fc415SJohn Crispin #define ASCTXFCON_TXFITLMASK	0x3F00
902f0fc415SJohn Crispin #define ASCTXFCON_TXFITLOFF	8
912f0fc415SJohn Crispin #define ASCRXFCON_RXFEN		0x0001
922f0fc415SJohn Crispin #define ASCRXFCON_RXFFLU	0x0002
932f0fc415SJohn Crispin #define ASCRXFCON_RXFITLMASK	0x3F00
942f0fc415SJohn Crispin #define ASCRXFCON_RXFITLOFF	8
952f0fc415SJohn Crispin #define ASCFSTAT_RXFFLMASK	0x003F
962f0fc415SJohn Crispin #define ASCFSTAT_TXFFLMASK	0x3F00
972f0fc415SJohn Crispin #define ASCFSTAT_TXFREEMASK	0x3F000000
982f0fc415SJohn Crispin 
992f0fc415SJohn Crispin static struct ltq_uart_port *lqasc_port[MAXPORTS];
1002f0fc415SJohn Crispin static struct uart_driver lqasc_reg;
1012f0fc415SJohn Crispin 
10214208b38SRahul Tanwar struct ltq_soc_data {
10314208b38SRahul Tanwar 	int	(*fetch_irq)(struct device *dev, struct ltq_uart_port *ltq_port);
10414208b38SRahul Tanwar 	int	(*request_irq)(struct uart_port *port);
10514208b38SRahul Tanwar 	void	(*free_irq)(struct uart_port *port);
10614208b38SRahul Tanwar };
10714208b38SRahul Tanwar 
1082f0fc415SJohn Crispin struct ltq_uart_port {
1092f0fc415SJohn Crispin 	struct uart_port	port;
110ceff2676SJohn Crispin 	/* clock used to derive divider */
1112e81c1f3SSongjun Wu 	struct clk		*freqclk;
112ceff2676SJohn Crispin 	/* clock gating of the ASC core */
1132f0fc415SJohn Crispin 	struct clk		*clk;
1142f0fc415SJohn Crispin 	unsigned int		tx_irq;
1152f0fc415SJohn Crispin 	unsigned int		rx_irq;
1162f0fc415SJohn Crispin 	unsigned int		err_irq;
117b832776bSRahul Tanwar 	unsigned int		common_irq;
1184b967e63SRahul Tanwar 	spinlock_t		lock; /* exclusive access for multi core */
11914208b38SRahul Tanwar 
12014208b38SRahul Tanwar 	const struct ltq_soc_data	*soc;
1212f0fc415SJohn Crispin };
1222f0fc415SJohn Crispin 
asc_update_bits(u32 clear,u32 set,void __iomem * reg)123fccf231aSSongjun Wu static inline void asc_update_bits(u32 clear, u32 set, void __iomem *reg)
124fccf231aSSongjun Wu {
125d3a28a53SHauke Mehrtens 	u32 tmp = __raw_readl(reg);
126fccf231aSSongjun Wu 
127d3a28a53SHauke Mehrtens 	__raw_writel((tmp & ~clear) | set, reg);
128fccf231aSSongjun Wu }
129fccf231aSSongjun Wu 
1302f0fc415SJohn Crispin static inline struct
to_ltq_uart_port(struct uart_port * port)1312f0fc415SJohn Crispin ltq_uart_port *to_ltq_uart_port(struct uart_port *port)
1322f0fc415SJohn Crispin {
1332f0fc415SJohn Crispin 	return container_of(port, struct ltq_uart_port, port);
1342f0fc415SJohn Crispin }
1352f0fc415SJohn Crispin 
1362f0fc415SJohn Crispin static void
lqasc_stop_tx(struct uart_port * port)1372f0fc415SJohn Crispin lqasc_stop_tx(struct uart_port *port)
1382f0fc415SJohn Crispin {
1392f0fc415SJohn Crispin 	return;
1402f0fc415SJohn Crispin }
1412f0fc415SJohn Crispin 
lqasc_tx_ready(struct uart_port * port)1420fbf36bbSJiri Slaby static bool lqasc_tx_ready(struct uart_port *port)
1430fbf36bbSJiri Slaby {
1440fbf36bbSJiri Slaby 	u32 fstat = __raw_readl(port->membase + LTQ_ASC_FSTAT);
1450fbf36bbSJiri Slaby 
146ed9bf4aeSJiri Slaby 	return FIELD_GET(ASCFSTAT_TXFREEMASK, fstat);
1470fbf36bbSJiri Slaby }
1480fbf36bbSJiri Slaby 
1492f0fc415SJohn Crispin static void
lqasc_start_tx(struct uart_port * port)1502f0fc415SJohn Crispin lqasc_start_tx(struct uart_port *port)
1512f0fc415SJohn Crispin {
1522f0fc415SJohn Crispin 	unsigned long flags;
1534b967e63SRahul Tanwar 	struct ltq_uart_port *ltq_port = to_ltq_uart_port(port);
1542d141e68SJiri Slaby (SUSE) 	u8 ch;
1554b967e63SRahul Tanwar 
1564b967e63SRahul Tanwar 	spin_lock_irqsave(&ltq_port->lock, flags);
1572d141e68SJiri Slaby (SUSE) 	uart_port_tx(port, ch,
1582d141e68SJiri Slaby (SUSE) 		lqasc_tx_ready(port),
1592d141e68SJiri Slaby (SUSE) 		writeb(ch, port->membase + LTQ_ASC_TBUF));
1604b967e63SRahul Tanwar 	spin_unlock_irqrestore(&ltq_port->lock, flags);
1612f0fc415SJohn Crispin 	return;
1622f0fc415SJohn Crispin }
1632f0fc415SJohn Crispin 
1642f0fc415SJohn Crispin static void
lqasc_stop_rx(struct uart_port * port)1652f0fc415SJohn Crispin lqasc_stop_rx(struct uart_port *port)
1662f0fc415SJohn Crispin {
167d3a28a53SHauke Mehrtens 	__raw_writel(ASCWHBSTATE_CLRREN, port->membase + LTQ_ASC_WHBSTATE);
1682f0fc415SJohn Crispin }
1692f0fc415SJohn Crispin 
1702f0fc415SJohn Crispin static int
lqasc_rx_chars(struct uart_port * port)1712f0fc415SJohn Crispin lqasc_rx_chars(struct uart_port *port)
1722f0fc415SJohn Crispin {
17392a19f9cSJiri Slaby 	struct tty_port *tport = &port->state->port;
1742f0fc415SJohn Crispin 	unsigned int ch = 0, rsr = 0, fifocnt;
1752f0fc415SJohn Crispin 
176d3a28a53SHauke Mehrtens 	fifocnt = __raw_readl(port->membase + LTQ_ASC_FSTAT) &
177d3a28a53SHauke Mehrtens 		  ASCFSTAT_RXFFLMASK;
1782f0fc415SJohn Crispin 	while (fifocnt--) {
1792f0fc415SJohn Crispin 		u8 flag = TTY_NORMAL;
18089b8bd20SSongjun Wu 		ch = readb(port->membase + LTQ_ASC_RBUF);
181d3a28a53SHauke Mehrtens 		rsr = (__raw_readl(port->membase + LTQ_ASC_STATE)
1822f0fc415SJohn Crispin 			& ASCSTATE_ANY) | UART_DUMMY_UER_RX;
1832e124b4aSJiri Slaby 		tty_flip_buffer_push(tport);
1842f0fc415SJohn Crispin 		port->icount.rx++;
1852f0fc415SJohn Crispin 
1862f0fc415SJohn Crispin 		/*
1872f0fc415SJohn Crispin 		 * Note that the error handling code is
1882f0fc415SJohn Crispin 		 * out of the main execution path
1892f0fc415SJohn Crispin 		 */
1902f0fc415SJohn Crispin 		if (rsr & ASCSTATE_ANY) {
1912f0fc415SJohn Crispin 			if (rsr & ASCSTATE_PE) {
1922f0fc415SJohn Crispin 				port->icount.parity++;
193fccf231aSSongjun Wu 				asc_update_bits(0, ASCWHBSTATE_CLRPE,
1942f0fc415SJohn Crispin 					port->membase + LTQ_ASC_WHBSTATE);
1952f0fc415SJohn Crispin 			} else if (rsr & ASCSTATE_FE) {
1962f0fc415SJohn Crispin 				port->icount.frame++;
197fccf231aSSongjun Wu 				asc_update_bits(0, ASCWHBSTATE_CLRFE,
1982f0fc415SJohn Crispin 					port->membase + LTQ_ASC_WHBSTATE);
1992f0fc415SJohn Crispin 			}
2002f0fc415SJohn Crispin 			if (rsr & ASCSTATE_ROE) {
2012f0fc415SJohn Crispin 				port->icount.overrun++;
202fccf231aSSongjun Wu 				asc_update_bits(0, ASCWHBSTATE_CLRROE,
2032f0fc415SJohn Crispin 					port->membase + LTQ_ASC_WHBSTATE);
2042f0fc415SJohn Crispin 			}
2052f0fc415SJohn Crispin 
2062f0fc415SJohn Crispin 			rsr &= port->read_status_mask;
2072f0fc415SJohn Crispin 
2082f0fc415SJohn Crispin 			if (rsr & ASCSTATE_PE)
2092f0fc415SJohn Crispin 				flag = TTY_PARITY;
2102f0fc415SJohn Crispin 			else if (rsr & ASCSTATE_FE)
2112f0fc415SJohn Crispin 				flag = TTY_FRAME;
2122f0fc415SJohn Crispin 		}
2132f0fc415SJohn Crispin 
2142f0fc415SJohn Crispin 		if ((rsr & port->ignore_status_mask) == 0)
21592a19f9cSJiri Slaby 			tty_insert_flip_char(tport, ch, flag);
2162f0fc415SJohn Crispin 
2172f0fc415SJohn Crispin 		if (rsr & ASCSTATE_ROE)
2182f0fc415SJohn Crispin 			/*
2192f0fc415SJohn Crispin 			 * Overrun is special, since it's reported
2202f0fc415SJohn Crispin 			 * immediately, and doesn't affect the current
2212f0fc415SJohn Crispin 			 * character
2222f0fc415SJohn Crispin 			 */
22392a19f9cSJiri Slaby 			tty_insert_flip_char(tport, 0, TTY_OVERRUN);
2242f0fc415SJohn Crispin 	}
2252e124b4aSJiri Slaby 
2262f0fc415SJohn Crispin 	if (ch != 0)
2272e124b4aSJiri Slaby 		tty_flip_buffer_push(tport);
2282e124b4aSJiri Slaby 
2292f0fc415SJohn Crispin 	return 0;
2302f0fc415SJohn Crispin }
2312f0fc415SJohn Crispin 
2322f0fc415SJohn Crispin static irqreturn_t
lqasc_tx_int(int irq,void * _port)2332f0fc415SJohn Crispin lqasc_tx_int(int irq, void *_port)
2342f0fc415SJohn Crispin {
2352f0fc415SJohn Crispin 	unsigned long flags;
2362f0fc415SJohn Crispin 	struct uart_port *port = (struct uart_port *)_port;
2374b967e63SRahul Tanwar 	struct ltq_uart_port *ltq_port = to_ltq_uart_port(port);
2384b967e63SRahul Tanwar 
2394b967e63SRahul Tanwar 	spin_lock_irqsave(&ltq_port->lock, flags);
240d3a28a53SHauke Mehrtens 	__raw_writel(ASC_IRNCR_TIR, port->membase + LTQ_ASC_IRNCR);
2414b967e63SRahul Tanwar 	spin_unlock_irqrestore(&ltq_port->lock, flags);
2422f0fc415SJohn Crispin 	lqasc_start_tx(port);
2432f0fc415SJohn Crispin 	return IRQ_HANDLED;
2442f0fc415SJohn Crispin }
2452f0fc415SJohn Crispin 
2462f0fc415SJohn Crispin static irqreturn_t
lqasc_err_int(int irq,void * _port)2472f0fc415SJohn Crispin lqasc_err_int(int irq, void *_port)
2482f0fc415SJohn Crispin {
2492f0fc415SJohn Crispin 	unsigned long flags;
2502f0fc415SJohn Crispin 	struct uart_port *port = (struct uart_port *)_port;
2514b967e63SRahul Tanwar 	struct ltq_uart_port *ltq_port = to_ltq_uart_port(port);
2524b967e63SRahul Tanwar 
2534b967e63SRahul Tanwar 	spin_lock_irqsave(&ltq_port->lock, flags);
25430632003SBernhard Seibold 	__raw_writel(ASC_IRNCR_EIR, port->membase + LTQ_ASC_IRNCR);
2552f0fc415SJohn Crispin 	/* clear any pending interrupts */
256fccf231aSSongjun Wu 	asc_update_bits(0, ASCWHBSTATE_CLRPE | ASCWHBSTATE_CLRFE |
2572f0fc415SJohn Crispin 		ASCWHBSTATE_CLRROE, port->membase + LTQ_ASC_WHBSTATE);
2584b967e63SRahul Tanwar 	spin_unlock_irqrestore(&ltq_port->lock, flags);
2592f0fc415SJohn Crispin 	return IRQ_HANDLED;
2602f0fc415SJohn Crispin }
2612f0fc415SJohn Crispin 
2622f0fc415SJohn Crispin static irqreturn_t
lqasc_rx_int(int irq,void * _port)2632f0fc415SJohn Crispin lqasc_rx_int(int irq, void *_port)
2642f0fc415SJohn Crispin {
2652f0fc415SJohn Crispin 	unsigned long flags;
2662f0fc415SJohn Crispin 	struct uart_port *port = (struct uart_port *)_port;
2674b967e63SRahul Tanwar 	struct ltq_uart_port *ltq_port = to_ltq_uart_port(port);
2684b967e63SRahul Tanwar 
2694b967e63SRahul Tanwar 	spin_lock_irqsave(&ltq_port->lock, flags);
270d3a28a53SHauke Mehrtens 	__raw_writel(ASC_IRNCR_RIR, port->membase + LTQ_ASC_IRNCR);
2712f0fc415SJohn Crispin 	lqasc_rx_chars(port);
2724b967e63SRahul Tanwar 	spin_unlock_irqrestore(&ltq_port->lock, flags);
2732f0fc415SJohn Crispin 	return IRQ_HANDLED;
2742f0fc415SJohn Crispin }
2752f0fc415SJohn Crispin 
lqasc_irq(int irq,void * p)276b832776bSRahul Tanwar static irqreturn_t lqasc_irq(int irq, void *p)
277b832776bSRahul Tanwar {
278b832776bSRahul Tanwar 	unsigned long flags;
279b832776bSRahul Tanwar 	u32 stat;
280b832776bSRahul Tanwar 	struct uart_port *port = p;
281b832776bSRahul Tanwar 	struct ltq_uart_port *ltq_port = to_ltq_uart_port(port);
282b832776bSRahul Tanwar 
283b832776bSRahul Tanwar 	spin_lock_irqsave(&ltq_port->lock, flags);
284b832776bSRahul Tanwar 	stat = readl(port->membase + LTQ_ASC_IRNCR);
285b832776bSRahul Tanwar 	spin_unlock_irqrestore(&ltq_port->lock, flags);
286b832776bSRahul Tanwar 	if (!(stat & ASC_IRNCR_MASK))
287b832776bSRahul Tanwar 		return IRQ_NONE;
288b832776bSRahul Tanwar 
289b832776bSRahul Tanwar 	if (stat & ASC_IRNCR_TIR)
290b832776bSRahul Tanwar 		lqasc_tx_int(irq, p);
291b832776bSRahul Tanwar 
292b832776bSRahul Tanwar 	if (stat & ASC_IRNCR_RIR)
293b832776bSRahul Tanwar 		lqasc_rx_int(irq, p);
294b832776bSRahul Tanwar 
295b832776bSRahul Tanwar 	if (stat & ASC_IRNCR_EIR)
296b832776bSRahul Tanwar 		lqasc_err_int(irq, p);
297b832776bSRahul Tanwar 
298b832776bSRahul Tanwar 	return IRQ_HANDLED;
299b832776bSRahul Tanwar }
300b832776bSRahul Tanwar 
3012f0fc415SJohn Crispin static unsigned int
lqasc_tx_empty(struct uart_port * port)3022f0fc415SJohn Crispin lqasc_tx_empty(struct uart_port *port)
3032f0fc415SJohn Crispin {
3042f0fc415SJohn Crispin 	int status;
305d3a28a53SHauke Mehrtens 	status = __raw_readl(port->membase + LTQ_ASC_FSTAT) &
306d3a28a53SHauke Mehrtens 		 ASCFSTAT_TXFFLMASK;
3072f0fc415SJohn Crispin 	return status ? 0 : TIOCSER_TEMT;
3082f0fc415SJohn Crispin }
3092f0fc415SJohn Crispin 
3102f0fc415SJohn Crispin static unsigned int
lqasc_get_mctrl(struct uart_port * port)3112f0fc415SJohn Crispin lqasc_get_mctrl(struct uart_port *port)
3122f0fc415SJohn Crispin {
3132f0fc415SJohn Crispin 	return TIOCM_CTS | TIOCM_CAR | TIOCM_DSR;
3142f0fc415SJohn Crispin }
3152f0fc415SJohn Crispin 
3162f0fc415SJohn Crispin static void
lqasc_set_mctrl(struct uart_port * port,u_int mctrl)3172f0fc415SJohn Crispin lqasc_set_mctrl(struct uart_port *port, u_int mctrl)
3182f0fc415SJohn Crispin {
3192f0fc415SJohn Crispin }
3202f0fc415SJohn Crispin 
3212f0fc415SJohn Crispin static void
lqasc_break_ctl(struct uart_port * port,int break_state)3222f0fc415SJohn Crispin lqasc_break_ctl(struct uart_port *port, int break_state)
3232f0fc415SJohn Crispin {
3242f0fc415SJohn Crispin }
3252f0fc415SJohn Crispin 
3262f0fc415SJohn Crispin static int
lqasc_startup(struct uart_port * port)3272f0fc415SJohn Crispin lqasc_startup(struct uart_port *port)
3282f0fc415SJohn Crispin {
3292f0fc415SJohn Crispin 	struct ltq_uart_port *ltq_port = to_ltq_uart_port(port);
3302f0fc415SJohn Crispin 	int retval;
3314b967e63SRahul Tanwar 	unsigned long flags;
3322f0fc415SJohn Crispin 
3338ad2ee95SJohn Crispin 	if (!IS_ERR(ltq_port->clk))
3345034ce06SSongjun Wu 		clk_prepare_enable(ltq_port->clk);
3352e81c1f3SSongjun Wu 	port->uartclk = clk_get_rate(ltq_port->freqclk);
3362f0fc415SJohn Crispin 
3374b967e63SRahul Tanwar 	spin_lock_irqsave(&ltq_port->lock, flags);
338fccf231aSSongjun Wu 	asc_update_bits(ASCCLC_DISS | ASCCLC_RMCMASK, (1 << ASCCLC_RMCOFFSET),
3392f0fc415SJohn Crispin 		port->membase + LTQ_ASC_CLC);
3402f0fc415SJohn Crispin 
341d3a28a53SHauke Mehrtens 	__raw_writel(0, port->membase + LTQ_ASC_PISEL);
342d3a28a53SHauke Mehrtens 	__raw_writel(
3432f0fc415SJohn Crispin 		((TXFIFO_FL << ASCTXFCON_TXFITLOFF) & ASCTXFCON_TXFITLMASK) |
3442f0fc415SJohn Crispin 		ASCTXFCON_TXFEN | ASCTXFCON_TXFFLU,
3452f0fc415SJohn Crispin 		port->membase + LTQ_ASC_TXFCON);
346d3a28a53SHauke Mehrtens 	__raw_writel(
3472f0fc415SJohn Crispin 		((RXFIFO_FL << ASCRXFCON_RXFITLOFF) & ASCRXFCON_RXFITLMASK)
3482f0fc415SJohn Crispin 		| ASCRXFCON_RXFEN | ASCRXFCON_RXFFLU,
3492f0fc415SJohn Crispin 		port->membase + LTQ_ASC_RXFCON);
3502f0fc415SJohn Crispin 	/* make sure other settings are written to hardware before
3512f0fc415SJohn Crispin 	 * setting enable bits
3522f0fc415SJohn Crispin 	 */
3532f0fc415SJohn Crispin 	wmb();
354fccf231aSSongjun Wu 	asc_update_bits(0, ASCCON_M_8ASYNC | ASCCON_FEN | ASCCON_TOEN |
3552f0fc415SJohn Crispin 		ASCCON_ROEN, port->membase + LTQ_ASC_CON);
3562f0fc415SJohn Crispin 
3574b967e63SRahul Tanwar 	spin_unlock_irqrestore(&ltq_port->lock, flags);
3584b967e63SRahul Tanwar 
35914208b38SRahul Tanwar 	retval = ltq_port->soc->request_irq(port);
36014208b38SRahul Tanwar 	if (retval)
3612f0fc415SJohn Crispin 		return retval;
3622f0fc415SJohn Crispin 
363d3a28a53SHauke Mehrtens 	__raw_writel(ASC_IRNREN_RX | ASC_IRNREN_ERR | ASC_IRNREN_TX,
3642f0fc415SJohn Crispin 		port->membase + LTQ_ASC_IRNREN);
3652f0fc415SJohn Crispin 	return retval;
3662f0fc415SJohn Crispin }
3672f0fc415SJohn Crispin 
3682f0fc415SJohn Crispin static void
lqasc_shutdown(struct uart_port * port)3692f0fc415SJohn Crispin lqasc_shutdown(struct uart_port *port)
3702f0fc415SJohn Crispin {
3712f0fc415SJohn Crispin 	struct ltq_uart_port *ltq_port = to_ltq_uart_port(port);
3724b967e63SRahul Tanwar 	unsigned long flags;
3734b967e63SRahul Tanwar 
37414208b38SRahul Tanwar 	ltq_port->soc->free_irq(port);
3752f0fc415SJohn Crispin 
3764b967e63SRahul Tanwar 	spin_lock_irqsave(&ltq_port->lock, flags);
377d3a28a53SHauke Mehrtens 	__raw_writel(0, port->membase + LTQ_ASC_CON);
378fccf231aSSongjun Wu 	asc_update_bits(ASCRXFCON_RXFEN, ASCRXFCON_RXFFLU,
3792f0fc415SJohn Crispin 		port->membase + LTQ_ASC_RXFCON);
380fccf231aSSongjun Wu 	asc_update_bits(ASCTXFCON_TXFEN, ASCTXFCON_TXFFLU,
3812f0fc415SJohn Crispin 		port->membase + LTQ_ASC_TXFCON);
3824b967e63SRahul Tanwar 	spin_unlock_irqrestore(&ltq_port->lock, flags);
3838ad2ee95SJohn Crispin 	if (!IS_ERR(ltq_port->clk))
3845034ce06SSongjun Wu 		clk_disable_unprepare(ltq_port->clk);
3852f0fc415SJohn Crispin }
3862f0fc415SJohn Crispin 
3872f0fc415SJohn Crispin static void
lqasc_set_termios(struct uart_port * port,struct ktermios * new,const struct ktermios * old)388bec5b814SIlpo Järvinen lqasc_set_termios(struct uart_port *port, struct ktermios *new,
389bec5b814SIlpo Järvinen 		  const struct ktermios *old)
3902f0fc415SJohn Crispin {
3912f0fc415SJohn Crispin 	unsigned int cflag;
3922f0fc415SJohn Crispin 	unsigned int iflag;
3932f0fc415SJohn Crispin 	unsigned int divisor;
3942f0fc415SJohn Crispin 	unsigned int baud;
3952f0fc415SJohn Crispin 	unsigned int con = 0;
3962f0fc415SJohn Crispin 	unsigned long flags;
3974b967e63SRahul Tanwar 	struct ltq_uart_port *ltq_port = to_ltq_uart_port(port);
3982f0fc415SJohn Crispin 
3992f0fc415SJohn Crispin 	cflag = new->c_cflag;
4002f0fc415SJohn Crispin 	iflag = new->c_iflag;
4012f0fc415SJohn Crispin 
4022f0fc415SJohn Crispin 	switch (cflag & CSIZE) {
4032f0fc415SJohn Crispin 	case CS7:
4042f0fc415SJohn Crispin 		con = ASCCON_M_7ASYNC;
4052f0fc415SJohn Crispin 		break;
4062f0fc415SJohn Crispin 
4072f0fc415SJohn Crispin 	case CS5:
4082f0fc415SJohn Crispin 	case CS6:
4092f0fc415SJohn Crispin 	default:
4102f0fc415SJohn Crispin 		new->c_cflag &= ~ CSIZE;
4112f0fc415SJohn Crispin 		new->c_cflag |= CS8;
4122f0fc415SJohn Crispin 		con = ASCCON_M_8ASYNC;
4132f0fc415SJohn Crispin 		break;
4142f0fc415SJohn Crispin 	}
4152f0fc415SJohn Crispin 
4162f0fc415SJohn Crispin 	cflag &= ~CMSPAR; /* Mark/Space parity is not supported */
4172f0fc415SJohn Crispin 
4182f0fc415SJohn Crispin 	if (cflag & CSTOPB)
4192f0fc415SJohn Crispin 		con |= ASCCON_STP;
4202f0fc415SJohn Crispin 
4212f0fc415SJohn Crispin 	if (cflag & PARENB) {
4222f0fc415SJohn Crispin 		if (!(cflag & PARODD))
4232f0fc415SJohn Crispin 			con &= ~ASCCON_ODD;
4242f0fc415SJohn Crispin 		else
4252f0fc415SJohn Crispin 			con |= ASCCON_ODD;
4262f0fc415SJohn Crispin 	}
4272f0fc415SJohn Crispin 
4282f0fc415SJohn Crispin 	port->read_status_mask = ASCSTATE_ROE;
4292f0fc415SJohn Crispin 	if (iflag & INPCK)
4302f0fc415SJohn Crispin 		port->read_status_mask |= ASCSTATE_FE | ASCSTATE_PE;
4312f0fc415SJohn Crispin 
4322f0fc415SJohn Crispin 	port->ignore_status_mask = 0;
4332f0fc415SJohn Crispin 	if (iflag & IGNPAR)
4342f0fc415SJohn Crispin 		port->ignore_status_mask |= ASCSTATE_FE | ASCSTATE_PE;
4352f0fc415SJohn Crispin 
4362f0fc415SJohn Crispin 	if (iflag & IGNBRK) {
4372f0fc415SJohn Crispin 		/*
4382f0fc415SJohn Crispin 		 * If we're ignoring parity and break indicators,
4392f0fc415SJohn Crispin 		 * ignore overruns too (for real raw support).
4402f0fc415SJohn Crispin 		 */
4412f0fc415SJohn Crispin 		if (iflag & IGNPAR)
4422f0fc415SJohn Crispin 			port->ignore_status_mask |= ASCSTATE_ROE;
4432f0fc415SJohn Crispin 	}
4442f0fc415SJohn Crispin 
4452f0fc415SJohn Crispin 	if ((cflag & CREAD) == 0)
4462f0fc415SJohn Crispin 		port->ignore_status_mask |= UART_DUMMY_UER_RX;
4472f0fc415SJohn Crispin 
4482f0fc415SJohn Crispin 	/* set error signals  - framing, parity  and overrun, enable receiver */
4492f0fc415SJohn Crispin 	con |= ASCCON_FEN | ASCCON_TOEN | ASCCON_ROEN;
4502f0fc415SJohn Crispin 
4514b967e63SRahul Tanwar 	spin_lock_irqsave(&ltq_port->lock, flags);
4522f0fc415SJohn Crispin 
4532f0fc415SJohn Crispin 	/* set up CON */
454fccf231aSSongjun Wu 	asc_update_bits(0, con, port->membase + LTQ_ASC_CON);
4552f0fc415SJohn Crispin 
4562f0fc415SJohn Crispin 	/* Set baud rate - take a divider of 2 into account */
4572f0fc415SJohn Crispin 	baud = uart_get_baud_rate(port, new, old, 0, port->uartclk / 16);
4582f0fc415SJohn Crispin 	divisor = uart_get_divisor(port, baud);
4592f0fc415SJohn Crispin 	divisor = divisor / 2 - 1;
4602f0fc415SJohn Crispin 
4612f0fc415SJohn Crispin 	/* disable the baudrate generator */
462fccf231aSSongjun Wu 	asc_update_bits(ASCCON_R, 0, port->membase + LTQ_ASC_CON);
4632f0fc415SJohn Crispin 
4642f0fc415SJohn Crispin 	/* make sure the fractional divider is off */
465fccf231aSSongjun Wu 	asc_update_bits(ASCCON_FDE, 0, port->membase + LTQ_ASC_CON);
4662f0fc415SJohn Crispin 
4672f0fc415SJohn Crispin 	/* set up to use divisor of 2 */
468fccf231aSSongjun Wu 	asc_update_bits(ASCCON_BRS, 0, port->membase + LTQ_ASC_CON);
4692f0fc415SJohn Crispin 
4702f0fc415SJohn Crispin 	/* now we can write the new baudrate into the register */
471d3a28a53SHauke Mehrtens 	__raw_writel(divisor, port->membase + LTQ_ASC_BG);
4722f0fc415SJohn Crispin 
4732f0fc415SJohn Crispin 	/* turn the baudrate generator back on */
474fccf231aSSongjun Wu 	asc_update_bits(0, ASCCON_R, port->membase + LTQ_ASC_CON);
4752f0fc415SJohn Crispin 
4762f0fc415SJohn Crispin 	/* enable rx */
477d3a28a53SHauke Mehrtens 	__raw_writel(ASCWHBSTATE_SETREN, port->membase + LTQ_ASC_WHBSTATE);
4782f0fc415SJohn Crispin 
4794b967e63SRahul Tanwar 	spin_unlock_irqrestore(&ltq_port->lock, flags);
4802f0fc415SJohn Crispin 
4812f0fc415SJohn Crispin 	/* Don't rewrite B0 */
4822f0fc415SJohn Crispin 	if (tty_termios_baud_rate(new))
4832f0fc415SJohn Crispin 		tty_termios_encode_baud_rate(new, baud, baud);
484b7867f1bSJohn Crispin 
485b7867f1bSJohn Crispin 	uart_update_timeout(port, cflag, baud);
4862f0fc415SJohn Crispin }
4872f0fc415SJohn Crispin 
4882f0fc415SJohn Crispin static const char*
lqasc_type(struct uart_port * port)4892f0fc415SJohn Crispin lqasc_type(struct uart_port *port)
4902f0fc415SJohn Crispin {
4912f0fc415SJohn Crispin 	if (port->type == PORT_LTQ_ASC)
4922f0fc415SJohn Crispin 		return DRVNAME;
4932f0fc415SJohn Crispin 	else
4942f0fc415SJohn Crispin 		return NULL;
4952f0fc415SJohn Crispin }
4962f0fc415SJohn Crispin 
4972f0fc415SJohn Crispin static void
lqasc_release_port(struct uart_port * port)4982f0fc415SJohn Crispin lqasc_release_port(struct uart_port *port)
4992f0fc415SJohn Crispin {
5001511316fSSudip Mukherjee 	struct platform_device *pdev = to_platform_device(port->dev);
5011511316fSSudip Mukherjee 
5022f0fc415SJohn Crispin 	if (port->flags & UPF_IOREMAP) {
5031511316fSSudip Mukherjee 		devm_iounmap(&pdev->dev, port->membase);
5042f0fc415SJohn Crispin 		port->membase = NULL;
5052f0fc415SJohn Crispin 	}
5062f0fc415SJohn Crispin }
5072f0fc415SJohn Crispin 
5082f0fc415SJohn Crispin static int
lqasc_request_port(struct uart_port * port)5092f0fc415SJohn Crispin lqasc_request_port(struct uart_port *port)
5102f0fc415SJohn Crispin {
5112f0fc415SJohn Crispin 	struct platform_device *pdev = to_platform_device(port->dev);
5122f0fc415SJohn Crispin 	struct resource *res;
5132f0fc415SJohn Crispin 	int size;
5142f0fc415SJohn Crispin 
5152f0fc415SJohn Crispin 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
5162f0fc415SJohn Crispin 	if (!res) {
5172f0fc415SJohn Crispin 		dev_err(&pdev->dev, "cannot obtain I/O memory region");
5182f0fc415SJohn Crispin 		return -ENODEV;
5192f0fc415SJohn Crispin 	}
5202f0fc415SJohn Crispin 	size = resource_size(res);
5212f0fc415SJohn Crispin 
5222f0fc415SJohn Crispin 	res = devm_request_mem_region(&pdev->dev, res->start,
5232f0fc415SJohn Crispin 		size, dev_name(&pdev->dev));
5242f0fc415SJohn Crispin 	if (!res) {
5252f0fc415SJohn Crispin 		dev_err(&pdev->dev, "cannot request I/O memory region");
5262f0fc415SJohn Crispin 		return -EBUSY;
5272f0fc415SJohn Crispin 	}
5282f0fc415SJohn Crispin 
5292f0fc415SJohn Crispin 	if (port->flags & UPF_IOREMAP) {
5304bdc0d67SChristoph Hellwig 		port->membase = devm_ioremap(&pdev->dev,
5312f0fc415SJohn Crispin 			port->mapbase, size);
5322f0fc415SJohn Crispin 		if (port->membase == NULL)
5332f0fc415SJohn Crispin 			return -ENOMEM;
5342f0fc415SJohn Crispin 	}
5352f0fc415SJohn Crispin 	return 0;
5362f0fc415SJohn Crispin }
5372f0fc415SJohn Crispin 
5382f0fc415SJohn Crispin static void
lqasc_config_port(struct uart_port * port,int flags)5392f0fc415SJohn Crispin lqasc_config_port(struct uart_port *port, int flags)
5402f0fc415SJohn Crispin {
5412f0fc415SJohn Crispin 	if (flags & UART_CONFIG_TYPE) {
5422f0fc415SJohn Crispin 		port->type = PORT_LTQ_ASC;
5432f0fc415SJohn Crispin 		lqasc_request_port(port);
5442f0fc415SJohn Crispin 	}
5452f0fc415SJohn Crispin }
5462f0fc415SJohn Crispin 
5472f0fc415SJohn Crispin static int
lqasc_verify_port(struct uart_port * port,struct serial_struct * ser)5482f0fc415SJohn Crispin lqasc_verify_port(struct uart_port *port,
5492f0fc415SJohn Crispin 	struct serial_struct *ser)
5502f0fc415SJohn Crispin {
5512f0fc415SJohn Crispin 	int ret = 0;
5522f0fc415SJohn Crispin 	if (ser->type != PORT_UNKNOWN && ser->type != PORT_LTQ_ASC)
5532f0fc415SJohn Crispin 		ret = -EINVAL;
5542f0fc415SJohn Crispin 	if (ser->irq < 0 || ser->irq >= NR_IRQS)
5552f0fc415SJohn Crispin 		ret = -EINVAL;
5562f0fc415SJohn Crispin 	if (ser->baud_base < 9600)
5572f0fc415SJohn Crispin 		ret = -EINVAL;
5582f0fc415SJohn Crispin 	return ret;
5592f0fc415SJohn Crispin }
5602f0fc415SJohn Crispin 
5612331e068SBhumika Goyal static const struct uart_ops lqasc_pops = {
5622f0fc415SJohn Crispin 	.tx_empty =	lqasc_tx_empty,
5632f0fc415SJohn Crispin 	.set_mctrl =	lqasc_set_mctrl,
5642f0fc415SJohn Crispin 	.get_mctrl =	lqasc_get_mctrl,
5652f0fc415SJohn Crispin 	.stop_tx =	lqasc_stop_tx,
5662f0fc415SJohn Crispin 	.start_tx =	lqasc_start_tx,
5672f0fc415SJohn Crispin 	.stop_rx =	lqasc_stop_rx,
5682f0fc415SJohn Crispin 	.break_ctl =	lqasc_break_ctl,
5692f0fc415SJohn Crispin 	.startup =	lqasc_startup,
5702f0fc415SJohn Crispin 	.shutdown =	lqasc_shutdown,
5712f0fc415SJohn Crispin 	.set_termios =	lqasc_set_termios,
5722f0fc415SJohn Crispin 	.type =		lqasc_type,
5732f0fc415SJohn Crispin 	.release_port =	lqasc_release_port,
5742f0fc415SJohn Crispin 	.request_port =	lqasc_request_port,
5752f0fc415SJohn Crispin 	.config_port =	lqasc_config_port,
5762f0fc415SJohn Crispin 	.verify_port =	lqasc_verify_port,
5772f0fc415SJohn Crispin };
5782f0fc415SJohn Crispin 
579ea7d3fd8SRahul Tanwar #ifdef CONFIG_SERIAL_LANTIQ_CONSOLE
5802f0fc415SJohn Crispin static void
lqasc_console_putchar(struct uart_port * port,unsigned char ch)5813f8bab17SJiri Slaby lqasc_console_putchar(struct uart_port *port, unsigned char ch)
5822f0fc415SJohn Crispin {
5832f0fc415SJohn Crispin 	if (!port->membase)
5842f0fc415SJohn Crispin 		return;
5852f0fc415SJohn Crispin 
586d8544c9cSJiri Slaby 	while (!lqasc_tx_ready(port))
587d8544c9cSJiri Slaby 		;
588d8544c9cSJiri Slaby 
58989b8bd20SSongjun Wu 	writeb(ch, port->membase + LTQ_ASC_TBUF);
5902f0fc415SJohn Crispin }
5912f0fc415SJohn Crispin 
lqasc_serial_port_write(struct uart_port * port,const char * s,u_int count)592ec84aa0aSMartin Blumenstingl static void lqasc_serial_port_write(struct uart_port *port, const char *s,
593ec84aa0aSMartin Blumenstingl 				    u_int count)
594ec84aa0aSMartin Blumenstingl {
595ec84aa0aSMartin Blumenstingl 	uart_console_write(port, s, count, lqasc_console_putchar);
596ec84aa0aSMartin Blumenstingl }
5972f0fc415SJohn Crispin 
5982f0fc415SJohn Crispin static void
lqasc_console_write(struct console * co,const char * s,u_int count)5992f0fc415SJohn Crispin lqasc_console_write(struct console *co, const char *s, u_int count)
6002f0fc415SJohn Crispin {
6012f0fc415SJohn Crispin 	struct ltq_uart_port *ltq_port;
6024b967e63SRahul Tanwar 	unsigned long flags;
6032f0fc415SJohn Crispin 
6042f0fc415SJohn Crispin 	if (co->index >= MAXPORTS)
6052f0fc415SJohn Crispin 		return;
6062f0fc415SJohn Crispin 
6072f0fc415SJohn Crispin 	ltq_port = lqasc_port[co->index];
6082f0fc415SJohn Crispin 	if (!ltq_port)
6092f0fc415SJohn Crispin 		return;
6102f0fc415SJohn Crispin 
6114b967e63SRahul Tanwar 	spin_lock_irqsave(&ltq_port->lock, flags);
612ec84aa0aSMartin Blumenstingl 	lqasc_serial_port_write(&ltq_port->port, s, count);
6134b967e63SRahul Tanwar 	spin_unlock_irqrestore(&ltq_port->lock, flags);
6142f0fc415SJohn Crispin }
6152f0fc415SJohn Crispin 
6162f0fc415SJohn Crispin static int __init
lqasc_console_setup(struct console * co,char * options)6172f0fc415SJohn Crispin lqasc_console_setup(struct console *co, char *options)
6182f0fc415SJohn Crispin {
6192f0fc415SJohn Crispin 	struct ltq_uart_port *ltq_port;
6202f0fc415SJohn Crispin 	struct uart_port *port;
6212f0fc415SJohn Crispin 	int baud = 115200;
6222f0fc415SJohn Crispin 	int bits = 8;
6232f0fc415SJohn Crispin 	int parity = 'n';
6242f0fc415SJohn Crispin 	int flow = 'n';
6252f0fc415SJohn Crispin 
6262f0fc415SJohn Crispin 	if (co->index >= MAXPORTS)
6272f0fc415SJohn Crispin 		return -ENODEV;
6282f0fc415SJohn Crispin 
6292f0fc415SJohn Crispin 	ltq_port = lqasc_port[co->index];
6302f0fc415SJohn Crispin 	if (!ltq_port)
6312f0fc415SJohn Crispin 		return -ENODEV;
6322f0fc415SJohn Crispin 
6332f0fc415SJohn Crispin 	port = &ltq_port->port;
6342f0fc415SJohn Crispin 
6357c658e6bSThomas Langer 	if (!IS_ERR(ltq_port->clk))
6365034ce06SSongjun Wu 		clk_prepare_enable(ltq_port->clk);
6377c658e6bSThomas Langer 
6382e81c1f3SSongjun Wu 	port->uartclk = clk_get_rate(ltq_port->freqclk);
6392f0fc415SJohn Crispin 
6402f0fc415SJohn Crispin 	if (options)
6412f0fc415SJohn Crispin 		uart_parse_options(options, &baud, &parity, &bits, &flow);
6422f0fc415SJohn Crispin 	return uart_set_options(port, co, baud, parity, bits, flow);
6432f0fc415SJohn Crispin }
6442f0fc415SJohn Crispin 
6452f0fc415SJohn Crispin static struct console lqasc_console = {
6462f0fc415SJohn Crispin 	.name =		"ttyLTQ",
6472f0fc415SJohn Crispin 	.write =	lqasc_console_write,
6482f0fc415SJohn Crispin 	.device =	uart_console_device,
6492f0fc415SJohn Crispin 	.setup =	lqasc_console_setup,
6502f0fc415SJohn Crispin 	.flags =	CON_PRINTBUFFER,
6512f0fc415SJohn Crispin 	.index =	-1,
6522f0fc415SJohn Crispin 	.data =		&lqasc_reg,
6532f0fc415SJohn Crispin };
6542f0fc415SJohn Crispin 
6552f0fc415SJohn Crispin static int __init
lqasc_console_init(void)6562f0fc415SJohn Crispin lqasc_console_init(void)
6572f0fc415SJohn Crispin {
6582f0fc415SJohn Crispin 	register_console(&lqasc_console);
6592f0fc415SJohn Crispin 	return 0;
6602f0fc415SJohn Crispin }
6612f0fc415SJohn Crispin console_initcall(lqasc_console_init);
6622f0fc415SJohn Crispin 
lqasc_serial_early_console_write(struct console * co,const char * s,u_int count)663ec84aa0aSMartin Blumenstingl static void lqasc_serial_early_console_write(struct console *co,
664ec84aa0aSMartin Blumenstingl 					     const char *s,
665ec84aa0aSMartin Blumenstingl 					     u_int count)
666ec84aa0aSMartin Blumenstingl {
667ec84aa0aSMartin Blumenstingl 	struct earlycon_device *dev = co->data;
668ec84aa0aSMartin Blumenstingl 
669ec84aa0aSMartin Blumenstingl 	lqasc_serial_port_write(&dev->port, s, count);
670ec84aa0aSMartin Blumenstingl }
671ec84aa0aSMartin Blumenstingl 
672ec84aa0aSMartin Blumenstingl static int __init
lqasc_serial_early_console_setup(struct earlycon_device * device,const char * opt)673ec84aa0aSMartin Blumenstingl lqasc_serial_early_console_setup(struct earlycon_device *device,
674ec84aa0aSMartin Blumenstingl 				 const char *opt)
675ec84aa0aSMartin Blumenstingl {
676ec84aa0aSMartin Blumenstingl 	if (!device->port.membase)
677ec84aa0aSMartin Blumenstingl 		return -ENODEV;
678ec84aa0aSMartin Blumenstingl 
679ec84aa0aSMartin Blumenstingl 	device->con->write = lqasc_serial_early_console_write;
680ec84aa0aSMartin Blumenstingl 	return 0;
681ec84aa0aSMartin Blumenstingl }
6820de2580fSRahul Tanwar OF_EARLYCON_DECLARE(lantiq, "lantiq,asc", lqasc_serial_early_console_setup);
683b832776bSRahul Tanwar OF_EARLYCON_DECLARE(lantiq, "intel,lgm-asc", lqasc_serial_early_console_setup);
684ec84aa0aSMartin Blumenstingl 
685ea7d3fd8SRahul Tanwar #define LANTIQ_SERIAL_CONSOLE	(&lqasc_console)
686ea7d3fd8SRahul Tanwar 
687ea7d3fd8SRahul Tanwar #else
688ea7d3fd8SRahul Tanwar 
689ea7d3fd8SRahul Tanwar #define LANTIQ_SERIAL_CONSOLE	NULL
690ea7d3fd8SRahul Tanwar 
691ea7d3fd8SRahul Tanwar #endif /* CONFIG_SERIAL_LANTIQ_CONSOLE */
692ea7d3fd8SRahul Tanwar 
6932f0fc415SJohn Crispin static struct uart_driver lqasc_reg = {
6942f0fc415SJohn Crispin 	.owner =	THIS_MODULE,
6952f0fc415SJohn Crispin 	.driver_name =	DRVNAME,
6962f0fc415SJohn Crispin 	.dev_name =	"ttyLTQ",
6972f0fc415SJohn Crispin 	.major =	0,
6982f0fc415SJohn Crispin 	.minor =	0,
6992f0fc415SJohn Crispin 	.nr =		MAXPORTS,
700ea7d3fd8SRahul Tanwar 	.cons =		LANTIQ_SERIAL_CONSOLE,
7012f0fc415SJohn Crispin };
7022f0fc415SJohn Crispin 
fetch_irq_lantiq(struct device * dev,struct ltq_uart_port * ltq_port)70314208b38SRahul Tanwar static int fetch_irq_lantiq(struct device *dev, struct ltq_uart_port *ltq_port)
70414208b38SRahul Tanwar {
70514208b38SRahul Tanwar 	struct uart_port *port = &ltq_port->port;
706f087f01cSRob Herring 	struct platform_device *pdev = to_platform_device(dev);
707cb559bb9SMuhammad Usama Anjum 	int irq;
70814208b38SRahul Tanwar 
709cb559bb9SMuhammad Usama Anjum 	irq = platform_get_irq(pdev, 0);
710cb559bb9SMuhammad Usama Anjum 	if (irq < 0)
711cb559bb9SMuhammad Usama Anjum 		return irq;
712cb559bb9SMuhammad Usama Anjum 	ltq_port->tx_irq = irq;
713cb559bb9SMuhammad Usama Anjum 	irq = platform_get_irq(pdev, 1);
714cb559bb9SMuhammad Usama Anjum 	if (irq < 0)
715cb559bb9SMuhammad Usama Anjum 		return irq;
716cb559bb9SMuhammad Usama Anjum 	ltq_port->rx_irq = irq;
717cb559bb9SMuhammad Usama Anjum 	irq = platform_get_irq(pdev, 2);
718cb559bb9SMuhammad Usama Anjum 	if (irq < 0)
719cb559bb9SMuhammad Usama Anjum 		return irq;
720cb559bb9SMuhammad Usama Anjum 	ltq_port->err_irq = irq;
721f087f01cSRob Herring 
722f087f01cSRob Herring 	port->irq = ltq_port->tx_irq;
72314208b38SRahul Tanwar 
72414208b38SRahul Tanwar 	return 0;
72514208b38SRahul Tanwar }
72614208b38SRahul Tanwar 
request_irq_lantiq(struct uart_port * port)72714208b38SRahul Tanwar static int request_irq_lantiq(struct uart_port *port)
72814208b38SRahul Tanwar {
72914208b38SRahul Tanwar 	struct ltq_uart_port *ltq_port = to_ltq_uart_port(port);
73014208b38SRahul Tanwar 	int retval;
73114208b38SRahul Tanwar 
73214208b38SRahul Tanwar 	retval = request_irq(ltq_port->tx_irq, lqasc_tx_int,
73314208b38SRahul Tanwar 			     0, "asc_tx", port);
73414208b38SRahul Tanwar 	if (retval) {
73514208b38SRahul Tanwar 		dev_err(port->dev, "failed to request asc_tx\n");
73614208b38SRahul Tanwar 		return retval;
73714208b38SRahul Tanwar 	}
73814208b38SRahul Tanwar 
73914208b38SRahul Tanwar 	retval = request_irq(ltq_port->rx_irq, lqasc_rx_int,
74014208b38SRahul Tanwar 			     0, "asc_rx", port);
74114208b38SRahul Tanwar 	if (retval) {
74214208b38SRahul Tanwar 		dev_err(port->dev, "failed to request asc_rx\n");
74314208b38SRahul Tanwar 		goto err1;
74414208b38SRahul Tanwar 	}
74514208b38SRahul Tanwar 
74614208b38SRahul Tanwar 	retval = request_irq(ltq_port->err_irq, lqasc_err_int,
74714208b38SRahul Tanwar 			     0, "asc_err", port);
74814208b38SRahul Tanwar 	if (retval) {
74914208b38SRahul Tanwar 		dev_err(port->dev, "failed to request asc_err\n");
75014208b38SRahul Tanwar 		goto err2;
75114208b38SRahul Tanwar 	}
75214208b38SRahul Tanwar 	return 0;
75314208b38SRahul Tanwar 
75414208b38SRahul Tanwar err2:
75514208b38SRahul Tanwar 	free_irq(ltq_port->rx_irq, port);
75614208b38SRahul Tanwar err1:
75714208b38SRahul Tanwar 	free_irq(ltq_port->tx_irq, port);
75814208b38SRahul Tanwar 	return retval;
75914208b38SRahul Tanwar }
76014208b38SRahul Tanwar 
free_irq_lantiq(struct uart_port * port)76114208b38SRahul Tanwar static void free_irq_lantiq(struct uart_port *port)
76214208b38SRahul Tanwar {
76314208b38SRahul Tanwar 	struct ltq_uart_port *ltq_port = to_ltq_uart_port(port);
76414208b38SRahul Tanwar 
76514208b38SRahul Tanwar 	free_irq(ltq_port->tx_irq, port);
76614208b38SRahul Tanwar 	free_irq(ltq_port->rx_irq, port);
76714208b38SRahul Tanwar 	free_irq(ltq_port->err_irq, port);
76814208b38SRahul Tanwar }
76914208b38SRahul Tanwar 
fetch_irq_intel(struct device * dev,struct ltq_uart_port * ltq_port)770b832776bSRahul Tanwar static int fetch_irq_intel(struct device *dev, struct ltq_uart_port *ltq_port)
771b832776bSRahul Tanwar {
772b832776bSRahul Tanwar 	struct uart_port *port = &ltq_port->port;
773b832776bSRahul Tanwar 	int ret;
774b832776bSRahul Tanwar 
775f087f01cSRob Herring 	ret = platform_get_irq(to_platform_device(dev), 0);
776b832776bSRahul Tanwar 	if (ret < 0) {
777b832776bSRahul Tanwar 		dev_err(dev, "failed to fetch IRQ for serial port\n");
778b832776bSRahul Tanwar 		return ret;
779b832776bSRahul Tanwar 	}
780b832776bSRahul Tanwar 	ltq_port->common_irq = ret;
781b832776bSRahul Tanwar 	port->irq = ret;
782b832776bSRahul Tanwar 
783b832776bSRahul Tanwar 	return 0;
784b832776bSRahul Tanwar }
785b832776bSRahul Tanwar 
request_irq_intel(struct uart_port * port)786b832776bSRahul Tanwar static int request_irq_intel(struct uart_port *port)
787b832776bSRahul Tanwar {
788b832776bSRahul Tanwar 	struct ltq_uart_port *ltq_port = to_ltq_uart_port(port);
789b832776bSRahul Tanwar 	int retval;
790b832776bSRahul Tanwar 
791b832776bSRahul Tanwar 	retval = request_irq(ltq_port->common_irq, lqasc_irq, 0,
792b832776bSRahul Tanwar 			     "asc_irq", port);
793b832776bSRahul Tanwar 	if (retval)
794b832776bSRahul Tanwar 		dev_err(port->dev, "failed to request asc_irq\n");
795b832776bSRahul Tanwar 
796b832776bSRahul Tanwar 	return retval;
797b832776bSRahul Tanwar }
798b832776bSRahul Tanwar 
free_irq_intel(struct uart_port * port)799b832776bSRahul Tanwar static void free_irq_intel(struct uart_port *port)
800b832776bSRahul Tanwar {
801b832776bSRahul Tanwar 	struct ltq_uart_port *ltq_port = to_ltq_uart_port(port);
802b832776bSRahul Tanwar 
803b832776bSRahul Tanwar 	free_irq(ltq_port->common_irq, port);
804b832776bSRahul Tanwar }
805b832776bSRahul Tanwar 
lqasc_probe(struct platform_device * pdev)806ad406341SRahul Tanwar static int lqasc_probe(struct platform_device *pdev)
8072f0fc415SJohn Crispin {
808ceff2676SJohn Crispin 	struct device_node *node = pdev->dev.of_node;
8092f0fc415SJohn Crispin 	struct ltq_uart_port *ltq_port;
8102f0fc415SJohn Crispin 	struct uart_port *port;
81114208b38SRahul Tanwar 	struct resource *mmres;
812b871424fSSongjun Wu 	int line;
8132f0fc415SJohn Crispin 	int ret;
8142f0fc415SJohn Crispin 
8152f0fc415SJohn Crispin 	mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
81614208b38SRahul Tanwar 	if (!mmres) {
817ceff2676SJohn Crispin 		dev_err(&pdev->dev,
81814208b38SRahul Tanwar 			"failed to get memory for serial port\n");
8192f0fc415SJohn Crispin 		return -ENODEV;
8202f0fc415SJohn Crispin 	}
8212f0fc415SJohn Crispin 
82214208b38SRahul Tanwar 	ltq_port = devm_kzalloc(&pdev->dev, sizeof(struct ltq_uart_port),
82314208b38SRahul Tanwar 				GFP_KERNEL);
82414208b38SRahul Tanwar 	if (!ltq_port)
82514208b38SRahul Tanwar 		return -ENOMEM;
82614208b38SRahul Tanwar 
82714208b38SRahul Tanwar 	port = &ltq_port->port;
82814208b38SRahul Tanwar 
82914208b38SRahul Tanwar 	ltq_port->soc = of_device_get_match_data(&pdev->dev);
83014208b38SRahul Tanwar 	ret = ltq_port->soc->fetch_irq(&pdev->dev, ltq_port);
83114208b38SRahul Tanwar 	if (ret)
83214208b38SRahul Tanwar 		return ret;
83314208b38SRahul Tanwar 
834b871424fSSongjun Wu 	/* get serial id */
835b871424fSSongjun Wu 	line = of_alias_get_id(node, "serial");
836b871424fSSongjun Wu 	if (line < 0) {
837b871424fSSongjun Wu 		if (IS_ENABLED(CONFIG_LANTIQ)) {
838b871424fSSongjun Wu 			if (mmres->start == CPHYSADDR(LTQ_EARLY_ASC))
839b871424fSSongjun Wu 				line = 0;
840b871424fSSongjun Wu 			else
841ceff2676SJohn Crispin 				line = 1;
842b871424fSSongjun Wu 		} else {
843b871424fSSongjun Wu 			dev_err(&pdev->dev, "failed to get alias id, errno %d\n",
844b871424fSSongjun Wu 				line);
845b871424fSSongjun Wu 			return line;
846b871424fSSongjun Wu 		}
847b871424fSSongjun Wu 	}
8482f0fc415SJohn Crispin 
849ceff2676SJohn Crispin 	if (lqasc_port[line]) {
850ceff2676SJohn Crispin 		dev_err(&pdev->dev, "port %d already allocated\n", line);
851ceff2676SJohn Crispin 		return -EBUSY;
852ceff2676SJohn Crispin 	}
853ceff2676SJohn Crispin 
8542f0fc415SJohn Crispin 	port->iotype	= SERIAL_IO_MEM;
8555fda7a0eSPeter Hurley 	port->flags	= UPF_BOOT_AUTOCONF | UPF_IOREMAP;
8562f0fc415SJohn Crispin 	port->ops	= &lqasc_pops;
8572f0fc415SJohn Crispin 	port->fifosize	= 16;
858345523faSZheng Yongjun 	port->type	= PORT_LTQ_ASC;
859ceff2676SJohn Crispin 	port->line	= line;
8602f0fc415SJohn Crispin 	port->dev	= &pdev->dev;
861ceff2676SJohn Crispin 	/* unused, just to be backward-compatible */
8622f0fc415SJohn Crispin 	port->mapbase	= mmres->start;
8632f0fc415SJohn Crispin 
864dbbc26dbSSongjun Wu 	if (IS_ENABLED(CONFIG_LANTIQ) && !IS_ENABLED(CONFIG_COMMON_CLK))
8652e81c1f3SSongjun Wu 		ltq_port->freqclk = clk_get_fpi();
866dbbc26dbSSongjun Wu 	else
867dbbc26dbSSongjun Wu 		ltq_port->freqclk = devm_clk_get(&pdev->dev, "freq");
868dbbc26dbSSongjun Wu 
869dbbc26dbSSongjun Wu 
8702e81c1f3SSongjun Wu 	if (IS_ERR(ltq_port->freqclk)) {
871ceff2676SJohn Crispin 		pr_err("failed to get fpi clk\n");
872ceff2676SJohn Crispin 		return -ENOENT;
873ceff2676SJohn Crispin 	}
8742f0fc415SJohn Crispin 
875ceff2676SJohn Crispin 	/* not all asc ports have clock gates, lets ignore the return code */
876dbbc26dbSSongjun Wu 	if (IS_ENABLED(CONFIG_LANTIQ) && !IS_ENABLED(CONFIG_COMMON_CLK))
877ceff2676SJohn Crispin 		ltq_port->clk = clk_get(&pdev->dev, NULL);
878dbbc26dbSSongjun Wu 	else
879dbbc26dbSSongjun Wu 		ltq_port->clk = devm_clk_get(&pdev->dev, "asc");
8802f0fc415SJohn Crispin 
8814b967e63SRahul Tanwar 	spin_lock_init(&ltq_port->lock);
882ceff2676SJohn Crispin 	lqasc_port[line] = ltq_port;
8832f0fc415SJohn Crispin 	platform_set_drvdata(pdev, ltq_port);
8842f0fc415SJohn Crispin 
8852f0fc415SJohn Crispin 	ret = uart_add_one_port(&lqasc_reg, port);
8862f0fc415SJohn Crispin 
8872f0fc415SJohn Crispin 	return ret;
8882f0fc415SJohn Crispin }
8892f0fc415SJohn Crispin 
lqasc_remove(struct platform_device * pdev)890ad406341SRahul Tanwar static int lqasc_remove(struct platform_device *pdev)
891ad406341SRahul Tanwar {
892ad406341SRahul Tanwar 	struct uart_port *port = platform_get_drvdata(pdev);
893ad406341SRahul Tanwar 
894d5b3d02dSUwe Kleine-König 	uart_remove_one_port(&lqasc_reg, port);
895d5b3d02dSUwe Kleine-König 
896d5b3d02dSUwe Kleine-König 	return 0;
897ad406341SRahul Tanwar }
898ad406341SRahul Tanwar 
89914208b38SRahul Tanwar static const struct ltq_soc_data soc_data_lantiq = {
90014208b38SRahul Tanwar 	.fetch_irq = fetch_irq_lantiq,
90114208b38SRahul Tanwar 	.request_irq = request_irq_lantiq,
90214208b38SRahul Tanwar 	.free_irq = free_irq_lantiq,
90314208b38SRahul Tanwar };
90414208b38SRahul Tanwar 
905b832776bSRahul Tanwar static const struct ltq_soc_data soc_data_intel = {
906b832776bSRahul Tanwar 	.fetch_irq = fetch_irq_intel,
907b832776bSRahul Tanwar 	.request_irq = request_irq_intel,
908b832776bSRahul Tanwar 	.free_irq = free_irq_intel,
909b832776bSRahul Tanwar };
910b832776bSRahul Tanwar 
911ceff2676SJohn Crispin static const struct of_device_id ltq_asc_match[] = {
91214208b38SRahul Tanwar 	{ .compatible = "lantiq,asc", .data = &soc_data_lantiq },
913b832776bSRahul Tanwar 	{ .compatible = "intel,lgm-asc", .data = &soc_data_intel },
914ceff2676SJohn Crispin 	{},
915ceff2676SJohn Crispin };
916ad406341SRahul Tanwar MODULE_DEVICE_TABLE(of, ltq_asc_match);
917ceff2676SJohn Crispin 
9182f0fc415SJohn Crispin static struct platform_driver lqasc_driver = {
919ad406341SRahul Tanwar 	.probe		= lqasc_probe,
920ad406341SRahul Tanwar 	.remove		= lqasc_remove,
9212f0fc415SJohn Crispin 	.driver		= {
9222f0fc415SJohn Crispin 		.name	= DRVNAME,
923ceff2676SJohn Crispin 		.of_match_table = ltq_asc_match,
9242f0fc415SJohn Crispin 	},
9252f0fc415SJohn Crispin };
9262f0fc415SJohn Crispin 
92740efa6c8SSongjun Wu static int __init
init_lqasc(void)9282f0fc415SJohn Crispin init_lqasc(void)
9292f0fc415SJohn Crispin {
9302f0fc415SJohn Crispin 	int ret;
9312f0fc415SJohn Crispin 
9322f0fc415SJohn Crispin 	ret = uart_register_driver(&lqasc_reg);
9332f0fc415SJohn Crispin 	if (ret != 0)
9342f0fc415SJohn Crispin 		return ret;
9352f0fc415SJohn Crispin 
936ad406341SRahul Tanwar 	ret = platform_driver_register(&lqasc_driver);
9372f0fc415SJohn Crispin 	if (ret != 0)
9382f0fc415SJohn Crispin 		uart_unregister_driver(&lqasc_reg);
9392f0fc415SJohn Crispin 
9402f0fc415SJohn Crispin 	return ret;
9412f0fc415SJohn Crispin }
942ad406341SRahul Tanwar 
exit_lqasc(void)943ad406341SRahul Tanwar static void __exit exit_lqasc(void)
944ad406341SRahul Tanwar {
945ad406341SRahul Tanwar 	platform_driver_unregister(&lqasc_driver);
946ad406341SRahul Tanwar 	uart_unregister_driver(&lqasc_reg);
947ad406341SRahul Tanwar }
948ad406341SRahul Tanwar 
949ad406341SRahul Tanwar module_init(init_lqasc);
950ad406341SRahul Tanwar module_exit(exit_lqasc);
951ad406341SRahul Tanwar 
952ad406341SRahul Tanwar MODULE_DESCRIPTION("Serial driver for Lantiq & Intel gateway SoCs");
953ad406341SRahul Tanwar MODULE_LICENSE("GPL v2");
954