Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b334b773 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull tty/serial driver fixes from Greg KH:
 "Here are a few TTY and Serial driver fixes for reported regressions
  and crashes.

  All of these have been in linux-next with no reported problems"

* tag 'tty-4.1-rc7' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty:
  n_tty: Fix auditing support for cannonical mode
  serial: 8250_omap: provide complete custom startup & shutdown callbacks
  n_tty: Fix calculation of size in canon_copy_from_read_buf
  serial: imx: Fix DMA handling for IDLE condition aborts
  serial/amba-pl011: Unconditionally poll for FIFO space before each TX char
parents e900f2c0 72586c60
Loading
Loading
Loading
Loading
+16 −5
Original line number Diff line number Diff line
@@ -162,6 +162,17 @@ static inline int tty_put_user(struct tty_struct *tty, unsigned char x,
	return put_user(x, ptr);
}

static inline int tty_copy_to_user(struct tty_struct *tty,
					void __user *to,
					const void *from,
					unsigned long n)
{
	struct n_tty_data *ldata = tty->disc_data;

	tty_audit_add_data(tty, to, n, ldata->icanon);
	return copy_to_user(to, from, n);
}

/**
 *	n_tty_kick_worker - start input worker (if required)
 *	@tty: terminal
@@ -2070,8 +2081,8 @@ static int canon_copy_from_read_buf(struct tty_struct *tty,

	size = N_TTY_BUF_SIZE - tail;
	n = eol - tail;
	if (n > 4096)
		n += 4096;
	if (n > N_TTY_BUF_SIZE)
		n += N_TTY_BUF_SIZE;
	n += found;
	c = n;

@@ -2084,12 +2095,12 @@ static int canon_copy_from_read_buf(struct tty_struct *tty,
		    __func__, eol, found, n, c, size, more);

	if (n > size) {
		ret = copy_to_user(*b, read_buf_addr(ldata, tail), size);
		ret = tty_copy_to_user(tty, *b, read_buf_addr(ldata, tail), size);
		if (ret)
			return -EFAULT;
		ret = copy_to_user(*b + size, ldata->read_buf, n - size);
		ret = tty_copy_to_user(tty, *b + size, ldata->read_buf, n - size);
	} else
		ret = copy_to_user(*b, read_buf_addr(ldata, tail), n);
		ret = tty_copy_to_user(tty, *b, read_buf_addr(ldata, tail), n);

	if (ret)
		return -EFAULT;
+73 −9
Original line number Diff line number Diff line
@@ -562,12 +562,36 @@ static irqreturn_t omap_wake_irq(int irq, void *dev_id)
	return IRQ_NONE;
}

#ifdef CONFIG_SERIAL_8250_DMA
static int omap_8250_dma_handle_irq(struct uart_port *port);
#endif

static irqreturn_t omap8250_irq(int irq, void *dev_id)
{
	struct uart_port *port = dev_id;
	struct uart_8250_port *up = up_to_u8250p(port);
	unsigned int iir;
	int ret;

#ifdef CONFIG_SERIAL_8250_DMA
	if (up->dma) {
		ret = omap_8250_dma_handle_irq(port);
		return IRQ_RETVAL(ret);
	}
#endif

	serial8250_rpm_get(up);
	iir = serial_port_in(port, UART_IIR);
	ret = serial8250_handle_irq(port, iir);
	serial8250_rpm_put(up);

	return IRQ_RETVAL(ret);
}

static int omap_8250_startup(struct uart_port *port)
{
	struct uart_8250_port *up =
		container_of(port, struct uart_8250_port, port);
	struct uart_8250_port *up = up_to_u8250p(port);
	struct omap8250_priv *priv = port->private_data;

	int ret;

	if (priv->wakeirq) {
@@ -580,10 +604,31 @@ static int omap_8250_startup(struct uart_port *port)

	pm_runtime_get_sync(port->dev);

	ret = serial8250_do_startup(port);
	if (ret)
	up->mcr = 0;
	serial_out(up, UART_FCR, UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);

	serial_out(up, UART_LCR, UART_LCR_WLEN8);

	up->lsr_saved_flags = 0;
	up->msr_saved_flags = 0;

	if (up->dma) {
		ret = serial8250_request_dma(up);
		if (ret) {
			dev_warn_ratelimited(port->dev,
					     "failed to request DMA\n");
			up->dma = NULL;
		}
	}

	ret = request_irq(port->irq, omap8250_irq, IRQF_SHARED,
			  dev_name(port->dev), port);
	if (ret < 0)
		goto err;

	up->ier = UART_IER_RLSI | UART_IER_RDI;
	serial_out(up, UART_IER, up->ier);

#ifdef CONFIG_PM
	up->capabilities |= UART_CAP_RPM;
#endif
@@ -610,8 +655,7 @@ static int omap_8250_startup(struct uart_port *port)

static void omap_8250_shutdown(struct uart_port *port)
{
	struct uart_8250_port *up =
		container_of(port, struct uart_8250_port, port);
	struct uart_8250_port *up = up_to_u8250p(port);
	struct omap8250_priv *priv = port->private_data;

	flush_work(&priv->qos_work);
@@ -621,11 +665,24 @@ static void omap_8250_shutdown(struct uart_port *port)
	pm_runtime_get_sync(port->dev);

	serial_out(up, UART_OMAP_WER, 0);
	serial8250_do_shutdown(port);

	up->ier = 0;
	serial_out(up, UART_IER, 0);

	if (up->dma)
		serial8250_release_dma(up);

	/*
	 * Disable break condition and FIFOs
	 */
	if (up->lcr & UART_LCR_SBC)
		serial_out(up, UART_LCR, up->lcr & ~UART_LCR_SBC);
	serial_out(up, UART_FCR, UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);

	pm_runtime_mark_last_busy(port->dev);
	pm_runtime_put_autosuspend(port->dev);

	free_irq(port->irq, port);
	if (priv->wakeirq)
		free_irq(priv->wakeirq, port);
}
@@ -974,6 +1031,13 @@ static inline int omap_8250_rx_dma(struct uart_8250_port *p, unsigned int iir)
}
#endif

