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

Commit a2f1cb82 authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "serial: msm_geni_serial: Fix geni_wait_for_cmd_done timeouts"

parents 48dd209f afddc527
Loading
Loading
Loading
Loading
+101 −50
Original line number Diff line number Diff line
@@ -172,7 +172,8 @@ struct msm_geni_serial_port {
			unsigned int rx_fifo_wc,
			unsigned int rx_last_byte_valid,
			unsigned int rx_last,
			bool drop_rx);
			bool drop_rx,
			unsigned long *flags);
	struct device *wrapper_dev;
	struct se_geni_rsc serial_rsc;
	dma_addr_t tx_dma;
@@ -212,12 +213,12 @@ static int handle_rx_console(struct uart_port *uport,
			unsigned int rx_fifo_wc,
			unsigned int rx_last_byte_valid,
			unsigned int rx_last,
			bool drop_rx);
			bool drop_rx, unsigned long *flags);
static int handle_rx_hs(struct uart_port *uport,
			unsigned int rx_fifo_wc,
			unsigned int rx_last_byte_valid,
			unsigned int rx_last,
			bool drop_rx);
			bool drop_rx, unsigned long *flags);
static unsigned int msm_geni_serial_tx_empty(struct uart_port *port);
static int msm_geni_serial_power_on(struct uart_port *uport);
static void msm_geni_serial_power_off(struct uart_port *uport);
@@ -236,7 +237,8 @@ static int uart_line_id;

static struct msm_geni_serial_port msm_geni_console_port;
static struct msm_geni_serial_port msm_geni_serial_ports[GENI_UART_NR_PORTS];
static void msm_geni_serial_handle_isr(struct uart_port *uport);
static void msm_geni_serial_handle_isr(struct uart_port *uport,
				unsigned long *flags);

/*
 * The below API is required to check if uport->lock (spinlock)
@@ -297,8 +299,8 @@ static void msm_geni_serial_enable_interrupts(struct uart_port *uport)
	}
}

/* Disable the interrupts in order to do polling in an atomic contexts. */
static void msm_geni_serial_disable_interrupts(struct uart_port *uport)
/* Try disabling interrupts in order to do polling in an atomic contexts. */
static bool msm_serial_try_disable_interrupts(struct uart_port *uport)
{
	unsigned int geni_m_irq_en, geni_s_irq_en;
	unsigned int dma_m_irq_en, dma_s_irq_en;
@@ -309,7 +311,7 @@ static void msm_geni_serial_disable_interrupts(struct uart_port *uport)
	 * by framework as we can rely on ISR.
	 */
	if (!msm_geni_serial_spinlocked(uport))
		return;
		return false;

	geni_m_irq_en = geni_read_reg_nolog(uport->membase, SE_GENI_M_IRQ_EN);
	geni_s_irq_en = geni_read_reg_nolog(uport->membase, SE_GENI_S_IRQ_EN);
@@ -335,6 +337,8 @@ static void msm_geni_serial_disable_interrupts(struct uart_port *uport)
		geni_write_reg_nolog(dma_s_irq_en, uport->membase,
							SE_DMA_RX_IRQ_EN);
	}

	return true;
}

/*
@@ -343,17 +347,18 @@ static void msm_geni_serial_disable_interrupts(struct uart_port *uport)
 * in a non-atomic context. This API decides wheather to poll for
 * interrupt or depend on the isr based on in_atomic() call.
 */
