109864c1cSStefan-gabriel Mirea // SPDX-License-Identifier: GPL-2.0-or-later 209864c1cSStefan-gabriel Mirea /* 39905f32aSStefan-Gabriel Mirea * Freescale LINFlexD UART serial port driver 409864c1cSStefan-gabriel Mirea * 509864c1cSStefan-gabriel Mirea * Copyright 2012-2016 Freescale Semiconductor, Inc. 690500797SStefan-gabriel Mirea * Copyright 2017-2019 NXP 709864c1cSStefan-gabriel Mirea */ 809864c1cSStefan-gabriel Mirea 909864c1cSStefan-gabriel Mirea #include <linux/console.h> 1009864c1cSStefan-gabriel Mirea #include <linux/io.h> 1109864c1cSStefan-gabriel Mirea #include <linux/irq.h> 1209864c1cSStefan-gabriel Mirea #include <linux/module.h> 1309864c1cSStefan-gabriel Mirea #include <linux/of.h> 1409864c1cSStefan-gabriel Mirea #include <linux/of_device.h> 1509864c1cSStefan-gabriel Mirea #include <linux/serial_core.h> 1609864c1cSStefan-gabriel Mirea #include <linux/slab.h> 1709864c1cSStefan-gabriel Mirea #include <linux/tty_flip.h> 1809864c1cSStefan-gabriel Mirea #include <linux/delay.h> 1909864c1cSStefan-gabriel Mirea 2009864c1cSStefan-gabriel Mirea /* All registers are 32-bit width */ 2109864c1cSStefan-gabriel Mirea 2209864c1cSStefan-gabriel Mirea #define LINCR1 0x0000 /* LIN control register */ 2309864c1cSStefan-gabriel Mirea #define LINIER 0x0004 /* LIN interrupt enable register */ 2409864c1cSStefan-gabriel Mirea #define LINSR 0x0008 /* LIN status register */ 2509864c1cSStefan-gabriel Mirea #define LINESR 0x000C /* LIN error status register */ 2609864c1cSStefan-gabriel Mirea #define UARTCR 0x0010 /* UART mode control register */ 2709864c1cSStefan-gabriel Mirea #define UARTSR 0x0014 /* UART mode status register */ 2809864c1cSStefan-gabriel Mirea #define LINTCSR 0x0018 /* LIN timeout control status register */ 2909864c1cSStefan-gabriel Mirea #define LINOCR 0x001C /* LIN output compare register */ 3009864c1cSStefan-gabriel Mirea #define LINTOCR 0x0020 /* LIN timeout control register */ 3109864c1cSStefan-gabriel Mirea #define LINFBRR 0x0024 /* LIN fractional baud rate register */ 3209864c1cSStefan-gabriel Mirea #define LINIBRR 0x0028 /* LIN integer baud rate register */ 3309864c1cSStefan-gabriel Mirea #define LINCFR 0x002C /* LIN checksum field register */ 3409864c1cSStefan-gabriel Mirea #define LINCR2 0x0030 /* LIN control register 2 */ 3509864c1cSStefan-gabriel Mirea #define BIDR 0x0034 /* Buffer identifier register */ 3609864c1cSStefan-gabriel Mirea #define BDRL 0x0038 /* Buffer data register least significant */ 3709864c1cSStefan-gabriel Mirea #define BDRM 0x003C /* Buffer data register most significant */ 3809864c1cSStefan-gabriel Mirea #define IFER 0x0040 /* Identifier filter enable register */ 3909864c1cSStefan-gabriel Mirea #define IFMI 0x0044 /* Identifier filter match index */ 4009864c1cSStefan-gabriel Mirea #define IFMR 0x0048 /* Identifier filter mode register */ 4109864c1cSStefan-gabriel Mirea #define GCR 0x004C /* Global control register */ 4209864c1cSStefan-gabriel Mirea #define UARTPTO 0x0050 /* UART preset timeout register */ 4309864c1cSStefan-gabriel Mirea #define UARTCTO 0x0054 /* UART current timeout register */ 4409864c1cSStefan-gabriel Mirea 4509864c1cSStefan-gabriel Mirea /* 4609864c1cSStefan-gabriel Mirea * Register field definitions 4709864c1cSStefan-gabriel Mirea */ 4809864c1cSStefan-gabriel Mirea 4909864c1cSStefan-gabriel Mirea #define LINFLEXD_LINCR1_INIT BIT(0) 5009864c1cSStefan-gabriel Mirea #define LINFLEXD_LINCR1_MME BIT(4) 5109864c1cSStefan-gabriel Mirea #define LINFLEXD_LINCR1_BF BIT(7) 5209864c1cSStefan-gabriel Mirea 5309864c1cSStefan-gabriel Mirea #define LINFLEXD_LINSR_LINS_INITMODE BIT(12) 5409864c1cSStefan-gabriel Mirea #define LINFLEXD_LINSR_LINS_MASK (0xF << 12) 5509864c1cSStefan-gabriel Mirea 5609864c1cSStefan-gabriel Mirea #define LINFLEXD_LINIER_SZIE BIT(15) 5709864c1cSStefan-gabriel Mirea #define LINFLEXD_LINIER_OCIE BIT(14) 5809864c1cSStefan-gabriel Mirea #define LINFLEXD_LINIER_BEIE BIT(13) 5909864c1cSStefan-gabriel Mirea #define LINFLEXD_LINIER_CEIE BIT(12) 6009864c1cSStefan-gabriel Mirea #define LINFLEXD_LINIER_HEIE BIT(11) 6109864c1cSStefan-gabriel Mirea #define LINFLEXD_LINIER_FEIE BIT(8) 6209864c1cSStefan-gabriel Mirea #define LINFLEXD_LINIER_BOIE BIT(7) 6309864c1cSStefan-gabriel Mirea #define LINFLEXD_LINIER_LSIE BIT(6) 6409864c1cSStefan-gabriel Mirea #define LINFLEXD_LINIER_WUIE BIT(5) 6509864c1cSStefan-gabriel Mirea #define LINFLEXD_LINIER_DBFIE BIT(4) 6609864c1cSStefan-gabriel Mirea #define LINFLEXD_LINIER_DBEIETOIE BIT(3) 6709864c1cSStefan-gabriel Mirea #define LINFLEXD_LINIER_DRIE BIT(2) 6809864c1cSStefan-gabriel Mirea #define LINFLEXD_LINIER_DTIE BIT(1) 6909864c1cSStefan-gabriel Mirea #define LINFLEXD_LINIER_HRIE BIT(0) 7009864c1cSStefan-gabriel Mirea 7109864c1cSStefan-gabriel Mirea #define LINFLEXD_UARTCR_OSR_MASK (0xF << 24) 7209864c1cSStefan-gabriel Mirea #define LINFLEXD_UARTCR_OSR(uartcr) (((uartcr) \ 7309864c1cSStefan-gabriel Mirea & LINFLEXD_UARTCR_OSR_MASK) >> 24) 7409864c1cSStefan-gabriel Mirea 7509864c1cSStefan-gabriel Mirea #define LINFLEXD_UARTCR_ROSE BIT(23) 7609864c1cSStefan-gabriel Mirea 7709864c1cSStefan-gabriel Mirea #define LINFLEXD_UARTCR_RFBM BIT(9) 7809864c1cSStefan-gabriel Mirea #define LINFLEXD_UARTCR_TFBM BIT(8) 7909864c1cSStefan-gabriel Mirea #define LINFLEXD_UARTCR_WL1 BIT(7) 8009864c1cSStefan-gabriel Mirea #define LINFLEXD_UARTCR_PC1 BIT(6) 8109864c1cSStefan-gabriel Mirea 8209864c1cSStefan-gabriel Mirea #define LINFLEXD_UARTCR_RXEN BIT(5) 8309864c1cSStefan-gabriel Mirea #define LINFLEXD_UARTCR_TXEN BIT(4) 8409864c1cSStefan-gabriel Mirea #define LINFLEXD_UARTCR_PC0 BIT(3) 8509864c1cSStefan-gabriel Mirea 8609864c1cSStefan-gabriel Mirea #define LINFLEXD_UARTCR_PCE BIT(2) 8709864c1cSStefan-gabriel Mirea #define LINFLEXD_UARTCR_WL0 BIT(1) 8809864c1cSStefan-gabriel Mirea #define LINFLEXD_UARTCR_UART BIT(0) 8909864c1cSStefan-gabriel Mirea 9009864c1cSStefan-gabriel Mirea #define LINFLEXD_UARTSR_SZF BIT(15) 9109864c1cSStefan-gabriel Mirea #define LINFLEXD_UARTSR_OCF BIT(14) 9209864c1cSStefan-gabriel Mirea #define LINFLEXD_UARTSR_PE3 BIT(13) 9309864c1cSStefan-gabriel Mirea #define LINFLEXD_UARTSR_PE2 BIT(12) 9409864c1cSStefan-gabriel Mirea #define LINFLEXD_UARTSR_PE1 BIT(11) 9509864c1cSStefan-gabriel Mirea #define LINFLEXD_UARTSR_PE0 BIT(10) 9609864c1cSStefan-gabriel Mirea #define LINFLEXD_UARTSR_RMB BIT(9) 9709864c1cSStefan-gabriel Mirea #define LINFLEXD_UARTSR_FEF BIT(8) 9809864c1cSStefan-gabriel Mirea #define LINFLEXD_UARTSR_BOF BIT(7) 9909864c1cSStefan-gabriel Mirea #define LINFLEXD_UARTSR_RPS BIT(6) 10009864c1cSStefan-gabriel Mirea #define LINFLEXD_UARTSR_WUF BIT(5) 10109864c1cSStefan-gabriel Mirea #define LINFLEXD_UARTSR_4 BIT(4) 10209864c1cSStefan-gabriel Mirea 10309864c1cSStefan-gabriel Mirea #define LINFLEXD_UARTSR_TO BIT(3) 10409864c1cSStefan-gabriel Mirea 10509864c1cSStefan-gabriel Mirea #define LINFLEXD_UARTSR_DRFRFE BIT(2) 10609864c1cSStefan-gabriel Mirea #define LINFLEXD_UARTSR_DTFTFF BIT(1) 10709864c1cSStefan-gabriel Mirea #define LINFLEXD_UARTSR_NF BIT(0) 10809864c1cSStefan-gabriel Mirea #define LINFLEXD_UARTSR_PE (LINFLEXD_UARTSR_PE0 |\ 10909864c1cSStefan-gabriel Mirea LINFLEXD_UARTSR_PE1 |\ 11009864c1cSStefan-gabriel Mirea LINFLEXD_UARTSR_PE2 |\ 11109864c1cSStefan-gabriel Mirea LINFLEXD_UARTSR_PE3) 11209864c1cSStefan-gabriel Mirea 11309864c1cSStefan-gabriel Mirea #define LINFLEX_LDIV_MULTIPLIER (16) 11409864c1cSStefan-gabriel Mirea 11509864c1cSStefan-gabriel Mirea #define DRIVER_NAME "fsl-linflexuart" 11609864c1cSStefan-gabriel Mirea #define DEV_NAME "ttyLF" 11709864c1cSStefan-gabriel Mirea #define UART_NR 4 11809864c1cSStefan-gabriel Mirea 11909864c1cSStefan-gabriel Mirea #define EARLYCON_BUFFER_INITIAL_CAP 8 12009864c1cSStefan-gabriel Mirea 12109864c1cSStefan-gabriel Mirea #define PREINIT_DELAY 2000 /* us */ 12209864c1cSStefan-gabriel Mirea 12309864c1cSStefan-gabriel Mirea static const struct of_device_id linflex_dt_ids[] = { 12409864c1cSStefan-gabriel Mirea { 1252bd3661eSStefan-gabriel Mirea .compatible = "fsl,s32v234-linflexuart", 12609864c1cSStefan-gabriel Mirea }, 12709864c1cSStefan-gabriel Mirea { /* sentinel */ } 12809864c1cSStefan-gabriel Mirea }; 12909864c1cSStefan-gabriel Mirea MODULE_DEVICE_TABLE(of, linflex_dt_ids); 13009864c1cSStefan-gabriel Mirea 13109864c1cSStefan-gabriel Mirea #ifdef CONFIG_SERIAL_FSL_LINFLEXUART_CONSOLE 13209864c1cSStefan-gabriel Mirea static struct uart_port *earlycon_port; 13309864c1cSStefan-gabriel Mirea static bool linflex_earlycon_same_instance; 13406e9b2feSWei Yongjun static DEFINE_SPINLOCK(init_lock); 13509864c1cSStefan-gabriel Mirea static bool during_init; 13609864c1cSStefan-gabriel Mirea 13709864c1cSStefan-gabriel Mirea static struct { 13809864c1cSStefan-gabriel Mirea char *content; 13909864c1cSStefan-gabriel Mirea unsigned int len, cap; 14009864c1cSStefan-gabriel Mirea } earlycon_buf; 14109864c1cSStefan-gabriel Mirea #endif 14209864c1cSStefan-gabriel Mirea 14309864c1cSStefan-gabriel Mirea static void linflex_stop_tx(struct uart_port *port) 14409864c1cSStefan-gabriel Mirea { 14509864c1cSStefan-gabriel Mirea unsigned long ier; 14609864c1cSStefan-gabriel Mirea 14709864c1cSStefan-gabriel Mirea ier = readl(port->membase + LINIER); 14809864c1cSStefan-gabriel Mirea ier &= ~(LINFLEXD_LINIER_DTIE); 14909864c1cSStefan-gabriel Mirea writel(ier, port->membase + LINIER); 15009864c1cSStefan-gabriel Mirea } 15109864c1cSStefan-gabriel Mirea 15209864c1cSStefan-gabriel Mirea static void linflex_stop_rx(struct uart_port *port) 15309864c1cSStefan-gabriel Mirea { 15409864c1cSStefan-gabriel Mirea unsigned long ier; 15509864c1cSStefan-gabriel Mirea 15609864c1cSStefan-gabriel Mirea ier = readl(port->membase + LINIER); 15709864c1cSStefan-gabriel Mirea writel(ier & ~LINFLEXD_LINIER_DRIE, port->membase + LINIER); 15809864c1cSStefan-gabriel Mirea } 15909864c1cSStefan-gabriel Mirea 160*7b9528c2SJiri Slaby static void linflex_put_char(struct uart_port *sport, unsigned char c) 16109864c1cSStefan-gabriel Mirea { 16209864c1cSStefan-gabriel Mirea unsigned long status; 16309864c1cSStefan-gabriel Mirea 16409864c1cSStefan-gabriel Mirea writeb(c, sport->membase + BDRL); 16509864c1cSStefan-gabriel Mirea 16609864c1cSStefan-gabriel Mirea /* Waiting for data transmission completed. */ 16709864c1cSStefan-gabriel Mirea while (((status = readl(sport->membase + UARTSR)) & 16809864c1cSStefan-gabriel Mirea LINFLEXD_UARTSR_DTFTFF) != 16909864c1cSStefan-gabriel Mirea LINFLEXD_UARTSR_DTFTFF) 17009864c1cSStefan-gabriel Mirea ; 17109864c1cSStefan-gabriel Mirea 172*7b9528c2SJiri Slaby writel(status | LINFLEXD_UARTSR_DTFTFF, sport->membase + UARTSR); 173*7b9528c2SJiri Slaby } 174*7b9528c2SJiri Slaby 175*7b9528c2SJiri Slaby static inline void linflex_transmit_buffer(struct uart_port *sport) 176*7b9528c2SJiri Slaby { 177*7b9528c2SJiri Slaby struct circ_buf *xmit = &sport->state->xmit; 178*7b9528c2SJiri Slaby 179*7b9528c2SJiri Slaby while (!uart_circ_empty(xmit)) { 180*7b9528c2SJiri Slaby linflex_put_char(sport, xmit->buf[xmit->tail]); 18109864c1cSStefan-gabriel Mirea xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); 18209864c1cSStefan-gabriel Mirea sport->icount.tx++; 18309864c1cSStefan-gabriel Mirea } 18409864c1cSStefan-gabriel Mirea 18509864c1cSStefan-gabriel Mirea if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) 18609864c1cSStefan-gabriel Mirea uart_write_wakeup(sport); 18709864c1cSStefan-gabriel Mirea 18809864c1cSStefan-gabriel Mirea if (uart_circ_empty(xmit)) 18909864c1cSStefan-gabriel Mirea linflex_stop_tx(sport); 19009864c1cSStefan-gabriel Mirea } 19109864c1cSStefan-gabriel Mirea 19209864c1cSStefan-gabriel Mirea static void linflex_start_tx(struct uart_port *port) 19309864c1cSStefan-gabriel Mirea { 19409864c1cSStefan-gabriel Mirea unsigned long ier; 19509864c1cSStefan-gabriel Mirea 19609864c1cSStefan-gabriel Mirea linflex_transmit_buffer(port); 19709864c1cSStefan-gabriel Mirea ier = readl(port->membase + LINIER); 19809864c1cSStefan-gabriel Mirea writel(ier | LINFLEXD_LINIER_DTIE, port->membase + LINIER); 19909864c1cSStefan-gabriel Mirea } 20009864c1cSStefan-gabriel Mirea 20109864c1cSStefan-gabriel Mirea static irqreturn_t linflex_txint(int irq, void *dev_id) 20209864c1cSStefan-gabriel Mirea { 20309864c1cSStefan-gabriel Mirea struct uart_port *sport = dev_id; 20409864c1cSStefan-gabriel Mirea struct circ_buf *xmit = &sport->state->xmit; 20509864c1cSStefan-gabriel Mirea unsigned long flags; 20609864c1cSStefan-gabriel Mirea 20709864c1cSStefan-gabriel Mirea spin_lock_irqsave(&sport->lock, flags); 20809864c1cSStefan-gabriel Mirea 20909864c1cSStefan-gabriel Mirea if (sport->x_char) { 210*7b9528c2SJiri Slaby linflex_put_char(sport, sport->x_char); 21109864c1cSStefan-gabriel Mirea goto out; 21209864c1cSStefan-gabriel Mirea } 21309864c1cSStefan-gabriel Mirea 21409864c1cSStefan-gabriel Mirea if (uart_circ_empty(xmit) || uart_tx_stopped(sport)) { 21509864c1cSStefan-gabriel Mirea linflex_stop_tx(sport); 21609864c1cSStefan-gabriel Mirea goto out; 21709864c1cSStefan-gabriel Mirea } 21809864c1cSStefan-gabriel Mirea 21909864c1cSStefan-gabriel Mirea linflex_transmit_buffer(sport); 22009864c1cSStefan-gabriel Mirea 22109864c1cSStefan-gabriel Mirea if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) 22209864c1cSStefan-gabriel Mirea uart_write_wakeup(sport); 22309864c1cSStefan-gabriel Mirea 22409864c1cSStefan-gabriel Mirea out: 22509864c1cSStefan-gabriel Mirea spin_unlock_irqrestore(&sport->lock, flags); 22609864c1cSStefan-gabriel Mirea return IRQ_HANDLED; 22709864c1cSStefan-gabriel Mirea } 22809864c1cSStefan-gabriel Mirea 22909864c1cSStefan-gabriel Mirea static irqreturn_t linflex_rxint(int irq, void *dev_id) 23009864c1cSStefan-gabriel Mirea { 23109864c1cSStefan-gabriel Mirea struct uart_port *sport = dev_id; 23209864c1cSStefan-gabriel Mirea unsigned int flg; 23309864c1cSStefan-gabriel Mirea struct tty_port *port = &sport->state->port; 23409864c1cSStefan-gabriel Mirea unsigned long flags, status; 23509864c1cSStefan-gabriel Mirea unsigned char rx; 23690500797SStefan-gabriel Mirea bool brk; 23709864c1cSStefan-gabriel Mirea 23809864c1cSStefan-gabriel Mirea spin_lock_irqsave(&sport->lock, flags); 23909864c1cSStefan-gabriel Mirea 24009864c1cSStefan-gabriel Mirea status = readl(sport->membase + UARTSR); 24109864c1cSStefan-gabriel Mirea while (status & LINFLEXD_UARTSR_RMB) { 24209864c1cSStefan-gabriel Mirea rx = readb(sport->membase + BDRM); 24390500797SStefan-gabriel Mirea brk = false; 24409864c1cSStefan-gabriel Mirea flg = TTY_NORMAL; 24509864c1cSStefan-gabriel Mirea sport->icount.rx++; 24609864c1cSStefan-gabriel Mirea 24780f510f5SLiu Xiang if (status & (LINFLEXD_UARTSR_BOF | LINFLEXD_UARTSR_FEF | 24880f510f5SLiu Xiang LINFLEXD_UARTSR_PE)) { 24909864c1cSStefan-gabriel Mirea if (status & LINFLEXD_UARTSR_BOF) 25080f510f5SLiu Xiang sport->icount.overrun++; 25190500797SStefan-gabriel Mirea if (status & LINFLEXD_UARTSR_FEF) { 25280f510f5SLiu Xiang if (!rx) { 25390500797SStefan-gabriel Mirea brk = true; 25480f510f5SLiu Xiang sport->icount.brk++; 25580f510f5SLiu Xiang } else 25680f510f5SLiu Xiang sport->icount.frame++; 25790500797SStefan-gabriel Mirea } 25809864c1cSStefan-gabriel Mirea if (status & LINFLEXD_UARTSR_PE) 25980f510f5SLiu Xiang sport->icount.parity++; 26009864c1cSStefan-gabriel Mirea } 26109864c1cSStefan-gabriel Mirea 26280f510f5SLiu Xiang writel(status, sport->membase + UARTSR); 26309864c1cSStefan-gabriel Mirea status = readl(sport->membase + UARTSR); 26409864c1cSStefan-gabriel Mirea 26590500797SStefan-gabriel Mirea if (brk) { 26690500797SStefan-gabriel Mirea uart_handle_break(sport); 26790500797SStefan-gabriel Mirea } else { 26809864c1cSStefan-gabriel Mirea if (uart_handle_sysrq_char(sport, (unsigned char)rx)) 26909864c1cSStefan-gabriel Mirea continue; 27009864c1cSStefan-gabriel Mirea tty_insert_flip_char(port, rx, flg); 27109864c1cSStefan-gabriel Mirea } 27290500797SStefan-gabriel Mirea } 27309864c1cSStefan-gabriel Mirea 27409864c1cSStefan-gabriel Mirea spin_unlock_irqrestore(&sport->lock, flags); 27509864c1cSStefan-gabriel Mirea 27609864c1cSStefan-gabriel Mirea tty_flip_buffer_push(port); 27709864c1cSStefan-gabriel Mirea 27809864c1cSStefan-gabriel Mirea return IRQ_HANDLED; 27909864c1cSStefan-gabriel Mirea } 28009864c1cSStefan-gabriel Mirea 28109864c1cSStefan-gabriel Mirea static irqreturn_t linflex_int(int irq, void *dev_id) 28209864c1cSStefan-gabriel Mirea { 28309864c1cSStefan-gabriel Mirea struct uart_port *sport = dev_id; 28409864c1cSStefan-gabriel Mirea unsigned long status; 28509864c1cSStefan-gabriel Mirea 28609864c1cSStefan-gabriel Mirea status = readl(sport->membase + UARTSR); 28709864c1cSStefan-gabriel Mirea 28809864c1cSStefan-gabriel Mirea if (status & LINFLEXD_UARTSR_DRFRFE) 28909864c1cSStefan-gabriel Mirea linflex_rxint(irq, dev_id); 29009864c1cSStefan-gabriel Mirea if (status & LINFLEXD_UARTSR_DTFTFF) 29109864c1cSStefan-gabriel Mirea linflex_txint(irq, dev_id); 29209864c1cSStefan-gabriel Mirea 29309864c1cSStefan-gabriel Mirea return IRQ_HANDLED; 29409864c1cSStefan-gabriel Mirea } 29509864c1cSStefan-gabriel Mirea 29609864c1cSStefan-gabriel Mirea /* return TIOCSER_TEMT when transmitter is not busy */ 29709864c1cSStefan-gabriel Mirea static unsigned int linflex_tx_empty(struct uart_port *port) 29809864c1cSStefan-gabriel Mirea { 29909864c1cSStefan-gabriel Mirea unsigned long status; 30009864c1cSStefan-gabriel Mirea 30109864c1cSStefan-gabriel Mirea status = readl(port->membase + UARTSR) & LINFLEXD_UARTSR_DTFTFF; 30209864c1cSStefan-gabriel Mirea 30309864c1cSStefan-gabriel Mirea return status ? TIOCSER_TEMT : 0; 30409864c1cSStefan-gabriel Mirea } 30509864c1cSStefan-gabriel Mirea 30609864c1cSStefan-gabriel Mirea static unsigned int linflex_get_mctrl(struct uart_port *port) 30709864c1cSStefan-gabriel Mirea { 30809864c1cSStefan-gabriel Mirea return 0; 30909864c1cSStefan-gabriel Mirea } 31009864c1cSStefan-gabriel Mirea 31109864c1cSStefan-gabriel Mirea static void linflex_set_mctrl(struct uart_port *port, unsigned int mctrl) 31209864c1cSStefan-gabriel Mirea { 31309864c1cSStefan-gabriel Mirea } 31409864c1cSStefan-gabriel Mirea 31509864c1cSStefan-gabriel Mirea static void linflex_break_ctl(struct uart_port *port, int break_state) 31609864c1cSStefan-gabriel Mirea { 31709864c1cSStefan-gabriel Mirea } 31809864c1cSStefan-gabriel Mirea 31909864c1cSStefan-gabriel Mirea static void linflex_setup_watermark(struct uart_port *sport) 32009864c1cSStefan-gabriel Mirea { 32109864c1cSStefan-gabriel Mirea unsigned long cr, ier, cr1; 32209864c1cSStefan-gabriel Mirea 32309864c1cSStefan-gabriel Mirea /* Disable transmission/reception */ 32409864c1cSStefan-gabriel Mirea ier = readl(sport->membase + LINIER); 32509864c1cSStefan-gabriel Mirea ier &= ~(LINFLEXD_LINIER_DRIE | LINFLEXD_LINIER_DTIE); 32609864c1cSStefan-gabriel Mirea writel(ier, sport->membase + LINIER); 32709864c1cSStefan-gabriel Mirea 32809864c1cSStefan-gabriel Mirea cr = readl(sport->membase + UARTCR); 32909864c1cSStefan-gabriel Mirea cr &= ~(LINFLEXD_UARTCR_RXEN | LINFLEXD_UARTCR_TXEN); 33009864c1cSStefan-gabriel Mirea writel(cr, sport->membase + UARTCR); 33109864c1cSStefan-gabriel Mirea 33209864c1cSStefan-gabriel Mirea /* Enter initialization mode by setting INIT bit */ 33309864c1cSStefan-gabriel Mirea 33409864c1cSStefan-gabriel Mirea /* set the Linflex in master mode and activate by-pass filter */ 33509864c1cSStefan-gabriel Mirea cr1 = LINFLEXD_LINCR1_BF | LINFLEXD_LINCR1_MME 33609864c1cSStefan-gabriel Mirea | LINFLEXD_LINCR1_INIT; 33709864c1cSStefan-gabriel Mirea writel(cr1, sport->membase + LINCR1); 33809864c1cSStefan-gabriel Mirea 33909864c1cSStefan-gabriel Mirea /* wait for init mode entry */ 34009864c1cSStefan-gabriel Mirea while ((readl(sport->membase + LINSR) 34109864c1cSStefan-gabriel Mirea & LINFLEXD_LINSR_LINS_MASK) 34209864c1cSStefan-gabriel Mirea != LINFLEXD_LINSR_LINS_INITMODE) 34309864c1cSStefan-gabriel Mirea ; 34409864c1cSStefan-gabriel Mirea 34509864c1cSStefan-gabriel Mirea /* 34609864c1cSStefan-gabriel Mirea * UART = 0x1; - Linflex working in UART mode 34709864c1cSStefan-gabriel Mirea * TXEN = 0x1; - Enable transmission of data now 34809864c1cSStefan-gabriel Mirea * RXEn = 0x1; - Receiver enabled 34909864c1cSStefan-gabriel Mirea * WL0 = 0x1; - 8 bit data 35009864c1cSStefan-gabriel Mirea * PCE = 0x0; - No parity 35109864c1cSStefan-gabriel Mirea */ 35209864c1cSStefan-gabriel Mirea 35309864c1cSStefan-gabriel Mirea /* set UART bit to allow writing other bits */ 35409864c1cSStefan-gabriel Mirea writel(LINFLEXD_UARTCR_UART, sport->membase + UARTCR); 35509864c1cSStefan-gabriel Mirea 35609864c1cSStefan-gabriel Mirea cr = (LINFLEXD_UARTCR_RXEN | LINFLEXD_UARTCR_TXEN | 35709864c1cSStefan-gabriel Mirea LINFLEXD_UARTCR_WL0 | LINFLEXD_UARTCR_UART); 35809864c1cSStefan-gabriel Mirea 35909864c1cSStefan-gabriel Mirea writel(cr, sport->membase + UARTCR); 36009864c1cSStefan-gabriel Mirea 36109864c1cSStefan-gabriel Mirea cr1 &= ~(LINFLEXD_LINCR1_INIT); 36209864c1cSStefan-gabriel Mirea 36309864c1cSStefan-gabriel Mirea writel(cr1, sport->membase + LINCR1); 36409864c1cSStefan-gabriel Mirea 36509864c1cSStefan-gabriel Mirea ier = readl(sport->membase + LINIER); 36609864c1cSStefan-gabriel Mirea ier |= LINFLEXD_LINIER_DRIE; 36709864c1cSStefan-gabriel Mirea ier |= LINFLEXD_LINIER_DTIE; 36809864c1cSStefan-gabriel Mirea 36909864c1cSStefan-gabriel Mirea writel(ier, sport->membase + LINIER); 37009864c1cSStefan-gabriel Mirea } 37109864c1cSStefan-gabriel Mirea 37209864c1cSStefan-gabriel Mirea static int linflex_startup(struct uart_port *port) 37309864c1cSStefan-gabriel Mirea { 37409864c1cSStefan-gabriel Mirea int ret = 0; 37509864c1cSStefan-gabriel Mirea unsigned long flags; 37609864c1cSStefan-gabriel Mirea 37709864c1cSStefan-gabriel Mirea spin_lock_irqsave(&port->lock, flags); 37809864c1cSStefan-gabriel Mirea 37909864c1cSStefan-gabriel Mirea linflex_setup_watermark(port); 38009864c1cSStefan-gabriel Mirea 38109864c1cSStefan-gabriel Mirea spin_unlock_irqrestore(&port->lock, flags); 38209864c1cSStefan-gabriel Mirea 38309864c1cSStefan-gabriel Mirea ret = devm_request_irq(port->dev, port->irq, linflex_int, 0, 38409864c1cSStefan-gabriel Mirea DRIVER_NAME, port); 38509864c1cSStefan-gabriel Mirea 38609864c1cSStefan-gabriel Mirea return ret; 38709864c1cSStefan-gabriel Mirea } 38809864c1cSStefan-gabriel Mirea 38909864c1cSStefan-gabriel Mirea static void linflex_shutdown(struct uart_port *port) 39009864c1cSStefan-gabriel Mirea { 39109864c1cSStefan-gabriel Mirea unsigned long ier; 39209864c1cSStefan-gabriel Mirea unsigned long flags; 39309864c1cSStefan-gabriel Mirea 39409864c1cSStefan-gabriel Mirea spin_lock_irqsave(&port->lock, flags); 39509864c1cSStefan-gabriel Mirea 39609864c1cSStefan-gabriel Mirea /* disable interrupts */ 39709864c1cSStefan-gabriel Mirea ier = readl(port->membase + LINIER); 39809864c1cSStefan-gabriel Mirea ier &= ~(LINFLEXD_LINIER_DRIE | LINFLEXD_LINIER_DTIE); 39909864c1cSStefan-gabriel Mirea writel(ier, port->membase + LINIER); 40009864c1cSStefan-gabriel Mirea 40109864c1cSStefan-gabriel Mirea spin_unlock_irqrestore(&port->lock, flags); 40209864c1cSStefan-gabriel Mirea 40309864c1cSStefan-gabriel Mirea devm_free_irq(port->dev, port->irq, port); 40409864c1cSStefan-gabriel Mirea } 40509864c1cSStefan-gabriel Mirea 40609864c1cSStefan-gabriel Mirea static void 40709864c1cSStefan-gabriel Mirea linflex_set_termios(struct uart_port *port, struct ktermios *termios, 40809864c1cSStefan-gabriel Mirea struct ktermios *old) 40909864c1cSStefan-gabriel Mirea { 41009864c1cSStefan-gabriel Mirea unsigned long flags; 41109864c1cSStefan-gabriel Mirea unsigned long cr, old_cr, cr1; 41209864c1cSStefan-gabriel Mirea unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8; 41309864c1cSStefan-gabriel Mirea 41409864c1cSStefan-gabriel Mirea cr = readl(port->membase + UARTCR); 41509864c1cSStefan-gabriel Mirea old_cr = cr; 41609864c1cSStefan-gabriel Mirea 41709864c1cSStefan-gabriel Mirea /* Enter initialization mode by setting INIT bit */ 41809864c1cSStefan-gabriel Mirea cr1 = readl(port->membase + LINCR1); 41909864c1cSStefan-gabriel Mirea cr1 |= LINFLEXD_LINCR1_INIT; 42009864c1cSStefan-gabriel Mirea writel(cr1, port->membase + LINCR1); 42109864c1cSStefan-gabriel Mirea 42209864c1cSStefan-gabriel Mirea /* wait for init mode entry */ 42309864c1cSStefan-gabriel Mirea while ((readl(port->membase + LINSR) 42409864c1cSStefan-gabriel Mirea & LINFLEXD_LINSR_LINS_MASK) 42509864c1cSStefan-gabriel Mirea != LINFLEXD_LINSR_LINS_INITMODE) 42609864c1cSStefan-gabriel Mirea ; 42709864c1cSStefan-gabriel Mirea 42809864c1cSStefan-gabriel Mirea /* 42909864c1cSStefan-gabriel Mirea * only support CS8 and CS7, and for CS7 must enable PE. 43009864c1cSStefan-gabriel Mirea * supported mode: 43109864c1cSStefan-gabriel Mirea * - (7,e/o,1) 43209864c1cSStefan-gabriel Mirea * - (8,n,1) 43309864c1cSStefan-gabriel Mirea * - (8,e/o,1) 43409864c1cSStefan-gabriel Mirea */ 43509864c1cSStefan-gabriel Mirea /* enter the UART into configuration mode */ 43609864c1cSStefan-gabriel Mirea 43709864c1cSStefan-gabriel Mirea while ((termios->c_cflag & CSIZE) != CS8 && 43809864c1cSStefan-gabriel Mirea (termios->c_cflag & CSIZE) != CS7) { 43909864c1cSStefan-gabriel Mirea termios->c_cflag &= ~CSIZE; 44009864c1cSStefan-gabriel Mirea termios->c_cflag |= old_csize; 44109864c1cSStefan-gabriel Mirea old_csize = CS8; 44209864c1cSStefan-gabriel Mirea } 44309864c1cSStefan-gabriel Mirea 44409864c1cSStefan-gabriel Mirea if ((termios->c_cflag & CSIZE) == CS7) { 44509864c1cSStefan-gabriel Mirea /* Word length: WL1WL0:00 */ 44609864c1cSStefan-gabriel Mirea cr = old_cr & ~LINFLEXD_UARTCR_WL1 & ~LINFLEXD_UARTCR_WL0; 44709864c1cSStefan-gabriel Mirea } 44809864c1cSStefan-gabriel Mirea 44909864c1cSStefan-gabriel Mirea if ((termios->c_cflag & CSIZE) == CS8) { 45009864c1cSStefan-gabriel Mirea /* Word length: WL1WL0:01 */ 45109864c1cSStefan-gabriel Mirea cr = (old_cr | LINFLEXD_UARTCR_WL0) & ~LINFLEXD_UARTCR_WL1; 45209864c1cSStefan-gabriel Mirea } 45309864c1cSStefan-gabriel Mirea 45409864c1cSStefan-gabriel Mirea if (termios->c_cflag & CMSPAR) { 45509864c1cSStefan-gabriel Mirea if ((termios->c_cflag & CSIZE) != CS8) { 45609864c1cSStefan-gabriel Mirea termios->c_cflag &= ~CSIZE; 45709864c1cSStefan-gabriel Mirea termios->c_cflag |= CS8; 45809864c1cSStefan-gabriel Mirea } 45909864c1cSStefan-gabriel Mirea /* has a space/sticky bit */ 46009864c1cSStefan-gabriel Mirea cr |= LINFLEXD_UARTCR_WL0; 46109864c1cSStefan-gabriel Mirea } 46209864c1cSStefan-gabriel Mirea 46309864c1cSStefan-gabriel Mirea if (termios->c_cflag & CSTOPB) 46409864c1cSStefan-gabriel Mirea termios->c_cflag &= ~CSTOPB; 46509864c1cSStefan-gabriel Mirea 46609864c1cSStefan-gabriel Mirea /* parity must be enabled when CS7 to match 8-bits format */ 46709864c1cSStefan-gabriel Mirea if ((termios->c_cflag & CSIZE) == CS7) 46809864c1cSStefan-gabriel Mirea termios->c_cflag |= PARENB; 46909864c1cSStefan-gabriel Mirea 47009864c1cSStefan-gabriel Mirea if ((termios->c_cflag & PARENB)) { 47109864c1cSStefan-gabriel Mirea cr |= LINFLEXD_UARTCR_PCE; 47209864c1cSStefan-gabriel Mirea if (termios->c_cflag & PARODD) 47309864c1cSStefan-gabriel Mirea cr = (cr | LINFLEXD_UARTCR_PC0) & 47409864c1cSStefan-gabriel Mirea (~LINFLEXD_UARTCR_PC1); 47509864c1cSStefan-gabriel Mirea else 47609864c1cSStefan-gabriel Mirea cr = cr & (~LINFLEXD_UARTCR_PC1 & 47709864c1cSStefan-gabriel Mirea ~LINFLEXD_UARTCR_PC0); 47809864c1cSStefan-gabriel Mirea } else { 47909864c1cSStefan-gabriel Mirea cr &= ~LINFLEXD_UARTCR_PCE; 48009864c1cSStefan-gabriel Mirea } 48109864c1cSStefan-gabriel Mirea 48209864c1cSStefan-gabriel Mirea spin_lock_irqsave(&port->lock, flags); 48309864c1cSStefan-gabriel Mirea 48409864c1cSStefan-gabriel Mirea port->read_status_mask = 0; 48509864c1cSStefan-gabriel Mirea 48609864c1cSStefan-gabriel Mirea if (termios->c_iflag & INPCK) 48709864c1cSStefan-gabriel Mirea port->read_status_mask |= (LINFLEXD_UARTSR_FEF | 48809864c1cSStefan-gabriel Mirea LINFLEXD_UARTSR_PE0 | 48909864c1cSStefan-gabriel Mirea LINFLEXD_UARTSR_PE1 | 49009864c1cSStefan-gabriel Mirea LINFLEXD_UARTSR_PE2 | 49109864c1cSStefan-gabriel Mirea LINFLEXD_UARTSR_PE3); 49209864c1cSStefan-gabriel Mirea if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK)) 49309864c1cSStefan-gabriel Mirea port->read_status_mask |= LINFLEXD_UARTSR_FEF; 49409864c1cSStefan-gabriel Mirea 49509864c1cSStefan-gabriel Mirea /* characters to ignore */ 49609864c1cSStefan-gabriel Mirea port->ignore_status_mask = 0; 49709864c1cSStefan-gabriel Mirea if (termios->c_iflag & IGNPAR) 49809864c1cSStefan-gabriel Mirea port->ignore_status_mask |= LINFLEXD_UARTSR_PE; 49909864c1cSStefan-gabriel Mirea if (termios->c_iflag & IGNBRK) { 50009864c1cSStefan-gabriel Mirea port->ignore_status_mask |= LINFLEXD_UARTSR_PE; 50109864c1cSStefan-gabriel Mirea /* 50209864c1cSStefan-gabriel Mirea * if we're ignoring parity and break indicators, 50309864c1cSStefan-gabriel Mirea * ignore overruns too (for real raw support). 50409864c1cSStefan-gabriel Mirea */ 50509864c1cSStefan-gabriel Mirea if (termios->c_iflag & IGNPAR) 50609864c1cSStefan-gabriel Mirea port->ignore_status_mask |= LINFLEXD_UARTSR_BOF; 50709864c1cSStefan-gabriel Mirea } 50809864c1cSStefan-gabriel Mirea 50909864c1cSStefan-gabriel Mirea writel(cr, port->membase + UARTCR); 51009864c1cSStefan-gabriel Mirea 51109864c1cSStefan-gabriel Mirea cr1 &= ~(LINFLEXD_LINCR1_INIT); 51209864c1cSStefan-gabriel Mirea 51309864c1cSStefan-gabriel Mirea writel(cr1, port->membase + LINCR1); 51409864c1cSStefan-gabriel Mirea 51509864c1cSStefan-gabriel Mirea spin_unlock_irqrestore(&port->lock, flags); 51609864c1cSStefan-gabriel Mirea } 51709864c1cSStefan-gabriel Mirea 51809864c1cSStefan-gabriel Mirea static const char *linflex_type(struct uart_port *port) 51909864c1cSStefan-gabriel Mirea { 52009864c1cSStefan-gabriel Mirea return "FSL_LINFLEX"; 52109864c1cSStefan-gabriel Mirea } 52209864c1cSStefan-gabriel Mirea 52309864c1cSStefan-gabriel Mirea static void linflex_release_port(struct uart_port *port) 52409864c1cSStefan-gabriel Mirea { 52509864c1cSStefan-gabriel Mirea /* nothing to do */ 52609864c1cSStefan-gabriel Mirea } 52709864c1cSStefan-gabriel Mirea 52809864c1cSStefan-gabriel Mirea static int linflex_request_port(struct uart_port *port) 52909864c1cSStefan-gabriel Mirea { 53009864c1cSStefan-gabriel Mirea return 0; 53109864c1cSStefan-gabriel Mirea } 53209864c1cSStefan-gabriel Mirea 53309864c1cSStefan-gabriel Mirea /* configure/auto-configure the port */ 53409864c1cSStefan-gabriel Mirea static void linflex_config_port(struct uart_port *port, int flags) 53509864c1cSStefan-gabriel Mirea { 53609864c1cSStefan-gabriel Mirea if (flags & UART_CONFIG_TYPE) 53709864c1cSStefan-gabriel Mirea port->type = PORT_LINFLEXUART; 53809864c1cSStefan-gabriel Mirea } 53909864c1cSStefan-gabriel Mirea 54009864c1cSStefan-gabriel Mirea static const struct uart_ops linflex_pops = { 54109864c1cSStefan-gabriel Mirea .tx_empty = linflex_tx_empty, 54209864c1cSStefan-gabriel Mirea .set_mctrl = linflex_set_mctrl, 54309864c1cSStefan-gabriel Mirea .get_mctrl = linflex_get_mctrl, 54409864c1cSStefan-gabriel Mirea .stop_tx = linflex_stop_tx, 54509864c1cSStefan-gabriel Mirea .start_tx = linflex_start_tx, 54609864c1cSStefan-gabriel Mirea .stop_rx = linflex_stop_rx, 54709864c1cSStefan-gabriel Mirea .break_ctl = linflex_break_ctl, 54809864c1cSStefan-gabriel Mirea .startup = linflex_startup, 54909864c1cSStefan-gabriel Mirea .shutdown = linflex_shutdown, 55009864c1cSStefan-gabriel Mirea .set_termios = linflex_set_termios, 55109864c1cSStefan-gabriel Mirea .type = linflex_type, 55209864c1cSStefan-gabriel Mirea .request_port = linflex_request_port, 55309864c1cSStefan-gabriel Mirea .release_port = linflex_release_port, 55409864c1cSStefan-gabriel Mirea .config_port = linflex_config_port, 55509864c1cSStefan-gabriel Mirea }; 55609864c1cSStefan-gabriel Mirea 55709864c1cSStefan-gabriel Mirea static struct uart_port *linflex_ports[UART_NR]; 55809864c1cSStefan-gabriel Mirea 55909864c1cSStefan-gabriel Mirea #ifdef CONFIG_SERIAL_FSL_LINFLEXUART_CONSOLE 56009864c1cSStefan-gabriel Mirea static void linflex_console_putchar(struct uart_port *port, int ch) 56109864c1cSStefan-gabriel Mirea { 56209864c1cSStefan-gabriel Mirea unsigned long cr; 56309864c1cSStefan-gabriel Mirea 56409864c1cSStefan-gabriel Mirea cr = readl(port->membase + UARTCR); 56509864c1cSStefan-gabriel Mirea 56609864c1cSStefan-gabriel Mirea writeb(ch, port->membase + BDRL); 56709864c1cSStefan-gabriel Mirea 56809864c1cSStefan-gabriel Mirea if (!(cr & LINFLEXD_UARTCR_TFBM)) 56909864c1cSStefan-gabriel Mirea while ((readl(port->membase + UARTSR) & 57009864c1cSStefan-gabriel Mirea LINFLEXD_UARTSR_DTFTFF) 57109864c1cSStefan-gabriel Mirea != LINFLEXD_UARTSR_DTFTFF) 57209864c1cSStefan-gabriel Mirea ; 57309864c1cSStefan-gabriel Mirea else 57409864c1cSStefan-gabriel Mirea while (readl(port->membase + UARTSR) & 57509864c1cSStefan-gabriel Mirea LINFLEXD_UARTSR_DTFTFF) 57609864c1cSStefan-gabriel Mirea ; 57709864c1cSStefan-gabriel Mirea 57809864c1cSStefan-gabriel Mirea if (!(cr & LINFLEXD_UARTCR_TFBM)) { 57909864c1cSStefan-gabriel Mirea writel((readl(port->membase + UARTSR) | 58009864c1cSStefan-gabriel Mirea LINFLEXD_UARTSR_DTFTFF), 58109864c1cSStefan-gabriel Mirea port->membase + UARTSR); 58209864c1cSStefan-gabriel Mirea } 58309864c1cSStefan-gabriel Mirea } 58409864c1cSStefan-gabriel Mirea 58509864c1cSStefan-gabriel Mirea static void linflex_earlycon_putchar(struct uart_port *port, int ch) 58609864c1cSStefan-gabriel Mirea { 58709864c1cSStefan-gabriel Mirea unsigned long flags; 58809864c1cSStefan-gabriel Mirea char *ret; 58909864c1cSStefan-gabriel Mirea 59009864c1cSStefan-gabriel Mirea if (!linflex_earlycon_same_instance) { 59109864c1cSStefan-gabriel Mirea linflex_console_putchar(port, ch); 59209864c1cSStefan-gabriel Mirea return; 59309864c1cSStefan-gabriel Mirea } 59409864c1cSStefan-gabriel Mirea 59509864c1cSStefan-gabriel Mirea spin_lock_irqsave(&init_lock, flags); 59609864c1cSStefan-gabriel Mirea if (!during_init) 59709864c1cSStefan-gabriel Mirea goto outside_init; 59809864c1cSStefan-gabriel Mirea 59909864c1cSStefan-gabriel Mirea if (earlycon_buf.len >= 1 << CONFIG_LOG_BUF_SHIFT) 60009864c1cSStefan-gabriel Mirea goto init_release; 60109864c1cSStefan-gabriel Mirea 60209864c1cSStefan-gabriel Mirea if (!earlycon_buf.cap) { 60309864c1cSStefan-gabriel Mirea earlycon_buf.content = kmalloc(EARLYCON_BUFFER_INITIAL_CAP, 60409864c1cSStefan-gabriel Mirea GFP_ATOMIC); 60509864c1cSStefan-gabriel Mirea earlycon_buf.cap = earlycon_buf.content ? 60609864c1cSStefan-gabriel Mirea EARLYCON_BUFFER_INITIAL_CAP : 0; 60709864c1cSStefan-gabriel Mirea } else if (earlycon_buf.len == earlycon_buf.cap) { 60809864c1cSStefan-gabriel Mirea ret = krealloc(earlycon_buf.content, earlycon_buf.cap << 1, 60909864c1cSStefan-gabriel Mirea GFP_ATOMIC); 61009864c1cSStefan-gabriel Mirea if (ret) { 61109864c1cSStefan-gabriel Mirea earlycon_buf.content = ret; 61209864c1cSStefan-gabriel Mirea earlycon_buf.cap <<= 1; 61309864c1cSStefan-gabriel Mirea } 61409864c1cSStefan-gabriel Mirea } 61509864c1cSStefan-gabriel Mirea 61609864c1cSStefan-gabriel Mirea if (earlycon_buf.len < earlycon_buf.cap) 61709864c1cSStefan-gabriel Mirea earlycon_buf.content[earlycon_buf.len++] = ch; 61809864c1cSStefan-gabriel Mirea 61909864c1cSStefan-gabriel Mirea goto init_release; 62009864c1cSStefan-gabriel Mirea 62109864c1cSStefan-gabriel Mirea outside_init: 62209864c1cSStefan-gabriel Mirea linflex_console_putchar(port, ch); 62309864c1cSStefan-gabriel Mirea init_release: 62409864c1cSStefan-gabriel Mirea spin_unlock_irqrestore(&init_lock, flags); 62509864c1cSStefan-gabriel Mirea } 62609864c1cSStefan-gabriel Mirea 62709864c1cSStefan-gabriel Mirea static void linflex_string_write(struct uart_port *sport, const char *s, 62809864c1cSStefan-gabriel Mirea unsigned int count) 62909864c1cSStefan-gabriel Mirea { 63009864c1cSStefan-gabriel Mirea unsigned long cr, ier = 0; 63109864c1cSStefan-gabriel Mirea 63209864c1cSStefan-gabriel Mirea ier = readl(sport->membase + LINIER); 63309864c1cSStefan-gabriel Mirea linflex_stop_tx(sport); 63409864c1cSStefan-gabriel Mirea 63509864c1cSStefan-gabriel Mirea cr = readl(sport->membase + UARTCR); 63609864c1cSStefan-gabriel Mirea cr |= (LINFLEXD_UARTCR_TXEN); 63709864c1cSStefan-gabriel Mirea writel(cr, sport->membase + UARTCR); 63809864c1cSStefan-gabriel Mirea 63909864c1cSStefan-gabriel Mirea uart_console_write(sport, s, count, linflex_console_putchar); 64009864c1cSStefan-gabriel Mirea 64109864c1cSStefan-gabriel Mirea writel(ier, sport->membase + LINIER); 64209864c1cSStefan-gabriel Mirea } 64309864c1cSStefan-gabriel Mirea 64409864c1cSStefan-gabriel Mirea static void 64509864c1cSStefan-gabriel Mirea linflex_console_write(struct console *co, const char *s, unsigned int count) 64609864c1cSStefan-gabriel Mirea { 64709864c1cSStefan-gabriel Mirea struct uart_port *sport = linflex_ports[co->index]; 64809864c1cSStefan-gabriel Mirea unsigned long flags; 64909864c1cSStefan-gabriel Mirea int locked = 1; 65009864c1cSStefan-gabriel Mirea 65109864c1cSStefan-gabriel Mirea if (sport->sysrq) 65209864c1cSStefan-gabriel Mirea locked = 0; 65309864c1cSStefan-gabriel Mirea else if (oops_in_progress) 65409864c1cSStefan-gabriel Mirea locked = spin_trylock_irqsave(&sport->lock, flags); 65509864c1cSStefan-gabriel Mirea else 65609864c1cSStefan-gabriel Mirea spin_lock_irqsave(&sport->lock, flags); 65709864c1cSStefan-gabriel Mirea 65809864c1cSStefan-gabriel Mirea linflex_string_write(sport, s, count); 65909864c1cSStefan-gabriel Mirea 66009864c1cSStefan-gabriel Mirea if (locked) 66109864c1cSStefan-gabriel Mirea spin_unlock_irqrestore(&sport->lock, flags); 66209864c1cSStefan-gabriel Mirea } 66309864c1cSStefan-gabriel Mirea 66409864c1cSStefan-gabriel Mirea /* 66509864c1cSStefan-gabriel Mirea * if the port was already initialised (eg, by a boot loader), 66609864c1cSStefan-gabriel Mirea * try to determine the current setup. 66709864c1cSStefan-gabriel Mirea */ 66809864c1cSStefan-gabriel Mirea static void __init 66909864c1cSStefan-gabriel Mirea linflex_console_get_options(struct uart_port *sport, int *parity, int *bits) 67009864c1cSStefan-gabriel Mirea { 67109864c1cSStefan-gabriel Mirea unsigned long cr; 67209864c1cSStefan-gabriel Mirea 67309864c1cSStefan-gabriel Mirea cr = readl(sport->membase + UARTCR); 67409864c1cSStefan-gabriel Mirea cr &= LINFLEXD_UARTCR_RXEN | LINFLEXD_UARTCR_TXEN; 67509864c1cSStefan-gabriel Mirea 67609864c1cSStefan-gabriel Mirea if (!cr) 67709864c1cSStefan-gabriel Mirea return; 67809864c1cSStefan-gabriel Mirea 67909864c1cSStefan-gabriel Mirea /* ok, the port was enabled */ 68009864c1cSStefan-gabriel Mirea 68109864c1cSStefan-gabriel Mirea *parity = 'n'; 68209864c1cSStefan-gabriel Mirea if (cr & LINFLEXD_UARTCR_PCE) { 68309864c1cSStefan-gabriel Mirea if (cr & LINFLEXD_UARTCR_PC0) 68409864c1cSStefan-gabriel Mirea *parity = 'o'; 68509864c1cSStefan-gabriel Mirea else 68609864c1cSStefan-gabriel Mirea *parity = 'e'; 68709864c1cSStefan-gabriel Mirea } 68809864c1cSStefan-gabriel Mirea 68909864c1cSStefan-gabriel Mirea if ((cr & LINFLEXD_UARTCR_WL0) && ((cr & LINFLEXD_UARTCR_WL1) == 0)) { 69009864c1cSStefan-gabriel Mirea if (cr & LINFLEXD_UARTCR_PCE) 69109864c1cSStefan-gabriel Mirea *bits = 9; 69209864c1cSStefan-gabriel Mirea else 69309864c1cSStefan-gabriel Mirea *bits = 8; 69409864c1cSStefan-gabriel Mirea } 69509864c1cSStefan-gabriel Mirea } 69609864c1cSStefan-gabriel Mirea 69709864c1cSStefan-gabriel Mirea static int __init linflex_console_setup(struct console *co, char *options) 69809864c1cSStefan-gabriel Mirea { 69909864c1cSStefan-gabriel Mirea struct uart_port *sport; 70009864c1cSStefan-gabriel Mirea int baud = 115200; 70109864c1cSStefan-gabriel Mirea int bits = 8; 70209864c1cSStefan-gabriel Mirea int parity = 'n'; 70309864c1cSStefan-gabriel Mirea int flow = 'n'; 70409864c1cSStefan-gabriel Mirea int ret; 70509864c1cSStefan-gabriel Mirea int i; 70609864c1cSStefan-gabriel Mirea unsigned long flags; 70709864c1cSStefan-gabriel Mirea /* 70809864c1cSStefan-gabriel Mirea * check whether an invalid uart number has been specified, and 70909864c1cSStefan-gabriel Mirea * if so, search for the first available port that does have 71009864c1cSStefan-gabriel Mirea * console support. 71109864c1cSStefan-gabriel Mirea */ 71209864c1cSStefan-gabriel Mirea if (co->index == -1 || co->index >= ARRAY_SIZE(linflex_ports)) 71309864c1cSStefan-gabriel Mirea co->index = 0; 71409864c1cSStefan-gabriel Mirea 71509864c1cSStefan-gabriel Mirea sport = linflex_ports[co->index]; 71609864c1cSStefan-gabriel Mirea if (!sport) 71709864c1cSStefan-gabriel Mirea return -ENODEV; 71809864c1cSStefan-gabriel Mirea 71909864c1cSStefan-gabriel Mirea if (options) 72009864c1cSStefan-gabriel Mirea uart_parse_options(options, &baud, &parity, &bits, &flow); 72109864c1cSStefan-gabriel Mirea else 72209864c1cSStefan-gabriel Mirea linflex_console_get_options(sport, &parity, &bits); 72309864c1cSStefan-gabriel Mirea 72409864c1cSStefan-gabriel Mirea if (earlycon_port && sport->mapbase == earlycon_port->mapbase) { 72509864c1cSStefan-gabriel Mirea linflex_earlycon_same_instance = true; 72609864c1cSStefan-gabriel Mirea 72709864c1cSStefan-gabriel Mirea spin_lock_irqsave(&init_lock, flags); 72809864c1cSStefan-gabriel Mirea during_init = true; 72909864c1cSStefan-gabriel Mirea spin_unlock_irqrestore(&init_lock, flags); 73009864c1cSStefan-gabriel Mirea 73109864c1cSStefan-gabriel Mirea /* Workaround for character loss or output of many invalid 73209864c1cSStefan-gabriel Mirea * characters, when INIT mode is entered shortly after a 73309864c1cSStefan-gabriel Mirea * character has just been printed. 73409864c1cSStefan-gabriel Mirea */ 73509864c1cSStefan-gabriel Mirea udelay(PREINIT_DELAY); 73609864c1cSStefan-gabriel Mirea } 73709864c1cSStefan-gabriel Mirea 73809864c1cSStefan-gabriel Mirea linflex_setup_watermark(sport); 73909864c1cSStefan-gabriel Mirea 74009864c1cSStefan-gabriel Mirea ret = uart_set_options(sport, co, baud, parity, bits, flow); 74109864c1cSStefan-gabriel Mirea 74209864c1cSStefan-gabriel Mirea if (!linflex_earlycon_same_instance) 74309864c1cSStefan-gabriel Mirea goto done; 74409864c1cSStefan-gabriel Mirea 74509864c1cSStefan-gabriel Mirea spin_lock_irqsave(&init_lock, flags); 74609864c1cSStefan-gabriel Mirea 74709864c1cSStefan-gabriel Mirea /* Emptying buffer */ 74809864c1cSStefan-gabriel Mirea if (earlycon_buf.len) { 74909864c1cSStefan-gabriel Mirea for (i = 0; i < earlycon_buf.len; i++) 75009864c1cSStefan-gabriel Mirea linflex_console_putchar(earlycon_port, 75109864c1cSStefan-gabriel Mirea earlycon_buf.content[i]); 75209864c1cSStefan-gabriel Mirea 75309864c1cSStefan-gabriel Mirea kfree(earlycon_buf.content); 75409864c1cSStefan-gabriel Mirea earlycon_buf.len = 0; 75509864c1cSStefan-gabriel Mirea } 75609864c1cSStefan-gabriel Mirea 75709864c1cSStefan-gabriel Mirea during_init = false; 75809864c1cSStefan-gabriel Mirea spin_unlock_irqrestore(&init_lock, flags); 75909864c1cSStefan-gabriel Mirea 76009864c1cSStefan-gabriel Mirea done: 76109864c1cSStefan-gabriel Mirea return ret; 76209864c1cSStefan-gabriel Mirea } 76309864c1cSStefan-gabriel Mirea 76409864c1cSStefan-gabriel Mirea static struct uart_driver linflex_reg; 76509864c1cSStefan-gabriel Mirea static struct console linflex_console = { 76609864c1cSStefan-gabriel Mirea .name = DEV_NAME, 76709864c1cSStefan-gabriel Mirea .write = linflex_console_write, 76809864c1cSStefan-gabriel Mirea .device = uart_console_device, 76909864c1cSStefan-gabriel Mirea .setup = linflex_console_setup, 77009864c1cSStefan-gabriel Mirea .flags = CON_PRINTBUFFER, 77109864c1cSStefan-gabriel Mirea .index = -1, 77209864c1cSStefan-gabriel Mirea .data = &linflex_reg, 77309864c1cSStefan-gabriel Mirea }; 77409864c1cSStefan-gabriel Mirea 77509864c1cSStefan-gabriel Mirea static void linflex_earlycon_write(struct console *con, const char *s, 77609864c1cSStefan-gabriel Mirea unsigned int n) 77709864c1cSStefan-gabriel Mirea { 77809864c1cSStefan-gabriel Mirea struct earlycon_device *dev = con->data; 77909864c1cSStefan-gabriel Mirea 78009864c1cSStefan-gabriel Mirea uart_console_write(&dev->port, s, n, linflex_earlycon_putchar); 78109864c1cSStefan-gabriel Mirea } 78209864c1cSStefan-gabriel Mirea 78309864c1cSStefan-gabriel Mirea static int __init linflex_early_console_setup(struct earlycon_device *device, 78409864c1cSStefan-gabriel Mirea const char *options) 78509864c1cSStefan-gabriel Mirea { 78609864c1cSStefan-gabriel Mirea if (!device->port.membase) 78709864c1cSStefan-gabriel Mirea return -ENODEV; 78809864c1cSStefan-gabriel Mirea 78909864c1cSStefan-gabriel Mirea device->con->write = linflex_earlycon_write; 79009864c1cSStefan-gabriel Mirea earlycon_port = &device->port; 79109864c1cSStefan-gabriel Mirea 79209864c1cSStefan-gabriel Mirea return 0; 79309864c1cSStefan-gabriel Mirea } 79409864c1cSStefan-gabriel Mirea 7952bd3661eSStefan-gabriel Mirea OF_EARLYCON_DECLARE(linflex, "fsl,s32v234-linflexuart", 79609864c1cSStefan-gabriel Mirea linflex_early_console_setup); 79709864c1cSStefan-gabriel Mirea 79809864c1cSStefan-gabriel Mirea #define LINFLEX_CONSOLE (&linflex_console) 79909864c1cSStefan-gabriel Mirea #else 80009864c1cSStefan-gabriel Mirea #define LINFLEX_CONSOLE NULL 80109864c1cSStefan-gabriel Mirea #endif 80209864c1cSStefan-gabriel Mirea 80309864c1cSStefan-gabriel Mirea static struct uart_driver linflex_reg = { 80409864c1cSStefan-gabriel Mirea .owner = THIS_MODULE, 80509864c1cSStefan-gabriel Mirea .driver_name = DRIVER_NAME, 80609864c1cSStefan-gabriel Mirea .dev_name = DEV_NAME, 80709864c1cSStefan-gabriel Mirea .nr = ARRAY_SIZE(linflex_ports), 80809864c1cSStefan-gabriel Mirea .cons = LINFLEX_CONSOLE, 80909864c1cSStefan-gabriel Mirea }; 81009864c1cSStefan-gabriel Mirea 81109864c1cSStefan-gabriel Mirea static int linflex_probe(struct platform_device *pdev) 81209864c1cSStefan-gabriel Mirea { 81309864c1cSStefan-gabriel Mirea struct device_node *np = pdev->dev.of_node; 81409864c1cSStefan-gabriel Mirea struct uart_port *sport; 81509864c1cSStefan-gabriel Mirea struct resource *res; 81609864c1cSStefan-gabriel Mirea int ret; 81709864c1cSStefan-gabriel Mirea 81809864c1cSStefan-gabriel Mirea sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL); 81909864c1cSStefan-gabriel Mirea if (!sport) 82009864c1cSStefan-gabriel Mirea return -ENOMEM; 82109864c1cSStefan-gabriel Mirea 82209864c1cSStefan-gabriel Mirea ret = of_alias_get_id(np, "serial"); 82309864c1cSStefan-gabriel Mirea if (ret < 0) { 82409864c1cSStefan-gabriel Mirea dev_err(&pdev->dev, "failed to get alias id, errno %d\n", ret); 82509864c1cSStefan-gabriel Mirea return ret; 82609864c1cSStefan-gabriel Mirea } 82709864c1cSStefan-gabriel Mirea if (ret >= UART_NR) { 82809864c1cSStefan-gabriel Mirea dev_err(&pdev->dev, "driver limited to %d serial ports\n", 82909864c1cSStefan-gabriel Mirea UART_NR); 83009864c1cSStefan-gabriel Mirea return -ENOMEM; 83109864c1cSStefan-gabriel Mirea } 83209864c1cSStefan-gabriel Mirea 83309864c1cSStefan-gabriel Mirea sport->line = ret; 83409864c1cSStefan-gabriel Mirea 83509864c1cSStefan-gabriel Mirea res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 83609864c1cSStefan-gabriel Mirea if (!res) 83709864c1cSStefan-gabriel Mirea return -ENODEV; 83809864c1cSStefan-gabriel Mirea 83909864c1cSStefan-gabriel Mirea sport->mapbase = res->start; 84009864c1cSStefan-gabriel Mirea sport->membase = devm_ioremap_resource(&pdev->dev, res); 84109864c1cSStefan-gabriel Mirea if (IS_ERR(sport->membase)) 84209864c1cSStefan-gabriel Mirea return PTR_ERR(sport->membase); 84309864c1cSStefan-gabriel Mirea 84409864c1cSStefan-gabriel Mirea sport->dev = &pdev->dev; 84509864c1cSStefan-gabriel Mirea sport->type = PORT_LINFLEXUART; 84609864c1cSStefan-gabriel Mirea sport->iotype = UPIO_MEM; 84709864c1cSStefan-gabriel Mirea sport->irq = platform_get_irq(pdev, 0); 84809864c1cSStefan-gabriel Mirea sport->ops = &linflex_pops; 84909864c1cSStefan-gabriel Mirea sport->flags = UPF_BOOT_AUTOCONF; 8504151bbedSDmitry Safonov sport->has_sysrq = IS_ENABLED(CONFIG_SERIAL_FSL_LINFLEXUART_CONSOLE); 85109864c1cSStefan-gabriel Mirea 85209864c1cSStefan-gabriel Mirea linflex_ports[sport->line] = sport; 85309864c1cSStefan-gabriel Mirea 85409864c1cSStefan-gabriel Mirea platform_set_drvdata(pdev, sport); 85509864c1cSStefan-gabriel Mirea 856fa934fc1STang Bin return uart_add_one_port(&linflex_reg, sport); 85709864c1cSStefan-gabriel Mirea } 85809864c1cSStefan-gabriel Mirea 85909864c1cSStefan-gabriel Mirea static int linflex_remove(struct platform_device *pdev) 86009864c1cSStefan-gabriel Mirea { 86109864c1cSStefan-gabriel Mirea struct uart_port *sport = platform_get_drvdata(pdev); 86209864c1cSStefan-gabriel Mirea 86309864c1cSStefan-gabriel Mirea uart_remove_one_port(&linflex_reg, sport); 86409864c1cSStefan-gabriel Mirea 86509864c1cSStefan-gabriel Mirea return 0; 86609864c1cSStefan-gabriel Mirea } 86709864c1cSStefan-gabriel Mirea 86809864c1cSStefan-gabriel Mirea #ifdef CONFIG_PM_SLEEP 86909864c1cSStefan-gabriel Mirea static int linflex_suspend(struct device *dev) 87009864c1cSStefan-gabriel Mirea { 87109864c1cSStefan-gabriel Mirea struct uart_port *sport = dev_get_drvdata(dev); 87209864c1cSStefan-gabriel Mirea 87309864c1cSStefan-gabriel Mirea uart_suspend_port(&linflex_reg, sport); 87409864c1cSStefan-gabriel Mirea 87509864c1cSStefan-gabriel Mirea return 0; 87609864c1cSStefan-gabriel Mirea } 87709864c1cSStefan-gabriel Mirea 87809864c1cSStefan-gabriel Mirea static int linflex_resume(struct device *dev) 87909864c1cSStefan-gabriel Mirea { 88009864c1cSStefan-gabriel Mirea struct uart_port *sport = dev_get_drvdata(dev); 88109864c1cSStefan-gabriel Mirea 88209864c1cSStefan-gabriel Mirea uart_resume_port(&linflex_reg, sport); 88309864c1cSStefan-gabriel Mirea 88409864c1cSStefan-gabriel Mirea return 0; 88509864c1cSStefan-gabriel Mirea } 88609864c1cSStefan-gabriel Mirea #endif 88709864c1cSStefan-gabriel Mirea 88809864c1cSStefan-gabriel Mirea static SIMPLE_DEV_PM_OPS(linflex_pm_ops, linflex_suspend, linflex_resume); 88909864c1cSStefan-gabriel Mirea 89009864c1cSStefan-gabriel Mirea static struct platform_driver linflex_driver = { 89109864c1cSStefan-gabriel Mirea .probe = linflex_probe, 89209864c1cSStefan-gabriel Mirea .remove = linflex_remove, 89309864c1cSStefan-gabriel Mirea .driver = { 89409864c1cSStefan-gabriel Mirea .name = DRIVER_NAME, 89509864c1cSStefan-gabriel Mirea .of_match_table = linflex_dt_ids, 89609864c1cSStefan-gabriel Mirea .pm = &linflex_pm_ops, 89709864c1cSStefan-gabriel Mirea }, 89809864c1cSStefan-gabriel Mirea }; 89909864c1cSStefan-gabriel Mirea 90009864c1cSStefan-gabriel Mirea static int __init linflex_serial_init(void) 90109864c1cSStefan-gabriel Mirea { 90209864c1cSStefan-gabriel Mirea int ret; 90309864c1cSStefan-gabriel Mirea 90409864c1cSStefan-gabriel Mirea ret = uart_register_driver(&linflex_reg); 90509864c1cSStefan-gabriel Mirea if (ret) 90609864c1cSStefan-gabriel Mirea return ret; 90709864c1cSStefan-gabriel Mirea 90809864c1cSStefan-gabriel Mirea ret = platform_driver_register(&linflex_driver); 90909864c1cSStefan-gabriel Mirea if (ret) 91009864c1cSStefan-gabriel Mirea uart_unregister_driver(&linflex_reg); 91109864c1cSStefan-gabriel Mirea 91209864c1cSStefan-gabriel Mirea return ret; 91309864c1cSStefan-gabriel Mirea } 91409864c1cSStefan-gabriel Mirea 91509864c1cSStefan-gabriel Mirea static void __exit linflex_serial_exit(void) 91609864c1cSStefan-gabriel Mirea { 91709864c1cSStefan-gabriel Mirea platform_driver_unregister(&linflex_driver); 91809864c1cSStefan-gabriel Mirea uart_unregister_driver(&linflex_reg); 91909864c1cSStefan-gabriel Mirea } 92009864c1cSStefan-gabriel Mirea 92109864c1cSStefan-gabriel Mirea module_init(linflex_serial_init); 92209864c1cSStefan-gabriel Mirea module_exit(linflex_serial_exit); 92309864c1cSStefan-gabriel Mirea 9249905f32aSStefan-Gabriel Mirea MODULE_DESCRIPTION("Freescale LINFlexD serial port driver"); 92509864c1cSStefan-gabriel Mirea MODULE_LICENSE("GPL v2"); 926