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

Commit 62bf7693 authored by Kiran Gunda's avatar Kiran Gunda
Browse files

msm_serial_hs: Avoid RX getting stuck during rapid clock on/off



Due to race conditions in clock off and clock on sequence
BAM RX descriptor doesn't get queued or may get queued multiple
times. These issues may showup when the client issues rapid clock-on
clock-off operations.
This patch optimizes clock-off sequence while synchronizing clock
off/on with the BAM RX callback (In BAM RX callback, Forcestale
is not issued for every BAM RX callback).

CRs-Fixed: 601140
Change-Id: I882bb57b597f5789daa5fef33a4bc8da174110d0
Signed-off-by: default avatarKiran Gunda <kgunda@codeaurora.org>
parent 63627cc2
Loading
Loading
Loading
Loading
+79 −104
Original line number Diff line number Diff line
@@ -95,7 +95,6 @@ module_param_named(debug_mask, hs_serial_debug_mask,

#define MSM_HS_DBG(x...) do { \
	if (hs_serial_debug_mask >= DBG_LEV) { \
		pr_debug(x); \
		if (ipc_msm_hs_log_ctxt) \
			ipc_log_string(ipc_msm_hs_log_ctxt, x); \
	} \
@@ -103,7 +102,6 @@ module_param_named(debug_mask, hs_serial_debug_mask,

#define MSM_HS_INFO(x...) do { \
	if (hs_serial_debug_mask >= INFO_LEV) {\
		pr_info(x); \
		if (ipc_msm_hs_log_ctxt) \
			ipc_log_string(ipc_msm_hs_log_ctxt, x); \
	} \
@@ -207,6 +205,7 @@ struct msm_hs_rx {
	struct delayed_work flip_insert_work;
	struct tasklet_struct tlet;
	struct msm_hs_sps_ep_conn_data prod;
	bool rx_cmd_queued;
};
enum buffer_states {
	NONE_PENDING = 0x0,
@@ -273,6 +272,8 @@ struct msm_hs_port {
	int rx_count_callback;
	bool rx_bam_inprogress;
	unsigned int *reg_ptr;
	wait_queue_head_t bam_disconnect_wait;

};

unsigned int regmap_nonblsp[UART_DM_LAST] = {
@@ -428,6 +429,7 @@ static int msm_hs_clock_vote(struct msm_hs_port *msm_uport)
			}
		}
		msm_uport->clk_state = MSM_HS_CLK_ON;
		MSM_HS_DBG("%s: Clock ON successful\n", __func__);
	}


@@ -453,6 +455,7 @@ static void msm_hs_clock_unvote(struct msm_hs_port *msm_uport)
		if (msm_uport->pclk)
			clk_disable_unprepare(msm_uport->pclk);
		msm_uport->clk_state = MSM_HS_CLK_OFF;
		MSM_HS_DBG("%s: Clock OFF successful\n", __func__);
	}
}