bool geni_wait_for_cmd_done(struct uart_port *uport)
bool geni_wait_for_cmd_done(struct uart_port *uport, bool is_irq_masked)
{
	struct msm_geni_serial_port *msm_port = GET_DEV_PORT(uport);
	unsigned long timeout = POLL_ITERATIONS;
	unsigned long ret;
	unsigned long flags = 0;

	/*
	 * We need to do polling if spinlock is taken
	 * by framework as we cannot rely on ISR.
	 */
	if (msm_geni_serial_spinlocked(uport)) {
	if (is_irq_masked) {
		/*
		 * Polling is done for 1000 iterrations with
		 * 10 usecs interval which in total accumulates
@@ -361,13 +366,13 @@ bool geni_wait_for_cmd_done(struct uart_port *uport)
		 */
		if (msm_port->m_cmd) {
			while (!msm_port->m_cmd_done && timeout > 0) {
				msm_geni_serial_handle_isr(uport);
				msm_geni_serial_handle_isr(uport, &flags);
				timeout--;
				udelay(100);
			}
		} else if (msm_port->s_cmd) {
			while (!msm_port->s_cmd_done && timeout > 0) {
				msm_geni_serial_handle_isr(uport);
				msm_geni_serial_handle_isr(uport, &flags);
				timeout--;
				udelay(100);
			}
@@ -870,7 +875,7 @@ static void msm_geni_serial_poll_put_char(struct uart_port *uport,
	 * Ensure FIFO write goes through before polling for status but.
	 */
	mb();
	msm_geni_serial_disable_interrupts(uport);
	msm_serial_try_disable_interrupts(uport);
	msm_geni_serial_poll_tx_done(uport);
	msm_geni_serial_enable_interrupts(uport);
}
@@ -933,7 +938,7 @@ __msm_geni_serial_console_write(struct uart_port *uport, const char *s,
		mb();
		i += chars_to_write;
	}
	msm_geni_serial_disable_interrupts(uport);
	msm_serial_try_disable_interrupts(uport);
	msm_geni_serial_poll_tx_done(uport);
	msm_geni_serial_enable_interrupts(uport);
}
@@ -947,6 +952,7 @@ static void msm_geni_serial_console_write(struct console *co, const char *s,
	unsigned long flags;
	unsigned int geni_status;
	bool timeout;
	bool is_irq_masked;
	int irq_en;

	/* Max 1 port supported as of now */
@@ -965,14 +971,19 @@ static void msm_geni_serial_console_write(struct console *co, const char *s,
	geni_status = readl_relaxed(uport->membase + SE_GENI_STATUS);

	/* Cancel the current write to log the fault */
	if (!locked) {
	if ((geni_status & M_GENI_CMD_ACTIVE) && !locked) {
		port->m_cmd_done = false;
		port->m_cmd = true;
		reinit_completion(&port->m_cmd_timeout);
		msm_geni_serial_disable_interrupts(uport);
		is_irq_masked = msm_serial_try_disable_interrupts(uport);
		geni_cancel_m_cmd(uport->membase);

		timeout = geni_wait_for_cmd_done(uport);
		/*
		 * console should be in polling mode. Hence directly pass true
		 * as argument for wait_for_cmd_done here to handle cancel tx
		 * in polling mode.
		 */
		timeout = geni_wait_for_cmd_done(uport, true);
		if (timeout) {
			IPC_LOG_MSG(port->console_log,
				"%s: tx_cancel failed 0x%x\n",
@@ -981,7 +992,7 @@ static void msm_geni_serial_console_write(struct console *co, const char *s,

			reinit_completion(&port->m_cmd_timeout);
			geni_abort_m_cmd(uport->membase);
			timeout = geni_wait_for_cmd_done(uport);
			timeout = geni_wait_for_cmd_done(uport, true);
			if (timeout)
				IPC_LOG_MSG(port->console_log,
				"%s: tx abort failed 0x%x\n", __func__,
@@ -996,7 +1007,7 @@ static void msm_geni_serial_console_write(struct console *co, const char *s,
		/* It seems we can interrupt existing transfers unless all data
		 * has been sent, in which case we need to look for done first.
		 */
		msm_geni_serial_disable_interrupts(uport);
		msm_serial_try_disable_interrupts(uport);
		msm_geni_serial_poll_tx_done(uport);
		msm_geni_serial_enable_interrupts(uport);

@@ -1022,13 +1033,13 @@ static int handle_rx_console(struct uart_port *uport,
			unsigned int rx_fifo_wc,
			unsigned int rx_last_byte_valid,
			unsigned int rx_last,
			bool drop_rx)
			bool drop_rx, unsigned long *flags)
{
	int i, c;
	unsigned char *rx_char;
	unsigned long flags;
	struct tty_port *tport;
	struct msm_geni_serial_port *msm_port = GET_DEV_PORT(uport);
	bool locked;

	tport = &uport->state->port;
	for (i = 0; i < rx_fifo_wc; i++) {
@@ -1062,9 +1073,14 @@ static int handle_rx_console(struct uart_port *uport,
		 * release the port lock before calling tty_flip_buffer_push()
		 * to avoid deadlock scenarios.
		 */
		spin_unlock_irqrestore(&uport->lock, flags);
		locked = msm_geni_serial_spinlocked(uport);
		if (locked) {
			spin_unlock_irqrestore(&uport->lock, *flags);
			tty_flip_buffer_push(tport);
		spin_lock_irqsave(&uport->lock, flags);
			spin_lock_irqsave(&uport->lock, *flags);
		} else {
			tty_flip_buffer_push(tport);
		}
	}
	return 0;
}
@@ -1073,7 +1089,7 @@ static int handle_rx_console(struct uart_port *uport,
			unsigned int rx_fifo_wc,
			unsigned int rx_last_byte_valid,
			unsigned int rx_last,
			bool drop_rx)
			bool drop_rx, unsigned long *flags)
{
	return -EPERM;
}
@@ -1086,7 +1102,7 @@ static int msm_geni_serial_prep_dma_tx(struct uart_port *uport)
	struct circ_buf *xmit = &uport->state->xmit;
	unsigned int xmit_size;
	unsigned int dma_dbg;
	bool timeout;
	bool timeout, is_irq_masked;
	int ret = 0;

	xmit_size = uart_circ_chars_pending(xmit);
@@ -1117,13 +1133,13 @@ static int msm_geni_serial_prep_dma_tx(struct uart_port *uport)
		reinit_completion(&msm_port->m_cmd_timeout);

		/*
		 * Disabling the interrupts before giving the
		 * Try disabling interrupts before giving the
		 * cancel command as this might be in an atomic context.
		 */
		msm_geni_serial_disable_interrupts(uport);
		is_irq_masked = msm_serial_try_disable_interrupts(uport);
		geni_cancel_m_cmd(uport->membase);

		timeout = geni_wait_for_cmd_done(uport);
		timeout = geni_wait_for_cmd_done(uport, is_irq_masked);
		if (timeout) {
			IPC_LOG_MSG(msm_port->console_log,
			"%s: tx_cancel fail 0x%x\n", __func__,
@@ -1138,7 +1154,8 @@ static int msm_geni_serial_prep_dma_tx(struct uart_port *uport)
			/* Give abort command as cancel command failed */
			geni_abort_m_cmd(uport->membase);

			timeout = geni_wait_for_cmd_done(uport);
			timeout = geni_wait_for_cmd_done(uport,
							 is_irq_masked);
			if (timeout) {
				IPC_LOG_MSG(msm_port->console_log,
				"%s: tx abort failed 0x%x\n", __func__,
@@ -1160,7 +1177,8 @@ static int msm_geni_serial_prep_dma_tx(struct uart_port *uport)
				geni_write_reg_nolog(1, uport->membase,
						SE_DMA_TX_FSM_RST);

				timeout = geni_wait_for_cmd_done(uport);
				timeout = geni_wait_for_cmd_done(uport,
							is_irq_masked);
				if (timeout)
					IPC_LOG_MSG(msm_port->ipc_log_misc,
					"%s: tx fsm reset failed\n", __func__);
@@ -1243,7 +1261,7 @@ static void msm_geni_serial_start_tx(struct uart_port *uport)
static void stop_tx_sequencer(struct uart_port *uport)
{
	unsigned int geni_status;
	bool timeout;
	bool timeout, is_irq_masked;
	unsigned int dma_dbg;
	struct msm_geni_serial_port *port = GET_DEV_PORT(uport);

@@ -1258,9 +1276,15 @@ static void stop_tx_sequencer(struct uart_port *uport)
	port->m_cmd_done = false;
	port->m_cmd = true;
	reinit_completion(&port->m_cmd_timeout);
	/*
	 * Try to mask the interrupts before giving the
	 * cancel command as this might be in an atomic context
	 * from framework driver.
	 */
	is_irq_masked = msm_serial_try_disable_interrupts(uport);
	geni_cancel_m_cmd(uport->membase);

	timeout = geni_wait_for_cmd_done(uport);
	timeout = geni_wait_for_cmd_done(uport, is_irq_masked);
	if (timeout) {
		IPC_LOG_MSG(port->console_log, "%s: tx_cancel failed 0x%x\n",
		__func__, geni_read_reg_nolog(uport->membase, SE_GENI_STATUS));
@@ -1271,7 +1295,7 @@ static void stop_tx_sequencer(struct uart_port *uport)
		reinit_completion(&port->m_cmd_timeout);
		geni_abort_m_cmd(uport->membase);

		timeout = geni_wait_for_cmd_done(uport);
		timeout = geni_wait_for_cmd_done(uport, is_irq_masked);
		if (timeout) {
			IPC_LOG_MSG(port->console_log,
				"%s: tx abort failed 0x%x\n", __func__,
@@ -1290,7 +1314,8 @@ static void stop_tx_sequencer(struct uart_port *uport)
			geni_write_reg_nolog(1, uport->membase,
						SE_DMA_TX_FSM_RST);

			timeout = geni_wait_for_cmd_done(uport);
			timeout = geni_wait_for_cmd_done(uport,
							 is_irq_masked);
			if (timeout)
				IPC_LOG_MSG(port->ipc_log_misc,
				"%s: tx fsm reset failed\n", __func__);
@@ -1302,6 +1327,8 @@ static void stop_tx_sequencer(struct uart_port *uport)
			port->tx_dma = (dma_addr_t)NULL;
		}
	}
	/* Unmask the interrupts once the cancel operation is done. */
	msm_geni_serial_enable_interrupts(uport);
	port->m_cmd = false;
	port->xmit_size = 0;

@@ -1441,8 +1468,9 @@ static void msm_geni_serial_set_manual_flow(bool enable,
static void stop_rx_sequencer(struct uart_port *uport)
{
	unsigned int geni_status;
	bool timeout;
	bool timeout, is_irq_masked;
	struct msm_geni_serial_port *port = GET_DEV_PORT(uport);
	unsigned long flags = 0;

	IPC_LOG_MSG(port->ipc_log_misc, "%s\n", __func__);

@@ -1462,10 +1490,10 @@ static void stop_rx_sequencer(struct uart_port *uport)
	IPC_LOG_MSG(port->ipc_log_misc, "%s: Start 0x%x\n",
		    __func__, geni_status);
	/*
	 * Disabling the interrupts before giving the
	 * Try disabling interrupts before giving the
	 * cancel command as this might be in an atomic context.
	 */
	msm_geni_serial_disable_interrupts(uport);
	is_irq_masked = msm_serial_try_disable_interrupts(uport);
	geni_cancel_s_cmd(uport->membase);

	/*
@@ -1473,21 +1501,35 @@ static void stop_rx_sequencer(struct uart_port *uport)
	 * cancel control bit.
	 */
	mb();
	timeout = geni_wait_for_cmd_done(uport);
	timeout = geni_wait_for_cmd_done(uport, is_irq_masked);
	if (timeout) {
		bool is_rx_active;
		geni_status = geni_read_reg_nolog(uport->membase,
							SE_GENI_STATUS);
		/*
		 * Possible that stop_rx is called from system resume context
		 * for console usecase. In early resume, irq remains disabled
		 * in the system. call msm_geni_serial_handle_isr to clear
		 * the interrupts.
		 */
		is_rx_active = geni_status & S_GENI_CMD_ACTIVE;
		IPC_LOG_MSG(port->ipc_log_misc,
			"%s cancel failed 0x%x\n",  __func__, geni_status);
			    "%s cancel failed is_rx_active:%d 0x%x\n",
			    __func__, is_rx_active, geni_status);
		IPC_LOG_MSG(port->console_log,
			"%s cancel failed 0x%x\n",  __func__, geni_status);
			    "%s cancel failed is_rx_active:%d 0x%x\n",
			    __func__, is_rx_active, geni_status);
		if (uart_console(uport) && !is_rx_active) {
			msm_geni_serial_handle_isr(uport, &flags);
			goto exit_rx_seq;
		}
		port->s_cmd_done = false;
		reinit_completion(&port->s_cmd_timeout);
		geni_abort_s_cmd(uport->membase);
		/* Ensure this goes through before polling. */
		mb();

		timeout = geni_wait_for_cmd_done(uport);
		timeout = geni_wait_for_cmd_done(uport, is_irq_masked);
		if (timeout) {
			geni_status = geni_read_reg_nolog(uport->membase,
							SE_GENI_STATUS);
@@ -1526,7 +1568,7 @@ static int handle_rx_hs(struct uart_port *uport,
			unsigned int rx_fifo_wc,
			unsigned int rx_last_byte_valid,
			unsigned int rx_last,
			bool drop_rx)
			bool drop_rx, unsigned long *flags)
{
	unsigned char *rx_char;
	struct tty_port *tport;
@@ -1558,7 +1600,8 @@ static int handle_rx_hs(struct uart_port *uport,
	return ret;
}

static int msm_geni_serial_handle_rx(struct uart_port *uport, bool drop_rx)
static int msm_geni_serial_handle_rx(struct uart_port *uport, bool drop_rx,
				     unsigned long *flags)
{
	int ret = 0;
	unsigned int rx_fifo_status;
@@ -1577,7 +1620,7 @@ static int msm_geni_serial_handle_rx(struct uart_port *uport, bool drop_rx)
	rx_last = rx_fifo_status & RX_LAST;
	if (rx_fifo_wc)
		ret = port->handle_rx(uport, rx_fifo_wc, rx_last_byte_valid,
							rx_last, drop_rx);
						rx_last, drop_rx, flags);
	return ret;
}

@@ -1759,7 +1802,8 @@ static int msm_geni_serial_handle_dma_tx(struct uart_port *uport)
	return 0;
}

static void msm_geni_serial_handle_isr(struct uart_port *uport)
static void msm_geni_serial_handle_isr(struct uart_port *uport,
				       unsigned long *flags)
{
	unsigned int m_irq_status;
	unsigned int s_irq_status;
@@ -1833,13 +1877,20 @@ static void msm_geni_serial_handle_isr(struct uart_port *uport)
				"%s.sirq 0x%x break:%d\n",
				__func__, s_irq_status, uport->icount.brk);
		}

		if (s_irq_status & (S_CMD_CANCEL_EN | S_CMD_ABORT_EN))
		/*
		 * In case of stop_rx handling there is a chance
		 * for RX data can come in parallel. set drop_rx to
		 * avoid data push to framework from handle_rx_console()
		 * API for stop_rx case.
		 */
		if (s_irq_status & (S_CMD_CANCEL_EN | S_CMD_ABORT_EN)) {
			s_cmd_done = true;
			drop_rx = true;
		}

		if (s_irq_status & (S_RX_FIFO_WATERMARK_EN |
							S_RX_FIFO_LAST_EN))
			msm_geni_serial_handle_rx(uport, drop_rx);
			msm_geni_serial_handle_rx(uport, drop_rx, flags);
	} else {
		dma_tx_status = geni_read_reg_nolog(uport->membase,
							SE_DMA_TX_IRQ_STAT);
@@ -1933,7 +1984,7 @@ static irqreturn_t msm_geni_serial_isr(int isr, void *dev)
	unsigned long flags;

	spin_lock_irqsave(&uport->lock, flags);
	msm_geni_serial_handle_isr(uport);
	msm_geni_serial_handle_isr(uport, &flags);
	spin_unlock_irqrestore(&uport->lock, flags);
	return IRQ_HANDLED;
}
@@ -2102,7 +2153,7 @@ static int msm_geni_serial_port_setup(struct uart_port *uport)
		 * it else we could end up in data loss scenarios.
		 */
		msm_port->xfer_mode = FIFO_MODE;
		msm_geni_serial_disable_interrupts(uport);
		msm_serial_try_disable_interrupts(uport);
		msm_geni_serial_poll_tx_done(uport);
		msm_geni_serial_enable_interrupts(uport);
		se_get_packing_config(8, 1, false, &cfg0, &cfg1);