serial: stm32-usart: Add STM32 USART Driver
This drivers adds support to the STM32 USART controller, which is a standard serial driver. Tested-by: Chanwoo Choi <cw00.choi@samsung.com> Reviewed-by: Peter Hurley <peter@hurleysoftware.com> Reviewed-by: Vladimir Zapolskiy <vladimir_zapolskiy@mentor.com> Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com> Signed-off-by: Maxime Coquelin <mcoquelin.stm32@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
		
							parent
							
								
									2a4462418a
								
							
						
					
					
						commit
						48a6092fb4
					
				| @ -1608,6 +1608,23 @@ config SERIAL_SPRD_CONSOLE | ||||
| 	  with "earlycon" on the kernel command line. The console is | ||||
| 	  enabled when early_param is processed. | ||||
| 
 | ||||
| config SERIAL_STM32 | ||||
| 	tristate "STMicroelectronics STM32 serial port support" | ||||
| 	select SERIAL_CORE | ||||
| 	depends on ARM || COMPILE_TEST | ||||
| 	help | ||||
| 	  This driver is for the on-chip Serial Controller on | ||||
| 	  STMicroelectronics STM32 MCUs. | ||||
| 	  USART supports Rx & Tx functionality. | ||||
| 	  It support all industry standard baud rates. | ||||
| 
 | ||||
| 	  If unsure, say N. | ||||
| 
 | ||||
| config SERIAL_STM32_CONSOLE | ||||
| 	bool "Support for console on STM32" | ||||
| 	depends on SERIAL_STM32=y | ||||
| 	select SERIAL_CORE_CONSOLE | ||||
| 
 | ||||
| endmenu | ||||
| 
 | ||||
| config SERIAL_MCTRL_GPIO | ||||
|  | ||||
| @ -92,6 +92,7 @@ obj-$(CONFIG_SERIAL_FSL_LPUART)	+= fsl_lpuart.o | ||||
| obj-$(CONFIG_SERIAL_CONEXANT_DIGICOLOR)	+= digicolor-usart.o | ||||
| obj-$(CONFIG_SERIAL_MEN_Z135)	+= men_z135_uart.o | ||||
| obj-$(CONFIG_SERIAL_SPRD) += sprd_serial.o | ||||
| obj-$(CONFIG_SERIAL_STM32)	+= stm32-usart.o | ||||
| 
 | ||||
| # GPIOLIB helpers for modem control lines
 | ||||
| obj-$(CONFIG_SERIAL_MCTRL_GPIO)	+= serial_mctrl_gpio.o | ||||
|  | ||||
							
								
								
									
										739
									
								
								drivers/tty/serial/stm32-usart.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										739
									
								
								drivers/tty/serial/stm32-usart.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,739 @@ | ||||
| /*
 | ||||
|  * Copyright (C) Maxime Coquelin 2015 | ||||
|  * Author:  Maxime Coquelin <mcoquelin.stm32@gmail.com> | ||||
|  * License terms:  GNU General Public License (GPL), version 2 | ||||
|  * | ||||
|  * Inspired by st-asc.c from STMicroelectronics (c) | ||||
|  */ | ||||
| 
 | ||||
| #if defined(CONFIG_SERIAL_STM32_USART_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) | ||||
| #define SUPPORT_SYSRQ | ||||
| #endif | ||||
| 
 | ||||
| #include <linux/module.h> | ||||
| #include <linux/serial.h> | ||||
| #include <linux/console.h> | ||||
| #include <linux/sysrq.h> | ||||
| #include <linux/platform_device.h> | ||||
| #include <linux/io.h> | ||||
| #include <linux/irq.h> | ||||
| #include <linux/tty.h> | ||||
| #include <linux/tty_flip.h> | ||||
| #include <linux/delay.h> | ||||
| #include <linux/spinlock.h> | ||||
| #include <linux/pm_runtime.h> | ||||
| #include <linux/of.h> | ||||
| #include <linux/of_platform.h> | ||||
| #include <linux/serial_core.h> | ||||
| #include <linux/clk.h> | ||||
| 
 | ||||
| #define DRIVER_NAME "stm32-usart" | ||||
| 
 | ||||
| /* Register offsets */ | ||||
| #define USART_SR		0x00 | ||||
| #define USART_DR		0x04 | ||||
| #define USART_BRR		0x08 | ||||
| #define USART_CR1		0x0c | ||||
| #define USART_CR2		0x10 | ||||
| #define USART_CR3		0x14 | ||||
| #define USART_GTPR		0x18 | ||||
| 
 | ||||
