2017-11-06 17:11:51 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
2014-09-29 18:06:39 +00:00
|
|
|
/*
|
|
|
|
* 8250-core based driver for the OMAP internal UART
|
|
|
|
*
|
|
|
|
* based on omap-serial.c, Copyright (C) 2010 Texas Instruments.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2014 Sebastian Andrzej Siewior
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2023-10-04 06:26:48 +00:00
|
|
|
#include <linux/atomic.h>
|
2019-01-09 09:12:06 +00:00
|
|
|
#include <linux/clk.h>
|
2014-09-29 18:06:39 +00:00
|
|
|
#include <linux/device.h>
|
|
|
|
#include <linux/io.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/serial_8250.h>
|
|
|
|
#include <linux/serial_reg.h>
|
2014-09-29 18:06:48 +00:00
|
|
|
#include <linux/tty_flip.h>
|
2014-09-29 18:06:39 +00:00
|
|
|
#include <linux/platform_device.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/of.h>
|
|
|
|
#include <linux/of_gpio.h>
|
|
|
|
#include <linux/of_irq.h>
|
|
|
|
#include <linux/delay.h>
|
|
|
|
#include <linux/pm_runtime.h>
|
|
|
|
#include <linux/console.h>
|
|
|
|
#include <linux/pm_qos.h>
|
2015-06-10 06:35:00 +00:00
|
|
|
#include <linux/pm_wakeirq.h>
|
2014-09-29 18:06:43 +00:00
|
|
|
#include <linux/dma-mapping.h>
|
2020-10-29 05:19:30 +00:00
|
|
|
#include <linux/sys_soc.h>
|
2023-10-17 13:05:40 +00:00
|
|
|
#include <linux/pm_domain.h>
|
2014-09-29 18:06:39 +00:00
|
|
|
|
|
|
|
#include "8250.h"
|
|
|
|
|
|
|
|
#define DEFAULT_CLK_SPEED 48000000
|
2023-05-08 08:20:12 +00:00
|
|
|
#define OMAP_UART_REGSHIFT 2
|
2014-09-29 18:06:39 +00:00
|
|
|
|
|
|
|
#define UART_ERRATA_i202_MDR1_ACCESS (1 << 0)
|
|
|
|
#define OMAP_UART_WER_HAS_TX_WAKEUP (1 << 1)
|
2014-09-29 18:06:43 +00:00
|
|
|
#define OMAP_DMA_TX_KICK (1 << 2)
|
2015-07-14 08:02:07 +00:00
|
|
|
/*
|
|
|
|
* See Advisory 21 in AM437x errata SPRZ408B, updated April 2015.
|
|
|
|
* The same errata is applicable to AM335x and DRA7x processors too.
|
|
|
|
*/
|
|
|
|
#define UART_ERRATA_CLOCK_DISABLE (1 << 3)
|
2020-03-19 11:03:44 +00:00
|
|
|
#define UART_HAS_EFR2 BIT(4)
|
2020-10-29 05:19:30 +00:00
|
|
|
#define UART_HAS_RHR_IT_DIS BIT(5)
|
2021-06-22 14:57:04 +00:00
|
|
|
#define UART_RX_TIMEOUT_QUIRK BIT(6)
|
2022-10-16 08:02:00 +00:00
|
|
|
#define UART_HAS_NATIVE_RS485 BIT(7)
|
2014-09-29 18:06:39 +00:00
|
|
|
|
|
|
|
#define OMAP_UART_FCR_RX_TRIG 6
|
|
|
|
#define OMAP_UART_FCR_TX_TRIG 4
|
|
|
|
|
|
|
|
/* SCR register bitmasks */
|
|
|
|
#define OMAP_UART_SCR_RX_TRIG_GRANU1_MASK (1 << 7)
|
|
|
|
#define OMAP_UART_SCR_TX_TRIG_GRANU1_MASK (1 << 6)
|
|
|
|
#define OMAP_UART_SCR_TX_EMPTY (1 << 3)
|
|
|
|
#define OMAP_UART_SCR_DMAMODE_MASK (3 << 1)
|
|
|
|
#define OMAP_UART_SCR_DMAMODE_1 (1 << 1)
|
|
|
|
#define OMAP_UART_SCR_DMAMODE_CTL (1 << 0)
|
|
|
|
|
|
|
|
/* MVR register bitmasks */
|
|
|
|
#define OMAP_UART_MVR_SCHEME_SHIFT 30
|
|
|
|
#define OMAP_UART_LEGACY_MVR_MAJ_MASK 0xf0
|
|
|
|
#define OMAP_UART_LEGACY_MVR_MAJ_SHIFT 4
|
|
|
|
#define OMAP_UART_LEGACY_MVR_MIN_MASK 0x0f
|
|
|
|
#define OMAP_UART_MVR_MAJ_MASK 0x700
|
|
|
|
#define OMAP_UART_MVR_MAJ_SHIFT 8
|
|
|
|
#define OMAP_UART_MVR_MIN_MASK 0x3f
|
|
|
|
|
2015-07-14 08:02:07 +00:00
|
|
|
/* SYSC register bitmasks */
|
|
|
|
#define OMAP_UART_SYSC_SOFTRESET (1 << 1)
|
|
|
|
|
|
|
|
/* SYSS register bitmasks */
|
|
|
|
#define OMAP_UART_SYSS_RESETDONE (1 << 0)
|
|
|
|
|
2014-09-29 18:06:39 +00:00
|
|
|
#define UART_TI752_TLR_TX 0
|
|
|
|
#define UART_TI752_TLR_RX 4
|
|
|
|
|
|
|
|
#define TRIGGER_TLR_MASK(x) ((x & 0x3c) >> 2)
|
|
|
|
#define TRIGGER_FCR_MASK(x) (x & 3)
|
|
|
|
|
|
|
|
/* Enable XON/XOFF flow control on output */
|
|
|
|
#define OMAP_UART_SW_TX 0x08
|
|
|
|
/* Enable XON/XOFF flow control on input */
|
|
|
|
#define OMAP_UART_SW_RX 0x02
|
|
|
|
|
|
|
|
#define OMAP_UART_WER_MOD_WKUP 0x7f
|
|
|
|
#define OMAP_UART_TX_WAKEUP_EN (1 << 7)
|
|
|
|
|
|
|
|
#define TX_TRIGGER 1
|
|
|
|
#define RX_TRIGGER 48
|
|
|
|
|
|
|
|
#define OMAP_UART_TCR_RESTORE(x) ((x / 4) << 4)
|
|
|
|
#define OMAP_UART_TCR_HALT(x) ((x / 4) << 0)
|
|
|
|
|
|
|
|
#define UART_BUILD_REVISION(x, y) (((x) << 8) | (y))
|
|
|
|
|
|
|
|
#define OMAP_UART_REV_46 0x0406
|
|
|
|
#define OMAP_UART_REV_52 0x0502
|
|
|
|
#define OMAP_UART_REV_63 0x0603
|
|
|
|
|
2020-10-29 05:19:30 +00:00
|
|
|
/* Interrupt Enable Register 2 */
|
|
|
|
#define UART_OMAP_IER2 0x1B
|
|
|
|
#define UART_OMAP_IER2_RHR_IT_DIS BIT(2)
|
|
|
|
|
2022-10-16 08:02:00 +00:00
|
|
|
/* Mode Definition Register 3 */
|
|
|
|
#define UART_OMAP_MDR3 0x20
|
|
|
|
#define UART_OMAP_MDR3_DIR_POL BIT(3)
|
|
|
|
#define UART_OMAP_MDR3_DIR_EN BIT(4)
|
|
|
|
|
2020-03-19 11:03:44 +00:00
|
|
|
/* Enhanced features register 2 */
|
|
|
|
#define UART_OMAP_EFR2 0x23
|
|
|
|
#define UART_OMAP_EFR2_TIMEOUT_BEHAVE BIT(6)
|
|
|
|
|
2021-06-22 14:57:04 +00:00
|
|
|
/* RX FIFO occupancy indicator */
|
2021-09-03 05:05:50 +00:00
|
|
|
#define UART_OMAP_RX_LVL 0x19
|
2021-06-22 14:57:04 +00:00
|
|
|
|
2023-10-17 13:05:40 +00:00
|
|
|
/*
|
|
|
|
* Copy of the genpd flags for the console.
|
|
|
|
* Only used if console suspend is disabled
|
|
|
|
*/
|
|
|
|
static unsigned int genpd_flags_console;
|
|
|
|
|
2014-09-29 18:06:39 +00:00
|
|
|
struct omap8250_priv {
|
2023-05-08 08:20:12 +00:00
|
|
|
void __iomem *membase;
|
2014-09-29 18:06:39 +00:00
|
|
|
int line;
|
|
|
|
u8 habit;
|
|
|
|
u8 mdr1;
|
2022-10-16 08:02:00 +00:00
|
|
|
u8 mdr3;
|
2014-09-29 18:06:39 +00:00
|
|
|
u8 efr;
|
|
|
|
u8 scr;
|
|
|
|
u8 wer;
|
|
|
|
u8 xon;
|
|
|
|
u8 xoff;
|
2014-09-29 18:06:49 +00:00
|
|
|
u8 delayed_restore;
|
2014-09-29 18:06:39 +00:00
|
|
|
u16 quot;
|
|
|
|
|
2020-03-19 11:03:42 +00:00
|
|
|
u8 tx_trigger;
|
|
|
|
u8 rx_trigger;
|
2023-10-04 06:26:48 +00:00
|
|
|
atomic_t active;
|
2014-09-29 18:06:39 +00:00
|
|
|
bool is_suspending;
|
|
|
|
int wakeirq;
|
|
|
|
int wakeups_enabled;
|
|
|
|
u32 latency;
|
|
|
|
u32 calc_latency;
|
|
|
|
struct pm_qos_request pm_qos_request;
|
|
|
|
struct work_struct qos_work;
|
|
|
|
struct uart_8250_dma omap8250_dma;
|
2015-04-27 11:52:33 +00:00
|
|
|
spinlock_t rx_dma_lock;
|
2015-08-14 15:52:07 +00:00
|
|
|
bool rx_dma_broken;
|
2018-02-08 12:55:42 +00:00
|
|
|
bool throttled;
|
2014-09-29 18:06:39 +00:00
|
|
|
};
|
|
|
|
|
2020-03-19 11:03:42 +00:00
|
|
|
struct omap8250_dma_params {
|
|
|
|
u32 rx_size;
|
|
|
|
u8 rx_trigger;
|
|
|
|
u8 tx_trigger;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct omap8250_platdata {
|
|
|
|
struct omap8250_dma_params *dma_params;
|
|
|
|
u8 habit;
|
|
|
|
};
|
|
|
|
|
2016-04-10 05:14:36 +00:00
|
|
|
#ifdef CONFIG_SERIAL_8250_DMA
|
|
|
|
static void omap_8250_rx_dma_flush(struct uart_8250_port *p);
|
|
|
|
#else
|
|
|
|
static inline void omap_8250_rx_dma_flush(struct uart_8250_port *p) { }
|
|
|
|
#endif
|
|
|
|
|
2023-05-08 08:20:12 +00:00
|
|
|
static u32 uart_read(struct omap8250_priv *priv, u32 reg)
|
2014-09-29 18:06:39 +00:00
|
|
|
{
|
2023-05-08 08:20:12 +00:00
|
|
|
return readl(priv->membase + (reg << OMAP_UART_REGSHIFT));
|
|
|
|
}
|
|
|
|
|
2022-10-24 06:36:13 +00:00
|
|
|
/*
|
|
|
|
* Called on runtime PM resume path from omap8250_restore_regs(), and
|
|
|
|
* omap8250_set_mctrl().
|
|
|
|
*/
|
|
|
|
static void __omap8250_set_mctrl(struct uart_port *port, unsigned int mctrl)
|
2014-12-31 01:28:15 +00:00
|
|
|
{
|
|
|
|
struct uart_8250_port *up = up_to_u8250p(port);
|
|
|
|
struct omap8250_priv *priv = up->port.private_data;
|
|
|
|
u8 lcr;
|
|
|
|
|
|
|
|
serial8250_do_set_mctrl(port, mctrl);
|
|
|
|
|
2019-10-06 16:33:12 +00:00
|
|
|
if (!mctrl_gpio_to_gpiod(up->gpios, UART_GPIO_RTS)) {
|
2019-06-20 06:24:20 +00:00
|
|
|
/*
|
|
|
|
* Turn off autoRTS if RTS is lowered and restore autoRTS
|
|
|
|
* setting if RTS is raised
|
|
|
|
*/
|
|
|
|
lcr = serial_in(up, UART_LCR);
|
|
|
|
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
|
|
|
|
if ((mctrl & TIOCM_RTS) && (port->status & UPSTAT_AUTORTS))
|
|
|
|
priv->efr |= UART_EFR_RTS;
|
|
|
|
else
|
|
|
|
priv->efr &= ~UART_EFR_RTS;
|
|
|
|
serial_out(up, UART_EFR, priv->efr);
|
|
|
|
serial_out(up, UART_LCR, lcr);
|
|
|
|
}
|
2014-12-31 01:28:15 +00:00
|
|
|
}
|
|
|
|
|
2022-10-24 06:36:13 +00:00
|
|
|
static void omap8250_set_mctrl(struct uart_port *port, unsigned int mctrl)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = pm_runtime_resume_and_get(port->dev);
|
|
|
|
if (err)
|
|
|
|
return;
|
|
|
|
|
|
|
|
__omap8250_set_mctrl(port, mctrl);
|
|
|
|
|
|
|
|
pm_runtime_mark_last_busy(port->dev);
|
|
|
|
pm_runtime_put_autosuspend(port->dev);
|
|
|
|
}
|
|
|
|
|
2014-09-29 18:06:39 +00:00
|
|
|
/*
|
|
|
|
* Work Around for Errata i202 (2430, 3430, 3630, 4430 and 4460)
|
|
|
|
* The access to uart register after MDR1 Access
|
|
|
|
* causes UART to corrupt data.
|
|
|
|
*
|
|
|
|
* Need a delay =
|
|
|
|
* 5 L4 clock cycles + 5 UART functional clock cycle (@48MHz = ~0.2uS)
|
|
|
|
* give 10 times as much
|
|
|
|
*/
|
|
|
|
static void omap_8250_mdr1_errataset(struct uart_8250_port *up,
|
|
|
|
struct omap8250_priv *priv)
|
|
|
|
{
|
|
|
|
serial_out(up, UART_OMAP_MDR1, priv->mdr1);
|
|
|
|
udelay(2);
|
|
|
|
serial_out(up, UART_FCR, up->fcr | UART_FCR_CLEAR_XMIT |
|
|
|
|
UART_FCR_CLEAR_RCVR);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void omap_8250_get_divisor(struct uart_port *port, unsigned int baud,
|
|
|
|
struct omap8250_priv *priv)
|
|
|
|
{
|
|
|
|
unsigned int uartclk = port->uartclk;
|
|
|
|
unsigned int div_13, div_16;
|
|
|
|
unsigned int abs_d13, abs_d16;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Old custom speed handling.
|
|
|
|
*/
|
|
|
|
if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST) {
|
2017-09-26 10:40:02 +00:00
|
|
|
priv->quot = port->custom_divisor & UART_DIV_MAX;
|
2014-09-29 18:06:39 +00:00
|
|
|
/*
|
|
|
|
* I assume that nobody is using this. But hey, if somebody
|
|
|
|
* would like to specify the divisor _and_ the mode then the
|
|
|
|
* driver is ready and waiting for it.
|
|
|
|
*/
|
|
|
|
if (port->custom_divisor & (1 << 16))
|
|
|
|
priv->mdr1 = UART_OMAP_MDR1_13X_MODE;
|
|
|
|
else
|
|
|
|
priv->mdr1 = UART_OMAP_MDR1_16X_MODE;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
div_13 = DIV_ROUND_CLOSEST(uartclk, 13 * baud);
|
|
|
|
div_16 = DIV_ROUND_CLOSEST(uartclk, 16 * baud);
|
|
|
|
|
|
|
|
if (!div_13)
|
|
|
|
div_13 = 1;
|
|
|
|
if (!div_16)
|
|
|
|
div_16 = 1;
|
|
|
|
|
|
|
|
abs_d13 = abs(baud - uartclk / 13 / div_13);
|
|
|
|
abs_d16 = abs(baud - uartclk / 16 / div_16);
|
|
|
|
|
|
|
|
if (abs_d13 >= abs_d16) {
|
|
|
|
priv->mdr1 = UART_OMAP_MDR1_16X_MODE;
|
|
|
|
priv->quot = div_16;
|
|
|
|
} else {
|
|
|
|
priv->mdr1 = UART_OMAP_MDR1_13X_MODE;
|
|
|
|
priv->quot = div_13;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void omap8250_update_scr(struct uart_8250_port *up,
|
|
|
|
struct omap8250_priv *priv)
|
|
|
|
{
|
|
|
|
u8 old_scr;
|
|
|
|
|
|
|
|
old_scr = serial_in(up, UART_OMAP_SCR);
|
|
|
|
if (old_scr == priv->scr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The manual recommends not to enable the DMA mode selector in the SCR
|
|
|
|
* (instead of the FCR) register _and_ selecting the DMA mode as one
|
|
|
|
* register write because this may lead to malfunction.
|
|
|
|
*/
|
|
|
|
if (priv->scr & OMAP_UART_SCR_DMAMODE_MASK)
|
|
|
|
serial_out(up, UART_OMAP_SCR,
|
|
|
|
priv->scr & ~OMAP_UART_SCR_DMAMODE_MASK);
|
|
|
|
serial_out(up, UART_OMAP_SCR, priv->scr);
|
|
|
|
}
|
|
|
|
|
2015-07-14 08:02:05 +00:00
|
|
|
static void omap8250_update_mdr1(struct uart_8250_port *up,
|
|
|
|
struct omap8250_priv *priv)
|
|
|
|
{
|
|
|
|
if (priv->habit & UART_ERRATA_i202_MDR1_ACCESS)
|
|
|
|
omap_8250_mdr1_errataset(up, priv);
|
|
|
|
else
|
|
|
|
serial_out(up, UART_OMAP_MDR1, priv->mdr1);
|
|
|
|
}
|
|
|
|
|
2014-09-29 18:06:39 +00:00
|
|
|
static void omap8250_restore_regs(struct uart_8250_port *up)
|
|
|
|
{
|
|
|
|
struct omap8250_priv *priv = up->port.private_data;
|
2014-09-29 18:06:49 +00:00
|
|
|
struct uart_8250_dma *dma = up->dma;
|
serial: 8250: 8250_omap: Avoid RS485 RTS glitch on ->set_termios()
RS485-enabled UART ports on TI Sitara SoCs with active-low polarity
exhibit a Transmit Enable glitch on ->set_termios():
omap8250_restore_regs(), which is called from omap_8250_set_termios(),
sets the TCRTLR bit in the MCR register and clears all other bits,
including RTS. If RTS uses active-low polarity, it is now asserted
for no reason.
The TCRTLR bit is subsequently cleared by writing up->mcr to the MCR
register. That variable is always zero, so the RTS bit is still cleared
(incorrectly so if RTS is active-high).
(up->mcr is not, as one might think, a cache of the MCR register's
current value. Rather, it only caches a single bit of that register,
the AFE bit. And it only does so if the UART supports the AFE bit,
which OMAP does not. For details see serial8250_do_set_termios() and
serial8250_do_set_mctrl().)
Finally at the end of omap8250_restore_regs(), the MCR register is
restored (and RTS deasserted) by a call to up->port.ops->set_mctrl()
(which equals serial8250_set_mctrl()) and serial8250_em485_stop_tx().
So there's an RTS glitch between setting TCRTLR and calling
serial8250_em485_stop_tx(). Avoid by using a read-modify-write
when setting TCRTLR.
While at it, drop a redundant initialization of up->mcr. As explained
above, the variable isn't used by the driver and it is already
initialized to zero because it is part of the static struct
serial8250_ports[] declared in 8250_core.c. (Static structs are
initialized to zero per section 6.7.8 nr. 10 of the C99 standard.)
Cc: Jan Kiszka <jan.kiszka@siemens.com>
Cc: Su Bao Cheng <baocheng.su@siemens.com>
Tested-by: Matthias Schiffer <matthias.schiffer@ew.tq-group.com>
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Link: https://lore.kernel.org/r/6554b0241a2c7fd50f32576fdbafed96709e11e8.1664278942.git.lukas@wunner.de
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2022-09-27 11:52:34 +00:00
|
|
|
u8 mcr = serial8250_in_MCR(up);
|
2014-09-29 18:06:49 +00:00
|
|
|
|
2023-05-25 09:31:57 +00:00
|
|
|
/* Port locked to synchronize UART_IER access against the console. */
|
|
|
|
lockdep_assert_held_once(&up->port.lock);
|
|
|
|
|
2014-09-29 18:06:49 +00:00
|
|
|
if (dma && dma->tx_running) {
|
|
|
|
/*
|
|
|
|
* TCSANOW requests the change to occur immediately however if
|
|
|
|
* we have a TX-DMA operation in progress then it has been
|
|
|
|
* observed that it might stall and never complete. Therefore we
|
|
|
|
* delay DMA completes to prevent this hang from happen.
|
|
|
|
*/
|
|
|
|
priv->delayed_restore = 1;
|
|
|
|
return;
|
|
|
|
}
|
2014-09-29 18:06:39 +00:00
|
|
|
|
|
|
|
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
|
|
|
|
serial_out(up, UART_EFR, UART_EFR_ECB);
|
|
|
|
|
|
|
|
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
|
serial: 8250: 8250_omap: Avoid RS485 RTS glitch on ->set_termios()
RS485-enabled UART ports on TI Sitara SoCs with active-low polarity
exhibit a Transmit Enable glitch on ->set_termios():
omap8250_restore_regs(), which is called from omap_8250_set_termios(),
sets the TCRTLR bit in the MCR register and clears all other bits,
including RTS. If RTS uses active-low polarity, it is now asserted
for no reason.
The TCRTLR bit is subsequently cleared by writing up->mcr to the MCR
register. That variable is always zero, so the RTS bit is still cleared
(incorrectly so if RTS is active-high).
(up->mcr is not, as one might think, a cache of the MCR register's
current value. Rather, it only caches a single bit of that register,
the AFE bit. And it only does so if the UART supports the AFE bit,
which OMAP does not. For details see serial8250_do_set_termios() and
serial8250_do_set_mctrl().)
Finally at the end of omap8250_restore_regs(), the MCR register is
restored (and RTS deasserted) by a call to up->port.ops->set_mctrl()
(which equals serial8250_set_mctrl()) and serial8250_em485_stop_tx().
So there's an RTS glitch between setting TCRTLR and calling
serial8250_em485_stop_tx(). Avoid by using a read-modify-write
when setting TCRTLR.
While at it, drop a redundant initialization of up->mcr. As explained
above, the variable isn't used by the driver and it is already
initialized to zero because it is part of the static struct
serial8250_ports[] declared in 8250_core.c. (Static structs are
initialized to zero per section 6.7.8 nr. 10 of the C99 standard.)
Cc: Jan Kiszka <jan.kiszka@siemens.com>
Cc: Su Bao Cheng <baocheng.su@siemens.com>
Tested-by: Matthias Schiffer <matthias.schiffer@ew.tq-group.com>
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Link: https://lore.kernel.org/r/6554b0241a2c7fd50f32576fdbafed96709e11e8.1664278942.git.lukas@wunner.de
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2022-09-27 11:52:34 +00:00
|
|
|
serial8250_out_MCR(up, mcr | UART_MCR_TCRTLR);
|
2014-09-29 18:06:39 +00:00
|
|
|
serial_out(up, UART_FCR, up->fcr);
|
|
|
|
|
|
|
|
omap8250_update_scr(up, priv);
|
|
|
|
|
|
|
|
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
|
|
|
|
|
|
|
|
serial_out(up, UART_TI752_TCR, OMAP_UART_TCR_RESTORE(16) |
|
|
|
|
OMAP_UART_TCR_HALT(52));
|
|
|
|
serial_out(up, UART_TI752_TLR,
|
2020-03-19 11:03:42 +00:00
|
|
|
TRIGGER_TLR_MASK(priv->tx_trigger) << UART_TI752_TLR_TX |
|
|
|
|
TRIGGER_TLR_MASK(priv->rx_trigger) << UART_TI752_TLR_RX);
|
2014-09-29 18:06:39 +00:00
|
|
|
|
|
|
|
serial_out(up, UART_LCR, 0);
|
|
|
|
|
|
|
|
/* drop TCR + TLR access, we setup XON/XOFF later */
|
serial: 8250: 8250_omap: Avoid RS485 RTS glitch on ->set_termios()
RS485-enabled UART ports on TI Sitara SoCs with active-low polarity
exhibit a Transmit Enable glitch on ->set_termios():
omap8250_restore_regs(), which is called from omap_8250_set_termios(),
sets the TCRTLR bit in the MCR register and clears all other bits,
including RTS. If RTS uses active-low polarity, it is now asserted
for no reason.
The TCRTLR bit is subsequently cleared by writing up->mcr to the MCR
register. That variable is always zero, so the RTS bit is still cleared
(incorrectly so if RTS is active-high).
(up->mcr is not, as one might think, a cache of the MCR register's
current value. Rather, it only caches a single bit of that register,
the AFE bit. And it only does so if the UART supports the AFE bit,
which OMAP does not. For details see serial8250_do_set_termios() and
serial8250_do_set_mctrl().)
Finally at the end of omap8250_restore_regs(), the MCR register is
restored (and RTS deasserted) by a call to up->port.ops->set_mctrl()
(which equals serial8250_set_mctrl()) and serial8250_em485_stop_tx().
So there's an RTS glitch between setting TCRTLR and calling
serial8250_em485_stop_tx(). Avoid by using a read-modify-write
when setting TCRTLR.
While at it, drop a redundant initialization of up->mcr. As explained
above, the variable isn't used by the driver and it is already
initialized to zero because it is part of the static struct
serial8250_ports[] declared in 8250_core.c. (Static structs are
initialized to zero per section 6.7.8 nr. 10 of the C99 standard.)
Cc: Jan Kiszka <jan.kiszka@siemens.com>
Cc: Su Bao Cheng <baocheng.su@siemens.com>
Tested-by: Matthias Schiffer <matthias.schiffer@ew.tq-group.com>
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Link: https://lore.kernel.org/r/6554b0241a2c7fd50f32576fdbafed96709e11e8.1664278942.git.lukas@wunner.de
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2022-09-27 11:52:34 +00:00
|
|
|
serial8250_out_MCR(up, mcr);
|
|
|
|
|
2014-09-29 18:06:39 +00:00
|
|
|
serial_out(up, UART_IER, up->ier);
|
|
|
|
|
|
|
|
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
|
|
|
|
serial_dl_write(up, priv->quot);
|
|
|
|
|
2015-01-25 19:44:52 +00:00
|
|
|
serial_out(up, UART_EFR, priv->efr);
|
2014-09-29 18:06:39 +00:00
|
|
|
|
|
|
|
/* Configure flow control */
|
|
|
|
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
|
|
|
|
serial_out(up, UART_XON1, priv->xon);
|
|
|
|
serial_out(up, UART_XOFF1, priv->xoff);
|
|
|
|
|
|
|
|
serial_out(up, UART_LCR, up->lcr);
|
2015-07-14 08:02:05 +00:00
|
|
|
|
|
|
|
omap8250_update_mdr1(up, priv);
|
|
|
|
|
2022-10-24 06:36:13 +00:00
|
|
|
__omap8250_set_mctrl(&up->port, up->port.mctrl);
|
serial: Deassert Transmit Enable on probe in driver-specific way
When a UART port is newly registered, uart_configure_port() seeks to
deassert RS485 Transmit Enable by setting the RTS bit in port->mctrl.
However a number of UART drivers interpret a set RTS bit as *assertion*
instead of deassertion: Affected drivers include those using
serial8250_em485_config() (except 8250_bcm2835aux.c) and some using
mctrl_gpio (e.g. imx.c).
Since the interpretation of the RTS bit is driver-specific, it is not
suitable as a means to centrally deassert Transmit Enable in the serial
core. Instead, the serial core must call on drivers to deassert it in
their driver-specific way. One way to achieve that is to call
->rs485_config(). It implicitly deasserts Transmit Enable.
So amend uart_configure_port() and uart_resume_port() to invoke
uart_rs485_config(). That allows removing calls to uart_rs485_config()
from drivers' ->probe() hooks and declaring the function static.
Skip any invocation of ->set_mctrl() if RS485 is enabled. RS485 has no
hardware flow control, so the modem control lines are irrelevant and
need not be touched. When leaving RS485 mode, reset the modem control
lines to the state stored in port->mctrl. That way, UARTs which are
muxed between RS485 and RS232 transceivers drive the lines correctly
when switched to RS232. (serial8250_do_startup() historically raises
the OUT1 modem signal because otherwise interrupts are not signaled on
ancient PC UARTs, but I believe that no longer applies to modern,
RS485-capable UARTs and is thus safe to be skipped.)
imx.c modifies port->mctrl whenever Transmit Enable is asserted and
deasserted. Stop it from doing that so port->mctrl reflects the RS232
line state.
8250_omap.c deasserts Transmit Enable on ->runtime_resume() by calling
->set_mctrl(). Because that is now a no-op in RS485 mode, amend the
function to call serial8250_em485_stop_tx().
fsl_lpuart.c retrieves and applies the RS485 device tree properties
after registering the UART port. Because applying now happens on
registration in uart_configure_port(), move retrieval of the properties
ahead of uart_add_one_port().
Link: https://lore.kernel.org/all/20220329085050.311408-1-matthias.schiffer@ew.tq-group.com/
Link: https://lore.kernel.org/all/8f538a8903795f22f9acc94a9a31b03c9c4ccacb.camel@ginzinger.com/
Fixes: d3b3404df318 ("serial: Fix incorrect rs485 polarity on uart open")
Cc: stable@vger.kernel.org # v4.14+
Reported-by: Matthias Schiffer <matthias.schiffer@ew.tq-group.com>
Reported-by: Roosen Henri <Henri.Roosen@ginzinger.com>
Tested-by: Matthias Schiffer <matthias.schiffer@ew.tq-group.com>
Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Link: https://lore.kernel.org/r/2de36eba3fbe11278d5002e4e501afe0ceaca039.1663863805.git.lukas@wunner.de
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2022-09-22 16:27:33 +00:00
|
|
|
|
2022-10-16 08:02:00 +00:00
|
|
|
serial_out(up, UART_OMAP_MDR3, priv->mdr3);
|
|
|
|
|
|
|
|
if (up->port.rs485.flags & SER_RS485_ENABLED &&
|
|
|
|
up->port.rs485_config == serial8250_em485_config)
|
serial: Deassert Transmit Enable on probe in driver-specific way
When a UART port is newly registered, uart_configure_port() seeks to
deassert RS485 Transmit Enable by setting the RTS bit in port->mctrl.
However a number of UART drivers interpret a set RTS bit as *assertion*
instead of deassertion: Affected drivers include those using
serial8250_em485_config() (except 8250_bcm2835aux.c) and some using
mctrl_gpio (e.g. imx.c).
Since the interpretation of the RTS bit is driver-specific, it is not
suitable as a means to centrally deassert Transmit Enable in the serial
core. Instead, the serial core must call on drivers to deassert it in
their driver-specific way. One way to achieve that is to call
->rs485_config(). It implicitly deasserts Transmit Enable.
So amend uart_configure_port() and uart_resume_port() to invoke
uart_rs485_config(). That allows removing calls to uart_rs485_config()
from drivers' ->probe() hooks and declaring the function static.
Skip any invocation of ->set_mctrl() if RS485 is enabled. RS485 has no
hardware flow control, so the modem control lines are irrelevant and
need not be touched. When leaving RS485 mode, reset the modem control
lines to the state stored in port->mctrl. That way, UARTs which are
muxed between RS485 and RS232 transceivers drive the lines correctly
when switched to RS232. (serial8250_do_startup() historically raises
the OUT1 modem signal because otherwise interrupts are not signaled on
ancient PC UARTs, but I believe that no longer applies to modern,
RS485-capable UARTs and is thus safe to be skipped.)
imx.c modifies port->mctrl whenever Transmit Enable is asserted and
deasserted. Stop it from doing that so port->mctrl reflects the RS232
line state.
8250_omap.c deasserts Transmit Enable on ->runtime_resume() by calling
->set_mctrl(). Because that is now a no-op in RS485 mode, amend the
function to call serial8250_em485_stop_tx().
fsl_lpuart.c retrieves and applies the RS485 device tree properties
after registering the UART port. Because applying now happens on
registration in uart_configure_port(), move retrieval of the properties
ahead of uart_add_one_port().
Link: https://lore.kernel.org/all/20220329085050.311408-1-matthias.schiffer@ew.tq-group.com/
Link: https://lore.kernel.org/all/8f538a8903795f22f9acc94a9a31b03c9c4ccacb.camel@ginzinger.com/
Fixes: d3b3404df318 ("serial: Fix incorrect rs485 polarity on uart open")
Cc: stable@vger.kernel.org # v4.14+
Reported-by: Matthias Schiffer <matthias.schiffer@ew.tq-group.com>
Reported-by: Roosen Henri <Henri.Roosen@ginzinger.com>
Tested-by: Matthias Schiffer <matthias.schiffer@ew.tq-group.com>
Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Link: https://lore.kernel.org/r/2de36eba3fbe11278d5002e4e501afe0ceaca039.1663863805.git.lukas@wunner.de
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2022-09-22 16:27:33 +00:00
|
|
|
serial8250_em485_stop_tx(up);
|
2014-09-29 18:06:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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,
|
2022-08-16 11:57:37 +00:00
|
|
|
const struct ktermios *old)
|
2014-09-29 18:06:39 +00:00
|
|
|
{
|
2016-02-18 19:22:59 +00:00
|
|
|
struct uart_8250_port *up = up_to_u8250p(port);
|
2014-09-29 18:06:39 +00:00
|
|
|
struct omap8250_priv *priv = up->port.private_data;
|
|
|
|
unsigned char cval = 0;
|
|
|
|
unsigned int baud;
|
|
|
|
|
2022-02-24 09:55:55 +00:00
|
|
|
cval = UART_LCR_WLEN(tty_get_char_size(termios->c_cflag));
|
2014-09-29 18:06:39 +00:00
|
|
|
|
|
|
|
if (termios->c_cflag & CSTOPB)
|
|
|
|
cval |= UART_LCR_STOP;
|
|
|
|
if (termios->c_cflag & PARENB)
|
|
|
|
cval |= UART_LCR_PARITY;
|
|
|
|
if (!(termios->c_cflag & PARODD))
|
|
|
|
cval |= UART_LCR_EPAR;
|
|
|
|
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,
|
2017-09-26 10:40:02 +00:00
|
|
|
port->uartclk / 16 / UART_DIV_MAX,
|
2014-09-29 18:06:39 +00:00
|
|
|
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);
|
2023-09-14 18:37:29 +00:00
|
|
|
uart_port_lock_irq(port);
|
2014-09-29 18:06:39 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
if (termios->c_iflag & INPCK)
|
|
|
|
up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE;
|
|
|
|
if (termios->c_iflag & (IGNBRK | PARMRK))
|
|
|
|
up->port.read_status_mask |= UART_LSR_BI;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Characters to ignore
|
|
|
|
*/
|
|
|
|
up->port.ignore_status_mask = 0;
|
|
|
|
if (termios->c_iflag & IGNPAR)
|
|
|
|
up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
|
|
|
|
if (termios->c_iflag & IGNBRK) {
|
|
|
|
up->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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ignore all characters if CREAD is not set
|
|
|
|
*/
|
|
|
|
if ((termios->c_cflag & CREAD) == 0)
|
|
|
|
up->port.ignore_status_mask |= UART_LSR_DR;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Modem status interrupts
|
|
|
|
*/
|
|
|
|
up->ier &= ~UART_IER_MSI;
|
|
|
|
if (UART_ENABLE_MS(&up->port, termios->c_cflag))
|
|
|
|
up->ier |= UART_IER_MSI;
|
|
|
|
|
|
|
|
up->lcr = cval;
|
|
|
|
/* Up to here it was mostly serial8250_do_set_termios() */
|
|
|
|
|
|
|
|
/*
|
2017-12-06 15:52:23 +00:00
|
|
|
* We enable TRIG_GRANU for RX and TX and additionally we set
|
2014-09-29 18:06:39 +00:00
|
|
|
* SCR_TX_EMPTY bit. The result is the following:
|
|
|
|
* - RX_TRIGGER amount of bytes in the FIFO will cause an interrupt.
|
|
|
|
* - less than RX_TRIGGER number of bytes will also cause an interrupt
|
|
|
|
* once the UART decides that there no new bytes arriving.
|
|
|
|
* - Once THRE is enabled, the interrupt will be fired once the FIFO is
|
|
|
|
* empty - the trigger level is ignored here.
|
|
|
|
*
|
|
|
|
* Once DMA is enabled:
|
|
|
|
* - UART will assert the TX DMA line once there is room for TX_TRIGGER
|
|
|
|
* bytes in the TX FIFO. On each assert the DMA engine will move
|
|
|
|
* TX_TRIGGER bytes into the FIFO.
|
|
|
|
* - UART will assert the RX DMA line once there are RX_TRIGGER bytes in
|
|
|
|
* the FIFO and move RX_TRIGGER bytes.
|
|
|
|
* This is because threshold and trigger values are the same.
|
|
|
|
*/
|
|
|
|
up->fcr = UART_FCR_ENABLE_FIFO;
|
2020-03-19 11:03:42 +00:00
|
|
|
up->fcr |= TRIGGER_FCR_MASK(priv->tx_trigger) << OMAP_UART_FCR_TX_TRIG;
|
|
|
|
up->fcr |= TRIGGER_FCR_MASK(priv->rx_trigger) << OMAP_UART_FCR_RX_TRIG;
|
2014-09-29 18:06:39 +00:00
|
|
|
|
|
|
|
priv->scr = OMAP_UART_SCR_RX_TRIG_GRANU1_MASK | OMAP_UART_SCR_TX_EMPTY |
|
|
|
|
OMAP_UART_SCR_TX_TRIG_GRANU1_MASK;
|
|
|
|
|
2014-09-29 18:06:49 +00:00
|
|
|
if (up->dma)
|
|
|
|
priv->scr |= OMAP_UART_SCR_DMAMODE_1 |
|
|
|
|
OMAP_UART_SCR_DMAMODE_CTL;
|
|
|
|
|
2014-09-29 18:06:39 +00:00
|
|
|
priv->xon = termios->c_cc[VSTART];
|
|
|
|
priv->xoff = termios->c_cc[VSTOP];
|
|
|
|
|
|
|
|
priv->efr = 0;
|
serial: core: Rework hw-assisted flow control support
hw-assisted flow control support was added to the serial core
in v3.8 with commits,
dba05832cbe4f ("SERIAL: core: add hardware assisted h/w flow control support")
2cbacafd7af0f ("SERIAL: core: add hardware assisted s/w flow control support")
9aba8d5b01119 ("SERIAL: core: add throttle/unthrottle callbacks for hardware
assisted flow control")
Since then, additional requirements for serial core support have arisen.
Specifically,
1. Separate tx and rx flow control settings for UARTs which only support
tx flow control (ie., autoCTS).
2. Disable sw-assisted CTS flow control in autoCTS mode
3. Support for RTS flow control by serial core and userspace in autoRTS mode
Distinguish mode from capability; introduce UPSTAT_AUTORTS, UPSTAT_AUTOCTS
and UPSTAT_AUTOXOFF which, when set by the uart driver, enable serial core
support for hw-assisted rx, hw-assisted tx and hw-assisted in-band/IXOFF
rx flow control, respectively. [Note: hw-assisted in-band/IXON tx flow
control does not require serial core support/intervention and can be
enabled by the uart driver when required.]
These modes must be set/reset in the driver's set_termios() method, based
on termios settings, and thus can be safely queried in any context in which
one of the port lock, port mutex or termios rwsem are held. Set these modes
in the 2 in-tree drivers, omap-serial and 8250_omap, which currently
use UPF_HARD_FLOW/UPF_SOFT_FLOW support.
Retain UPF_HARD_FLOW and UPF_SOFT_FLOW as capabilities; re-define
UPF_HARD_FLOW as both UPF_AUTO_RTS and UPF_AUTO_CTS to allow for distinct
and separate rx and tx flow control capabilities.
Disable sw-assisted CTS flow control when UPSTAT_AUTOCTS is enabled.
Signed-off-by: Peter Hurley <peter@hurleysoftware.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2015-01-25 19:44:51 +00:00
|
|
|
up->port.status &= ~(UPSTAT_AUTOCTS | UPSTAT_AUTORTS | UPSTAT_AUTOXOFF);
|
|
|
|
|
2019-06-20 06:24:20 +00:00
|
|
|
if (termios->c_cflag & CRTSCTS && up->port.flags & UPF_HARD_FLOW &&
|
2019-10-06 16:33:12 +00:00
|
|
|
!mctrl_gpio_to_gpiod(up->gpios, UART_GPIO_RTS) &&
|
|
|
|
!mctrl_gpio_to_gpiod(up->gpios, UART_GPIO_CTS)) {
|
2015-01-25 19:44:52 +00:00
|
|
|
/* Enable AUTOCTS (autoRTS is enabled when RTS is raised) */
|
serial: core: Rework hw-assisted flow control support
hw-assisted flow control support was added to the serial core
in v3.8 with commits,
dba05832cbe4f ("SERIAL: core: add hardware assisted h/w flow control support")
2cbacafd7af0f ("SERIAL: core: add hardware assisted s/w flow control support")
9aba8d5b01119 ("SERIAL: core: add throttle/unthrottle callbacks for hardware
assisted flow control")
Since then, additional requirements for serial core support have arisen.
Specifically,
1. Separate tx and rx flow control settings for UARTs which only support
tx flow control (ie., autoCTS).
2. Disable sw-assisted CTS flow control in autoCTS mode
3. Support for RTS flow control by serial core and userspace in autoRTS mode
Distinguish mode from capability; introduce UPSTAT_AUTORTS, UPSTAT_AUTOCTS
and UPSTAT_AUTOXOFF which, when set by the uart driver, enable serial core
support for hw-assisted rx, hw-assisted tx and hw-assisted in-band/IXOFF
rx flow control, respectively. [Note: hw-assisted in-band/IXON tx flow
control does not require serial core support/intervention and can be
enabled by the uart driver when required.]
These modes must be set/reset in the driver's set_termios() method, based
on termios settings, and thus can be safely queried in any context in which
one of the port lock, port mutex or termios rwsem are held. Set these modes
in the 2 in-tree drivers, omap-serial and 8250_omap, which currently
use UPF_HARD_FLOW/UPF_SOFT_FLOW support.
Retain UPF_HARD_FLOW and UPF_SOFT_FLOW as capabilities; re-define
UPF_HARD_FLOW as both UPF_AUTO_RTS and UPF_AUTO_CTS to allow for distinct
and separate rx and tx flow control capabilities.
Disable sw-assisted CTS flow control when UPSTAT_AUTOCTS is enabled.
Signed-off-by: Peter Hurley <peter@hurleysoftware.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2015-01-25 19:44:51 +00:00
|
|
|
up->port.status |= UPSTAT_AUTOCTS | UPSTAT_AUTORTS;
|
2015-01-25 19:44:52 +00:00
|
|
|
priv->efr |= UART_EFR_CTS;
|
2014-09-29 18:06:39 +00:00
|
|
|
} else if (up->port.flags & UPF_SOFT_FLOW) {
|
|
|
|
/*
|
2015-06-27 13:28:55 +00:00
|
|
|
* OMAP rx s/w flow control is borked; the transmitter remains
|
|
|
|
* stuck off even if rx flow control is subsequently disabled
|
2014-09-29 18:06:39 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* IXOFF Flag:
|
|
|
|
* Enable XON/XOFF flow control on output.
|
|
|
|
* Transmit XON1, XOFF1
|
|
|
|
*/
|
serial: core: Rework hw-assisted flow control support
hw-assisted flow control support was added to the serial core
in v3.8 with commits,
dba05832cbe4f ("SERIAL: core: add hardware assisted h/w flow control support")
2cbacafd7af0f ("SERIAL: core: add hardware assisted s/w flow control support")
9aba8d5b01119 ("SERIAL: core: add throttle/unthrottle callbacks for hardware
assisted flow control")
Since then, additional requirements for serial core support have arisen.
Specifically,
1. Separate tx and rx flow control settings for UARTs which only support
tx flow control (ie., autoCTS).
2. Disable sw-assisted CTS flow control in autoCTS mode
3. Support for RTS flow control by serial core and userspace in autoRTS mode
Distinguish mode from capability; introduce UPSTAT_AUTORTS, UPSTAT_AUTOCTS
and UPSTAT_AUTOXOFF which, when set by the uart driver, enable serial core
support for hw-assisted rx, hw-assisted tx and hw-assisted in-band/IXOFF
rx flow control, respectively. [Note: hw-assisted in-band/IXON tx flow
control does not require serial core support/intervention and can be
enabled by the uart driver when required.]
These modes must be set/reset in the driver's set_termios() method, based
on termios settings, and thus can be safely queried in any context in which
one of the port lock, port mutex or termios rwsem are held. Set these modes
in the 2 in-tree drivers, omap-serial and 8250_omap, which currently
use UPF_HARD_FLOW/UPF_SOFT_FLOW support.
Retain UPF_HARD_FLOW and UPF_SOFT_FLOW as capabilities; re-define
UPF_HARD_FLOW as both UPF_AUTO_RTS and UPF_AUTO_CTS to allow for distinct
and separate rx and tx flow control capabilities.
Disable sw-assisted CTS flow control when UPSTAT_AUTOCTS is enabled.
Signed-off-by: Peter Hurley <peter@hurleysoftware.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2015-01-25 19:44:51 +00:00
|
|
|
if (termios->c_iflag & IXOFF) {
|
|
|
|
up->port.status |= UPSTAT_AUTOXOFF;
|
2014-09-29 18:06:39 +00:00
|
|
|
priv->efr |= OMAP_UART_SW_TX;
|
serial: core: Rework hw-assisted flow control support
hw-assisted flow control support was added to the serial core
in v3.8 with commits,
dba05832cbe4f ("SERIAL: core: add hardware assisted h/w flow control support")
2cbacafd7af0f ("SERIAL: core: add hardware assisted s/w flow control support")
9aba8d5b01119 ("SERIAL: core: add throttle/unthrottle callbacks for hardware
assisted flow control")
Since then, additional requirements for serial core support have arisen.
Specifically,
1. Separate tx and rx flow control settings for UARTs which only support
tx flow control (ie., autoCTS).
2. Disable sw-assisted CTS flow control in autoCTS mode
3. Support for RTS flow control by serial core and userspace in autoRTS mode
Distinguish mode from capability; introduce UPSTAT_AUTORTS, UPSTAT_AUTOCTS
and UPSTAT_AUTOXOFF which, when set by the uart driver, enable serial core
support for hw-assisted rx, hw-assisted tx and hw-assisted in-band/IXOFF
rx flow control, respectively. [Note: hw-assisted in-band/IXON tx flow
control does not require serial core support/intervention and can be
enabled by the uart driver when required.]
These modes must be set/reset in the driver's set_termios() method, based
on termios settings, and thus can be safely queried in any context in which
one of the port lock, port mutex or termios rwsem are held. Set these modes
in the 2 in-tree drivers, omap-serial and 8250_omap, which currently
use UPF_HARD_FLOW/UPF_SOFT_FLOW support.
Retain UPF_HARD_FLOW and UPF_SOFT_FLOW as capabilities; re-define
UPF_HARD_FLOW as both UPF_AUTO_RTS and UPF_AUTO_CTS to allow for distinct
and separate rx and tx flow control capabilities.
Disable sw-assisted CTS flow control when UPSTAT_AUTOCTS is enabled.
Signed-off-by: Peter Hurley <peter@hurleysoftware.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2015-01-25 19:44:51 +00:00
|
|
|
}
|
2014-09-29 18:06:39 +00:00
|
|
|
}
|
|
|
|
omap8250_restore_regs(up);
|
|
|
|
|
2023-09-14 18:37:29 +00:00
|
|
|
uart_port_unlock_irq(&up->port);
|
2014-09-29 18:06:39 +00:00
|
|
|
pm_runtime_mark_last_busy(port->dev);
|
|
|
|
pm_runtime_put_autosuspend(port->dev);
|
|
|
|
|
|
|
|
/* calculate wakeup latency constraint */
|
|
|
|
priv->calc_latency = USEC_PER_SEC * 64 * 8 / baud;
|
|
|
|
priv->latency = priv->calc_latency;
|
|
|
|
|
|
|
|
schedule_work(&priv->qos_work);
|
|
|
|
|
|
|
|
/* Don't rewrite B0 */
|
|
|
|
if (tty_termios_baud_rate(termios))
|
|
|
|
tty_termios_encode_baud_rate(termios, baud, baud);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* same as 8250 except that we may have extra flow bits set in EFR */
|
|
|
|
static void omap_8250_pm(struct uart_port *port, unsigned int state,
|
|
|
|
unsigned int oldstate)
|
|
|
|
{
|
2014-12-31 21:32:49 +00:00
|
|
|
struct uart_8250_port *up = up_to_u8250p(port);
|
|
|
|
u8 efr;
|
2014-09-29 18:06:39 +00:00
|
|
|
|
|
|
|
pm_runtime_get_sync(port->dev);
|
2023-05-25 09:31:59 +00:00
|
|
|
|
|
|
|
/* Synchronize UART_IER access against the console. */
|
2023-09-14 18:37:29 +00:00
|
|
|
uart_port_lock_irq(port);
|
2023-05-25 09:31:59 +00:00
|
|
|
|
2014-09-29 18:06:39 +00:00
|
|
|
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
|
2014-12-31 21:32:49 +00:00
|
|
|
efr = serial_in(up, UART_EFR);
|
|
|
|
serial_out(up, UART_EFR, efr | UART_EFR_ECB);
|
2014-09-29 18:06:39 +00:00
|
|
|
serial_out(up, UART_LCR, 0);
|
|
|
|
|
|
|
|
serial_out(up, UART_IER, (state != 0) ? UART_IERX_SLEEP : 0);
|
|
|
|
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
|
2014-12-31 21:32:49 +00:00
|
|
|
serial_out(up, UART_EFR, efr);
|
2014-09-29 18:06:39 +00:00
|
|
|
serial_out(up, UART_LCR, 0);
|
|
|
|
|
2023-09-14 18:37:29 +00:00
|
|
|
uart_port_unlock_irq(port);
|
2023-05-25 09:31:59 +00:00
|
|
|
|
2014-09-29 18:06:39 +00:00
|
|
|
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,
|
|
|
|
struct omap8250_priv *priv)
|
|
|
|
{
|
2021-07-15 14:07:59 +00:00
|
|
|
static const struct soc_device_attribute k3_soc_devices[] = {
|
2020-11-11 11:26:53 +00:00
|
|
|
{ .family = "AM65X", },
|
|
|
|
{ .family = "J721E", .revision = "SR1.0" },
|
|
|
|
{ /* sentinel */ }
|
|
|
|
};
|
2014-09-29 18:06:39 +00:00
|
|
|
u32 mvr, scheme;
|
|
|
|
u16 revision, major, minor;
|
|
|
|
|
2023-05-08 08:20:12 +00:00
|
|
|
mvr = uart_read(priv, UART_OMAP_MVER);
|
2014-09-29 18:06:39 +00:00
|
|
|
|
|
|
|
/* Check revision register scheme */
|
|
|
|
scheme = mvr >> OMAP_UART_MVR_SCHEME_SHIFT;
|
|
|
|
|
|
|
|
switch (scheme) {
|
|
|
|
case 0: /* Legacy Scheme: OMAP2/3 */
|
|
|
|
/* MINOR_REV[0:4], MAJOR_REV[4:7] */
|
|
|
|
major = (mvr & OMAP_UART_LEGACY_MVR_MAJ_MASK) >>
|
|
|
|
OMAP_UART_LEGACY_MVR_MAJ_SHIFT;
|
|
|
|
minor = (mvr & OMAP_UART_LEGACY_MVR_MIN_MASK);
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
/* New Scheme: OMAP4+ */
|
|
|
|
/* MINOR_REV[0:5], MAJOR_REV[8:10] */
|
|
|
|
major = (mvr & OMAP_UART_MVR_MAJ_MASK) >>
|
|
|
|
OMAP_UART_MVR_MAJ_SHIFT;
|
|
|
|
minor = (mvr & OMAP_UART_MVR_MIN_MASK);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
dev_warn(up->port.dev,
|
|
|
|
"Unknown revision, defaulting to highest\n");
|
|
|
|
/* highest possible revision */
|
|
|
|
major = 0xff;
|
|
|
|
minor = 0xff;
|
|
|
|
}
|
|
|
|
/* normalize revision for the driver */
|
|
|
|
revision = UART_BUILD_REVISION(major, minor);
|
|
|
|
|
|
|
|
switch (revision) {
|
|
|
|
case OMAP_UART_REV_46:
|
2015-07-14 08:02:06 +00:00
|
|
|
priv->habit |= UART_ERRATA_i202_MDR1_ACCESS;
|
2014-09-29 18:06:39 +00:00
|
|
|
break;
|
|
|
|
case OMAP_UART_REV_52:
|
2015-07-14 08:02:06 +00:00
|
|
|
priv->habit |= UART_ERRATA_i202_MDR1_ACCESS |
|
2014-09-29 18:06:39 +00:00
|
|
|
OMAP_UART_WER_HAS_TX_WAKEUP;
|
|
|
|
break;
|
|
|
|
case OMAP_UART_REV_63:
|
2015-07-14 08:02:06 +00:00
|
|
|
priv->habit |= UART_ERRATA_i202_MDR1_ACCESS |
|
2014-09-29 18:06:39 +00:00
|
|
|
OMAP_UART_WER_HAS_TX_WAKEUP;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2020-11-11 11:26:53 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* AM65x SR1.0, AM65x SR2.0 and J721e SR1.0 don't
|
|
|
|
* don't have RHR_IT_DIS bit in IER2 register. So drop to flag
|
|
|
|
* to enable errata workaround.
|
|
|
|
*/
|
|
|
|
if (soc_device_match(k3_soc_devices))
|
|
|
|
priv->habit &= ~UART_HAS_RHR_IT_DIS;
|
2014-09-29 18:06:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void omap8250_uart_qos_work(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct omap8250_priv *priv;
|
|
|
|
|
|
|
|
priv = container_of(work, struct omap8250_priv, qos_work);
|
2020-02-11 23:27:04 +00:00
|
|
|
cpu_latency_qos_update_request(&priv->pm_qos_request, priv->latency);
|
2014-09-29 18:06:39 +00:00
|
|
|
}
|
|
|
|
|
2015-05-20 20:07:35 +00:00
|
|
|
#ifdef CONFIG_SERIAL_8250_DMA
|
|
|
|
static int omap_8250_dma_handle_irq(struct uart_port *port);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static irqreturn_t omap8250_irq(int irq, void *dev_id)
|
|
|
|
{
|
2023-05-08 08:20:13 +00:00
|
|
|
struct omap8250_priv *priv = dev_id;
|
|
|
|
struct uart_8250_port *up = serial8250_get_port(priv->line);
|
|
|
|
struct uart_port *port = &up->port;
|
2021-07-27 10:35:33 +00:00
|
|
|
unsigned int iir, lsr;
|
2015-05-20 20:07:35 +00:00
|
|
|
int ret;
|
|
|
|
|
2023-10-04 06:26:48 +00:00
|
|
|
pm_runtime_get_noresume(port->dev);
|
|
|
|
|
|
|
|
/* Shallow idle state wake-up to an IO interrupt? */
|
|
|
|
if (atomic_add_unless(&priv->active, 1, 1)) {
|
|
|
|
priv->latency = priv->calc_latency;
|
|
|
|
schedule_work(&priv->qos_work);
|
|
|
|
}
|
|
|
|
|
2015-05-20 20:07:35 +00:00
|
|
|
#ifdef CONFIG_SERIAL_8250_DMA
|
|
|
|
if (up->dma) {
|
|
|
|
ret = omap_8250_dma_handle_irq(port);
|
2023-10-04 06:26:48 +00:00
|
|
|
pm_runtime_mark_last_busy(port->dev);
|
|
|
|
pm_runtime_put(port->dev);
|
2015-05-20 20:07:35 +00:00
|
|
|
return IRQ_RETVAL(ret);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2021-07-27 10:35:33 +00:00
|
|
|
lsr = serial_port_in(port, UART_LSR);
|
2015-05-20 20:07:35 +00:00
|
|
|
iir = serial_port_in(port, UART_IIR);
|
|
|
|
ret = serial8250_handle_irq(port, iir);
|
2021-06-22 14:57:04 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* On K3 SoCs, it is observed that RX TIMEOUT is signalled after
|
|
|
|
* FIFO has been drained, in which case a dummy read of RX FIFO
|
|
|
|
* is required to clear RX TIMEOUT condition.
|
|
|
|
*/
|
|
|
|
if (priv->habit & UART_RX_TIMEOUT_QUIRK &&
|
|
|
|
(iir & UART_IIR_RX_TIMEOUT) == UART_IIR_RX_TIMEOUT &&
|
|
|
|
serial_port_in(port, UART_OMAP_RX_LVL) == 0) {
|
|
|
|
serial_port_in(port, UART_RX);
|
|
|
|
}
|
|
|
|
|
2021-07-27 10:35:33 +00:00
|
|
|
/* Stop processing interrupts on input overrun */
|
|
|
|
if ((lsr & UART_LSR_OE) && up->overrun_backoff_time_ms > 0) {
|
|
|
|
unsigned long delay;
|
|
|
|
|
2023-05-25 09:31:58 +00:00
|
|
|
/* Synchronize UART_IER access against the console. */
|
2023-09-14 18:37:29 +00:00
|
|
|
uart_port_lock(port);
|
2021-07-27 10:35:33 +00:00
|
|
|
up->ier = port->serial_in(port, UART_IER);
|
|
|
|
if (up->ier & (UART_IER_RLSI | UART_IER_RDI)) {
|
|
|
|
port->ops->stop_rx(port);
|
|
|
|
} else {
|
|
|
|
/* Keep restarting the timer until
|
|
|
|
* the input overrun subsides.
|
|
|
|
*/
|
|
|
|
cancel_delayed_work(&up->overrun_backoff);
|
|
|
|
}
|
2023-09-14 18:37:29 +00:00
|
|
|
uart_port_unlock(port);
|
2021-07-27 10:35:33 +00:00
|
|
|
|
|
|
|
delay = msecs_to_jiffies(up->overrun_backoff_time_ms);
|
|
|
|
schedule_delayed_work(&up->overrun_backoff, delay);
|
|
|
|
}
|
|
|
|
|
2023-10-04 06:26:48 +00:00
|
|
|
pm_runtime_mark_last_busy(port->dev);
|
|
|
|
pm_runtime_put(port->dev);
|
2015-05-20 20:07:35 +00:00
|
|
|
|
|
|
|
return IRQ_RETVAL(ret);
|
|
|
|
}
|
|
|
|
|
2014-09-29 18:06:39 +00:00
|
|
|
static int omap_8250_startup(struct uart_port *port)
|
|
|
|
{
|
2015-05-20 20:07:35 +00:00
|
|
|
struct uart_8250_port *up = up_to_u8250p(port);
|
2014-09-29 18:06:39 +00:00
|
|
|
struct omap8250_priv *priv = port->private_data;
|
2023-05-08 08:20:14 +00:00
|
|
|
struct uart_8250_dma *dma = &priv->omap8250_dma;
|
2014-09-29 18:06:39 +00:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (priv->wakeirq) {
|
2015-06-10 06:35:00 +00:00
|
|
|
ret = dev_pm_set_dedicated_wake_irq(port->dev, priv->wakeirq);
|
2014-09-29 18:06:39 +00:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
pm_runtime_get_sync(port->dev);
|
|
|
|
|
2015-05-20 20:07:35 +00:00
|
|
|
serial_out(up, UART_FCR, UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
|
|
|
|
|
|
|
|
serial_out(up, UART_LCR, UART_LCR_WLEN8);
|
|
|
|
|
|
|
|
up->lsr_saved_flags = 0;
|
|
|
|
up->msr_saved_flags = 0;
|
|
|
|
|
2017-04-22 13:07:19 +00:00
|
|
|
/* Disable DMA for console UART */
|
2023-05-08 08:20:14 +00:00
|
|
|
if (dma->fn && !uart_console(port)) {
|
|
|
|
up->dma = &priv->omap8250_dma;
|
2015-05-20 20:07:35 +00:00
|
|
|
ret = serial8250_request_dma(up);
|
|
|
|
if (ret) {
|
|
|
|
dev_warn_ratelimited(port->dev,
|
|
|
|
"failed to request DMA\n");
|
|
|
|
up->dma = NULL;
|
|
|
|
}
|
2023-05-08 08:20:14 +00:00
|
|
|
} else {
|
|
|
|
up->dma = NULL;
|
2015-05-20 20:07:35 +00:00
|
|
|
}
|
|
|
|
|
2023-05-25 09:31:52 +00:00
|
|
|
/* Synchronize UART_IER access against the console. */
|
2023-09-14 18:37:29 +00:00
|
|
|
uart_port_lock_irq(port);
|
2015-05-20 20:07:35 +00:00
|
|
|
up->ier = UART_IER_RLSI | UART_IER_RDI;
|
|
|
|
serial_out(up, UART_IER, up->ier);
|
2023-09-14 18:37:29 +00:00
|
|
|
uart_port_unlock_irq(port);
|
2015-05-20 20:07:35 +00:00
|
|
|
|
2014-12-19 14:27:58 +00:00
|
|
|
#ifdef CONFIG_PM
|
2014-09-29 18:06:39 +00:00
|
|
|
up->capabilities |= UART_CAP_RPM;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Enable module level wake up */
|
|
|
|
priv->wer = OMAP_UART_WER_MOD_WKUP;
|
|
|
|
if (priv->habit & OMAP_UART_WER_HAS_TX_WAKEUP)
|
|
|
|
priv->wer |= OMAP_UART_TX_WAKEUP_EN;
|
|
|
|
serial_out(up, UART_OMAP_WER, priv->wer);
|
|
|
|
|
2023-05-25 09:31:56 +00:00
|
|
|
if (up->dma && !(priv->habit & UART_HAS_EFR2)) {
|
2023-09-14 18:37:29 +00:00
|
|
|
uart_port_lock_irq(port);
|
2016-04-10 05:14:36 +00:00
|
|
|
up->dma->rx_dma(up);
|
2023-09-14 18:37:29 +00:00
|
|
|
uart_port_unlock_irq(port);
|
2023-05-25 09:31:56 +00:00
|
|
|
}
|
2014-09-29 18:06:49 +00:00
|
|
|
|
2023-05-08 08:20:13 +00:00
|
|
|
enable_irq(up->port.irq);
|
|
|
|
|
2014-09-29 18:06:39 +00:00
|
|
|
pm_runtime_mark_last_busy(port->dev);
|
|
|
|
pm_runtime_put_autosuspend(port->dev);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void omap_8250_shutdown(struct uart_port *port)
|
|
|
|
{
|
2015-05-20 20:07:35 +00:00
|
|
|
struct uart_8250_port *up = up_to_u8250p(port);
|
2014-09-29 18:06:39 +00:00
|
|
|
struct omap8250_priv *priv = port->private_data;
|
|
|
|
|
|
|
|
flush_work(&priv->qos_work);
|
2014-09-29 18:06:49 +00:00
|
|
|
if (up->dma)
|
2016-04-10 05:14:36 +00:00
|
|
|
omap_8250_rx_dma_flush(up);
|
2014-09-29 18:06:39 +00:00
|
|
|
|
|
|
|
pm_runtime_get_sync(port->dev);
|
|
|
|
|
|
|
|
serial_out(up, UART_OMAP_WER, 0);
|
2020-03-19 11:03:44 +00:00
|
|
|
if (priv->habit & UART_HAS_EFR2)
|
|
|
|
serial_out(up, UART_OMAP_EFR2, 0x0);
|
2015-05-20 20:07:35 +00:00
|
|
|
|
2023-05-25 09:31:59 +00:00
|
|
|
/* Synchronize UART_IER access against the console. */
|
2023-09-14 18:37:29 +00:00
|
|
|
uart_port_lock_irq(port);
|
2015-05-20 20:07:35 +00:00
|
|
|
up->ier = 0;
|
|
|
|
serial_out(up, UART_IER, 0);
|
2023-09-14 18:37:29 +00:00
|
|
|
uart_port_unlock_irq(port);
|
2023-05-08 08:20:13 +00:00
|
|
|
disable_irq_nosync(up->port.irq);
|
|
|
|
dev_pm_clear_wake_irq(port->dev);
|
2015-05-20 20:07:35 +00:00
|
|
|
|
2023-05-08 08:20:14 +00:00
|
|
|
serial8250_release_dma(up);
|
|
|
|
up->dma = NULL;
|
2015-05-20 20:07:35 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Disable break condition and FIFOs
|
|
|
|
*/
|
|
|
|
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);
|
2014-09-29 18:06:39 +00:00
|
|
|
|
|
|
|
pm_runtime_mark_last_busy(port->dev);
|
|
|
|
pm_runtime_put_autosuspend(port->dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void omap_8250_throttle(struct uart_port *port)
|
|
|
|
{
|
2018-02-08 12:55:42 +00:00
|
|
|
struct omap8250_priv *priv = port->private_data;
|
2014-09-29 18:06:39 +00:00
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
pm_runtime_get_sync(port->dev);
|
|
|
|
|
2023-09-14 18:37:29 +00:00
|
|
|
uart_port_lock_irqsave(port, &flags);
|
2020-03-19 10:32:30 +00:00
|
|
|
port->ops->stop_rx(port);
|
2018-02-08 12:55:42 +00:00
|
|
|
priv->throttled = true;
|
2023-09-14 18:37:29 +00:00
|
|
|
uart_port_unlock_irqrestore(port, flags);
|
2014-09-29 18:06:39 +00:00
|
|
|
|
|
|
|
pm_runtime_mark_last_busy(port->dev);
|
|
|
|
pm_runtime_put_autosuspend(port->dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void omap_8250_unthrottle(struct uart_port *port)
|
|
|
|
{
|
2018-02-08 12:55:42 +00:00
|
|
|
struct omap8250_priv *priv = port->private_data;
|
2016-02-18 19:22:59 +00:00
|
|
|
struct uart_8250_port *up = up_to_u8250p(port);
|
2014-09-29 18:06:39 +00:00
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
pm_runtime_get_sync(port->dev);
|
|
|
|
|
2023-05-25 09:31:59 +00:00
|
|
|
/* Synchronize UART_IER access against the console. */
|
2023-09-14 18:37:29 +00:00
|
|
|
uart_port_lock_irqsave(port, &flags);
|
2018-02-08 12:55:42 +00:00
|
|
|
priv->throttled = false;
|
|
|
|
if (up->dma)
|
|
|
|
up->dma->rx_dma(up);
|
2014-09-29 18:06:39 +00:00
|
|
|
up->ier |= UART_IER_RLSI | UART_IER_RDI;
|
2020-03-19 10:32:30 +00:00
|
|
|
port->read_status_mask |= UART_LSR_DR;
|
2014-09-29 18:06:39 +00:00
|
|
|
serial_out(up, UART_IER, up->ier);
|
2023-09-14 18:37:29 +00:00
|
|
|
uart_port_unlock_irqrestore(port, flags);
|
2014-09-29 18:06:39 +00:00
|
|
|
|
|
|
|
pm_runtime_mark_last_busy(port->dev);
|
|
|
|
pm_runtime_put_autosuspend(port->dev);
|
|
|
|
}
|
|
|
|
|
2022-10-16 08:02:00 +00:00
|
|
|
static int omap8250_rs485_config(struct uart_port *port,
|
|
|
|
struct ktermios *termios,
|
|
|
|
struct serial_rs485 *rs485)
|
|
|
|
{
|
|
|
|
struct omap8250_priv *priv = port->private_data;
|
|
|
|
struct uart_8250_port *up = up_to_u8250p(port);
|
|
|
|
u32 fixed_delay_rts_before_send = 0;
|
|
|
|
u32 fixed_delay_rts_after_send = 0;
|
|
|
|
unsigned int baud;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* There is a fixed delay of 3 bit clock cycles after the TX shift
|
|
|
|
* register is going empty to allow time for the stop bit to transition
|
|
|
|
* through the transceiver before direction is changed to receive.
|
|
|
|
*
|
|
|
|
* Additionally there appears to be a 1 bit clock delay between writing
|
|
|
|
* to the THR register and transmission of the start bit, per page 8783
|
|
|
|
* of the AM65 TRM: https://www.ti.com/lit/ug/spruid7e/spruid7e.pdf
|
|
|
|
*/
|
|
|
|
if (priv->quot) {
|
2022-11-09 07:04:34 +00:00
|
|
|
if (priv->mdr1 == UART_OMAP_MDR1_16X_MODE)
|
2022-10-16 08:02:00 +00:00
|
|
|
baud = port->uartclk / (16 * priv->quot);
|
|
|
|
else
|
|
|
|
baud = port->uartclk / (13 * priv->quot);
|
|
|
|
|
|
|
|
fixed_delay_rts_after_send = 3 * MSEC_PER_SEC / baud;
|
|
|
|
fixed_delay_rts_before_send = 1 * MSEC_PER_SEC / baud;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Fall back to RS485 software emulation if the UART is missing
|
|
|
|
* hardware support, if the device tree specifies an mctrl_gpio
|
|
|
|
* (indicates that RTS is unavailable due to a pinmux conflict)
|
|
|
|
* or if the requested delays exceed the fixed hardware delays.
|
|
|
|
*/
|
|
|
|
if (!(priv->habit & UART_HAS_NATIVE_RS485) ||
|
|
|
|
mctrl_gpio_to_gpiod(up->gpios, UART_GPIO_RTS) ||
|
|
|
|
rs485->delay_rts_after_send > fixed_delay_rts_after_send ||
|
|
|
|
rs485->delay_rts_before_send > fixed_delay_rts_before_send) {
|
|
|
|
priv->mdr3 &= ~UART_OMAP_MDR3_DIR_EN;
|
|
|
|
serial_out(up, UART_OMAP_MDR3, priv->mdr3);
|
|
|
|
|
|
|
|
port->rs485_config = serial8250_em485_config;
|
|
|
|
return serial8250_em485_config(port, termios, rs485);
|
|
|
|
}
|
|
|
|
|
|
|
|
rs485->delay_rts_after_send = fixed_delay_rts_after_send;
|
|
|
|
rs485->delay_rts_before_send = fixed_delay_rts_before_send;
|
|
|
|
|
|
|
|
if (rs485->flags & SER_RS485_ENABLED)
|
|
|
|
priv->mdr3 |= UART_OMAP_MDR3_DIR_EN;
|
|
|
|
else
|
|
|
|
priv->mdr3 &= ~UART_OMAP_MDR3_DIR_EN;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Retain same polarity semantics as RS485 software emulation,
|
|
|
|
* i.e. SER_RS485_RTS_ON_SEND means driving RTS low on send.
|
|
|
|
*/
|
|
|
|
if (rs485->flags & SER_RS485_RTS_ON_SEND)
|
|
|
|
priv->mdr3 &= ~UART_OMAP_MDR3_DIR_POL;
|
|
|
|
else
|
|
|
|
priv->mdr3 |= UART_OMAP_MDR3_DIR_POL;
|
|
|
|
|
|
|
|
serial_out(up, UART_OMAP_MDR3, priv->mdr3);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-09-29 18:06:43 +00:00
|
|
|
#ifdef CONFIG_SERIAL_8250_DMA
|
2016-04-10 05:14:36 +00:00
|
|
|
static int omap_8250_rx_dma(struct uart_8250_port *p);
|
2014-09-29 18:06:44 +00:00
|
|
|
|
2020-03-19 11:03:41 +00:00
|
|
|
/* Must be called while priv->rx_dma_lock is held */
|
2016-04-10 05:14:35 +00:00
|
|
|
static void __dma_rx_do_complete(struct uart_8250_port *p)
|
2014-09-29 18:06:44 +00:00
|
|
|
{
|
|
|
|
struct uart_8250_dma *dma = p->dma;
|
|
|
|
struct tty_port *tty_port = &p->port.state->port;
|
2020-10-29 05:19:30 +00:00
|
|
|
struct omap8250_priv *priv = p->port.private_data;
|
2020-03-19 11:03:40 +00:00
|
|
|
struct dma_chan *rxchan = dma->rxchan;
|
|
|
|
dma_cookie_t cookie;
|
2014-09-29 18:06:44 +00:00
|
|
|
struct dma_tx_state state;
|
|
|
|
int count;
|
2015-08-14 16:01:03 +00:00
|
|
|
int ret;
|
2020-10-29 05:19:30 +00:00
|
|
|
u32 reg;
|
2014-09-29 18:06:44 +00:00
|
|
|
|
2015-04-27 11:52:33 +00:00
|
|
|
if (!dma->rx_running)
|
2020-03-19 11:03:41 +00:00
|
|
|
goto out;
|
2015-04-27 11:52:33 +00:00
|
|
|
|
2020-03-19 11:03:40 +00:00
|
|
|
cookie = dma->rx_cookie;
|
2014-09-29 18:06:44 +00:00
|
|
|
dma->rx_running = 0;
|
2020-10-29 05:19:30 +00:00
|
|
|
|
|
|
|
/* Re-enable RX FIFO interrupt now that transfer is complete */
|
|
|
|
if (priv->habit & UART_HAS_RHR_IT_DIS) {
|
|
|
|
reg = serial_in(p, UART_OMAP_IER2);
|
|
|
|
reg &= ~UART_OMAP_IER2_RHR_IT_DIS;
|
2023-10-31 11:09:09 +00:00
|
|
|
serial_out(p, UART_OMAP_IER2, reg);
|
2020-10-29 05:19:30 +00:00
|
|
|
}
|
|
|
|
|
2020-03-19 11:03:40 +00:00
|
|
|
dmaengine_tx_status(rxchan, cookie, &state);
|
2014-09-29 18:06:44 +00:00
|
|
|
|
2020-03-19 11:03:40 +00:00
|
|
|
count = dma->rx_size - state.residue + state.in_flight_bytes;
|
|
|
|
if (count < dma->rx_size) {
|
|
|
|
dmaengine_terminate_async(rxchan);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Poll for teardown to complete which guarantees in
|
|
|
|
* flight data is drained.
|
|
|
|
*/
|
|
|
|
if (state.in_flight_bytes) {
|
|
|
|
int poll_count = 25;
|
2014-09-29 18:06:44 +00:00
|
|
|
|
2020-03-19 11:03:40 +00:00
|
|
|
while (dmaengine_tx_status(rxchan, cookie, NULL) &&
|
|
|
|
poll_count--)
|
|
|
|
cpu_relax();
|
2014-09-29 18:06:44 +00:00
|
|
|
|
2021-04-29 07:19:22 +00:00
|
|
|
if (poll_count == -1)
|
2020-03-19 11:03:40 +00:00
|
|
|
dev_err(p->port.dev, "teardown incomplete\n");
|
|
|
|
}
|
|
|
|
}
|
2020-03-19 11:03:39 +00:00
|
|
|
if (!count)
|
2020-03-19 11:03:41 +00:00
|
|
|
goto out;
|
2015-08-14 16:01:03 +00:00
|
|
|
ret = tty_insert_flip_string(tty_port, dma->rx_buf, count);
|
|
|
|
|
|
|
|
p->port.icount.rx += ret;
|
|
|
|
p->port.icount.buf_overrun += count - ret;
|
2020-03-19 11:03:41 +00:00
|
|
|
out:
|
2015-04-27 11:52:33 +00:00
|
|
|
|
2014-09-29 18:06:44 +00:00
|
|
|
tty_flip_buffer_push(tty_port);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __dma_rx_complete(void *param)
|
|
|
|
{
|
2017-06-20 05:42:12 +00:00
|
|
|
struct uart_8250_port *p = param;
|
2018-02-08 12:55:42 +00:00
|
|
|
struct omap8250_priv *priv = p->port.private_data;
|
2017-06-20 05:42:12 +00:00
|
|
|
struct uart_8250_dma *dma = p->dma;
|
|
|
|
struct dma_tx_state state;
|
|
|
|
unsigned long flags;
|
|
|
|
|
2023-05-25 09:31:59 +00:00
|
|
|
/* Synchronize UART_IER access against the console. */
|
2023-09-14 18:37:29 +00:00
|
|
|
uart_port_lock_irqsave(&p->port, &flags);
|
2017-06-20 05:42:12 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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) {
|
2023-09-14 18:37:29 +00:00
|
|
|
uart_port_unlock_irqrestore(&p->port, flags);
|
2017-06-20 05:42:12 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
__dma_rx_do_complete(p);
|
2020-03-19 11:03:44 +00:00
|
|
|
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);
|
|
|
|
}
|
2017-06-20 05:42:12 +00:00
|
|
|
|
2023-09-14 18:37:29 +00:00
|
|
|
uart_port_unlock_irqrestore(&p->port, flags);
|
2014-09-29 18:06:44 +00:00
|
|
|
}
|
|
|
|
|
2015-04-27 11:52:33 +00:00
|
|
|
static void omap_8250_rx_dma_flush(struct uart_8250_port *p)
|
|
|
|
{
|
|
|
|
struct omap8250_priv *priv = p->port.private_data;
|
|
|
|
struct uart_8250_dma *dma = p->dma;
|
2017-01-20 08:16:52 +00:00
|
|
|
struct dma_tx_state state;
|
2015-04-27 11:52:33 +00:00
|
|
|
unsigned long flags;
|
2015-08-14 15:52:07 +00:00
|
|
|
int ret;
|
2015-04-27 11:52:33 +00:00
|
|
|
|
|
|
|
spin_lock_irqsave(&priv->rx_dma_lock, flags);
|
|
|
|
|
|
|
|
if (!dma->rx_running) {
|
|
|
|
spin_unlock_irqrestore(&priv->rx_dma_lock, flags);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-01-20 08:16:52 +00:00
|
|
|
ret = dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state);
|
|
|
|
if (ret == DMA_IN_PROGRESS) {
|
|
|
|
ret = dmaengine_pause(dma->rxchan);
|
|
|
|
if (WARN_ON_ONCE(ret))
|
|
|
|
priv->rx_dma_broken = true;
|
|
|
|
}
|
2016-04-10 05:14:35 +00:00
|
|
|
__dma_rx_do_complete(p);
|
2020-03-19 11:03:41 +00:00
|
|
|
spin_unlock_irqrestore(&priv->rx_dma_lock, flags);
|
2015-04-27 11:52:33 +00:00
|
|
|
}
|
|
|
|
|
2016-04-10 05:14:36 +00:00
|
|
|
static int omap_8250_rx_dma(struct uart_8250_port *p)
|
2014-09-29 18:06:44 +00:00
|
|
|
{
|
2015-04-27 11:52:33 +00:00
|
|
|
struct omap8250_priv *priv = p->port.private_data;
|
2014-09-29 18:06:44 +00:00
|
|
|
struct uart_8250_dma *dma = p->dma;
|
2015-04-27 11:52:33 +00:00
|
|
|
int err = 0;
|
2014-09-29 18:06:44 +00:00
|
|
|
struct dma_async_tx_descriptor *desc;
|
2015-04-27 11:52:33 +00:00
|
|
|
unsigned long flags;
|
2020-10-29 05:19:30 +00:00
|
|
|
u32 reg;
|
2014-09-29 18:06:44 +00:00
|
|
|
|
2023-05-25 09:31:56 +00:00
|
|
|
/* Port locked to synchronize UART_IER access against the console. */
|
|
|
|
lockdep_assert_held_once(&p->port.lock);
|
|
|
|
|
2015-08-14 15:52:07 +00:00
|
|
|
if (priv->rx_dma_broken)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2015-04-27 11:52:33 +00:00
|
|
|
spin_lock_irqsave(&priv->rx_dma_lock, flags);
|
|
|
|
|
2020-03-19 11:03:44 +00:00
|
|
|
if (dma->rx_running) {
|
|
|
|
enum dma_status state;
|
|
|
|
|
|
|
|
state = dmaengine_tx_status(dma->rxchan, dma->rx_cookie, NULL);
|
|
|
|
if (state == DMA_COMPLETE) {
|
|
|
|
/*
|
|
|
|
* Disable RX interrupts to allow RX DMA completion
|
|
|
|
* callback to run.
|
|
|
|
*/
|
|
|
|
p->ier &= ~(UART_IER_RLSI | UART_IER_RDI);
|
|
|
|
serial_out(p, UART_IER, p->ier);
|
|
|
|
}
|
2015-04-27 11:52:33 +00:00
|
|
|
goto out;
|
2020-03-19 11:03:44 +00:00
|
|
|
}
|
2014-09-29 18:06:44 +00:00
|
|
|
|
|
|
|
desc = dmaengine_prep_slave_single(dma->rxchan, dma->rx_addr,
|
|
|
|
dma->rx_size, DMA_DEV_TO_MEM,
|
|
|
|
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
2015-04-27 11:52:33 +00:00
|
|
|
if (!desc) {
|
|
|
|
err = -EBUSY;
|
|
|
|
goto out;
|
|
|
|
}
|
2014-09-29 18:06:44 +00:00
|
|
|
|
|
|
|
dma->rx_running = 1;
|
|
|
|
desc->callback = __dma_rx_complete;
|
|
|
|
desc->callback_param = p;
|
|
|
|
|
|
|
|
dma->rx_cookie = dmaengine_submit(desc);
|
|
|
|
|
2020-10-29 05:19:30 +00:00
|
|
|
/*
|
|
|
|
* Disable RX FIFO interrupt while RX DMA is enabled, else
|
|
|
|
* spurious interrupt may be raised when data is in the RX FIFO
|
|
|
|
* but is yet to be drained by DMA.
|
|
|
|
*/
|
|
|
|
if (priv->habit & UART_HAS_RHR_IT_DIS) {
|
|
|
|
reg = serial_in(p, UART_OMAP_IER2);
|
|
|
|
reg |= UART_OMAP_IER2_RHR_IT_DIS;
|
2023-10-31 11:09:09 +00:00
|
|
|
serial_out(p, UART_OMAP_IER2, reg);
|
2020-10-29 05:19:30 +00:00
|
|
|
}
|
|
|
|
|
2014-09-29 18:06:44 +00:00
|
|
|
dma_async_issue_pending(dma->rxchan);
|
2015-04-27 11:52:33 +00:00
|
|
|
out:
|
|
|
|
spin_unlock_irqrestore(&priv->rx_dma_lock, flags);
|
|
|
|
return err;
|
2014-09-29 18:06:44 +00:00
|
|
|
}
|
|
|
|
|
2014-09-29 18:06:43 +00:00
|
|
|
static int omap_8250_tx_dma(struct uart_8250_port *p);
|
|
|
|
|
|
|
|
static void omap_8250_dma_tx_complete(void *param)
|
|
|
|
{
|
|
|
|
struct uart_8250_port *p = param;
|
|
|
|
struct uart_8250_dma *dma = p->dma;
|
|
|
|
struct circ_buf *xmit = &p->port.state->xmit;
|
|
|
|
unsigned long flags;
|
|
|
|
bool en_thri = false;
|
2014-09-29 18:06:49 +00:00
|
|
|
struct omap8250_priv *priv = p->port.private_data;
|
2014-09-29 18:06:43 +00:00
|
|
|
|
|
|
|
dma_sync_single_for_cpu(dma->txchan->device->dev, dma->tx_addr,
|
|
|
|
UART_XMIT_SIZE, DMA_TO_DEVICE);
|
|
|
|
|
2023-09-14 18:37:29 +00:00
|
|
|
uart_port_lock_irqsave(&p->port, &flags);
|
2014-09-29 18:06:43 +00:00
|
|
|
|
|
|
|
dma->tx_running = 0;
|
|
|
|
|
2022-09-09 09:12:58 +00:00
|
|
|
uart_xmit_advance(&p->port, dma->tx_size);
|
2014-09-29 18:06:43 +00:00
|
|
|
|
2014-09-29 18:06:49 +00:00
|
|
|
if (priv->delayed_restore) {
|
|
|
|
priv->delayed_restore = 0;
|
|
|
|
omap8250_restore_regs(p);
|
|
|
|
}
|
|
|
|
|
2014-09-29 18:06:43 +00:00
|
|
|
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
|
|
|
|
uart_write_wakeup(&p->port);
|
|
|
|
|
|
|
|
if (!uart_circ_empty(xmit) && !uart_tx_stopped(&p->port)) {
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = omap_8250_tx_dma(p);
|
|
|
|
if (ret)
|
|
|
|
en_thri = true;
|
|
|
|
} else if (p->capabilities & UART_CAP_RPM) {
|
|
|
|
en_thri = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (en_thri) {
|
|
|
|
dma->tx_err = 1;
|
2019-06-17 13:53:20 +00:00
|
|
|
serial8250_set_THRI(p);
|
2014-09-29 18:06:43 +00:00
|
|
|
}
|
|
|
|
|
2023-09-14 18:37:29 +00:00
|
|
|
uart_port_unlock_irqrestore(&p->port, flags);
|
2014-09-29 18:06:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int omap_8250_tx_dma(struct uart_8250_port *p)
|
|
|
|
{
|
|
|
|
struct uart_8250_dma *dma = p->dma;
|
|
|
|
struct omap8250_priv *priv = p->port.private_data;
|
|
|
|
struct circ_buf *xmit = &p->port.state->xmit;
|
|
|
|
struct dma_async_tx_descriptor *desc;
|
|
|
|
unsigned int skip_byte = 0;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (dma->tx_running)
|
|
|
|
return 0;
|
|
|
|
if (uart_tx_stopped(&p->port) || uart_circ_empty(xmit)) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Even if no data, we need to return an error for the two cases
|
|
|
|
* below so serial8250_tx_chars() is invoked and properly clears
|
|
|
|
* THRI and/or runtime suspend.
|
|
|
|
*/
|
|
|
|
if (dma->tx_err || p->capabilities & UART_CAP_RPM) {
|
|
|
|
ret = -EBUSY;
|
|
|
|
goto err;
|
|
|
|
}
|
2019-06-17 13:53:20 +00:00
|
|
|
serial8250_clear_THRI(p);
|
2014-09-29 18:06:43 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
dma->tx_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
|
|
|
|
if (priv->habit & OMAP_DMA_TX_KICK) {
|
|
|
|
u8 tx_lvl;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We need to put the first byte into the FIFO in order to start
|
|
|
|
* the DMA transfer. For transfers smaller than four bytes we
|
|
|
|
* don't bother doing DMA at all. It seem not matter if there
|
|
|
|
* are still bytes in the FIFO from the last transfer (in case
|
|
|
|
* we got here directly from omap_8250_dma_tx_complete()). Bytes
|
|
|
|
* leaving the FIFO seem not to trigger the DMA transfer. It is
|
|
|
|
* really the byte that we put into the FIFO.
|
|
|
|
* If the FIFO is already full then we most likely got here from
|
|
|
|
* omap_8250_dma_tx_complete(). And this means the DMA engine
|
|
|
|
* just completed its work. We don't have to wait the complete
|
|
|
|
* 86us at 115200,8n1 but around 60us (not to mention lower
|
|
|
|
* baudrates). So in that case we take the interrupt and try
|
|
|
|
* again with an empty FIFO.
|
|
|
|
*/
|
|
|
|
tx_lvl = serial_in(p, UART_OMAP_TX_LVL);
|
|
|
|
if (tx_lvl == p->tx_loadsz) {
|
|
|
|
ret = -EBUSY;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
if (dma->tx_size < 4) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
skip_byte = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
desc = dmaengine_prep_slave_single(dma->txchan,
|
|
|
|
dma->tx_addr + xmit->tail + skip_byte,
|
|
|
|
dma->tx_size - skip_byte, DMA_MEM_TO_DEV,
|
|
|
|
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
|
|
|
if (!desc) {
|
|
|
|
ret = -EBUSY;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
dma->tx_running = 1;
|
|
|
|
|
|
|
|
desc->callback = omap_8250_dma_tx_complete;
|
|
|
|
desc->callback_param = p;
|
|
|
|
|
|
|
|
dma->tx_cookie = dmaengine_submit(desc);
|
|
|
|
|
|
|
|
dma_sync_single_for_device(dma->txchan->device->dev, dma->tx_addr,
|
|
|
|
UART_XMIT_SIZE, DMA_TO_DEVICE);
|
|
|
|
|
|
|
|
dma_async_issue_pending(dma->txchan);
|
|
|
|
if (dma->tx_err)
|
|
|
|
dma->tx_err = 0;
|
|
|
|
|
2019-06-17 13:53:20 +00:00
|
|
|
serial8250_clear_THRI(p);
|
2014-09-29 18:06:43 +00:00
|
|
|
if (skip_byte)
|
|
|
|
serial_out(p, UART_TX, xmit->buf[xmit->tail]);
|
|
|
|
return 0;
|
|
|
|
err:
|
|
|
|
dma->tx_err = 1;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-04-10 05:14:36 +00:00
|
|
|
static bool handle_rx_dma(struct uart_8250_port *up, unsigned int iir)
|
|
|
|
{
|
|
|
|
switch (iir & 0x3f) {
|
|
|
|
case UART_IIR_RLSI:
|
|
|
|
case UART_IIR_RX_TIMEOUT:
|
|
|
|
case UART_IIR_RDI:
|
|
|
|
omap_8250_rx_dma_flush(up);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return omap_8250_rx_dma(up);
|
|
|
|
}
|
|
|
|
|
2022-06-24 20:42:05 +00:00
|
|
|
static u16 omap_8250_handle_rx_dma(struct uart_8250_port *up, u8 iir, u16 status)
|
2020-03-19 11:03:44 +00:00
|
|
|
{
|
|
|
|
if ((status & (UART_LSR_DR | UART_LSR_BI)) &&
|
|
|
|
(iir & UART_IIR_RDI)) {
|
|
|
|
if (handle_rx_dma(up, iir)) {
|
|
|
|
status = serial8250_rx_chars(up, status);
|
|
|
|
omap_8250_rx_dma(up);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void am654_8250_handle_rx_dma(struct uart_8250_port *up, u8 iir,
|
2022-06-24 20:42:05 +00:00
|
|
|
u16 status)
|
2020-03-19 11:03:44 +00:00
|
|
|
{
|
2023-05-25 09:31:59 +00:00
|
|
|
/* Port locked to synchronize UART_IER access against the console. */
|
|
|
|
lockdep_assert_held_once(&up->port.lock);
|
|
|
|
|
2020-03-19 11:03:44 +00:00
|
|
|
/*
|
|
|
|
* Queue a new transfer if FIFO has data.
|
|
|
|
*/
|
|
|
|
if ((status & (UART_LSR_DR | UART_LSR_BI)) &&
|
|
|
|
(up->ier & UART_IER_RDI)) {
|
|
|
|
omap_8250_rx_dma(up);
|
|
|
|
serial_out(up, UART_OMAP_EFR2, UART_OMAP_EFR2_TIMEOUT_BEHAVE);
|
|
|
|
} else if ((iir & 0x3f) == UART_IIR_RX_TIMEOUT) {
|
|
|
|
/*
|
|
|
|
* Disable RX timeout, read IIR to clear
|
|
|
|
* current timeout condition, clear EFR2 to
|
|
|
|
* periodic timeouts, re-enable interrupts.
|
|
|
|
*/
|
|
|
|
up->ier &= ~(UART_IER_RLSI | UART_IER_RDI);
|
|
|
|
serial_out(up, UART_IER, up->ier);
|
|
|
|
omap_8250_rx_dma_flush(up);
|
|
|
|
serial_in(up, UART_IIR);
|
|
|
|
serial_out(up, UART_OMAP_EFR2, 0x0);
|
|
|
|
up->ier |= UART_IER_RLSI | UART_IER_RDI;
|
|
|
|
serial_out(up, UART_IER, up->ier);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-29 18:06:48 +00:00
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
* 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);
|
2020-03-19 11:03:44 +00:00
|
|
|
struct omap8250_priv *priv = up->port.private_data;
|
2022-06-24 20:42:05 +00:00
|
|
|
u16 status;
|
2014-09-29 18:06:48 +00:00
|
|
|
u8 iir;
|
|
|
|
|
|
|
|
iir = serial_port_in(port, UART_IIR);
|
|
|
|
if (iir & UART_IIR_NO_INT) {
|
2020-03-19 11:03:43 +00:00
|
|
|
return IRQ_HANDLED;
|
2014-09-29 18:06:48 +00:00
|
|
|
}
|
|
|
|
|
2023-09-14 18:37:29 +00:00
|
|
|
uart_port_lock(port);
|
2014-09-29 18:06:48 +00:00
|
|
|
|
|
|
|
status = serial_port_in(port, UART_LSR);
|
|
|
|
|
2023-11-01 17:14:31 +00:00
|
|
|
if ((iir & 0x3f) != UART_IIR_THRI) {
|
|
|
|
if (priv->habit & UART_HAS_EFR2)
|
|
|
|
am654_8250_handle_rx_dma(up, iir, status);
|
|
|
|
else
|
|
|
|
status = omap_8250_handle_rx_dma(up, iir, status);
|
|
|
|
}
|
2020-03-19 11:03:44 +00:00
|
|
|
|
2014-09-29 18:06:48 +00:00
|
|
|
serial8250_modem_status(up);
|
|
|
|
if (status & UART_LSR_THRE && up->dma->tx_err) {
|
|
|
|
if (uart_tx_stopped(&up->port) ||
|
|
|
|
uart_circ_empty(&up->port.state->xmit)) {
|
|
|
|
up->dma->tx_err = 0;
|
|
|
|
serial8250_tx_chars(up);
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* try again due to an earlier failer which
|
|
|
|
* might have been resolved by now.
|
|
|
|
*/
|
2016-04-10 03:49:41 +00:00
|
|
|
if (omap_8250_tx_dma(up))
|
2014-09-29 18:06:48 +00:00
|
|
|
serial8250_tx_chars(up);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-16 14:05:55 +00:00
|
|
|
uart_unlock_and_check_sysrq(port);
|
|
|
|
|
2014-09-29 18:06:48 +00:00
|
|
|
return 1;
|
|
|
|
}
|
2014-09-29 18:06:49 +00:00
|
|
|
|
|
|
|
static bool the_no_dma_filter_fn(struct dma_chan *chan, void *param)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
2016-04-10 05:14:36 +00:00
|
|
|
static inline int omap_8250_rx_dma(struct uart_8250_port *p)
|
2014-09-29 18:06:49 +00:00
|
|
|
{
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2014-09-29 18:06:43 +00:00
|
|
|
#endif
|
|
|
|
|
2015-05-20 20:07:35 +00:00
|
|
|
static int omap8250_no_handle_irq(struct uart_port *port)
|
|
|
|
{
|
|
|
|
/* IRQ has not been requested but handling irq? */
|
|
|
|
WARN_ONCE(1, "Unexpected irq handling before port startup\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-03-19 11:03:44 +00:00
|
|
|
static struct omap8250_dma_params am654_dma = {
|
|
|
|
.rx_size = SZ_2K,
|
|
|
|
.rx_trigger = 1,
|
|
|
|
.tx_trigger = TX_TRIGGER,
|
|
|
|
};
|
|
|
|
|
2020-03-19 11:03:42 +00:00
|
|
|
static struct omap8250_dma_params am33xx_dma = {
|
|
|
|
.rx_size = RX_TRIGGER,
|
|
|
|
.rx_trigger = RX_TRIGGER,
|
|
|
|
.tx_trigger = TX_TRIGGER,
|
|
|
|
};
|
|
|
|
|
2020-03-19 11:03:44 +00:00
|
|
|
static struct omap8250_platdata am654_platdata = {
|
|
|
|
.dma_params = &am654_dma,
|
2021-06-22 14:57:04 +00:00
|
|
|
.habit = UART_HAS_EFR2 | UART_HAS_RHR_IT_DIS |
|
2022-10-16 08:02:00 +00:00
|
|
|
UART_RX_TIMEOUT_QUIRK | UART_HAS_NATIVE_RS485,
|
2020-03-19 11:03:44 +00:00
|
|
|
};
|
|
|
|
|
2020-03-19 11:03:42 +00:00
|
|
|
static struct omap8250_platdata am33xx_platdata = {
|
|
|
|
.dma_params = &am33xx_dma,
|
|
|
|
.habit = OMAP_DMA_TX_KICK | UART_ERRATA_CLOCK_DISABLE,
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct omap8250_platdata omap4_platdata = {
|
|
|
|
.dma_params = &am33xx_dma,
|
|
|
|
.habit = UART_ERRATA_CLOCK_DISABLE,
|
|
|
|
};
|
2015-07-14 08:02:06 +00:00
|
|
|
|
|
|
|
static const struct of_device_id omap8250_dt_ids[] = {
|
2020-03-19 11:03:44 +00:00
|
|
|
{ .compatible = "ti,am654-uart", .data = &am654_platdata, },
|
2015-07-14 08:02:06 +00:00
|
|
|
{ .compatible = "ti,omap2-uart" },
|
|
|
|
{ .compatible = "ti,omap3-uart" },
|
2020-03-19 11:03:42 +00:00
|
|
|
{ .compatible = "ti,omap4-uart", .data = &omap4_platdata, },
|
|
|
|
{ .compatible = "ti,am3352-uart", .data = &am33xx_platdata, },
|
|
|
|
{ .compatible = "ti,am4372-uart", .data = &am33xx_platdata, },
|
|
|
|
{ .compatible = "ti,dra742-uart", .data = &omap4_platdata, },
|
2015-07-14 08:02:06 +00:00
|
|
|
{},
|
|
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(of, omap8250_dt_ids);
|
|
|
|
|
2014-09-29 18:06:39 +00:00
|
|
|
static int omap8250_probe(struct platform_device *pdev)
|
|
|
|
{
|
2019-01-09 09:12:04 +00:00
|
|
|
struct device_node *np = pdev->dev.of_node;
|
2014-09-29 18:06:39 +00:00
|
|
|
struct omap8250_priv *priv;
|
2020-03-19 11:03:42 +00:00
|
|
|
const struct omap8250_platdata *pdata;
|
2014-09-29 18:06:39 +00:00
|
|
|
struct uart_8250_port up;
|
2020-06-18 12:20:24 +00:00
|
|
|
struct resource *regs;
|
2014-09-29 18:06:39 +00:00
|
|
|
void __iomem *membase;
|
2024-03-04 12:27:12 +00:00
|
|
|
int ret;
|
2014-09-29 18:06:39 +00:00
|
|
|
|
2020-06-18 12:20:24 +00:00
|
|
|
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
|
|
if (!regs) {
|
|
|
|
dev_err(&pdev->dev, "missing registers\n");
|
2014-09-29 18:06:39 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
|
|
|
if (!priv)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2020-01-06 08:43:50 +00:00
|
|
|
membase = devm_ioremap(&pdev->dev, regs->start,
|
2014-09-29 18:06:39 +00:00
|
|
|
resource_size(regs));
|
|
|
|
if (!membase)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
memset(&up, 0, sizeof(up));
|
|
|
|
up.port.dev = &pdev->dev;
|
|
|
|
up.port.mapbase = regs->start;
|
|
|
|
up.port.membase = membase;
|
|
|
|
/*
|
|
|
|
* It claims to be 16C750 compatible however it is a little different.
|
|
|
|
* It has EFR and has no FCR7_64byte bit. The AFE (which it claims to
|
|
|
|
* have) is enabled via EFR instead of MCR. The type is set here 8250
|
|
|
|
* just to get things going. UNKNOWN does not work for a few reasons and
|
|
|
|
* we don't need our own type since we don't use 8250's set_termios()
|
|
|
|
* or pm callback.
|
|
|
|
*/
|
|
|
|
up.port.type = PORT_8250;
|
2024-03-04 12:27:12 +00:00
|
|
|
up.port.flags = UPF_FIXED_PORT | UPF_FIXED_TYPE | UPF_SOFT_FLOW | UPF_HARD_FLOW;
|
2014-09-29 18:06:39 +00:00
|
|
|
up.port.private_data = priv;
|
|
|
|
|
|
|
|
up.tx_loadsz = 64;
|
|
|
|
up.capabilities = UART_CAP_FIFO;
|
2014-12-19 14:27:58 +00:00
|
|
|
#ifdef CONFIG_PM
|
2014-09-29 18:06:39 +00:00
|
|
|
/*
|
2014-12-19 14:27:58 +00:00
|
|
|
* Runtime PM is mostly transparent. However to do it right we need to a
|
2014-09-29 18:06:39 +00:00
|
|
|
* TX empty interrupt before we can put the device to auto idle. So if
|
2014-12-19 14:27:58 +00:00
|
|
|
* PM is not enabled we don't add that flag and can spare that one extra
|
|
|
|
* interrupt in the TX path.
|
2014-09-29 18:06:39 +00:00
|
|
|
*/
|
|
|
|
up.capabilities |= UART_CAP_RPM;
|
|
|
|
#endif
|
|
|
|
up.port.set_termios = omap_8250_set_termios;
|
2014-12-31 01:28:15 +00:00
|
|
|
up.port.set_mctrl = omap8250_set_mctrl;
|
2014-09-29 18:06:39 +00:00
|
|
|
up.port.pm = omap_8250_pm;
|
|
|
|
up.port.startup = omap_8250_startup;
|
|
|
|
up.port.shutdown = omap_8250_shutdown;
|
|
|
|
up.port.throttle = omap_8250_throttle;
|
|
|
|
up.port.unthrottle = omap_8250_unthrottle;
|
2022-10-16 08:02:00 +00:00
|
|
|
up.port.rs485_config = omap8250_rs485_config;
|
|
|
|
/* same rs485_supported for software emulation and native RS485 */
|
2022-09-16 11:09:55 +00:00
|
|
|
up.port.rs485_supported = serial8250_em485_supported;
|
2020-02-28 13:31:06 +00:00
|
|
|
up.rs485_start_tx = serial8250_em485_start_tx;
|
|
|
|
up.rs485_stop_tx = serial8250_em485_stop_tx;
|
2019-12-13 00:06:06 +00:00
|
|
|
up.port.has_sysrq = IS_ENABLED(CONFIG_SERIAL_8250_CONSOLE);
|
2014-09-29 18:06:39 +00:00
|
|
|
|
2024-03-04 12:27:12 +00:00
|
|
|
ret = uart_read_port_properties(&up.port);
|
|
|
|
if (ret)
|
2014-11-12 09:28:33 +00:00
|
|
|
return ret;
|
|
|
|
|
2024-03-04 12:27:12 +00:00
|
|
|
up.port.regshift = OMAP_UART_REGSHIFT;
|
|
|
|
up.port.fifosize = 64;
|
|
|
|
|
|
|
|
if (!up.port.uartclk) {
|
2019-01-09 09:12:06 +00:00
|
|
|
struct clk *clk;
|
|
|
|
|
|
|
|
clk = devm_clk_get(&pdev->dev, NULL);
|
|
|
|
if (IS_ERR(clk)) {
|
|
|
|
if (PTR_ERR(clk) == -EPROBE_DEFER)
|
|
|
|
return -EPROBE_DEFER;
|
|
|
|
} else {
|
|
|
|
up.port.uartclk = clk_get_rate(clk);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-27 10:35:33 +00:00
|
|
|
if (of_property_read_u32(np, "overrun-throttle-ms",
|
|
|
|
&up.overrun_backoff_time_ms) != 0)
|
|
|
|
up.overrun_backoff_time_ms = 0;
|
|
|
|
|
2020-03-19 11:03:42 +00:00
|
|
|
pdata = of_device_get_match_data(&pdev->dev);
|
|
|
|
if (pdata)
|
|
|
|
priv->habit |= pdata->habit;
|
2019-01-09 09:12:04 +00:00
|
|
|
|
2014-09-29 18:06:39 +00:00
|
|
|
if (!up.port.uartclk) {
|
|
|
|
up.port.uartclk = DEFAULT_CLK_SPEED;
|
|
|
|
dev_warn(&pdev->dev,
|
|
|
|
"No clock speed specified: using default: %d\n",
|
|
|
|
DEFAULT_CLK_SPEED);
|
|
|
|
}
|
|
|
|
|
2023-05-08 08:20:12 +00:00
|
|
|
priv->membase = membase;
|
|
|
|
priv->line = -ENODEV;
|
2020-02-11 23:04:31 +00:00
|
|
|
priv->latency = PM_QOS_CPU_LATENCY_DEFAULT_VALUE;
|
|
|
|
priv->calc_latency = PM_QOS_CPU_LATENCY_DEFAULT_VALUE;
|
2020-02-11 23:27:04 +00:00
|
|
|
cpu_latency_qos_add_request(&priv->pm_qos_request, priv->latency);
|
2014-09-29 18:06:39 +00:00
|
|
|
INIT_WORK(&priv->qos_work, omap8250_uart_qos_work);
|
|
|
|
|
2015-04-27 11:52:33 +00:00
|
|
|
spin_lock_init(&priv->rx_dma_lock);
|
|
|
|
|
2023-05-08 08:20:12 +00:00
|
|
|
platform_set_drvdata(pdev, priv);
|
|
|
|
|
2014-09-29 18:06:39 +00:00
|
|
|
device_init_wakeup(&pdev->dev, true);
|
2020-03-20 12:52:00 +00:00
|
|
|
pm_runtime_enable(&pdev->dev);
|
2014-09-29 18:06:39 +00:00
|
|
|
pm_runtime_use_autosuspend(&pdev->dev);
|
2019-07-23 11:54:00 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Disable runtime PM until autosuspend delay unless specifically
|
|
|
|
* enabled by the user via sysfs. This is the historic way to
|
|
|
|
* prevent an unsafe default policy with lossy characters on wake-up.
|
|
|
|
* For serdev devices this is not needed, the policy can be managed by
|
|
|
|
* the serdev driver.
|
|
|
|
*/
|
|
|
|
if (!of_get_available_child_count(pdev->dev.of_node))
|
|
|
|
pm_runtime_set_autosuspend_delay(&pdev->dev, -1);
|
2014-09-29 18:06:39 +00:00
|
|
|
|
|
|
|
pm_runtime_get_sync(&pdev->dev);
|
|
|
|
|
|
|
|
omap_serial_fill_features_erratas(&up, priv);
|
2015-05-20 20:07:35 +00:00
|
|
|
up.port.handle_irq = omap8250_no_handle_irq;
|
2020-03-19 11:03:42 +00:00
|
|
|
priv->rx_trigger = RX_TRIGGER;
|
|
|
|
priv->tx_trigger = TX_TRIGGER;
|
2014-09-29 18:06:49 +00:00
|
|
|
#ifdef CONFIG_SERIAL_8250_DMA
|
2019-01-09 09:12:04 +00:00
|
|
|
/*
|
|
|
|
* Oh DMA support. If there are no DMA properties in the DT then
|
|
|
|
* we will fall back to a generic DMA channel which does not
|
|
|
|
* really work here. To ensure that we do not get a generic DMA
|
|
|
|
* channel assigned, we have the the_no_dma_filter_fn() here.
|
|
|
|
* To avoid "failed to request DMA" messages we check for DMA
|
|
|
|
* properties in DT.
|
|
|
|
*/
|
|
|
|
ret = of_property_count_strings(np, "dma-names");
|
|
|
|
if (ret == 2) {
|
2020-03-19 11:03:42 +00:00
|
|
|
struct omap8250_dma_params *dma_params = NULL;
|
2023-05-08 08:20:14 +00:00
|
|
|
struct uart_8250_dma *dma = &priv->omap8250_dma;
|
2020-03-19 11:03:42 +00:00
|
|
|
|
2023-05-08 08:20:14 +00:00
|
|
|
dma->fn = the_no_dma_filter_fn;
|
|
|
|
dma->tx_dma = omap_8250_tx_dma;
|
|
|
|
dma->rx_dma = omap_8250_rx_dma;
|
2020-03-19 11:03:42 +00:00
|
|
|
if (pdata)
|
|
|
|
dma_params = pdata->dma_params;
|
|
|
|
|
|
|
|
if (dma_params) {
|
2023-05-08 08:20:14 +00:00
|
|
|
dma->rx_size = dma_params->rx_size;
|
|
|
|
dma->rxconf.src_maxburst = dma_params->rx_trigger;
|
|
|
|
dma->txconf.dst_maxburst = dma_params->tx_trigger;
|
2020-03-19 11:03:42 +00:00
|
|
|
priv->rx_trigger = dma_params->rx_trigger;
|
|
|
|
priv->tx_trigger = dma_params->tx_trigger;
|
|
|
|
} else {
|
2023-05-08 08:20:14 +00:00
|
|
|
dma->rx_size = RX_TRIGGER;
|
|
|
|
dma->rxconf.src_maxburst = RX_TRIGGER;
|
|
|
|
dma->txconf.dst_maxburst = TX_TRIGGER;
|
2020-03-19 11:03:42 +00:00
|
|
|
}
|
2014-09-29 18:06:49 +00:00
|
|
|
}
|
|
|
|
#endif
|
2023-05-08 08:20:13 +00:00
|
|
|
|
2024-03-04 12:27:12 +00:00
|
|
|
irq_set_status_flags(up.port.irq, IRQ_NOAUTOEN);
|
|
|
|
ret = devm_request_irq(&pdev->dev, up.port.irq, omap8250_irq, 0,
|
2023-05-08 08:20:13 +00:00
|
|
|
dev_name(&pdev->dev), priv);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
priv->wakeirq = irq_of_parse_and_map(np, 1);
|
|
|
|
|
2014-09-29 18:06:39 +00:00
|
|
|
ret = serial8250_register_8250_port(&up);
|
|
|
|
if (ret < 0) {
|
|
|
|
dev_err(&pdev->dev, "unable to register 8250 port\n");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
priv->line = ret;
|
|
|
|
pm_runtime_mark_last_busy(&pdev->dev);
|
|
|
|
pm_runtime_put_autosuspend(&pdev->dev);
|
|
|
|
return 0;
|
|
|
|
err:
|
2017-01-20 20:22:31 +00:00
|
|
|
pm_runtime_dont_use_autosuspend(&pdev->dev);
|
|
|
|
pm_runtime_put_sync(&pdev->dev);
|
2023-05-08 08:20:11 +00:00
|
|
|
flush_work(&priv->qos_work);
|
2014-09-29 18:06:39 +00:00
|
|
|
pm_runtime_disable(&pdev->dev);
|
2023-05-08 08:20:11 +00:00
|
|
|
cpu_latency_qos_remove_request(&priv->pm_qos_request);
|
2014-09-29 18:06:39 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2023-11-10 15:29:31 +00:00
|
|
|
static void omap8250_remove(struct platform_device *pdev)
|
2014-09-29 18:06:39 +00:00
|
|
|
{
|
|
|
|
struct omap8250_priv *priv = platform_get_drvdata(pdev);
|
2023-05-08 08:20:14 +00:00
|
|
|
struct uart_8250_port *up;
|
2022-10-28 10:58:13 +00:00
|
|
|
int err;
|
|
|
|
|
|
|
|
err = pm_runtime_resume_and_get(&pdev->dev);
|
|
|
|
if (err)
|
2023-11-10 15:29:29 +00:00
|
|
|
dev_err(&pdev->dev, "Failed to resume hardware\n");
|
2014-09-29 18:06:39 +00:00
|
|
|
|
2023-05-08 08:20:14 +00:00
|
|
|
up = serial8250_get_port(priv->line);
|
|
|
|
omap_8250_shutdown(&up->port);
|
2023-05-08 08:20:12 +00:00
|
|
|
serial8250_unregister_port(priv->line);
|
|
|
|
priv->line = -ENODEV;
|
2017-01-20 20:22:31 +00:00
|
|
|
pm_runtime_dont_use_autosuspend(&pdev->dev);
|
2014-09-29 18:06:39 +00:00
|
|
|
pm_runtime_put_sync(&pdev->dev);
|
2022-10-28 11:00:44 +00:00
|
|
|
flush_work(&priv->qos_work);
|
2014-09-29 18:06:39 +00:00
|
|
|
pm_runtime_disable(&pdev->dev);
|
2020-02-11 23:27:04 +00:00
|
|
|
cpu_latency_qos_remove_request(&priv->pm_qos_request);
|
2014-09-29 18:06:39 +00:00
|
|
|
device_init_wakeup(&pdev->dev, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int omap8250_prepare(struct device *dev)
|
|
|
|
{
|
|
|
|
struct omap8250_priv *priv = dev_get_drvdata(dev);
|
|
|
|
|
|
|
|
if (!priv)
|
|
|
|
return 0;
|
|
|
|
priv->is_suspending = true;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void omap8250_complete(struct device *dev)
|
|
|
|
{
|
|
|
|
struct omap8250_priv *priv = dev_get_drvdata(dev);
|
|
|
|
|
|
|
|
if (!priv)
|
|
|
|
return;
|
|
|
|
priv->is_suspending = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int omap8250_suspend(struct device *dev)
|
|
|
|
{
|
|
|
|
struct omap8250_priv *priv = dev_get_drvdata(dev);
|
2018-04-02 12:20:37 +00:00
|
|
|
struct uart_8250_port *up = serial8250_get_port(priv->line);
|
2023-10-17 13:05:40 +00:00
|
|
|
struct generic_pm_domain *genpd = pd_to_genpd(dev->pm_domain);
|
2023-09-26 06:13:17 +00:00
|
|
|
int err = 0;
|
2014-09-29 18:06:39 +00:00
|
|
|
|
|
|
|
serial8250_suspend_port(priv->line);
|
2018-04-02 12:20:37 +00:00
|
|
|
|
2023-06-14 04:59:19 +00:00
|
|
|
err = pm_runtime_resume_and_get(dev);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2018-04-02 12:20:37 +00:00
|
|
|
if (!device_may_wakeup(dev))
|
|
|
|
priv->wer = 0;
|
|
|
|
serial_out(up, UART_OMAP_WER, priv->wer);
|
2023-10-17 13:05:40 +00:00
|
|
|
if (uart_console(&up->port)) {
|
|
|
|
if (console_suspend_enabled)
|
|
|
|
err = pm_runtime_force_suspend(dev);
|
|
|
|
else {
|
|
|
|
/*
|
|
|
|
* The pd shall not be powered-off (no console suspend).
|
|
|
|
* Make copy of genpd flags before to set it always on.
|
|
|
|
* The original value is restored during the resume.
|
|
|
|
*/
|
|
|
|
genpd_flags_console = genpd->flags;
|
|
|
|
genpd->flags |= GENPD_FLAG_ALWAYS_ON;
|
|
|
|
}
|
|
|
|
}
|
2014-09-29 18:06:39 +00:00
|
|
|
flush_work(&priv->qos_work);
|
2023-06-14 04:59:19 +00:00
|
|
|
|
|
|
|
return err;
|
2014-09-29 18:06:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int omap8250_resume(struct device *dev)
|
|
|
|
{
|
|
|
|
struct omap8250_priv *priv = dev_get_drvdata(dev);
|
2023-09-26 06:13:17 +00:00
|
|
|
struct uart_8250_port *up = serial8250_get_port(priv->line);
|
2023-10-17 13:05:40 +00:00
|
|
|
struct generic_pm_domain *genpd = pd_to_genpd(dev->pm_domain);
|
2023-06-14 04:59:19 +00:00
|
|
|
int err;
|
2014-09-29 18:06:39 +00:00
|
|
|
|
2023-09-26 06:13:17 +00:00
|
|
|
if (uart_console(&up->port) && console_suspend_enabled) {
|
2023-10-17 13:05:40 +00:00
|
|
|
if (console_suspend_enabled) {
|
|
|
|
err = pm_runtime_force_resume(dev);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
} else
|
|
|
|
genpd->flags = genpd_flags_console;
|
2023-09-26 06:13:17 +00:00
|
|
|
}
|
|
|
|
|
2014-09-29 18:06:39 +00:00
|
|
|
serial8250_resume_port(priv->line);
|
2023-06-14 04:59:19 +00:00
|
|
|
/* Paired with pm_runtime_resume_and_get() in omap8250_suspend() */
|
|
|
|
pm_runtime_mark_last_busy(dev);
|
|
|
|
pm_runtime_put_autosuspend(dev);
|
|
|
|
|
2014-09-29 18:06:39 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int omap8250_lost_context(struct uart_8250_port *up)
|
|
|
|
{
|
|
|
|
u32 val;
|
|
|
|
|
2015-07-14 08:02:07 +00:00
|
|
|
val = serial_in(up, UART_OMAP_SCR);
|
2014-09-29 18:06:39 +00:00
|
|
|
/*
|
2015-07-14 08:02:07 +00:00
|
|
|
* If we lose context, then SCR is set to its reset value of zero.
|
|
|
|
* After set_termios() we set bit 3 of SCR (TX_EMPTY_CTL_IT) to 1,
|
|
|
|
* among other bits, to never set the register back to zero again.
|
2014-09-29 18:06:39 +00:00
|
|
|
*/
|
2015-07-14 08:02:07 +00:00
|
|
|
if (!val)
|
2014-09-29 18:06:39 +00:00
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-05-15 06:57:06 +00:00
|
|
|
static void uart_write(struct omap8250_priv *priv, u32 reg, u32 val)
|
|
|
|
{
|
|
|
|
writel(val, priv->membase + (reg << OMAP_UART_REGSHIFT));
|
|
|
|
}
|
|
|
|
|
2015-07-14 08:02:07 +00:00
|
|
|
/* TODO: in future, this should happen via API in drivers/reset/ */
|
|
|
|
static int omap8250_soft_reset(struct device *dev)
|
|
|
|
{
|
|
|
|
struct omap8250_priv *priv = dev_get_drvdata(dev);
|
|
|
|
int timeout = 100;
|
|
|
|
int sysc;
|
|
|
|
int syss;
|
|
|
|
|
2018-05-04 17:44:09 +00:00
|
|
|
/*
|
|
|
|
* At least on omap4, unused uarts may not idle after reset without
|
|
|
|
* a basic scr dma configuration even with no dma in use. The
|
|
|
|
* module clkctrl status bits will be 1 instead of 3 blocking idle
|
|
|
|
* for the whole clockdomain. The softreset below will clear scr,
|
|
|
|
* and we restore it on resume so this is safe to do on all SoCs
|
|
|
|
* needing omap8250_soft_reset() quirk. Do it in two writes as
|
|
|
|
* recommended in the comment for omap8250_update_scr().
|
|
|
|
*/
|
2023-05-08 08:20:12 +00:00
|
|
|
uart_write(priv, UART_OMAP_SCR, OMAP_UART_SCR_DMAMODE_1);
|
|
|
|
uart_write(priv, UART_OMAP_SCR,
|
2018-05-04 17:44:09 +00:00
|
|
|
OMAP_UART_SCR_DMAMODE_1 | OMAP_UART_SCR_DMAMODE_CTL);
|
|
|
|
|
2023-05-08 08:20:12 +00:00
|
|
|
sysc = uart_read(priv, UART_OMAP_SYSC);
|
2015-07-14 08:02:07 +00:00
|
|
|
|
|
|
|
/* softreset the UART */
|
|
|
|
sysc |= OMAP_UART_SYSC_SOFTRESET;
|
2023-05-08 08:20:12 +00:00
|
|
|
uart_write(priv, UART_OMAP_SYSC, sysc);
|
2015-07-14 08:02:07 +00:00
|
|
|
|
|
|
|
/* By experiments, 1us enough for reset complete on AM335x */
|
|
|
|
do {
|
|
|
|
udelay(1);
|
2023-05-08 08:20:12 +00:00
|
|
|
syss = uart_read(priv, UART_OMAP_SYSS);
|
2015-07-14 08:02:07 +00:00
|
|
|
} while (--timeout && !(syss & OMAP_UART_SYSS_RESETDONE));
|
|
|
|
|
|
|
|
if (!timeout) {
|
|
|
|
dev_err(dev, "timed out waiting for reset done\n");
|
|
|
|
return -ETIMEDOUT;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-09-29 18:06:39 +00:00
|
|
|
static int omap8250_runtime_suspend(struct device *dev)
|
|
|
|
{
|
|
|
|
struct omap8250_priv *priv = dev_get_drvdata(dev);
|
2023-05-08 08:20:12 +00:00
|
|
|
struct uart_8250_port *up = NULL;
|
2017-01-20 20:22:31 +00:00
|
|
|
|
2023-05-08 08:20:12 +00:00
|
|
|
if (priv->line >= 0)
|
|
|
|
up = serial8250_get_port(priv->line);
|
2014-09-29 18:06:39 +00:00
|
|
|
|
2015-07-14 08:02:07 +00:00
|
|
|
if (priv->habit & UART_ERRATA_CLOCK_DISABLE) {
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = omap8250_soft_reset(dev);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2023-05-08 08:20:12 +00:00
|
|
|
if (up) {
|
|
|
|
/* Restore to UART mode after reset (for wakeup) */
|
|
|
|
omap8250_update_mdr1(up, priv);
|
|
|
|
/* Restore wakeup enable register */
|
|
|
|
serial_out(up, UART_OMAP_WER, priv->wer);
|
|
|
|
}
|
2015-07-14 08:02:07 +00:00
|
|
|
}
|
|
|
|
|
2023-05-08 08:20:12 +00:00
|
|
|
if (up && up->dma && up->dma->rxchan)
|
2016-04-10 05:14:36 +00:00
|
|
|
omap_8250_rx_dma_flush(up);
|
2014-09-29 18:06:39 +00:00
|
|
|
|
2020-02-11 23:04:31 +00:00
|
|
|
priv->latency = PM_QOS_CPU_LATENCY_DEFAULT_VALUE;
|
2014-09-29 18:06:39 +00:00
|
|
|
schedule_work(&priv->qos_work);
|
2023-10-04 06:26:48 +00:00
|
|
|
atomic_set(&priv->active, 0);
|
2014-09-29 18:06:39 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int omap8250_runtime_resume(struct device *dev)
|
|
|
|
{
|
|
|
|
struct omap8250_priv *priv = dev_get_drvdata(dev);
|
2023-05-08 08:20:12 +00:00
|
|
|
struct uart_8250_port *up = NULL;
|
2014-09-29 18:06:39 +00:00
|
|
|
|
2023-10-04 06:26:48 +00:00
|
|
|
/* Did the hardware wake to a device IO interrupt before a wakeirq? */
|
|
|
|
if (atomic_read(&priv->active))
|
|
|
|
return 0;
|
|
|
|
|
2023-05-08 08:20:12 +00:00
|
|
|
if (priv->line >= 0)
|
|
|
|
up = serial8250_get_port(priv->line);
|
2014-09-29 18:06:39 +00:00
|
|
|
|
2023-05-25 09:31:57 +00:00
|
|
|
if (up && omap8250_lost_context(up)) {
|
2023-09-14 18:37:29 +00:00
|
|
|
uart_port_lock_irq(&up->port);
|
2014-09-29 18:06:39 +00:00
|
|
|
omap8250_restore_regs(up);
|
2023-09-14 18:37:29 +00:00
|
|
|
uart_port_unlock_irq(&up->port);
|
2023-05-25 09:31:57 +00:00
|
|
|
}
|
2014-09-29 18:06:39 +00:00
|
|
|
|
2023-05-25 09:31:56 +00:00
|
|
|
if (up && up->dma && up->dma->rxchan && !(priv->habit & UART_HAS_EFR2)) {
|
2023-09-14 18:37:29 +00:00
|
|
|
uart_port_lock_irq(&up->port);
|
2016-04-10 05:14:36 +00:00
|
|
|
omap_8250_rx_dma(up);
|
2023-09-14 18:37:29 +00:00
|
|
|
uart_port_unlock_irq(&up->port);
|
2023-05-25 09:31:56 +00:00
|
|
|
}
|
2014-09-29 18:06:49 +00:00
|
|
|
|
2023-10-04 06:26:48 +00:00
|
|
|
atomic_set(&priv->active, 1);
|
2014-09-29 18:06:39 +00:00
|
|
|
priv->latency = priv->calc_latency;
|
|
|
|
schedule_work(&priv->qos_work);
|
2023-10-04 06:26:48 +00:00
|
|
|
|
2014-09-29 18:06:39 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-12-18 17:47:12 +00:00
|
|
|
#ifdef CONFIG_SERIAL_8250_OMAP_TTYO_FIXUP
|
|
|
|
static int __init omap8250_console_fixup(void)
|
|
|
|
{
|
|
|
|
char *omap_str;
|
|
|
|
char *options;
|
|
|
|
u8 idx;
|
|
|
|
|
|
|
|
if (strstr(boot_command_line, "console=ttyS"))
|
|
|
|
/* user set a ttyS based name for the console */
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
omap_str = strstr(boot_command_line, "console=ttyO");
|
|
|
|
if (!omap_str)
|
|
|
|
/* user did not set ttyO based console, so we don't care */
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
omap_str += 12;
|
|
|
|
if ('0' <= *omap_str && *omap_str <= '9')
|
|
|
|
idx = *omap_str - '0';
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
omap_str++;
|
|
|
|
if (omap_str[0] == ',') {
|
|
|
|
omap_str++;
|
|
|
|
options = omap_str;
|
|
|
|
} else {
|
|
|
|
options = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
add_preferred_console("ttyS", idx, options);
|
|
|
|
pr_err("WARNING: Your 'console=ttyO%d' has been replaced by 'ttyS%d'\n",
|
|
|
|
idx, idx);
|
|
|
|
pr_err("This ensures that you still see kernel messages. Please\n");
|
|
|
|
pr_err("update your kernel commandline.\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
console_initcall(omap8250_console_fixup);
|
|
|
|
#endif
|
|
|
|
|
2014-09-29 18:06:39 +00:00
|
|
|
static const struct dev_pm_ops omap8250_dev_pm_ops = {
|
2023-05-17 20:20:07 +00:00
|
|
|
SYSTEM_SLEEP_PM_OPS(omap8250_suspend, omap8250_resume)
|
|
|
|
RUNTIME_PM_OPS(omap8250_runtime_suspend,
|
2014-09-29 18:06:39 +00:00
|
|
|
omap8250_runtime_resume, NULL)
|
2023-05-17 20:20:07 +00:00
|
|
|
.prepare = pm_sleep_ptr(omap8250_prepare),
|
|
|
|
.complete = pm_sleep_ptr(omap8250_complete),
|
2014-09-29 18:06:39 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct platform_driver omap8250_platform_driver = {
|
|
|
|
.driver = {
|
|
|
|
.name = "omap8250",
|
2023-05-17 20:20:07 +00:00
|
|
|
.pm = pm_ptr(&omap8250_dev_pm_ops),
|
2014-09-29 18:06:39 +00:00
|
|
|
.of_match_table = omap8250_dt_ids,
|
|
|
|
},
|
|
|
|
.probe = omap8250_probe,
|
2023-11-10 15:29:31 +00:00
|
|
|
.remove_new = omap8250_remove,
|
2014-09-29 18:06:39 +00:00
|
|
|
};
|
|
|
|
module_platform_driver(omap8250_platform_driver);
|
|
|
|
|
|
|
|
MODULE_AUTHOR("Sebastian Andrzej Siewior");
|
|
|
|
MODULE_DESCRIPTION("OMAP 8250 Driver");
|
|
|
|
MODULE_LICENSE("GPL v2");
|