From ada8618ff3bfe183cc2c1ae34239a5168ba34411 Mon Sep 17 00:00:00 2001 From: Alexandre TORGUE Date: Thu, 15 Sep 2016 18:42:33 +0200 Subject: serial: stm32: adding support for stm32f7 Register offset management rework to support both stm32f4 (default) and stm32f7. Driver rework to ensure same functional level on both stm32f4 and stm32f7: no new feature in this version yet. Signed-off-by: Gerald Baeza Signed-off-by: Alexandre TORGUE Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/stm32-usart.c | 269 +++++++++++++++++++++++++++++++-------- 1 file changed, 219 insertions(+), 50 deletions(-) (limited to 'drivers/tty/serial/stm32-usart.c') diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index f89d1f79be18..e7ffd01669f5 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -1,6 +1,7 @@ /* * Copyright (C) Maxime Coquelin 2015 - * Author: Maxime Coquelin + * Authors: Maxime Coquelin + * Gerald Baeza * License terms: GNU General Public License (GPL), version 2 * * Inspired by st-asc.c from STMicroelectronics (c) @@ -29,16 +30,74 @@ #define DRIVER_NAME "stm32-usart" +struct stm32_usart_offsets { + u8 cr1; + u8 cr2; + u8 cr3; + u8 brr; + u8 gtpr; + u8 rtor; + u8 rqr; + u8 isr; + u8 icr; + u8 rdr; + u8 tdr; +}; + +struct stm32_usart_config { + u8 uart_enable_bit; /* USART_CR1_UE */ + bool has_7bits_data; +}; + +struct stm32_usart_info { + struct stm32_usart_offsets ofs; + struct stm32_usart_config cfg; +}; + +#define UNDEF_REG ~0 + /* 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 */ +struct stm32_usart_info stm32f4_info = { + .ofs = { + .isr = 0x00, + .rdr = 0x04, + .tdr = 0x04, + .brr = 0x08, + .cr1 = 0x0c, + .cr2 = 0x10, + .cr3 = 0x14, + .gtpr = 0x18, + .rtor = UNDEF_REG, + .rqr = UNDEF_REG, + .icr = UNDEF_REG, + }, + .cfg = { + .uart_enable_bit = 13, + .has_7bits_data = false, + } +}; + +struct stm32_usart_info stm32f7_info = { + .ofs = { + .cr1 = 0x00, + .cr2 = 0x04, + .cr3 = 0x08, + .brr = 0x0c, + .gtpr = 0x10, + .rtor = 0x14, + .rqr = 0x18, + .isr = 0x1c, + .icr = 0x20, + .rdr = 0x24, + .tdr = 0x28, + }, + .cfg = { + .uart_enable_bit = 0, + .has_7bits_data = true, + } +}; + +/* USART_SR (F4) / USART_ISR (F7) */ #define USART_SR_PE BIT(0) #define USART_SR_FE BIT(1) #define USART_SR_NF BIT(2) @@ -48,7 +107,16 @@ #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_CTSIF BIT(9) +#define USART_SR_CTS BIT(10) /* F7 */ +#define USART_SR_RTOF BIT(11) /* F7 */ +#define USART_SR_EOBF BIT(12) /* F7 */ +#define USART_SR_ABRE BIT(14) /* F7 */ +#define USART_SR_ABRF BIT(15) /* F7 */ +#define USART_SR_BUSY BIT(16) /* F7 */ +#define USART_SR_CMF BIT(17) /* F7 */ +#define USART_SR_SBKF BIT(18) /* F7 */ +#define USART_SR_TEACK BIT(21) /* F7 */ #define USART_SR_ERR_MASK (USART_SR_LBD | USART_SR_ORE | \ USART_SR_FE | USART_SR_PE) /* Dummy bits */ @@ -64,7 +132,7 @@ /* USART_CR1 */ #define USART_CR1_SBK BIT(0) -#define USART_CR1_RWU BIT(1) +#define USART_CR1_RWU BIT(1) /* F4 */ #define USART_CR1_RE BIT(2) #define USART_CR1_TE BIT(3) #define USART_CR1_IDLEIE BIT(4) @@ -76,12 +144,20 @@ #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_M0 BIT(12) /* F7 */ +#define USART_CR1_MME BIT(13) /* F7 */ +#define USART_CR1_CMIE BIT(14) /* F7 */ #define USART_CR1_OVER8 BIT(15) -#define USART_CR1_IE_MASK GENMASK(8, 4) +#define USART_CR1_DEDT_MASK GENMASK(20, 16) /* F7 */ +#define USART_CR1_DEAT_MASK GENMASK(25, 21) /* F7 */ +#define USART_CR1_RTOIE BIT(26) /* F7 */ +#define USART_CR1_EOBIE BIT(27) /* F7 */ +#define USART_CR1_M1 BIT(28) /* F7 */ +#define USART_CR1_IE_MASK (GENMASK(8, 4) | BIT(14) | BIT(26) | BIT(27)) /* USART_CR2 */ -#define USART_CR2_ADD_MASK GENMASK(3, 0) +#define USART_CR2_ADD_MASK GENMASK(3, 0) /* F4 */ +#define USART_CR2_ADDM7 BIT(4) /* F7 */ #define USART_CR2_LBDL BIT(5) #define USART_CR2_LBDIE BIT(6) #define USART_CR2_LBCL BIT(8) @@ -91,6 +167,15 @@ #define USART_CR2_STOP_2B BIT(13) #define USART_CR2_STOP_MASK GENMASK(13, 12) #define USART_CR2_LINEN BIT(14) +#define USART_CR2_SWAP BIT(15) /* F7 */ +#define USART_CR2_RXINV BIT(16) /* F7 */ +#define USART_CR2_TXINV BIT(17) /* F7 */ +#define USART_CR2_DATAINV BIT(18) /* F7 */ +#define USART_CR2_MSBFIRST BIT(19) /* F7 */ +#define USART_CR2_ABREN BIT(20) /* F7 */ +#define USART_CR2_ABRMOD_MASK GENMASK(22, 21) /* F7 */ +#define USART_CR2_RTOEN BIT(23) /* F7 */ +#define USART_CR2_ADD_F7_MASK GENMASK(31, 24) /* F7 */ /* USART_CR3 */ #define USART_CR3_EIE BIT(0) @@ -105,18 +190,47 @@ #define USART_CR3_CTSE BIT(9) #define USART_CR3_CTSIE BIT(10) #define USART_CR3_ONEBIT BIT(11) +#define USART_CR3_OVRDIS BIT(12) /* F7 */ +#define USART_CR3_DDRE BIT(13) /* F7 */ +#define USART_CR3_DEM BIT(14) /* F7 */ +#define USART_CR3_DEP BIT(15) /* F7 */ +#define USART_CR3_SCARCNT_MASK GENMASK(19, 17) /* F7 */ /* USART_GTPR */ #define USART_GTPR_PSC_MASK GENMASK(7, 0) #define USART_GTPR_GT_MASK GENMASK(15, 8) -#define DRIVER_NAME "stm32-usart" +/* USART_RTOR */ +#define USART_RTOR_RTO_MASK GENMASK(23, 0) /* F7 */ +#define USART_RTOR_BLEN_MASK GENMASK(31, 24) /* F7 */ + +/* USART_RQR */ +#define USART_RQR_ABRRQ BIT(0) /* F7 */ +#define USART_RQR_SBKRQ BIT(1) /* F7 */ +#define USART_RQR_MMRQ BIT(2) /* F7 */ +#define USART_RQR_RXFRQ BIT(3) /* F7 */ +#define USART_RQR_TXFRQ BIT(4) /* F7 */ + +/* USART_ICR */ +#define USART_ICR_PECF BIT(0) /* F7 */ +#define USART_ICR_FFECF BIT(1) /* F7 */ +#define USART_ICR_NCF BIT(2) /* F7 */ +#define USART_ICR_ORECF BIT(3) /* F7 */ +#define USART_ICR_IDLECF BIT(4) /* F7 */ +#define USART_ICR_TCCF BIT(6) /* F7 */ +#define USART_ICR_LBDCF BIT(8) /* F7 */ +#define USART_ICR_CTSCF BIT(9) /* F7 */ +#define USART_ICR_RTOCF BIT(11) /* F7 */ +#define USART_ICR_EOBCF BIT(12) /* F7 */ +#define USART_ICR_CMCF BIT(17) /* F7 */ + #define STM32_SERIAL_NAME "ttyS" #define STM32_MAX_PORTS 6 struct stm32_port { struct uart_port port; struct clk *clk; + struct stm32_usart_info *info; bool hw_flow_control; }; @@ -151,6 +265,8 @@ static void stm32_clr_bits(struct uart_port *port, u32 reg, u32 bits) static void stm32_receive_chars(struct uart_port *port) { struct tty_port *tport = &port->state->port; + struct stm32_port *stm32_port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; unsigned long c; u32 sr; char flag; @@ -158,9 +274,9 @@ static void stm32_receive_chars(struct uart_port *port) if (port->irq_wake) pm_wakeup_event(tport->tty->dev, 0); - while ((sr = readl_relaxed(port->membase + USART_SR)) & USART_SR_RXNE) { + while ((sr = readl_relaxed(port->membase + ofs->isr)) & USART_SR_RXNE) { sr |= USART_SR_DUMMY_RX; - c = readl_relaxed(port->membase + USART_DR); + c = readl_relaxed(port->membase + ofs->rdr); flag = TTY_NORMAL; port->icount.rx++; @@ -170,6 +286,10 @@ static void stm32_receive_chars(struct uart_port *port) if (uart_handle_break(port)) continue; } else if (sr & USART_SR_ORE) { + if (ofs->icr != UNDEF_REG) + writel_relaxed(USART_ICR_ORECF, + port->membase + + ofs->icr); port->icount.overrun++; } else if (sr & USART_SR_PE) { port->icount.parity++; @@ -199,10 +319,12 @@ static void stm32_receive_chars(struct uart_port *port) static void stm32_transmit_chars(struct uart_port *port) { + struct stm32_port *stm32_port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; struct circ_buf *xmit = &port->state->xmit; if (port->x_char) { - writel_relaxed(port->x_char, port->membase + USART_DR); + writel_relaxed(port->x_char, port->membase + ofs->tdr); port->x_char = 0; port->icount.tx++; return; @@ -218,7 +340,7 @@ static void stm32_transmit_chars(struct uart_port *port) return; } - writel_relaxed(xmit->buf[xmit->tail], port->membase + USART_DR); + writel_relaxed(xmit->buf[xmit->tail], port->membase + ofs->tdr); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); port->icount.tx++; @@ -232,11 +354,13 @@ static void stm32_transmit_chars(struct uart_port *port) static irqreturn_t stm32_interrupt(int irq, void *ptr) { struct uart_port *port = ptr; + struct stm32_port *stm32_port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; u32 sr; spin_lock(&port->lock); - sr = readl_relaxed(port->membase + USART_SR); + sr = readl_relaxed(port->membase + ofs->isr); if (sr & USART_SR_RXNE) stm32_receive_chars(port); @@ -251,15 +375,21 @@ static irqreturn_t stm32_interrupt(int irq, void *ptr) static unsigned int stm32_tx_empty(struct uart_port *port) { - return readl_relaxed(port->membase + USART_SR) & USART_SR_TXE; + struct stm32_port *stm32_port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; + + return readl_relaxed(port->membase + ofs->isr) & USART_SR_TXE; } static void stm32_set_mctrl(struct uart_port *port, unsigned int mctrl) { + struct stm32_port *stm32_port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; + if ((mctrl & TIOCM_RTS) && (port->status & UPSTAT_AUTORTS)) - stm32_set_bits(port, USART_CR3, USART_CR3_RTSE); + stm32_set_bits(port, ofs->cr3, USART_CR3_RTSE); else - stm32_clr_bits(port, USART_CR3, USART_CR3_RTSE); + stm32_clr_bits(port, ofs->cr3, USART_CR3_RTSE); } static unsigned int stm32_get_mctrl(struct uart_port *port) @@ -271,44 +401,56 @@ static unsigned int stm32_get_mctrl(struct uart_port *port) /* Transmit stop */ static void stm32_stop_tx(struct uart_port *port) { - stm32_clr_bits(port, USART_CR1, USART_CR1_TXEIE); + struct stm32_port *stm32_port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; + + stm32_clr_bits(port, ofs->cr1, USART_CR1_TXEIE); } /* There are probably characters waiting to be transmitted. */ static void stm32_start_tx(struct uart_port *port) { + struct stm32_port *stm32_port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; 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); + stm32_set_bits(port, ofs->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) { + struct stm32_port *stm32_port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; unsigned long flags; spin_lock_irqsave(&port->lock, flags); - stm32_clr_bits(port, USART_CR1, USART_CR1_RXNEIE); + stm32_clr_bits(port, ofs->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) { + struct stm32_port *stm32_port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; unsigned long flags; spin_lock_irqsave(&port->lock, flags); - stm32_set_bits(port, USART_CR1, USART_CR1_RXNEIE); + stm32_set_bits(port, ofs->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); + struct stm32_port *stm32_port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; + + stm32_clr_bits(port, ofs->cr1, USART_CR1_RXNEIE); } /* Handle breaks - ignored by us */ @@ -318,6 +460,8 @@ static void stm32_break_ctl(struct uart_port *port, int break_state) static int stm32_startup(struct uart_port *port) { + struct stm32_port *stm32_port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; const char *name = to_platform_device(port->dev)->name; u32 val; int ret; @@ -327,17 +471,19 @@ static int stm32_startup(struct uart_port *port) return ret; val = USART_CR1_RXNEIE | USART_CR1_TE | USART_CR1_RE; - stm32_set_bits(port, USART_CR1, val); + stm32_set_bits(port, ofs->cr1, val); return 0; } static void stm32_shutdown(struct uart_port *port) { + struct stm32_port *stm32_port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; u32 val; val = USART_CR1_TXEIE | USART_CR1_RXNEIE | USART_CR1_TE | USART_CR1_RE; - stm32_set_bits(port, USART_CR1, val); + stm32_set_bits(port, ofs->cr1, val); free_irq(port->irq, port); } @@ -346,6 +492,8 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { struct stm32_port *stm32_port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; + struct stm32_usart_config *cfg = &stm32_port->info->cfg; unsigned int baud; u32 usartdiv, mantissa, fraction, oversampling; tcflag_t cflag = termios->c_cflag; @@ -360,9 +508,10 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios, spin_lock_irqsave(&port->lock, flags); /* Stop serial port and reset value */ - writel_relaxed(0, port->membase + USART_CR1); + writel_relaxed(0, port->membase + ofs->cr1); - cr1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE | USART_CR1_RXNEIE; + cr1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_RXNEIE; + cr1 |= BIT(cfg->uart_enable_bit); cr2 = 0; cr3 = 0; @@ -371,8 +520,12 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios, if (cflag & PARENB) { cr1 |= USART_CR1_PCE; - if ((cflag & CSIZE) == CS8) - cr1 |= USART_CR1_M; + if ((cflag & CSIZE) == CS8) { + if (cfg->has_7bits_data) + cr1 |= USART_CR1_M0; + else + cr1 |= USART_CR1_M; + } } if (cflag & PARODD) @@ -394,15 +547,15 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios, */ if (usartdiv < 16) { oversampling = 8; - stm32_set_bits(port, USART_CR1, USART_CR1_OVER8); + stm32_set_bits(port, ofs->cr1, USART_CR1_OVER8); } else { oversampling = 16; - stm32_clr_bits(port, USART_CR1, USART_CR1_OVER8); + stm32_clr_bits(port, ofs->cr1, USART_CR1_OVER8); } mantissa = (usartdiv / oversampling) << USART_BRR_DIV_M_SHIFT; fraction = usartdiv % oversampling; - writel_relaxed(mantissa | fraction, port->membase + USART_BRR); + writel_relaxed(mantissa | fraction, port->membase + ofs->brr); uart_update_timeout(port, cflag, baud); @@ -430,9 +583,9 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios, 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); + writel_relaxed(cr3, port->membase + ofs->cr3); + writel_relaxed(cr2, port->membase + ofs->cr2); + writel_relaxed(cr1, port->membase + ofs->cr1); spin_unlock_irqrestore(&port->lock, flags); } @@ -469,6 +622,8 @@ static void stm32_pm(struct uart_port *port, unsigned int state, { struct stm32_port *stm32port = container_of(port, struct stm32_port, port); + struct stm32_usart_offsets *ofs = &stm32port->info->ofs; + struct stm32_usart_config *cfg = &stm32port->info->cfg; unsigned long flags = 0; switch (state) { @@ -477,7 +632,7 @@ static void stm32_pm(struct uart_port *port, unsigned int state, break; case UART_PM_STATE_OFF: spin_lock_irqsave(&port->lock, flags); - stm32_clr_bits(port, USART_CR1, USART_CR1_UE); + stm32_clr_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit)); spin_unlock_irqrestore(&port->lock, flags); clk_disable_unprepare(stm32port->clk); break; @@ -567,8 +722,10 @@ static struct stm32_port *stm32_of_get_stm32_port(struct platform_device *pdev) #ifdef CONFIG_OF static const struct of_device_id stm32_match[] = { - { .compatible = "st,stm32-usart", }, - { .compatible = "st,stm32-uart", }, + { .compatible = "st,stm32-usart", .data = &stm32f4_info}, + { .compatible = "st,stm32-uart", .data = &stm32f4_info}, + { .compatible = "st,stm32f7-usart", .data = &stm32f7_info}, + { .compatible = "st,stm32f7-uart", .data = &stm32f7_info}, {}, }; @@ -577,13 +734,20 @@ MODULE_DEVICE_TABLE(of, stm32_match); static int stm32_serial_probe(struct platform_device *pdev) { - int ret; + const struct of_device_id *match; struct stm32_port *stm32port; + int ret; stm32port = stm32_of_get_stm32_port(pdev); if (!stm32port) return -ENODEV; + match = of_match_device(stm32_match, &pdev->dev); + if (match && match->data) + stm32port->info = (struct stm32_usart_info *)match->data; + else + return -EINVAL; + ret = stm32_init_port(stm32port, pdev); if (ret) return ret; @@ -608,15 +772,20 @@ static int stm32_serial_remove(struct platform_device *pdev) #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)) + struct stm32_port *stm32_port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; + + while (!(readl_relaxed(port->membase + ofs->isr) & USART_SR_TXE)) cpu_relax(); - writel_relaxed(ch, port->membase + USART_DR); + writel_relaxed(ch, port->membase + ofs->tdr); } static void stm32_console_write(struct console *co, const char *s, unsigned cnt) { struct uart_port *port = &stm32_ports[co->index].port; + struct stm32_port *stm32_port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; unsigned long flags; u32 old_cr1, new_cr1; int locked = 1; @@ -630,14 +799,14 @@ static void stm32_console_write(struct console *co, const char *s, unsigned cnt) spin_lock(&port->lock); /* Save and disable interrupts */ - old_cr1 = readl_relaxed(port->membase + USART_CR1); + old_cr1 = readl_relaxed(port->membase + ofs->cr1); new_cr1 = old_cr1 & ~USART_CR1_IE_MASK; - writel_relaxed(new_cr1, port->membase + USART_CR1); + writel_relaxed(new_cr1, port->membase + ofs->cr1); uart_console_write(port, s, cnt, stm32_console_putchar); /* Restore interrupt state */ - writel_relaxed(old_cr1, port->membase + USART_CR1); + writel_relaxed(old_cr1, port->membase + ofs->cr1); if (locked) spin_unlock(&port->lock); -- cgit v1.2.3 From bc5a0b55ba944cfe847cc2210609c44ff9dccd06 Mon Sep 17 00:00:00 2001 From: Alexandre TORGUE Date: Thu, 15 Sep 2016 18:42:35 +0200 Subject: serial: stm32: header file creation Signed-off-by: Gerald Baeza Signed-off-by: Alexandre TORGUE Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/stm32-usart.c | 209 +------------------------------------ drivers/tty/serial/stm32-usart.h | 215 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 216 insertions(+), 208 deletions(-) create mode 100644 drivers/tty/serial/stm32-usart.h (limited to 'drivers/tty/serial/stm32-usart.c') diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index e7ffd01669f5..6236deb28905 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -28,214 +28,7 @@ #include #include -#define DRIVER_NAME "stm32-usart" - -struct stm32_usart_offsets { - u8 cr1; - u8 cr2; - u8 cr3; - u8 brr; - u8 gtpr; - u8 rtor; - u8 rqr; - u8 isr; - u8 icr; - u8 rdr; - u8 tdr; -}; - -struct stm32_usart_config { - u8 uart_enable_bit; /* USART_CR1_UE */ - bool has_7bits_data; -}; - -struct stm32_usart_info { - struct stm32_usart_offsets ofs; - struct stm32_usart_config cfg; -}; - -#define UNDEF_REG ~0 - -/* Register offsets */ -struct stm32_usart_info stm32f4_info = { - .ofs = { - .isr = 0x00, - .rdr = 0x04, - .tdr = 0x04, - .brr = 0x08, - .cr1 = 0x0c, - .cr2 = 0x10, - .cr3 = 0x14, - .gtpr = 0x18, - .rtor = UNDEF_REG, - .rqr = UNDEF_REG, - .icr = UNDEF_REG, - }, - .cfg = { - .uart_enable_bit = 13, - .has_7bits_data = false, - } -}; - -struct stm32_usart_info stm32f7_info = { - .ofs = { - .cr1 = 0x00, - .cr2 = 0x04, - .cr3 = 0x08, - .brr = 0x0c, - .gtpr = 0x10, - .rtor = 0x14, - .rqr = 0x18, - .isr = 0x1c, - .icr = 0x20, - .rdr = 0x24, - .tdr = 0x28, - }, - .cfg = { - .uart_enable_bit = 0, - .has_7bits_data = true, - } -}; - -/* USART_SR (F4) / USART_ISR (F7) */ -#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_CTSIF BIT(9) -#define USART_SR_CTS BIT(10) /* F7 */ -#define USART_SR_RTOF BIT(11) /* F7 */ -#define USART_SR_EOBF BIT(12) /* F7 */ -#define USART_SR_ABRE BIT(14) /* F7 */ -#define USART_SR_ABRF BIT(15) /* F7 */ -#define USART_SR_BUSY BIT(16) /* F7 */ -#define USART_SR_CMF BIT(17) /* F7 */ -#define USART_SR_SBKF BIT(18) /* F7 */ -#define USART_SR_TEACK BIT(21) /* F7 */ -#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) /* F4 */ -#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_M0 BIT(12) /* F7 */ -#define USART_CR1_MME BIT(13) /* F7 */ -#define USART_CR1_CMIE BIT(14) /* F7 */ -#define USART_CR1_OVER8 BIT(15) -#define USART_CR1_DEDT_MASK GENMASK(20, 16) /* F7 */ -#define USART_CR1_DEAT_MASK GENMASK(25, 21) /* F7 */ -#define USART_CR1_RTOIE BIT(26) /* F7 */ -#define USART_CR1_EOBIE BIT(27) /* F7 */ -#define USART_CR1_M1 BIT(28) /* F7 */ -#define USART_CR1_IE_MASK (GENMASK(8, 4) | BIT(14) | BIT(26) | BIT(27)) - -/* USART_CR2 */ -#define USART_CR2_ADD_MASK GENMASK(3, 0) /* F4 */ -#define USART_CR2_ADDM7 BIT(4) /* F7 */ -#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) -#define USART_CR2_SWAP BIT(15) /* F7 */ -#define USART_CR2_RXINV BIT(16) /* F7 */ -#define USART_CR2_TXINV BIT(17) /* F7 */ -#define USART_CR2_DATAINV BIT(18) /* F7 */ -#define USART_CR2_MSBFIRST BIT(19) /* F7 */ -#define USART_CR2_ABREN BIT(20) /* F7 */ -#define USART_CR2_ABRMOD_MASK GENMASK(22, 21) /* F7 */ -#define USART_CR2_RTOEN BIT(23) /* F7 */ -#define USART_CR2_ADD_F7_MASK GENMASK(31, 24) /* F7 */ - -/* 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) -#define USART_CR3_OVRDIS BIT(12) /* F7 */ -#define USART_CR3_DDRE BIT(13) /* F7 */ -#define USART_CR3_DEM BIT(14) /* F7 */ -#define USART_CR3_DEP BIT(15) /* F7 */ -#define USART_CR3_SCARCNT_MASK GENMASK(19, 17) /* F7 */ - -/* USART_GTPR */ -#define USART_GTPR_PSC_MASK GENMASK(7, 0) -#define USART_GTPR_GT_MASK GENMASK(15, 8) - -/* USART_RTOR */ -#define USART_RTOR_RTO_MASK GENMASK(23, 0) /* F7 */ -#define USART_RTOR_BLEN_MASK GENMASK(31, 24) /* F7 */ - -/* USART_RQR */ -#define USART_RQR_ABRRQ BIT(0) /* F7 */ -#define USART_RQR_SBKRQ BIT(1) /* F7 */ -#define USART_RQR_MMRQ BIT(2) /* F7 */ -#define USART_RQR_RXFRQ BIT(3) /* F7 */ -#define USART_RQR_TXFRQ BIT(4) /* F7 */ - -/* USART_ICR */ -#define USART_ICR_PECF BIT(0) /* F7 */ -#define USART_ICR_FFECF BIT(1) /* F7 */ -#define USART_ICR_NCF BIT(2) /* F7 */ -#define USART_ICR_ORECF BIT(3) /* F7 */ -#define USART_ICR_IDLECF BIT(4) /* F7 */ -#define USART_ICR_TCCF BIT(6) /* F7 */ -#define USART_ICR_LBDCF BIT(8) /* F7 */ -#define USART_ICR_CTSCF BIT(9) /* F7 */ -#define USART_ICR_RTOCF BIT(11) /* F7 */ -#define USART_ICR_EOBCF BIT(12) /* F7 */ -#define USART_ICR_CMCF BIT(17) /* F7 */ - -#define STM32_SERIAL_NAME "ttyS" -#define STM32_MAX_PORTS 6 - -struct stm32_port { - struct uart_port port; - struct clk *clk; - struct stm32_usart_info *info; - bool hw_flow_control; -}; - -static struct stm32_port stm32_ports[STM32_MAX_PORTS]; -static struct uart_driver stm32_usart_driver; +#include "stm32-usart.h" static void stm32_stop_tx(struct uart_port *port); diff --git a/drivers/tty/serial/stm32-usart.h b/drivers/tty/serial/stm32-usart.h new file mode 100644 index 000000000000..75f8388c91df --- /dev/null +++ b/drivers/tty/serial/stm32-usart.h @@ -0,0 +1,215 @@ +/* + * Copyright (C) Maxime Coquelin 2015 + * Authors: Maxime Coquelin + * Gerald Baeza + * License terms: GNU General Public License (GPL), version 2 + */ + +#define DRIVER_NAME "stm32-usart" + +struct stm32_usart_offsets { + u8 cr1; + u8 cr2; + u8 cr3; + u8 brr; + u8 gtpr; + u8 rtor; + u8 rqr; + u8 isr; + u8 icr; + u8 rdr; + u8 tdr; +}; + +struct stm32_usart_config { + u8 uart_enable_bit; /* USART_CR1_UE */ + bool has_7bits_data; +}; + +struct stm32_usart_info { + struct stm32_usart_offsets ofs; + struct stm32_usart_config cfg; +}; + +#define UNDEF_REG ~0 + +/* Register offsets */ +struct stm32_usart_info stm32f4_info = { + .ofs = { + .isr = 0x00, + .rdr = 0x04, + .tdr = 0x04, + .brr = 0x08, + .cr1 = 0x0c, + .cr2 = 0x10, + .cr3 = 0x14, + .gtpr = 0x18, + .rtor = UNDEF_REG, + .rqr = UNDEF_REG, + .icr = UNDEF_REG, + }, + .cfg = { + .uart_enable_bit = 13, + .has_7bits_data = false, + } +}; + +struct stm32_usart_info stm32f7_info = { + .ofs = { + .cr1 = 0x00, + .cr2 = 0x04, + .cr3 = 0x08, + .brr = 0x0c, + .gtpr = 0x10, + .rtor = 0x14, + .rqr = 0x18, + .isr = 0x1c, + .icr = 0x20, + .rdr = 0x24, + .tdr = 0x28, + }, + .cfg = { + .uart_enable_bit = 0, + .has_7bits_data = true, + } +}; + +/* USART_SR (F4) / USART_ISR (F7) */ +#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_CTSIF BIT(9) +#define USART_SR_CTS BIT(10) /* F7 */ +#define USART_SR_RTOF BIT(11) /* F7 */ +#define USART_SR_EOBF BIT(12) /* F7 */ +#define USART_SR_ABRE BIT(14) /* F7 */ +#define USART_SR_ABRF BIT(15) /* F7 */ +#define USART_SR_BUSY BIT(16) /* F7 */ +#define USART_SR_CMF BIT(17) /* F7 */ +#define USART_SR_SBKF BIT(18) /* F7 */ +#define USART_SR_TEACK BIT(21) /* F7 */ +#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) /* F4 */ +#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_M0 BIT(12) /* F7 */ +#define USART_CR1_MME BIT(13) /* F7 */ +#define USART_CR1_CMIE BIT(14) /* F7 */ +#define USART_CR1_OVER8 BIT(15) +#define USART_CR1_DEDT_MASK GENMASK(20, 16) /* F7 */ +#define USART_CR1_DEAT_MASK GENMASK(25, 21) /* F7 */ +#define USART_CR1_RTOIE BIT(26) /* F7 */ +#define USART_CR1_EOBIE BIT(27) /* F7 */ +#define USART_CR1_M1 BIT(28) /* F7 */ +#define USART_CR1_IE_MASK (GENMASK(8, 4) | BIT(14) | BIT(26) | BIT(27)) + +/* USART_CR2 */ +#define USART_CR2_ADD_MASK GENMASK(3, 0) /* F4 */ +#define USART_CR2_ADDM7 BIT(4) /* F7 */ +#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) +#define USART_CR2_SWAP BIT(15) /* F7 */ +#define USART_CR2_RXINV BIT(16) /* F7 */ +#define USART_CR2_TXINV BIT(17) /* F7 */ +#define USART_CR2_DATAINV BIT(18) /* F7 */ +#define USART_CR2_MSBFIRST BIT(19) /* F7 */ +#define USART_CR2_ABREN BIT(20) /* F7 */ +#define USART_CR2_ABRMOD_MASK GENMASK(22, 21) /* F7 */ +#define USART_CR2_RTOEN BIT(23) /* F7 */ +#define USART_CR2_ADD_F7_MASK GENMASK(31, 24) /* F7 */ + +/* 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) +#define USART_CR3_OVRDIS BIT(12) /* F7 */ +#define USART_CR3_DDRE BIT(13) /* F7 */ +#define USART_CR3_DEM BIT(14) /* F7 */ +#define USART_CR3_DEP BIT(15) /* F7 */ +#define USART_CR3_SCARCNT_MASK GENMASK(19, 17) /* F7 */ + +/* USART_GTPR */ +#define USART_GTPR_PSC_MASK GENMASK(7, 0) +#define USART_GTPR_GT_MASK GENMASK(15, 8) + +/* USART_RTOR */ +#define USART_RTOR_RTO_MASK GENMASK(23, 0) /* F7 */ +#define USART_RTOR_BLEN_MASK GENMASK(31, 24) /* F7 */ + +/* USART_RQR */ +#define USART_RQR_ABRRQ BIT(0) /* F7 */ +#define USART_RQR_SBKRQ BIT(1) /* F7 */ +#define USART_RQR_MMRQ BIT(2) /* F7 */ +#define USART_RQR_RXFRQ BIT(3) /* F7 */ +#define USART_RQR_TXFRQ BIT(4) /* F7 */ + +/* USART_ICR */ +#define USART_ICR_PECF BIT(0) /* F7 */ +#define USART_ICR_FFECF BIT(1) /* F7 */ +#define USART_ICR_NCF BIT(2) /* F7 */ +#define USART_ICR_ORECF BIT(3) /* F7 */ +#define USART_ICR_IDLECF BIT(4) /* F7 */ +#define USART_ICR_TCCF BIT(6) /* F7 */ +#define USART_ICR_LBDCF BIT(8) /* F7 */ +#define USART_ICR_CTSCF BIT(9) /* F7 */ +#define USART_ICR_RTOCF BIT(11) /* F7 */ +#define USART_ICR_EOBCF BIT(12) /* F7 */ +#define USART_ICR_CMCF BIT(17) /* F7 */ + +#define STM32_SERIAL_NAME "ttyS" +#define STM32_MAX_PORTS 6 + +struct stm32_port { + struct uart_port port; + struct clk *clk; + struct stm32_usart_info *info; + bool hw_flow_control; +}; + +static struct stm32_port stm32_ports[STM32_MAX_PORTS]; +static struct uart_driver stm32_usart_driver; -- cgit v1.2.3 From a14f66a427f8c960ca9cf1048f1bbf7607f85809 Mon Sep 17 00:00:00 2001 From: Alexandre TORGUE Date: Thu, 15 Sep 2016 18:42:36 +0200 Subject: serial: stm32: disable tx and rx during shutdown Signed-off-by: Gerald Baeza Signed-off-by: Alexandre TORGUE Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/stm32-usart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/tty/serial/stm32-usart.c') diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index 6236deb28905..184b0183bb38 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -276,7 +276,7 @@ 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, ofs->cr1, val); + stm32_clr_bits(port, ofs->cr1, val); free_irq(port->irq, port); } -- cgit v1.2.3 From 59bed2dfe03e9e572bfb5a2d29effc1791eedcbc Mon Sep 17 00:00:00 2001 From: Alexandre TORGUE Date: Thu, 15 Sep 2016 18:42:37 +0200 Subject: serial: stm32: correct flow control property spelling "st,hw-flow-ctrl" property is documented in device tree binding whereas "auto-flow-control" was used in the code. The driver is now aligned with the binding name "st,hw-flow-ctrl". Signed-off-by: Gerald Baeza Signed-off-by: Alexandre TORGUE Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/stm32-usart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/tty/serial/stm32-usart.c') diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index 184b0183bb38..ab294b927b67 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -508,7 +508,7 @@ static struct stm32_port *stm32_of_get_stm32_port(struct platform_device *pdev) return NULL; stm32_ports[id].hw_flow_control = of_property_read_bool(np, - "auto-flow-control"); + "st,hw-flow-ctrl"); stm32_ports[id].port.line = id; return &stm32_ports[id]; } -- cgit v1.2.3 From 511c7b1baa8ae86dbcf83b8f489d39ff4b64a123 Mon Sep 17 00:00:00 2001 From: Alexandre TORGUE Date: Thu, 15 Sep 2016 18:42:38 +0200 Subject: serial: stm32: clock disabling management Keep the clock enabled at the end of stm32_init_port but disable it in stm32_serial_remove. Note that stm32_pm function is there to manage the clock at runtime. Signed-off-by: Gerald Baeza Signed-off-by: Alexandre TORGUE Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/stm32-usart.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/tty/serial/stm32-usart.c') diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index ab294b927b67..520e7defc08d 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -487,8 +487,6 @@ static int stm32_init_port(struct stm32_port *stm32port, if (!stm32port->port.uartclk) ret = -EINVAL; - clk_disable_unprepare(stm32port->clk); - return ret; } @@ -557,6 +555,9 @@ static int stm32_serial_probe(struct platform_device *pdev) static int stm32_serial_remove(struct platform_device *pdev) { struct uart_port *port = platform_get_drvdata(pdev); + struct stm32_port *stm32_port = to_stm32_port(port); + + clk_disable_unprepare(stm32_port->clk); return uart_remove_one_port(&stm32_usart_driver, port); } -- cgit v1.2.3 From 3489187204eb75e5635d8836babfd0a18be613f4 Mon Sep 17 00:00:00 2001 From: Alexandre TORGUE Date: Thu, 15 Sep 2016 18:42:40 +0200 Subject: serial: stm32: adding dma support This patch adds dma mode support for rx and tx with pio mode as fallback in case of dma error. Signed-off-by: Gerald Baeza Signed-off-by: Alexandre TORGUE Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/stm32-usart.c | 389 ++++++++++++++++++++++++++++++++++++--- drivers/tty/serial/stm32-usart.h | 14 ++ 2 files changed, 378 insertions(+), 25 deletions(-) (limited to 'drivers/tty/serial/stm32-usart.c') diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index 520e7defc08d..24c4d821472a 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -11,26 +11,31 @@ #define SUPPORT_SYSRQ #endif -#include -#include +#include #include -#include -#include +#include +#include +#include +#include #include +#include #include -#include -#include -#include -#include -#include +#include #include #include +#include +#include #include -#include +#include +#include +#include +#include +#include #include "stm32-usart.h" static void stm32_stop_tx(struct uart_port *port); +static void stm32_transmit_chars(struct uart_port *port); static inline struct stm32_port *to_stm32_port(struct uart_port *port) { @@ -55,7 +60,48 @@ static void stm32_clr_bits(struct uart_port *port, u32 reg, u32 bits) writel_relaxed(val, port->membase + reg); } -static void stm32_receive_chars(struct uart_port *port) +int stm32_pending_rx(struct uart_port *port, u32 *sr, int *last_res, + bool threaded) +{ + struct stm32_port *stm32_port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; + enum dma_status status; + struct dma_tx_state state; + + *sr = readl_relaxed(port->membase + ofs->isr); + + if (threaded && stm32_port->rx_ch) { + status = dmaengine_tx_status(stm32_port->rx_ch, + stm32_port->rx_ch->cookie, + &state); + if ((status == DMA_IN_PROGRESS) && + (*last_res != state.residue)) + return 1; + else + return 0; + } else if (*sr & USART_SR_RXNE) { + return 1; + } + return 0; +} + +unsigned long stm32_get_char(struct uart_port *port, u32 *sr, int *last_res) +{ + struct stm32_port *stm32_port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; + unsigned long c; + + if (stm32_port->rx_ch) { + c = stm32_port->rx_buf[RX_BUF_L - (*last_res)--]; + if ((*last_res) == 0) + *last_res = RX_BUF_L; + return c; + } else { + return readl_relaxed(port->membase + ofs->rdr); + } +} + +static void stm32_receive_chars(struct uart_port *port, bool threaded) { struct tty_port *tport = &port->state->port; struct stm32_port *stm32_port = to_stm32_port(port); @@ -63,13 +109,14 @@ static void stm32_receive_chars(struct uart_port *port) unsigned long c; u32 sr; char flag; + static int last_res = RX_BUF_L; if (port->irq_wake) pm_wakeup_event(tport->tty->dev, 0); - while ((sr = readl_relaxed(port->membase + ofs->isr)) & USART_SR_RXNE) { + while (stm32_pending_rx(port, &sr, &last_res, threaded)) { sr |= USART_SR_DUMMY_RX; - c = readl_relaxed(port->membase + ofs->rdr); + c = stm32_get_char(port, &sr, &last_res); flag = TTY_NORMAL; port->icount.rx++; @@ -110,6 +157,124 @@ static void stm32_receive_chars(struct uart_port *port) spin_lock(&port->lock); } +static void stm32_tx_dma_complete(void *arg) +{ + struct uart_port *port = arg; + struct stm32_port *stm32port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32port->info->ofs; + unsigned int isr; + int ret; + + ret = readl_relaxed_poll_timeout_atomic(port->membase + ofs->isr, + isr, + (isr & USART_SR_TC), + 10, 100000); + + if (ret) + dev_err(port->dev, "terminal count not set\n"); + + if (ofs->icr == UNDEF_REG) + stm32_clr_bits(port, ofs->isr, USART_SR_TC); + else + stm32_set_bits(port, ofs->icr, USART_CR_TC); + + stm32_clr_bits(port, ofs->cr3, USART_CR3_DMAT); + stm32port->tx_dma_busy = false; + + /* Let's see if we have pending data to send */ + stm32_transmit_chars(port); +} + +static void stm32_transmit_chars_pio(struct uart_port *port) +{ + struct stm32_port *stm32_port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; + struct circ_buf *xmit = &port->state->xmit; + unsigned int isr; + int ret; + + if (stm32_port->tx_dma_busy) { + stm32_clr_bits(port, ofs->cr3, USART_CR3_DMAT); + stm32_port->tx_dma_busy = false; + } + + ret = readl_relaxed_poll_timeout_atomic(port->membase + ofs->isr, + isr, + (isr & USART_SR_TXE), + 10, 100); + + if (ret) + dev_err(port->dev, "tx empty not set\n"); + + stm32_set_bits(port, ofs->cr1, USART_CR1_TXEIE); + + writel_relaxed(xmit->buf[xmit->tail], port->membase + ofs->tdr); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; +} + +static void stm32_transmit_chars_dma(struct uart_port *port) +{ + struct stm32_port *stm32port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32port->info->ofs; + struct circ_buf *xmit = &port->state->xmit; + struct dma_async_tx_descriptor *desc = NULL; + dma_cookie_t cookie; + unsigned int count, i; + + if (stm32port->tx_dma_busy) + return; + + stm32port->tx_dma_busy = true; + + count = uart_circ_chars_pending(xmit); + + if (count > TX_BUF_L) + count = TX_BUF_L; + + if (xmit->tail < xmit->head) { + memcpy(&stm32port->tx_buf[0], &xmit->buf[xmit->tail], count); + } else { + size_t one = UART_XMIT_SIZE - xmit->tail; + size_t two; + + if (one > count) + one = count; + two = count - one; + + memcpy(&stm32port->tx_buf[0], &xmit->buf[xmit->tail], one); + if (two) + memcpy(&stm32port->tx_buf[one], &xmit->buf[0], two); + } + + desc = dmaengine_prep_slave_single(stm32port->tx_ch, + stm32port->tx_dma_buf, + count, + DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT); + + if (!desc) { + for (i = count; i > 0; i--) + stm32_transmit_chars_pio(port); + return; + } + + desc->callback = stm32_tx_dma_complete; + desc->callback_param = port; + + /* Push current DMA TX transaction in the pending queue */ + cookie = dmaengine_submit(desc); + + /* Issue pending DMA TX requests */ + dma_async_issue_pending(stm32port->tx_ch); + + stm32_clr_bits(port, ofs->isr, USART_SR_TC); + stm32_set_bits(port, ofs->cr3, USART_CR3_DMAT); + + xmit->tail = (xmit->tail + count) & (UART_XMIT_SIZE - 1); + port->icount.tx += count; +} + static void stm32_transmit_chars(struct uart_port *port) { struct stm32_port *stm32_port = to_stm32_port(port); @@ -117,9 +282,13 @@ static void stm32_transmit_chars(struct uart_port *port) struct circ_buf *xmit = &port->state->xmit; if (port->x_char) { + if (stm32_port->tx_dma_busy) + stm32_clr_bits(port, ofs->cr3, USART_CR3_DMAT); writel_relaxed(port->x_char, port->membase + ofs->tdr); port->x_char = 0; port->icount.tx++; + if (stm32_port->tx_dma_busy) + stm32_set_bits(port, ofs->cr3, USART_CR3_DMAT); return; } @@ -133,9 +302,10 @@ static void stm32_transmit_chars(struct uart_port *port) return; } - writel_relaxed(xmit->buf[xmit->tail], port->membase + ofs->tdr); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - port->icount.tx++; + if (stm32_port->tx_ch) + stm32_transmit_chars_dma(port); + else + stm32_transmit_chars_pio(port); if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(port); @@ -151,16 +321,30 @@ static irqreturn_t stm32_interrupt(int irq, void *ptr) struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; u32 sr; - spin_lock(&port->lock); - sr = readl_relaxed(port->membase + ofs->isr); - if (sr & USART_SR_RXNE) - stm32_receive_chars(port); + if ((sr & USART_SR_RXNE) && !(stm32_port->rx_ch)) + stm32_receive_chars(port, false); - if (sr & USART_SR_TXE) + if ((sr & USART_SR_TXE) && !(stm32_port->tx_ch)) stm32_transmit_chars(port); + if (stm32_port->rx_ch) + return IRQ_WAKE_THREAD; + else + return IRQ_HANDLED; +} + +static irqreturn_t stm32_threaded_interrupt(int irq, void *ptr) +{ + struct uart_port *port = ptr; + struct stm32_port *stm32_port = to_stm32_port(port); + + spin_lock(&port->lock); + + if (stm32_port->rx_ch) + stm32_receive_chars(port, true); + spin_unlock(&port->lock); return IRQ_HANDLED; @@ -203,14 +387,12 @@ static void stm32_stop_tx(struct uart_port *port) /* There are probably characters waiting to be transmitted. */ static void stm32_start_tx(struct uart_port *port) { - struct stm32_port *stm32_port = to_stm32_port(port); - struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; struct circ_buf *xmit = &port->state->xmit; if (uart_circ_empty(xmit)) return; - stm32_set_bits(port, ofs->cr1, USART_CR1_TXEIE | USART_CR1_TE); + stm32_transmit_chars(port); } /* Throttle the remote when input buffer is about to overflow. */ @@ -259,7 +441,9 @@ static int stm32_startup(struct uart_port *port) u32 val; int ret; - ret = request_irq(port->irq, stm32_interrupt, 0, name, port); + ret = request_threaded_irq(port->irq, stm32_interrupt, + stm32_threaded_interrupt, + IRQF_NO_SUSPEND, name, port); if (ret) return ret; @@ -376,6 +560,9 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios, if ((termios->c_cflag & CREAD) == 0) port->ignore_status_mask |= USART_SR_DUMMY_RX; + if (stm32_port->rx_ch) + cr3 |= USART_CR3_DMAR; + writel_relaxed(cr3, port->membase + ofs->cr3); writel_relaxed(cr2, port->membase + ofs->cr2); writel_relaxed(cr1, port->membase + ofs->cr1); @@ -523,6 +710,129 @@ static const struct of_device_id stm32_match[] = { MODULE_DEVICE_TABLE(of, stm32_match); #endif +static int stm32_of_dma_rx_probe(struct stm32_port *stm32port, + struct platform_device *pdev) +{ + struct stm32_usart_offsets *ofs = &stm32port->info->ofs; + struct uart_port *port = &stm32port->port; + struct device *dev = &pdev->dev; + struct dma_slave_config config; + struct dma_async_tx_descriptor *desc = NULL; + dma_cookie_t cookie; + int ret; + + /* Request DMA RX channel */ + stm32port->rx_ch = dma_request_slave_channel(dev, "rx"); + if (!stm32port->rx_ch) { + dev_info(dev, "rx dma alloc failed\n"); + return -ENODEV; + } + stm32port->rx_buf = dma_alloc_coherent(&pdev->dev, RX_BUF_L, + &stm32port->rx_dma_buf, + GFP_KERNEL); + if (!stm32port->rx_buf) { + ret = -ENOMEM; + goto alloc_err; + } + + /* Configure DMA channel */ + memset(&config, 0, sizeof(config)); + config.src_addr = (dma_addr_t)port->membase + ofs->rdr; + config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + + ret = dmaengine_slave_config(stm32port->rx_ch, &config); + if (ret < 0) { + dev_err(dev, "rx dma channel config failed\n"); + ret = -ENODEV; + goto config_err; + } + + /* Prepare a DMA cyclic transaction */ + desc = dmaengine_prep_dma_cyclic(stm32port->rx_ch, + stm32port->rx_dma_buf, + RX_BUF_L, RX_BUF_P, DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT); + if (!desc) { + dev_err(dev, "rx dma prep cyclic failed\n"); + ret = -ENODEV; + goto config_err; + } + + /* No callback as dma buffer is drained on usart interrupt */ + desc->callback = NULL; + desc->callback_param = NULL; + + /* Push current DMA transaction in the pending queue */ + cookie = dmaengine_submit(desc); + + /* Issue pending DMA requests */ + dma_async_issue_pending(stm32port->rx_ch); + + return 0; + +config_err: + dma_free_coherent(&pdev->dev, + RX_BUF_L, stm32port->rx_buf, + stm32port->rx_dma_buf); + +alloc_err: + dma_release_channel(stm32port->rx_ch); + stm32port->rx_ch = NULL; + + return ret; +} + +static int stm32_of_dma_tx_probe(struct stm32_port *stm32port, + struct platform_device *pdev) +{ + struct stm32_usart_offsets *ofs = &stm32port->info->ofs; + struct uart_port *port = &stm32port->port; + struct device *dev = &pdev->dev; + struct dma_slave_config config; + int ret; + + stm32port->tx_dma_busy = false; + + /* Request DMA TX channel */ + stm32port->tx_ch = dma_request_slave_channel(dev, "tx"); + if (!stm32port->tx_ch) { + dev_info(dev, "tx dma alloc failed\n"); + return -ENODEV; + } + stm32port->tx_buf = dma_alloc_coherent(&pdev->dev, TX_BUF_L, + &stm32port->tx_dma_buf, + GFP_KERNEL); + if (!stm32port->tx_buf) { + ret = -ENOMEM; + goto alloc_err; + } + + /* Configure DMA channel */ + memset(&config, 0, sizeof(config)); + config.dst_addr = (dma_addr_t)port->membase + ofs->tdr; + config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + + ret = dmaengine_slave_config(stm32port->tx_ch, &config); + if (ret < 0) { + dev_err(dev, "tx dma channel config failed\n"); + ret = -ENODEV; + goto config_err; + } + + return 0; + +config_err: + dma_free_coherent(&pdev->dev, + TX_BUF_L, stm32port->tx_buf, + stm32port->tx_dma_buf); + +alloc_err: + dma_release_channel(stm32port->tx_ch); + stm32port->tx_ch = NULL; + + return ret; +} + static int stm32_serial_probe(struct platform_device *pdev) { const struct of_device_id *match; @@ -547,6 +857,14 @@ static int stm32_serial_probe(struct platform_device *pdev) if (ret) return ret; + ret = stm32_of_dma_rx_probe(stm32port, pdev); + if (ret) + dev_info(&pdev->dev, "interrupt mode used for rx (no dma)\n"); + + ret = stm32_of_dma_tx_probe(stm32port, pdev); + if (ret) + dev_info(&pdev->dev, "interrupt mode used for tx (no dma)\n"); + platform_set_drvdata(pdev, &stm32port->port); return 0; @@ -556,6 +874,27 @@ static int stm32_serial_remove(struct platform_device *pdev) { struct uart_port *port = platform_get_drvdata(pdev); struct stm32_port *stm32_port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; + + stm32_clr_bits(port, ofs->cr3, USART_CR3_DMAR); + + if (stm32_port->rx_ch) + dma_release_channel(stm32_port->rx_ch); + + if (stm32_port->rx_dma_buf) + dma_free_coherent(&pdev->dev, + RX_BUF_L, stm32_port->rx_buf, + stm32_port->rx_dma_buf); + + stm32_clr_bits(port, ofs->cr3, USART_CR3_DMAT); + + if (stm32_port->tx_ch) + dma_release_channel(stm32_port->tx_ch); + + if (stm32_port->tx_dma_buf) + dma_free_coherent(&pdev->dev, + TX_BUF_L, stm32_port->tx_buf, + stm32_port->tx_dma_buf); clk_disable_unprepare(stm32_port->clk); diff --git a/drivers/tty/serial/stm32-usart.h b/drivers/tty/serial/stm32-usart.h index 75f8388c91df..41d974923102 100644 --- a/drivers/tty/serial/stm32-usart.h +++ b/drivers/tty/serial/stm32-usart.h @@ -99,6 +99,9 @@ struct stm32_usart_info stm32f7_info = { /* Dummy bits */ #define USART_SR_DUMMY_RX BIT(16) +/* USART_ICR (F7) */ +#define USART_CR_TC BIT(6) + /* USART_DR */ #define USART_DR_MASK GENMASK(8, 0) @@ -204,10 +207,21 @@ struct stm32_usart_info stm32f7_info = { #define STM32_SERIAL_NAME "ttyS" #define STM32_MAX_PORTS 6 +#define RX_BUF_L 200 /* dma rx buffer length */ +#define RX_BUF_P RX_BUF_L /* dma rx buffer period */ +#define TX_BUF_L 200 /* dma tx buffer length */ + struct stm32_port { struct uart_port port; struct clk *clk; struct stm32_usart_info *info; + struct dma_chan *rx_ch; /* dma rx channel */ + dma_addr_t rx_dma_buf; /* dma rx buffer bus address */ + unsigned char *rx_buf; /* dma rx buffer cpu address */ + struct dma_chan *tx_ch; /* dma tx channel */ + dma_addr_t tx_dma_buf; /* dma tx buffer bus address */ + unsigned char *tx_buf; /* dma tx buffer cpu address */ + bool tx_dma_busy; /* dma tx busy */ bool hw_flow_control; }; -- cgit v1.2.3 From 01d32d71610b0c56ce2c56bec370e275607a96e7 Mon Sep 17 00:00:00 2001 From: Alexandre TORGUE Date: Thu, 15 Sep 2016 18:42:41 +0200 Subject: serial: stm32: fix spin_lock management Signed-off-by: Gerald Baeza Signed-off-by: Alexandre TORGUE Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/stm32-usart.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/tty/serial/stm32-usart.c') diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index 24c4d821472a..3b99d790fb3f 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -321,6 +321,8 @@ static irqreturn_t stm32_interrupt(int irq, void *ptr) struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; u32 sr; + spin_lock(&port->lock); + sr = readl_relaxed(port->membase + ofs->isr); if ((sr & USART_SR_RXNE) && !(stm32_port->rx_ch)) @@ -329,6 +331,8 @@ static irqreturn_t stm32_interrupt(int irq, void *ptr) if ((sr & USART_SR_TXE) && !(stm32_port->tx_ch)) stm32_transmit_chars(port); + spin_unlock(&port->lock); + if (stm32_port->rx_ch) return IRQ_WAKE_THREAD; else -- cgit v1.2.3 From 87f1f809c9b90903e6a9ee0a8356a2303fd4270d Mon Sep 17 00:00:00 2001 From: Alexandre TORGUE Date: Thu, 15 Sep 2016 18:42:42 +0200 Subject: serial: stm32: fix uart enable management Signed-off-by: Gerald Baeza Signed-off-by: Alexandre TORGUE Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/stm32-usart.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/tty/serial/stm32-usart.c') diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index 3b99d790fb3f..4d3001b77e7e 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -461,9 +461,11 @@ static void stm32_shutdown(struct uart_port *port) { struct stm32_port *stm32_port = to_stm32_port(port); struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; + struct stm32_usart_config *cfg = &stm32_port->info->cfg; u32 val; val = USART_CR1_TXEIE | USART_CR1_RXNEIE | USART_CR1_TE | USART_CR1_RE; + val |= BIT(cfg->uart_enable_bit); stm32_clr_bits(port, ofs->cr1, val); free_irq(port->irq, port); @@ -923,6 +925,7 @@ static void stm32_console_write(struct console *co, const char *s, unsigned cnt) struct uart_port *port = &stm32_ports[co->index].port; struct stm32_port *stm32_port = to_stm32_port(port); struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; + struct stm32_usart_config *cfg = &stm32_port->info->cfg; unsigned long flags; u32 old_cr1, new_cr1; int locked = 1; @@ -935,9 +938,10 @@ static void stm32_console_write(struct console *co, const char *s, unsigned cnt) else spin_lock(&port->lock); - /* Save and disable interrupts */ + /* Save and disable interrupts, enable the transmitter */ old_cr1 = readl_relaxed(port->membase + ofs->cr1); new_cr1 = old_cr1 & ~USART_CR1_IE_MASK; + new_cr1 |= USART_CR1_TE | BIT(cfg->uart_enable_bit); writel_relaxed(new_cr1, port->membase + ofs->cr1); uart_console_write(port, s, cnt, stm32_console_putchar); -- cgit v1.2.3 From 8e5481d98bbf1de0ff06a3e488b668572d578e61 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 23 Sep 2016 21:38:51 +0200 Subject: serial: stm32: use mapbase instead of membase for DMA Building this driver with a 64-bit dma_addr_t type results in a compiler warning: drivers/tty/serial/stm32-usart.c: In function 'stm32_of_dma_rx_probe': drivers/tty/serial/stm32-usart.c:746:20: error: cast from pointer to integer of different size [-Werror=pointer-to-int-cast] drivers/tty/serial/stm32-usart.c: In function 'stm32_of_dma_tx_probe': drivers/tty/serial/stm32-usart.c:818:20: error: cast from pointer to integer of different size [-Werror=pointer-to-int-cast] While the type conversion here is harmless, this hints at a different problem: we pass an __iomem pointer into a DMA engine, which expects a phys_addr_t. This happens to work because stm32 has no MMU and ioremap() is an identity mapping here, but it's still an incorrect API use. Using dma_addr_t is doubly wrong here, because that would be the result of dma_map_single() rather than the physical address. Using the mapbase instead fixes multiple issues: - the warning is gone - we don't go through ioremap in error - the cast is gone, making it use the correct resource_size_t/phys_addr_t type in the process. Fixes: 3489187204eb ("serial: stm32: adding dma support") Signed-off-by: Arnd Bergmann Reviewed-by: Gerald Baeza Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/stm32-usart.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/tty/serial/stm32-usart.c') diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index 4d3001b77e7e..2adb678a863b 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -743,7 +743,7 @@ static int stm32_of_dma_rx_probe(struct stm32_port *stm32port, /* Configure DMA channel */ memset(&config, 0, sizeof(config)); - config.src_addr = (dma_addr_t)port->membase + ofs->rdr; + config.src_addr = port->mapbase + ofs->rdr; config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; ret = dmaengine_slave_config(stm32port->rx_ch, &config); @@ -815,7 +815,7 @@ static int stm32_of_dma_tx_probe(struct stm32_port *stm32port, /* Configure DMA channel */ memset(&config, 0, sizeof(config)); - config.dst_addr = (dma_addr_t)port->membase + ofs->tdr; + config.dst_addr = port->mapbase + ofs->tdr; config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; ret = dmaengine_slave_config(stm32port->tx_ch, &config); -- cgit v1.2.3 From b97055bcf17551196a498e624b544c3e9c804ef0 Mon Sep 17 00:00:00 2001 From: Baoyou Xie Date: Mon, 26 Sep 2016 19:58:56 +0800 Subject: serial: stm32: mark symbols static where possible We get 2 warnings when building kernel with W=1: drivers/tty/serial/stm32-usart.c:63:5: warning: no previous prototype for 'stm32_pending_rx' [-Wmissing-prototypes] drivers/tty/serial/stm32-usart.c:88:15: warning: no previous prototype for 'stm32_get_char' [-Wmissing-prototypes] In fact, these two functions are only used in the file in which they are declared and don't need a declaration, but can be made static. So this patch marks these functions with 'static'. Signed-off-by: Baoyou Xie Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/stm32-usart.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/tty/serial/stm32-usart.c') diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index 2adb678a863b..033856287ca2 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -60,8 +60,8 @@ static void stm32_clr_bits(struct uart_port *port, u32 reg, u32 bits) writel_relaxed(val, port->membase + reg); } -int stm32_pending_rx(struct uart_port *port, u32 *sr, int *last_res, - bool threaded) +static int stm32_pending_rx(struct uart_port *port, u32 *sr, int *last_res, + bool threaded) { struct stm32_port *stm32_port = to_stm32_port(port); struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; @@ -85,7 +85,8 @@ int stm32_pending_rx(struct uart_port *port, u32 *sr, int *last_res, return 0; } -unsigned long stm32_get_char(struct uart_port *port, u32 *sr, int *last_res) +static unsigned long +stm32_get_char(struct uart_port *port, u32 *sr, int *last_res) { struct stm32_port *stm32_port = to_stm32_port(port); struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; -- cgit v1.2.3