| /* USART_SR */ | ||||
| #define USART_SR_PE		BIT(0) | ||||
| #define USART_SR_FE		BIT(1) | ||||
| #define USART_SR_NF		BIT(2) | ||||
| #define USART_SR_ORE		BIT(3) | ||||
| #define USART_SR_IDLE		BIT(4) | ||||
| #define USART_SR_RXNE		BIT(5) | ||||
| #define USART_SR_TC		BIT(6) | ||||
| #define USART_SR_TXE		BIT(7) | ||||
| #define USART_SR_LBD		BIT(8) | ||||
| #define USART_SR_CTS		BIT(9) | ||||
| #define USART_SR_ERR_MASK	(USART_SR_LBD | USART_SR_ORE | \ | ||||
| 				 USART_SR_FE | USART_SR_PE) | ||||
| /* Dummy bits */ | ||||
| #define USART_SR_DUMMY_RX	BIT(16) | ||||
| 
 | ||||
| /* USART_DR */ | ||||
| #define USART_DR_MASK		GENMASK(8, 0) | ||||
| 
 | ||||
| /* USART_BRR */ | ||||
| #define USART_BRR_DIV_F_MASK	GENMASK(3, 0) | ||||
| #define USART_BRR_DIV_M_MASK	GENMASK(15, 4) | ||||
| #define USART_BRR_DIV_M_SHIFT	4 | ||||
| 
 | ||||
| /* USART_CR1 */ | ||||
| #define USART_CR1_SBK		BIT(0) | ||||
| #define USART_CR1_RWU		BIT(1) | ||||
| #define USART_CR1_RE		BIT(2) | ||||
| #define USART_CR1_TE		BIT(3) | ||||
| #define USART_CR1_IDLEIE	BIT(4) | ||||
| #define USART_CR1_RXNEIE	BIT(5) | ||||
| #define USART_CR1_TCIE		BIT(6) | ||||
| #define USART_CR1_TXEIE		BIT(7) | ||||
| #define USART_CR1_PEIE		BIT(8) | ||||
| #define USART_CR1_PS		BIT(9) | ||||
| #define USART_CR1_PCE		BIT(10) | ||||
| #define USART_CR1_WAKE		BIT(11) | ||||
| #define USART_CR1_M		BIT(12) | ||||
| #define USART_CR1_UE		BIT(13) | ||||
| #define USART_CR1_OVER8		BIT(15) | ||||
| #define USART_CR1_IE_MASK	GENMASK(8, 4) | ||||
| 
 | ||||
| /* USART_CR2 */ | ||||
| #define USART_CR2_ADD_MASK	GENMASK(3, 0) | ||||
| #define USART_CR2_LBDL		BIT(5) | ||||
| #define USART_CR2_LBDIE		BIT(6) | ||||
| #define USART_CR2_LBCL		BIT(8) | ||||
| #define USART_CR2_CPHA		BIT(9) | ||||
| #define USART_CR2_CPOL		BIT(10) | ||||
| #define USART_CR2_CLKEN		BIT(11) | ||||
| #define USART_CR2_STOP_2B	BIT(13) | ||||
| #define USART_CR2_STOP_MASK	GENMASK(13, 12) | ||||
| #define USART_CR2_LINEN		BIT(14) | ||||
| 
 | ||||
| /* USART_CR3 */ | ||||
| #define USART_CR3_EIE		BIT(0) | ||||
| #define USART_CR3_IREN		BIT(1) | ||||
| #define USART_CR3_IRLP		BIT(2) | ||||
| #define USART_CR3_HDSEL		BIT(3) | ||||
| #define USART_CR3_NACK		BIT(4) | ||||
| #define USART_CR3_SCEN		BIT(5) | ||||
| #define USART_CR3_DMAR		BIT(6) | ||||
| #define USART_CR3_DMAT		BIT(7) | ||||
| #define USART_CR3_RTSE		BIT(8) | ||||
| #define USART_CR3_CTSE		BIT(9) | ||||
| #define USART_CR3_CTSIE		BIT(10) | ||||
| #define USART_CR3_ONEBIT	BIT(11) | ||||
| 
 | ||||
| /* USART_GTPR */ | ||||
| #define USART_GTPR_PSC_MASK	GENMASK(7, 0) | ||||
| #define USART_GTPR_GT_MASK	GENMASK(15, 8) | ||||
| 
 | ||||
| #define DRIVER_NAME "stm32-usart" | ||||
| #define STM32_SERIAL_NAME "ttyS" | ||||
| #define STM32_MAX_PORTS 6 | ||||
| 
 | ||||
