921 lines · 21.4 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* * Derived from many drivers using generic_serial interface. * * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr> * *  Serial driver for BCM63xx integrated UART. * * Hardware flow control was _not_ tested since I only have RX/TX on * my board. */ #include <linux/kernel.h>#include <linux/platform_device.h>#include <linux/init.h>#include <linux/delay.h>#include <linux/module.h>#include <linux/console.h>#include <linux/clk.h>#include <linux/tty.h>#include <linux/tty_flip.h>#include <linux/sysrq.h>#include <linux/serial.h>#include <linux/serial_core.h>#include <linux/serial_bcm63xx.h>#include <linux/io.h>#include <linux/of.h> #define BCM63XX_NR_UARTS	2 static struct uart_port ports[BCM63XX_NR_UARTS]; /* * rx interrupt mask / stat * * mask: *  - rx fifo full *  - rx fifo above threshold *  - rx fifo not empty for too long */#define UART_RX_INT_MASK	(UART_IR_MASK(UART_IR_RXOVER) |		\				UART_IR_MASK(UART_IR_RXTHRESH) |	\				UART_IR_MASK(UART_IR_RXTIMEOUT)) #define UART_RX_INT_STAT	(UART_IR_STAT(UART_IR_RXOVER) |		\				UART_IR_STAT(UART_IR_RXTHRESH) |	\				UART_IR_STAT(UART_IR_RXTIMEOUT)) /* * tx interrupt mask / stat * * mask: * - tx fifo empty * - tx fifo below threshold */#define UART_TX_INT_MASK	(UART_IR_MASK(UART_IR_TXEMPTY) |	\				UART_IR_MASK(UART_IR_TXTRESH)) #define UART_TX_INT_STAT	(UART_IR_STAT(UART_IR_TXEMPTY) |	\				UART_IR_STAT(UART_IR_TXTRESH)) /* * external input interrupt * * mask: any edge on CTS, DCD */#define UART_EXTINP_INT_MASK	(UART_EXTINP_IRMASK(UART_EXTINP_IR_CTS) | \				 UART_EXTINP_IRMASK(UART_EXTINP_IR_DCD)) /* * handy uart register accessor */static inline unsigned int bcm_uart_readl(struct uart_port *port,					 unsigned int offset){	return __raw_readl(port->membase + offset);} static inline void bcm_uart_writel(struct uart_port *port,				  unsigned int value, unsigned int offset){	__raw_writel(value, port->membase + offset);} /* * serial core request to check if uart tx fifo is empty */static unsigned int bcm_uart_tx_empty(struct uart_port *port){	unsigned int val; 	val = bcm_uart_readl(port, UART_IR_REG);	return (val & UART_IR_STAT(UART_IR_TXEMPTY)) ? 1 : 0;} /* * serial core request to set RTS and DTR pin state and loopback mode */static void bcm_uart_set_mctrl(struct uart_port *port, unsigned int mctrl){	unsigned int val; 	val = bcm_uart_readl(port, UART_MCTL_REG);	val &= ~(UART_MCTL_DTR_MASK | UART_MCTL_RTS_MASK);	/* invert of written value is reflected on the pin */	if (!(mctrl & TIOCM_DTR))		val |= UART_MCTL_DTR_MASK;	if (!(mctrl & TIOCM_RTS))		val |= UART_MCTL_RTS_MASK;	bcm_uart_writel(port, val, UART_MCTL_REG); 	val = bcm_uart_readl(port, UART_CTL_REG);	if (mctrl & TIOCM_LOOP)		val |= UART_CTL_LOOPBACK_MASK;	else		val &= ~UART_CTL_LOOPBACK_MASK;	bcm_uart_writel(port, val, UART_CTL_REG);} /* * serial core request to return RI, CTS, DCD and DSR pin state */static unsigned int bcm_uart_get_mctrl(struct uart_port *port){	unsigned int val, mctrl; 	mctrl = 0;	val = bcm_uart_readl(port, UART_EXTINP_REG);	if (val & UART_EXTINP_RI_MASK)		mctrl |= TIOCM_RI;	if (val & UART_EXTINP_CTS_MASK)		mctrl |= TIOCM_CTS;	if (val & UART_EXTINP_DCD_MASK)		mctrl |= TIOCM_CD;	if (val & UART_EXTINP_DSR_MASK)		mctrl |= TIOCM_DSR;	return mctrl;} /* * serial core request to disable tx ASAP (used for flow control) */static void bcm_uart_stop_tx(struct uart_port *port){	unsigned int val; 	val = bcm_uart_readl(port, UART_CTL_REG);	val &= ~(UART_CTL_TXEN_MASK);	bcm_uart_writel(port, val, UART_CTL_REG); 	val = bcm_uart_readl(port, UART_IR_REG);	val &= ~UART_TX_INT_MASK;	bcm_uart_writel(port, val, UART_IR_REG);} /* * serial core request to (re)enable tx */static void bcm_uart_start_tx(struct uart_port *port){	unsigned int val; 	val = bcm_uart_readl(port, UART_IR_REG);	val |= UART_TX_INT_MASK;	bcm_uart_writel(port, val, UART_IR_REG); 	val = bcm_uart_readl(port, UART_CTL_REG);	val |= UART_CTL_TXEN_MASK;	bcm_uart_writel(port, val, UART_CTL_REG);} /* * serial core request to stop rx, called before port shutdown */static void bcm_uart_stop_rx(struct uart_port *port){	unsigned int val; 	val = bcm_uart_readl(port, UART_IR_REG);	val &= ~UART_RX_INT_MASK;	bcm_uart_writel(port, val, UART_IR_REG);} /* * serial core request to enable modem status interrupt reporting */static void bcm_uart_enable_ms(struct uart_port *port){	unsigned int val; 	val = bcm_uart_readl(port, UART_IR_REG);	val |= UART_IR_MASK(UART_IR_EXTIP);	bcm_uart_writel(port, val, UART_IR_REG);} /* * serial core request to start/stop emitting break char */static void bcm_uart_break_ctl(struct uart_port *port, int ctl){	unsigned long flags;	unsigned int val; 	uart_port_lock_irqsave(port, &flags); 	val = bcm_uart_readl(port, UART_CTL_REG);	if (ctl)		val |= UART_CTL_XMITBRK_MASK;	else		val &= ~UART_CTL_XMITBRK_MASK;	bcm_uart_writel(port, val, UART_CTL_REG); 	uart_port_unlock_irqrestore(port, flags);} /* * return port type in string format */static const char *bcm_uart_type(struct uart_port *port){	return (port->type == PORT_BCM63XX) ? "bcm63xx_uart" : NULL;} /* * read all chars in rx fifo and send them to core */static void bcm_uart_do_rx(struct uart_port *port){	struct tty_port *tty_port = &port->state->port;	unsigned int max_count; 	/* limit number of char read in interrupt, should not be	 * higher than fifo size anyway since we're much faster than	 * serial port */	max_count = 32;	do {		unsigned int iestat, c, cstat;		char flag; 		/* get overrun/fifo empty information from ier		 * register */		iestat = bcm_uart_readl(port, UART_IR_REG); 		if (unlikely(iestat & UART_IR_STAT(UART_IR_RXOVER))) {			unsigned int val; 			/* fifo reset is required to clear			 * interrupt */			val = bcm_uart_readl(port, UART_CTL_REG);			val |= UART_CTL_RSTRXFIFO_MASK;			bcm_uart_writel(port, val, UART_CTL_REG); 			port->icount.overrun++;			tty_insert_flip_char(tty_port, 0, TTY_OVERRUN);		} 		if (!(iestat & UART_IR_STAT(UART_IR_RXNOTEMPTY)))			break; 		cstat = c = bcm_uart_readl(port, UART_FIFO_REG);		port->icount.rx++;		flag = TTY_NORMAL;		c &= 0xff; 		if (unlikely((cstat & UART_FIFO_ANYERR_MASK))) {			/* do stats first */			if (cstat & UART_FIFO_BRKDET_MASK) {				port->icount.brk++;				if (uart_handle_break(port))					continue;			} 			if (cstat & UART_FIFO_PARERR_MASK)				port->icount.parity++;			if (cstat & UART_FIFO_FRAMEERR_MASK)				port->icount.frame++; 			/* update flag wrt read_status_mask */			cstat &= port->read_status_mask;			if (cstat & UART_FIFO_BRKDET_MASK)				flag = TTY_BREAK;			if (cstat & UART_FIFO_FRAMEERR_MASK)				flag = TTY_FRAME;			if (cstat & UART_FIFO_PARERR_MASK)				flag = TTY_PARITY;		} 		if (uart_prepare_sysrq_char(port, c))			continue; 		if ((cstat & port->ignore_status_mask) == 0)			tty_insert_flip_char(tty_port, c, flag); 	} while (--max_count); 	tty_flip_buffer_push(tty_port);} /* * fill tx fifo with chars to send, stop when fifo is about to be full * or when all chars have been sent. */static void bcm_uart_do_tx(struct uart_port *port){	unsigned int val;	bool pending;	u8 ch; 	val = bcm_uart_readl(port, UART_MCTL_REG);	val = (val & UART_MCTL_TXFIFOFILL_MASK) >> UART_MCTL_TXFIFOFILL_SHIFT;	pending = uart_port_tx_limited_flags(port, ch, UART_TX_NOSTOP,		port->fifosize - val,		true,		bcm_uart_writel(port, ch, UART_FIFO_REG),		({}));	if (pending)		return; 	/* nothing to send, disable transmit interrupt */	val = bcm_uart_readl(port, UART_IR_REG);	val &= ~UART_TX_INT_MASK;	bcm_uart_writel(port, val, UART_IR_REG); 	if (uart_tx_stopped(port))		bcm_uart_stop_tx(port);} /* * process uart interrupt */static irqreturn_t bcm_uart_interrupt(int irq, void *dev_id){	struct uart_port *port;	unsigned int irqstat; 	port = dev_id;	uart_port_lock(port); 	irqstat = bcm_uart_readl(port, UART_IR_REG);	if (irqstat & UART_RX_INT_STAT)		bcm_uart_do_rx(port); 	if (irqstat & UART_TX_INT_STAT)		bcm_uart_do_tx(port); 	if (irqstat & UART_IR_MASK(UART_IR_EXTIP)) {		unsigned int estat; 		estat = bcm_uart_readl(port, UART_EXTINP_REG);		if (estat & UART_EXTINP_IRSTAT(UART_EXTINP_IR_CTS))			uart_handle_cts_change(port,					       estat & UART_EXTINP_CTS_MASK);		if (estat & UART_EXTINP_IRSTAT(UART_EXTINP_IR_DCD))			uart_handle_dcd_change(port,					       estat & UART_EXTINP_DCD_MASK);	} 	uart_unlock_and_check_sysrq(port);	return IRQ_HANDLED;} /* * enable rx & tx operation on uart */static void bcm_uart_enable(struct uart_port *port){	unsigned int val; 	val = bcm_uart_readl(port, UART_CTL_REG);	val |= (UART_CTL_BRGEN_MASK | UART_CTL_TXEN_MASK | UART_CTL_RXEN_MASK);	bcm_uart_writel(port, val, UART_CTL_REG);} /* * disable rx & tx operation on uart */static void bcm_uart_disable(struct uart_port *port){	unsigned int val; 	val = bcm_uart_readl(port, UART_CTL_REG);	val &= ~(UART_CTL_BRGEN_MASK | UART_CTL_TXEN_MASK |		 UART_CTL_RXEN_MASK);	bcm_uart_writel(port, val, UART_CTL_REG);} /* * clear all unread data in rx fifo and unsent data in tx fifo */static void bcm_uart_flush(struct uart_port *port){	unsigned int val; 	/* empty rx and tx fifo */	val = bcm_uart_readl(port, UART_CTL_REG);	val |= UART_CTL_RSTRXFIFO_MASK | UART_CTL_RSTTXFIFO_MASK;	bcm_uart_writel(port, val, UART_CTL_REG); 	/* read any pending char to make sure all irq status are	 * cleared */	(void)bcm_uart_readl(port, UART_FIFO_REG);} /* * serial core request to initialize uart and start rx operation */static int bcm_uart_startup(struct uart_port *port){	unsigned int val;	int ret; 	/* mask all irq and flush port */	bcm_uart_disable(port);	bcm_uart_writel(port, 0, UART_IR_REG);	bcm_uart_flush(port); 	/* clear any pending external input interrupt */	(void)bcm_uart_readl(port, UART_EXTINP_REG); 	/* set rx/tx fifo thresh to fifo half size */	val = bcm_uart_readl(port, UART_MCTL_REG);	val &= ~(UART_MCTL_RXFIFOTHRESH_MASK | UART_MCTL_TXFIFOTHRESH_MASK);	val |= (port->fifosize / 2) << UART_MCTL_RXFIFOTHRESH_SHIFT;	val |= (port->fifosize / 2) << UART_MCTL_TXFIFOTHRESH_SHIFT;	bcm_uart_writel(port, val, UART_MCTL_REG); 	/* set rx fifo timeout to 1 char time */	val = bcm_uart_readl(port, UART_CTL_REG);	val &= ~UART_CTL_RXTMOUTCNT_MASK;	val |= 1 << UART_CTL_RXTMOUTCNT_SHIFT;	bcm_uart_writel(port, val, UART_CTL_REG); 	/* report any edge on dcd and cts */	val = UART_EXTINP_INT_MASK;	val |= UART_EXTINP_DCD_NOSENSE_MASK;	val |= UART_EXTINP_CTS_NOSENSE_MASK;	bcm_uart_writel(port, val, UART_EXTINP_REG); 	/* register irq and enable rx interrupts */	ret = request_irq(port->irq, bcm_uart_interrupt, 0,			  dev_name(port->dev), port);	if (ret)		return ret;	bcm_uart_writel(port, UART_RX_INT_MASK, UART_IR_REG);	bcm_uart_enable(port);	return 0;} /* * serial core request to flush & disable uart */static void bcm_uart_shutdown(struct uart_port *port){	unsigned long flags; 	uart_port_lock_irqsave(port, &flags);	bcm_uart_writel(port, 0, UART_IR_REG);	uart_port_unlock_irqrestore(port, flags); 	bcm_uart_disable(port);	bcm_uart_flush(port);	free_irq(port->irq, port);} /* * serial core request to change current uart setting */static void bcm_uart_set_termios(struct uart_port *port, struct ktermios *new,				 const struct ktermios *old){	unsigned int ctl, baud, quot, ier;	unsigned long flags;	int tries; 	uart_port_lock_irqsave(port, &flags); 	/* Drain the hot tub fully before we power it off for the winter. */	for (tries = 3; !bcm_uart_tx_empty(port) && tries; tries--)		mdelay(10); 	/* disable uart while changing speed */	bcm_uart_disable(port);	bcm_uart_flush(port); 	/* update Control register */	ctl = bcm_uart_readl(port, UART_CTL_REG);	ctl &= ~UART_CTL_BITSPERSYM_MASK; 	switch (new->c_cflag & CSIZE) {	case CS5:		ctl |= (0 << UART_CTL_BITSPERSYM_SHIFT);		break;	case CS6:		ctl |= (1 << UART_CTL_BITSPERSYM_SHIFT);		break;	case CS7:		ctl |= (2 << UART_CTL_BITSPERSYM_SHIFT);		break;	default:		ctl |= (3 << UART_CTL_BITSPERSYM_SHIFT);

Lines 1–500 of 921.