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

Commit 33d9b8b2 authored by Peter Hurley's avatar Peter Hurley Committed by Greg Kroah-Hartman
Browse files

serial: 8250: Extract IIR logic steering from rx dma



Using fake IIR values to perform rx dma operations unnecessarily
conflates separate operations, stopping in-progress dma with starting
new dma.

Introduce serial8250_rx_dma_flush() to handle stopping in-progress dma
[omap8250 already has equivalent omap_8250_rx_dma_flush()].
Replace rx_dma(UART_IIR_RX_TIMEOUT) with the equivalent *_rx_dma_flush(),
and rx_dma(0) with the equivalent *_rx_dma(). Handle IIR steering
in the irq handler with handle_rx_dma() helper.

Signed-off-by: default avatarPeter Hurley <peter@hurleysoftware.com>
Reviewed-by: default avatarHeikki Krogerus <heikki.krogerus@linux.intel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent b74fdd23
Loading
Loading
Loading
Loading
+5 −3
Original line number Diff line number Diff line
@@ -17,7 +17,7 @@

struct uart_8250_dma {
	int (*tx_dma)(struct uart_8250_port *p);
	int (*rx_dma)(struct uart_8250_port *p, unsigned int iir);
	int (*rx_dma)(struct uart_8250_port *p);

