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(<q_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(<q_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(<q_port->lock, flags);
240d3a28a53SHauke Mehrtens __raw_writel(ASC_IRNCR_TIR, port->membase + LTQ_ASC_IRNCR);
2414b967e63SRahul Tanwar spin_unlock_irqrestore(<q_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(<q_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(<q_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(<q_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(<q_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(<q_port->lock, flags);
284b832776bSRahul Tanwar stat = readl(port->membase + LTQ_ASC_IRNCR);
285b832776bSRahul Tanwar spin_unlock_irqrestore(<q_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(<q_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(<q_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(<q_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(<q_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(<q_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(<q_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(<q_port->lock, flags);
612ec84aa0aSMartin Blumenstingl lqasc_serial_port_write(<q_port->port, s, count);
6134b967e63SRahul Tanwar spin_unlock_irqrestore(<q_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 = <q_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 = <q_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 = <q_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 = <q_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(<q_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