@@ -598,19 +601,28 @@ static void dump_uart_hs_registers(struct msm_hs_port *msm_uport)
		MSM_HS_WARN("%s: Failed.Clocks are OFF\n", __func__);
		return;
	}
	MSM_HS_DBG("UART_DM_MR1:%x\n", msm_hs_read(uport,
		UART_DM_MR1));
	MSM_HS_DBG("UART_DM_MR2:%x\n", msm_hs_read(uport,
		UART_DM_MR2));
	MSM_HS_DBG("UART_DM_IPR:%x\n", msm_hs_read(uport,
		UART_DM_IPR));
	MSM_HS_DBG("UART_DM_RFWR:%x\n", msm_hs_read(uport,
		UART_DM_RFWR));
	MSM_HS_DBG("UART_DM_SR:%x\n", msm_hs_read(uport,
		UART_DM_SR));
	MSM_HS_DBG("UART_DM_IMR: %x\n", msm_hs_read(uport,
		UART_DM_IMR));
	MSM_HS_DBG("=============================================\n");

	MSM_HS_DBG(
	"MR1:%x MR2:%x TFWR:%x RFWR:%x DMEN:%x IMR:%x MISR:%x NCF_TX:%x\n",
	msm_hs_read(uport, UART_DM_MR1),
	msm_hs_read(uport, UART_DM_MR2),
	msm_hs_read(uport, UART_DM_TFWR),
	msm_hs_read(uport, UART_DM_RFWR),
	msm_hs_read(uport, UART_DM_DMEN),
	msm_hs_read(uport, UART_DM_IMR),
	msm_hs_read(uport, UART_DM_MISR),
	msm_hs_read(uport, UART_DM_NCF_TX));
	MSM_HS_INFO("SR:%x ISR:%x DMRX:%x RX_SNAP:%x TXFS:%x RXFS:%x\n",
	msm_hs_read(uport, UART_DM_SR),
	msm_hs_read(uport, UART_DM_ISR),
	msm_hs_read(uport, UART_DM_DMRX),
	msm_hs_read(uport, UART_DM_RX_TOTAL_SNAP),
	msm_hs_read(uport, UART_DM_TXFS),
	msm_hs_read(uport, UART_DM_RXFS));
	MSM_HS_DBG("clk_req_state:0x%x rx.flush:%u\n",
				msm_uport->clk_req_off_state,
					msm_uport->rx.flush);
	MSM_HS_DBG("clk_state:%d", msm_uport->clk_state);
}

static void msm_hs_release_port(struct uart_port *port)
@@ -1322,6 +1334,9 @@ static void hsuart_disconnect_rx_endpoint_work(struct work_struct *w)

	wake_lock_timeout(&msm_uport->rx.wake_lock, HZ / 2);
	msm_uport->rx.flush = FLUSH_SHUTDOWN;
	MSM_HS_DBG("%s: Calling Completion\n", __func__);
	wake_up(&msm_uport->bam_disconnect_wait);
	MSM_HS_DBG("%s: Done Completion\n", __func__);
	wake_up(&msm_uport->rx.wait);
}

@@ -1478,8 +1493,6 @@ static void msm_hs_start_rx_locked(struct uart_port *uport)
	msm_hs_write(uport, UART_DM_CR, RESET_STALE_INT);
	msm_hs_write(uport, UART_DM_DMRX, UARTDM_RX_BUF_SIZE);
	msm_hs_write(uport, UART_DM_CR, STALE_EVENT_ENABLE);
	msm_uport->imr_reg |= UARTDM_ISR_RXLEV_BMSK;

	/*
	 * Enable UARTDM Rx Interface as previously it has been
	 * disable in set_termios before configuring baud rate.
@@ -1519,10 +1532,8 @@ static void msm_hs_start_rx_locked(struct uart_port *uport)
		sps_transfer_one(sps_pipe_handle, rx->rbuffer,
			UARTDM_RX_BUF_SIZE, msm_uport, flags);
		msm_uport->rx_bam_inprogress = false;
		msm_uport->rx.rx_cmd_queued = true;
		wake_up(&msm_uport->rx.wait);
	} else {
		msm_dmov_enqueue_cmd(msm_uport->dma_rx_channel,
				&msm_uport->rx.xfer);
	}
	MSM_HS_DBG("%s:Enqueue Rx Cmd\n", __func__);
	dump_uart_hs_registers(msm_uport);
@@ -1600,6 +1611,8 @@ static void msm_serial_hs_rx_tlet(unsigned long tlet_ptr)
	notify = &msm_uport->notify;
	rx = &msm_uport->rx;

	msm_uport->rx.rx_cmd_queued = false;

	status = msm_hs_read(uport, UART_DM_SR);

	spin_lock_irqsave(&uport->lock, flags);
@@ -1646,19 +1659,17 @@ static void msm_serial_hs_rx_tlet(unsigned long tlet_ptr)
		}
	}

	if (error_f)
	if (error_f) {
		if (msm_uport->clk_state == MSM_HS_CLK_ON)
			msm_hs_write(uport, UART_DM_CR, RESET_ERROR_STATUS);

	if (msm_uport->clk_req_off_state == CLK_REQ_OFF_FLUSH_ISSUED)
		msm_uport->clk_req_off_state = CLK_REQ_OFF_RXSTALE_FLUSHED;
		else
			MSM_HS_WARN("%s: Failed.Clocks are OFF\n", __func__);
	}
	flush = msm_uport->rx.flush;
	if (flush == FLUSH_IGNORE)
		if (!msm_uport->rx.buffer_pending)
		if (!msm_uport->rx.buffer_pending) {
			MSM_HS_DBG("%s: calling start_rx_locked\n", __func__);
			msm_hs_start_rx_locked(uport);

	if (flush == FLUSH_STOP) {
		msm_uport->rx.flush = FLUSH_SHUTDOWN;
		wake_up(&msm_uport->rx.wait);
		}
	if (flush >= FLUSH_DATA_INVALID)
		goto out;
@@ -1666,9 +1677,12 @@ static void msm_serial_hs_rx_tlet(unsigned long tlet_ptr)
	if (is_blsp_uart(msm_uport)) {
		rx_count = msm_uport->rx_count_callback;
	} else {
		if (msm_uport->clk_state == MSM_HS_CLK_ON) {
			rx_count = msm_hs_read(uport, UART_DM_RX_TOTAL_SNAP);
			/* order the read of rx.buffer */
			rmb();
		} else
			MSM_HS_WARN("%s: Failed.Clocks are OFF\n", __func__);
	}

	MSM_HS_DBG("%s():[UART_RX]<%d>\n", __func__, rx_count);