| struct stm32_port { | ||||
| 	struct uart_port port; | ||||
| 	struct clk *clk; | ||||
| 	bool hw_flow_control; | ||||
| }; | ||||
| 
 | ||||
| static struct stm32_port stm32_ports[STM32_MAX_PORTS]; | ||||
| static struct uart_driver stm32_usart_driver; | ||||
| 
 | ||||
| static void stm32_stop_tx(struct uart_port *port); | ||||
| 
 | ||||
| static inline struct stm32_port *to_stm32_port(struct uart_port *port) | ||||
| { | ||||
| 	return container_of(port, struct stm32_port, port); | ||||
| } | ||||
| 
 | ||||
| static void stm32_set_bits(struct uart_port *port, u32 reg, u32 bits) | ||||
| { | ||||
| 	u32 val; | ||||
| 
 | ||||
| 	val = readl_relaxed(port->membase + reg); | ||||
| 	val |= bits; | ||||
| 	writel_relaxed(val, port->membase + reg); | ||||
| } | ||||
| 
 | ||||
| static void stm32_clr_bits(struct uart_port *port, u32 reg, u32 bits) | ||||
| { | ||||
| 	u32 val; | ||||
| 
 | ||||
| 	val = readl_relaxed(port->membase + reg); | ||||
| 	val &= ~bits; | ||||
| 	writel_relaxed(val, port->membase + reg); | ||||
| } | ||||
| 
 | ||||
| static void stm32_receive_chars(struct uart_port *port) | ||||
| { | ||||
| 	struct tty_port *tport = &port->state->port; | ||||
| 	unsigned long c; | ||||
| 	u32 sr; | ||||
| 	char flag; | ||||
| 
 | ||||
| 	if (port->irq_wake) | ||||
| 		pm_wakeup_event(tport->tty->dev, 0); | ||||
| 
 | ||||
| 	while ((sr = readl_relaxed(port->membase + USART_SR)) & USART_SR_RXNE) { | ||||
| 		sr |= USART_SR_DUMMY_RX; | ||||
| 		c = readl_relaxed(port->membase + USART_DR); | ||||
| 		flag = TTY_NORMAL; | ||||
| 		port->icount.rx++; | ||||
| 
 | ||||
| 		if (sr & USART_SR_ERR_MASK) { | ||||
| 			if (sr & USART_SR_LBD) { | ||||
| 				port->icount.brk++; | ||||
| 				if (uart_handle_break(port)) | ||||
| 					continue; | ||||
| 			} else if (sr & USART_SR_ORE) { | ||||
| 				port->icount.overrun++; | ||||
| 			} else if (sr & USART_SR_PE) { | ||||
| 				port->icount.parity++; | ||||
| 			} else if (sr & USART_SR_FE) { | ||||
| 				port->icount.frame++; | ||||
| 			} | ||||
| 
 | ||||
| 			sr &= port->read_status_mask; | ||||
| 
 | ||||
| 			if (sr & USART_SR_LBD) | ||||
| 				flag = TTY_BREAK; | ||||
| 			else if (sr & USART_SR_PE) | ||||
| 				flag = TTY_PARITY; | ||||
| 			else if (sr & USART_SR_FE) | ||||
| 				flag = TTY_FRAME; | ||||
| 		} | ||||
| 
 | ||||
| 		if (uart_handle_sysrq_char(port, c)) | ||||
| 			continue; | ||||
| 		uart_insert_char(port, sr, USART_SR_ORE, c, flag); | ||||
| 	} | ||||
| 
 | ||||
| 	spin_unlock(&port->lock); | ||||
| 	tty_flip_buffer_push(tport); | ||||
| 	spin_lock(&port->lock); | ||||
| } | ||||
| 
 | ||||
