TTY/Serial driver updates for 6.5-rc1.

Here is the big set of tty/serial driver updates for 6.5-rc1.
 
 Included in here are:
   - tty_audit code cleanups from Jiri
   - more 8250 cleanups from Ilpo
   - samsung_tty driver bugfixes
   - 8250 lock port updates
   - usual fsl_lpuart driver updates and fixes
   - other small serial driver fixes and updates, full details in the
     shortlog.
 
 All of these have been in linux-next for a while with no reported
 issues.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 
 iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCZKKVTQ8cZ3JlZ0Brcm9h
 aC5jb20ACgkQMUfUDdst+yl+wACgwWX8fdrkBHASPVkcYOn8xa27E08AnjNz2Y8K
 vvOII6EEYKwFjEkjAoIX
 =By18
 -----END PGP SIGNATURE-----

Merge tag 'tty-6.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty

Pull tty/serial driver updates from Greg KH:
 "Here is the big set of tty/serial driver updates for 6.5-rc1.

  Included in here are:

   - tty_audit code cleanups from Jiri

   - more 8250 cleanups from Ilpo

   - samsung_tty driver bugfixes

   - 8250 lock port updates

   - usual fsl_lpuart driver updates and fixes

   - other small serial driver fixes and updates, full details in the
     shortlog

  All of these have been in linux-next for a while with no reported
  issues"

* tag 'tty-6.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: (58 commits)
  tty_audit: make data of tty_audit_log() const
  tty_audit: make tty pointers in exposed functions const
  tty_audit: make icanon a bool
  tty_audit: invert the condition in tty_audit_log()
  tty_audit: use kzalloc() in tty_audit_buf_alloc()
  tty_audit: use TASK_COMM_LEN for task comm
  Revert "8250: add support for ASIX devices with a FIFO bug"
  serial: atmel: don't enable IRQs prematurely
  tty: serial: Add Nuvoton ma35d1 serial driver support
  tty: serial: fsl_lpuart: add earlycon for imx8ulp platform
  tty: serial: imx: fix rs485 rx after tx
  selftests: tty: add selftest for tty timestamp updates
  tty: tty_io: update timestamps on all device nodes
  tty: fix hang on tty device with no_room set
  serial: core: fix -EPROBE_DEFER handling in init
  serial: 8250_omap: Use force_suspend and resume for system suspend
  tty: serial: samsung_tty: Use abs() to simplify some code
  tty: serial: samsung_tty: Fix a memory leak in s3c24xx_serial_getclk() when iterating clk
  tty: serial: samsung_tty: Fix a memory leak in s3c24xx_serial_getclk() in case of error
  serial: 8250: Apply FSL workarounds also without SERIAL_8250_CONSOLE
  ...
This commit is contained in:
Linus Torvalds 2023-07-03 13:14:58 -07:00
commit 868a9fd948
52 changed files with 2279 additions and 424 deletions

View File