@@ -1684,26 +1698,22 @@ static void msm_serial_hs_rx_tlet(unsigned long tlet_ptr)
				retval << 5 | (rx_count - retval) << 16;
		}
	}

	MSM_HS_DBG("%s() read rx buffer complete, issue sw stale", __func__);
	msm_hs_write(uport, UART_DM_CR, FORCE_STALE_EVENT);
	/* order the read of rx.buffer and the start of next rx xfer */
	wmb();

	if (!msm_uport->rx.buffer_pending) {
	if (!msm_uport->rx.buffer_pending && !msm_uport->rx.rx_cmd_queued) {
		if (is_blsp_uart(msm_uport)) {
			msm_uport->rx.flush = FLUSH_NONE;
			msm_uport->rx_bam_inprogress = true;
			sps_pipe_handle = rx->prod.pipe_handle;
			MSM_HS_DBG("Queing bam descriptor\n");
			/* Queue transfer request to SPS */
			sps_transfer_one(sps_pipe_handle, rx->rbuffer,
				UARTDM_RX_BUF_SIZE, msm_uport, sps_flags);
			msm_uport->rx_bam_inprogress = false;
			msm_uport->rx.rx_cmd_queued = true;
			wake_up(&msm_uport->rx.wait);
		} else {

		} else
			msm_hs_start_rx_locked(uport);
	}
	}
