summaryrefslogtreecommitdiff
path: root/drivers/tty/serial/8250/8250_omap.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/tty/serial/8250/8250_omap.c')
-rw-r--r--drivers/tty/serial/8250/8250_omap.c278
1 files changed, 145 insertions, 133 deletions
diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c
index 88b58f44e4e9..9e49ef48b851 100644
--- a/drivers/tty/serial/8250/8250_omap.c
+++ b/drivers/tty/serial/8250/8250_omap.c
@@ -27,6 +27,8 @@
#include <linux/pm_wakeirq.h>
#include <linux/dma-mapping.h>
#include <linux/sys_soc.h>
+#include <linux/reboot.h>
+#include <linux/pinctrl/consumer.h>
#include "8250.h"
@@ -145,6 +147,9 @@ struct omap8250_priv {
spinlock_t rx_dma_lock;
bool rx_dma_broken;
bool throttled;
+
+ struct pinctrl *pinctrl;
+ struct pinctrl_state *pinctrl_wakeup;
};
struct omap8250_dma_params {
@@ -176,7 +181,7 @@ static u32 uart_read(struct omap8250_priv *priv, u32 reg)
static void __omap8250_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
struct uart_8250_port *up = up_to_u8250p(port);
- struct omap8250_priv *priv = up->port.private_data;
+ struct omap8250_priv *priv = port->private_data;
u8 lcr;
serial8250_do_set_mctrl(port, mctrl);
@@ -303,12 +308,13 @@ static void omap8250_update_mdr1(struct uart_8250_port *up,
static void omap8250_restore_regs(struct uart_8250_port *up)
{
- struct omap8250_priv *priv = up->port.private_data;
+ struct uart_port *port = &up->port;
+ struct omap8250_priv *priv = port->private_data;
struct uart_8250_dma *dma = up->dma;
u8 mcr = serial8250_in_MCR(up);
/* Port locked to synchronize UART_IER access against the console. */
- lockdep_assert_held_once(&up->port.lock);
+ lockdep_assert_held_once(&port->lock);
if (dma && dma->tx_running) {
/*
@@ -359,27 +365,21 @@ static void omap8250_restore_regs(struct uart_8250_port *up)
omap8250_update_mdr1(up, priv);
- __omap8250_set_mctrl(&up->port, up->port.mctrl);
+ __omap8250_set_mctrl(port, port->mctrl);
serial_out(up, UART_OMAP_MDR3, priv->mdr3);
- if (up->port.rs485.flags & SER_RS485_ENABLED &&
- up->port.rs485_config == serial8250_em485_config)
- serial8250_em485_stop_tx(up);
+ if (port->rs485.flags & SER_RS485_ENABLED &&
+ port->rs485_config == serial8250_em485_config)
+ serial8250_em485_stop_tx(up, true);
}
-/*
- * OMAP can use "CLK / (16 or 13) / div" for baud rate. And then we have have
- * some differences in how we want to handle flow control.
- */
-static void omap_8250_set_termios(struct uart_port *port,
- struct ktermios *termios,
- const struct ktermios *old)
+static void omap_8250_set_termios_atomic(struct uart_port *port, struct ktermios *termios,
+ const struct ktermios *old, unsigned int baud)
{
struct uart_8250_port *up = up_to_u8250p(port);
- struct omap8250_priv *priv = up->port.private_data;
- unsigned char cval = 0;
- unsigned int baud;
+ struct omap8250_priv *priv = port->private_data;
+ u8 cval;
cval = UART_LCR_WLEN(tty_get_char_size(termios->c_cflag));
@@ -392,59 +392,59 @@ static void omap_8250_set_termios(struct uart_port *port,
if (termios->c_cflag & CMSPAR)
cval |= UART_LCR_SPAR;
- /*
- * Ask the core to calculate the divisor for us.
- */
- baud = uart_get_baud_rate(port, termios, old,
- port->uartclk / 16 / UART_DIV_MAX,
- port->uartclk / 13);
omap_8250_get_divisor(port, baud, priv);
/*
* Ok, we're now changing the port state. Do it with
* interrupts disabled.
*/
- pm_runtime_get_sync(port->dev);
- uart_port_lock_irq(port);
+ guard(serial8250_rpm)(up);
+ guard(uart_port_lock_irq)(port);
/*
* Update the per-port timeout.
*/
uart_update_timeout(port, termios->c_cflag, baud);
- up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
+ /*
+ * Specify which conditions may be considered for error
+ * handling and the ignoring of characters. The actual
+ * ignoring of characters only occurs if the bit is set
+ * in @ignore_status_mask as well.
+ */
+ port->read_status_mask = UART_LSR_OE | UART_LSR_DR;
if (termios->c_iflag & INPCK)
- up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE;
+ port->read_status_mask |= UART_LSR_FE | UART_LSR_PE;
if (termios->c_iflag & (IGNBRK | PARMRK))
- up->port.read_status_mask |= UART_LSR_BI;
+ port->read_status_mask |= UART_LSR_BI;
/*
* Characters to ignore
*/
- up->port.ignore_status_mask = 0;
+ port->ignore_status_mask = 0;
if (termios->c_iflag & IGNPAR)
- up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
+ port->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
if (termios->c_iflag & IGNBRK) {
- up->port.ignore_status_mask |= UART_LSR_BI;
+ port->ignore_status_mask |= UART_LSR_BI;
/*
* If we're ignoring parity and break indicators,
* ignore overruns too (for real raw support).
*/
if (termios->c_iflag & IGNPAR)
- up->port.ignore_status_mask |= UART_LSR_OE;
+ port->ignore_status_mask |= UART_LSR_OE;
}
/*
* ignore all characters if CREAD is not set
*/
if ((termios->c_cflag & CREAD) == 0)
- up->port.ignore_status_mask |= UART_LSR_DR;
+ port->ignore_status_mask |= UART_LSR_DR;
/*
* Modem status interrupts
*/
up->ier &= ~UART_IER_MSI;
- if (UART_ENABLE_MS(&up->port, termios->c_cflag))
+ if (UART_ENABLE_MS(port, termios->c_cflag))
up->ier |= UART_IER_MSI;
up->lcr = cval;
@@ -482,15 +482,15 @@ static void omap_8250_set_termios(struct uart_port *port,
priv->xoff = termios->c_cc[VSTOP];
priv->efr = 0;
- up->port.status &= ~(UPSTAT_AUTOCTS | UPSTAT_AUTORTS | UPSTAT_AUTOXOFF);
+ port->status &= ~(UPSTAT_AUTOCTS | UPSTAT_AUTORTS | UPSTAT_AUTOXOFF);
- if (termios->c_cflag & CRTSCTS && up->port.flags & UPF_HARD_FLOW &&
+ if (termios->c_cflag & CRTSCTS && port->flags & UPF_HARD_FLOW &&
!mctrl_gpio_to_gpiod(up->gpios, UART_GPIO_RTS) &&
!mctrl_gpio_to_gpiod(up->gpios, UART_GPIO_CTS)) {
/* Enable AUTOCTS (autoRTS is enabled when RTS is raised) */
- up->port.status |= UPSTAT_AUTOCTS | UPSTAT_AUTORTS;
+ port->status |= UPSTAT_AUTOCTS | UPSTAT_AUTORTS;
priv->efr |= UART_EFR_CTS;
- } else if (up->port.flags & UPF_SOFT_FLOW) {
+ } else if (port->flags & UPF_SOFT_FLOW) {
/*
* OMAP rx s/w flow control is borked; the transmitter remains
* stuck off even if rx flow control is subsequently disabled
@@ -502,15 +502,32 @@ static void omap_8250_set_termios(struct uart_port *port,
* Transmit XON1, XOFF1
*/
if (termios->c_iflag & IXOFF) {
- up->port.status |= UPSTAT_AUTOXOFF;
+ port->status |= UPSTAT_AUTOXOFF;
priv->efr |= OMAP_UART_SW_TX;
}
}
omap8250_restore_regs(up);
+}
- uart_port_unlock_irq(&up->port);
- pm_runtime_mark_last_busy(port->dev);
- pm_runtime_put_autosuspend(port->dev);
+/*
+ * OMAP can use "CLK / (16 or 13) / div" for baud rate. And then we have have
+ * some differences in how we want to handle flow control.
+ */
+static void omap_8250_set_termios(struct uart_port *port,
+ struct ktermios *termios,
+ const struct ktermios *old)
+{
+ struct omap8250_priv *priv = port->private_data;
+ unsigned int baud;
+
+ /*
+ * Ask the core to calculate the divisor for us.
+ */
+ baud = uart_get_baud_rate(port, termios, old,
+ port->uartclk / 16 / UART_DIV_MAX,
+ port->uartclk / 13);
+
+ omap_8250_set_termios_atomic(port, termios, old, baud);
/* calculate wakeup latency constraint */
priv->calc_latency = USEC_PER_SEC * 64 * 8 / baud;
@@ -530,10 +547,9 @@ static void omap_8250_pm(struct uart_port *port, unsigned int state,
struct uart_8250_port *up = up_to_u8250p(port);
u8 efr;
- pm_runtime_get_sync(port->dev);
-
+ guard(serial8250_rpm)(up);
/* Synchronize UART_IER access against the console. */
- uart_port_lock_irq(port);
+ guard(uart_port_lock_irq)(port);
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
efr = serial_in(up, UART_EFR);
@@ -544,11 +560,6 @@ static void omap_8250_pm(struct uart_port *port, unsigned int state,
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
serial_out(up, UART_EFR, efr);
serial_out(up, UART_LCR, 0);
-
- uart_port_unlock_irq(port);
-
- pm_runtime_mark_last_busy(port->dev);
- pm_runtime_put_autosuspend(port->dev);
}
static void omap_serial_fill_features_erratas(struct uart_8250_port *up,
@@ -686,7 +697,7 @@ static irqreturn_t omap8250_irq(int irq, void *dev_id)
/* Synchronize UART_IER access against the console. */
uart_port_lock(port);
- up->ier = port->serial_in(port, UART_IER);
+ up->ier = serial_port_in(port, UART_IER);
if (up->ier & (UART_IER_RLSI | UART_IER_RDI)) {
port->ops->stop_rx(port);
} else {
@@ -720,7 +731,11 @@ static int omap_8250_startup(struct uart_port *port)
return ret;
}
- pm_runtime_get_sync(port->dev);
+#ifdef CONFIG_PM
+ up->capabilities |= UART_CAP_RPM;
+#endif
+
+ guard(serial8250_rpm)(up);
serial_out(up, UART_FCR, UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
@@ -743,14 +758,10 @@ static int omap_8250_startup(struct uart_port *port)
}
/* Synchronize UART_IER access against the console. */
- uart_port_lock_irq(port);
- up->ier = UART_IER_RLSI | UART_IER_RDI;
- serial_out(up, UART_IER, up->ier);
- uart_port_unlock_irq(port);
-
-#ifdef CONFIG_PM
- up->capabilities |= UART_CAP_RPM;
-#endif
+ scoped_guard(uart_port_lock_irq, port) {
+ up->ier = UART_IER_RLSI | UART_IER_RDI;
+ serial_out(up, UART_IER, up->ier);
+ }
/* Enable module level wake up */
priv->wer = OMAP_UART_WER_MOD_WKUP;
@@ -759,15 +770,12 @@ static int omap_8250_startup(struct uart_port *port)
serial_out(up, UART_OMAP_WER, priv->wer);
if (up->dma && !(priv->habit & UART_HAS_EFR2)) {
- uart_port_lock_irq(port);
+ guard(uart_port_lock_irq)(port);
up->dma->rx_dma(up);
- uart_port_unlock_irq(port);
}
- enable_irq(up->port.irq);
+ enable_irq(port->irq);
- pm_runtime_mark_last_busy(port->dev);
- pm_runtime_put_autosuspend(port->dev);
return 0;
}
@@ -776,22 +784,23 @@ static void omap_8250_shutdown(struct uart_port *port)
struct uart_8250_port *up = up_to_u8250p(port);
struct omap8250_priv *priv = port->private_data;
+ guard(serial8250_rpm)(up);
+
flush_work(&priv->qos_work);
if (up->dma)
omap_8250_rx_dma_flush(up);
- pm_runtime_get_sync(port->dev);
-
serial_out(up, UART_OMAP_WER, 0);
if (priv->habit & UART_HAS_EFR2)
serial_out(up, UART_OMAP_EFR2, 0x0);
/* Synchronize UART_IER access against the console. */
- uart_port_lock_irq(port);
- up->ier = 0;
- serial_out(up, UART_IER, 0);
- uart_port_unlock_irq(port);
- disable_irq_nosync(up->port.irq);
+ scoped_guard(uart_port_lock_irq, port) {
+ up->ier = 0;
+ serial_out(up, UART_IER, 0);
+ }
+
+ disable_irq_nosync(port->irq);
dev_pm_clear_wake_irq(port->dev);
serial8250_release_dma(up);
@@ -803,47 +812,33 @@ static void omap_8250_shutdown(struct uart_port *port)
if (up->lcr & UART_LCR_SBC)
serial_out(up, UART_LCR, up->lcr & ~UART_LCR_SBC);
serial_out(up, UART_FCR, UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
-
- pm_runtime_mark_last_busy(port->dev);
- pm_runtime_put_autosuspend(port->dev);
}
static void omap_8250_throttle(struct uart_port *port)
{
struct omap8250_priv *priv = port->private_data;
- unsigned long flags;
- pm_runtime_get_sync(port->dev);
+ guard(serial8250_rpm)(up_to_u8250p(port));
+ guard(uart_port_lock_irqsave)(port);
- uart_port_lock_irqsave(port, &flags);
port->ops->stop_rx(port);
priv->throttled = true;
- uart_port_unlock_irqrestore(port, flags);
-
- pm_runtime_mark_last_busy(port->dev);
- pm_runtime_put_autosuspend(port->dev);
}
static void omap_8250_unthrottle(struct uart_port *port)
{
struct omap8250_priv *priv = port->private_data;
struct uart_8250_port *up = up_to_u8250p(port);
- unsigned long flags;
-
- pm_runtime_get_sync(port->dev);
+ guard(serial8250_rpm)(up);
/* Synchronize UART_IER access against the console. */
- uart_port_lock_irqsave(port, &flags);
+ guard(uart_port_lock_irqsave)(port);
+
priv->throttled = false;
if (up->dma)
up->dma->rx_dma(up);
up->ier |= UART_IER_RLSI | UART_IER_RDI;
- port->read_status_mask |= UART_LSR_DR;
serial_out(up, UART_IER, up->ier);
- uart_port_unlock_irqrestore(port, flags);
-
- pm_runtime_mark_last_busy(port->dev);
- pm_runtime_put_autosuspend(port->dev);
}
static int omap8250_rs485_config(struct uart_port *port,
@@ -981,30 +976,26 @@ static void __dma_rx_complete(void *param)
struct omap8250_priv *priv = p->port.private_data;
struct uart_8250_dma *dma = p->dma;
struct dma_tx_state state;
- unsigned long flags;
/* Synchronize UART_IER access against the console. */
- uart_port_lock_irqsave(&p->port, &flags);
+ guard(uart_port_lock_irqsave)(&p->port);
/*
* If the tx status is not DMA_COMPLETE, then this is a delayed
* completion callback. A previous RX timeout flush would have
* already pushed the data, so exit.
*/
- if (dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state) !=
- DMA_COMPLETE) {
- uart_port_unlock_irqrestore(&p->port, flags);
+ if (dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state) != DMA_COMPLETE)
return;
- }
+
__dma_rx_do_complete(p);
- if (!priv->throttled) {
- p->ier |= UART_IER_RLSI | UART_IER_RDI;
- serial_out(p, UART_IER, p->ier);
- if (!(priv->habit & UART_HAS_EFR2))
- omap_8250_rx_dma(p);
- }
+ if (priv->throttled)
+ return;
- uart_port_unlock_irqrestore(&p->port, flags);
+ p->ier |= UART_IER_RLSI | UART_IER_RDI;
+ serial_out(p, UART_IER, p->ier);
+ if (!(priv->habit & UART_HAS_EFR2))
+ omap_8250_rx_dma(p);
}
static void omap_8250_rx_dma_flush(struct uart_8250_port *p)
@@ -1102,14 +1093,13 @@ static void omap_8250_dma_tx_complete(void *param)
struct uart_8250_port *p = param;
struct uart_8250_dma *dma = p->dma;
struct tty_port *tport = &p->port.state->port;
- unsigned long flags;
bool en_thri = false;
struct omap8250_priv *priv = p->port.private_data;
dma_sync_single_for_cpu(dma->txchan->device->dev, dma->tx_addr,
UART_XMIT_SIZE, DMA_TO_DEVICE);
- uart_port_lock_irqsave(&p->port, &flags);
+ guard(uart_port_lock_irqsave)(&p->port);
dma->tx_running = 0;
@@ -1137,8 +1127,6 @@ static void omap_8250_dma_tx_complete(void *param)
dma->tx_err = 1;
serial8250_set_THRI(p);
}
-
- uart_port_unlock_irqrestore(&p->port, flags);
}
static int omap_8250_tx_dma(struct uart_8250_port *p)
@@ -1168,16 +1156,6 @@ static int omap_8250_tx_dma(struct uart_8250_port *p)
return 0;
}
- sg_init_table(&sg, 1);
- ret = kfifo_dma_out_prepare_mapped(&tport->xmit_fifo, &sg, 1,
- UART_XMIT_SIZE, dma->tx_addr);
- if (ret != 1) {
- serial8250_clear_THRI(p);
- return 0;
- }
-
- dma->tx_size = sg_dma_len(&sg);
-
if (priv->habit & OMAP_DMA_TX_KICK) {
unsigned char c;
u8 tx_lvl;
@@ -1202,18 +1180,22 @@ static int omap_8250_tx_dma(struct uart_8250_port *p)
ret = -EBUSY;
goto err;
}
- if (dma->tx_size < 4) {
+ if (kfifo_len(&tport->xmit_fifo) < 4) {
ret = -EINVAL;
goto err;
}
- if (!kfifo_get(&tport->xmit_fifo, &c)) {
+ if (!uart_fifo_out(&p->port, &c, 1)) {
ret = -EINVAL;
goto err;
}
skip_byte = c;
- /* now we need to recompute due to kfifo_get */
- kfifo_dma_out_prepare_mapped(&tport->xmit_fifo, &sg, 1,
- UART_XMIT_SIZE, dma->tx_addr);
+ }
+
+ sg_init_table(&sg, 1);
+ ret = kfifo_dma_out_prepare_mapped(&tport->xmit_fifo, &sg, 1, UART_XMIT_SIZE, dma->tx_addr);
+ if (ret != 1) {
+ ret = -EINVAL;
+ goto err;
}
desc = dmaengine_prep_slave_sg(dma->txchan, &sg, 1, DMA_MEM_TO_DEV,
@@ -1223,6 +1205,7 @@ static int omap_8250_tx_dma(struct uart_8250_port *p)
goto err;
}
+ dma->tx_size = sg_dma_len(&sg);
dma->tx_running = 1;
desc->callback = omap_8250_dma_tx_complete;
@@ -1304,13 +1287,13 @@ static void am654_8250_handle_rx_dma(struct uart_8250_port *up, u8 iir,
/*
* This is mostly serial8250_handle_irq(). We have a slightly different DMA
- * hoook for RX/TX and need different logic for them in the ISR. Therefore we
+ * hook for RX/TX and need different logic for them in the ISR. Therefore we
* use the default routine in the non-DMA case and this one for with DMA.
*/
static int omap_8250_dma_handle_irq(struct uart_port *port)
{
struct uart_8250_port *up = up_to_u8250p(port);
- struct omap8250_priv *priv = up->port.private_data;
+ struct omap8250_priv *priv = port->private_data;
u16 status;
u8 iir;
@@ -1332,13 +1315,13 @@ static int omap_8250_dma_handle_irq(struct uart_port *port)
serial8250_modem_status(up);
if (status & UART_LSR_THRE && up->dma->tx_err) {
- if (uart_tx_stopped(&up->port) ||
- kfifo_is_empty(&up->port.state->port.xmit_fifo)) {
+ if (uart_tx_stopped(port) ||
+ kfifo_is_empty(&port->state->port.xmit_fifo)) {
up->dma->tx_err = 0;
serial8250_tx_chars(up);
} else {
/*
- * try again due to an earlier failer which
+ * try again due to an earlier failure which
* might have been resolved by now.
*/
if (omap_8250_tx_dma(up))
@@ -1371,6 +1354,18 @@ static int omap8250_no_handle_irq(struct uart_port *port)
return 0;
}
+static int omap8250_select_wakeup_pinctrl(struct device *dev,
+ struct omap8250_priv *priv)
+{
+ if (IS_ERR_OR_NULL(priv->pinctrl_wakeup))
+ return 0;
+
+ if (!device_may_wakeup(dev))
+ return 0;
+
+ return pinctrl_select_state(priv->pinctrl, priv->pinctrl_wakeup);
+}
+
static struct omap8250_dma_params am654_dma = {
.rx_size = SZ_2K,
.rx_trigger = 1,
@@ -1595,6 +1590,11 @@ static int omap8250_probe(struct platform_device *pdev)
priv->line = ret;
pm_runtime_mark_last_busy(&pdev->dev);
pm_runtime_put_autosuspend(&pdev->dev);
+
+ priv->pinctrl = devm_pinctrl_get(&pdev->dev);
+ if (!IS_ERR_OR_NULL(priv->pinctrl))
+ priv->pinctrl_wakeup = pinctrl_lookup_state(priv->pinctrl, "wakeup");
+
return 0;
err:
pm_runtime_dont_use_autosuspend(&pdev->dev);
@@ -1652,6 +1652,13 @@ static int omap8250_suspend(struct device *dev)
struct uart_8250_port *up = serial8250_get_port(priv->line);
int err = 0;
+ err = omap8250_select_wakeup_pinctrl(dev, priv);
+ if (err) {
+ dev_err(dev, "Failed to select wakeup pinctrl, aborting suspend %pe\n",
+ ERR_PTR(err));
+ return err;
+ }
+
serial8250_suspend_port(priv->line);
err = pm_runtime_resume_and_get(dev);
@@ -1673,6 +1680,13 @@ static int omap8250_resume(struct device *dev)
struct uart_8250_port *up = serial8250_get_port(priv->line);
int err;
+ err = pinctrl_select_default_state(dev);
+ if (err) {
+ dev_err(dev, "Failed to select default pinctrl state on resume: %pe\n",
+ ERR_PTR(err));
+ return err;
+ }
+
if (uart_console(&up->port) && console_suspend_enabled) {
err = pm_runtime_force_resume(dev);
if (err)
@@ -1794,15 +1808,13 @@ static int omap8250_runtime_resume(struct device *dev)
up = serial8250_get_port(priv->line);
if (up && omap8250_lost_context(up)) {
- uart_port_lock_irq(&up->port);
+ guard(uart_port_lock_irq)(&up->port);
omap8250_restore_regs(up);
- uart_port_unlock_irq(&up->port);
}
if (up && up->dma && up->dma->rxchan && !(priv->habit & UART_HAS_EFR2)) {
- uart_port_lock_irq(&up->port);
+ guard(uart_port_lock_irq)(&up->port);
omap_8250_rx_dma(up);
- uart_port_unlock_irq(&up->port);
}
atomic_set(&priv->active, 1);
@@ -1867,7 +1879,7 @@ static struct platform_driver omap8250_platform_driver = {
.of_match_table = omap8250_dt_ids,
},
.probe = omap8250_probe,
- .remove_new = omap8250_remove,
+ .remove = omap8250_remove,
};
module_platform_driver(omap8250_platform_driver);