@ -51,9 +51,9 @@ static void alchemy_8250_pm(struct uart_port *port, unsigned int state,
#define PORT(_base, _irq) \
{ \
.mapbase = _base, \
.mapsize = 0x1000, \
.irq = _irq, \
.regshift = 2, \
.iotype = UPIO_AU, \
.flags = UPF_SKIP_TEST | UPF_IOREMAP | \
UPF_FIXED_TYPE, \
.type = PORT_16550A, \
@ -124,8 +124,14 @@ static void __init alchemy_setup_uarts(int ctype)
au1xx0_uart_device.dev.platform_data = ports;
/* Fill up uartclk. */
for (s = 0; s < c; s++)
for (s = 0; s < c; s++) {
ports[s].uartclk = uartclk;
if (au_platform_setup(&ports[s]) < 0) {
kfree(ports);
printk(KERN_INFO "Alchemy: missing support for UARTs\n");
return;
}
}
if (platform_device_register(&au1xx0_uart_device))
printk(KERN_INFO "Alchemy: failed to register UARTs\n");
}

View File

@ -508,12 +508,16 @@ static void __init fixup_port_irq(int index,
port->irq = virq;
#ifdef CONFIG_SERIAL_8250_FSL
if (of_device_is_compatible(np, "fsl,ns16550")) {
port->handle_irq = fsl8250_handle_irq;
port->has_sysrq = IS_ENABLED(CONFIG_SERIAL_8250_CONSOLE);
if (IS_ENABLED(CONFIG_SERIAL_8250) &&
of_device_is_compatible(np, "fsl,ns16550")) {
if (IS_REACHABLE(CONFIG_SERIAL_8250_FSL)) {
port->handle_irq = fsl8250_handle_irq;
port->has_sysrq = IS_ENABLED(CONFIG_SERIAL_8250_CONSOLE);
} else {
pr_warn_once("Not activating Freescale specific workaround for device %pOFP\n",
np);
}
}
#endif
}
static void __init fixup_port_pio(int index,

View File

@ -203,8 +203,8 @@ static void n_tty_kick_worker(struct tty_struct *tty)
struct n_tty_data *ldata = tty->disc_data;
/* Did the input worker stop? Restart it */
if (unlikely(ldata->no_room)) {
ldata->no_room = 0;
if (unlikely(READ_ONCE(ldata->no_room))) {
WRITE_ONCE(ldata->no_room, 0);
WARN_RATELIMIT(tty->port->itty == NULL,
"scheduling with invalid itty\n");
@ -1697,7 +1697,7 @@ n_tty_receive_buf_common(struct tty_struct *tty, const unsigned char *cp,
if (overflow && room < 0)
ldata->read_head--;
room = overflow;
ldata->no_room = flow && !room;
WRITE_ONCE(ldata->no_room, flow && !room);
} else
overflow = 0;
@ -1728,6 +1728,17 @@ n_tty_receive_buf_common(struct tty_struct *tty, const unsigned char *cp,
} else
n_tty_check_throttle(tty);
if (unlikely(ldata->no_room)) {
/*
* Barrier here is to ensure to read the latest read_tail in
* chars_in_buffer() and to make sure that read_tail is not loaded
* before ldata->no_room is set.
*/
smp_mb();
if (!chars_in_buffer(tty))
n_tty_kick_worker(tty);
}
up_read(&tty->termios_rwsem);
return rcvd;
@ -2281,8 +2292,14 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
if (time)
timeout = time;
}
if (old_tail != ldata->read_tail)
if (old_tail != ldata->read_tail) {
/*
* Make sure no_room is not read in n_tty_kick_worker()
* before setting ldata->read_tail in copy_from_read_buf().
*/
smp_mb();
n_tty_kick_worker(tty);
}
up_read(&tty->termios_rwsem);
remove_wait_queue(&tty->read_wait, &wait);

View File

@ -91,7 +91,6 @@ struct serial8250_config {
#define UART_BUG_TXEN BIT(1) /* UART has buggy TX IIR status */
#define UART_BUG_NOMSR BIT(2) /* UART has buggy MSR status bits (Au1x00) */
#define UART_BUG_THRE BIT(3) /* UART has buggy THRE reassertion */
#define UART_BUG_PARITY BIT(4) /* UART mishandles parity if FIFO enabled */
#define UART_BUG_TXRACE BIT(5) /* UART Tx fails to set remote DR */
@ -167,18 +166,21 @@ static unsigned int __maybe_unused serial_icr_read(struct uart_8250_port *up,
void serial8250_clear_and_reinit_fifos(struct uart_8250_port *p);
static inline int serial_dl_read(struct uart_8250_port *up)
static inline u32 serial_dl_read(struct uart_8250_port *up)
{
return up->dl_read(up);
}
static inline void serial_dl_write(struct uart_8250_port *up, int value)
static inline void serial_dl_write(struct uart_8250_port *up, u32 value)
{
up->dl_write(up, value);
}
static inline bool serial8250_set_THRI(struct uart_8250_port *up)
{
/* Port locked to synchronize UART_IER access against the console. */
lockdep_assert_held_once(&up->port.lock);
if (up->ier & UART_IER_THRI)
return false;
up->ier |= UART_IER_THRI;
@ -188,6 +190,9 @@ static inline bool serial8250_set_THRI(struct uart_8250_port *up)
static inline bool serial8250_clear_THRI(struct uart_8250_port *up)
{
/* Port locked to synchronize UART_IER access against the console. */
lockdep_assert_held_once(&up->port.lock);
if (!(up->ier & UART_IER_THRI))
return false;
up->ier &= ~UART_IER_THRI;

View File

@ -275,6 +275,9 @@ static void __aspeed_vuart_set_throttle(struct uart_8250_port *up,
{
unsigned char irqs = UART_IER_RLSI | UART_IER_RDI;
/* Port locked to synchronize UART_IER access against the console. */
lockdep_assert_held_once(&up->port.lock);
up->ier &= ~irqs;
if (!throttle)
up->ier |= irqs;

View File

@ -605,9 +605,13 @@ static int brcmuart_startup(struct uart_port *port)
/*
* Disable the Receive Data Interrupt because the DMA engine
* will handle this.
*
* Synchronize UART_IER access against the console.
*/
spin_lock_irq(&port->lock);
up->ier &= ~UART_IER_RDI;
serial_port_out(port, UART_IER, up->ier);
spin_unlock_irq(&port->lock);
priv->tx_running = false;
priv->dma.rx_dma = NULL;

View File

@ -488,6 +488,34 @@ static inline void serial8250_apply_quirks(struct uart_8250_port *up)
up->port.quirks |= skip_txen_test ? UPQ_NO_TXEN_TEST : 0;
}
static struct uart_8250_port *serial8250_setup_port(int index)
{
struct uart_8250_port *up;
if (index >= UART_NR)
return NULL;
up = &serial8250_ports[index];
up->port.line = index;
serial8250_init_port(up);
if (!base_ops)
base_ops = up->port.ops;
up->port.ops = &univ8250_port_ops;
timer_setup(&up->timer, serial8250_timeout, 0);
up->ops = &univ8250_driver_ops;
if (IS_ENABLED(CONFIG_ALPHA_JENSEN) ||
(IS_ENABLED(CONFIG_ALPHA_GENERIC) && alpha_jensen()))
up->port.set_mctrl = alpha_jensen_set_mctrl;
serial8250_set_defaults(up);
return up;
}
static void __init serial8250_isa_init_ports(void)
{
struct uart_8250_port *up;
@ -501,26 +529,13 @@ static void __init serial8250_isa_init_ports(void)
if (nr_uarts > UART_NR)
nr_uarts = UART_NR;
for (i = 0; i < nr_uarts; i++) {
struct uart_8250_port *up = &serial8250_ports[i];
struct uart_port *port = &up->port;
port->line = i;
serial8250_init_port(up);
if (!base_ops)
base_ops = port->ops;
port->ops = &univ8250_port_ops;
timer_setup(&up->timer, serial8250_timeout, 0);
up->ops = &univ8250_driver_ops;
if (IS_ENABLED(CONFIG_ALPHA_JENSEN) ||
(IS_ENABLED(CONFIG_ALPHA_GENERIC) && alpha_jensen()))
port->set_mctrl = alpha_jensen_set_mctrl;
serial8250_set_defaults(up);
}
/*
* Set up initial isa ports based on nr_uart module param, or else
* default to CONFIG_SERIAL_8250_RUNTIME_UARTS. Note that we do not
* need to increase nr_uarts when setting up the initial isa ports.
*/
for (i = 0; i < nr_uarts; i++)
serial8250_setup_port(i);
/* chain base port ops to support Remote Supervisor Adapter */
univ8250_port_ops = *base_ops;
@ -586,16 +601,29 @@ static void univ8250_console_write(struct console *co, const char *s,
static int univ8250_console_setup(struct console *co, char *options)
{
struct uart_8250_port *up;
struct uart_port *port;
int retval;
int retval, i;
/*
* Check whether an invalid uart number has been specified, and
* if so, search for the first available port that does have
* console support.
*/
if (co->index >= nr_uarts)
if (co->index >= UART_NR)
co->index = 0;
/*
* If the console is past the initial isa ports, init more ports up to
* co->index as needed and increment nr_uarts accordingly.
*/
for (i = nr_uarts; i <= co->index; i++) {
up = serial8250_setup_port(i);
if (!up)
return -ENODEV;
nr_uarts++;
}
port = &serial8250_ports[co->index].port;
/* link port to console */
port->cons = co;
@ -822,12 +850,16 @@ static int serial8250_probe(struct platform_device *dev)
uart.port.iotype = p->iotype;
uart.port.flags = p->flags;
uart.port.mapbase = p->mapbase;
uart.port.mapsize = p->mapsize;
uart.port.hub6 = p->hub6;
uart.port.has_sysrq = p->has_sysrq;
uart.port.private_data = p->private_data;
uart.port.type = p->type;
uart.bugs = p->bugs;
uart.port.serial_in = p->serial_in;
uart.port.serial_out = p->serial_out;
uart.dl_read = p->dl_read;
uart.dl_write = p->dl_write;
uart.port.handle_irq = p->handle_irq;
uart.port.handle_break = p->handle_break;
uart.port.set_termios = p->set_termios;
@ -990,12 +1022,24 @@ int serial8250_register_8250_port(const struct uart_8250_port *up)
mutex_lock(&serial_mutex);
uart = serial8250_find_match_or_unused(&up->port);
if (uart && uart->port.type != PORT_8250_CIR) {
if (!uart) {
/*
* If the port is past the initial isa ports, initialize a new
* port and increment nr_uarts accordingly.
*/
uart = serial8250_setup_port(nr_uarts);
if (!uart)
goto unlock;
nr_uarts++;
}
if (uart->port.type != PORT_8250_CIR) {
struct mctrl_gpios *gpios;
if (uart->port.dev)
uart_remove_one_port(&serial8250_reg, &uart->port);
uart->port.ctrl_id = up->port.ctrl_id;
uart->port.iobase = up->port.iobase;
uart->port.membase = up->port.membase;
uart->port.irq = up->port.irq;
@ -1120,6 +1164,7 @@ int serial8250_register_8250_port(const struct uart_8250_port *up)
}
}
unlock:
mutex_unlock(&serial_mutex);
return ret;

View File

@ -36,7 +36,6 @@
static unsigned int serial8250_early_in(struct uart_port *port, int offset)
{
int reg_offset = offset;
offset <<= port->regshift;
switch (port->iotype) {
@ -50,8 +49,6 @@ static unsigned int serial8250_early_in(struct uart_port *port, int offset)
return ioread32be(port->membase + offset);
case UPIO_PORT:
return inb(port->iobase + offset);
case UPIO_AU:
return port->serial_in(port, reg_offset);
default:
return 0;
}
@ -59,7 +56,6 @@ static unsigned int serial8250_early_in(struct uart_port *port, int offset)
static void serial8250_early_out(struct uart_port *port, int offset, int value)
{
int reg_offset = offset;
offset <<= port->regshift;
switch (port->iotype) {
@ -78,9 +74,6 @@ static void serial8250_early_out(struct uart_port *port, int offset, int value)
case UPIO_PORT:
outb(value, port->iobase + offset);
break;
case UPIO_AU:
port->serial_out(port, reg_offset, value);
break;
}
}
@ -199,17 +192,3 @@ OF_EARLYCON_DECLARE(omap8250, "ti,omap3-uart", early_omap8250_setup);
OF_EARLYCON_DECLARE(omap8250, "ti,omap4-uart", early_omap8250_setup);
#endif
#ifdef CONFIG_SERIAL_8250_RT288X
static int __init early_au_setup(struct earlycon_device *dev, const char *opt)
{
dev->port.serial_in = au_serial_in;
dev->port.serial_out = au_serial_out;
dev->port.iotype = UPIO_AU;
dev->con->write = early_serial8250_write;
return 0;
}
OF_EARLYCON_DECLARE(palmchip, "ralink,rt2880-uart", early_au_setup);
#endif

View File

@ -139,12 +139,12 @@ static void serial8250_em_serial_out(struct uart_port *p, int offset, int value)
}
}
static int serial8250_em_serial_dl_read(struct uart_8250_port *up)
static u32 serial8250_em_serial_dl_read(struct uart_8250_port *up)
{
return serial_in(up, UART_DLL_EM) | serial_in(up, UART_DLM_EM) << 8;
}
static void serial8250_em_serial_dl_write(struct uart_8250_port *up, int value)
static void serial8250_em_serial_dl_write(struct uart_8250_port *up, u32 value)
{
serial_out(up, UART_DLL_EM, value & 0xff);
serial_out(up, UART_DLM_EM, value >> 8 & 0xff);

View File

@ -198,8 +198,12 @@ static int xr17v35x_startup(struct uart_port *port)
/*
* Make sure all interrups are masked until initialization is
* complete and the FIFOs are cleared
*
* Synchronize UART_IER access against the console.
*/
spin_lock_irq(&port->lock);
serial_port_out(port, UART_IER, 0);
spin_unlock_irq(&port->lock);
return serial8250_do_startup(port);
}

View File

@ -38,7 +38,19 @@ int fsl8250_handle_irq(struct uart_port *port)
return 0;
}
/* This is the WAR; if last event was BRK, then read and return */
/*
* For a single break the hardware reports LSR.BI for each character
* time. This is described in the MPC8313E chip errata as "General17".
* A typical break has a duration of 0.3s, with a 115200n8 configuration
* that (theoretically) corresponds to ~3500 interrupts in these 0.3s.
* In practise it's less (around 500) because of hardware
* and software latencies. The workaround recommended by the vendor is
* to read the RX register (to clear LSR.DR and thus prevent a FIFO
* aging interrupt). To prevent the irq from retriggering LSR must not be
* read. (This would clear LSR.BI, hardware would reassert the BI event
* immediately and interrupt the CPU again. The hardware clears LSR.BI
* when the next valid char is read.)
*/
if (unlikely(up->lsr_saved_flags & UART_LSR_BI)) {
up->lsr_saved_flags &= ~UART_LSR_BI;
port->serial_in(port, UART_RX);
@ -172,3 +184,6 @@ static struct platform_driver fsl8250_platform_driver = {
module_platform_driver(fsl8250_platform_driver);
#endif
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Handling of Freescale specific 8250 variants");

View File

@ -222,11 +222,17 @@ static void mtk8250_shutdown(struct uart_port *port)
static void mtk8250_disable_intrs(struct uart_8250_port *up, int mask)
{
/* Port locked to synchronize UART_IER access against the console. */
lockdep_assert_held_once(&up->port.lock);
serial_out(up, UART_IER, serial_in(up, UART_IER) & (~mask));
}
static void mtk8250_enable_intrs(struct uart_8250_port *up, int mask)
{
/* Port locked to synchronize UART_IER access against the console. */
lockdep_assert_held_once(&up->port.lock);
serial_out(up, UART_IER, serial_in(up, UART_IER) | mask);
}
@ -235,6 +241,9 @@ static void mtk8250_set_flow_ctrl(struct uart_8250_port *up, int mode)
struct uart_port *port = &up->port;
int lcr = serial_in(up, UART_LCR);
/* Port locked to synchronize UART_IER access against the console. */
lockdep_assert_held_once(&port->lock);
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
serial_out(up, MTK_UART_EFR, UART_EFR_ECB);
serial_out(up, UART_LCR, lcr);
@ -422,12 +431,7 @@ static int __maybe_unused mtk8250_runtime_suspend(struct device *dev)
while
(serial_in(up, MTK_UART_DEBUG0));
if (data->clk_count == 0U) {
dev_dbg(dev, "%s clock count is 0\n", __func__);
} else {
clk_disable_unprepare(data->bus_clk);
data->clk_count--;
}
clk_disable_unprepare(data->bus_clk);
return 0;
}
@ -435,19 +439,8 @@ static int __maybe_unused mtk8250_runtime_suspend(struct device *dev)
static int __maybe_unused mtk8250_runtime_resume(struct device *dev)
{
struct mtk8250_data *data = dev_get_drvdata(dev);
int err;
if (data->clk_count > 0U) {
dev_dbg(dev, "%s clock count is %d\n", __func__,
data->clk_count);
} else {
err = clk_prepare_enable(data->bus_clk);
if (err) {
dev_warn(dev, "Can't enable bus clock\n");
return err;
}
data->clk_count++;
}
clk_prepare_enable(data->bus_clk);
return 0;
}
@ -456,14 +449,12 @@ static void
mtk8250_do_pm(struct uart_port *port, unsigned int state, unsigned int old)
{
if (!state)
if (!mtk8250_runtime_resume(port->dev))
pm_runtime_get_sync(port->dev);
pm_runtime_get_sync(port->dev);
serial8250_do_pm(port, state, old);
if (state)
if (!pm_runtime_put_sync_suspend(port->dev))
mtk8250_runtime_suspend(port->dev);
pm_runtime_put_sync_suspend(port->dev);
}
#ifdef CONFIG_SERIAL_8250_DMA
@ -495,7 +486,7 @@ static int mtk8250_probe_of(struct platform_device *pdev, struct uart_port *p,
return 0;
}
data->bus_clk = devm_clk_get(&pdev->dev, "bus");
data->bus_clk = devm_clk_get_enabled(&pdev->dev, "bus");
if (IS_ERR(data->bus_clk))
return PTR_ERR(data->bus_clk);
@ -578,25 +569,16 @@ static int mtk8250_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, data);
pm_runtime_enable(&pdev->dev);
err = mtk8250_runtime_resume(&pdev->dev);
if (err)
goto err_pm_disable;
data->line = serial8250_register_8250_port(&uart);
if (data->line < 0) {
err = data->line;
goto err_pm_disable;
}
if (data->line < 0)
return data->line;
data->rx_wakeup_irq = platform_get_irq_optional(pdev, 1);
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
return 0;
err_pm_disable:
pm_runtime_disable(&pdev->dev);
return err;
}
static int mtk8250_remove(struct platform_device *pdev)
@ -610,9 +592,6 @@ static int mtk8250_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
pm_runtime_put_noidle(&pdev->dev);
if (!pm_runtime_status_suspended(&pdev->dev))
mtk8250_runtime_suspend(&pdev->dev);
return 0;
}

View File

@ -171,11 +171,13 @@ static int of_platform_serial_setup(struct platform_device *ofdev,
switch (type) {
case PORT_RT2880:
port->iotype = UPIO_AU;
ret = rt288x_setup(port);
if (ret)
goto err_unprepare;
break;
}
if (IS_ENABLED(CONFIG_SERIAL_8250_FSL) &&
if (IS_REACHABLE(CONFIG_SERIAL_8250_FSL) &&
(of_device_is_compatible(np, "fsl,ns16550") ||
of_device_is_compatible(np, "fsl,16550-FIFO64"))) {
port->handle_irq = fsl8250_handle_irq;

View File

@ -32,6 +32,7 @@
#include "8250.h"
#define DEFAULT_CLK_SPEED 48000000
#define OMAP_UART_REGSHIFT 2
#define UART_ERRATA_i202_MDR1_ACCESS (1 << 0)
#define OMAP_UART_WER_HAS_TX_WAKEUP (1 << 1)
@ -115,6 +116,7 @@
#define UART_OMAP_RX_LVL 0x19
struct omap8250_priv {
void __iomem *membase;
int line;
u8 habit;
u8 mdr1;
@ -159,9 +161,9 @@ static void omap_8250_rx_dma_flush(struct uart_8250_port *p);
static inline void omap_8250_rx_dma_flush(struct uart_8250_port *p) { }
#endif
static u32 uart_read(struct uart_8250_port *up, u32 reg)
static u32 uart_read(struct omap8250_priv *priv, u32 reg)
{
return readl(up->port.membase + (reg << up->port.regshift));
return readl(priv->membase + (reg << OMAP_UART_REGSHIFT));
}
/*
@ -302,6 +304,9 @@ static void omap8250_restore_regs(struct uart_8250_port *up)
struct uart_8250_dma *dma = up->dma;
u8 mcr = serial8250_in_MCR(up);
/* Port locked to synchronize UART_IER access against the console. */
lockdep_assert_held_once(&up->port.lock);
if (dma && dma->tx_running) {
/*
* TCSANOW requests the change to occur immediately however if
@ -523,6 +528,10 @@ static void omap_8250_pm(struct uart_port *port, unsigned int state,
u8 efr;
pm_runtime_get_sync(port->dev);
/* Synchronize UART_IER access against the console. */
spin_lock_irq(&port->lock);
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
efr = serial_in(up, UART_EFR);
serial_out(up, UART_EFR, efr | UART_EFR_ECB);
@ -533,6 +542,8 @@ static void omap_8250_pm(struct uart_port *port, unsigned int state,
serial_out(up, UART_EFR, efr);
serial_out(up, UART_LCR, 0);
spin_unlock_irq(&port->lock);
pm_runtime_mark_last_busy(port->dev);
pm_runtime_put_autosuspend(port->dev);
}
@ -548,7 +559,7 @@ static void omap_serial_fill_features_erratas(struct uart_8250_port *up,
u32 mvr, scheme;
u16 revision, major, minor;
mvr = uart_read(up, UART_OMAP_MVER);
mvr = uart_read(priv, UART_OMAP_MVER);
/* Check revision register scheme */
scheme = mvr >> OMAP_UART_MVR_SCHEME_SHIFT;
@ -616,9 +627,9 @@ static int omap_8250_dma_handle_irq(struct uart_port *port);
static irqreturn_t omap8250_irq(int irq, void *dev_id)
{
struct uart_port *port = dev_id;
struct omap8250_priv *priv = port->private_data;
struct uart_8250_port *up = up_to_u8250p(port);
struct omap8250_priv *priv = dev_id;
struct uart_8250_port *up = serial8250_get_port(priv->line);
struct uart_port *port = &up->port;
unsigned int iir, lsr;
int ret;
@ -649,6 +660,8 @@ static irqreturn_t omap8250_irq(int irq, void *dev_id)
if ((lsr & UART_LSR_OE) && up->overrun_backoff_time_ms > 0) {
unsigned long delay;
/* Synchronize UART_IER access against the console. */
spin_lock(&port->lock);
up->ier = port->serial_in(port, UART_IER);
if (up->ier & (UART_IER_RLSI | UART_IER_RDI)) {
port->ops->stop_rx(port);
@ -658,6 +671,7 @@ static irqreturn_t omap8250_irq(int irq, void *dev_id)
*/
cancel_delayed_work(&up->overrun_backoff);
}
spin_unlock(&port->lock);
delay = msecs_to_jiffies(up->overrun_backoff_time_ms);
schedule_delayed_work(&up->overrun_backoff, delay);
@ -672,6 +686,7 @@ static int omap_8250_startup(struct uart_port *port)
{
struct uart_8250_port *up = up_to_u8250p(port);
struct omap8250_priv *priv = port->private_data;
struct uart_8250_dma *dma = &priv->omap8250_dma;
int ret;
if (priv->wakeirq) {
@ -690,25 +705,23 @@ static int omap_8250_startup(struct uart_port *port)
up->msr_saved_flags = 0;
/* Disable DMA for console UART */
if (uart_console(port))
up->dma = NULL;
if (up->dma) {
if (dma->fn && !uart_console(port)) {
up->dma = &priv->omap8250_dma;
ret = serial8250_request_dma(up);
if (ret) {
dev_warn_ratelimited(port->dev,
"failed to request DMA\n");
up->dma = NULL;
}
} else {
up->dma = NULL;
}
ret = request_irq(port->irq, omap8250_irq, IRQF_SHARED,
dev_name(port->dev), port);
if (ret < 0)
goto err;
/* Synchronize UART_IER access against the console. */
spin_lock_irq(&port->lock);
up->ier = UART_IER_RLSI | UART_IER_RDI;
serial_out(up, UART_IER, up->ier);
spin_unlock_irq(&port->lock);
#ifdef CONFIG_PM
up->capabilities |= UART_CAP_RPM;
@ -720,17 +733,17 @@ static int omap_8250_startup(struct uart_port *port)
priv->wer |= OMAP_UART_TX_WAKEUP_EN;
serial_out(up, UART_OMAP_WER, priv->wer);
if (up->dma && !(priv->habit & UART_HAS_EFR2))
if (up->dma && !(priv->habit & UART_HAS_EFR2)) {
spin_lock_irq(&port->lock);
up->dma->rx_dma(up);
spin_unlock_irq(&port->lock);
}
enable_irq(up->port.irq);
pm_runtime_mark_last_busy(port->dev);
pm_runtime_put_autosuspend(port->dev);
return 0;
err:
pm_runtime_mark_last_busy(port->dev);
pm_runtime_put_autosuspend(port->dev);
dev_pm_clear_wake_irq(port->dev);
return ret;
}
static void omap_8250_shutdown(struct uart_port *port)
@ -748,11 +761,16 @@ static void omap_8250_shutdown(struct uart_port *port)
if (priv->habit & UART_HAS_EFR2)
serial_out(up, UART_OMAP_EFR2, 0x0);
/* Synchronize UART_IER access against the console. */
spin_lock_irq(&port->lock);
up->ier = 0;
serial_out(up, UART_IER, 0);
spin_unlock_irq(&port->lock);
disable_irq_nosync(up->port.irq);
dev_pm_clear_wake_irq(port->dev);
if (up->dma)
serial8250_release_dma(up);
serial8250_release_dma(up);
up->dma = NULL;
/*
* Disable break condition and FIFOs
@ -763,8 +781,6 @@ static void omap_8250_shutdown(struct uart_port *port)
pm_runtime_mark_last_busy(port->dev);
pm_runtime_put_autosuspend(port->dev);
free_irq(port->irq, port);
dev_pm_clear_wake_irq(port->dev);
}
static void omap_8250_throttle(struct uart_port *port)
@ -791,6 +807,7 @@ static void omap_8250_unthrottle(struct uart_port *port)
pm_runtime_get_sync(port->dev);
/* Synchronize UART_IER access against the console. */
spin_lock_irqsave(&port->lock, flags);
priv->throttled = false;
if (up->dma)
@ -941,6 +958,7 @@ static void __dma_rx_complete(void *param)
struct dma_tx_state state;
unsigned long flags;
/* Synchronize UART_IER access against the console. */
spin_lock_irqsave(&p->port.lock, flags);
/*
@ -998,6 +1016,9 @@ static int omap_8250_rx_dma(struct uart_8250_port *p)
unsigned long flags;
u32 reg;
/* Port locked to synchronize UART_IER access against the console. */
lockdep_assert_held_once(&p->port.lock);
if (priv->rx_dma_broken)
return -EINVAL;
@ -1212,6 +1233,9 @@ static u16 omap_8250_handle_rx_dma(struct uart_8250_port *up, u8 iir, u16 status
static void am654_8250_handle_rx_dma(struct uart_8250_port *up, u8 iir,
u16 status)
{
/* Port locked to synchronize UART_IER access against the console. */
lockdep_assert_held_once(&up->port.lock);
/*
* Queue a new transfer if FIFO has data.
*/
@ -1394,7 +1418,7 @@ static int omap8250_probe(struct platform_device *pdev)
UPF_HARD_FLOW;
up.port.private_data = priv;
up.port.regshift = 2;
up.port.regshift = OMAP_UART_REGSHIFT;
up.port.fifosize = 64;
up.tx_loadsz = 64;
up.capabilities = UART_CAP_FIFO;
@ -1444,8 +1468,6 @@ static int omap8250_probe(struct platform_device *pdev)
&up.overrun_backoff_time_ms) != 0)
up.overrun_backoff_time_ms = 0;
priv->wakeirq = irq_of_parse_and_map(np, 1);
pdata = of_device_get_match_data(&pdev->dev);
if (pdata)
priv->habit |= pdata->habit;
@ -1457,6 +1479,8 @@ static int omap8250_probe(struct platform_device *pdev)
DEFAULT_CLK_SPEED);
}
priv->membase = membase;
priv->line = -ENODEV;
priv->latency = PM_QOS_CPU_LATENCY_DEFAULT_VALUE;
priv->calc_latency = PM_QOS_CPU_LATENCY_DEFAULT_VALUE;
cpu_latency_qos_add_request(&priv->pm_qos_request, priv->latency);
@ -1464,6 +1488,8 @@ static int omap8250_probe(struct platform_device *pdev)
spin_lock_init(&priv->rx_dma_lock);
platform_set_drvdata(pdev, priv);
device_init_wakeup(&pdev->dev, true);
pm_runtime_enable(&pdev->dev);
pm_runtime_use_autosuspend(&pdev->dev);
@ -1498,64 +1524,77 @@ static int omap8250_probe(struct platform_device *pdev)
ret = of_property_count_strings(np, "dma-names");
if (ret == 2) {
struct omap8250_dma_params *dma_params = NULL;
struct uart_8250_dma *dma = &priv->omap8250_dma;
up.dma = &priv->omap8250_dma;
up.dma->fn = the_no_dma_filter_fn;
up.dma->tx_dma = omap_8250_tx_dma;
up.dma->rx_dma = omap_8250_rx_dma;
dma->fn = the_no_dma_filter_fn;
dma->tx_dma = omap_8250_tx_dma;
dma->rx_dma = omap_8250_rx_dma;
if (pdata)
dma_params = pdata->dma_params;
if (dma_params) {
up.dma->rx_size = dma_params->rx_size;
up.dma->rxconf.src_maxburst = dma_params->rx_trigger;
up.dma->txconf.dst_maxburst = dma_params->tx_trigger;
dma->rx_size = dma_params->rx_size;
dma->rxconf.src_maxburst = dma_params->rx_trigger;
dma->txconf.dst_maxburst = dma_params->tx_trigger;
priv->rx_trigger = dma_params->rx_trigger;
priv->tx_trigger = dma_params->tx_trigger;
} else {
up.dma->rx_size = RX_TRIGGER;
up.dma->rxconf.src_maxburst = RX_TRIGGER;
up.dma->txconf.dst_maxburst = TX_TRIGGER;
dma->rx_size = RX_TRIGGER;
dma->rxconf.src_maxburst = RX_TRIGGER;
dma->txconf.dst_maxburst = TX_TRIGGER;
}
}
#endif
irq_set_status_flags(irq, IRQ_NOAUTOEN);
ret = devm_request_irq(&pdev->dev, irq, omap8250_irq, 0,
dev_name(&pdev->dev), priv);
if (ret < 0)
return ret;
priv->wakeirq = irq_of_parse_and_map(np, 1);
ret = serial8250_register_8250_port(&up);
if (ret < 0) {
dev_err(&pdev->dev, "unable to register 8250 port\n");
goto err;
}
priv->line = ret;
platform_set_drvdata(pdev, priv);
pm_runtime_mark_last_busy(&pdev->dev);
pm_runtime_put_autosuspend(&pdev->dev);
return 0;
err:
pm_runtime_dont_use_autosuspend(&pdev->dev);
pm_runtime_put_sync(&pdev->dev);
flush_work(&priv->qos_work);
pm_runtime_disable(&pdev->dev);
cpu_latency_qos_remove_request(&priv->pm_qos_request);
return ret;
}
static int omap8250_remove(struct platform_device *pdev)
{
struct omap8250_priv *priv = platform_get_drvdata(pdev);
struct uart_8250_port *up;
int err;
err = pm_runtime_resume_and_get(&pdev->dev);
if (err)
return err;
up = serial8250_get_port(priv->line);
omap_8250_shutdown(&up->port);
serial8250_unregister_port(priv->line);
priv->line = -ENODEV;
pm_runtime_dont_use_autosuspend(&pdev->dev);
pm_runtime_put_sync(&pdev->dev);
flush_work(&priv->qos_work);
pm_runtime_disable(&pdev->dev);
serial8250_unregister_port(priv->line);
cpu_latency_qos_remove_request(&priv->pm_qos_request);
device_init_wakeup(&pdev->dev, false);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int omap8250_prepare(struct device *dev)
{
struct omap8250_priv *priv = dev_get_drvdata(dev);
@ -1579,33 +1618,38 @@ static int omap8250_suspend(struct device *dev)
{
struct omap8250_priv *priv = dev_get_drvdata(dev);
struct uart_8250_port *up = serial8250_get_port(priv->line);
int err;
serial8250_suspend_port(priv->line);
pm_runtime_get_sync(dev);
err = pm_runtime_resume_and_get(dev);
if (err)
return err;
if (!device_may_wakeup(dev))
priv->wer = 0;
serial_out(up, UART_OMAP_WER, priv->wer);
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
err = pm_runtime_force_suspend(dev);
flush_work(&priv->qos_work);
return 0;
return err;
}
static int omap8250_resume(struct device *dev)
{
struct omap8250_priv *priv = dev_get_drvdata(dev);
int err;
err = pm_runtime_force_resume(dev);
if (err)
return err;
serial8250_resume_port(priv->line);
/* Paired with pm_runtime_resume_and_get() in omap8250_suspend() */
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
return 0;
}
#else
#define omap8250_prepare NULL
#define omap8250_complete NULL
#endif
#ifdef CONFIG_PM
static int omap8250_lost_context(struct uart_8250_port *up)
{
u32 val;
@ -1621,11 +1665,15 @@ static int omap8250_lost_context(struct uart_8250_port *up)
return 0;
}
static void uart_write(struct omap8250_priv *priv, u32 reg, u32 val)
{
writel(val, priv->membase + (reg << OMAP_UART_REGSHIFT));
}
/* 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);
struct uart_8250_port *up = serial8250_get_port(priv->line);
int timeout = 100;
int sysc;
int syss;
@ -1639,20 +1687,20 @@ static int omap8250_soft_reset(struct device *dev)
* needing omap8250_soft_reset() quirk. Do it in two writes as
* recommended in the comment for omap8250_update_scr().
*/
serial_out(up, UART_OMAP_SCR, OMAP_UART_SCR_DMAMODE_1);
serial_out(up, UART_OMAP_SCR,
uart_write(priv, UART_OMAP_SCR, OMAP_UART_SCR_DMAMODE_1);
uart_write(priv, UART_OMAP_SCR,
OMAP_UART_SCR_DMAMODE_1 | OMAP_UART_SCR_DMAMODE_CTL);
sysc = serial_in(up, UART_OMAP_SYSC);
sysc = uart_read(priv, UART_OMAP_SYSC);
/* softreset the UART */
sysc |= OMAP_UART_SYSC_SOFTRESET;
serial_out(up, UART_OMAP_SYSC, sysc);
uart_write(priv, UART_OMAP_SYSC, sysc);
/* By experiments, 1us enough for reset complete on AM335x */
do {
udelay(1);
syss = serial_in(up, UART_OMAP_SYSS);
syss = uart_read(priv, UART_OMAP_SYSS);
} while (--timeout && !(syss & OMAP_UART_SYSS_RESETDONE));
if (!timeout) {
@ -1666,13 +1714,10 @@ static int omap8250_soft_reset(struct device *dev)
static int omap8250_runtime_suspend(struct device *dev)
{
struct omap8250_priv *priv = dev_get_drvdata(dev);
struct uart_8250_port *up;
struct uart_8250_port *up = NULL;
/* In case runtime-pm tries this before we are setup */
if (!priv)
return 0;
up = serial8250_get_port(priv->line);
if (priv->line >= 0)
up = serial8250_get_port(priv->line);
/*
* When using 'no_console_suspend', the console UART must not be
* suspended. Since driver suspend is managed by runtime suspend,
@ -1680,7 +1725,7 @@ static int omap8250_runtime_suspend(struct device *dev)
* active during suspend.
*/
if (priv->is_suspending && !console_suspend_enabled) {
if (uart_console(&up->port))
if (up && uart_console(&up->port))
return -EBUSY;
}
@ -1691,13 +1736,15 @@ static int omap8250_runtime_suspend(struct device *dev)
if (ret)
return ret;
/* 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);
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);
}
}
if (up->dma && up->dma->rxchan)
if (up && up->dma && up->dma->rxchan)
omap_8250_rx_dma_flush(up);
priv->latency = PM_QOS_CPU_LATENCY_DEFAULT_VALUE;
@ -1709,25 +1756,27 @@ static int omap8250_runtime_suspend(struct device *dev)
static int omap8250_runtime_resume(struct device *dev)
{
struct omap8250_priv *priv = dev_get_drvdata(dev);
struct uart_8250_port *up;
struct uart_8250_port *up = NULL;
/* In case runtime-pm tries this before we are setup */
if (!priv)
return 0;
if (priv->line >= 0)
up = serial8250_get_port(priv->line);
up = serial8250_get_port(priv->line);
if (omap8250_lost_context(up))
if (up && omap8250_lost_context(up)) {
spin_lock_irq(&up->port.lock);
omap8250_restore_regs(up);
spin_unlock_irq(&up->port.lock);
}
if (up->dma && up->dma->rxchan && !(priv->habit & UART_HAS_EFR2))
if (up && up->dma && up->dma->rxchan && !(priv->habit & UART_HAS_EFR2)) {
spin_lock_irq(&up->port.lock);
omap_8250_rx_dma(up);
spin_unlock_irq(&up->port.lock);
}
priv->latency = priv->calc_latency;
schedule_work(&priv->qos_work);
return 0;
}
#endif
#ifdef CONFIG_SERIAL_8250_OMAP_TTYO_FIXUP
static int __init omap8250_console_fixup(void)
@ -1770,17 +1819,17 @@ console_initcall(omap8250_console_fixup);
#endif
static const struct dev_pm_ops omap8250_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(omap8250_suspend, omap8250_resume)
SET_RUNTIME_PM_OPS(omap8250_runtime_suspend,
SYSTEM_SLEEP_PM_OPS(omap8250_suspend, omap8250_resume)
RUNTIME_PM_OPS(omap8250_runtime_suspend,
omap8250_runtime_resume, NULL)
.prepare = omap8250_prepare,
.complete = omap8250_complete,
.prepare = pm_sleep_ptr(omap8250_prepare),
.complete = pm_sleep_ptr(omap8250_complete),
};
static struct platform_driver omap8250_platform_driver = {
.driver = {
.name = "omap8250",
.pm = &omap8250_dev_pm_ops,
.pm = pm_ptr(&omap8250_dev_pm_ops),
.of_match_table = omap8250_dt_ids,
},
.probe = omap8250_probe,

View File

@ -1232,14 +1232,6 @@ static int pci_oxsemi_tornado_setup(struct serial_private *priv,
return pci_default_setup(priv, board, up, idx);
}
static int pci_asix_setup(struct serial_private *priv,
const struct pciserial_board *board,
struct uart_8250_port *port, int idx)
{
port->bugs |= UART_BUG_PARITY;
return pci_default_setup(priv, board, port, idx);
}
#define QPCR_TEST_FOR1 0x3F
#define QPCR_TEST_GET1 0x00
#define QPCR_TEST_FOR2 0x40
@ -1955,7 +1947,6 @@ pci_moxa_setup(struct serial_private *priv,
#define PCI_DEVICE_ID_WCH_CH355_4S 0x7173
#define PCI_VENDOR_ID_AGESTAR 0x5372
#define PCI_DEVICE_ID_AGESTAR_9375 0x6872
#define PCI_VENDOR_ID_ASIX 0x9710
#define PCI_DEVICE_ID_BROADCOM_TRUMANAGE 0x160a
#define PCI_DEVICE_ID_AMCC_ADDIDATA_APCI7800 0x818e
@ -2600,16 +2591,6 @@ static struct pci_serial_quirk pci_serial_quirks[] = {
.exit = pci_wch_ch38x_exit,
.setup = pci_wch_ch38x_setup,
},
/*
* ASIX devices with FIFO bug
*/
{
.vendor = PCI_VENDOR_ID_ASIX,
.device = PCI_ANY_ID,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.setup = pci_asix_setup,
},
/*
* Broadcom TruManage (NetXtreme)
*/

View File

@ -325,7 +325,7 @@ static const struct serial8250_config uart_config[] = {
};
/* Uart divisor latch read */
static int default_serial_dl_read(struct uart_8250_port *up)
static u32 default_serial_dl_read(struct uart_8250_port *up)
{
/* Assign these in pieces to truncate any bits above 7. */
unsigned char dll = serial_in(up, UART_DLL);
@ -335,72 +335,12 @@ static int default_serial_dl_read(struct uart_8250_port *up)
}
/* Uart divisor latch write */
static void default_serial_dl_write(struct uart_8250_port *up, int value)
static void default_serial_dl_write(struct uart_8250_port *up, u32 value)
{
serial_out(up, UART_DLL, value & 0xff);
serial_out(up, UART_DLM, value >> 8 & 0xff);
}
#ifdef CONFIG_SERIAL_8250_RT288X
#define UART_REG_UNMAPPED -1
/* Au1x00/RT288x UART hardware has a weird register layout */
static const s8 au_io_in_map[8] = {
[UART_RX] = 0,
[UART_IER] = 2,
[UART_IIR] = 3,
[UART_LCR] = 5,
[UART_MCR] = 6,
[UART_LSR] = 7,
[UART_MSR] = 8,
[UART_SCR] = UART_REG_UNMAPPED,
};
static const s8 au_io_out_map[8] = {
[UART_TX] = 1,
[UART_IER] = 2,
[UART_FCR] = 4,
[UART_LCR] = 5,
[UART_MCR] = 6,
[UART_LSR] = UART_REG_UNMAPPED,
[UART_MSR] = UART_REG_UNMAPPED,
[UART_SCR] = UART_REG_UNMAPPED,
};
unsigned int au_serial_in(struct uart_port *p, int offset)
{
if (offset >= ARRAY_SIZE(au_io_in_map))
return UINT_MAX;
offset = au_io_in_map[offset];
if (offset == UART_REG_UNMAPPED)
return UINT_MAX;
return __raw_readl(p->membase + (offset << p->regshift));
}
void au_serial_out(struct uart_port *p, int offset, int value)
{
if (offset >= ARRAY_SIZE(au_io_out_map))
return;
offset = au_io_out_map[offset];
if (offset == UART_REG_UNMAPPED)
return;
__raw_writel(value, p->membase + (offset << p->regshift));
}
/* Au1x00 haven't got a standard divisor latch */
static int au_serial_dl_read(struct uart_8250_port *up)
{
return __raw_readl(up->port.membase + 0x28);
}
static void au_serial_dl_write(struct uart_8250_port *up, int value)
{
__raw_writel(value, up->port.membase + 0x28);
}
#endif
static unsigned int hub6_serial_in(struct uart_port *p, int offset)
{
offset = offset << p->regshift;
@ -510,15 +450,6 @@ static void set_io_from_upio(struct uart_port *p)
p->serial_out = mem32be_serial_out;
break;
#ifdef CONFIG_SERIAL_8250_RT288X
case UPIO_AU:
p->serial_in = au_serial_in;
p->serial_out = au_serial_out;
up->dl_read = au_serial_dl_read;
up->dl_write = au_serial_dl_write;
break;
#endif
default:
p->serial_in = io_serial_in;
p->serial_out = io_serial_out;
@ -608,6 +539,9 @@ EXPORT_SYMBOL_GPL(serial8250_rpm_put);
*/
static int serial8250_em485_init(struct uart_8250_port *p)
{
/* Port locked to synchronize UART_IER access against the console. */
lockdep_assert_held_once(&p->port.lock);
if (p->em485)
goto deassert_rts;
@ -746,6 +680,8 @@ static void serial8250_set_sleep(struct uart_8250_port *p, int sleep)
serial8250_rpm_get(p);
if (p->capabilities & UART_CAP_SLEEP) {
/* Synchronize UART_IER access against the console. */
spin_lock_irq(&p->port.lock);
if (p->capabilities & UART_CAP_EFR) {
lcr = serial_in(p, UART_LCR);
efr = serial_in(p, UART_EFR);
@ -759,6 +695,7 @@ static void serial8250_set_sleep(struct uart_8250_port *p, int sleep)
serial_out(p, UART_EFR, efr);
serial_out(p, UART_LCR, lcr);
}
spin_unlock_irq(&p->port.lock);
}
serial8250_rpm_put(p);
@ -766,6 +703,9 @@ static void serial8250_set_sleep(struct uart_8250_port *p, int sleep)
static void serial8250_clear_IER(struct uart_8250_port *up)
{
/* Port locked to synchronize UART_IER access against the console. */
lockdep_assert_held_once(&up->port.lock);
if (up->capabilities & UART_CAP_UUE)
serial_out(up, UART_IER, UART_IER_UUE);
else
@ -848,7 +788,7 @@ static void disable_rsa(struct uart_8250_port *up)
static int size_fifo(struct uart_8250_port *up)
{
unsigned char old_fcr, old_mcr, old_lcr;
unsigned short old_dl;
u32 old_dl;
int count;
old_lcr = serial_in(up, UART_LCR);
@ -1038,6 +978,9 @@ static void autoconfig_16550a(struct uart_8250_port *up)
unsigned char status1, status2;
unsigned int iersave;
/* Port locked to synchronize UART_IER access against the console. */
lockdep_assert_held_once(&up->port.lock);
up->port.type = PORT_16550A;
up->capabilities |= UART_CAP_FIFO;
@ -1221,6 +1164,8 @@ static void autoconfig(struct uart_8250_port *up)
/*
* We really do need global IRQs disabled here - we're going to
* be frobbing the chips IRQ enable register to see if it exists.
*
* Synchronize UART_IER access against the console.
*/
spin_lock_irqsave(&port->lock, flags);
@ -1393,7 +1338,10 @@ static void autoconfig_irq(struct uart_8250_port *up)
/* forget possible initially masked and pending IRQ */
probe_irq_off(probe_irq_on());
save_mcr = serial8250_in_MCR(up);
/* Synchronize UART_IER access against the console. */
spin_lock_irq(&port->lock);
save_ier = serial_in(up, UART_IER);
spin_unlock_irq(&port->lock);
serial8250_out_MCR(up, UART_MCR_OUT1 | UART_MCR_OUT2);
irqs = probe_irq_on();
@ -1405,7 +1353,10 @@ static void autoconfig_irq(struct uart_8250_port *up)
serial8250_out_MCR(up,
UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2);
}
/* Synchronize UART_IER access against the console. */
spin_lock_irq(&port->lock);
serial_out(up, UART_IER, UART_IER_ALL_INTR);
spin_unlock_irq(&port->lock);
serial_in(up, UART_LSR);
serial_in(up, UART_RX);
serial_in(up, UART_IIR);
@ -1415,7 +1366,10 @@ static void autoconfig_irq(struct uart_8250_port *up)
irq = probe_irq_off(irqs);
serial8250_out_MCR(up, save_mcr);
/* Synchronize UART_IER access against the console. */
spin_lock_irq(&port->lock);
serial_out(up, UART_IER, save_ier);
spin_unlock_irq(&port->lock);
if (port->flags & UPF_FOURPORT)
outb_p(save_ICP, ICP);
@ -1430,6 +1384,9 @@ static void serial8250_stop_rx(struct uart_port *port)
{
struct uart_8250_port *up = up_to_u8250p(port);
/* Port locked to synchronize UART_IER access against the console. */
lockdep_assert_held_once(&port->lock);
serial8250_rpm_get(up);
up->ier &= ~(UART_IER_RLSI | UART_IER_RDI);
@ -1449,6 +1406,9 @@ void serial8250_em485_stop_tx(struct uart_8250_port *p)
{
unsigned char mcr = serial8250_in_MCR(p);
/* Port locked to synchronize UART_IER access against the console. */
lockdep_assert_held_once(&p->port.lock);
if (p->port.rs485.flags & SER_RS485_RTS_AFTER_SEND)
mcr |= UART_MCR_RTS;
else
@ -1498,6 +1458,9 @@ static void __stop_tx_rs485(struct uart_8250_port *p, u64 stop_delay)
{
struct uart_8250_em485 *em485 = p->em485;
/* Port locked to synchronize UART_IER access against the console. */
lockdep_assert_held_once(&p->port.lock);
stop_delay += (u64)p->port.rs485.delay_rts_after_send * NSEC_PER_MSEC;
/*
@ -1677,6 +1640,9 @@ static void serial8250_start_tx(struct uart_port *port)
struct uart_8250_port *up = up_to_u8250p(port);
struct uart_8250_em485 *em485 = up->em485;
/* Port locked to synchronize UART_IER access against the console. */
lockdep_assert_held_once(&port->lock);
if (!port->x_char && uart_circ_empty(&port->state->xmit))
return;
@ -1704,6 +1670,9 @@ static void serial8250_disable_ms(struct uart_port *port)
{
struct uart_8250_port *up = up_to_u8250p(port);
/* Port locked to synchronize UART_IER access against the console. */
lockdep_assert_held_once(&port->lock);
/* no MSR capabilities */
if (up->bugs & UART_BUG_NOMSR)
return;
@ -1718,6 +1687,9 @@ static void serial8250_enable_ms(struct uart_port *port)
{
struct uart_8250_port *up = up_to_u8250p(port);
/* Port locked to synchronize UART_IER access against the console. */
lockdep_assert_held_once(&port->lock);
/* no MSR capabilities */
if (up->bugs & UART_BUG_NOMSR)
return;
@ -2174,6 +2146,14 @@ static void serial8250_put_poll_char(struct uart_port *port,
unsigned int ier;
struct uart_8250_port *up = up_to_u8250p(port);
/*
* Normally the port is locked to synchronize UART_IER access
* against the console. However, this function is only used by
* KDB/KGDB, where it may not be possible to acquire the port
* lock because all other CPUs are quiesced. The quiescence
* should allow safe lockless usage here.
*/
serial8250_rpm_get(up);
/*
* First save the IER then disable the interrupts
@ -2219,7 +2199,12 @@ int serial8250_do_startup(struct uart_port *port)
serial8250_rpm_get(up);
if (port->type == PORT_16C950) {
/* Wake up and initialize UART */
/*
* Wake up and initialize UART
*
* Synchronize UART_IER access against the console.
*/
spin_lock_irqsave(&port->lock, flags);
up->acr = 0;
serial_port_out(port, UART_LCR, UART_LCR_CONF_MODE_B);
serial_port_out(port, UART_EFR, UART_EFR_ECB);
@ -2229,12 +2214,19 @@ int serial8250_do_startup(struct uart_port *port)
serial_port_out(port, UART_LCR, UART_LCR_CONF_MODE_B);
serial_port_out(port, UART_EFR, UART_EFR_ECB);
serial_port_out(port, UART_LCR, 0);
spin_unlock_irqrestore(&port->lock, flags);
}
if (port->type == PORT_DA830) {
/* Reset the port */
/*
* Reset the port
*
* Synchronize UART_IER access against the console.
*/
spin_lock_irqsave(&port->lock, flags);
serial_port_out(port, UART_IER, 0);
serial_port_out(port, UART_DA830_PWREMU_MGMT, 0);
spin_unlock_irqrestore(&port->lock, flags);
mdelay(10);
/* Enable Tx, Rx and free run mode */
@ -2345,6 +2337,8 @@ int serial8250_do_startup(struct uart_port *port)
* this interrupt whenever the transmitter is idle and
* the interrupt is enabled. Delays are necessary to
* allow register changes to become visible.
*
* Synchronize UART_IER access against the console.
*/
spin_lock_irqsave(&port->lock, flags);
@ -2495,6 +2489,8 @@ void serial8250_do_shutdown(struct uart_port *port)
serial8250_rpm_get(up);
/*
* Disable interrupts from this port
*
* Synchronize UART_IER access against the console.
*/
spin_lock_irqsave(&port->lock, flags);
up->ier = 0;
@ -2636,11 +2632,8 @@ static unsigned char serial8250_compute_lcr(struct uart_8250_port *up,
if (c_cflag & CSTOPB)
cval |= UART_LCR_STOP;
if (c_cflag & PARENB) {
if (c_cflag & PARENB)
cval |= UART_LCR_PARITY;
if (up->bugs & UART_BUG_PARITY)
up->fifo_bug = true;
}
if (!(c_cflag & PARODD))
cval |= UART_LCR_EPAR;
if (c_cflag & CMSPAR)
@ -2794,6 +2787,8 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
/*
* Ok, we're now changing the port state. Do it with
* interrupts disabled.
*
* Synchronize UART_IER access against the console.
*/
serial8250_rpm_get(up);
spin_lock_irqsave(&port->lock, flags);
@ -2801,8 +2796,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
up->lcr = cval; /* Save computed LCR */
if (up->capabilities & UART_CAP_FIFO && port->fifosize > 1) {
/* NOTE: If fifo_bug is not set, a user can set RX_trigger. */
if ((baud < 2400 && !up->dma) || up->fifo_bug) {
if (baud < 2400 && !up->dma) {
up->fcr &= ~UART_FCR_TRIGGER_MASK;
up->fcr |= UART_FCR_TRIGGER_1;
}
@ -2969,11 +2963,6 @@ static unsigned int serial8250_port_size(struct uart_8250_port *pt)
{
if (pt->port.mapsize)
return pt->port.mapsize;
if (pt->port.iotype == UPIO_AU) {
if (pt->port.type == PORT_RT2880)
return 0x100;
return 0x1000;
}
if (is_omap1_8250(pt))
return 0x16 << pt->port.regshift;
@ -3138,8 +3127,7 @@ static int do_set_rxtrig(struct tty_port *port, unsigned char bytes)
struct uart_8250_port *up = up_to_u8250p(uport);
int rxtrig;
if (!(up->capabilities & UART_CAP_FIFO) || uport->fifosize <= 1 ||
up->fifo_bug)
if (!(up->capabilities & UART_CAP_FIFO) || uport->fifosize <= 1)
return -EINVAL;
rxtrig = bytes_to_fcr_rxtrig(up, bytes);
@ -3223,10 +3211,6 @@ static void serial8250_config_port(struct uart_port *port, int flags)
if (flags & UART_CONFIG_TYPE)
autoconfig(up);
/* if access method is AU, it is a 16550 with a quirk */
if (port->type == PORT_16550A && port->iotype == UPIO_AU)
up->bugs |= UART_BUG_NOMSR;
/* HW bugs may trigger IRQ while IIR == NO_INT */
if (port->type == PORT_TEGRA)
up->bugs |= UART_BUG_NOMSR;
@ -3293,6 +3277,7 @@ void serial8250_init_port(struct uart_8250_port *up)
struct uart_port *port = &up->port;
spin_lock_init(&port->lock);
port->ctrl_id = 0;
port->ops = &serial8250_pops;
port->has_sysrq = IS_ENABLED(CONFIG_SERIAL_8250_CONSOLE);

View File

@ -60,7 +60,7 @@ static const struct of_device_id serial_pxa_dt_ids[] = {
MODULE_DEVICE_TABLE(of, serial_pxa_dt_ids);
/* Uart divisor latch write */
static void serial_pxa_dl_write(struct uart_8250_port *up, int value)
static void serial_pxa_dl_write(struct uart_8250_port *up, u32 value)
{
unsigned int dll;

View File

@ -0,0 +1,136 @@
// SPDX-License-Identifier: GPL-2.0
/*
* RT288x/Au1xxx driver
*/
#include <linux/module.h>
#include <linux/io.h>
#include <linux/init.h>
#include <linux/console.h>
#include <linux/serial.h>
#include <linux/serial_8250.h>
#include "8250.h"
#define RT288X_DL 0x28
/* Au1x00/RT288x UART hardware has a weird register layout */
static const u8 au_io_in_map[7] = {
[UART_RX] = 0,
[UART_IER] = 2,
[UART_IIR] = 3,
[UART_LCR] = 5,
[UART_MCR] = 6,
[UART_LSR] = 7,
[UART_MSR] = 8,
};
static const u8 au_io_out_map[5] = {
[UART_TX] = 1,
[UART_IER] = 2,
[UART_FCR] = 4,
[UART_LCR] = 5,
[UART_MCR] = 6,
};
static unsigned int au_serial_in(struct uart_port *p, int offset)
{
if (offset >= ARRAY_SIZE(au_io_in_map))
return UINT_MAX;
offset = au_io_in_map[offset];
return __raw_readl(p->membase + (offset << p->regshift));
}
static void au_serial_out(struct uart_port *p, int offset, int value)
{
if (offset >= ARRAY_SIZE(au_io_out_map))
return;
offset = au_io_out_map[offset];
__raw_writel(value, p->membase + (offset << p->regshift));
}
/* Au1x00 haven't got a standard divisor latch */
static u32 au_serial_dl_read(struct uart_8250_port *up)
{
return __raw_readl(up->port.membase + RT288X_DL);
}
static void au_serial_dl_write(struct uart_8250_port *up, u32 value)
{
__raw_writel(value, up->port.membase + RT288X_DL);
}
int au_platform_setup(struct plat_serial8250_port *p)
{
p->iotype = UPIO_AU;
p->serial_in = au_serial_in;
p->serial_out = au_serial_out;
p->dl_read = au_serial_dl_read;
p->dl_write = au_serial_dl_write;
p->mapsize = 0x1000;
p->bugs |= UART_BUG_NOMSR;
return 0;
}
EXPORT_SYMBOL_GPL(au_platform_setup);
int rt288x_setup(struct uart_port *p)
{
struct uart_8250_port *up = up_to_u8250p(p);
p->iotype = UPIO_AU;
p->serial_in = au_serial_in;
p->serial_out = au_serial_out;
up->dl_read = au_serial_dl_read;
up->dl_write = au_serial_dl_write;
p->mapsize = 0x100;
up->bugs |= UART_BUG_NOMSR;
return 0;
}
EXPORT_SYMBOL_GPL(rt288x_setup);
#ifdef CONFIG_SERIAL_8250_CONSOLE
static void au_putc(struct uart_port *port, unsigned char c)
{
unsigned int status;
au_serial_out(port, UART_TX, c);
for (;;) {
status = au_serial_in(port, UART_LSR);
if (uart_lsr_tx_empty(status))
break;
cpu_relax();
}
}
static void au_early_serial8250_write(struct console *console,
const char *s, unsigned int count)
{
struct earlycon_device *device = console->data;
struct uart_port *port = &device->port;
uart_console_write(port, s, count, au_putc);
}
static int __init early_au_setup(struct earlycon_device *dev, const char *opt)
{
rt288x_setup(&dev->port);
dev->con->write = au_early_serial8250_write;
return 0;
}
OF_EARLYCON_DECLARE(palmchip, "ralink,rt2880-uart", early_au_setup);
#endif
MODULE_DESCRIPTION("RT288x/Au1xxx UART driver");
MODULE_LICENSE("GPL");

View File

@ -145,12 +145,12 @@ static void uniphier_serial_out(struct uart_port *p, int offset, int value)
* The divisor latch register exists at different address.
* Override dl_read/write callbacks.
*/
static int uniphier_serial_dl_read(struct uart_8250_port *up)
static u32 uniphier_serial_dl_read(struct uart_8250_port *up)
{
return readl(up->port.membase + UNIPHIER_UART_DLR);
}
static void uniphier_serial_dl_write(struct uart_8250_port *up, int value)
static void uniphier_serial_dl_write(struct uart_8250_port *up, u32 value)
{
writel(value, up->port.membase + UNIPHIER_UART_DLR);
}

View File

@ -71,14 +71,16 @@ config SERIAL_8250_16550A_VARIANTS
console, you can say N here.
config SERIAL_8250_FINTEK
bool "Support for Fintek F81216A LPC to 4 UART RS485 API"
bool "Support for Fintek variants"
depends on SERIAL_8250
help
Selecting this option will add support for the RS485 capabilities
of the Fintek F81216A LPC to 4 UART.
Selecting this option will add support for the RS232 and RS485
capabilities of the Fintek F81216A LPC to 4 UART as well similar
variants.
If this option is not selected the device will be configured as a
standard 16550A serial port.
standard 16550A serial port, however the device may not function
correctly without this option enabled.
If unsure, say N.
@ -377,9 +379,9 @@ config SERIAL_8250_BCM2835AUX
If unsure, say N.
config SERIAL_8250_FSL
bool "Freescale 16550 UART support" if COMPILE_TEST && !(PPC || ARM || ARM64)
depends on SERIAL_8250_CONSOLE
default PPC || ARM || ARM64
tristate "Freescale 16550 UART support" if COMPILE_TEST && !(PPC || ARM || ARM64)
depends on SERIAL_8250
default SERIAL_8250 if PPC || ARM || ARM64
help
Selecting this option enables a workaround for a break-detection
erratum for Freescale 16550 UARTs in the 8250 driver. It also

View File

@ -35,6 +35,7 @@ obj-$(CONFIG_SERIAL_8250_DW) += 8250_dw.o
obj-$(CONFIG_SERIAL_8250_EM) += 8250_em.o
obj-$(CONFIG_SERIAL_8250_IOC3) += 8250_ioc3.o
obj-$(CONFIG_SERIAL_8250_OMAP) += 8250_omap.o
obj-$(CONFIG_SERIAL_8250_RT288X) += 8250_rt288x.o
obj-$(CONFIG_SERIAL_8250_LPC18XX) += 8250_lpc18xx.o
obj-$(CONFIG_SERIAL_8250_MT6577) += 8250_mtk.o
obj-$(CONFIG_SERIAL_8250_UNIPHIER) += 8250_uniphier.o

View File

@ -1555,6 +1555,29 @@ config SERIAL_SUNPLUS_CONSOLE
you can alter that using a kernel command line option such as
"console=ttySUPx".
config SERIAL_NUVOTON_MA35D1
tristate "Nuvoton MA35D1 family UART support"
depends on ARCH_MA35 || COMPILE_TEST
select SERIAL_CORE
help
This driver supports Nuvoton MA35D1 family UART ports. If you would
like to use them, you must answer Y or M to this option. Note that
for use as console, it must be included in kernel and not as a
module. If you enable this option, Ma35D1 serial ports in the system
will be registered as ttyNVTx.
config SERIAL_NUVOTON_MA35D1_CONSOLE
bool "Console on a Nuvotn MA35D1 family UART port"
depends on SERIAL_NUVOTON_MA35D1=y
select SERIAL_CORE_CONSOLE
help
Select this options if you'd like to use the UART port0 of the
Nuvoton MA35D1 family as a console.
Even if you say Y here, the currently visible virtual console
(/dev/tty0) will still be used as the system console by default,
but you can alter that using a kernel command line option such as
"console=ttyNVTx".
endmenu
config SERIAL_MCTRL_GPIO

View File

@ -3,7 +3,8 @@
# Makefile for the kernel serial device drivers.
#
obj-$(CONFIG_SERIAL_CORE) += serial_core.o
obj-$(CONFIG_SERIAL_CORE) += serial_base.o
serial_base-y := serial_core.o serial_base_bus.o serial_ctrl.o serial_port.o
obj-$(CONFIG_SERIAL_EARLYCON) += earlycon.o
obj-$(CONFIG_SERIAL_EARLYCON_SEMIHOST) += earlycon-semihost.o
@ -21,7 +22,7 @@ obj-$(CONFIG_SERIAL_SUNSAB) += sunsab.o
obj-$(CONFIG_SERIAL_21285) += 21285.o
# Now bring in any enabled 8250/16450/16550 type drivers.
obj-$(CONFIG_SERIAL_8250) += 8250/
obj-y += 8250/
obj-$(CONFIG_SERIAL_AMBA_PL010) += amba-pl010.o
obj-$(CONFIG_SERIAL_AMBA_PL011) += amba-pl011.o
@ -93,3 +94,4 @@ obj-$(CONFIG_SERIAL_MCTRL_GPIO) += serial_mctrl_gpio.o
obj-$(CONFIG_SERIAL_KGDB_NMI) += kgdb_nmi.o
obj-$(CONFIG_KGDB_SERIAL_CONSOLE) += kgdboc.o
obj-$(CONFIG_SERIAL_NUVOTON_MA35D1) += ma35d1_serial.o

View File

@ -2166,6 +2166,13 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios,
* ----------^----------^----------^----------^-----
*/
pl011_write_lcr_h(uap, lcr_h);
/*
* Receive was disabled by pl011_disable_uart during shutdown.
* Need to reenable receive if you need to use a tty_driver
* returns from tty_find_polling_driver() after a port shutdown.
*/
old_cr |= UART011_CR_RXE;
pl011_write(old_cr, uap, REG_CR);
spin_unlock_irqrestore(&port->lock, flags);

View File

@ -868,11 +868,11 @@ static void atmel_complete_tx_dma(void *arg)
dmaengine_terminate_all(chan);
uart_xmit_advance(port, atmel_port->tx_len);
spin_lock_irq(&atmel_port->lock_tx);
spin_lock(&atmel_port->lock_tx);
async_tx_ack(atmel_port->desc_tx);
atmel_port->cookie_tx = -EINVAL;
atmel_port->desc_tx = NULL;
spin_unlock_irq(&atmel_port->lock_tx);
spin_unlock(&atmel_port->lock_tx);
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(port);
@ -3006,14 +3006,13 @@ static int atmel_serial_remove(struct platform_device *pdev)
{
struct uart_port *port = platform_get_drvdata(pdev);
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
int ret = 0;
tasklet_kill(&atmel_port->tasklet_rx);
tasklet_kill(&atmel_port->tasklet_tx);
device_init_wakeup(&pdev->dev, 0);
ret = uart_remove_one_port(&atmel_uart, port);
uart_remove_one_port(&atmel_uart, port);
kfree(atmel_port->rx_ring.buf);
@ -3023,7 +3022,7 @@ static int atmel_serial_remove(struct platform_device *pdev)
pdev->dev.of_node = NULL;
return ret;
return 0;
}
static SIMPLE_DEV_PM_OPS(atmel_serial_pm_ops, atmel_serial_suspend,

View File

@ -514,7 +514,9 @@ static int uart_clps711x_remove(struct platform_device *pdev)
{
struct clps711x_port *s = platform_get_drvdata(pdev);
return uart_remove_one_port(&clps711x_uart, &s->port);
uart_remove_one_port(&clps711x_uart, &s->port);
return 0;
}
static const struct of_device_id __maybe_unused clps711x_uart_dt_ids[] = {

View File

@ -1431,7 +1431,10 @@ static int cpm_uart_probe(struct platform_device *ofdev)
static int cpm_uart_remove(struct platform_device *ofdev)
{
struct uart_cpm_port *pinfo = platform_get_drvdata(ofdev);
return uart_remove_one_port(&cpm_reg, &pinfo->port);
uart_remove_one_port(&cpm_reg, &pinfo->port);
return 0;
}
static const struct of_device_id cpm_uart_match[] = {

View File

@ -238,6 +238,7 @@
/* Rx DMA timeout in ms, which is used to calculate Rx ring buffer size */
#define DMA_RX_TIMEOUT (10)
#define DMA_RX_IDLE_CHARS 8
#define UART_AUTOSUSPEND_TIMEOUT 3000
#define DRIVER_NAME "fsl-lpuart"
@ -282,6 +283,7 @@ struct lpuart_port {
struct scatterlist rx_sgl, tx_sgl[2];
struct circ_buf rx_ring;
int rx_dma_rng_buf_len;
int last_residue;
unsigned int dma_tx_nents;
wait_queue_head_t dma_wait;
bool is_cs7; /* Set to true when character size is 7 */
@ -331,7 +333,7 @@ static struct lpuart_soc_data imx8qxp_data = {
.devtype = IMX8QXP_LPUART,
.iotype = UPIO_MEM32,
.reg_off = IMX_REG_OFF,
.rx_watermark = 31,
.rx_watermark = 7, /* A lower watermark is ideal for low baud rates. */
};
static struct lpuart_soc_data imxrt1050_data = {
.devtype = IMXRT1050_LPUART,
@ -1255,6 +1257,8 @@ static void lpuart_copy_rx_to_tty(struct lpuart_port *sport)
sport->port.icount.rx += copied;
}
sport->last_residue = state.residue;
exit:
dma_sync_sg_for_device(chan->device->dev, &sport->rx_sgl, 1,
DMA_FROM_DEVICE);
@ -1272,11 +1276,43 @@ static void lpuart_dma_rx_complete(void *arg)
lpuart_copy_rx_to_tty(sport);
}
/*
* Timer function to simulate the hardware EOP (End Of Package) event.
* The timer callback is to check for new RX data and copy to TTY buffer.
* If no new data are received since last interval, the EOP condition is
* met, complete the DMA transfer by copying the data. Otherwise, just
* restart timer.
*/
static void lpuart_timer_func(struct timer_list *t)
{
struct lpuart_port *sport = from_timer(sport, t, lpuart_timer);
enum dma_status dmastat;
struct dma_chan *chan = sport->dma_rx_chan;
struct circ_buf *ring = &sport->rx_ring;
struct dma_tx_state state;
unsigned long flags;
int count;
lpuart_copy_rx_to_tty(sport);
dmastat = dmaengine_tx_status(chan, sport->dma_rx_cookie, &state);
if (dmastat == DMA_ERROR) {
dev_err(sport->port.dev, "Rx DMA transfer failed!\n");
return;
}
ring->head = sport->rx_sgl.length - state.residue;
count = CIRC_CNT(ring->head, ring->tail, sport->rx_sgl.length);
/* Check if new data received before copying */
if ((count != 0) && (sport->last_residue == state.residue))
lpuart_copy_rx_to_tty(sport);
else
mod_timer(&sport->lpuart_timer,
jiffies + sport->dma_rx_timeout);
if (spin_trylock_irqsave(&sport->port.lock, flags)) {
sport->last_residue = state.residue;
spin_unlock_irqrestore(&sport->port.lock, flags);
}
}
static inline int lpuart_start_rx_dma(struct lpuart_port *sport)
@ -1297,9 +1333,20 @@ static inline int lpuart_start_rx_dma(struct lpuart_port *sport)
*/
sport->rx_dma_rng_buf_len = (DMA_RX_TIMEOUT * baud / bits / 1000) * 2;
sport->rx_dma_rng_buf_len = (1 << fls(sport->rx_dma_rng_buf_len));
sport->rx_dma_rng_buf_len = max_t(int,
sport->rxfifo_size * 2,
sport->rx_dma_rng_buf_len);
/*
* Keep this condition check in case rxfifo_size is unavailable
* for some SoCs.
*/
if (sport->rx_dma_rng_buf_len < 16)
sport->rx_dma_rng_buf_len = 16;
sport->last_residue = 0;
sport->dma_rx_timeout = max(nsecs_to_jiffies(
sport->port.frame_time * DMA_RX_IDLE_CHARS), 1UL);
ring->buf = kzalloc(sport->rx_dma_rng_buf_len, GFP_ATOMIC);
if (!ring->buf)
return -ENOMEM;
@ -1689,12 +1736,13 @@ static void lpuart_rx_dma_startup(struct lpuart_port *sport)
if (!sport->dma_rx_chan)
goto err;
/* set default Rx DMA timeout */
sport->dma_rx_timeout = msecs_to_jiffies(DMA_RX_TIMEOUT);
ret = lpuart_start_rx_dma(sport);
if (ret)
goto err;
/* set Rx DMA timeout */
sport->dma_rx_timeout = msecs_to_jiffies(DMA_RX_TIMEOUT);
if (!sport->dma_rx_timeout)
sport->dma_rx_timeout = 1;
@ -2676,6 +2724,7 @@ OF_EARLYCON_DECLARE(lpuart, "fsl,vf610-lpuart", lpuart_early_console_setup);
OF_EARLYCON_DECLARE(lpuart32, "fsl,ls1021a-lpuart", lpuart32_early_console_setup);
OF_EARLYCON_DECLARE(lpuart32, "fsl,ls1028a-lpuart", ls1028a_early_console_setup);
OF_EARLYCON_DECLARE(lpuart32, "fsl,imx7ulp-lpuart", lpuart32_imx_early_console_setup);
OF_EARLYCON_DECLARE(lpuart32, "fsl,imx8ulp-lpuart", lpuart32_imx_early_console_setup);
OF_EARLYCON_DECLARE(lpuart32, "fsl,imx8qxp-lpuart", lpuart32_imx_early_console_setup);
OF_EARLYCON_DECLARE(lpuart32, "fsl,imxrt1050-lpuart", lpuart32_imx_early_console_setup);
EARLYCON_DECLARE(lpuart, lpuart_early_console_setup);

View File

@ -369,6 +369,16 @@ static void imx_uart_soft_reset(struct imx_port *sport)
sport->idle_counter = 0;
}
static void imx_uart_disable_loopback_rs485(struct imx_port *sport)
{
unsigned int uts;
/* See SER_RS485_ENABLED/UTS_LOOP comment in imx_uart_probe() */
uts = imx_uart_readl(sport, imx_uart_uts_reg(sport));
uts &= ~UTS_LOOP;
imx_uart_writel(sport, uts, imx_uart_uts_reg(sport));
}
/* called with port.lock taken and irqs off */
static void imx_uart_start_rx(struct uart_port *port)
{
@ -390,6 +400,7 @@ static void imx_uart_start_rx(struct uart_port *port)
/* Write UCR2 first as it includes RXEN */
imx_uart_writel(sport, ucr2, UCR2);
imx_uart_writel(sport, ucr1, UCR1);
imx_uart_disable_loopback_rs485(sport);
}
/* called with port.lock taken and irqs off */
@ -1422,7 +1433,7 @@ static int imx_uart_startup(struct uart_port *port)
int retval;
unsigned long flags;
int dma_is_inited = 0;
u32 ucr1, ucr2, ucr3, ucr4, uts;
u32 ucr1, ucr2, ucr3, ucr4;
retval = clk_prepare_enable(sport->clk_per);
if (retval)
@ -1521,10 +1532,7 @@ static int imx_uart_startup(struct uart_port *port)
imx_uart_writel(sport, ucr2, UCR2);
}
/* See SER_RS485_ENABLED/UTS_LOOP comment in imx_uart_probe() */
uts = imx_uart_readl(sport, imx_uart_uts_reg(sport));
uts &= ~UTS_LOOP;
imx_uart_writel(sport, uts, imx_uart_uts_reg(sport));
imx_uart_disable_loopback_rs485(sport);
spin_unlock_irqrestore(&sport->port.lock, flags);
@ -2467,7 +2475,9 @@ static int imx_uart_remove(struct platform_device *pdev)
{
struct imx_port *sport = platform_get_drvdata(pdev);
return uart_remove_one_port(&imx_uart_uart_driver, &sport->port);
uart_remove_one_port(&imx_uart_uart_driver, &sport->port);
return 0;
}
static void imx_uart_restore_context(struct imx_port *sport)

View File

@ -890,7 +890,9 @@ static int lqasc_remove(struct platform_device *pdev)
{
struct uart_port *port = platform_get_drvdata(pdev);
return uart_remove_one_port(&lqasc_reg, port);
uart_remove_one_port(&lqasc_reg, port);
return 0;
}
static const struct ltq_soc_data soc_data_lantiq = {

View File

@ -0,0 +1,821 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* MA35D1 serial driver
* Copyright (C) 2023 Nuvoton Technology Corp.
*/
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/iopoll.h>
#include <linux/serial_core.h>
#include <linux/slab.h>
#include <linux/tty_flip.h>
#include <linux/units.h>
#define MA35_UART_NR 17
#define MA35_RBR_REG 0x00
#define MA35_THR_REG 0x00
#define MA35_IER_REG 0x04
#define MA35_FCR_REG 0x08
#define MA35_LCR_REG 0x0C
#define MA35_MCR_REG 0x10
#define MA35_MSR_REG 0x14
#define MA35_FSR_REG 0x18
#define MA35_ISR_REG 0x1C
#define MA35_TOR_REG 0x20
#define MA35_BAUD_REG 0x24
#define MA35_ALTCTL_REG 0x2C
#define MA35_FUN_SEL_REG 0x30
#define MA35_WKCTL_REG 0x40
#define MA35_WKSTS_REG 0x44
/* MA35_IER_REG - Interrupt Enable Register */
#define MA35_IER_RDA_IEN BIT(0) /* RBR Available Interrupt Enable */
#define MA35_IER_THRE_IEN BIT(1) /* THR Empty Interrupt Enable */
#define MA35_IER_RLS_IEN BIT(2) /* RX Line Status Interrupt Enable */
#define MA35_IER_RTO_IEN BIT(4) /* RX Time-out Interrupt Enable */
#define MA35_IER_BUFERR_IEN BIT(5) /* Buffer Error Interrupt Enable */
#define MA35_IER_TIME_OUT_EN BIT(11) /* RX Buffer Time-out Counter Enable */
#define MA35_IER_AUTO_RTS BIT(12) /* nRTS Auto-flow Control Enable */
#define MA35_IER_AUTO_CTS BIT(13) /* nCTS Auto-flow Control Enable */
/* MA35_FCR_REG - FIFO Control Register */
#define MA35_FCR_RFR BIT(1) /* RX Field Software Reset */
#define MA35_FCR_TFR BIT(2) /* TX Field Software Reset */
#define MA35_FCR_RFITL_MASK GENMASK(7, 4) /* RX FIFO Interrupt Trigger Level */
#define MA35_FCR_RFITL_1BYTE FIELD_PREP(MA35_FCR_RFITL_MASK, 0)
#define MA35_FCR_RFITL_4BYTES FIELD_PREP(MA35_FCR_RFITL_MASK, 1)
#define MA35_FCR_RFITL_8BYTES FIELD_PREP(MA35_FCR_RFITL_MASK, 2)
#define MA35_FCR_RFITL_14BYTES FIELD_PREP(MA35_FCR_RFITL_MASK, 3)
#define MA35_FCR_RFITL_30BYTES FIELD_PREP(MA35_FCR_RFITL_MASK, 4)
#define MA35_FCR_RTSTL_MASK GENMASK(19, 16) /* nRTS Trigger Level */
#define MA35_FCR_RTSTL_1BYTE FIELD_PREP(MA35_FCR_RTSTL_MASK, 0)
#define MA35_FCR_RTSTL_4BYTES FIELD_PREP(MA35_FCR_RTSTL_MASK, 1)
#define MA35_FCR_RTSTL_8BYTES FIELD_PREP(MA35_FCR_RTSTL_MASK, 2)
#define MA35_FCR_RTSTL_14BYTES FIELD_PREP(MA35_FCR_RTSTL_MASK, 3)
#define MA35_FCR_RTSTLL_30BYTES FIELD_PREP(MA35_FCR_RTSTL_MASK, 4)
/* MA35_LCR_REG - Line Control Register */
#define MA35_LCR_NSB BIT(2) /* Number of “STOP Bit” */
#define MA35_LCR_PBE BIT(3) /* Parity Bit Enable */
#define MA35_LCR_EPE BIT(4) /* Even Parity Enable */
#define MA35_LCR_SPE BIT(5) /* Stick Parity Enable */
#define MA35_LCR_BREAK BIT(6) /* Break Control */
#define MA35_LCR_WLS_MASK GENMASK(1, 0) /* Word Length Selection */
#define MA35_LCR_WLS_5BITS FIELD_PREP(MA35_LCR_WLS_MASK, 0)
#define MA35_LCR_WLS_6BITS FIELD_PREP(MA35_LCR_WLS_MASK, 1)
#define MA35_LCR_WLS_7BITS FIELD_PREP(MA35_LCR_WLS_MASK, 2)
#define MA35_LCR_WLS_8BITS FIELD_PREP(MA35_LCR_WLS_MASK, 3)
/* MA35_MCR_REG - Modem Control Register */
#define MA35_MCR_RTS_CTRL BIT(1) /* nRTS Signal Control */
#define MA35_MCR_RTSACTLV BIT(9) /* nRTS Pin Active Level */
#define MA35_MCR_RTSSTS BIT(13) /* nRTS Pin Status (Read Only) */
/* MA35_MSR_REG - Modem Status Register */
#define MA35_MSR_CTSDETF BIT(0) /* Detect nCTS State Change Flag */
#define MA35_MSR_CTSSTS BIT(4) /* nCTS Pin Status (Read Only) */
#define MA35_MSR_CTSACTLV BIT(8) /* nCTS Pin Active Level */
/* MA35_FSR_REG - FIFO Status Register */
#define MA35_FSR_RX_OVER_IF BIT(0) /* RX Overflow Error Interrupt Flag */
#define MA35_FSR_PEF BIT(4) /* Parity Error Flag*/
#define MA35_FSR_FEF BIT(5) /* Framing Error Flag */
#define MA35_FSR_BIF BIT(6) /* Break Interrupt Flag */
#define MA35_FSR_RX_EMPTY BIT(14) /* Receiver FIFO Empty (Read Only) */
#define MA35_FSR_RX_FULL BIT(15) /* Receiver FIFO Full (Read Only) */
#define MA35_FSR_TX_EMPTY BIT(22) /* Transmitter FIFO Empty (Read Only) */
#define MA35_FSR_TX_FULL BIT(23) /* Transmitter FIFO Full (Read Only) */
#define MA35_FSR_TX_OVER_IF BIT(24) /* TX Overflow Error Interrupt Flag */
#define MA35_FSR_TE_FLAG BIT(28) /* Transmitter Empty Flag (Read Only) */
#define MA35_FSR_RXPTR_MSK GENMASK(13, 8) /* TX FIFO Pointer mask */
#define MA35_FSR_TXPTR_MSK GENMASK(21, 16) /* RX FIFO Pointer mask */
/* MA35_ISR_REG - Interrupt Status Register */
#define MA35_ISR_RDA_IF BIT(0) /* RBR Available Interrupt Flag */
#define MA35_ISR_THRE_IF BIT(1) /* THR Empty Interrupt Flag */
#define MA35_ISR_RLSIF BIT(2) /* Receive Line Interrupt Flag */
#define MA35_ISR_MODEMIF BIT(3) /* MODEM Interrupt Flag */
#define MA35_ISR_RXTO_IF BIT(4) /* RX Time-out Interrupt Flag */
#define MA35_ISR_BUFEIF BIT(5) /* Buffer Error Interrupt Flag */
#define MA35_ISR_WK_IF BIT(6) /* UART Wake-up Interrupt Flag */
#define MA35_ISR_RDAINT BIT(8) /* RBR Available Interrupt Indicator */
#define MA35_ISR_THRE_INT BIT(9) /* THR Empty Interrupt Indicator */
#define MA35_ISR_ALL 0xFFFFFFFF
/* MA35_BAUD_REG - Baud Rate Divider Register */
#define MA35_BAUD_MODE_MASK GENMASK(29, 28)
#define MA35_BAUD_MODE0 FIELD_PREP(MA35_BAUD_MODE_MASK, 0)
#define MA35_BAUD_MODE1 FIELD_PREP(MA35_BAUD_MODE_MASK, 2)
#define MA35_BAUD_MODE2 FIELD_PREP(MA35_BAUD_MODE_MASK, 3)
#define MA35_BAUD_MASK GENMASK(15, 0)
/* MA35_ALTCTL_REG - Alternate Control/Status Register */
#define MA35_ALTCTL_RS485AUD BIT(10) /* RS-485 Auto Direction Function */
/* MA35_FUN_SEL_REG - Function Select Register */
#define MA35_FUN_SEL_MASK GENMASK(2, 0)
#define MA35_FUN_SEL_UART FIELD_PREP(MA35_FUN_SEL_MASK, 0)
#define MA35_FUN_SEL_RS485 FIELD_PREP(MA35_FUN_SEL_MASK, 3)
/* The constrain for MA35D1 UART baud rate divider */
#define MA35_BAUD_DIV_MAX 0xFFFF
#define MA35_BAUD_DIV_MIN 11
/* UART FIFO depth */
#define MA35_UART_FIFO_DEPTH 32
/* UART console clock */
#define MA35_UART_CONSOLE_CLK (24 * HZ_PER_MHZ)
/* UART register ioremap size */
#define MA35_UART_REG_SIZE 0x100
/* Rx Timeout */
#define MA35_UART_RX_TOUT 0x40
#define MA35_IER_CONFIG (MA35_IER_RTO_IEN | MA35_IER_RDA_IEN | \
MA35_IER_TIME_OUT_EN | MA35_IER_BUFERR_IEN)
#define MA35_ISR_IF_CHECK (MA35_ISR_RDA_IF | MA35_ISR_RXTO_IF | \
MA35_ISR_THRE_INT | MA35_ISR_BUFEIF)
#define MA35_FSR_TX_BOTH_EMPTY (MA35_FSR_TE_FLAG | MA35_FSR_TX_EMPTY)
static struct uart_driver ma35d1serial_reg;
struct uart_ma35d1_port {
struct uart_port port;
struct clk *clk;
u16 capabilities; /* port capabilities */
u8 ier;
u8 lcr;
u8 mcr;
u32 baud_rate;
u32 console_baud_rate;
u32 console_line;
u32 console_int;
};
static struct uart_ma35d1_port ma35d1serial_ports[MA35_UART_NR];
static struct uart_ma35d1_port *to_ma35d1_uart_port(struct uart_port *uart)
{
return container_of(uart, struct uart_ma35d1_port, port);
}
static u32 serial_in(struct uart_ma35d1_port *p, u32 offset)
{
return readl_relaxed(p->port.membase + offset);
}
static void serial_out(struct uart_ma35d1_port *p, u32 offset, u32 value)
{
writel_relaxed(value, p->port.membase + offset);
}
static void __stop_tx(struct uart_ma35d1_port *p)
{
u32 ier;
ier = serial_in(p, MA35_IER_REG);
if (ier & MA35_IER_THRE_IEN)
serial_out(p, MA35_IER_REG, ier & ~MA35_IER_THRE_IEN);
}
static void ma35d1serial_stop_tx(struct uart_port *port)
{
struct uart_ma35d1_port *up = to_ma35d1_uart_port(port);
__stop_tx(up);
}
static void transmit_chars(struct uart_ma35d1_port *up)
{
u32 count;
u8 ch;
if (uart_tx_stopped(&up->port)) {
ma35d1serial_stop_tx(&up->port);
return;
}
count = MA35_UART_FIFO_DEPTH - FIELD_GET(MA35_FSR_TXPTR_MSK,
serial_in(up, MA35_FSR_REG));
uart_port_tx_limited(&up->port, ch, count,
!(serial_in(up, MA35_FSR_REG) & MA35_FSR_TX_FULL),
serial_out(up, MA35_THR_REG, ch),
({}));
}
static void ma35d1serial_start_tx(struct uart_port *port)
{
struct uart_ma35d1_port *up = to_ma35d1_uart_port(port);
u32 ier;
ier = serial_in(up, MA35_IER_REG);
serial_out(up, MA35_IER_REG, ier & ~MA35_IER_THRE_IEN);
transmit_chars(up);
serial_out(up, MA35_IER_REG, ier | MA35_IER_THRE_IEN);
}
static void ma35d1serial_stop_rx(struct uart_port *port)
{
struct uart_ma35d1_port *up = to_ma35d1_uart_port(port);
u32 ier;
ier = serial_in(up, MA35_IER_REG);
ier &= ~MA35_IER_RDA_IEN;
serial_out(up, MA35_IER_REG, ier);
}
static void receive_chars(struct uart_ma35d1_port *up)
{
int max_count = 256;
u8 ch, flag;
u32 fsr;
fsr = serial_in(up, MA35_FSR_REG);
do {
flag = TTY_NORMAL;
up->port.icount.rx++;
if (unlikely(fsr & (MA35_FSR_BIF | MA35_FSR_FEF |
MA35_FSR_PEF | MA35_FSR_RX_OVER_IF))) {
if (fsr & MA35_FSR_BIF) {
up->port.icount.brk++;
if (uart_handle_break(&up->port))
continue;
}
if (fsr & MA35_FSR_FEF)
up->port.icount.frame++;
if (fsr & MA35_FSR_PEF)
up->port.icount.parity++;
if (fsr & MA35_FSR_RX_OVER_IF)
up->port.icount.overrun++;
serial_out(up, MA35_FSR_REG,
fsr & (MA35_FSR_BIF | MA35_FSR_FEF |
MA35_FSR_PEF | MA35_FSR_RX_OVER_IF));
if (fsr & MA35_FSR_BIF)
flag = TTY_BREAK;
else if (fsr & MA35_FSR_PEF)
flag = TTY_PARITY;
else if (fsr & MA35_FSR_FEF)
flag = TTY_FRAME;
}
ch = serial_in(up, MA35_RBR_REG);
if (uart_handle_sysrq_char(&up->port, ch))
continue;
spin_lock(&up->port.lock);
uart_insert_char(&up->port, fsr, MA35_FSR_RX_OVER_IF, ch, flag);
spin_unlock(&up->port.lock);
fsr = serial_in(up, MA35_FSR_REG);
} while (!(fsr & MA35_FSR_RX_EMPTY) && (max_count-- > 0));
spin_lock(&up->port.lock);
tty_flip_buffer_push(&up->port.state->port);
spin_unlock(&up->port.lock);
}
static irqreturn_t ma35d1serial_interrupt(int irq, void *dev_id)
{
struct uart_port *port = dev_id;
struct uart_ma35d1_port *up = to_ma35d1_uart_port(port);
u32 isr, fsr;
isr = serial_in(up, MA35_ISR_REG);
fsr = serial_in(up, MA35_FSR_REG);
if (!(isr & MA35_ISR_IF_CHECK))
return IRQ_NONE;
if (isr & (MA35_ISR_RDA_IF | MA35_ISR_RXTO_IF))
receive_chars(up);
if (isr & MA35_ISR_THRE_INT)
transmit_chars(up);
if (fsr & MA35_FSR_TX_OVER_IF)
serial_out(up, MA35_FSR_REG, MA35_FSR_TX_OVER_IF);
return IRQ_HANDLED;
}
static u32 ma35d1serial_tx_empty(struct uart_port *port)
{
struct uart_ma35d1_port *up = to_ma35d1_uart_port(port);
u32 fsr;
fsr = serial_in(up, MA35_FSR_REG);
if ((fsr & MA35_FSR_TX_BOTH_EMPTY) == MA35_FSR_TX_BOTH_EMPTY)
return TIOCSER_TEMT;
else
return 0;
}
static u32 ma35d1serial_get_mctrl(struct uart_port *port)
{
struct uart_ma35d1_port *up = to_ma35d1_uart_port(port);
u32 status;
u32 ret = 0;
status = serial_in(up, MA35_MSR_REG);
if (!(status & MA35_MSR_CTSSTS))
ret |= TIOCM_CTS;
return ret;
}
static void ma35d1serial_set_mctrl(struct uart_port *port, u32 mctrl)
{
struct uart_ma35d1_port *up = to_ma35d1_uart_port(port);
u32 mcr, msr, ier;
mcr = serial_in(up, MA35_MCR_REG);
mcr &= ~MA35_MCR_RTS_CTRL;
if (mctrl & TIOCM_RTS)
mcr |= MA35_MCR_RTSACTLV;
else
mcr &= ~MA35_MCR_RTSACTLV;
if (up->mcr & UART_MCR_AFE) {
ier = serial_in(up, MA35_IER_REG);
ier |= MA35_IER_AUTO_RTS | MA35_IER_AUTO_CTS;
serial_out(up, MA35_IER_REG, ier);
up->port.flags |= UPF_HARD_FLOW;
} else {
ier = serial_in(up, MA35_IER_REG);
ier &= ~(MA35_IER_AUTO_RTS | MA35_IER_AUTO_CTS);
serial_out(up, MA35_IER_REG, ier);
up->port.flags &= ~UPF_HARD_FLOW;
}
msr = serial_in(up, MA35_MSR_REG);
msr |= MA35_MSR_CTSACTLV;
serial_out(up, MA35_MSR_REG, msr);
serial_out(up, MA35_MCR_REG, mcr);
}
static void ma35d1serial_break_ctl(struct uart_port *port, int break_state)
{
struct uart_ma35d1_port *up = to_ma35d1_uart_port(port);
unsigned long flags;
u32 lcr;
spin_lock_irqsave(&up->port.lock, flags);
lcr = serial_in(up, MA35_LCR_REG);
if (break_state != 0)
lcr |= MA35_LCR_BREAK;
else
lcr &= ~MA35_LCR_BREAK;
serial_out(up, MA35_LCR_REG, lcr);
spin_unlock_irqrestore(&up->port.lock, flags);
}
static int ma35d1serial_startup(struct uart_port *port)
{
struct uart_ma35d1_port *up = to_ma35d1_uart_port(port);
u32 fcr;
int retval;
/* Reset FIFO */
serial_out(up, MA35_FCR_REG, MA35_FCR_TFR | MA35_FCR_RFR);
/* Clear pending interrupts */
serial_out(up, MA35_ISR_REG, MA35_ISR_ALL);
retval = request_irq(port->irq, ma35d1serial_interrupt, 0,
dev_name(port->dev), port);
if (retval) {
dev_err(up->port.dev, "request irq failed.\n");
return retval;
}
fcr = serial_in(up, MA35_FCR_REG);
fcr |= MA35_FCR_RFITL_4BYTES | MA35_FCR_RTSTL_8BYTES;
serial_out(up, MA35_FCR_REG, fcr);
serial_out(up, MA35_LCR_REG, MA35_LCR_WLS_8BITS);
serial_out(up, MA35_TOR_REG, MA35_UART_RX_TOUT);
serial_out(up, MA35_IER_REG, MA35_IER_CONFIG);
return 0;
}
static void ma35d1serial_shutdown(struct uart_port *port)
{
struct uart_ma35d1_port *up = to_ma35d1_uart_port(port);
serial_out(up, MA35_IER_REG, 0);
free_irq(port->irq, port);
}
static void ma35d1serial_set_termios(struct uart_port *port,
struct ktermios *termios,
const struct ktermios *old)
{
struct uart_ma35d1_port *up = to_ma35d1_uart_port(port);
unsigned long flags;
u32 baud, quot;
u32 lcr = 0;
lcr = UART_LCR_WLEN(tty_get_char_size(termios->c_cflag));
if (termios->c_cflag & CSTOPB)
lcr |= MA35_LCR_NSB;
if (termios->c_cflag & PARENB)
lcr |= MA35_LCR_PBE;
if (!(termios->c_cflag & PARODD))
lcr |= MA35_LCR_EPE;
if (termios->c_cflag & CMSPAR)
lcr |= MA35_LCR_SPE;
baud = uart_get_baud_rate(port, termios, old,
port->uartclk / MA35_BAUD_DIV_MAX,
port->uartclk / MA35_BAUD_DIV_MIN);
/* MA35D1 UART baud rate equation: baudrate = UART_CLK / (quot + 2) */
quot = (port->uartclk / baud) - 2;
/*
* Ok, we're now changing the port state. Do it with
* interrupts disabled.
*/
spin_lock_irqsave(&up->port.lock, flags);
up->port.read_status_mask = MA35_FSR_RX_OVER_IF;
if (termios->c_iflag & INPCK)
up->port.read_status_mask |= MA35_FSR_FEF | MA35_FSR_PEF;
if (termios->c_iflag & (BRKINT | PARMRK))
up->port.read_status_mask |= MA35_FSR_BIF;
/* Characteres to ignore */
up->port.ignore_status_mask = 0;
if (termios->c_iflag & IGNPAR)
up->port.ignore_status_mask |= MA35_FSR_FEF | MA35_FSR_PEF;
if (termios->c_iflag & IGNBRK) {
up->port.ignore_status_mask |= MA35_FSR_BIF;
/*
* 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 |= MA35_FSR_RX_OVER_IF;
}
if (termios->c_cflag & CRTSCTS)
up->mcr |= UART_MCR_AFE;
else
up->mcr &= ~UART_MCR_AFE;
uart_update_timeout(port, termios->c_cflag, baud);
ma35d1serial_set_mctrl(&up->port, up->port.mctrl);
serial_out(up, MA35_BAUD_REG, MA35_BAUD_MODE2 | FIELD_PREP(MA35_BAUD_MASK, quot));
serial_out(up, MA35_LCR_REG, lcr);
spin_unlock_irqrestore(&up->port.lock, flags);
}
static const char *ma35d1serial_type(struct uart_port *port)
{
return "ma35d1-uart";
}
static void ma35d1serial_config_port(struct uart_port *port, int flags)
{
/*
* Driver core for serial ports forces a non-zero value for port type.
* Write an arbitrary value here to accommodate the serial core driver,
* as ID part of UAPI is redundant.
*/
port->type = 1;
}
static int ma35d1serial_verify_port(struct uart_port *port, struct serial_struct *ser)
{
if (port->type != PORT_UNKNOWN && ser->type != 1)
return -EINVAL;
return 0;
}
static const struct uart_ops ma35d1serial_ops = {
.tx_empty = ma35d1serial_tx_empty,
.set_mctrl = ma35d1serial_set_mctrl,
.get_mctrl = ma35d1serial_get_mctrl,
.stop_tx = ma35d1serial_stop_tx,
.start_tx = ma35d1serial_start_tx,
.stop_rx = ma35d1serial_stop_rx,
.break_ctl = ma35d1serial_break_ctl,
.startup = ma35d1serial_startup,
.shutdown = ma35d1serial_shutdown,
.set_termios = ma35d1serial_set_termios,
.type = ma35d1serial_type,
.config_port = ma35d1serial_config_port,
.verify_port = ma35d1serial_verify_port,
};
static const struct of_device_id ma35d1_serial_of_match[] = {
{ .compatible = "nuvoton,ma35d1-uart" },
{},
};
MODULE_DEVICE_TABLE(of, ma35d1_serial_of_match);
#ifdef CONFIG_SERIAL_NUVOTON_MA35D1_CONSOLE
static struct device_node *ma35d1serial_uart_nodes[MA35_UART_NR];
static void wait_for_xmitr(struct uart_ma35d1_port *up)
{
unsigned int reg = 0;
read_poll_timeout_atomic(serial_in, reg, reg & MA35_FSR_TX_EMPTY,
1, 10000, false,
up, MA35_FSR_REG);
}
static void ma35d1serial_console_putchar(struct uart_port *port, unsigned char ch)
{
struct uart_ma35d1_port *up = to_ma35d1_uart_port(port);
wait_for_xmitr(up);
serial_out(up, MA35_THR_REG, ch);
}
/*
* Print a string to the serial port trying not to disturb
* any possible real use of the port...
*
* The console_lock must be held when we get here.
*/
static void ma35d1serial_console_write(struct console *co, const char *s, u32 count)
{
struct uart_ma35d1_port *up = &ma35d1serial_ports[co->index];
unsigned long flags;
int locked = 1;
u32 ier;
if (up->port.sysrq)
locked = 0;
else if (oops_in_progress)
locked = spin_trylock_irqsave(&up->port.lock, flags);
else
spin_lock_irqsave(&up->port.lock, flags);
/*
* First save the IER then disable the interrupts
*/
ier = serial_in(up, MA35_IER_REG);
serial_out(up, MA35_IER_REG, 0);
uart_console_write(&up->port, s, count, ma35d1serial_console_putchar);
wait_for_xmitr(up);
serial_out(up, MA35_IER_REG, ier);
if (locked)
spin_unlock_irqrestore(&up->port.lock, flags);
}
static int __init ma35d1serial_console_setup(struct console *co, char *options)
{
struct device_node *np;
struct uart_ma35d1_port *p;
u32 val32[4];
struct uart_port *port;
int baud = 115200;
int bits = 8;
int parity = 'n';
int flow = 'n';
if ((co->index < 0) || (co->index >= MA35_UART_NR)) {
pr_debug("Console Port%x out of range\n", co->index);
return -EINVAL;
}
np = ma35d1serial_uart_nodes[co->index];
p = &ma35d1serial_ports[co->index];
if (!np || !p)
return -ENODEV;
if (of_property_read_u32_array(np, "reg", val32, ARRAY_SIZE(val32)) != 0)
return -EINVAL;
p->port.iobase = val32[1];
p->port.membase = ioremap(p->port.iobase, MA35_UART_REG_SIZE);
if (!p->port.membase)
return -ENOMEM;
p->port.ops = &ma35d1serial_ops;
p->port.line = 0;
p->port.uartclk = MA35_UART_CONSOLE_CLK;
port = &ma35d1serial_ports[co->index].port;
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
return uart_set_options(port, co, baud, parity, bits, flow);
}
static struct console ma35d1serial_console = {
.name = "ttyNVT",
.write = ma35d1serial_console_write,
.device = uart_console_device,
.setup = ma35d1serial_console_setup,
.flags = CON_PRINTBUFFER | CON_ENABLED,
.index = -1,
.data = &ma35d1serial_reg,
};
static void ma35d1serial_console_init_port(void)
{
u32 i = 0;
struct device_node *np;
for_each_matching_node(np, ma35d1_serial_of_match) {
if (ma35d1serial_uart_nodes[i] == NULL) {
of_node_get(np);
ma35d1serial_uart_nodes[i] = np;
i++;
if (i == MA35_UART_NR)
break;
}
}
}
static int __init ma35d1serial_console_init(void)
{
ma35d1serial_console_init_port();
register_console(&ma35d1serial_console);
return 0;
}
console_initcall(ma35d1serial_console_init);
#define MA35D1SERIAL_CONSOLE (&ma35d1serial_console)
#else
#define MA35D1SERIAL_CONSOLE NULL
#endif
static struct uart_driver ma35d1serial_reg = {
.owner = THIS_MODULE,
.driver_name = "serial",
.dev_name = "ttyNVT",
.major = TTY_MAJOR,
.minor = 64,
.cons = MA35D1SERIAL_CONSOLE,
.nr = MA35_UART_NR,
};
/*
* Register a set of serial devices attached to a platform device.
* The list is terminated with a zero flags entry, which means we expect
* all entries to have at least UPF_BOOT_AUTOCONF set.
*/
static int ma35d1serial_probe(struct platform_device *pdev)
{
struct resource *res_mem;
struct uart_ma35d1_port *up;
int ret = 0;
if (pdev->dev.of_node) {
ret = of_alias_get_id(pdev->dev.of_node, "serial");
if (ret < 0) {
dev_err(&pdev->dev, "failed to get alias/pdev id, errno %d\n", ret);
return ret;
}
}
up = &ma35d1serial_ports[ret];
up->port.line = ret;
res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res_mem)
return -ENODEV;
up->port.iobase = res_mem->start;
up->port.membase = ioremap(up->port.iobase, MA35_UART_REG_SIZE);
up->port.ops = &ma35d1serial_ops;
spin_lock_init(&up->port.lock);
up->clk = of_clk_get(pdev->dev.of_node, 0);
if (IS_ERR(up->clk)) {
ret = PTR_ERR(up->clk);
dev_err(&pdev->dev, "failed to get core clk: %d\n", ret);
goto err_iounmap;
}
ret = clk_prepare_enable(up->clk);
if (ret)
goto err_iounmap;
if (up->port.line != 0)
up->port.uartclk = clk_get_rate(up->clk);
ret = platform_get_irq(pdev, 0);
if (ret < 0)
goto err_clk_disable;
up->port.irq = ret;
up->port.dev = &pdev->dev;
up->port.flags = UPF_BOOT_AUTOCONF;
platform_set_drvdata(pdev, up);
ret = uart_add_one_port(&ma35d1serial_reg, &up->port);
if (ret < 0)
goto err_free_irq;
return 0;
err_free_irq:
free_irq(up->port.irq, &up->port);
err_clk_disable:
clk_disable_unprepare(up->clk);
err_iounmap:
iounmap(up->port.membase);
return ret;
}
/*
* Remove serial ports registered against a platform device.
*/
static int ma35d1serial_remove(struct platform_device *dev)
{
struct uart_port *port = platform_get_drvdata(dev);
struct uart_ma35d1_port *up = to_ma35d1_uart_port(port);
uart_remove_one_port(&ma35d1serial_reg, port);
clk_disable_unprepare(up->clk);
return 0;
}
static int ma35d1serial_suspend(struct platform_device *dev, pm_message_t state)
{
struct uart_port *port = platform_get_drvdata(dev);
struct uart_ma35d1_port *up = to_ma35d1_uart_port(port);
uart_suspend_port(&ma35d1serial_reg, &up->port);
if (up->port.line == 0) {
up->console_baud_rate = serial_in(up, MA35_BAUD_REG);
up->console_line = serial_in(up, MA35_LCR_REG);
up->console_int = serial_in(up, MA35_IER_REG);
}
return 0;
}
static int ma35d1serial_resume(struct platform_device *dev)
{
struct uart_port *port = platform_get_drvdata(dev);
struct uart_ma35d1_port *up = to_ma35d1_uart_port(port);
if (up->port.line == 0) {
serial_out(up, MA35_BAUD_REG, up->console_baud_rate);
serial_out(up, MA35_LCR_REG, up->console_line);
serial_out(up, MA35_IER_REG, up->console_int);
}
uart_resume_port(&ma35d1serial_reg, &up->port);
return 0;
}
static struct platform_driver ma35d1serial_driver = {
.probe = ma35d1serial_probe,
.remove = ma35d1serial_remove,
.suspend = ma35d1serial_suspend,
.resume = ma35d1serial_resume,
.driver = {
.name = "ma35d1-uart",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(ma35d1_serial_of_match),
},
};
static int __init ma35d1serial_init(void)
{
int ret;
ret = uart_register_driver(&ma35d1serial_reg);
if (ret)
return ret;
ret = platform_driver_register(&ma35d1serial_driver);
if (ret)
uart_unregister_driver(&ma35d1serial_reg);
return ret;
}
static void __exit ma35d1serial_exit(void)
{
platform_driver_unregister(&ma35d1serial_driver);
uart_unregister_driver(&ma35d1serial_reg);
}
module_init(ma35d1serial_init);
module_exit(ma35d1serial_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("MA35D1 serial driver");

View File

@ -1636,7 +1636,7 @@ static struct i2c_driver max310x_i2c_driver = {
.of_match_table = max310x_dt_ids,
.pm = &max310x_pm_ops,
},
.probe_new = max310x_i2c_probe,
.probe = max310x_i2c_probe,
.remove = max310x_i2c_remove,
};
#endif

View File

@ -24,6 +24,7 @@
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/math.h>
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/io.h>
@ -1459,8 +1460,12 @@ static unsigned int s3c24xx_serial_getclk(struct s3c24xx_uart_port *ourport,
continue;
rate = clk_get_rate(clk);
if (!rate)
if (!rate) {
dev_err(ourport->port.dev,
"Failed to get clock rate for %s.\n", clkname);
clk_put(clk);
continue;
}
if (ourport->info->has_divslot) {
unsigned long div = rate / req_baud;
@ -1481,15 +1486,21 @@ static unsigned int s3c24xx_serial_getclk(struct s3c24xx_uart_port *ourport,
}
quot--;
calc_deviation = req_baud - baud;
if (calc_deviation < 0)
calc_deviation = -calc_deviation;
calc_deviation = abs(req_baud - baud);
if (calc_deviation < deviation) {
/*
* If we find a better clk, release the previous one, if
* any.
*/
if (!IS_ERR(*best_clk))
clk_put(*best_clk);
*best_clk = clk;
best_quot = quot;
*clk_num = cnt;
deviation = calc_deviation;
} else {
clk_put(clk);
}
}

View File

@ -1709,7 +1709,7 @@ static struct i2c_driver sc16is7xx_i2c_uart_driver = {
.name = SC16IS7XX_NAME,
.of_match_table = sc16is7xx_dt_ids,
},
.probe_new = sc16is7xx_i2c_probe,
.probe = sc16is7xx_i2c_probe,
.remove = sc16is7xx_i2c_remove,
.id_table = sc16is7xx_i2c_id_table,
};

View File

@ -0,0 +1,46 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Serial core related functions, serial port device drivers do not need this.
*
* Copyright (C) 2023 Texas Instruments Incorporated - https://www.ti.com/
* Author: Tony Lindgren <tony@atomide.com>
*/
#define to_serial_base_ctrl_device(d) container_of((d), struct serial_ctrl_device, dev)
#define to_serial_base_port_device(d) container_of((d), struct serial_port_device, dev)
struct uart_driver;
struct uart_port;
struct device_driver;
struct device;
struct serial_ctrl_device {
struct device dev;
};
struct serial_port_device {
struct device dev;
struct uart_port *port;
};
int serial_base_ctrl_init(void);
void serial_base_ctrl_exit(void);
int serial_base_port_init(void);
void serial_base_port_exit(void);
int serial_base_driver_register(struct device_driver *driver);
void serial_base_driver_unregister(struct device_driver *driver);
struct serial_ctrl_device *serial_base_ctrl_add(struct uart_port *port,
struct device *parent);
struct serial_port_device *serial_base_port_add(struct uart_port *port,
struct serial_ctrl_device *parent);
void serial_base_ctrl_device_remove(struct serial_ctrl_device *ctrl_dev);
void serial_base_port_device_remove(struct serial_port_device *port_dev);
int serial_ctrl_register_port(struct uart_driver *drv, struct uart_port *port);
void serial_ctrl_unregister_port(struct uart_driver *drv, struct uart_port *port);
int serial_core_register_port(struct uart_driver *drv, struct uart_port *port);
void serial_core_unregister_port(struct uart_driver *drv, struct uart_port *port);

View File

@ -0,0 +1,205 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Serial base bus layer for controllers
*
* Copyright (C) 2023 Texas Instruments Incorporated - https://www.ti.com/
* Author: Tony Lindgren <tony@atomide.com>
*
* The serial core bus manages the serial core controller instances.
*/
#include <linux/container_of.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/serial_core.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include "serial_base.h"
static bool serial_base_initialized;
static int serial_base_match(struct device *dev, struct device_driver *drv)
{
int len = strlen(drv->name);
return !strncmp(dev_name(dev), drv->name, len);
}
static struct bus_type serial_base_bus_type = {
.name = "serial-base",
.match = serial_base_match,
};
int serial_base_driver_register(struct device_driver *driver)
{
driver->bus = &serial_base_bus_type;
return driver_register(driver);
}
void serial_base_driver_unregister(struct device_driver *driver)
{
driver_unregister(driver);
}
static int serial_base_device_init(struct uart_port *port,
struct device *dev,
struct device *parent_dev,
const struct device_type *type,
void (*release)(struct device *dev),
int id)
{
device_initialize(dev);
dev->type = type;
dev->parent = parent_dev;
dev->bus = &serial_base_bus_type;
dev->release = release;
if (!serial_base_initialized) {
dev_dbg(port->dev, "uart_add_one_port() called before arch_initcall()?\n");
return -EPROBE_DEFER;
}
return dev_set_name(dev, "%s.%s.%d", type->name, dev_name(port->dev), id);
}
static const struct device_type serial_ctrl_type = {
.name = "ctrl",
};
static void serial_base_ctrl_release(struct device *dev)
{
struct serial_ctrl_device *ctrl_dev = to_serial_base_ctrl_device(dev);
kfree(ctrl_dev);
}
void serial_base_ctrl_device_remove(struct serial_ctrl_device *ctrl_dev)
{
if (!ctrl_dev)
return;
device_del(&ctrl_dev->dev);
}
struct serial_ctrl_device *serial_base_ctrl_add(struct uart_port *port,
struct device *parent)
{
struct serial_ctrl_device *ctrl_dev;
int err;
ctrl_dev = kzalloc(sizeof(*ctrl_dev), GFP_KERNEL);
if (!ctrl_dev)
return ERR_PTR(-ENOMEM);
err = serial_base_device_init(port, &ctrl_dev->dev,
parent, &serial_ctrl_type,
serial_base_ctrl_release,
port->ctrl_id);
if (err)
goto err_put_device;
err = device_add(&ctrl_dev->dev);
if (err)
goto err_put_device;
return ctrl_dev;
err_put_device:
put_device(&ctrl_dev->dev);
return ERR_PTR(err);
}
static const struct device_type serial_port_type = {
.name = "port",
};
static void serial_base_port_release(struct device *dev)
{
struct serial_port_device *port_dev = to_serial_base_port_device(dev);
kfree(port_dev);
}
struct serial_port_device *serial_base_port_add(struct uart_port *port,
struct serial_ctrl_device *ctrl_dev)
{
struct serial_port_device *port_dev;
int err;
port_dev = kzalloc(sizeof(*port_dev), GFP_KERNEL);
if (!port_dev)
return ERR_PTR(-ENOMEM);
err = serial_base_device_init(port, &port_dev->dev,
&ctrl_dev->dev, &serial_port_type,
serial_base_port_release,
port->line);
if (err)
goto err_put_device;
port_dev->port = port;
err = device_add(&port_dev->dev);
if (err)
goto err_put_device;
return port_dev;
err_put_device:
put_device(&port_dev->dev);
return ERR_PTR(err);
}
void serial_base_port_device_remove(struct serial_port_device *port_dev)
{
if (!port_dev)
return;
device_del(&port_dev->dev);
}
static int serial_base_init(void)
{
int ret;
ret = bus_register(&serial_base_bus_type);
if (ret)
return ret;
ret = serial_base_ctrl_init();
if (ret)
goto err_bus_unregister;
ret = serial_base_port_init();
if (ret)
goto err_ctrl_exit;
serial_base_initialized = true;
return 0;
err_ctrl_exit:
serial_base_ctrl_exit();
err_bus_unregister:
bus_unregister(&serial_base_bus_type);
return ret;
}
arch_initcall(serial_base_init);
static void serial_base_exit(void)
{
serial_base_port_exit();
serial_base_ctrl_exit();
bus_unregister(&serial_base_bus_type);
}
module_exit(serial_base_exit);
MODULE_AUTHOR("Tony Lindgren <tony@atomide.com>");
MODULE_DESCRIPTION("Serial core bus");
MODULE_LICENSE("GPL");

View File

@ -17,6 +17,7 @@
#include <linux/gpio/consumer.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/pm_runtime.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/device.h>
@ -31,6 +32,8 @@
#include <linux/irq.h>
#include <linux/uaccess.h>
#include "serial_base.h"
/*
* This is used to lock changes in serial line configuration.
*/
@ -134,9 +137,30 @@ static void __uart_start(struct tty_struct *tty)
{
struct uart_state *state = tty->driver_data;
struct uart_port *port = state->uart_port;
struct serial_port_device *port_dev;
int err;
if (port && !(port->flags & UPF_DEAD) && !uart_tx_stopped(port))
if (!port || port->flags & UPF_DEAD || uart_tx_stopped(port))
return;
port_dev = port->port_dev;
/* Increment the runtime PM usage count for the active check below */
err = pm_runtime_get(&port_dev->dev);
if (err < 0) {
pm_runtime_put_noidle(&port_dev->dev);
return;
}
/*
* Start TX if enabled, and kick runtime PM. If the device is not
* enabled, serial_port_runtime_resume() calls start_tx() again
* after enabling the device.
*/
if (pm_runtime_active(&port_dev->dev))
port->ops->start_tx(port);
pm_runtime_mark_last_busy(&port_dev->dev);
pm_runtime_put_autosuspend(&port_dev->dev);
}
static void uart_start(struct tty_struct *tty)
@ -2333,8 +2357,11 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport)
* able to Re-start_rx later.
*/
if (!console_suspend_enabled && uart_console(uport)) {
if (uport->ops->start_rx)
if (uport->ops->start_rx) {
spin_lock_irq(&uport->lock);
uport->ops->stop_rx(uport);
spin_unlock_irq(&uport->lock);
}
goto unlock;
}
@ -2427,8 +2454,11 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport)
if (console_suspend_enabled)
uart_change_pm(state, UART_PM_STATE_ON);
uport->ops->set_termios(uport, &termios, NULL);
if (!console_suspend_enabled && uport->ops->start_rx)
if (!console_suspend_enabled && uport->ops->start_rx) {
spin_lock_irq(&uport->lock);
uport->ops->start_rx(uport);
spin_unlock_irq(&uport->lock);
}
if (console_suspend_enabled)
console_start(uport->cons);
}
@ -3042,7 +3072,7 @@ static const struct attribute_group tty_dev_attr_group = {
};
/**
* uart_add_one_port - attach a driver-defined port structure
* serial_core_add_one_port - attach a driver-defined port structure
* @drv: pointer to the uart low level driver structure for this port
* @uport: uart port structure to use for this port.
*
@ -3051,8 +3081,9 @@ static const struct attribute_group tty_dev_attr_group = {
* This allows the driver @drv to register its own uart_port structure with the
* core driver. The main purpose is to allow the low level uart drivers to
* expand uart_port, rather than having yet more levels of structures.
* Caller must hold port_mutex.
*/
int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)
static int serial_core_add_one_port(struct uart_driver *drv, struct uart_port *uport)
{
struct uart_state *state;
struct tty_port *port;
@ -3066,7 +3097,6 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)
state = drv->state + uport->line;
port = &state->port;
mutex_lock(&port_mutex);
mutex_lock(&port->mutex);
if (state->uart_port) {
ret = -EINVAL;
@ -3131,21 +3161,14 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)
uport->line);
}
/*
* Ensure UPF_DEAD is not set.
*/
uport->flags &= ~UPF_DEAD;
out:
mutex_unlock(&port->mutex);
mutex_unlock(&port_mutex);
return ret;
}
EXPORT_SYMBOL(uart_add_one_port);
/**
* uart_remove_one_port - detach a driver defined port structure
* serial_core_remove_one_port - detach a driver defined port structure
* @drv: pointer to the uart low level driver structure for this port
* @uport: uart port structure for this port
*
@ -3153,21 +3176,16 @@ EXPORT_SYMBOL(uart_add_one_port);
*
* This unhooks (and hangs up) the specified port structure from the core
* driver. No further calls will be made to the low-level code for this port.
* Caller must hold port_mutex.
*/
int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport)
static void serial_core_remove_one_port(struct uart_driver *drv,
struct uart_port *uport)
{
struct uart_state *state = drv->state + uport->line;
struct tty_port *port = &state->port;
struct uart_port *uart_port;
struct tty_struct *tty;
int ret = 0;
mutex_lock(&port_mutex);
/*
* Mark the port "dead" - this prevents any opens from
* succeeding while we shut down the port.
*/
mutex_lock(&port->mutex);
uart_port = uart_port_check(state);
if (uart_port != uport)
@ -3176,10 +3194,8 @@ int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport)
if (!uart_port) {
mutex_unlock(&port->mutex);
ret = -EINVAL;
goto out;
return;
}
uport->flags |= UPF_DEAD;
mutex_unlock(&port->mutex);
/*
@ -3211,18 +3227,14 @@ int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport)
* Indicate that there isn't a port here anymore.
*/
uport->type = PORT_UNKNOWN;
uport->port_dev = NULL;
mutex_lock(&port->mutex);
WARN_ON(atomic_dec_return(&state->refcount) < 0);
wait_event(state->remove_wait, !atomic_read(&state->refcount));
state->uart_port = NULL;
mutex_unlock(&port->mutex);
out:
mutex_unlock(&port_mutex);
return ret;
}
EXPORT_SYMBOL(uart_remove_one_port);
/**
* uart_match_port - are the two ports equivalent?
@ -3257,6 +3269,144 @@ bool uart_match_port(const struct uart_port *port1,
}
EXPORT_SYMBOL(uart_match_port);
static struct serial_ctrl_device *
serial_core_get_ctrl_dev(struct serial_port_device *port_dev)
{
struct device *dev = &port_dev->dev;
return to_serial_base_ctrl_device(dev->parent);
}
/*
* Find a registered serial core controller device if one exists. Returns
* the first device matching the ctrl_id. Caller must hold port_mutex.
*/
static struct serial_ctrl_device *serial_core_ctrl_find(struct uart_driver *drv,
struct device *phys_dev,
int ctrl_id)
{
struct uart_state *state;
int i;
lockdep_assert_held(&port_mutex);
for (i = 0; i < drv->nr; i++) {
state = drv->state + i;
if (!state->uart_port || !state->uart_port->port_dev)
continue;
if (state->uart_port->dev == phys_dev &&
state->uart_port->ctrl_id == ctrl_id)
return serial_core_get_ctrl_dev(state->uart_port->port_dev);
}
return NULL;
}
static struct serial_ctrl_device *serial_core_ctrl_device_add(struct uart_port *port)
{
return serial_base_ctrl_add(port, port->dev);
}
static int serial_core_port_device_add(struct serial_ctrl_device *ctrl_dev,
struct uart_port *port)
{
struct serial_port_device *port_dev;
port_dev = serial_base_port_add(port, ctrl_dev);
if (IS_ERR(port_dev))
return PTR_ERR(port_dev);
port->port_dev = port_dev;
return 0;
}
/*
* Initialize a serial core port device, and a controller device if needed.
*/
int serial_core_register_port(struct uart_driver *drv, struct uart_port *port)
{
struct serial_ctrl_device *ctrl_dev, *new_ctrl_dev = NULL;
int ret;
mutex_lock(&port_mutex);
/*
* Prevent serial_port_runtime_resume() from trying to use the port
* until serial_core_add_one_port() has completed
*/
port->flags |= UPF_DEAD;
/* Inititalize a serial core controller device if needed */
ctrl_dev = serial_core_ctrl_find(drv, port->dev, port->ctrl_id);
if (!ctrl_dev) {
new_ctrl_dev = serial_core_ctrl_device_add(port);
if (IS_ERR(new_ctrl_dev)) {
ret = PTR_ERR(new_ctrl_dev);
goto err_unlock;
}
ctrl_dev = new_ctrl_dev;
}
/*
* Initialize a serial core port device. Tag the port dead to prevent
* serial_port_runtime_resume() trying to do anything until port has
* been registered. It gets cleared by serial_core_add_one_port().
*/
ret = serial_core_port_device_add(ctrl_dev, port);
if (ret)
goto err_unregister_ctrl_dev;
ret = serial_core_add_one_port(drv, port);
if (ret)
goto err_unregister_port_dev;
port->flags &= ~UPF_DEAD;
mutex_unlock(&port_mutex);
return 0;
err_unregister_port_dev:
serial_base_port_device_remove(port->port_dev);
err_unregister_ctrl_dev:
serial_base_ctrl_device_remove(new_ctrl_dev);
err_unlock:
mutex_unlock(&port_mutex);
return ret;
}
/*
* Removes a serial core port device, and the related serial core controller
* device if the last instance.
*/
void serial_core_unregister_port(struct uart_driver *drv, struct uart_port *port)
{
struct device *phys_dev = port->dev;
struct serial_port_device *port_dev = port->port_dev;
struct serial_ctrl_device *ctrl_dev = serial_core_get_ctrl_dev(port_dev);
int ctrl_id = port->ctrl_id;
mutex_lock(&port_mutex);
port->flags |= UPF_DEAD;
serial_core_remove_one_port(drv, port);
/* Note that struct uart_port *port is no longer valid at this point */
serial_base_port_device_remove(port_dev);
/* Drop the serial core controller device if no ports are using it */
if (!serial_core_ctrl_find(drv, phys_dev, ctrl_id))
serial_base_ctrl_device_remove(ctrl_dev);
mutex_unlock(&port_mutex);
}
/**
* uart_handle_dcd_change - handle a change of carrier detect state
* @uport: uart_port structure for the open port

View File

@ -0,0 +1,68 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Serial core controller driver
*
* Copyright (C) 2023 Texas Instruments Incorporated - https://www.ti.com/
* Author: Tony Lindgren <tony@atomide.com>
*
* This driver manages the serial core controller struct device instances.
* The serial core controller devices are children of the physical serial
* port device.
*/
#include <linux/device.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/serial_core.h>
#include <linux/spinlock.h>
#include "serial_base.h"
static int serial_ctrl_probe(struct device *dev)
{
pm_runtime_enable(dev);
return 0;
}
static int serial_ctrl_remove(struct device *dev)
{
pm_runtime_disable(dev);
return 0;
}
/*
* Serial core controller device init functions. Note that the physical
* serial port device driver may not have completed probe at this point.
*/
int serial_ctrl_register_port(struct uart_driver *drv, struct uart_port *port)
{
return serial_core_register_port(drv, port);
}
void serial_ctrl_unregister_port(struct uart_driver *drv, struct uart_port *port)
{
serial_core_unregister_port(drv, port);
}
static struct device_driver serial_ctrl_driver = {
.name = "ctrl",
.suppress_bind_attrs = true,
.probe = serial_ctrl_probe,
.remove = serial_ctrl_remove,
};
int serial_base_ctrl_init(void)
{
return serial_base_driver_register(&serial_ctrl_driver);
}
void serial_base_ctrl_exit(void)
{
serial_base_driver_unregister(&serial_ctrl_driver);
}
MODULE_AUTHOR("Tony Lindgren <tony@atomide.com>");
MODULE_DESCRIPTION("Serial core controller driver");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,105 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Serial core port device driver
*
* Copyright (C) 2023 Texas Instruments Incorporated - https://www.ti.com/
* Author: Tony Lindgren <tony@atomide.com>
*/
#include <linux/device.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/serial_core.h>
#include <linux/spinlock.h>
#include "serial_base.h"
#define SERIAL_PORT_AUTOSUSPEND_DELAY_MS 500
/* Only considers pending TX for now. Caller must take care of locking */
static int __serial_port_busy(struct uart_port *port)
{
return !uart_tx_stopped(port) &&
uart_circ_chars_pending(&port->state->xmit);
}
static int serial_port_runtime_resume(struct device *dev)
{
struct serial_port_device *port_dev = to_serial_base_port_device(dev);
struct uart_port *port;
unsigned long flags;
port = port_dev->port;
if (port->flags & UPF_DEAD)
goto out;
/* Flush any pending TX for the port */
spin_lock_irqsave(&port->lock, flags);
if (__serial_port_busy(port))
port->ops->start_tx(port);
spin_unlock_irqrestore(&port->lock, flags);
out:
pm_runtime_mark_last_busy(dev);
return 0;
}
static DEFINE_RUNTIME_DEV_PM_OPS(serial_port_pm,
NULL, serial_port_runtime_resume, NULL);
static int serial_port_probe(struct device *dev)
{
pm_runtime_enable(dev);
pm_runtime_set_autosuspend_delay(dev, SERIAL_PORT_AUTOSUSPEND_DELAY_MS);
pm_runtime_use_autosuspend(dev);
return 0;
}
static int serial_port_remove(struct device *dev)
{
pm_runtime_dont_use_autosuspend(dev);
pm_runtime_disable(dev);
return 0;
}
/*
* Serial core port device init functions. Note that the physical serial
* port device driver may not have completed probe at this point.
*/
int uart_add_one_port(struct uart_driver *drv, struct uart_port *port)
{
return serial_ctrl_register_port(drv, port);
}
EXPORT_SYMBOL(uart_add_one_port);
void uart_remove_one_port(struct uart_driver *drv, struct uart_port *port)
{
serial_ctrl_unregister_port(drv, port);
}
EXPORT_SYMBOL(uart_remove_one_port);
static struct device_driver serial_port_driver = {
.name = "port",
.suppress_bind_attrs = true,
.probe = serial_port_probe,
.remove = serial_port_remove,
.pm = pm_ptr(&serial_port_pm),
};
int serial_base_port_init(void)
{
return serial_base_driver_register(&serial_port_driver);
}
void serial_base_port_exit(void)
{
serial_base_driver_unregister(&serial_port_driver);
}
MODULE_AUTHOR("Tony Lindgren <tony@atomide.com>");
MODULE_DESCRIPTION("Serial controller port driver");
MODULE_LICENSE("GPL");

View File

@ -754,7 +754,7 @@ static struct asc_port *asc_of_get_asc_port(struct platform_device *pdev)
asc_ports[id].hw_flow_control = of_property_read_bool(np,
"uart-has-rtscts");
asc_ports[id].force_m1 = of_property_read_bool(np, "st,force_m1");
asc_ports[id].force_m1 = of_property_read_bool(np, "st,force-m1");
asc_ports[id].port.line = id;
asc_ports[id].rts = NULL;
@ -796,7 +796,9 @@ static int asc_serial_remove(struct platform_device *pdev)
{
struct uart_port *port = platform_get_drvdata(pdev);
return uart_remove_one_port(&asc_uart_driver, port);
uart_remove_one_port(&asc_uart_driver, port);
return 0;
}
#ifdef CONFIG_PM_SLEEP

View File

@ -1755,13 +1755,10 @@ static int stm32_usart_serial_remove(struct platform_device *pdev)
struct uart_port *port = platform_get_drvdata(pdev);
struct stm32_port *stm32_port = to_stm32_port(port);
const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
int err;
u32 cr3;
pm_runtime_get_sync(&pdev->dev);
err = uart_remove_one_port(&stm32_usart_driver, port);
if (err)
return(err);
uart_remove_one_port(&stm32_usart_driver, port);
pm_runtime_disable(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev);

View File

@ -685,18 +685,15 @@ static int ulite_assign(struct device *dev, int id, phys_addr_t base, int irq,
*
* @dev: pointer to device structure
*/
static int ulite_release(struct device *dev)
static void ulite_release(struct device *dev)
{
struct uart_port *port = dev_get_drvdata(dev);
int rc = 0;
if (port) {
rc = uart_remove_one_port(&ulite_uart_driver, port);
uart_remove_one_port(&ulite_uart_driver, port);
dev_set_drvdata(dev, NULL);
port->mapbase = 0;
}
return rc;
}
/**
@ -900,14 +897,13 @@ static int ulite_remove(struct platform_device *pdev)
{
struct uart_port *port = dev_get_drvdata(&pdev->dev);
struct uartlite_data *pdata = port->private_data;
int rc;
clk_disable_unprepare(pdata->clk);
rc = ulite_release(&pdev->dev);
ulite_release(&pdev->dev);
pm_runtime_disable(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev);
pm_runtime_dont_use_autosuspend(&pdev->dev);
return rc;
return 0;
}
/* work with hotplug and coldplug */

View File

@ -1670,14 +1670,13 @@ static int cdns_uart_remove(struct platform_device *pdev)
{
struct uart_port *port = platform_get_drvdata(pdev);
struct cdns_uart *cdns_uart_data = port->private_data;
int rc;
/* Remove the cdns_uart port from the serial core */
#ifdef CONFIG_COMMON_CLK
clk_notifier_unregister(cdns_uart_data->uartclk,
&cdns_uart_data->clk_rate_change_nb);
#endif
rc = uart_remove_one_port(cdns_uart_data->cdns_uart_driver, port);
uart_remove_one_port(cdns_uart_data->cdns_uart_driver, port);
port->mapbase = 0;
clk_disable_unprepare(cdns_uart_data->uartclk);
clk_disable_unprepare(cdns_uart_data->pclk);
@ -1693,7 +1692,7 @@ static int cdns_uart_remove(struct platform_device *pdev)
if (!--instances)
uart_unregister_driver(cdns_uart_data->cdns_uart_driver);
return rc;
return 0;
}
static struct platform_driver cdns_uart_platform_driver = {

View File

@ -99,14 +99,15 @@ extern int tty_ldisc_autoload;
/* tty_audit.c */
#ifdef CONFIG_AUDIT
void tty_audit_add_data(struct tty_struct *tty, const void *data, size_t size);
void tty_audit_tiocsti(struct tty_struct *tty, char ch);
void tty_audit_add_data(const struct tty_struct *tty, const void *data,
size_t size);
void tty_audit_tiocsti(const struct tty_struct *tty, char ch);
#else
static inline void tty_audit_add_data(struct tty_struct *tty, const void *data,
size_t size)
static inline void tty_audit_add_data(const struct tty_struct *tty,
const void *data, size_t size)
{
}
static inline void tty_audit_tiocsti(struct tty_struct *tty, char ch)
static inline void tty_audit_tiocsti(const struct tty_struct *tty, char ch)
{
}
#endif

View File

@ -15,7 +15,7 @@
struct tty_audit_buf {
struct mutex mutex; /* Protects all data below */
dev_t dev; /* The TTY which the data is from */
unsigned icanon:1;
bool icanon;
size_t valid;
unsigned char *data; /* Allocated size N_TTY_BUF_SIZE */
};
@ -33,16 +33,16 @@ static struct tty_audit_buf *tty_audit_buf_alloc(void)
{
struct tty_audit_buf *buf;
buf = kmalloc(sizeof(*buf), GFP_KERNEL);
buf = kzalloc(sizeof(*buf), GFP_KERNEL);
if (!buf)
goto err;
buf->data = kmalloc(N_TTY_BUF_SIZE, GFP_KERNEL);
if (!buf->data)
goto err_buf;
mutex_init(&buf->mutex);
buf->dev = MKDEV(0, 0);
buf->icanon = 0;
buf->valid = 0;
return buf;
err_buf:
@ -59,27 +59,27 @@ static void tty_audit_buf_free(struct tty_audit_buf *buf)
}
static void tty_audit_log(const char *description, dev_t dev,
unsigned char *data, size_t size)
const unsigned char *data, size_t size)
{
struct audit_buffer *ab;
pid_t pid = task_pid_nr(current);
uid_t uid = from_kuid(&init_user_ns, task_uid(current));
uid_t loginuid = from_kuid(&init_user_ns, audit_get_loginuid(current));
unsigned int sessionid = audit_get_sessionid(current);
char name[TASK_COMM_LEN];
ab = audit_log_start(audit_context(), GFP_KERNEL, AUDIT_TTY);
if (ab) {
char name[sizeof(current->comm)];
if (!ab)
return;
audit_log_format(ab, "%s pid=%u uid=%u auid=%u ses=%u major=%d"
" minor=%d comm=", description, pid, uid,
loginuid, sessionid, MAJOR(dev), MINOR(dev));
get_task_comm(name, current);
audit_log_untrustedstring(ab, name);
audit_log_format(ab, " data=");
audit_log_n_hex(ab, data, size);
audit_log_end(ab);
}
audit_log_format(ab, "%s pid=%u uid=%u auid=%u ses=%u major=%d minor=%d comm=",
description, pid, uid, loginuid, sessionid,
MAJOR(dev), MINOR(dev));
get_task_comm(name, current);
audit_log_untrustedstring(ab, name);
audit_log_format(ab, " data=");
audit_log_n_hex(ab, data, size);
audit_log_end(ab);
}
/*
@ -134,7 +134,7 @@ void tty_audit_fork(struct signal_struct *sig)
/*
* tty_audit_tiocsti - Log TIOCSTI
*/
void tty_audit_tiocsti(struct tty_struct *tty, char ch)
void tty_audit_tiocsti(const struct tty_struct *tty, char ch)
{
dev_t dev;
@ -199,11 +199,12 @@ static struct tty_audit_buf *tty_audit_buf_get(void)
*
* Audit @data of @size from @tty, if necessary.
*/
void tty_audit_add_data(struct tty_struct *tty, const void *data, size_t size)
void tty_audit_add_data(const struct tty_struct *tty, const void *data,
size_t size)
{
struct tty_audit_buf *buf;
unsigned int icanon = !!L_ICANON(tty);
unsigned int audit_tty;
bool icanon = L_ICANON(tty);
dev_t dev;
audit_tty = READ_ONCE(current->signal->audit_tty);

View File

@ -101,6 +101,7 @@
#include <linux/compat.h>
#include <linux/uaccess.h>
#include <linux/termios_internal.h>
#include <linux/fs.h>
#include <linux/kbd_kern.h>
#include <linux/vt_kern.h>
@ -811,18 +812,26 @@ void start_tty(struct tty_struct *tty)
}
EXPORT_SYMBOL(start_tty);
static void tty_update_time(struct timespec64 *time)
static void tty_update_time(struct tty_struct *tty, bool mtime)
{
time64_t sec = ktime_get_real_seconds();
struct tty_file_private *priv;
/*
* We only care if the two values differ in anything other than the
* lower three bits (i.e every 8 seconds). If so, then we can update
* the time of the tty device, otherwise it could be construded as a
* security leak to let userspace know the exact timing of the tty.
*/
if ((sec ^ time->tv_sec) & ~7)
time->tv_sec = sec;
spin_lock(&tty->files_lock);
list_for_each_entry(priv, &tty->tty_files, list) {
struct inode *inode = file_inode(priv->file);
struct timespec64 *time = mtime ? &inode->i_mtime : &inode->i_atime;
/*
* We only care if the two values differ in anything other than the
* lower three bits (i.e every 8 seconds). If so, then we can update
* the time of the tty device, otherwise it could be construded as a
* security leak to let userspace know the exact timing of the tty.
*/
if ((sec ^ time->tv_sec) & ~7)
time->tv_sec = sec;
}
spin_unlock(&tty->files_lock);
}
/*
@ -928,7 +937,7 @@ static ssize_t tty_read(struct kiocb *iocb, struct iov_iter *to)
tty_ldisc_deref(ld);
if (i > 0)
tty_update_time(&inode->i_atime);
tty_update_time(tty, false);
return i;
}
@ -1036,7 +1045,7 @@ static inline ssize_t do_tty_write(
cond_resched();
}
if (written) {
tty_update_time(&file_inode(file)->i_mtime);
tty_update_time(tty, true);
ret = written;
}
out:

View File

@ -7,17 +7,34 @@
#ifndef _LINUX_SERIAL_8250_H
#define _LINUX_SERIAL_8250_H
#include <linux/errno.h>
#include <linux/serial_core.h>
#include <linux/serial_reg.h>
#include <linux/platform_device.h>
struct uart_8250_port;
/*
* This is the platform device platform_data structure
*
* @mapsize: Port size for ioremap()
* @bugs: Port bugs
*
* @dl_read: ``u32 ()(struct uart_8250_port *up)``
*
* UART divisor latch read.
*
* @dl_write: ``void ()(struct uart_8250_port *up, u32 value)``
*
* Write @value into UART divisor latch.
*
* Locking: Caller holds port's lock.
*/
struct plat_serial8250_port {
unsigned long iobase; /* io base address */
void __iomem *membase; /* ioremap cookie or NULL */
resource_size_t mapbase; /* resource base */
resource_size_t mapsize;
unsigned int uartclk; /* UART clock rate */
unsigned int irq; /* interrupt number */
unsigned long irqflags; /* request_irq flags */
@ -28,8 +45,11 @@ struct plat_serial8250_port {
unsigned char has_sysrq; /* supports magic SysRq */
unsigned int type; /* If UPF_FIXED_TYPE */
upf_t flags; /* UPF_* flags */
u16 bugs; /* port bugs */
unsigned int (*serial_in)(struct uart_port *, int);
void (*serial_out)(struct uart_port *, int, int);
u32 (*dl_read)(struct uart_8250_port *up);
void (*dl_write)(struct uart_8250_port *up, u32 value);
void (*set_termios)(struct uart_port *,
struct ktermios *new,
const struct ktermios *old);
@ -90,15 +110,23 @@ struct uart_8250_em485 {
* their own 8250 ports without registering their own
* platform device. Using these will make your driver
* dependent on the 8250 driver.
*
* @dl_read: ``u32 ()(struct uart_8250_port *port)``
*
* UART divisor latch read.
*
* @dl_write: ``void ()(struct uart_8250_port *port, u32 value)``
*
* Write @value into UART divisor latch.
*
* Locking: Caller holds port's lock.
*/
struct uart_8250_port {
struct uart_port port;
struct timer_list timer; /* "no irq" timer */
struct list_head list; /* ports on this IRQ */
u32 capabilities; /* port capabilities */
unsigned short bugs; /* port bugs */
bool fifo_bug; /* min RX trigger if enabled */
u16 bugs; /* port bugs */
unsigned int tx_loadsz; /* transmit fifo load size */
unsigned char acr;
unsigned char fcr;
@ -129,8 +157,8 @@ struct uart_8250_port {
const struct uart_8250_ops *ops;
/* 8250 specific callbacks */
int (*dl_read)(struct uart_8250_port *);
void (*dl_write)(struct uart_8250_port *, int);
u32 (*dl_read)(struct uart_8250_port *up);
void (*dl_write)(struct uart_8250_port *up, u32 value);
struct uart_8250_em485 *em485;
void (*rs485_start_tx)(struct uart_8250_port *);
@ -183,8 +211,11 @@ void serial8250_set_isa_configurator(void (*v)(int port, struct uart_port *up,
u32 *capabilities));
#ifdef CONFIG_SERIAL_8250_RT288X
unsigned int au_serial_in(struct uart_port *p, int offset);
void au_serial_out(struct uart_port *p, int offset, int value);
int rt288x_setup(struct uart_port *p);
int au_platform_setup(struct plat_serial8250_port *p);
#else
static inline int rt288x_setup(struct uart_port *p) { return -ENODEV; }
static inline int au_platform_setup(struct plat_serial8250_port *p) { return -ENODEV; }
#endif
#endif

View File

@ -28,6 +28,7 @@
struct uart_port;
struct serial_struct;
struct serial_port_device;
struct device;
struct gpio_desc;
@ -458,6 +459,7 @@ struct uart_port {
struct serial_rs485 *rs485);
int (*iso7816_config)(struct uart_port *,
struct serial_iso7816 *iso7816);
int ctrl_id; /* optional serial core controller id */
unsigned int irq; /* irq number */
unsigned long irqflags; /* irq flags */
unsigned int uartclk; /* base uart clock */
@ -563,7 +565,8 @@ struct uart_port {
unsigned int minor;
resource_size_t mapbase; /* for ioremap */
resource_size_t mapsize;
struct device *dev; /* parent device */
struct device *dev; /* serial port physical parent device */
struct serial_port_device *port_dev; /* serial core port device */
unsigned long sysrq; /* sysrq timeout */
unsigned int sysrq_ch; /* char for sysrq */
@ -853,7 +856,7 @@ void uart_console_write(struct uart_port *port, const char *s,
int uart_register_driver(struct uart_driver *uart);
void uart_unregister_driver(struct uart_driver *uart);
int uart_add_one_port(struct uart_driver *reg, struct uart_port *port);
int uart_remove_one_port(struct uart_driver *reg, struct uart_port *port);
void uart_remove_one_port(struct uart_driver *reg, struct uart_port *port);
bool uart_match_port(const struct uart_port *port1,
const struct uart_port *port2);

View File

@ -87,6 +87,7 @@ TARGETS += timers
endif
TARGETS += tmpfs
TARGETS += tpm2
TARGETS += tty
TARGETS += user
TARGETS += vDSO
TARGETS += mm

View File

@ -0,0 +1,2 @@
# SPDX-License-Identifier: GPL-2.0-only
tty_tstamp_update

View File

@ -0,0 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
CFLAGS = -O2 -Wall
TEST_GEN_PROGS := tty_tstamp_update
include ../lib.mk

View File

@ -0,0 +1,88 @@
// SPDX-License-Identifier: GPL-2.0
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/limits.h>
#include "../kselftest.h"
#define MIN_TTY_PATH_LEN 8
static bool tty_valid(char *tty)
{
if (strlen(tty) < MIN_TTY_PATH_LEN)
return false;
if (strncmp(tty, "/dev/tty", MIN_TTY_PATH_LEN) == 0 ||
strncmp(tty, "/dev/pts", MIN_TTY_PATH_LEN) == 0)
return true;
return false;
}
static int write_dev_tty(void)
{
FILE *f;
int r = 0;
f = fopen("/dev/tty", "r+");
if (!f)
return -errno;
r = fprintf(f, "hello, world!\n");
if (r != strlen("hello, world!\n"))
r = -EIO;
fclose(f);
return r;
}
int main(int argc, char **argv)
{
int r;
char tty[PATH_MAX] = {};
struct stat st1, st2;
ksft_print_header();
ksft_set_plan(1);
r = readlink("/proc/self/fd/0", tty, PATH_MAX);
if (r < 0)
ksft_exit_fail_msg("readlink on /proc/self/fd/0 failed: %m\n");
if (!tty_valid(tty))
ksft_exit_skip("invalid tty path '%s'\n", tty);
r = stat(tty, &st1);
if (r < 0)
ksft_exit_fail_msg("stat failed on tty path '%s': %m\n", tty);
/* We need to wait at least 8 seconds in order to observe timestamp change */
/* https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=fbf47635315ab308c9b58a1ea0906e711a9228de */
sleep(10);
r = write_dev_tty();
if (r < 0)
ksft_exit_fail_msg("failed to write to /dev/tty: %s\n",
strerror(-r));
r = stat(tty, &st2);
if (r < 0)
ksft_exit_fail_msg("stat failed on tty path '%s': %m\n", tty);
/* We wrote to the terminal so timestamps should have been updated */
if (st1.st_atim.tv_sec == st2.st_atim.tv_sec &&
st1.st_mtim.tv_sec == st2.st_mtim.tv_sec) {
ksft_test_result_fail("tty timestamps not updated\n");
ksft_exit_fail();
}
ksft_test_result_pass(
"timestamps of terminal '%s' updated after write to /dev/tty\n", tty);
return EXIT_SUCCESS;
}