| static void stm32_transmit_chars(struct uart_port *port) | ||||
| { | ||||
| 	struct circ_buf *xmit = &port->state->xmit; | ||||
| 
 | ||||
| 	if (port->x_char) { | ||||
| 		writel_relaxed(port->x_char, port->membase + USART_DR); | ||||
| 		port->x_char = 0; | ||||
| 		port->icount.tx++; | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	if (uart_tx_stopped(port)) { | ||||
| 		stm32_stop_tx(port); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	if (uart_circ_empty(xmit)) { | ||||
| 		stm32_stop_tx(port); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	writel_relaxed(xmit->buf[xmit->tail], port->membase + USART_DR); | ||||
| 	xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); | ||||
| 	port->icount.tx++; | ||||
| 
 | ||||
| 	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) | ||||
| 		uart_write_wakeup(port); | ||||
| 
 | ||||
| 	if (uart_circ_empty(xmit)) | ||||
| 		stm32_stop_tx(port); | ||||
| } | ||||
| 
 | ||||
| static irqreturn_t stm32_interrupt(int irq, void *ptr) | ||||
| { | ||||
| 	struct uart_port *port = ptr; | ||||
| 	u32 sr; | ||||
| 
 | ||||
| 	spin_lock(&port->lock); | ||||
| 
 | ||||
| 	sr = readl_relaxed(port->membase + USART_SR); | ||||
| 
 | ||||
| 	if (sr & USART_SR_RXNE) | ||||
| 		stm32_receive_chars(port); | ||||
| 
 | ||||
| 	if (sr & USART_SR_TXE) | ||||
| 		stm32_transmit_chars(port); | ||||
| 
 | ||||
| 	spin_unlock(&port->lock); | ||||
| 
 | ||||
| 	return IRQ_HANDLED; | ||||
| } | ||||
| 
 | ||||
| static unsigned int stm32_tx_empty(struct uart_port *port) | ||||
| { | ||||
| 	return readl_relaxed(port->membase + USART_SR) & USART_SR_TXE; | ||||
| } | ||||
| 
 | ||||
| static void stm32_set_mctrl(struct uart_port *port, unsigned int mctrl) | ||||
| { | ||||
| 	if ((mctrl & TIOCM_RTS) && (port->status & UPSTAT_AUTORTS)) | ||||
| 		stm32_set_bits(port, USART_CR3, USART_CR3_RTSE); | ||||
| 	else | ||||
| 		stm32_clr_bits(port, USART_CR3, USART_CR3_RTSE); | ||||
| } | ||||
| 
 | ||||
| static unsigned int stm32_get_mctrl(struct uart_port *port) | ||||
| { | ||||
| 	/* This routine is used to get signals of: DCD, DSR, RI, and CTS */ | ||||
| 	return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; | ||||
| } | ||||
| 
 | ||||
| /* Transmit stop */ | ||||
| static void stm32_stop_tx(struct uart_port *port) | ||||
| { | ||||
| 	stm32_clr_bits(port, USART_CR1, USART_CR1_TXEIE); | ||||
| } | ||||
| 
 | ||||
| /* There are probably characters waiting to be transmitted. */ | ||||
| static void stm32_start_tx(struct uart_port *port) | ||||
| { | ||||
| 	struct circ_buf *xmit = &port->state->xmit; | ||||
| 
 | ||||
| 	if (uart_circ_empty(xmit)) | ||||
| 		return; | ||||
| 
 | ||||
| 	stm32_set_bits(port, USART_CR1, USART_CR1_TXEIE | USART_CR1_TE); | ||||
| } | ||||
| 
 | ||||
| /* Throttle the remote when input buffer is about to overflow. */ | ||||
| static void stm32_throttle(struct uart_port *port) | ||||
| { | ||||
| 	unsigned long flags; | ||||
| 
 | ||||
| 	spin_lock_irqsave(&port->lock, flags); | ||||
| 	stm32_clr_bits(port, USART_CR1, USART_CR1_RXNEIE); | ||||
| 	spin_unlock_irqrestore(&port->lock, flags); | ||||
| } | ||||
| 
 | ||||
| /* Unthrottle the remote, the input buffer can now accept data. */ | ||||
| static void stm32_unthrottle(struct uart_port *port) | ||||
| { | ||||
| 	unsigned long flags; | ||||
| 
 | ||||
| 	spin_lock_irqsave(&port->lock, flags); | ||||
| 	stm32_set_bits(port, USART_CR1, USART_CR1_RXNEIE); | ||||
| 	spin_unlock_irqrestore(&port->lock, flags); | ||||
| } | ||||
| 
 | ||||
| /* Receive stop */ | ||||
| static void stm32_stop_rx(struct uart_port *port) | ||||
| { | ||||
| 	stm32_clr_bits(port, USART_CR1, USART_CR1_RXNEIE); | ||||
| } | ||||
| 
 | ||||
| /* Handle breaks - ignored by us */ | ||||
| static void stm32_break_ctl(struct uart_port *port, int break_state) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| static int stm32_startup(struct uart_port *port) | ||||
| { | ||||
| 	const char *name = to_platform_device(port->dev)->name; | ||||
| 	u32 val; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = request_irq(port->irq, stm32_interrupt, IRQF_NO_SUSPEND, | ||||
| 			  name, port); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	val = USART_CR1_RXNEIE | USART_CR1_TE | USART_CR1_RE; | ||||
| 	stm32_set_bits(port, USART_CR1, val); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void stm32_shutdown(struct uart_port *port) | ||||
| { | ||||
| 	u32 val; | ||||
| 
 | ||||
| 	val = USART_CR1_TXEIE | USART_CR1_RXNEIE | USART_CR1_TE | USART_CR1_RE; | ||||
| 	stm32_set_bits(port, USART_CR1, val); | ||||
| 
 | ||||
| 	free_irq(port->irq, port); | ||||
| } | ||||
| 
 | ||||
| static void stm32_set_termios(struct uart_port *port, struct ktermios *termios, | ||||
| 			    struct ktermios *old) | ||||
| { | ||||
| 	struct stm32_port *stm32_port = to_stm32_port(port); | ||||
| 	unsigned int baud; | ||||
| 	u32 usartdiv, mantissa, fraction, oversampling; | ||||
| 	tcflag_t cflag = termios->c_cflag; | ||||
| 	u32 cr1, cr2, cr3; | ||||
| 	unsigned long flags; | ||||
| 
 | ||||
| 	if (!stm32_port->hw_flow_control) | ||||
| 		cflag &= ~CRTSCTS; | ||||
| 
 | ||||
| 	baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 8); | ||||
| 
 | ||||
| 	spin_lock_irqsave(&port->lock, flags); | ||||
| 
 | ||||
| 	/* Stop serial port and reset value */ | ||||
| 	writel_relaxed(0, port->membase + USART_CR1); | ||||
| 
 | ||||
| 	cr1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE | USART_CR1_RXNEIE; | ||||
| 	cr2 = 0; | ||||
| 	cr3 = 0; | ||||
| 
 | ||||
| 	if (cflag & CSTOPB) | ||||
| 		cr2 |= USART_CR2_STOP_2B; | ||||
| 
 | ||||
| 	if (cflag & PARENB) { | ||||
| 		cr1 |= USART_CR1_PCE; | ||||
| 		if ((cflag & CSIZE) == CS8) | ||||
| 			cr1 |= USART_CR1_M; | ||||
| 	} | ||||
| 
 | ||||
| 	if (cflag & PARODD) | ||||
| 		cr1 |= USART_CR1_PS; | ||||
| 
 | ||||
| 	port->status &= ~(UPSTAT_AUTOCTS | UPSTAT_AUTORTS); | ||||
| 	if (cflag & CRTSCTS) { | ||||
| 		port->status |= UPSTAT_AUTOCTS | UPSTAT_AUTORTS; | ||||
| 		cr3 |= USART_CR3_CTSE; | ||||
| 	} | ||||
| 
 | ||||
| 	usartdiv = DIV_ROUND_CLOSEST(port->uartclk, baud); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * The USART supports 16 or 8 times oversampling. | ||||
| 	 * By default we prefer 16 times oversampling, so that the receiver | ||||
| 	 * has a better tolerance to clock deviations. | ||||
| 	 * 8 times oversampling is only used to achieve higher speeds. | ||||
| 	 */ | ||||
| 	if (usartdiv < 16) { | ||||
| 		oversampling = 8; | ||||
| 		stm32_set_bits(port, USART_CR1, USART_CR1_OVER8); | ||||
| 	} else { | ||||
| 		oversampling = 16; | ||||
| 		stm32_clr_bits(port, USART_CR1, USART_CR1_OVER8); | ||||
| 	} | ||||
| 
 | ||||
| 	mantissa = (usartdiv / oversampling) << USART_BRR_DIV_M_SHIFT; | ||||
| 	fraction = usartdiv % oversampling; | ||||
| 	writel_relaxed(mantissa | fraction, port->membase + USART_BRR); | ||||
| 
 | ||||
| 	uart_update_timeout(port, cflag, baud); | ||||
| 
 | ||||
| 	port->read_status_mask = USART_SR_ORE; | ||||
| 	if (termios->c_iflag & INPCK) | ||||
| 		port->read_status_mask |= USART_SR_PE | USART_SR_FE; | ||||
| 	if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK)) | ||||
| 		port->read_status_mask |= USART_SR_LBD; | ||||
| 
 | ||||
