Commit c01ac4b9 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull tty/serial driver fixes from Greg KH:
 "Here are some small tty and serial driver fixes for 6.12-rc4:

   - qcom-geni serial driver fixes, wow what a mess of a UART chip that
     thing is...

   - vt infoleak fix for odd font sizes

   - imx serial driver bugfix

   - yet-another n_gsm ldisc bugfix, slowly chipping down the issues in
     that piece of code

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

* tag 'tty-6.12-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty:
  serial: qcom-geni: rename suspend functions
  serial: qcom-geni: drop unused receive parameter
  serial: qcom-geni: drop flip buffer WARN()
  serial: qcom-geni: fix rx cancel dma status bit
  serial: qcom-geni: fix receiver enable
  serial: qcom-geni: fix dma rx cancellation
  serial: qcom-geni: fix shutdown race
  serial: qcom-geni: revert broken hibernation support
  serial: qcom-geni: fix polled console initialisation
  serial: imx: Update mctrl old_status on RTSD interrupt
  tty: n_gsm: Fix use-after-free in gsm_cleanup_mux
  vt: prevent kernel-infoleak in con_font_get()
parents b68c1895 be847a3a
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -3157,6 +3157,8 @@ static void gsm_cleanup_mux(struct gsm_mux *gsm, bool disc)
	mutex_unlock(&gsm->mutex);
	/* Now wipe the queues */
	tty_ldisc_flush(gsm->tty);

	guard(spinlock_irqsave)(&gsm->tx_lock);
	list_for_each_entry_safe(txq, ntxq, &gsm->tx_ctrl_list, list)
		kfree(txq);
	INIT_LIST_HEAD(&gsm->tx_ctrl_list);
+15 −0
Original line number Diff line number Diff line
@@ -762,6 +762,21 @@ static irqreturn_t __imx_uart_rtsint(int irq, void *dev_id)

	imx_uart_writel(sport, USR1_RTSD, USR1);
	usr1 = imx_uart_readl(sport, USR1) & USR1_RTSS;
	/*
	 * Update sport->old_status here, so any follow-up calls to
	 * imx_uart_mctrl_check() will be able to recognize that RTS
	 * state changed since last imx_uart_mctrl_check() call.
	 *
	 * In case RTS has been detected as asserted here and later on
	 * deasserted by the time imx_uart_mctrl_check() was called,
	 * imx_uart_mctrl_check() can detect the RTS state change and
	 * trigger uart_handle_cts_change() to unblock the port for
	 * further TX transfers.
	 */
	if (usr1 & USR1_RTSS)
		sport->old_status |= TIOCM_CTS;
	else
		sport->old_status &= ~TIOCM_CTS;
	uart_handle_cts_change(&sport->port, usr1);
	wake_up_interruptible(&sport->port.state->port.delta_msr_wait);

+48 −55
Original line number Diff line number Diff line
@@ -147,6 +147,7 @@ static struct uart_driver qcom_geni_uart_driver;

static void __qcom_geni_serial_cancel_tx_cmd(struct uart_port *uport);
static void qcom_geni_serial_cancel_tx_cmd(struct uart_port *uport);
static int qcom_geni_serial_port_setup(struct uart_port *uport);

static inline struct qcom_geni_serial_port *to_dev_port(struct uart_port *uport)
{
@@ -395,6 +396,23 @@ static void qcom_geni_serial_poll_put_char(struct uart_port *uport,
	writel(c, uport->membase + SE_GENI_TX_FIFOn);
	qcom_geni_serial_poll_tx_done(uport);
}

static int qcom_geni_serial_poll_init(struct uart_port *uport)
{
	struct qcom_geni_serial_port *port = to_dev_port(uport);
	int ret;

	if (!port->setup) {
		ret = qcom_geni_serial_port_setup(uport);
		if (ret)
			return ret;
	}

	if (!qcom_geni_serial_secondary_active(uport))
		geni_se_setup_s_cmd(&port->se, UART_START_READ, 0);

	return 0;
}
#endif

#ifdef CONFIG_SERIAL_QCOM_GENI_CONSOLE
@@ -562,7 +580,7 @@ static void handle_rx_console(struct uart_port *uport, u32 bytes, bool drop)
}
#endif /* CONFIG_SERIAL_QCOM_GENI_CONSOLE */

