TTY/Serial driver fixes for 5.6-rc3

Here are a number of small tty and serial driver fixes for 5.6-rc3 that
 resolve a bunch of reported issues.
 
 They are:
   - vt selection and ioctl fixes
   - serdev bugfix
   - atmel serial driver fixes
   - qcom serial driver fixes
   - other minor serial driver fixes
 
 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-----
 
 iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCXk+/Dw8cZ3JlZ0Brcm9h
 aC5jb20ACgkQMUfUDdst+ymGXgCfcYfb71LB7KbExN4GFETm93pca+QAoI+ihzFw
 zQJFJAe/dmiP2TifAdY7
 =oeU9
 -----END PGP SIGNATURE-----

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

Pull tty/serial driver fixes from Greg KH:
 "Here are a number of small tty and serial driver fixes for 5.6-rc3
  that resolve a bunch of reported issues.

  They are:
   - vt selection and ioctl fixes
   - serdev bugfix
   - atmel serial driver fixes
   - qcom serial driver fixes
   - other minor serial driver fixes

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

* tag 'tty-5.6-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty:
  vt: selection, close sel_buffer race
  vt: selection, handle pending signals in paste_selection
  serial: cpm_uart: call cpm_muram_init before registering console
  tty: serial: qcom_geni_serial: Fix RX cancel command failure
  serial: 8250: Check UPF_IRQ_SHARED in advance
  tty: serial: imx: setup the correct sg entry for tx dma
  vt: vt_ioctl: fix race in VT_RESIZEX
  vt: fix scrollback flushing on background consoles
  tty: serial: tegra: Handle RX transfer in PIO mode if DMA wasn't started
  tty/serial: atmel: manage shutdown in case of RS485 or ISO7816 mode
  serdev: ttyport: restore client ops on deregistration
  serial: ar933x_uart: set UART_CS_{RX,TX}_READY_ORIDE
This commit is contained in:
Linus Torvalds 2020-02-21 12:48:29 -08:00
commit ef11f1b76a
16 changed files with 104 additions and 51 deletions

View File