| 	/* Characters to ignore */ | ||||
| 	port->ignore_status_mask = 0; | ||||
| 	if (termios->c_iflag & IGNPAR) | ||||
| 		port->ignore_status_mask = USART_SR_PE | USART_SR_FE; | ||||
| 	if (termios->c_iflag & IGNBRK) { | ||||
| 		port->ignore_status_mask |= USART_SR_LBD; | ||||
| 		/*
 | ||||
| 		 * If we're ignoring parity and break indicators, | ||||
| 		 * ignore overruns too (for real raw support). | ||||
| 		 */ | ||||
| 		if (termios->c_iflag & IGNPAR) | ||||
| 			port->ignore_status_mask |= USART_SR_ORE; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Ignore all characters if CREAD is not set */ | ||||
| 	if ((termios->c_cflag & CREAD) == 0) | ||||
| 		port->ignore_status_mask |= USART_SR_DUMMY_RX; | ||||
| 
 | ||||
| 	writel_relaxed(cr3, port->membase + USART_CR3); | ||||
| 	writel_relaxed(cr2, port->membase + USART_CR2); | ||||
| 	writel_relaxed(cr1, port->membase + USART_CR1); | ||||
| 
 | ||||
| 	spin_unlock_irqrestore(&port->lock, flags); | ||||
| } | ||||
| 
 | ||||