static void handle_rx_uart(struct uart_port *uport, u32 bytes, bool drop)
static void handle_rx_uart(struct uart_port *uport, u32 bytes)
{
	struct qcom_geni_serial_port *port = to_dev_port(uport);
	struct tty_port *tport = &uport->state->port;
@@ -570,9 +588,8 @@ static void handle_rx_uart(struct uart_port *uport, u32 bytes, bool drop)

	ret = tty_insert_flip_string(tport, port->rx_buf, bytes);
	if (ret != bytes) {
		dev_err(uport->dev, "%s:Unable to push data ret %d_bytes %d\n",
				__func__, ret, bytes);
		WARN_ON_ONCE(1);
		dev_err_ratelimited(uport->dev, "failed to push data (%d < %u)\n",
				ret, bytes);
	}
	uport->icount.rx += ret;
	tty_flip_buffer_push(tport);
@@ -787,17 +804,27 @@ static void qcom_geni_serial_start_rx_fifo(struct uart_port *uport)
static void qcom_geni_serial_stop_rx_dma(struct uart_port *uport)
{
	struct qcom_geni_serial_port *port = to_dev_port(uport);
	bool done;

	if (!qcom_geni_serial_secondary_active(uport))
		return;

	geni_se_cancel_s_cmd(&port->se);
	qcom_geni_serial_poll_bit(uport, SE_GENI_S_IRQ_STATUS,
				  S_CMD_CANCEL_EN, true);

	if (qcom_geni_serial_secondary_active(uport))
	done = qcom_geni_serial_poll_bit(uport, SE_DMA_RX_IRQ_STAT,
			RX_EOT, true);
	if (done) {
		writel(RX_EOT | RX_DMA_DONE,
				uport->membase + SE_DMA_RX_IRQ_CLR);
	} else {
		qcom_geni_serial_abort_rx(uport);

		writel(1, uport->membase + SE_DMA_RX_FSM_RST);
		qcom_geni_serial_poll_bit(uport, SE_DMA_RX_IRQ_STAT,
				RX_RESET_DONE, true);
		writel(RX_RESET_DONE | RX_DMA_DONE,
				uport->membase + SE_DMA_RX_IRQ_CLR);
	}

	if (port->rx_dma_addr) {
		geni_se_rx_dma_unprep(&port->se, port->rx_dma_addr,
				      DMA_RX_BUF_SIZE);
@@ -846,7 +873,7 @@ static void qcom_geni_serial_handle_rx_dma(struct uart_port *uport, bool drop)
	}

	if (!drop)
		handle_rx_uart(uport, rx_in, drop);
		handle_rx_uart(uport, rx_in);

	ret = geni_se_rx_dma_prep(&port->se, port->rx_buf,
				  DMA_RX_BUF_SIZE,
@@ -1096,10 +1123,12 @@ static void qcom_geni_serial_shutdown(struct uart_port *uport)
{
	disable_irq(uport->irq);

	uart_port_lock_irq(uport);
	qcom_geni_serial_stop_tx(uport);
	qcom_geni_serial_stop_rx(uport);

	qcom_geni_serial_cancel_tx_cmd(uport);
	uart_port_unlock_irq(uport);
}

static void qcom_geni_serial_flush_buffer(struct uart_port *uport)
@@ -1152,7 +1181,6 @@ static int qcom_geni_serial_port_setup(struct uart_port *uport)
			       false, true, true);
	geni_se_init(&port->se, UART_RX_WM, port->rx_fifo_depth - 2);
	geni_se_select_mode(&port->se, port->dev_data->mode);
	qcom_geni_serial_start_rx(uport);
	port->setup = true;

	return 0;
@@ -1168,6 +1196,11 @@ static int qcom_geni_serial_startup(struct uart_port *uport)
		if (ret)
			return ret;
	}

	uart_port_lock_irq(uport);
	qcom_geni_serial_start_rx(uport);
	uart_port_unlock_irq(uport);

	enable_irq(uport->irq);

	return 0;
@@ -1253,7 +1286,6 @@ static void qcom_geni_serial_set_termios(struct uart_port *uport,
	unsigned int avg_bw_core;
	unsigned long timeout;

	qcom_geni_serial_stop_rx(uport);
	/* baud rate */
	baud = uart_get_baud_rate(uport, termios, old, 300, 4000000);

@@ -1269,7 +1301,7 @@ static void qcom_geni_serial_set_termios(struct uart_port *uport,
		dev_err(port->se.dev,
			"Couldn't find suitable clock rate for %u\n",
			baud * sampling_rate);
		goto out_restart_rx;
		return;
	}

	dev_dbg(port->se.dev, "desired_rate = %u, clk_rate = %lu, clk_div = %u\n",
@@ -1360,8 +1392,6 @@ static void qcom_geni_serial_set_termios(struct uart_port *uport,
	writel(stop_bit_len, uport->membase + SE_UART_TX_STOP_BIT_LEN);
	writel(ser_clk_cfg, uport->membase + GENI_SER_M_CLK_CFG);
	writel(ser_clk_cfg, uport->membase + GENI_SER_S_CLK_CFG);
out_restart_rx:
	qcom_geni_serial_start_rx(uport);
}

#ifdef CONFIG_SERIAL_QCOM_GENI_CONSOLE
@@ -1582,7 +1612,7 @@ static const struct uart_ops qcom_geni_console_pops = {
#ifdef CONFIG_CONSOLE_POLL
	.poll_get_char	= qcom_geni_serial_get_char,
	.poll_put_char	= qcom_geni_serial_poll_put_char,
	.poll_init = qcom_geni_serial_port_setup,
	.poll_init = qcom_geni_serial_poll_init,
#endif
	.pm = qcom_geni_serial_pm,
};
@@ -1749,7 +1779,7 @@ static void qcom_geni_serial_remove(struct platform_device *pdev)
	uart_remove_one_port(drv, &port->uport);
}

static int qcom_geni_serial_sys_suspend(struct device *dev)
static int qcom_geni_serial_suspend(struct device *dev)
{
	struct qcom_geni_serial_port *port = dev_get_drvdata(dev);
	struct uart_port *uport = &port->uport;
@@ -1766,7 +1796,7 @@ static int qcom_geni_serial_sys_suspend(struct device *dev)
	return uart_suspend_port(private_data->drv, uport);
}

static int qcom_geni_serial_sys_resume(struct device *dev)
static int qcom_geni_serial_resume(struct device *dev)
{
	int ret;
	struct qcom_geni_serial_port *port = dev_get_drvdata(dev);
@@ -1781,38 +1811,6 @@ static int qcom_geni_serial_sys_resume(struct device *dev)
	return ret;
}

static int qcom_geni_serial_sys_hib_resume(struct device *dev)
{
	int ret = 0;
	struct uart_port *uport;
	struct qcom_geni_private_data *private_data;
	struct qcom_geni_serial_port *port = dev_get_drvdata(dev);

	uport = &port->uport;
	private_data = uport->private_data;

	if (uart_console(uport)) {
		geni_icc_set_tag(&port->se, QCOM_ICC_TAG_ALWAYS);
		geni_icc_set_bw(&port->se);
		ret = uart_resume_port(private_data->drv, uport);
		/*
		 * For hibernation usecase clients for
		 * console UART won't call port setup during restore,
		 * hence call port setup for console uart.
		 */
		qcom_geni_serial_port_setup(uport);
	} else {
		/*
		 * Peripheral register settings are lost during hibernation.
		 * Update setup flag such that port setup happens again
		 * during next session. Clients of HS-UART will close and
		 * open the port during hibernation.
		 */
		port->setup = false;
	}
	return ret;
}

static const struct qcom_geni_device_data qcom_geni_console_data = {
	.console = true,
	.mode = GENI_SE_FIFO,
@@ -1824,12 +1822,7 @@ static const struct qcom_geni_device_data qcom_geni_uart_data = {
};

static const struct dev_pm_ops qcom_geni_serial_pm_ops = {
	.suspend = pm_sleep_ptr(qcom_geni_serial_sys_suspend),
	.resume = pm_sleep_ptr(qcom_geni_serial_sys_resume),
	.freeze = pm_sleep_ptr(qcom_geni_serial_sys_suspend),
	.poweroff = pm_sleep_ptr(qcom_geni_serial_sys_suspend),
	.restore = pm_sleep_ptr(qcom_geni_serial_sys_hib_resume),
	.thaw = pm_sleep_ptr(qcom_geni_serial_sys_hib_resume),
	SYSTEM_SLEEP_PM_OPS(qcom_geni_serial_suspend, qcom_geni_serial_resume)
};

static const struct of_device_id qcom_geni_serial_match_table[] = {
+1 −1
Original line number Diff line number Diff line
@@ -4726,7 +4726,7 @@ static int con_font_get(struct vc_data *vc, struct console_font_op *op)
		return -EINVAL;

	if (op->data) {
		font.data = kvmalloc(max_font_size, GFP_KERNEL);
		font.data = kvzalloc(max_font_size, GFP_KERNEL);
		if (!font.data)
			return -ENOMEM;
	} else
+1 −1
Original line number Diff line number Diff line
@@ -258,8 +258,8 @@ struct geni_se {
#define RX_DMA_PARITY_ERR		BIT(5)
#define RX_DMA_BREAK			GENMASK(8, 7)
#define RX_GENI_GP_IRQ			GENMASK(10, 5)
#define RX_GENI_CANCEL_IRQ		BIT(11)
#define RX_GENI_GP_IRQ_EXT		GENMASK(13, 12)
#define RX_GENI_CANCEL_IRQ		BIT(14)

/* SE_HW_PARAM_0 fields */
#define TX_FIFO_WIDTH_MSK		GENMASK(29, 24)