Message ID | 1428424234-28572-11-git-send-email-mcoquelin.stm32@gmail.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On Tue, Apr 7, 2015 at 7:30 PM, Maxime Coquelin <mcoquelin.stm32@gmail.com> wrote: > 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: Andy Shevchenko <andy.shevchenko@gmail.com> > Signed-off-by: Maxime Coquelin <mcoquelin.stm32@gmail.com> > --- > drivers/tty/serial/Kconfig | 17 + > drivers/tty/serial/Makefile | 1 + > drivers/tty/serial/stm32-usart.c | 735 +++++++++++++++++++++++++++++++++++++++ > include/uapi/linux/serial_core.h | 3 + > 4 files changed, 756 insertions(+) > create mode 100644 drivers/tty/serial/stm32-usart.c > > diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig > index d2501f0..880cb4f 100644 > --- a/drivers/tty/serial/Kconfig > +++ b/drivers/tty/serial/Kconfig > @@ -1611,6 +1611,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 > diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile > index 599be4b..67c5023 100644 > --- a/drivers/tty/serial/Makefile > +++ b/drivers/tty/serial/Makefile > @@ -95,6 +95,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 > diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c > new file mode 100644 > index 0000000..4adc430 > --- /dev/null > +++ b/drivers/tty/serial/stm32-usart.c > @@ -0,0 +1,735 @@ > +/* > + * 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; > + > + 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; > + > + if (oops_in_progress) { > + locked = spin_trylock_irqsave(&port->lock, flags); > + } else { > + locked = 1; > + spin_lock_irqsave(&port->lock, flags); > + } > + > + /* 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_irqrestore(&port->lock, 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"); > diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h > index b212281..93ba148 100644 > --- a/include/uapi/linux/serial_core.h > +++ b/include/uapi/linux/serial_core.h > @@ -258,4 +258,7 @@ > /* Cris v10 / v32 SoC */ > #define PORT_CRIS 112 > > +/* STM32 USART */ > +#define PORT_STM32 113 > + > #endif /* _UAPILINUX_SERIAL_CORE_H */ > -- > 1.9.1 >
Am 07.04.2015 um 18:30 schrieb Maxime Coquelin: > 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> > Signed-off-by: Maxime Coquelin <mcoquelin.stm32@gmail.com> > --- > drivers/tty/serial/Kconfig | 17 + > drivers/tty/serial/Makefile | 1 + > drivers/tty/serial/stm32-usart.c | 735 +++++++++++++++++++++++++++++++++++++++ > include/uapi/linux/serial_core.h | 3 + > 4 files changed, 756 insertions(+) > create mode 100644 drivers/tty/serial/stm32-usart.c [...] > diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c > new file mode 100644 > index 0000000..4adc430 > --- /dev/null > +++ b/drivers/tty/serial/stm32-usart.c [...] > +#define DRIVER_NAME "stm32-usart" > +#define STM32_SERIAL_NAME "ttyS" I'm surprised no one has complained about ttyS yet. Doesn't that need to be unique, such as ttySTM (efm32 uses ttyefm), to avoid clashes between serial drivers? ttyS was exclusive to the 8250 driver, I thought. Regards, Andreas
Hi Andreas, On 04/07/2015 12:51 PM, Andreas Färber wrote: > Am 07.04.2015 um 18:30 schrieb Maxime Coquelin: >> 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> >> Signed-off-by: Maxime Coquelin <mcoquelin.stm32@gmail.com> >> --- >> drivers/tty/serial/Kconfig | 17 + >> drivers/tty/serial/Makefile | 1 + >> drivers/tty/serial/stm32-usart.c | 735 +++++++++++++++++++++++++++++++++++++++ >> include/uapi/linux/serial_core.h | 3 + >> 4 files changed, 756 insertions(+) >> create mode 100644 drivers/tty/serial/stm32-usart.c > [...] >> diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c >> new file mode 100644 >> index 0000000..4adc430 >> --- /dev/null >> +++ b/drivers/tty/serial/stm32-usart.c > [...] >> +#define DRIVER_NAME "stm32-usart" >> +#define STM32_SERIAL_NAME "ttyS" > > I'm surprised no one has complained about ttyS yet. Doesn't that need to > be unique, such as ttySTM (efm32 uses ttyefm), to avoid clashes between > serial drivers? ttyS was exclusive to the 8250 driver, I thought. As long as this platform doesn't support 8250 h/w, I don't care; and by the time this platform is ready for multiconfig, I'll have ttyS coexistence fixed. Regards, Peter Hurley
Hi Maxime, On 07.04.2015 19:30, Maxime Coquelin wrote: > 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> > Signed-off-by: Maxime Coquelin <mcoquelin.stm32@gmail.com> > --- > drivers/tty/serial/Kconfig | 17 + > drivers/tty/serial/Makefile | 1 + > drivers/tty/serial/stm32-usart.c | 735 +++++++++++++++++++++++++++++++++++++++ > include/uapi/linux/serial_core.h | 3 + > 4 files changed, 756 insertions(+) > create mode 100644 drivers/tty/serial/stm32-usart.c [snip] > +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; > + > + if (oops_in_progress) { > + locked = spin_trylock_irqsave(&port->lock, flags); > + } else { > + locked = 1; locked = 1 is done from the declaration. Also do you want to add "if (port->sysrq) locked = 0;" check? > + spin_lock_irqsave(&port->lock, flags); > + } > + [snip] Everything else is fine from point of view. Reviewed-by: Vladimir Zapolskiy <vladimir_zapolskiy@mentor.com> -- With best wishes, Vladimir
Hi Vladimir, Thanks for the review, and sorry for the late reply. 2015-04-09 15:04 GMT+02:00 Vladimir Zapolskiy <vladimir_zapolskiy@mentor.com>: > Hi Maxime, > > On 07.04.2015 19:30, Maxime Coquelin wrote: >> 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> >> Signed-off-by: Maxime Coquelin <mcoquelin.stm32@gmail.com> >> --- >> drivers/tty/serial/Kconfig | 17 + >> drivers/tty/serial/Makefile | 1 + >> drivers/tty/serial/stm32-usart.c | 735 +++++++++++++++++++++++++++++++++++++++ >> include/uapi/linux/serial_core.h | 3 + >> 4 files changed, 756 insertions(+) >> create mode 100644 drivers/tty/serial/stm32-usart.c > > [snip] > >> +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; >> + >> + if (oops_in_progress) { >> + locked = spin_trylock_irqsave(&port->lock, flags); >> + } else { >> + locked = 1; ID > > locked = 1 is done from the declaration. Right, I will fix this in next version > > Also do you want to add "if (port->sysrq) locked = 0;" check? Yes, that makes sense. It will be also in v7. > >> + spin_lock_irqsave(&port->lock, flags); >> + } >> + > > [snip] > > Everything else is fine from point of view. > > Reviewed-by: Vladimir Zapolskiy <vladimir_zapolskiy@mentor.com> Regards, Maxime > > -- > With best wishes, > Vladimir
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index d2501f0..880cb4f 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1611,6 +1611,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 diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 599be4b..67c5023 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -95,6 +95,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 diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c new file mode 100644 index 0000000..4adc430 --- /dev/null +++ b/drivers/tty/serial/stm32-usart.c @@ -0,0 +1,735 @@ +/* + * 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; + + 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; + + if (oops_in_progress) { + locked = spin_trylock_irqsave(&port->lock, flags); + } else { + locked = 1; + spin_lock_irqsave(&port->lock, flags); + } + + /* 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_irqrestore(&port->lock, 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"); diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h index b212281..93ba148 100644 --- a/include/uapi/linux/serial_core.h +++ b/include/uapi/linux/serial_core.h @@ -258,4 +258,7 @@ /* Cris v10 / v32 SoC */ #define PORT_CRIS 112 +/* STM32 USART */ +#define PORT_STM32 113 + #endif /* _UAPILINUX_SERIAL_CORE_H */