	/* Filter function */
	dma_filter_fn		fn;
@@ -189,7 +189,8 @@ static inline int is_omap1510_8250(struct uart_8250_port *pt)

#ifdef CONFIG_SERIAL_8250_DMA
extern int serial8250_tx_dma(struct uart_8250_port *);
extern int serial8250_rx_dma(struct uart_8250_port *, unsigned int iir);
extern int serial8250_rx_dma(struct uart_8250_port *);
extern void serial8250_rx_dma_flush(struct uart_8250_port *);
extern int serial8250_request_dma(struct uart_8250_port *);
extern void serial8250_release_dma(struct uart_8250_port *);
#else
@@ -197,10 +198,11 @@ static inline int serial8250_tx_dma(struct uart_8250_port *p)
{
	return -1;
}
static inline int serial8250_rx_dma(struct uart_8250_port *p, unsigned int iir)
static inline int serial8250_rx_dma(struct uart_8250_port *p)
{
	return -1;
}
static inline void serial8250_rx_dma_flush(struct uart_8250_port *p) { }
static inline int serial8250_request_dma(struct uart_8250_port *p)
{
	return -1;
+12 −20
Original line number Diff line number Diff line
@@ -110,30 +110,11 @@ int serial8250_tx_dma(struct uart_8250_port *p)
	return ret;
}

int serial8250_rx_dma(struct uart_8250_port *p, unsigned int iir)
int serial8250_rx_dma(struct uart_8250_port *p)
{
	struct uart_8250_dma		*dma = p->dma;
	struct dma_async_tx_descriptor	*desc;

	switch (iir & 0x3f) {
	case UART_IIR_RLSI:
		/* 8250_core handles errors and break interrupts */
		return -EIO;
	case UART_IIR_RX_TIMEOUT:
		/*
		 * If RCVR FIFO trigger level was not reached, complete the
		 * transfer and let 8250_core copy the remaining data.
		 */
		if (dma->rx_running) {
			dmaengine_pause(dma->rxchan);
			__dma_rx_complete(p);
			dmaengine_terminate_all(dma->rxchan);
		}
		return -ETIMEDOUT;
	default:
		break;
	}

	if (dma->rx_running)
		return 0;

@@ -154,6 +135,17 @@ int serial8250_rx_dma(struct uart_8250_port *p, unsigned int iir)
	return 0;
}

void serial8250_rx_dma_flush(struct uart_8250_port *p)
{
	struct uart_8250_dma *dma = p->dma;

	if (dma->rx_running) {
		dmaengine_pause(dma->rxchan);
		__dma_rx_complete(p);
		dmaengine_terminate_all(dma->rxchan);
	}
}

int serial8250_request_dma(struct uart_8250_port *p)
{
	struct uart_8250_dma	*dma = p->dma;
+28 −39
Original line number Diff line number Diff line
@@ -115,6 +115,12 @@ struct omap8250_priv {
	bool rx_dma_broken;
};

#ifdef CONFIG_SERIAL_8250_DMA
static void omap_8250_rx_dma_flush(struct uart_8250_port *p);
#else
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)
{
	return readl(up->port.membase + (reg << up->port.regshift));
@@ -635,7 +641,7 @@ static int omap_8250_startup(struct uart_port *port)
	serial_out(up, UART_OMAP_WER, priv->wer);

	if (up->dma)
		up->dma->rx_dma(up, 0);
		up->dma->rx_dma(up);

	pm_runtime_mark_last_busy(port->dev);
	pm_runtime_put_autosuspend(port->dev);
@@ -654,7 +660,7 @@ static void omap_8250_shutdown(struct uart_port *port)

	flush_work(&priv->qos_work);
	if (up->dma)
		up->dma->rx_dma(up, UART_IIR_RX_TIMEOUT);
		omap_8250_rx_dma_flush(up);

	pm_runtime_get_sync(port->dev);

@@ -742,7 +748,7 @@ static void omap_8250_unthrottle(struct uart_port *port)
}

#ifdef CONFIG_SERIAL_8250_DMA
static int omap_8250_rx_dma(struct uart_8250_port *p, unsigned int iir);
static int omap_8250_rx_dma(struct uart_8250_port *p);

static void __dma_rx_do_complete(struct uart_8250_port *p)
{
@@ -778,7 +784,7 @@ static void __dma_rx_do_complete(struct uart_8250_port *p)
static void __dma_rx_complete(void *param)
{
	__dma_rx_do_complete(param);
	omap_8250_rx_dma(param, 0);
	omap_8250_rx_dma(param);
}

static void omap_8250_rx_dma_flush(struct uart_8250_port *p)
@@ -804,7 +810,7 @@ static void omap_8250_rx_dma_flush(struct uart_8250_port *p)
	__dma_rx_do_complete(p);
}

static int omap_8250_rx_dma(struct uart_8250_port *p, unsigned int iir)
static int omap_8250_rx_dma(struct uart_8250_port *p)
{
	struct omap8250_priv		*priv = p->port.private_data;
	struct uart_8250_dma            *dma = p->dma;
@@ -812,35 +818,6 @@ static int omap_8250_rx_dma(struct uart_8250_port *p, unsigned int iir)
	struct dma_async_tx_descriptor  *desc;
	unsigned long			flags;

	switch (iir & 0x3f) {
	case UART_IIR_RLSI:
		/* 8250_core handles errors and break interrupts */
		omap_8250_rx_dma_flush(p);
		return -EIO;
	case UART_IIR_RX_TIMEOUT:
		/*
		 * If RCVR FIFO trigger level was not reached, complete the
		 * transfer and let 8250_core copy the remaining data.
		 */
		omap_8250_rx_dma_flush(p);
		return -ETIMEDOUT;
	case UART_IIR_RDI:
		/*
		 * The OMAP UART is a special BEAST. If we receive RDI we _have_
		 * a DMA transfer programmed but it didn't work. One reason is
		 * that we were too slow and there were too many bytes in the
		 * FIFO, the UART counted wrong and never kicked the DMA engine
		 * to do anything. That means once we receive RDI on OMAP then
		 * the DMA won't do anything soon so we have to cancel the DMA
		 * transfer and purge the FIFO manually.
		 */
		omap_8250_rx_dma_flush(p);
		return -ETIMEDOUT;

	default:
		break;
	}

	if (priv->rx_dma_broken)
		return -EINVAL;

@@ -1014,6 +991,18 @@ static int omap_8250_tx_dma(struct uart_8250_port *p)
	return ret;
}

static bool handle_rx_dma(struct uart_8250_port *up, unsigned int iir)
{
	switch (iir & 0x3f) {
	case UART_IIR_RLSI:
	case UART_IIR_RX_TIMEOUT:
	case UART_IIR_RDI:
		omap_8250_rx_dma_flush(up);
		return true;
	}
	return omap_8250_rx_dma(up);
}

/*
 * This is mostly serial8250_handle_irq(). We have a slightly different DMA
 * hoook for RX/TX and need different logic for them in the ISR. Therefore we
@@ -1039,9 +1028,9 @@ static int omap_8250_dma_handle_irq(struct uart_port *port)
	status = serial_port_in(port, UART_LSR);

	if (status & (UART_LSR_DR | UART_LSR_BI)) {
		if (omap_8250_rx_dma(up, iir)) {
		if (handle_rx_dma(up, iir)) {
			status = serial8250_rx_chars(up, status);
			omap_8250_rx_dma(up, 0);
			omap_8250_rx_dma(up);
		}
	}
	serial8250_modem_status(up);
@@ -1072,7 +1061,7 @@ static bool the_no_dma_filter_fn(struct dma_chan *chan, void *param)

#else

static inline int omap_8250_rx_dma(struct uart_8250_port *p, unsigned int iir)
static inline int omap_8250_rx_dma(struct uart_8250_port *p)
{
	return -EINVAL;
}
@@ -1383,7 +1372,7 @@ static int omap8250_runtime_suspend(struct device *dev)
	}

	if (up->dma && up->dma->rxchan)
		omap_8250_rx_dma(up, UART_IIR_RX_TIMEOUT);
		omap_8250_rx_dma_flush(up);

	priv->latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE;
	schedule_work(&priv->qos_work);
@@ -1406,7 +1395,7 @@ static int omap8250_runtime_resume(struct device *dev)
		omap8250_restore_regs(up);

	if (up->dma && up->dma->rxchan)
		omap_8250_rx_dma(up, 0);
		omap_8250_rx_dma(up);

	priv->latency = priv->calc_latency;
	schedule_work(&priv->qos_work);
+13 −1
Original line number Diff line number Diff line
@@ -1788,6 +1788,18 @@ unsigned int serial8250_modem_status(struct uart_8250_port *up)
}
EXPORT_SYMBOL_GPL(serial8250_modem_status);

static bool handle_rx_dma(struct uart_8250_port *up, unsigned int iir)
{
	switch (iir & 0x3f) {
	case UART_IIR_RX_TIMEOUT:
		serial8250_rx_dma_flush(up);
		/* fall-through */
	case UART_IIR_RLSI:
		return true;
	}
	return up->dma->rx_dma(up);
}

/*
 * This handles the interrupt from one port.
 */
@@ -1807,7 +1819,7 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir)
	DEBUG_INTR("status = %x...", status);

	if (status & (UART_LSR_DR | UART_LSR_BI)) {
		if (!up->dma || up->dma->rx_dma(up, iir))
		if (!up->dma || handle_rx_dma(up, iir))
			status = serial8250_rx_chars(up, status);
	}
	serial8250_modem_status(up);