@ -265,7 +265,6 @@ struct device *serdev_tty_port_register(struct tty_port *port,
struct device *parent,
struct tty_driver *drv, int idx)
{
const struct tty_port_client_operations *old_ops;
struct serdev_controller *ctrl;
struct serport *serport;
int ret;
@ -284,7 +283,6 @@ struct device *serdev_tty_port_register(struct tty_port *port,
ctrl->ops = &ctrl_ops;
old_ops = port->client_ops;
port->client_ops = &client_ops;
port->client_data = ctrl;
@ -297,7 +295,7 @@ struct device *serdev_tty_port_register(struct tty_port *port,
err_reset_data:
port->client_data = NULL;
port->client_ops = old_ops;
port->client_ops = &tty_port_default_client_ops;
serdev_controller_put(ctrl);
return ERR_PTR(ret);
@ -312,8 +310,8 @@ int serdev_tty_port_unregister(struct tty_port *port)
return -ENODEV;
serdev_controller_remove(ctrl);
port->client_ops = NULL;
port->client_data = NULL;
port->client_ops = &tty_port_default_client_ops;
serdev_controller_put(ctrl);
return 0;

View File

@ -446,7 +446,6 @@ static int aspeed_vuart_probe(struct platform_device *pdev)
port.port.line = rc;
port.port.irq = irq_of_parse_and_map(np, 0);
port.port.irqflags = IRQF_SHARED;
port.port.handle_irq = aspeed_vuart_handle_irq;
port.port.iotype = UPIO_MEM;
port.port.type = PORT_16550A;

View File

@ -174,7 +174,7 @@ static int serial_link_irq_chain(struct uart_8250_port *up)
struct hlist_head *h;
struct hlist_node *n;
struct irq_info *i;
int ret, irq_flags = up->port.flags & UPF_SHARE_IRQ ? IRQF_SHARED : 0;
int ret;
mutex_lock(&hash_mutex);
@ -209,9 +209,8 @@ static int serial_link_irq_chain(struct uart_8250_port *up)
INIT_LIST_HEAD(&up->list);
i->head = &up->list;
spin_unlock_irq(&i->lock);
irq_flags |= up->port.irqflags;
ret = request_irq(up->port.irq, serial8250_interrupt,
irq_flags, up->port.name, i);
up->port.irqflags, up->port.name, i);
if (ret < 0)
serial_do_unlink(i, up);
}

View File

@ -202,7 +202,6 @@ static int of_platform_serial_setup(struct platform_device *ofdev,
port->type = type;
port->uartclk = clk;
port->irqflags |= IRQF_SHARED;
if (of_property_read_bool(np, "no-loopback-test"))
port->flags |= UPF_SKIP_TEST;

View File

@ -2177,6 +2177,10 @@ int serial8250_do_startup(struct uart_port *port)
}
}
/* Check if we need to have shared IRQs */
if (port->irq && (up->port.flags & UPF_SHARE_IRQ))
up->port.irqflags |= IRQF_SHARED;
if (port->irq && !(up->port.flags & UPF_NO_THRE_TEST)) {
unsigned char iir1;
/*

View File

@ -286,6 +286,10 @@ static void ar933x_uart_set_termios(struct uart_port *port,
ar933x_uart_rmw_set(up, AR933X_UART_CS_REG,
AR933X_UART_CS_HOST_INT_EN);
/* enable RX and TX ready overide */
ar933x_uart_rmw_set(up, AR933X_UART_CS_REG,
AR933X_UART_CS_TX_READY_ORIDE | AR933X_UART_CS_RX_READY_ORIDE);
/* reenable the UART */
ar933x_uart_rmw(up, AR933X_UART_CS_REG,
AR933X_UART_CS_IF_MODE_M << AR933X_UART_CS_IF_MODE_S,
@ -418,6 +422,10 @@ static int ar933x_uart_startup(struct uart_port *port)
ar933x_uart_rmw_set(up, AR933X_UART_CS_REG,
AR933X_UART_CS_HOST_INT_EN);
/* enable RX and TX ready overide */
ar933x_uart_rmw_set(up, AR933X_UART_CS_REG,
AR933X_UART_CS_TX_READY_ORIDE | AR933X_UART_CS_RX_READY_ORIDE);
/* Enable RX interrupts */
up->ier = AR933X_UART_INT_RX_VALID;
ar933x_uart_write(up, AR933X_UART_INT_EN_REG, up->ier);

View File

@ -570,7 +570,8 @@ static void atmel_stop_tx(struct uart_port *port)
atmel_uart_writel(port, ATMEL_US_IDR, atmel_port->tx_done_mask);
if (atmel_uart_is_half_duplex(port))
atmel_start_rx(port);
if (!atomic_read(&atmel_port->tasklet_shutdown))
atmel_start_rx(port);
}

View File

@ -1373,6 +1373,7 @@ static struct console cpm_scc_uart_console = {
static int __init cpm_uart_console_init(void)
{
cpm_muram_init();
register_console(&cpm_scc_uart_console);
return 0;
}

View File

@ -599,7 +599,7 @@ static void imx_uart_dma_tx(struct imx_port *sport)
sport->tx_bytes = uart_circ_chars_pending(xmit);
if (xmit->tail < xmit->head) {
if (xmit->tail < xmit->head || xmit->head == 0) {
sport->dma_tx_nents = 1;
sg_init_one(sgl, xmit->buf + xmit->tail, sport->tx_bytes);
} else {

View File

@ -129,6 +129,7 @@ static int handle_rx_console(struct uart_port *uport, u32 bytes, bool drop);
static int handle_rx_uart(struct uart_port *uport, u32 bytes, bool drop);
static unsigned int qcom_geni_serial_tx_empty(struct uart_port *port);
static void qcom_geni_serial_stop_rx(struct uart_port *uport);
static void qcom_geni_serial_handle_rx(struct uart_port *uport, bool drop);
static const unsigned long root_freq[] = {7372800, 14745600, 19200000, 29491200,
32000000, 48000000, 64000000, 80000000,
@ -599,7 +600,7 @@ static void qcom_geni_serial_stop_rx(struct uart_port *uport)
u32 irq_en;
u32 status;
struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
u32 irq_clear = S_CMD_DONE_EN;
u32 s_irq_status;
irq_en = readl(uport->membase + SE_GENI_S_IRQ_EN);
irq_en &= ~(S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN);
@ -615,10 +616,19 @@ static void qcom_geni_serial_stop_rx(struct uart_port *uport)
return;
geni_se_cancel_s_cmd(&port->se);
qcom_geni_serial_poll_bit(uport, SE_GENI_S_CMD_CTRL_REG,
S_GENI_CMD_CANCEL, false);
qcom_geni_serial_poll_bit(uport, SE_GENI_S_IRQ_STATUS,
S_CMD_CANCEL_EN, true);
/*
* If timeout occurs secondary engine remains active
* and Abort sequence is executed.
*/
s_irq_status = readl(uport->membase + SE_GENI_S_IRQ_STATUS);
/* Flush the Rx buffer */
if (s_irq_status & S_RX_FIFO_LAST_EN)
qcom_geni_serial_handle_rx(uport, true);
writel(s_irq_status, uport->membase + SE_GENI_S_IRQ_CLEAR);
status = readl(uport->membase + SE_GENI_STATUS);
writel(irq_clear, uport->membase + SE_GENI_S_IRQ_CLEAR);
if (status & S_GENI_CMD_ACTIVE)
qcom_geni_serial_abort_rx(uport);
}

View File

@ -692,11 +692,22 @@ static void tegra_uart_copy_rx_to_tty(struct tegra_uart_port *tup,
count, DMA_TO_DEVICE);
}
static void do_handle_rx_pio(struct tegra_uart_port *tup)
{
struct tty_struct *tty = tty_port_tty_get(&tup->uport.state->port);
struct tty_port *port = &tup->uport.state->port;
tegra_uart_handle_rx_pio(tup, port);
if (tty) {
tty_flip_buffer_push(port);
tty_kref_put(tty);
}
}
static void tegra_uart_rx_buffer_push(struct tegra_uart_port *tup,
unsigned int residue)
{
struct tty_port *port = &tup->uport.state->port;
struct tty_struct *tty = tty_port_tty_get(port);
unsigned int count;
async_tx_ack(tup->rx_dma_desc);
@ -705,11 +716,7 @@ static void tegra_uart_rx_buffer_push(struct tegra_uart_port *tup,
/* If we are here, DMA is stopped */
tegra_uart_copy_rx_to_tty(tup, port, count);
tegra_uart_handle_rx_pio(tup, port);
if (tty) {
tty_flip_buffer_push(port);
tty_kref_put(tty);
}
do_handle_rx_pio(tup);
}
static void tegra_uart_rx_dma_complete(void *args)
@ -749,8 +756,10 @@ static void tegra_uart_terminate_rx_dma(struct tegra_uart_port *tup)
{
struct dma_tx_state state;
if (!tup->rx_dma_active)
if (!tup->rx_dma_active) {
do_handle_rx_pio(tup);
return;
}
dmaengine_terminate_all(tup->rx_dma_chan);
dmaengine_tx_status(tup->rx_dma_chan, tup->rx_cookie, &state);
@ -816,18 +825,6 @@ static void tegra_uart_handle_modem_signal_change(struct uart_port *u)
uart_handle_cts_change(&tup->uport, msr & UART_MSR_CTS);
}
static void do_handle_rx_pio(struct tegra_uart_port *tup)
{
struct tty_struct *tty = tty_port_tty_get(&tup->uport.state->port);
struct tty_port *port = &tup->uport.state->port;
tegra_uart_handle_rx_pio(tup, port);
if (tty) {
tty_flip_buffer_push(port);
tty_kref_put(tty);
}
}
static irqreturn_t tegra_uart_isr(int irq, void *data)
{
struct tegra_uart_port *tup = data;

View File

@ -52,10 +52,11 @@ static void tty_port_default_wakeup(struct tty_port *port)
}
}
static const struct tty_port_client_operations default_client_ops = {
const struct tty_port_client_operations tty_port_default_client_ops = {
.receive_buf = tty_port_default_receive_buf,
.write_wakeup = tty_port_default_wakeup,
};
EXPORT_SYMBOL_GPL(tty_port_default_client_ops);
void tty_port_init(struct tty_port *port)
{
@ -68,7 +69,7 @@ void tty_port_init(struct tty_port *port)
spin_lock_init(&port->lock);
port->close_delay = (50 * HZ) / 100;
port->closing_wait = (3000 * HZ) / 100;
port->client_ops = &default_client_ops;
port->client_ops = &tty_port_default_client_ops;
kref_init(&port->kref);
}
EXPORT_SYMBOL(tty_port_init);

View File

@ -16,6 +16,7 @@
#include <linux/tty.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/types.h>
@ -29,6 +30,8 @@
#include <linux/console.h>
#include <linux/tty_flip.h>
#include <linux/sched/signal.h>
/* Don't take this from <ctype.h>: 011-015 on the screen aren't spaces */
#define isspace(c) ((c) == ' ')
@ -43,6 +46,7 @@ static volatile int sel_start = -1; /* cleared by clear_selection */
static int sel_end;
static int sel_buffer_lth;
static char *sel_buffer;
static DEFINE_MUTEX(sel_lock);
/* clear_selection, highlight and highlight_pointer can be called
from interrupt (via scrollback/front) */
@ -184,7 +188,7 @@ int set_selection_kernel(struct tiocl_selection *v, struct tty_struct *tty)
char *bp, *obp;
int i, ps, pe, multiplier;
u32 c;
int mode;
int mode, ret = 0;
poke_blanked_console();
@ -210,6 +214,7 @@ int set_selection_kernel(struct tiocl_selection *v, struct tty_struct *tty)
if (ps > pe) /* make sel_start <= sel_end */
swap(ps, pe);
mutex_lock(&sel_lock);
if (sel_cons != vc_cons[fg_console].d) {
clear_selection();
sel_cons = vc_cons[fg_console].d;
@ -255,9 +260,10 @@ int set_selection_kernel(struct tiocl_selection *v, struct tty_struct *tty)
break;
case TIOCL_SELPOINTER:
highlight_pointer(pe);
return 0;
goto unlock;
default:
return -EINVAL;
ret = -EINVAL;
goto unlock;
}
/* remove the pointer */
@ -279,7 +285,7 @@ int set_selection_kernel(struct tiocl_selection *v, struct tty_struct *tty)
else if (new_sel_start == sel_start)
{
if (new_sel_end == sel_end) /* no action required */
return 0;
goto unlock;
else if (new_sel_end > sel_end) /* extend to right */
highlight(sel_end + 2, new_sel_end);
else /* contract from right */
@ -307,7 +313,8 @@ int set_selection_kernel(struct tiocl_selection *v, struct tty_struct *tty)
if (!bp) {
printk(KERN_WARNING "selection: kmalloc() failed\n");
clear_selection();
return -ENOMEM;
ret = -ENOMEM;
goto unlock;
}
kfree(sel_buffer);
sel_buffer = bp;
@ -332,7 +339,9 @@ int set_selection_kernel(struct tiocl_selection *v, struct tty_struct *tty)
}
}
sel_buffer_lth = bp - sel_buffer;
return 0;
unlock:
mutex_unlock(&sel_lock);
return ret;
}
EXPORT_SYMBOL_GPL(set_selection_kernel);
@ -350,6 +359,7 @@ int paste_selection(struct tty_struct *tty)
unsigned int count;
struct tty_ldisc *ld;
DECLARE_WAITQUEUE(wait, current);
int ret = 0;
console_lock();
poke_blanked_console();
@ -361,10 +371,17 @@ int paste_selection(struct tty_struct *tty)
tty_buffer_lock_exclusive(&vc->port);
add_wait_queue(&vc->paste_wait, &wait);
mutex_lock(&sel_lock);
while (sel_buffer && sel_buffer_lth > pasted) {
set_current_state(TASK_INTERRUPTIBLE);
if (signal_pending(current)) {
ret = -EINTR;
break;
}
if (tty_throttled(tty)) {
mutex_unlock(&sel_lock);
schedule();
mutex_lock(&sel_lock);
continue;
}
__set_current_state(TASK_RUNNING);
@ -373,11 +390,12 @@ int paste_selection(struct tty_struct *tty)
count);
pasted += count;
}
mutex_unlock(&sel_lock);
remove_wait_queue(&vc->paste_wait, &wait);
__set_current_state(TASK_RUNNING);
tty_buffer_unlock_exclusive(&vc->port);
tty_ldisc_deref(ld);
return 0;
return ret;
}
EXPORT_SYMBOL_GPL(paste_selection);

