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:
commit
ef11f1b76a
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
/*
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
Loading…
Reference in New Issue