out:
	if (msm_uport->rx.buffer_pending) {
		MSM_HS_WARN("tty buffer exhausted. Stalling\n");
@@ -2075,13 +2085,13 @@ static int msm_hs_check_clock_off(struct uart_port *uport)
{
	unsigned long sr_status;
	unsigned long flags;
	int ret;
	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
	struct circ_buf *tx_buf = &uport->state->xmit;

	mutex_lock(&msm_uport->clk_mutex);
	spin_lock_irqsave(&uport->lock, flags);

	MSM_HS_DBG("In %s:\n", __func__);
	/* Cancel if tx tty buffer is not empty, dma is in flight,
	 * or tx fifo is not empty
	 */
@@ -2090,6 +2100,8 @@ static int msm_hs_check_clock_off(struct uart_port *uport)
	    msm_uport->imr_reg & UARTDM_ISR_TXLEV_BMSK) {
		spin_unlock_irqrestore(&uport->lock, flags);
		mutex_unlock(&msm_uport->clk_mutex);
		if (msm_uport->clk_state == MSM_HS_CLK_REQUEST_OFF)
			msm_uport->clk_state = MSM_HS_CLK_ON;
		MSM_HS_DBG("%s(): clkstate %d", __func__, msm_uport->clk_state);
		return -1;
	}
@@ -2105,41 +2117,6 @@ static int msm_hs_check_clock_off(struct uart_port *uport)
		return 0;  /* retry */
	}

	/* Make sure forced RXSTALE flush complete */
	switch (msm_uport->clk_req_off_state) {
	case CLK_REQ_OFF_START:
		msm_uport->clk_req_off_state = CLK_REQ_OFF_RXSTALE_ISSUED;

		if (!is_blsp_uart(msm_uport)) {
			msm_hs_write(uport, UART_DM_CR, FORCE_STALE_EVENT);
			/*
			* Before returning make sure that device writel
			* completed. Hence mb() requires here.
			*/
			mb();
		}
		spin_unlock_irqrestore(&uport->lock, flags);
		mutex_unlock(&msm_uport->clk_mutex);
		MSM_HS_DBG("%s(): CLK_REQ_OFF_START -> %d",
			__func__, msm_uport->clk_req_off_state);
		return 0;  /* RXSTALE flush not complete - retry */
	case CLK_REQ_OFF_RXSTALE_ISSUED:
	case CLK_REQ_OFF_FLUSH_ISSUED:
		spin_unlock_irqrestore(&uport->lock, flags);
		if (is_blsp_uart(msm_uport)) {
			msm_uport->clk_req_off_state =
				CLK_REQ_OFF_RXSTALE_FLUSHED;
		}
		mutex_unlock(&msm_uport->clk_mutex);
		MSM_HS_DBG("%s(): CLK_REQ_OFF STALE/FLUSH ISSUED -> %d",
			__func__, msm_uport->clk_req_off_state);
		return 0;  /* RXSTALE flush not complete - retry */
	case CLK_REQ_OFF_RXSTALE_FLUSHED:
		MSM_HS_DBG("%s(): CLK_REQ_OFF STALE FLUSHED -> %d",
			__func__, msm_uport->clk_req_off_state);
		break;  /* continue */
	}

	if (msm_uport->rx.flush != FLUSH_SHUTDOWN) {
		if (msm_uport->rx.flush == FLUSH_NONE) {
			msm_hs_stop_rx_locked(uport);
@@ -2147,18 +2124,9 @@ static int msm_hs_check_clock_off(struct uart_port *uport)
				msm_uport->rx_discard_flush_issued = true;
		}

		MSM_HS_DBG("%s: rx.flush %d clk_state %d\n", __func__,
			msm_uport->rx.flush, msm_uport->clk_state);
		spin_unlock_irqrestore(&uport->lock, flags);
		if (msm_uport->rx_discard_flush_issued) {
			MSM_HS_DBG("%s(): wainting for flush completion.\n",
								__func__);
			ret = wait_event_timeout(msm_uport->rx.wait,
				msm_uport->rx_discard_flush_issued == false,
				RX_FLUSH_COMPLETE_TIMEOUT);
			if (!ret)
				MSM_HS_ERR("%s(): Flush complete pending.\n",
								__func__);
		}

		mutex_unlock(&msm_uport->clk_mutex);
		return 0;  /* come back later to really clock off */
	}