View File

@ -936,10 +936,21 @@ static void flush_scrollback(struct vc_data *vc)
WARN_CONSOLE_UNLOCKED();
set_origin(vc);
if (vc->vc_sw->con_flush_scrollback)
if (vc->vc_sw->con_flush_scrollback) {
vc->vc_sw->con_flush_scrollback(vc);
else
} else if (con_is_visible(vc)) {
/*
* When no con_flush_scrollback method is provided then the
* legacy way for flushing the scrollback buffer is to use
* a side effect of the con_switch method. We do it only on
* the foreground console as background consoles have no
* scrollback buffers in that case and we obviously don't
* want to switch to them.
*/
hide_cursor(vc);
vc->vc_sw->con_switch(vc);
set_cursor(vc);
}
}
/*

View File

@ -876,15 +876,20 @@ int vt_ioctl(struct tty_struct *tty,
return -EINVAL;
for (i = 0; i < MAX_NR_CONSOLES; i++) {
struct vc_data *vcp;
if (!vc_cons[i].d)
continue;
console_lock();
if (v.v_vlin)
vc_cons[i].d->vc_scan_lines = v.v_vlin;
if (v.v_clin)
vc_cons[i].d->vc_font.height = v.v_clin;
vc_cons[i].d->vc_resize_user = 1;
vc_resize(vc_cons[i].d, v.v_cols, v.v_rows);
vcp = vc_cons[i].d;
if (vcp) {
if (v.v_vlin)
vcp->vc_scan_lines = v.v_vlin;
if (v.v_clin)
vcp->vc_font.height = v.v_clin;
vcp->vc_resize_user = 1;
vc_resize(vcp, v.v_cols, v.v_rows);
}
console_unlock();
}
break;

View File

@ -225,6 +225,8 @@ struct tty_port_client_operations {
void (*write_wakeup)(struct tty_port *port);
};
extern const struct tty_port_client_operations tty_port_default_client_ops;
struct tty_port {
struct tty_bufhead buf; /* Locked internally */
struct tty_struct *tty; /* Back pointer */