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

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

Merge "tty: serial: msm_geni_serial: Resolve race btw stop rx and cancel rx"

parents d5a06a1b f2d05793
Loading
Loading
Loading
Loading
+103 −58
Original line number Diff line number Diff line
@@ -210,6 +210,7 @@ struct msm_geni_serial_port {
	bool s_cmd;
	struct completion m_cmd_timeout;
	struct completion s_cmd_timeout;
	spinlock_t rx_lock;
};

static const struct uart_ops msm_geni_serial_pops;
@@ -236,6 +237,7 @@ static int msm_geni_serial_runtime_suspend(struct device *dev);
static int msm_geni_serial_get_ver_info(struct uart_port *uport);
static void msm_geni_serial_set_manual_flow(bool enable,
				struct msm_geni_serial_port *port);
static bool handle_rx_dma_xfer(u32 s_irq_status, struct uart_port *uport);
static int uart_line_id;

#define GET_DEV_PORT(uport) \
@@ -1438,6 +1440,7 @@ static int stop_rx_sequencer(struct uart_port *uport)
	unsigned long flags = 0;
	bool is_rx_active;
	unsigned int stale_delay;
	u32 dma_rx_status, s_irq_status;

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

@@ -1462,6 +1465,25 @@ static int stop_rx_sequencer(struct uart_port *uport)
		stale_delay = (STALE_COUNT * SEC_TO_USEC) / port->cur_baud;
		stale_delay = (2 * stale_delay) + SYSTEM_DELAY;
		udelay(stale_delay);

		dma_rx_status = geni_read_reg_nolog(uport->membase,
						SE_DMA_RX_IRQ_STAT);
		/* The transfer is completed at HW level and the completion
		 * interrupt is delayed. So process the transfer completion
		 * before issuing the cancel command to resolve the race
		 * btw cancel RX and completion interrupt.
		 */
		if (dma_rx_status) {
			s_irq_status = geni_read_reg_nolog(uport->membase,
							SE_GENI_S_IRQ_STATUS);
			geni_write_reg_nolog(s_irq_status, uport->membase,
							SE_GENI_S_IRQ_CLEAR);
			geni_se_dump_dbg_regs(&port->serial_rsc,
				uport->membase, port->ipc_log_misc);
			IPC_LOG_MSG(port->ipc_log_misc, "%s: Interrupt delay\n",
					 __func__);
			handle_rx_dma_xfer(s_irq_status, uport);
		}
	}

	IPC_LOG_MSG(port->ipc_log_misc, "%s: Start 0x%x\n",
@@ -1863,6 +1885,77 @@ static int msm_geni_serial_handle_dma_tx(struct uart_port *uport)
	return 0;
}

static bool handle_rx_dma_xfer(u32 s_irq_status, struct uart_port *uport)
{
	bool ret = false;
	bool drop_rx = false;
	struct msm_geni_serial_port *msm_port = GET_DEV_PORT(uport);
	u32 dma_rx_status;

	spin_lock(&msm_port->rx_lock);
	dma_rx_status = geni_read_reg_nolog(uport->membase,
						SE_DMA_RX_IRQ_STAT);

	if (dma_rx_status) {
		geni_write_reg_nolog(dma_rx_status, uport->membase,
					SE_DMA_RX_IRQ_CLR);

		if (dma_rx_status & RX_RESET_DONE) {
			IPC_LOG_MSG(msm_port->ipc_log_misc,
			"%s.Reset done.  0x%x.\n", __func__, dma_rx_status);
			ret = true;
			goto exit;
		}

		if (dma_rx_status & UART_DMA_RX_ERRS) {
			if (dma_rx_status & UART_DMA_RX_PARITY_ERR)
				uport->icount.parity++;
			IPC_LOG_MSG(msm_port->ipc_log_misc,
				"%s.Rx Errors.  0x%x parity:%d\n",
					__func__, dma_rx_status,
					uport->icount.parity);
			drop_rx = true;
		} else if (dma_rx_status & UART_DMA_RX_BREAK) {
			uport->icount.brk++;
			IPC_LOG_MSG(msm_port->ipc_log_misc,
				"%s.Rx Errors.  0x%x break:%d\n",
				__func__, dma_rx_status,
				uport->icount.brk);
		}

		if (dma_rx_status & RX_EOT ||
				dma_rx_status & RX_DMA_DONE) {
			msm_geni_serial_handle_dma_rx(uport,
						drop_rx);
			if (!(dma_rx_status & RX_GENI_CANCEL_IRQ)) {
				geni_se_rx_dma_start(uport->membase,
				DMA_RX_BUF_SIZE, &msm_port->rx_dma);
			} else {
				IPC_LOG_MSG(msm_port->ipc_log_misc,
				"%s. not mapping rx dma\n",
				__func__);
			}
		}
		if (dma_rx_status & RX_SBE) {
			IPC_LOG_MSG(msm_port->ipc_log_misc,
				"%s.Rx Errors.  0x%x\n",
				__func__, dma_rx_status);
			WARN_ON(1);
		}

		if (dma_rx_status & (RX_EOT | RX_GENI_CANCEL_IRQ | RX_DMA_DONE))
			ret = true;

	}

	if (s_irq_status & (S_CMD_CANCEL_EN | S_CMD_ABORT_EN))
		ret = true;

exit:
	spin_unlock(&msm_port->rx_lock);
	return ret;
}