@@ -2242,11 +2210,6 @@ static irqreturn_t msm_hs_isr(int irq, void *dev)
		mb();
		MSM_HS_DBG("%s:Stal Interrupt\n", __func__);

		if (msm_uport->clk_req_off_state ==
					CLK_REQ_OFF_RXSTALE_ISSUED)
			msm_uport->clk_req_off_state =
					CLK_REQ_OFF_FLUSH_ISSUED;

		if (!is_blsp_uart(msm_uport) && (rx->flush == FLUSH_NONE)) {
			rx->flush = FLUSH_DATA_READY;
			msm_dmov_flush(msm_uport->dma_rx_channel, 1);
@@ -2346,7 +2309,6 @@ void msm_hs_request_clock_off(struct uart_port *uport) {
	spin_lock_irqsave(&uport->lock, flags);
	if (msm_uport->clk_state == MSM_HS_CLK_ON) {
		msm_uport->clk_state = MSM_HS_CLK_REQUEST_OFF;
		msm_uport->clk_req_off_state = CLK_REQ_OFF_START;
		data = msm_hs_read(uport, UART_DM_SR);
		MSM_HS_DBG("%s(): TXEMT, queuing clock off work\n",
			__func__);
@@ -2377,7 +2339,7 @@ void msm_hs_request_clock_on(struct uart_port *uport)

		ret = msm_hs_clock_vote(msm_uport);
		if (ret) {
			dev_err(uport->dev, "Clock ON Failure"
			MSM_HS_INFO("Clock ON Failure"
			"For UART CLK Stalling HSUART\n");
			break;
		}
@@ -2385,6 +2347,22 @@ void msm_hs_request_clock_on(struct uart_port *uport)
		spin_lock_irqsave(&uport->lock, flags);
		/* else fall-through */
	case MSM_HS_CLK_REQUEST_OFF:
		hrtimer_cancel(&msm_uport->clk_off_timer);
		if (msm_uport->rx.flush == FLUSH_STOP) {
			spin_unlock_irqrestore(&uport->lock, flags);
			MSM_HS_DBG("%s:Calling wait forxcompletion\n",
					__func__);
			ret = wait_event_timeout(msm_uport->bam_disconnect_wait,
				msm_uport->rx.flush == FLUSH_SHUTDOWN, 300);
			if (!ret)
				MSM_HS_ERR("BAM Disconnect not happened\n");
			spin_lock_irqsave(&uport->lock, flags);
			MSM_HS_DBG("%s:DONE wait for completion\n", __func__);
		}
		MSM_HS_DBG("%s:clock state %d\n\n", __func__,
				msm_uport->clk_state);
		if (msm_uport->clk_state == MSM_HS_CLK_REQUEST_OFF)
				msm_uport->clk_state = MSM_HS_CLK_ON;
		if (msm_uport->rx.flush == FLUSH_STOP ||
		    msm_uport->rx.flush == FLUSH_SHUTDOWN) {
			msm_hs_write(uport, UART_DM_CR, RESET_RX);
@@ -2397,7 +2375,8 @@ void msm_hs_request_clock_on(struct uart_port *uport)
			/* Complete above device write. Hence mb() here. */
			mb();
		}
		hrtimer_try_to_cancel(&msm_uport->clk_off_timer);

		MSM_HS_DBG("%s: rx.flush %d\n", __func__, msm_uport->rx.flush);
		if (msm_uport->rx.flush == FLUSH_SHUTDOWN) {
			if (is_blsp_uart(msm_uport)) {
				spin_unlock_irqrestore(&uport->lock, flags);
@@ -2408,7 +2387,7 @@ void msm_hs_request_clock_on(struct uart_port *uport)
		}
		if (msm_uport->rx.flush == FLUSH_STOP)
			msm_uport->rx.flush = FLUSH_IGNORE;
		msm_uport->clk_state = MSM_HS_CLK_ON;

		break;
	case MSM_HS_CLK_ON:
		break;
@@ -2721,17 +2700,12 @@ static int msm_hs_startup(struct uart_port *uport)
		}
		disable_irq(msm_uport->wakeup.irq);
	}

	msm_hs_clock_vote(msm_uport);

	spin_lock_irqsave(&uport->lock, flags);

	msm_hs_start_rx_locked(uport);

	spin_unlock_irqrestore(&uport->lock, flags);

	msm_hs_clock_unvote(msm_uport);

	pm_runtime_enable(uport->dev);

	return 0;
@@ -2767,6 +2741,7 @@ static int uartdm_init_port(struct uart_port *uport)

	init_waitqueue_head(&rx->wait);
	init_waitqueue_head(&tx->wait);
	init_waitqueue_head(&msm_uport->bam_disconnect_wait);
	wake_lock_init(&rx->wake_lock, WAKE_LOCK_SUSPEND, "msm_serial_hs_rx");
	wake_lock_init(&msm_uport->dma_wake_lock, WAKE_LOCK_SUSPEND,
		       "msm_serial_hs_dma");