| static const char *stm32_type(struct uart_port *port) | ||||
| { | ||||
| 	return (port->type == PORT_STM32) ? DRIVER_NAME : NULL; | ||||
| } | ||||
| 
 | ||||
| static void stm32_release_port(struct uart_port *port) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| static int stm32_request_port(struct uart_port *port) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void stm32_config_port(struct uart_port *port, int flags) | ||||
| { | ||||
| 	if (flags & UART_CONFIG_TYPE) | ||||
| 		port->type = PORT_STM32; | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| stm32_verify_port(struct uart_port *port, struct serial_struct *ser) | ||||
| { | ||||
| 	/* No user changeable parameters */ | ||||
| 	return -EINVAL; | ||||
| } | ||||
| 
 | ||||
| static void stm32_pm(struct uart_port *port, unsigned int state, | ||||
| 		unsigned int oldstate) | ||||
| { | ||||
| 	struct stm32_port *stm32port = container_of(port, | ||||
| 			struct stm32_port, port); | ||||
| 	unsigned long flags = 0; | ||||
| 
 | ||||
| 	switch (state) { | ||||
| 	case UART_PM_STATE_ON: | ||||
| 		clk_prepare_enable(stm32port->clk); | ||||
| 		break; | ||||
| 	case UART_PM_STATE_OFF: | ||||
| 		spin_lock_irqsave(&port->lock, flags); | ||||
| 		stm32_clr_bits(port, USART_CR1, USART_CR1_UE); | ||||
| 		spin_unlock_irqrestore(&port->lock, flags); | ||||
| 		clk_disable_unprepare(stm32port->clk); | ||||
| 		break; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static const struct uart_ops stm32_uart_ops = { | ||||
| 	.tx_empty	= stm32_tx_empty, | ||||
| 	.set_mctrl	= stm32_set_mctrl, | ||||
| 	.get_mctrl	= stm32_get_mctrl, | ||||
| 	.stop_tx	= stm32_stop_tx, | ||||
| 	.start_tx	= stm32_start_tx, | ||||
| 	.throttle	= stm32_throttle, | ||||
| 	.unthrottle	= stm32_unthrottle, | ||||
| 	.stop_rx	= stm32_stop_rx, | ||||
| 	.break_ctl	= stm32_break_ctl, | ||||
| 	.startup	= stm32_startup, | ||||
| 	.shutdown	= stm32_shutdown, | ||||
| 	.set_termios	= stm32_set_termios, | ||||
| 	.pm		= stm32_pm, | ||||
| 	.type		= stm32_type, | ||||
| 	.release_port	= stm32_release_port, | ||||
| 	.request_port	= stm32_request_port, | ||||
| 	.config_port	= stm32_config_port, | ||||
| 	.verify_port	= stm32_verify_port, | ||||
| }; | ||||
| 
 | ||||
| static int stm32_init_port(struct stm32_port *stm32port, | ||||
| 			  struct platform_device *pdev) | ||||
| { | ||||
| 	struct uart_port *port = &stm32port->port; | ||||
| 	struct resource *res; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	port->iotype	= UPIO_MEM; | ||||
| 	port->flags	= UPF_BOOT_AUTOCONF; | ||||
| 	port->ops	= &stm32_uart_ops; | ||||
| 	port->dev	= &pdev->dev; | ||||
| 	port->irq	= platform_get_irq(pdev, 0); | ||||
| 
 | ||||
| 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||||
| 	port->membase = devm_ioremap_resource(&pdev->dev, res); | ||||
| 	if (IS_ERR(port->membase)) | ||||
| 		return PTR_ERR(port->membase); | ||||
| 	port->mapbase = res->start; | ||||
| 
 | ||||
| 	spin_lock_init(&port->lock); | ||||
| 
 | ||||
| 	stm32port->clk = devm_clk_get(&pdev->dev, NULL); | ||||
| 	if (IS_ERR(stm32port->clk)) | ||||
| 		return PTR_ERR(stm32port->clk); | ||||
| 
 | ||||
| 	/* Ensure that clk rate is correct by enabling the clk */ | ||||
| 	ret = clk_prepare_enable(stm32port->clk); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	stm32port->port.uartclk = clk_get_rate(stm32port->clk); | ||||
| 	if (!stm32port->port.uartclk) | ||||
| 		ret = -EINVAL; | ||||
| 
 | ||||
| 	clk_disable_unprepare(stm32port->clk); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static struct stm32_port *stm32_of_get_stm32_port(struct platform_device *pdev) | ||||
| { | ||||
| 	struct device_node *np = pdev->dev.of_node; | ||||
| 	int id; | ||||
| 
 | ||||
| 	if (!np) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	id = of_alias_get_id(np, "serial"); | ||||
| 	if (id < 0) | ||||
| 		id = 0; | ||||
| 
 | ||||
| 	if (WARN_ON(id >= STM32_MAX_PORTS)) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	stm32_ports[id].hw_flow_control = of_property_read_bool(np, | ||||
| 							"auto-flow-control"); | ||||
| 	stm32_ports[id].port.line = id; | ||||
| 	return &stm32_ports[id]; | ||||
| } | ||||
| 
 | ||||
| #ifdef CONFIG_OF | ||||
| static const struct of_device_id stm32_match[] = { | ||||
| 	{ .compatible = "st,stm32-usart", }, | ||||
| 	{ .compatible = "st,stm32-uart", }, | ||||
| 	{}, | ||||
| }; | ||||
| 
 | ||||
| MODULE_DEVICE_TABLE(of, stm32_match); | ||||
| #endif | ||||
| 
 | ||||
| static int stm32_serial_probe(struct platform_device *pdev) | ||||
| { | ||||
| 	int ret; | ||||
| 	struct stm32_port *stm32port; | ||||
| 
 | ||||
| 	stm32port = stm32_of_get_stm32_port(pdev); | ||||
| 	if (!stm32port) | ||||
| 		return -ENODEV; | ||||
| 
 | ||||
| 	ret = stm32_init_port(stm32port, pdev); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	ret = uart_add_one_port(&stm32_usart_driver, &stm32port->port); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	platform_set_drvdata(pdev, &stm32port->port); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int stm32_serial_remove(struct platform_device *pdev) | ||||
| { | ||||
| 	struct uart_port *port = platform_get_drvdata(pdev); | ||||
| 
 | ||||
| 	return uart_remove_one_port(&stm32_usart_driver, port); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| #ifdef CONFIG_SERIAL_STM32_CONSOLE | ||||
| static void stm32_console_putchar(struct uart_port *port, int ch) | ||||
| { | ||||
| 	while (!(readl_relaxed(port->membase + USART_SR) & USART_SR_TXE)) | ||||
| 		cpu_relax(); | ||||
| 
 | ||||
| 	writel_relaxed(ch, port->membase + USART_DR); | ||||
| } | ||||
| 
 | ||||
| static void stm32_console_write(struct console *co, const char *s, unsigned cnt) | ||||
| { | ||||
| 	struct uart_port *port = &stm32_ports[co->index].port; | ||||
| 	unsigned long flags; | ||||
| 	u32 old_cr1, new_cr1; | ||||
| 	int locked = 1; | ||||
| 
 | ||||
| 	local_irq_save(flags); | ||||
| 	if (port->sysrq) | ||||
| 		locked = 0; | ||||
| 	else if (oops_in_progress) | ||||
| 		locked = spin_trylock(&port->lock); | ||||
| 	else | ||||
| 		spin_lock(&port->lock); | ||||
| 
 | ||||
| 	/* Save and disable interrupts */ | ||||
| 	old_cr1 = readl_relaxed(port->membase + USART_CR1); | ||||
| 	new_cr1 = old_cr1 & ~USART_CR1_IE_MASK; | ||||
| 	writel_relaxed(new_cr1, port->membase + USART_CR1); | ||||
| 
 | ||||
| 	uart_console_write(port, s, cnt, stm32_console_putchar); | ||||
| 
 | ||||
| 	/* Restore interrupt state */ | ||||
| 	writel_relaxed(old_cr1, port->membase + USART_CR1); | ||||
| 
 | ||||
| 	if (locked) | ||||
| 		spin_unlock(&port->lock); | ||||
| 	local_irq_restore(flags); | ||||
| } | ||||
| 
 | ||||
| static int stm32_console_setup(struct console *co, char *options) | ||||
| { | ||||
| 	struct stm32_port *stm32port; | ||||
| 	int baud = 9600; | ||||
| 	int bits = 8; | ||||
| 	int parity = 'n'; | ||||
| 	int flow = 'n'; | ||||
| 
 | ||||
| 	if (co->index >= STM32_MAX_PORTS) | ||||
| 		return -ENODEV; | ||||
| 
 | ||||
| 	stm32port = &stm32_ports[co->index]; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * This driver does not support early console initialization | ||||
| 	 * (use ARM early printk support instead), so we only expect | ||||
| 	 * this to be called during the uart port registration when the | ||||
| 	 * driver gets probed and the port should be mapped at that point. | ||||
| 	 */ | ||||
| 	if (stm32port->port.mapbase == 0 || stm32port->port.membase == NULL) | ||||
| 		return -ENXIO; | ||||
| 
 | ||||
| 	if (options) | ||||
| 		uart_parse_options(options, &baud, &parity, &bits, &flow); | ||||
| 
 | ||||
| 	return uart_set_options(&stm32port->port, co, baud, parity, bits, flow); | ||||
| } | ||||
| 
 | ||||
| static struct console stm32_console = { | ||||
| 	.name		= STM32_SERIAL_NAME, | ||||
| 	.device		= uart_console_device, | ||||
| 	.write		= stm32_console_write, | ||||
| 	.setup		= stm32_console_setup, | ||||
| 	.flags		= CON_PRINTBUFFER, | ||||
| 	.index		= -1, | ||||
| 	.data		= &stm32_usart_driver, | ||||
| }; | ||||
| 
 | ||||
| #define STM32_SERIAL_CONSOLE (&stm32_console) | ||||
| 
 | ||||
| #else | ||||
| #define STM32_SERIAL_CONSOLE NULL | ||||
| #endif /* CONFIG_SERIAL_STM32_CONSOLE */ | ||||
| 
 | ||||
| static struct uart_driver stm32_usart_driver = { | ||||
| 	.driver_name	= DRIVER_NAME, | ||||
| 	.dev_name	= STM32_SERIAL_NAME, | ||||
| 	.major		= 0, | ||||
| 	.minor		= 0, | ||||
| 	.nr		= STM32_MAX_PORTS, | ||||
| 	.cons		= STM32_SERIAL_CONSOLE, | ||||
| }; | ||||
| 
 | ||||
| static struct platform_driver stm32_serial_driver = { | ||||
| 	.probe		= stm32_serial_probe, | ||||
| 	.remove		= stm32_serial_remove, | ||||
| 	.driver	= { | ||||
| 		.name	= DRIVER_NAME, | ||||
| 		.of_match_table = of_match_ptr(stm32_match), | ||||
| 	}, | ||||
| }; | ||||
| 
 | ||||
| static int __init usart_init(void) | ||||
| { | ||||
| 	static char banner[] __initdata = "STM32 USART driver initialized"; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	pr_info("%s\n", banner); | ||||
| 
 | ||||
| 	ret = uart_register_driver(&stm32_usart_driver); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	ret = platform_driver_register(&stm32_serial_driver); | ||||
| 	if (ret) | ||||
| 		uart_unregister_driver(&stm32_usart_driver); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static void __exit usart_exit(void) | ||||
| { | ||||
| 	platform_driver_unregister(&stm32_serial_driver); | ||||
| 	uart_unregister_driver(&stm32_usart_driver); | ||||
| } | ||||
| 
 | ||||
| module_init(usart_init); | ||||
| module_exit(usart_exit); | ||||
| 
 | ||||
| MODULE_ALIAS("platform:" DRIVER_NAME); | ||||
| MODULE_DESCRIPTION("STMicroelectronics STM32 serial port driver"); | ||||
| MODULE_LICENSE("GPL v2"); | ||||
| @ -258,4 +258,7 @@ | ||||
| /* Cris v10 / v32 SoC */ | ||||
| #define PORT_CRIS	112 | ||||
| 
 | ||||
| /* STM32 USART */ | ||||
| #define PORT_STM32	113 | ||||
| 
 | ||||
| #endif /* _UAPILINUX_SERIAL_CORE_H */ | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user