static int omap8250_no_handle_irq(struct uart_port *port)
{
	/* IRQ has not been requested but handling irq? */
	WARN_ONCE(1, "Unexpected irq handling before port startup\n");
	return 0;
}

static int omap8250_probe(struct platform_device *pdev)
{
	struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -1075,6 +1139,7 @@ static int omap8250_probe(struct platform_device *pdev)
	pm_runtime_get_sync(&pdev->dev);

	omap_serial_fill_features_erratas(&up, priv);
	up.port.handle_irq = omap8250_no_handle_irq;
#ifdef CONFIG_SERIAL_8250_DMA
	if (pdev->dev.of_node) {
		/*
@@ -1088,7 +1153,6 @@ static int omap8250_probe(struct platform_device *pdev)
		ret = of_property_count_strings(pdev->dev.of_node, "dma-names");
		if (ret == 2) {
			up.dma = &priv->omap8250_dma;
			up.port.handle_irq = omap_8250_dma_handle_irq;
			priv->omap8250_dma.fn = the_no_dma_filter_fn;
			priv->omap8250_dma.tx_dma = omap_8250_tx_dma;
			priv->omap8250_dma.rx_dma = omap_8250_rx_dma;
+8 −8
Original line number Diff line number Diff line
@@ -1249,20 +1249,19 @@ __acquires(&uap->port.lock)

/*
 * Transmit a character
 * There must be at least one free entry in the TX FIFO to accept the char.
 *
 * Returns true if the FIFO might have space in it afterwards;
 * returns false if the FIFO definitely became full.
 * Returns true if the character was successfully queued to the FIFO.
 * Returns false otherwise.
 */
static bool pl011_tx_char(struct uart_amba_port *uap, unsigned char c)
{
	if (readw(uap->port.membase + UART01x_FR) & UART01x_FR_TXFF)
		return false; /* unable to transmit character */

	writew(c, uap->port.membase + UART01x_DR);
	uap->port.icount.tx++;

	if (likely(uap->tx_irq_seen > 1))
	return true;

	return !(readw(uap->port.membase + UART01x_FR) & UART01x_FR_TXFF);
}

static bool pl011_tx_chars(struct uart_amba_port *uap)
@@ -1296,7 +1295,8 @@ static bool pl011_tx_chars(struct uart_amba_port *uap)
		return false;

	if (uap->port.x_char) {
		pl011_tx_char(uap, uap->port.x_char);
		if (!pl011_tx_char(uap, uap->port.x_char))
			goto done;
		uap->port.x_char = 0;
		--count;
	}
+8 −0
Original line number Diff line number Diff line
@@ -911,6 +911,14 @@ static void dma_rx_callback(void *data)

	status = dmaengine_tx_status(chan, (dma_cookie_t)0, &state);
	count = RX_BUF_SIZE - state.residue;

	if (readl(sport->port.membase + USR2) & USR2_IDLE) {
		/* In condition [3] the SDMA counted up too early */
		count--;

		writel(USR2_IDLE, sport->port.membase + USR2);
	}

	dev_dbg(sport->port.dev, "We get %d bytes.\n", count);

	if (count) {