mirror of
https://github.com/torvalds/linux.git
synced 2024-11-16 17:12:06 +00:00
6333a48506
Data may be stored in DMA RX buffer, when suspending. The data needs to be pushed to the upper layer. We can't rely on the timeout IRQ (RTOR) that can't be triggered into low power state. So safely clear DMA request (DMAR), force the DMA reception routines to push RX buffer content, before disabling RX DMA. This way, handover to pio mode is safe. Only call tty_flip_buffer_push() when there is RX data to handle. Move the locking outside of stm32_usart_receive_chars() to prevent a race condition, when disabling DMA request upon suspend / pm_runtime_suspend. Data may be received under IRQ and pushed before stm32_usart_receive_chars() has pushed older data from DMA rx_buf upon suspend. The sequence in suspend routine needs proper locking to avoid this. Signed-off-by: Fabrice Gasnier <fabrice.gasnier@foss.st.com> Signed-off-by: Erwan Le Ray <erwan.leray@foss.st.com> Link: https://lore.kernel.org/r/20211025134229.8456-4-erwan.leray@foss.st.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1857 lines
50 KiB
C
1857 lines
50 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (C) Maxime Coquelin 2015
|
|
* Copyright (C) STMicroelectronics SA 2017
|
|
* Authors: Maxime Coquelin <mcoquelin.stm32@gmail.com>
|
|
* Gerald Baeza <gerald.baeza@foss.st.com>
|
|
* Erwan Le Ray <erwan.leray@foss.st.com>
|
|
*
|
|
* Inspired by st-asc.c from STMicroelectronics (c)
|
|
*/
|
|
|
|
#include <linux/clk.h>
|
|
#include <linux/console.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/dma-direction.h>
|
|
#include <linux/dmaengine.h>
|
|
#include <linux/dma-mapping.h>
|
|
#include <linux/io.h>
|
|
#include <linux/iopoll.h>
|
|
#include <linux/irq.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_platform.h>
|
|
#include <linux/pinctrl/consumer.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/pm_runtime.h>
|
|
#include <linux/pm_wakeirq.h>
|
|
#include <linux/serial_core.h>
|
|
#include <linux/serial.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/sysrq.h>
|
|
#include <linux/tty_flip.h>
|
|
#include <linux/tty.h>
|
|
|
|
#include "serial_mctrl_gpio.h"
|
|
#include "stm32-usart.h"
|
|
|
|
static void stm32_usart_stop_tx(struct uart_port *port);
|
|
static void stm32_usart_transmit_chars(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_usart_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_usart_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_usart_config_reg_rs485(u32 *cr1, u32 *cr3, u32 delay_ADE,
|
|
u32 delay_DDE, u32 baud)
|
|
{
|
|
u32 rs485_deat_dedt;
|
|
u32 rs485_deat_dedt_max = (USART_CR1_DEAT_MASK >> USART_CR1_DEAT_SHIFT);
|
|
bool over8;
|
|
|
|
*cr3 |= USART_CR3_DEM;
|
|
over8 = *cr1 & USART_CR1_OVER8;
|
|
|
|
if (over8)
|
|
rs485_deat_dedt = delay_ADE * baud * 8;
|
|
else
|
|
rs485_deat_dedt = delay_ADE * baud * 16;
|
|
|
|
rs485_deat_dedt = DIV_ROUND_CLOSEST(rs485_deat_dedt, 1000);
|
|
rs485_deat_dedt = rs485_deat_dedt > rs485_deat_dedt_max ?
|
|
rs485_deat_dedt_max : rs485_deat_dedt;
|
|
rs485_deat_dedt = (rs485_deat_dedt << USART_CR1_DEAT_SHIFT) &
|
|
USART_CR1_DEAT_MASK;
|
|
*cr1 |= rs485_deat_dedt;
|
|
|
|
if (over8)
|
|
rs485_deat_dedt = delay_DDE * baud * 8;
|
|
else
|
|
rs485_deat_dedt = delay_DDE * baud * 16;
|
|
|
|
rs485_deat_dedt = DIV_ROUND_CLOSEST(rs485_deat_dedt, 1000);
|
|
rs485_deat_dedt = rs485_deat_dedt > rs485_deat_dedt_max ?
|
|
rs485_deat_dedt_max : rs485_deat_dedt;
|
|
rs485_deat_dedt = (rs485_deat_dedt << USART_CR1_DEDT_SHIFT) &
|
|
USART_CR1_DEDT_MASK;
|
|
*cr1 |= rs485_deat_dedt;
|
|
}
|
|
|
|
static int stm32_usart_config_rs485(struct uart_port *port,
|
|
struct serial_rs485 *rs485conf)
|
|
{
|
|
struct stm32_port *stm32_port = to_stm32_port(port);
|
|
const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
|
|
const struct stm32_usart_config *cfg = &stm32_port->info->cfg;
|
|
u32 usartdiv, baud, cr1, cr3;
|
|
bool over8;
|
|
|
|
stm32_usart_clr_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit));
|
|
|
|
port->rs485 = *rs485conf;
|
|
|
|
rs485conf->flags |= SER_RS485_RX_DURING_TX;
|
|
|
|
if (rs485conf->flags & SER_RS485_ENABLED) {
|
|
cr1 = readl_relaxed(port->membase + ofs->cr1);
|
|
cr3 = readl_relaxed(port->membase + ofs->cr3);
|
|
usartdiv = readl_relaxed(port->membase + ofs->brr);
|
|
usartdiv = usartdiv & GENMASK(15, 0);
|
|
over8 = cr1 & USART_CR1_OVER8;
|
|
|
|
if (over8)
|
|
usartdiv = usartdiv | (usartdiv & GENMASK(4, 0))
|
|
<< USART_BRR_04_R_SHIFT;
|
|
|
|
baud = DIV_ROUND_CLOSEST(port->uartclk, usartdiv);
|
|
stm32_usart_config_reg_rs485(&cr1, &cr3,
|
|
rs485conf->delay_rts_before_send,
|
|
rs485conf->delay_rts_after_send,
|
|
baud);
|
|
|
|
if (rs485conf->flags & SER_RS485_RTS_ON_SEND) {
|
|
cr3 &= ~USART_CR3_DEP;
|
|
rs485conf->flags &= ~SER_RS485_RTS_AFTER_SEND;
|
|
} else {
|
|
cr3 |= USART_CR3_DEP;
|
|
rs485conf->flags |= SER_RS485_RTS_AFTER_SEND;
|
|
}
|
|
|
|
writel_relaxed(cr3, port->membase + ofs->cr3);
|
|
writel_relaxed(cr1, port->membase + ofs->cr1);
|
|
} else {
|
|
stm32_usart_clr_bits(port, ofs->cr3,
|
|
USART_CR3_DEM | USART_CR3_DEP);
|
|
stm32_usart_clr_bits(port, ofs->cr1,
|
|
USART_CR1_DEDT_MASK | USART_CR1_DEAT_MASK);
|
|
}
|
|
|
|
stm32_usart_set_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int stm32_usart_init_rs485(struct uart_port *port,
|
|
struct platform_device *pdev)
|
|
{
|
|
struct serial_rs485 *rs485conf = &port->rs485;
|
|
|
|
rs485conf->flags = 0;
|
|
rs485conf->delay_rts_before_send = 0;
|
|
rs485conf->delay_rts_after_send = 0;
|
|
|
|
if (!pdev->dev.of_node)
|
|
return -ENODEV;
|
|
|
|
return uart_get_rs485_mode(port);
|
|
}
|
|
|
|
static bool stm32_usart_rx_dma_enabled(struct uart_port *port)
|
|
{
|
|
struct stm32_port *stm32_port = to_stm32_port(port);
|
|
const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
|
|
|
|
if (!stm32_port->rx_ch)
|
|
return false;
|
|
|
|
return !!(readl_relaxed(port->membase + ofs->cr3) & USART_CR3_DMAR);
|
|
}
|
|
|
|
/* Return true when data is pending (in pio mode), and false when no data is pending. */
|
|
static bool stm32_usart_pending_rx_pio(struct uart_port *port, u32 *sr)
|
|
{
|
|
struct stm32_port *stm32_port = to_stm32_port(port);
|
|
const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
|
|
|
|
*sr = readl_relaxed(port->membase + ofs->isr);
|
|
/* Get pending characters in RDR or FIFO */
|
|
if (*sr & USART_SR_RXNE) {
|
|
/* Get all pending characters from the RDR or the FIFO when using interrupts */
|
|
if (!stm32_usart_rx_dma_enabled(port))
|
|
return true;
|
|
|
|
/* Handle only RX data errors when using DMA */
|
|
if (*sr & USART_SR_ERR_MASK)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static unsigned long stm32_usart_get_char_pio(struct uart_port *port)
|
|
{
|
|
struct stm32_port *stm32_port = to_stm32_port(port);
|
|
const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
|
|
unsigned long c;
|
|
|
|
c = readl_relaxed(port->membase + ofs->rdr);
|
|
/* Apply RDR data mask */
|
|
c &= stm32_port->rdr_mask;
|
|
|
|
return c;
|
|
}
|
|
|
|
static unsigned int stm32_usart_receive_chars_pio(struct uart_port *port)
|
|
{
|
|
struct stm32_port *stm32_port = to_stm32_port(port);
|
|
const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
|
|
unsigned long c;
|
|
unsigned int size = 0;
|
|
u32 sr;
|
|
char flag;
|
|
|
|
while (stm32_usart_pending_rx_pio(port, &sr)) {
|
|
sr |= USART_SR_DUMMY_RX;
|
|
flag = TTY_NORMAL;
|
|
|
|
/*
|
|
* Status bits has to be cleared before reading the RDR:
|
|
* In FIFO mode, reading the RDR will pop the next data
|
|
* (if any) along with its status bits into the SR.
|
|
* Not doing so leads to misalignement between RDR and SR,
|
|
* and clear status bits of the next rx data.
|
|
*
|
|
* Clear errors flags for stm32f7 and stm32h7 compatible
|
|
* devices. On stm32f4 compatible devices, the error bit is
|
|
* cleared by the sequence [read SR - read DR].
|
|
*/
|
|
if ((sr & USART_SR_ERR_MASK) && ofs->icr != UNDEF_REG)
|
|
writel_relaxed(sr & USART_SR_ERR_MASK,
|
|
port->membase + ofs->icr);
|
|
|
|
c = stm32_usart_get_char_pio(port);
|
|
port->icount.rx++;
|
|
size++;
|
|
if (sr & USART_SR_ERR_MASK) {
|
|
if (sr & USART_SR_ORE) {
|
|
port->icount.overrun++;
|
|
} else if (sr & USART_SR_PE) {
|
|
port->icount.parity++;
|
|
} else if (sr & USART_SR_FE) {
|
|
/* Break detection if character is null */
|
|
if (!c) {
|
|
port->icount.brk++;
|
|
if (uart_handle_break(port))
|
|
continue;
|
|
} else {
|
|
port->icount.frame++;
|
|
}
|
|
}
|
|
|
|
sr &= port->read_status_mask;
|
|
|
|
if (sr & USART_SR_PE) {
|
|
flag = TTY_PARITY;
|
|
} else if (sr & USART_SR_FE) {
|
|
if (!c)
|
|
flag = TTY_BREAK;
|
|
else
|
|
flag = TTY_FRAME;
|
|
}
|
|
}
|
|
|
|
if (uart_prepare_sysrq_char(port, c))
|
|
continue;
|
|
uart_insert_char(port, sr, USART_SR_ORE, c, flag);
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
static void stm32_usart_push_buffer_dma(struct uart_port *port, unsigned int dma_size)
|
|
{
|
|
struct stm32_port *stm32_port = to_stm32_port(port);
|
|
struct tty_port *ttyport = &stm32_port->port.state->port;
|
|
unsigned char *dma_start;
|
|
int dma_count, i;
|
|
|
|
dma_start = stm32_port->rx_buf + (RX_BUF_L - stm32_port->last_res);
|
|
|
|
/*
|
|
* Apply rdr_mask on buffer in order to mask parity bit.
|
|
* This loop is useless in cs8 mode because DMA copies only
|
|
* 8 bits and already ignores parity bit.
|
|
*/
|
|
if (!(stm32_port->rdr_mask == (BIT(8) - 1)))
|
|
for (i = 0; i < dma_size; i++)
|
|
*(dma_start + i) &= stm32_port->rdr_mask;
|
|
|
|
dma_count = tty_insert_flip_string(ttyport, dma_start, dma_size);
|
|
port->icount.rx += dma_count;
|
|
if (dma_count != dma_size)
|
|
port->icount.buf_overrun++;
|
|
stm32_port->last_res -= dma_count;
|
|
if (stm32_port->last_res == 0)
|
|
stm32_port->last_res = RX_BUF_L;
|
|
}
|
|
|
|
static unsigned int stm32_usart_receive_chars_dma(struct uart_port *port)
|
|
{
|
|
struct stm32_port *stm32_port = to_stm32_port(port);
|
|
unsigned int dma_size, size = 0;
|
|
|
|
/* DMA buffer is configured in cyclic mode and handles the rollback of the buffer. */
|
|
if (stm32_port->rx_dma_state.residue > stm32_port->last_res) {
|
|
/* Conditional first part: from last_res to end of DMA buffer */
|
|
dma_size = stm32_port->last_res;
|
|
stm32_usart_push_buffer_dma(port, dma_size);
|
|
size = dma_size;
|
|
}
|
|
|
|
dma_size = stm32_port->last_res - stm32_port->rx_dma_state.residue;
|
|
stm32_usart_push_buffer_dma(port, dma_size);
|
|
size += dma_size;
|
|
|
|
return size;
|
|
}
|
|
|
|
static unsigned int stm32_usart_receive_chars(struct uart_port *port, bool force_dma_flush)
|
|
{
|
|
struct stm32_port *stm32_port = to_stm32_port(port);
|
|
const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
|
|
enum dma_status rx_dma_status;
|
|
u32 sr;
|
|
unsigned int size = 0;
|
|
|
|
if (stm32_usart_rx_dma_enabled(port) || force_dma_flush) {
|
|
rx_dma_status = dmaengine_tx_status(stm32_port->rx_ch,
|
|
stm32_port->rx_ch->cookie,
|
|
&stm32_port->rx_dma_state);
|
|
if (rx_dma_status == DMA_IN_PROGRESS) {
|
|
/* Empty DMA buffer */
|
|
size = stm32_usart_receive_chars_dma(port);
|
|
sr = readl_relaxed(port->membase + ofs->isr);
|
|
if (sr & USART_SR_ERR_MASK) {
|
|
/* Disable DMA request line */
|
|
stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_DMAR);
|
|
|
|
/* Switch to PIO mode to handle the errors */
|
|
size += stm32_usart_receive_chars_pio(port);
|
|
|
|
/* Switch back to DMA mode */
|
|
stm32_usart_set_bits(port, ofs->cr3, USART_CR3_DMAR);
|
|
}
|
|
} else {
|
|
/* Disable RX DMA */
|
|
dmaengine_terminate_async(stm32_port->rx_ch);
|
|
stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_DMAR);
|
|
/* Fall back to interrupt mode */
|
|
dev_dbg(port->dev, "DMA error, fallback to irq mode\n");
|
|
size = stm32_usart_receive_chars_pio(port);
|
|
}
|
|
} else {
|
|
size = stm32_usart_receive_chars_pio(port);
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
static void stm32_usart_tx_dma_complete(void *arg)
|
|
{
|
|
struct uart_port *port = arg;
|
|
struct stm32_port *stm32port = to_stm32_port(port);
|
|
const struct stm32_usart_offsets *ofs = &stm32port->info->ofs;
|
|
unsigned long flags;
|
|
|
|
dmaengine_terminate_async(stm32port->tx_ch);
|
|
stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_DMAT);
|
|
stm32port->tx_dma_busy = false;
|
|
|
|
/* Let's see if we have pending data to send */
|
|
spin_lock_irqsave(&port->lock, flags);
|
|
stm32_usart_transmit_chars(port);
|
|
spin_unlock_irqrestore(&port->lock, flags);
|
|
}
|
|
|
|
static void stm32_usart_tx_interrupt_enable(struct uart_port *port)
|
|
{
|
|
struct stm32_port *stm32_port = to_stm32_port(port);
|
|
const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
|
|
|
|
/*
|
|
* Enables TX FIFO threashold irq when FIFO is enabled,
|
|
* or TX empty irq when FIFO is disabled
|
|
*/
|
|
if (stm32_port->fifoen && stm32_port->txftcfg >= 0)
|
|
stm32_usart_set_bits(port, ofs->cr3, USART_CR3_TXFTIE);
|
|
else
|
|
stm32_usart_set_bits(port, ofs->cr1, USART_CR1_TXEIE);
|
|
}
|
|
|
|
static void stm32_usart_rx_dma_complete(void *arg)
|
|
{
|
|
struct uart_port *port = arg;
|
|
struct tty_port *tport = &port->state->port;
|
|
unsigned int size;
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&port->lock, flags);
|
|
size = stm32_usart_receive_chars(port, false);
|
|
uart_unlock_and_check_sysrq_irqrestore(port, flags);
|
|
if (size)
|
|
tty_flip_buffer_push(tport);
|
|
}
|
|
|
|
static void stm32_usart_tx_interrupt_disable(struct uart_port *port)
|
|
{
|
|
struct stm32_port *stm32_port = to_stm32_port(port);
|
|
const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
|
|
|
|
if (stm32_port->fifoen && stm32_port->txftcfg >= 0)
|
|
stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_TXFTIE);
|
|
else
|
|
stm32_usart_clr_bits(port, ofs->cr1, USART_CR1_TXEIE);
|
|
}
|
|
|
|
static void stm32_usart_transmit_chars_pio(struct uart_port *port)
|
|
{
|
|
struct stm32_port *stm32_port = to_stm32_port(port);
|
|
const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
|
|
struct circ_buf *xmit = &port->state->xmit;
|
|
|
|
if (stm32_port->tx_dma_busy) {
|
|
stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_DMAT);
|
|
stm32_port->tx_dma_busy = false;
|
|
}
|
|
|
|
while (!uart_circ_empty(xmit)) {
|
|
/* Check that TDR is empty before filling FIFO */
|
|
if (!(readl_relaxed(port->membase + ofs->isr) & USART_SR_TXE))
|
|
break;
|
|
writel_relaxed(xmit->buf[xmit->tail], port->membase + ofs->tdr);
|
|
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
|
|
port->icount.tx++;
|
|
}
|
|
|
|
/* rely on TXE irq (mask or unmask) for sending remaining data */
|
|
if (uart_circ_empty(xmit))
|
|
stm32_usart_tx_interrupt_disable(port);
|
|
else
|
|
stm32_usart_tx_interrupt_enable(port);
|
|
}
|
|
|
|
static void stm32_usart_transmit_chars_dma(struct uart_port *port)
|
|
{
|
|
struct stm32_port *stm32port = to_stm32_port(port);
|
|
const struct stm32_usart_offsets *ofs = &stm32port->info->ofs;
|
|
struct circ_buf *xmit = &port->state->xmit;
|
|
struct dma_async_tx_descriptor *desc = NULL;
|
|
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)
|
|
goto fallback_err;
|
|
|
|
desc->callback = stm32_usart_tx_dma_complete;
|
|
desc->callback_param = port;
|
|
|
|
/* Push current DMA TX transaction in the pending queue */
|
|
if (dma_submit_error(dmaengine_submit(desc))) {
|
|
/* dma no yet started, safe to free resources */
|
|
dmaengine_terminate_async(stm32port->tx_ch);
|
|
goto fallback_err;
|
|
}
|
|
|
|
/* Issue pending DMA TX requests */
|
|
dma_async_issue_pending(stm32port->tx_ch);
|
|
|
|
stm32_usart_set_bits(port, ofs->cr3, USART_CR3_DMAT);
|
|
|
|
xmit->tail = (xmit->tail + count) & (UART_XMIT_SIZE - 1);
|
|
port->icount.tx += count;
|
|
return;
|
|
|
|
fallback_err:
|
|
for (i = count; i > 0; i--)
|
|
stm32_usart_transmit_chars_pio(port);
|
|
}
|
|
|
|
static void stm32_usart_transmit_chars(struct uart_port *port)
|
|
{
|
|
struct stm32_port *stm32_port = to_stm32_port(port);
|
|
const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
|
|
struct circ_buf *xmit = &port->state->xmit;
|
|
|
|
if (port->x_char) {
|
|
if (stm32_port->tx_dma_busy)
|
|
stm32_usart_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_usart_set_bits(port, ofs->cr3, USART_CR3_DMAT);
|
|
return;
|
|
}
|
|
|
|
if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
|
|
stm32_usart_tx_interrupt_disable(port);
|
|
return;
|
|
}
|
|
|
|
if (ofs->icr == UNDEF_REG)
|
|
stm32_usart_clr_bits(port, ofs->isr, USART_SR_TC);
|
|
else
|
|
writel_relaxed(USART_ICR_TCCF, port->membase + ofs->icr);
|
|
|
|
if (stm32_port->tx_ch)
|
|
stm32_usart_transmit_chars_dma(port);
|
|
else
|
|
stm32_usart_transmit_chars_pio(port);
|
|
|
|
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
|
|
uart_write_wakeup(port);
|
|
|
|
if (uart_circ_empty(xmit))
|
|
stm32_usart_tx_interrupt_disable(port);
|
|
}
|
|
|
|
static irqreturn_t stm32_usart_interrupt(int irq, void *ptr)
|
|
{
|
|
struct uart_port *port = ptr;
|
|
struct tty_port *tport = &port->state->port;
|
|
struct stm32_port *stm32_port = to_stm32_port(port);
|
|
const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
|
|
u32 sr;
|
|
unsigned int size;
|
|
|
|
sr = readl_relaxed(port->membase + ofs->isr);
|
|
|
|
if ((sr & USART_SR_RTOF) && ofs->icr != UNDEF_REG)
|
|
writel_relaxed(USART_ICR_RTOCF,
|
|
port->membase + ofs->icr);
|
|
|
|
if ((sr & USART_SR_WUF) && ofs->icr != UNDEF_REG) {
|
|
/* Clear wake up flag and disable wake up interrupt */
|
|
writel_relaxed(USART_ICR_WUCF,
|
|
port->membase + ofs->icr);
|
|
stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_WUFIE);
|
|
if (irqd_is_wakeup_set(irq_get_irq_data(port->irq)))
|
|
pm_wakeup_event(tport->tty->dev, 0);
|
|
}
|
|
|
|
/*
|
|
* rx errors in dma mode has to be handled ASAP to avoid overrun as the DMA request
|
|
* line has been masked by HW and rx data are stacking in FIFO.
|
|
*/
|
|
if (!stm32_port->throttled) {
|
|
if (((sr & USART_SR_RXNE) && !stm32_usart_rx_dma_enabled(port)) ||
|
|
((sr & USART_SR_ERR_MASK) && stm32_usart_rx_dma_enabled(port))) {
|
|
spin_lock(&port->lock);
|
|
size = stm32_usart_receive_chars(port, false);
|
|
uart_unlock_and_check_sysrq(port);
|
|
if (size)
|
|
tty_flip_buffer_push(tport);
|
|
}
|
|
}
|
|
|
|
if ((sr & USART_SR_TXE) && !(stm32_port->tx_ch)) {
|
|
spin_lock(&port->lock);
|
|
stm32_usart_transmit_chars(port);
|
|
spin_unlock(&port->lock);
|
|
}
|
|
|
|
if (stm32_usart_rx_dma_enabled(port))
|
|
return IRQ_WAKE_THREAD;
|
|
else
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static irqreturn_t stm32_usart_threaded_interrupt(int irq, void *ptr)
|
|
{
|
|
struct uart_port *port = ptr;
|
|
struct tty_port *tport = &port->state->port;
|
|
struct stm32_port *stm32_port = to_stm32_port(port);
|
|
unsigned int size;
|
|
unsigned long flags;
|
|
|
|
/* Receiver timeout irq for DMA RX */
|
|
if (!stm32_port->throttled) {
|
|
spin_lock_irqsave(&port->lock, flags);
|
|
size = stm32_usart_receive_chars(port, false);
|
|
uart_unlock_and_check_sysrq_irqrestore(port, flags);
|
|
if (size)
|
|
tty_flip_buffer_push(tport);
|
|
}
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static unsigned int stm32_usart_tx_empty(struct uart_port *port)
|
|
{
|
|
struct stm32_port *stm32_port = to_stm32_port(port);
|
|
const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
|
|
|
|
if (readl_relaxed(port->membase + ofs->isr) & USART_SR_TC)
|
|
return TIOCSER_TEMT;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void stm32_usart_set_mctrl(struct uart_port *port, unsigned int mctrl)
|
|
{
|
|
struct stm32_port *stm32_port = to_stm32_port(port);
|
|
const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
|
|
|
|
if ((mctrl & TIOCM_RTS) && (port->status & UPSTAT_AUTORTS))
|
|
stm32_usart_set_bits(port, ofs->cr3, USART_CR3_RTSE);
|
|
else
|
|
stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_RTSE);
|
|
|
|
mctrl_gpio_set(stm32_port->gpios, mctrl);
|
|
}
|
|
|
|
static unsigned int stm32_usart_get_mctrl(struct uart_port *port)
|
|
{
|
|
struct stm32_port *stm32_port = to_stm32_port(port);
|
|
unsigned int ret;
|
|
|
|
/* This routine is used to get signals of: DCD, DSR, RI, and CTS */
|
|
ret = TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;
|
|
|
|
return mctrl_gpio_get(stm32_port->gpios, &ret);
|
|
}
|
|
|
|
static void stm32_usart_enable_ms(struct uart_port *port)
|
|
{
|
|
mctrl_gpio_enable_ms(to_stm32_port(port)->gpios);
|
|
}
|
|
|
|
static void stm32_usart_disable_ms(struct uart_port *port)
|
|
{
|
|
mctrl_gpio_disable_ms(to_stm32_port(port)->gpios);
|
|
}
|
|
|
|
/* Transmit stop */
|
|
static void stm32_usart_stop_tx(struct uart_port *port)
|
|
{
|
|
struct stm32_port *stm32_port = to_stm32_port(port);
|
|
struct serial_rs485 *rs485conf = &port->rs485;
|
|
|
|
stm32_usart_tx_interrupt_disable(port);
|
|
|
|
if (rs485conf->flags & SER_RS485_ENABLED) {
|
|
if (rs485conf->flags & SER_RS485_RTS_ON_SEND) {
|
|
mctrl_gpio_set(stm32_port->gpios,
|
|
stm32_port->port.mctrl & ~TIOCM_RTS);
|
|
} else {
|
|
mctrl_gpio_set(stm32_port->gpios,
|
|
stm32_port->port.mctrl | TIOCM_RTS);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* There are probably characters waiting to be transmitted. */
|
|
static void stm32_usart_start_tx(struct uart_port *port)
|
|
{
|
|
struct stm32_port *stm32_port = to_stm32_port(port);
|
|
struct serial_rs485 *rs485conf = &port->rs485;
|
|
struct circ_buf *xmit = &port->state->xmit;
|
|
|
|
if (uart_circ_empty(xmit))
|
|
return;
|
|
|
|
if (rs485conf->flags & SER_RS485_ENABLED) {
|
|
if (rs485conf->flags & SER_RS485_RTS_ON_SEND) {
|
|
mctrl_gpio_set(stm32_port->gpios,
|
|
stm32_port->port.mctrl | TIOCM_RTS);
|
|
} else {
|
|
mctrl_gpio_set(stm32_port->gpios,
|
|
stm32_port->port.mctrl & ~TIOCM_RTS);
|
|
}
|
|
}
|
|
|
|
stm32_usart_transmit_chars(port);
|
|
}
|
|
|
|
/* Flush the transmit buffer. */
|
|
static void stm32_usart_flush_buffer(struct uart_port *port)
|
|
{
|
|
struct stm32_port *stm32_port = to_stm32_port(port);
|
|
const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
|
|
|
|
if (stm32_port->tx_ch) {
|
|
dmaengine_terminate_async(stm32_port->tx_ch);
|
|
stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_DMAT);
|
|
stm32_port->tx_dma_busy = false;
|
|
}
|
|
}
|
|
|
|
/* Throttle the remote when input buffer is about to overflow. */
|
|
static void stm32_usart_throttle(struct uart_port *port)
|
|
{
|
|
struct stm32_port *stm32_port = to_stm32_port(port);
|
|
const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&port->lock, flags);
|
|
|
|
/*
|
|
* Disable DMA request line if enabled, so the RX data gets queued into the FIFO.
|
|
* Hardware flow control is triggered when RX FIFO is full.
|
|
*/
|
|
if (stm32_usart_rx_dma_enabled(port))
|
|
stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_DMAR);
|
|
|
|
stm32_usart_clr_bits(port, ofs->cr1, stm32_port->cr1_irq);
|
|
if (stm32_port->cr3_irq)
|
|
stm32_usart_clr_bits(port, ofs->cr3, stm32_port->cr3_irq);
|
|
|
|
stm32_port->throttled = true;
|
|
spin_unlock_irqrestore(&port->lock, flags);
|
|
}
|
|
|
|
/* Unthrottle the remote, the input buffer can now accept data. */
|
|
static void stm32_usart_unthrottle(struct uart_port *port)
|
|
{
|
|
struct stm32_port *stm32_port = to_stm32_port(port);
|
|
const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&port->lock, flags);
|
|
stm32_usart_set_bits(port, ofs->cr1, stm32_port->cr1_irq);
|
|
if (stm32_port->cr3_irq)
|
|
stm32_usart_set_bits(port, ofs->cr3, stm32_port->cr3_irq);
|
|
|
|
/*
|
|
* Switch back to DMA mode (re-enable DMA request line).
|
|
* Hardware flow control is stopped when FIFO is not full any more.
|
|
*/
|
|
if (stm32_port->rx_ch)
|
|
stm32_usart_set_bits(port, ofs->cr3, USART_CR3_DMAR);
|
|
|
|
stm32_port->throttled = false;
|
|
spin_unlock_irqrestore(&port->lock, flags);
|
|
}
|
|
|
|
/* Receive stop */
|
|
static void stm32_usart_stop_rx(struct uart_port *port)
|
|
{
|
|
struct stm32_port *stm32_port = to_stm32_port(port);
|
|
const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
|
|
|
|
/* Disable DMA request line. */
|
|
if (stm32_port->rx_ch)
|
|
stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_DMAR);
|
|
|
|
stm32_usart_clr_bits(port, ofs->cr1, stm32_port->cr1_irq);
|
|
if (stm32_port->cr3_irq)
|
|
stm32_usart_clr_bits(port, ofs->cr3, stm32_port->cr3_irq);
|
|
}
|
|
|
|
/* Handle breaks - ignored by us */
|
|
static void stm32_usart_break_ctl(struct uart_port *port, int break_state)
|
|
{
|
|
}
|
|
|
|
static int stm32_usart_start_rx_dma_cyclic(struct uart_port *port)
|
|
{
|
|
struct stm32_port *stm32_port = to_stm32_port(port);
|
|
const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
|
|
struct dma_async_tx_descriptor *desc;
|
|
int ret;
|
|
|
|
stm32_port->last_res = RX_BUF_L;
|
|
/* Prepare a DMA cyclic transaction */
|
|
desc = dmaengine_prep_dma_cyclic(stm32_port->rx_ch,
|
|
stm32_port->rx_dma_buf,
|
|
RX_BUF_L, RX_BUF_P,
|
|
DMA_DEV_TO_MEM,
|
|
DMA_PREP_INTERRUPT);
|
|
if (!desc) {
|
|
dev_err(port->dev, "rx dma prep cyclic failed\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
desc->callback = stm32_usart_rx_dma_complete;
|
|
desc->callback_param = port;
|
|
|
|
/* Push current DMA transaction in the pending queue */
|
|
ret = dma_submit_error(dmaengine_submit(desc));
|
|
if (ret) {
|
|
dmaengine_terminate_sync(stm32_port->rx_ch);
|
|
return ret;
|
|
}
|
|
|
|
/* Issue pending DMA requests */
|
|
dma_async_issue_pending(stm32_port->rx_ch);
|
|
|
|
/*
|
|
* DMA request line not re-enabled at resume when port is throttled.
|
|
* It will be re-enabled by unthrottle ops.
|
|
*/
|
|
if (!stm32_port->throttled)
|
|
stm32_usart_set_bits(port, ofs->cr3, USART_CR3_DMAR);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int stm32_usart_startup(struct uart_port *port)
|
|
{
|
|
struct stm32_port *stm32_port = to_stm32_port(port);
|
|
const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
|
|
const struct stm32_usart_config *cfg = &stm32_port->info->cfg;
|
|
const char *name = to_platform_device(port->dev)->name;
|
|
u32 val;
|
|
int ret;
|
|
|
|
ret = request_threaded_irq(port->irq, stm32_usart_interrupt,
|
|
stm32_usart_threaded_interrupt,
|
|
IRQF_ONESHOT | IRQF_NO_SUSPEND,
|
|
name, port);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (stm32_port->swap) {
|
|
val = readl_relaxed(port->membase + ofs->cr2);
|
|
val |= USART_CR2_SWAP;
|
|
writel_relaxed(val, port->membase + ofs->cr2);
|
|
}
|
|
|
|
/* RX FIFO Flush */
|
|
if (ofs->rqr != UNDEF_REG)
|
|
writel_relaxed(USART_RQR_RXFRQ, port->membase + ofs->rqr);
|
|
|
|
if (stm32_port->rx_ch) {
|
|
ret = stm32_usart_start_rx_dma_cyclic(port);
|
|
if (ret) {
|
|
free_irq(port->irq, port);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
/* RX enabling */
|
|
val = stm32_port->cr1_irq | USART_CR1_RE | BIT(cfg->uart_enable_bit);
|
|
stm32_usart_set_bits(port, ofs->cr1, val);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void stm32_usart_shutdown(struct uart_port *port)
|
|
{
|
|
struct stm32_port *stm32_port = to_stm32_port(port);
|
|
const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
|
|
const struct stm32_usart_config *cfg = &stm32_port->info->cfg;
|
|
u32 val, isr;
|
|
int ret;
|
|
|
|
/* Disable modem control interrupts */
|
|
stm32_usart_disable_ms(port);
|
|
|
|
val = USART_CR1_TXEIE | USART_CR1_TE;
|
|
val |= stm32_port->cr1_irq | USART_CR1_RE;
|
|
val |= BIT(cfg->uart_enable_bit);
|
|
if (stm32_port->fifoen)
|
|
val |= USART_CR1_FIFOEN;
|
|
|
|
ret = readl_relaxed_poll_timeout(port->membase + ofs->isr,
|
|
isr, (isr & USART_SR_TC),
|
|
10, 100000);
|
|
|
|
/* Send the TC error message only when ISR_TC is not set */
|
|
if (ret)
|
|
dev_err(port->dev, "Transmission is not complete\n");
|
|
|
|
/* Disable RX DMA. */
|
|
if (stm32_port->rx_ch)
|
|
dmaengine_terminate_async(stm32_port->rx_ch);
|
|
|
|
/* flush RX & TX FIFO */
|
|
if (ofs->rqr != UNDEF_REG)
|
|
writel_relaxed(USART_RQR_TXFRQ | USART_RQR_RXFRQ,
|
|
port->membase + ofs->rqr);
|
|
|
|
stm32_usart_clr_bits(port, ofs->cr1, val);
|
|
|
|
free_irq(port->irq, port);
|
|
}
|
|
|
|
static void stm32_usart_set_termios(struct uart_port *port,
|
|
struct ktermios *termios,
|
|
struct ktermios *old)
|
|
{
|
|
struct stm32_port *stm32_port = to_stm32_port(port);
|
|
const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
|
|
const struct stm32_usart_config *cfg = &stm32_port->info->cfg;
|
|
struct serial_rs485 *rs485conf = &port->rs485;
|
|
unsigned int baud, bits;
|
|
u32 usartdiv, mantissa, fraction, oversampling;
|
|
tcflag_t cflag = termios->c_cflag;
|
|
u32 cr1, cr2, cr3, isr;
|
|
unsigned long flags;
|
|
int ret;
|
|
|
|
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);
|
|
|
|
ret = readl_relaxed_poll_timeout_atomic(port->membase + ofs->isr,
|
|
isr,
|
|
(isr & USART_SR_TC),
|
|
10, 100000);
|
|
|
|
/* Send the TC error message only when ISR_TC is not set. */
|
|
if (ret)
|
|
dev_err(port->dev, "Transmission is not complete\n");
|
|
|
|
/* Stop serial port and reset value */
|
|
writel_relaxed(0, port->membase + ofs->cr1);
|
|
|
|
/* flush RX & TX FIFO */
|
|
if (ofs->rqr != UNDEF_REG)
|
|
writel_relaxed(USART_RQR_TXFRQ | USART_RQR_RXFRQ,
|
|
port->membase + ofs->rqr);
|
|
|
|
cr1 = USART_CR1_TE | USART_CR1_RE;
|
|
if (stm32_port->fifoen)
|
|
cr1 |= USART_CR1_FIFOEN;
|
|
cr2 = stm32_port->swap ? USART_CR2_SWAP : 0;
|
|
|
|
/* Tx and RX FIFO configuration */
|
|
cr3 = readl_relaxed(port->membase + ofs->cr3);
|
|
cr3 &= USART_CR3_TXFTIE | USART_CR3_RXFTIE;
|
|
if (stm32_port->fifoen) {
|
|
if (stm32_port->txftcfg >= 0)
|
|
cr3 |= stm32_port->txftcfg << USART_CR3_TXFTCFG_SHIFT;
|
|
if (stm32_port->rxftcfg >= 0)
|
|
cr3 |= stm32_port->rxftcfg << USART_CR3_RXFTCFG_SHIFT;
|
|
}
|
|
|
|
if (cflag & CSTOPB)
|
|
cr2 |= USART_CR2_STOP_2B;
|
|
|
|
bits = tty_get_char_size(cflag);
|
|
stm32_port->rdr_mask = (BIT(bits) - 1);
|
|
|
|
if (cflag & PARENB) {
|
|
bits++;
|
|
cr1 |= USART_CR1_PCE;
|
|
}
|
|
|
|
/*
|
|
* Word length configuration:
|
|
* CS8 + parity, 9 bits word aka [M1:M0] = 0b01
|
|
* CS7 or (CS6 + parity), 7 bits word aka [M1:M0] = 0b10
|
|
* CS8 or (CS7 + parity), 8 bits word aka [M1:M0] = 0b00
|
|
* M0 and M1 already cleared by cr1 initialization.
|
|
*/
|
|
if (bits == 9)
|
|
cr1 |= USART_CR1_M0;
|
|
else if ((bits == 7) && cfg->has_7bits_data)
|
|
cr1 |= USART_CR1_M1;
|
|
else if (bits != 8)
|
|
dev_dbg(port->dev, "Unsupported data bits config: %u bits\n"
|
|
, bits);
|
|
|
|
if (ofs->rtor != UNDEF_REG && (stm32_port->rx_ch ||
|
|
(stm32_port->fifoen &&
|
|
stm32_port->rxftcfg >= 0))) {
|
|
if (cflag & CSTOPB)
|
|
bits = bits + 3; /* 1 start bit + 2 stop bits */
|
|
else
|
|
bits = bits + 2; /* 1 start bit + 1 stop bit */
|
|
|
|
/* RX timeout irq to occur after last stop bit + bits */
|
|
stm32_port->cr1_irq = USART_CR1_RTOIE;
|
|
writel_relaxed(bits, port->membase + ofs->rtor);
|
|
cr2 |= USART_CR2_RTOEN;
|
|
/*
|
|
* Enable fifo threshold irq in two cases, either when there is no DMA, or when
|
|
* wake up over usart, from low power until the DMA gets re-enabled by resume.
|
|
*/
|
|
stm32_port->cr3_irq = USART_CR3_RXFTIE;
|
|
}
|
|
|
|
cr1 |= stm32_port->cr1_irq;
|
|
cr3 |= stm32_port->cr3_irq;
|
|
|
|
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 | USART_CR3_RTSE;
|
|
}
|
|
|
|
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;
|
|
cr1 |= USART_CR1_OVER8;
|
|
stm32_usart_set_bits(port, ofs->cr1, USART_CR1_OVER8);
|
|
} else {
|
|
oversampling = 16;
|
|
cr1 &= ~USART_CR1_OVER8;
|
|
stm32_usart_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 + ofs->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_FE;
|
|
|
|
/* 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_FE;
|
|
/*
|
|
* 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;
|
|
|
|
if (stm32_port->rx_ch) {
|
|
/*
|
|
* Setup DMA to collect only valid data and enable error irqs.
|
|
* This also enables break reception when using DMA.
|
|
*/
|
|
cr1 |= USART_CR1_PEIE;
|
|
cr3 |= USART_CR3_EIE;
|
|
cr3 |= USART_CR3_DMAR;
|
|
cr3 |= USART_CR3_DDRE;
|
|
}
|
|
|
|
if (rs485conf->flags & SER_RS485_ENABLED) {
|
|
stm32_usart_config_reg_rs485(&cr1, &cr3,
|
|
rs485conf->delay_rts_before_send,
|
|
rs485conf->delay_rts_after_send,
|
|
baud);
|
|
if (rs485conf->flags & SER_RS485_RTS_ON_SEND) {
|
|
cr3 &= ~USART_CR3_DEP;
|
|
rs485conf->flags &= ~SER_RS485_RTS_AFTER_SEND;
|
|
} else {
|
|
cr3 |= USART_CR3_DEP;
|
|
rs485conf->flags |= SER_RS485_RTS_AFTER_SEND;
|
|
}
|
|
|
|
} else {
|
|
cr3 &= ~(USART_CR3_DEM | USART_CR3_DEP);
|
|
cr1 &= ~(USART_CR1_DEDT_MASK | USART_CR1_DEAT_MASK);
|
|
}
|
|
|
|
/* Configure wake up from low power on start bit detection */
|
|
if (stm32_port->wakeup_src) {
|
|
cr3 &= ~USART_CR3_WUS_MASK;
|
|
cr3 |= USART_CR3_WUS_START_BIT;
|
|
}
|
|
|
|
writel_relaxed(cr3, port->membase + ofs->cr3);
|
|
writel_relaxed(cr2, port->membase + ofs->cr2);
|
|
writel_relaxed(cr1, port->membase + ofs->cr1);
|
|
|
|
stm32_usart_set_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit));
|
|
spin_unlock_irqrestore(&port->lock, flags);
|
|
|
|
/* Handle modem control interrupts */
|
|
if (UART_ENABLE_MS(port, termios->c_cflag))
|
|
stm32_usart_enable_ms(port);
|
|
else
|
|
stm32_usart_disable_ms(port);
|
|
}
|
|
|
|
static const char *stm32_usart_type(struct uart_port *port)
|
|
{
|
|
return (port->type == PORT_STM32) ? DRIVER_NAME : NULL;
|
|
}
|
|
|
|
static void stm32_usart_release_port(struct uart_port *port)
|
|
{
|
|
}
|
|
|
|
static int stm32_usart_request_port(struct uart_port *port)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static void stm32_usart_config_port(struct uart_port *port, int flags)
|
|
{
|
|
if (flags & UART_CONFIG_TYPE)
|
|
port->type = PORT_STM32;
|
|
}
|
|
|
|
static int
|
|
stm32_usart_verify_port(struct uart_port *port, struct serial_struct *ser)
|
|
{
|
|
/* No user changeable parameters */
|
|
return -EINVAL;
|
|
}
|
|
|
|
static void stm32_usart_pm(struct uart_port *port, unsigned int state,
|
|
unsigned int oldstate)
|
|
{
|
|
struct stm32_port *stm32port = container_of(port,
|
|
struct stm32_port, port);
|
|
const struct stm32_usart_offsets *ofs = &stm32port->info->ofs;
|
|
const struct stm32_usart_config *cfg = &stm32port->info->cfg;
|
|
unsigned long flags;
|
|
|
|
switch (state) {
|
|
case UART_PM_STATE_ON:
|
|
pm_runtime_get_sync(port->dev);
|
|
break;
|
|
case UART_PM_STATE_OFF:
|
|
spin_lock_irqsave(&port->lock, flags);
|
|
stm32_usart_clr_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit));
|
|
spin_unlock_irqrestore(&port->lock, flags);
|
|
pm_runtime_put_sync(port->dev);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static const struct uart_ops stm32_uart_ops = {
|
|
.tx_empty = stm32_usart_tx_empty,
|
|
.set_mctrl = stm32_usart_set_mctrl,
|
|
.get_mctrl = stm32_usart_get_mctrl,
|
|
.stop_tx = stm32_usart_stop_tx,
|
|
.start_tx = stm32_usart_start_tx,
|
|
.throttle = stm32_usart_throttle,
|
|
.unthrottle = stm32_usart_unthrottle,
|
|
.stop_rx = stm32_usart_stop_rx,
|
|
.enable_ms = stm32_usart_enable_ms,
|
|
.break_ctl = stm32_usart_break_ctl,
|
|
.startup = stm32_usart_startup,
|
|
.shutdown = stm32_usart_shutdown,
|
|
.flush_buffer = stm32_usart_flush_buffer,
|
|
.set_termios = stm32_usart_set_termios,
|
|
.pm = stm32_usart_pm,
|
|
.type = stm32_usart_type,
|
|
.release_port = stm32_usart_release_port,
|
|
.request_port = stm32_usart_request_port,
|
|
.config_port = stm32_usart_config_port,
|
|
.verify_port = stm32_usart_verify_port,
|
|
};
|
|
|
|
/*
|
|
* STM32H7 RX & TX FIFO threshold configuration (CR3 RXFTCFG / TXFTCFG)
|
|
* Note: 1 isn't a valid value in RXFTCFG / TXFTCFG. In this case,
|
|
* RXNEIE / TXEIE can be used instead of threshold irqs: RXFTIE / TXFTIE.
|
|
* So, RXFTCFG / TXFTCFG bitfields values are encoded as array index + 1.
|
|
*/
|
|
static const u32 stm32h7_usart_fifo_thresh_cfg[] = { 1, 2, 4, 8, 12, 14, 16 };
|
|
|
|
static void stm32_usart_get_ftcfg(struct platform_device *pdev, const char *p,
|
|
int *ftcfg)
|
|
{
|
|
u32 bytes, i;
|
|
|
|
/* DT option to get RX & TX FIFO threshold (default to 8 bytes) */
|
|
if (of_property_read_u32(pdev->dev.of_node, p, &bytes))
|
|
bytes = 8;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(stm32h7_usart_fifo_thresh_cfg); i++)
|
|
if (stm32h7_usart_fifo_thresh_cfg[i] >= bytes)
|
|
break;
|
|
if (i >= ARRAY_SIZE(stm32h7_usart_fifo_thresh_cfg))
|
|
i = ARRAY_SIZE(stm32h7_usart_fifo_thresh_cfg) - 1;
|
|
|
|
dev_dbg(&pdev->dev, "%s set to %d bytes\n", p,
|
|
stm32h7_usart_fifo_thresh_cfg[i]);
|
|
|
|
/* Provide FIFO threshold ftcfg (1 is invalid: threshold irq unused) */
|
|
if (i)
|
|
*ftcfg = i - 1;
|
|
else
|
|
*ftcfg = -EINVAL;
|
|
}
|
|
|
|
static void stm32_usart_deinit_port(struct stm32_port *stm32port)
|
|
{
|
|
clk_disable_unprepare(stm32port->clk);
|
|
}
|
|
|
|
static int stm32_usart_init_port(struct stm32_port *stm32port,
|
|
struct platform_device *pdev)
|
|
{
|
|
struct uart_port *port = &stm32port->port;
|
|
struct resource *res;
|
|
int ret, irq;
|
|
|
|
irq = platform_get_irq(pdev, 0);
|
|
if (irq < 0)
|
|
return irq;
|
|
|
|
port->iotype = UPIO_MEM;
|
|
port->flags = UPF_BOOT_AUTOCONF;
|
|
port->ops = &stm32_uart_ops;
|
|
port->dev = &pdev->dev;
|
|
port->fifosize = stm32port->info->cfg.fifosize;
|
|
port->has_sysrq = IS_ENABLED(CONFIG_SERIAL_STM32_CONSOLE);
|
|
port->irq = irq;
|
|
port->rs485_config = stm32_usart_config_rs485;
|
|
|
|
ret = stm32_usart_init_rs485(port, pdev);
|
|
if (ret)
|
|
return ret;
|
|
|
|
stm32port->wakeup_src = stm32port->info->cfg.has_wakeup &&
|
|
of_property_read_bool(pdev->dev.of_node, "wakeup-source");
|
|
|
|
stm32port->swap = stm32port->info->cfg.has_swap &&
|
|
of_property_read_bool(pdev->dev.of_node, "rx-tx-swap");
|
|
|
|
stm32port->fifoen = stm32port->info->cfg.has_fifo;
|
|
if (stm32port->fifoen) {
|
|
stm32_usart_get_ftcfg(pdev, "rx-threshold",
|
|
&stm32port->rxftcfg);
|
|
stm32_usart_get_ftcfg(pdev, "tx-threshold",
|
|
&stm32port->txftcfg);
|
|
}
|
|
|
|
port->membase = devm_platform_get_and_ioremap_resource(pdev, 0, &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;
|
|
goto err_clk;
|
|
}
|
|
|
|
stm32port->gpios = mctrl_gpio_init(&stm32port->port, 0);
|
|
if (IS_ERR(stm32port->gpios)) {
|
|
ret = PTR_ERR(stm32port->gpios);
|
|
goto err_clk;
|
|
}
|
|
|
|
/*
|
|
* Both CTS/RTS gpios and "st,hw-flow-ctrl" (deprecated) or "uart-has-rtscts"
|
|
* properties should not be specified.
|
|
*/
|
|
if (stm32port->hw_flow_control) {
|
|
if (mctrl_gpio_to_gpiod(stm32port->gpios, UART_GPIO_CTS) ||
|
|
mctrl_gpio_to_gpiod(stm32port->gpios, UART_GPIO_RTS)) {
|
|
dev_err(&pdev->dev, "Conflicting RTS/CTS config\n");
|
|
ret = -EINVAL;
|
|
goto err_clk;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
|
|
err_clk:
|
|
clk_disable_unprepare(stm32port->clk);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static struct stm32_port *stm32_usart_of_get_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) {
|
|
dev_err(&pdev->dev, "failed to get alias id, errno %d\n", id);
|
|
return NULL;
|
|
}
|
|
|
|
if (WARN_ON(id >= STM32_MAX_PORTS))
|
|
return NULL;
|
|
|
|
stm32_ports[id].hw_flow_control =
|
|
of_property_read_bool (np, "st,hw-flow-ctrl") /*deprecated*/ ||
|
|
of_property_read_bool (np, "uart-has-rtscts");
|
|
stm32_ports[id].port.line = id;
|
|
stm32_ports[id].cr1_irq = USART_CR1_RXNEIE;
|
|
stm32_ports[id].cr3_irq = 0;
|
|
stm32_ports[id].last_res = RX_BUF_L;
|
|
return &stm32_ports[id];
|
|
}
|
|
|
|
#ifdef CONFIG_OF
|
|
static const struct of_device_id stm32_match[] = {
|
|
{ .compatible = "st,stm32-uart", .data = &stm32f4_info},
|
|
{ .compatible = "st,stm32f7-uart", .data = &stm32f7_info},
|
|
{ .compatible = "st,stm32h7-uart", .data = &stm32h7_info},
|
|
{},
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(of, stm32_match);
|
|
#endif
|
|
|
|
static void stm32_usart_of_dma_rx_remove(struct stm32_port *stm32port,
|
|
struct platform_device *pdev)
|
|
{
|
|
if (stm32port->rx_buf)
|
|
dma_free_coherent(&pdev->dev, RX_BUF_L, stm32port->rx_buf,
|
|
stm32port->rx_dma_buf);
|
|
}
|
|
|
|
static int stm32_usart_of_dma_rx_probe(struct stm32_port *stm32port,
|
|
struct platform_device *pdev)
|
|
{
|
|
const 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;
|
|
|
|
/*
|
|
* Using DMA and threaded handler for the console could lead to
|
|
* deadlocks.
|
|
*/
|
|
if (uart_console(port))
|
|
return -ENODEV;
|
|
|
|
stm32port->rx_buf = dma_alloc_coherent(dev, RX_BUF_L,
|
|
&stm32port->rx_dma_buf,
|
|
GFP_KERNEL);
|
|
if (!stm32port->rx_buf)
|
|
return -ENOMEM;
|
|
|
|
/* Configure DMA channel */
|
|
memset(&config, 0, sizeof(config));
|
|
config.src_addr = port->mapbase + 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");
|
|
stm32_usart_of_dma_rx_remove(stm32port, pdev);
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void stm32_usart_of_dma_tx_remove(struct stm32_port *stm32port,
|
|
struct platform_device *pdev)
|
|
{
|
|
if (stm32port->tx_buf)
|
|
dma_free_coherent(&pdev->dev, TX_BUF_L, stm32port->tx_buf,
|
|
stm32port->tx_dma_buf);
|
|
}
|
|
|
|
static int stm32_usart_of_dma_tx_probe(struct stm32_port *stm32port,
|
|
struct platform_device *pdev)
|
|
{
|
|
const 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;
|
|
|
|
stm32port->tx_buf = dma_alloc_coherent(dev, TX_BUF_L,
|
|
&stm32port->tx_dma_buf,
|
|
GFP_KERNEL);
|
|
if (!stm32port->tx_buf)
|
|
return -ENOMEM;
|
|
|
|
/* Configure DMA channel */
|
|
memset(&config, 0, sizeof(config));
|
|
config.dst_addr = port->mapbase + 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");
|
|
stm32_usart_of_dma_tx_remove(stm32port, pdev);
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int stm32_usart_serial_probe(struct platform_device *pdev)
|
|
{
|
|
struct stm32_port *stm32port;
|
|
int ret;
|
|
|
|
stm32port = stm32_usart_of_get_port(pdev);
|
|
if (!stm32port)
|
|
return -ENODEV;
|
|
|
|
stm32port->info = of_device_get_match_data(&pdev->dev);
|
|
if (!stm32port->info)
|
|
return -EINVAL;
|
|
|
|
ret = stm32_usart_init_port(stm32port, pdev);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (stm32port->wakeup_src) {
|
|
device_set_wakeup_capable(&pdev->dev, true);
|
|
ret = dev_pm_set_wake_irq(&pdev->dev, stm32port->port.irq);
|
|
if (ret)
|
|
goto err_deinit_port;
|
|
}
|
|
|
|
stm32port->rx_ch = dma_request_chan(&pdev->dev, "rx");
|
|
if (PTR_ERR(stm32port->rx_ch) == -EPROBE_DEFER) {
|
|
ret = -EPROBE_DEFER;
|
|
goto err_wakeirq;
|
|
}
|
|
/* Fall back in interrupt mode for any non-deferral error */
|
|
if (IS_ERR(stm32port->rx_ch))
|
|
stm32port->rx_ch = NULL;
|
|
|
|
stm32port->tx_ch = dma_request_chan(&pdev->dev, "tx");
|
|
if (PTR_ERR(stm32port->tx_ch) == -EPROBE_DEFER) {
|
|
ret = -EPROBE_DEFER;
|
|
goto err_dma_rx;
|
|
}
|
|
/* Fall back in interrupt mode for any non-deferral error */
|
|
if (IS_ERR(stm32port->tx_ch))
|
|
stm32port->tx_ch = NULL;
|
|
|
|
if (stm32port->rx_ch && stm32_usart_of_dma_rx_probe(stm32port, pdev)) {
|
|
/* Fall back in interrupt mode */
|
|
dma_release_channel(stm32port->rx_ch);
|
|
stm32port->rx_ch = NULL;
|
|
}
|
|
|
|
if (stm32port->tx_ch && stm32_usart_of_dma_tx_probe(stm32port, pdev)) {
|
|
/* Fall back in interrupt mode */
|
|
dma_release_channel(stm32port->tx_ch);
|
|
stm32port->tx_ch = NULL;
|
|
}
|
|
|
|
if (!stm32port->rx_ch)
|
|
dev_info(&pdev->dev, "interrupt mode for rx (no dma)\n");
|
|
if (!stm32port->tx_ch)
|
|
dev_info(&pdev->dev, "interrupt mode for tx (no dma)\n");
|
|
|
|
platform_set_drvdata(pdev, &stm32port->port);
|
|
|
|
pm_runtime_get_noresume(&pdev->dev);
|
|
pm_runtime_set_active(&pdev->dev);
|
|
pm_runtime_enable(&pdev->dev);
|
|
|
|
ret = uart_add_one_port(&stm32_usart_driver, &stm32port->port);
|
|
if (ret)
|
|
goto err_port;
|
|
|
|
pm_runtime_put_sync(&pdev->dev);
|
|
|
|
return 0;
|
|
|
|
err_port:
|
|
pm_runtime_disable(&pdev->dev);
|
|
pm_runtime_set_suspended(&pdev->dev);
|
|
pm_runtime_put_noidle(&pdev->dev);
|
|
|
|
if (stm32port->tx_ch) {
|
|
stm32_usart_of_dma_tx_remove(stm32port, pdev);
|
|
dma_release_channel(stm32port->tx_ch);
|
|
}
|
|
|
|
if (stm32port->rx_ch)
|
|
stm32_usart_of_dma_rx_remove(stm32port, pdev);
|
|
|
|
err_dma_rx:
|
|
if (stm32port->rx_ch)
|
|
dma_release_channel(stm32port->rx_ch);
|
|
|
|
err_wakeirq:
|
|
if (stm32port->wakeup_src)
|
|
dev_pm_clear_wake_irq(&pdev->dev);
|
|
|
|
err_deinit_port:
|
|
if (stm32port->wakeup_src)
|
|
device_set_wakeup_capable(&pdev->dev, false);
|
|
|
|
stm32_usart_deinit_port(stm32port);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int stm32_usart_serial_remove(struct platform_device *pdev)
|
|
{
|
|
struct uart_port *port = platform_get_drvdata(pdev);
|
|
struct stm32_port *stm32_port = to_stm32_port(port);
|
|
const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
|
|
int err;
|
|
u32 cr3;
|
|
|
|
pm_runtime_get_sync(&pdev->dev);
|
|
err = uart_remove_one_port(&stm32_usart_driver, port);
|
|
if (err)
|
|
return(err);
|
|
|
|
pm_runtime_disable(&pdev->dev);
|
|
pm_runtime_set_suspended(&pdev->dev);
|
|
pm_runtime_put_noidle(&pdev->dev);
|
|
|
|
stm32_usart_clr_bits(port, ofs->cr1, USART_CR1_PEIE);
|
|
cr3 = readl_relaxed(port->membase + ofs->cr3);
|
|
cr3 &= ~USART_CR3_EIE;
|
|
cr3 &= ~USART_CR3_DMAR;
|
|
cr3 &= ~USART_CR3_DDRE;
|
|
writel_relaxed(cr3, port->membase + ofs->cr3);
|
|
|
|
if (stm32_port->tx_ch) {
|
|
dmaengine_terminate_async(stm32_port->tx_ch);
|
|
stm32_usart_of_dma_tx_remove(stm32_port, pdev);
|
|
dma_release_channel(stm32_port->tx_ch);
|
|
}
|
|
|
|
if (stm32_port->rx_ch) {
|
|
stm32_usart_of_dma_rx_remove(stm32_port, pdev);
|
|
dma_release_channel(stm32_port->rx_ch);
|
|
}
|
|
|
|
stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_DMAT);
|
|
|
|
if (stm32_port->wakeup_src) {
|
|
dev_pm_clear_wake_irq(&pdev->dev);
|
|
device_init_wakeup(&pdev->dev, false);
|
|
}
|
|
|
|
stm32_usart_deinit_port(stm32_port);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_SERIAL_STM32_CONSOLE
|
|
static void stm32_usart_console_putchar(struct uart_port *port, int ch)
|
|
{
|
|
struct stm32_port *stm32_port = to_stm32_port(port);
|
|
const 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 + ofs->tdr);
|
|
}
|
|
|
|
static void stm32_usart_console_write(struct console *co, const char *s,
|
|
unsigned int cnt)
|
|
{
|
|
struct uart_port *port = &stm32_ports[co->index].port;
|
|
struct stm32_port *stm32_port = to_stm32_port(port);
|
|
const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
|
|
const struct stm32_usart_config *cfg = &stm32_port->info->cfg;
|
|
unsigned long flags;
|
|
u32 old_cr1, new_cr1;
|
|
int locked = 1;
|
|
|
|
if (oops_in_progress)
|
|
locked = spin_trylock_irqsave(&port->lock, flags);
|
|
else
|
|
spin_lock_irqsave(&port->lock, flags);
|
|
|
|
/* 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_usart_console_putchar);
|
|
|
|
/* Restore interrupt state */
|
|
writel_relaxed(old_cr1, port->membase + ofs->cr1);
|
|
|
|
if (locked)
|
|
spin_unlock_irqrestore(&port->lock, flags);
|
|
}
|
|
|
|
static int stm32_usart_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)
|
|
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_usart_console_write,
|
|
.setup = stm32_usart_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 int __maybe_unused stm32_usart_serial_en_wakeup(struct uart_port *port,
|
|
bool enable)
|
|
{
|
|
struct stm32_port *stm32_port = to_stm32_port(port);
|
|
const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
|
|
struct tty_port *tport = &port->state->port;
|
|
int ret;
|
|
unsigned int size;
|
|
unsigned long flags;
|
|
|
|
if (!stm32_port->wakeup_src || !tty_port_initialized(tport))
|
|
return 0;
|
|
|
|
/*
|
|
* Enable low-power wake-up and wake-up irq if argument is set to
|
|
* "enable", disable low-power wake-up and wake-up irq otherwise
|
|
*/
|
|
if (enable) {
|
|
stm32_usart_set_bits(port, ofs->cr1, USART_CR1_UESM);
|
|
stm32_usart_set_bits(port, ofs->cr3, USART_CR3_WUFIE);
|
|
|
|
/*
|
|
* When DMA is used for reception, it must be disabled before
|
|
* entering low-power mode and re-enabled when exiting from
|
|
* low-power mode.
|
|
*/
|
|
if (stm32_port->rx_ch) {
|
|
spin_lock_irqsave(&port->lock, flags);
|
|
/* Avoid race with RX IRQ when DMAR is cleared */
|
|
stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_DMAR);
|
|
/* Poll data from DMA RX buffer if any */
|
|
size = stm32_usart_receive_chars(port, true);
|
|
dmaengine_terminate_async(stm32_port->rx_ch);
|
|
uart_unlock_and_check_sysrq_irqrestore(port, flags);
|
|
if (size)
|
|
tty_flip_buffer_push(tport);
|
|
}
|
|
|
|
/* Poll data from RX FIFO if any */
|
|
stm32_usart_receive_chars(port, false);
|
|
} else {
|
|
if (stm32_port->rx_ch) {
|
|
ret = stm32_usart_start_rx_dma_cyclic(port);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
stm32_usart_clr_bits(port, ofs->cr1, USART_CR1_UESM);
|
|
stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_WUFIE);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int __maybe_unused stm32_usart_serial_suspend(struct device *dev)
|
|
{
|
|
struct uart_port *port = dev_get_drvdata(dev);
|
|
int ret;
|
|
|
|
uart_suspend_port(&stm32_usart_driver, port);
|
|
|
|
if (device_may_wakeup(dev) || device_wakeup_path(dev)) {
|
|
ret = stm32_usart_serial_en_wakeup(port, true);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* When "no_console_suspend" is enabled, keep the pinctrl default state
|
|
* and rely on bootloader stage to restore this state upon resume.
|
|
* Otherwise, apply the idle or sleep states depending on wakeup
|
|
* capabilities.
|
|
*/
|
|
if (console_suspend_enabled || !uart_console(port)) {
|
|
if (device_may_wakeup(dev) || device_wakeup_path(dev))
|
|
pinctrl_pm_select_idle_state(dev);
|
|
else
|
|
pinctrl_pm_select_sleep_state(dev);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int __maybe_unused stm32_usart_serial_resume(struct device *dev)
|
|
{
|
|
struct uart_port *port = dev_get_drvdata(dev);
|
|
int ret;
|
|
|
|
pinctrl_pm_select_default_state(dev);
|
|
|
|
if (device_may_wakeup(dev) || device_wakeup_path(dev)) {
|
|
ret = stm32_usart_serial_en_wakeup(port, false);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
return uart_resume_port(&stm32_usart_driver, port);
|
|
}
|
|
|
|
static int __maybe_unused stm32_usart_runtime_suspend(struct device *dev)
|
|
{
|
|
struct uart_port *port = dev_get_drvdata(dev);
|
|
struct stm32_port *stm32port = container_of(port,
|
|
struct stm32_port, port);
|
|
|
|
clk_disable_unprepare(stm32port->clk);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int __maybe_unused stm32_usart_runtime_resume(struct device *dev)
|
|
{
|
|
struct uart_port *port = dev_get_drvdata(dev);
|
|
struct stm32_port *stm32port = container_of(port,
|
|
struct stm32_port, port);
|
|
|
|
return clk_prepare_enable(stm32port->clk);
|
|
}
|
|
|
|
static const struct dev_pm_ops stm32_serial_pm_ops = {
|
|
SET_RUNTIME_PM_OPS(stm32_usart_runtime_suspend,
|
|
stm32_usart_runtime_resume, NULL)
|
|
SET_SYSTEM_SLEEP_PM_OPS(stm32_usart_serial_suspend,
|
|
stm32_usart_serial_resume)
|
|
};
|
|
|
|
static struct platform_driver stm32_serial_driver = {
|
|
.probe = stm32_usart_serial_probe,
|
|
.remove = stm32_usart_serial_remove,
|
|
.driver = {
|
|
.name = DRIVER_NAME,
|
|
.pm = &stm32_serial_pm_ops,
|
|
.of_match_table = of_match_ptr(stm32_match),
|
|
},
|
|
};
|
|
|
|
static int __init stm32_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 stm32_usart_exit(void)
|
|
{
|
|
platform_driver_unregister(&stm32_serial_driver);
|
|
uart_unregister_driver(&stm32_usart_driver);
|
|
}
|
|
|
|
module_init(stm32_usart_init);
|
|
module_exit(stm32_usart_exit);
|
|
|
|
MODULE_ALIAS("platform:" DRIVER_NAME);
|
|
MODULE_DESCRIPTION("STMicroelectronics STM32 serial port driver");
|
|
MODULE_LICENSE("GPL v2");
|