static void msm_geni_serial_handle_isr(struct uart_port *uport,
				       unsigned long *flags,
				       bool is_irq_masked)
@@ -1994,70 +2087,19 @@ static void msm_geni_serial_handle_isr(struct uart_port *uport,
						TX_GENI_CANCEL_IRQ))
				m_cmd_done = true;

			if (m_irq_status & (M_CMD_CANCEL_EN | M_CMD_ABORT_EN))
				m_cmd_done = true;

			if ((dma_tx_status & TX_DMA_DONE) && !m_cmd_done)
				msm_geni_serial_handle_dma_tx(uport);
		}

		if (dma_rx_status) {
			geni_write_reg_nolog(dma_rx_status, uport->membase,
						SE_DMA_RX_IRQ_CLR);

			if (dma_rx_status & RX_RESET_DONE) {
				IPC_LOG_MSG(msm_port->ipc_log_misc,
					"%s.Reset done.  0x%x.\n",
						__func__, dma_rx_status);
				s_cmd_done = true;
				goto exit_geni_serial_isr;
			}

			if (dma_rx_status & UART_DMA_RX_ERRS) {
				if (dma_rx_status & UART_DMA_RX_PARITY_ERR)
					uport->icount.parity++;
				IPC_LOG_MSG(msm_port->ipc_log_misc,
					"%s.Rx Errors.  0x%x parity:%d\n",
					__func__, dma_rx_status,
					uport->icount.parity);
				drop_rx = true;
			} else if (dma_rx_status & UART_DMA_RX_BREAK) {
				uport->icount.brk++;
				IPC_LOG_MSG(msm_port->ipc_log_misc,
					"%s.Rx Errors.  0x%x break:%d\n",
					__func__, dma_rx_status,
					uport->icount.brk);
			}

			if (dma_rx_status & RX_EOT ||
					dma_rx_status & RX_DMA_DONE) {
				msm_geni_serial_handle_dma_rx(uport,
							drop_rx);
				if (!(dma_rx_status & RX_GENI_CANCEL_IRQ)) {
					geni_se_rx_dma_start(uport->membase,
					DMA_RX_BUF_SIZE, &msm_port->rx_dma);
				} else {
					IPC_LOG_MSG(msm_port->ipc_log_misc,
						"%s. not mapping rx dma\n",
						__func__);
				}
			}

			if (dma_rx_status & RX_SBE) {
				IPC_LOG_MSG(msm_port->ipc_log_misc,
					"%s.Rx Errors.  0x%x\n",
					__func__, dma_rx_status);
				WARN_ON(1);
			}
		if (m_irq_status & (M_CMD_CANCEL_EN | M_CMD_ABORT_EN))
			m_cmd_done = true;

			if (dma_rx_status & (RX_EOT | RX_GENI_CANCEL_IRQ |
								RX_DMA_DONE))
				s_cmd_done = true;
		if (dma_rx_status)
			s_cmd_done = handle_rx_dma_xfer(s_irq_status, uport);

		if (s_irq_status & (S_CMD_CANCEL_EN | S_CMD_ABORT_EN))
			s_cmd_done = true;
	}
	}

exit_geni_serial_isr:
	if (m_cmd_done) {
@@ -3386,6 +3428,9 @@ static int msm_geni_serial_probe(struct platform_device *pdev)
	if (ret)
		goto exit_wakeup_unregister;

	if (!uart_console(uport))
		spin_lock_init(&dev_port->rx_lock);

	IPC_LOG_MSG(dev_port->ipc_log_misc, "%s: port:%s irq:%d\n", __func__,
		    uport->name, uport->irq);