1924 lines · 54.5 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0+/* * Cadence UART driver (found in Xilinx Zynq) * * Copyright (c) 2011 - 2014 Xilinx, Inc. * * This driver has originally been pushed by Xilinx using a Zynq-branding. This * still shows in the naming of this file, the kconfig symbols and some symbols * in the code. */ #include <linux/platform_device.h>#include <linux/serial.h>#include <linux/console.h>#include <linux/serial_core.h>#include <linux/slab.h>#include <linux/tty.h>#include <linux/tty_flip.h>#include <linux/clk.h>#include <linux/irq.h>#include <linux/io.h>#include <linux/of.h>#include <linux/module.h>#include <linux/pm_runtime.h>#include <linux/gpio/consumer.h>#include <linux/delay.h>#include <linux/reset.h> #define CDNS_UART_TTY_NAME	"ttyPS"#define CDNS_UART_NAME		"xuartps"#define CDNS_UART_MAJOR		0	/* use dynamic node allocation */#define CDNS_UART_MINOR		0	/* works best with devtmpfs */#define CDNS_UART_NR_PORTS	16#define CDNS_UART_FIFO_SIZE	64	/* FIFO size */#define TX_TIMEOUT		500000 /* Rx Trigger level */static int rx_trigger_level = 56;module_param(rx_trigger_level, uint, 0444);MODULE_PARM_DESC(rx_trigger_level, "Rx trigger level, 1-63 bytes"); /* Rx Timeout */static int rx_timeout = 10;module_param(rx_timeout, uint, 0444);MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255"); /* Register offsets for the UART. */#define CDNS_UART_CR		0x00  /* Control Register */#define CDNS_UART_MR		0x04  /* Mode Register */#define CDNS_UART_IER		0x08  /* Interrupt Enable */#define CDNS_UART_IDR		0x0C  /* Interrupt Disable */#define CDNS_UART_IMR		0x10  /* Interrupt Mask */#define CDNS_UART_ISR		0x14  /* Interrupt Status */#define CDNS_UART_BAUDGEN	0x18  /* Baud Rate Generator */#define CDNS_UART_RXTOUT	0x1C  /* RX Timeout */#define CDNS_UART_RXWM		0x20  /* RX FIFO Trigger Level */#define CDNS_UART_MODEMCR	0x24  /* Modem Control */#define CDNS_UART_MODEMSR	0x28  /* Modem Status */#define CDNS_UART_SR		0x2C  /* Channel Status */#define CDNS_UART_FIFO		0x30  /* FIFO */#define CDNS_UART_BAUDDIV	0x34  /* Baud Rate Divider */#define CDNS_UART_FLOWDEL	0x38  /* Flow Delay */#define CDNS_UART_IRRX_PWIDTH	0x3C  /* IR Min Received Pulse Width */#define CDNS_UART_IRTX_PWIDTH	0x40  /* IR Transmitted pulse Width */#define CDNS_UART_TXWM		0x44  /* TX FIFO Trigger Level */#define CDNS_UART_RXBS		0x48  /* RX FIFO byte status register */ /* Control Register Bit Definitions */#define CDNS_UART_CR_STOPBRK	0x00000100  /* Stop TX break */#define CDNS_UART_CR_STARTBRK	0x00000080  /* Set TX break */#define CDNS_UART_CR_TX_DIS	0x00000020  /* TX disabled. */#define CDNS_UART_CR_TX_EN	0x00000010  /* TX enabled */#define CDNS_UART_CR_RX_DIS	0x00000008  /* RX disabled. */#define CDNS_UART_CR_RX_EN	0x00000004  /* RX enabled */#define CDNS_UART_CR_TXRST	0x00000002  /* TX logic reset */#define CDNS_UART_CR_RXRST	0x00000001  /* RX logic reset */#define CDNS_UART_CR_RST_TO	0x00000040  /* Restart Timeout Counter */#define CDNS_UART_RXBS_PARITY    0x00000001 /* Parity error status */#define CDNS_UART_RXBS_FRAMING   0x00000002 /* Framing error status */#define CDNS_UART_RXBS_BRK       0x00000004 /* Overrun error status */ /* * Mode Register: * The mode register (MR) defines the mode of transfer as well as the data * format. If this register is modified during transmission or reception, * data validity cannot be guaranteed. */#define CDNS_UART_MR_CLKSEL		0x00000001  /* Pre-scalar selection */#define CDNS_UART_MR_CHMODE_L_LOOP	0x00000200  /* Local loop back mode */#define CDNS_UART_MR_CHMODE_NORM	0x00000000  /* Normal mode */#define CDNS_UART_MR_CHMODE_MASK	0x00000300  /* Mask for mode bits */ #define CDNS_UART_MR_STOPMODE_2_BIT	0x00000080  /* 2 stop bits */#define CDNS_UART_MR_STOPMODE_1_BIT	0x00000000  /* 1 stop bit */ #define CDNS_UART_MR_PARITY_NONE	0x00000020  /* No parity mode */#define CDNS_UART_MR_PARITY_MARK	0x00000018  /* Mark parity mode */#define CDNS_UART_MR_PARITY_SPACE	0x00000010  /* Space parity mode */#define CDNS_UART_MR_PARITY_ODD		0x00000008  /* Odd parity mode */#define CDNS_UART_MR_PARITY_EVEN	0x00000000  /* Even parity mode */ #define CDNS_UART_MR_CHARLEN_6_BIT	0x00000006  /* 6 bits data */#define CDNS_UART_MR_CHARLEN_7_BIT	0x00000004  /* 7 bits data */#define CDNS_UART_MR_CHARLEN_8_BIT	0x00000000  /* 8 bits data */ /* * Interrupt Registers: * Interrupt control logic uses the interrupt enable register (IER) and the * interrupt disable register (IDR) to set the value of the bits in the * interrupt mask register (IMR). The IMR determines whether to pass an * interrupt to the interrupt status register (ISR). * Writing a 1 to IER Enables an interrupt, writing a 1 to IDR disables an * interrupt. IMR and ISR are read only, and IER and IDR are write only. * Reading either IER or IDR returns 0x00. * All four registers have the same bit definitions. */#define CDNS_UART_IXR_TOUT	0x00000100 /* RX Timeout error interrupt */#define CDNS_UART_IXR_PARITY	0x00000080 /* Parity error interrupt */#define CDNS_UART_IXR_FRAMING	0x00000040 /* Framing error interrupt */#define CDNS_UART_IXR_OVERRUN	0x00000020 /* Overrun error interrupt */#define CDNS_UART_IXR_TXFULL	0x00000010 /* TX FIFO Full interrupt */#define CDNS_UART_IXR_TXEMPTY	0x00000008 /* TX FIFO empty interrupt */#define CDNS_UART_ISR_RXEMPTY	0x00000002 /* RX FIFO empty interrupt */#define CDNS_UART_IXR_RXTRIG	0x00000001 /* RX FIFO trigger interrupt */#define CDNS_UART_IXR_RXFULL	0x00000004 /* RX FIFO full interrupt. */#define CDNS_UART_IXR_RXEMPTY	0x00000002 /* RX FIFO empty interrupt. */#define CDNS_UART_IXR_RXMASK	0x000021e7 /* Valid RX bit mask */ 	/*	 * Do not enable parity error interrupt for the following	 * reason: When parity error interrupt is enabled, each Rx	 * parity error always results in 2 events. The first one	 * being parity error interrupt and the second one with a	 * proper Rx interrupt with the incoming data.  Disabling	 * parity error interrupt ensures better handling of parity	 * error events. With this change, for a parity error case, we	 * get a Rx interrupt with parity error set in ISR register	 * and we still handle parity errors in the desired way.	 */ #define CDNS_UART_RX_IRQS	(CDNS_UART_IXR_FRAMING | \				 CDNS_UART_IXR_OVERRUN | \				 CDNS_UART_IXR_RXTRIG |	 \				 CDNS_UART_IXR_TOUT) /* Goes in read_status_mask for break detection as the HW doesn't do it*/#define CDNS_UART_IXR_BRK	0x00002000 #define CDNS_UART_RXBS_SUPPORT BIT(1)/* * Modem Control register: * The read/write Modem Control register controls the interface with the modem * or data set, or a peripheral device emulating a modem. */#define CDNS_UART_MODEMCR_FCM	0x00000020 /* Automatic flow control mode */#define CDNS_UART_MODEMCR_RTS	0x00000002 /* Request to send output control */#define CDNS_UART_MODEMCR_DTR	0x00000001 /* Data Terminal Ready */ /* * Modem Status register: * The read/write Modem Status register reports the interface with the modem * or data set, or a peripheral device emulating a modem. */#define CDNS_UART_MODEMSR_DCD	BIT(7) /* Data Carrier Detect */#define CDNS_UART_MODEMSR_RI	BIT(6) /* Ting Indicator */#define CDNS_UART_MODEMSR_DSR	BIT(5) /* Data Set Ready */#define CDNS_UART_MODEMSR_CTS	BIT(4) /* Clear To Send */ /* * Channel Status Register: * The channel status register (CSR) is provided to enable the control logic * to monitor the status of bits in the channel interrupt status register, * even if these are masked out by the interrupt mask register. */#define CDNS_UART_SR_RXEMPTY	0x00000002 /* RX FIFO empty */#define CDNS_UART_SR_TXEMPTY	0x00000008 /* TX FIFO empty */#define CDNS_UART_SR_TXFULL	0x00000010 /* TX FIFO full */#define CDNS_UART_SR_RXTRIG	0x00000001 /* Rx Trigger */#define CDNS_UART_SR_TACTIVE	0x00000800 /* TX state machine active */ /* baud dividers min/max values */#define CDNS_UART_BDIV_MIN	4#define CDNS_UART_BDIV_MAX	255#define CDNS_UART_CD_MAX	65535#define UART_AUTOSUSPEND_TIMEOUT	3000 /** * struct cdns_uart - device data * @port:		Pointer to the UART port * @uartclk:		Reference clock * @pclk:		APB clock * @baud:		Current baud rate * @clk_rate_change_nb:	Notifier block for clock changes * @quirks:		Flags for RXBS support. * @cts_override:	Modem control state override * @gpiod_rts:		Pointer to the gpio descriptor * @rs485_tx_started:	RS485 tx state * @tx_timer:		Timer for tx * @rstc:		Pointer to the reset control */struct cdns_uart {	struct uart_port	*port;	struct clk		*uartclk;	struct clk		*pclk;	unsigned int		baud;	struct notifier_block	clk_rate_change_nb;	u32			quirks;	bool cts_override;	struct gpio_desc	*gpiod_rts;	bool			rs485_tx_started;	struct hrtimer		tx_timer;	struct reset_control	*rstc;};struct cdns_platform_data {	u32 quirks;}; static struct serial_rs485 cdns_rs485_supported = {	.flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND |		 SER_RS485_RTS_AFTER_SEND,	.delay_rts_before_send = 1,	.delay_rts_after_send = 1,}; #define to_cdns_uart(_nb) container_of(_nb, struct cdns_uart, \		clk_rate_change_nb) /** * cdns_uart_handle_rx - Handle the received bytes along with Rx errors. * @dev_id: Id of the UART port * @isrstatus: The interrupt status register value as read * Return: None */static void cdns_uart_handle_rx(void *dev_id, unsigned int isrstatus){	struct uart_port *port = (struct uart_port *)dev_id;	struct cdns_uart *cdns_uart = port->private_data;	unsigned int data;	unsigned int rxbs_status = 0;	unsigned int status_mask;	unsigned int framerrprocessed = 0;	char status = TTY_NORMAL;	bool is_rxbs_support; 	is_rxbs_support = cdns_uart->quirks & CDNS_UART_RXBS_SUPPORT; 	while ((readl(port->membase + CDNS_UART_SR) &		CDNS_UART_SR_RXEMPTY) != CDNS_UART_SR_RXEMPTY) {		if (is_rxbs_support)			rxbs_status = readl(port->membase + CDNS_UART_RXBS);		data = readl(port->membase + CDNS_UART_FIFO);		port->icount.rx++;		/*		 * There is no hardware break detection in Zynq, so we interpret		 * framing error with all-zeros data as a break sequence.		 * Most of the time, there's another non-zero byte at the		 * end of the sequence.		 */		if (!is_rxbs_support && (isrstatus & CDNS_UART_IXR_FRAMING)) {			if (!data) {				port->read_status_mask |= CDNS_UART_IXR_BRK;				framerrprocessed = 1;				continue;			}		}		if (is_rxbs_support && (rxbs_status & CDNS_UART_RXBS_BRK)) {			port->icount.brk++;			status = TTY_BREAK;			if (uart_handle_break(port))				continue;		} 		isrstatus &= port->read_status_mask;		isrstatus &= ~port->ignore_status_mask;		status_mask = port->read_status_mask;		status_mask &= ~port->ignore_status_mask; 		if (data &&		    (port->read_status_mask & CDNS_UART_IXR_BRK)) {			port->read_status_mask &= ~CDNS_UART_IXR_BRK;			port->icount.brk++;			if (uart_handle_break(port))				continue;		} 		if (uart_prepare_sysrq_char(port, data))			continue; 		if (is_rxbs_support) {			if ((rxbs_status & CDNS_UART_RXBS_PARITY)			    && (status_mask & CDNS_UART_IXR_PARITY)) {				port->icount.parity++;				status = TTY_PARITY;			}			if ((rxbs_status & CDNS_UART_RXBS_FRAMING)			    && (status_mask & CDNS_UART_IXR_PARITY)) {				port->icount.frame++;				status = TTY_FRAME;			}		} else {			if (isrstatus & CDNS_UART_IXR_PARITY) {				port->icount.parity++;				status = TTY_PARITY;			}			if ((isrstatus & CDNS_UART_IXR_FRAMING) &&			    !framerrprocessed) {				port->icount.frame++;				status = TTY_FRAME;			}		}		if (isrstatus & CDNS_UART_IXR_OVERRUN) {			port->icount.overrun++;			tty_insert_flip_char(&port->state->port, 0,					     TTY_OVERRUN);		}		tty_insert_flip_char(&port->state->port, data, status);		isrstatus = 0;	} 	tty_flip_buffer_push(&port->state->port);} /** * cdns_rts_gpio_enable - Configure RTS/GPIO to high/low * @cdns_uart: Handle to the cdns_uart * @enable: Value to be set to RTS/GPIO */static void cdns_rts_gpio_enable(struct cdns_uart *cdns_uart, bool enable){	u32 val; 	if (cdns_uart->gpiod_rts) {		gpiod_set_value(cdns_uart->gpiod_rts, enable);	} else {		val = readl(cdns_uart->port->membase + CDNS_UART_MODEMCR);		if (enable)			val |= CDNS_UART_MODEMCR_RTS;		else			val &= ~CDNS_UART_MODEMCR_RTS;		writel(val, cdns_uart->port->membase + CDNS_UART_MODEMCR);	}} /** * cdns_rs485_tx_setup - Tx setup specific to rs485 * @cdns_uart: Handle to the cdns_uart */static void cdns_rs485_tx_setup(struct cdns_uart *cdns_uart){	bool enable; 	enable = cdns_uart->port->rs485.flags & SER_RS485_RTS_ON_SEND;	cdns_rts_gpio_enable(cdns_uart, enable); 	cdns_uart->rs485_tx_started = true;} /** * cdns_rs485_rx_setup - Rx setup specific to rs485 * @cdns_uart: Handle to the cdns_uart */static void cdns_rs485_rx_setup(struct cdns_uart *cdns_uart){	bool enable; 	enable = cdns_uart->port->rs485.flags & SER_RS485_RTS_AFTER_SEND;	cdns_rts_gpio_enable(cdns_uart, enable); 	cdns_uart->rs485_tx_started = false;} /** * cdns_uart_tx_empty -  Check whether TX is empty * @port: Handle to the uart port structure * * Return: TIOCSER_TEMT on success, 0 otherwise */static unsigned int cdns_uart_tx_empty(struct uart_port *port){	unsigned int status; 	status = readl(port->membase + CDNS_UART_SR);	status &= (CDNS_UART_SR_TXEMPTY | CDNS_UART_SR_TACTIVE);	return (status == CDNS_UART_SR_TXEMPTY) ? TIOCSER_TEMT : 0;} /** * cdns_rs485_rx_callback - Timer rx callback handler for rs485. * @t: Handle to the hrtimer structure */static enum hrtimer_restart cdns_rs485_rx_callback(struct hrtimer *t){	struct cdns_uart *cdns_uart = container_of(t, struct cdns_uart, tx_timer); 	/*	 * Default Rx should be setup, because Rx signaling path	 * need to enable to receive data.	 */	cdns_rs485_rx_setup(cdns_uart); 	return HRTIMER_NORESTART;} /** * cdns_calc_after_tx_delay - calculate delay required for after tx. * @cdns_uart: Handle to the cdns_uart */static u64 cdns_calc_after_tx_delay(struct cdns_uart *cdns_uart){	/*	 * Frame time + stop bit time + rs485.delay_rts_after_send	 */	return cdns_uart->port->frame_time	       + DIV_ROUND_UP(cdns_uart->port->frame_time, 7)	       + (u64)cdns_uart->port->rs485.delay_rts_after_send * NSEC_PER_MSEC;} /** * cdns_uart_handle_tx - Handle the bytes to be transmitted. * @dev_id: Id of the UART port * Return: None */static void cdns_uart_handle_tx(void *dev_id){	struct uart_port *port = (struct uart_port *)dev_id;	struct cdns_uart *cdns_uart = port->private_data;	struct tty_port *tport = &port->state->port;	unsigned int numbytes;	unsigned char ch;	ktime_t rts_delay; 	if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) {		/* Disable the TX Empty interrupt */		writel(CDNS_UART_IXR_TXEMPTY, port->membase + CDNS_UART_IDR);		/* Set RTS line after delay */		if (cdns_uart->port->rs485.flags & SER_RS485_ENABLED) {			cdns_uart->tx_timer.function = &cdns_rs485_rx_callback;			rts_delay = ns_to_ktime(cdns_calc_after_tx_delay(cdns_uart));			hrtimer_start(&cdns_uart->tx_timer, rts_delay, HRTIMER_MODE_REL);		}		return;	} 	numbytes = port->fifosize;	while (numbytes &&	       !(readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_TXFULL) &&	       uart_fifo_get(port, &ch)) {		writel(ch, port->membase + CDNS_UART_FIFO);		numbytes--;	} 	if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)		uart_write_wakeup(port); 	/* Enable the TX Empty interrupt */	writel(CDNS_UART_IXR_TXEMPTY, cdns_uart->port->membase + CDNS_UART_IER);} /** * cdns_uart_isr - Interrupt handler * @irq: Irq number * @dev_id: Id of the port * * Return: IRQHANDLED */static irqreturn_t cdns_uart_isr(int irq, void *dev_id){	struct uart_port *port = (struct uart_port *)dev_id;	unsigned int isrstatus; 	uart_port_lock(port); 	/* Read the interrupt status register to determine which	 * interrupt(s) is/are active and clear them.	 */	isrstatus = readl(port->membase + CDNS_UART_ISR);	writel(isrstatus, port->membase + CDNS_UART_ISR); 	if (isrstatus & CDNS_UART_IXR_TXEMPTY) {		cdns_uart_handle_tx(dev_id);		isrstatus &= ~CDNS_UART_IXR_TXEMPTY;	} 	isrstatus &= port->read_status_mask;	isrstatus &= ~port->ignore_status_mask;	/*	 * Skip RX processing if RX is disabled as RXEMPTY will never be set	 * as read bytes will not be removed from the FIFO.	 */	if (isrstatus & CDNS_UART_IXR_RXMASK &&	    !(readl(port->membase + CDNS_UART_CR) & CDNS_UART_CR_RX_DIS))		cdns_uart_handle_rx(dev_id, isrstatus); 	uart_unlock_and_check_sysrq(port);	return IRQ_HANDLED;} /** * cdns_uart_calc_baud_divs - Calculate baud rate divisors * @clk: UART module input clock

Lines 1–500 of 1924.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t