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

Commit cfe319dd authored by Naveen Kaje's avatar Naveen Kaje
Browse files

msm_serial_hs: wait for clock-off to complete before shutdown



Make sure clock-off request completes before closing the port to
avoid unclocked hardware access.

CRs-Fixed: 648827
Change-Id: I953a06c104830c5515551d5edc4dd9c9a037203f
Signed-off-by: default avatarNaveen Kaje <nkaje@codeaurora.org>
parent 760e6e62
Loading
Loading
Loading
Loading
+45 −18
Original line number Diff line number Diff line
@@ -336,6 +336,7 @@ static int msm_hs_clock_vote(struct msm_hs_port *msm_uport)
{
	int rc = 0;

	mutex_lock(&msm_uport->clk_mutex);
	if (1 == atomic_inc_return(&msm_uport->clk_count)) {
		msm_hs_bus_voting(msm_uport, BUS_SCALING);
		/* Turn on core clk and iface clk */
@@ -345,6 +346,7 @@ static int msm_hs_clock_vote(struct msm_hs_port *msm_uport)
				dev_err(msm_uport->uport.dev,
					"%s: Could not turn on pclk [%d]\n",
					__func__, rc);
				mutex_unlock(&msm_uport->clk_mutex);
				return rc;
			}
		}
@@ -355,11 +357,13 @@ static int msm_hs_clock_vote(struct msm_hs_port *msm_uport)
				"%s: Could not turn on core clk [%d]\n",
				__func__, rc);
			clk_disable_unprepare(msm_uport->pclk);
			mutex_unlock(&msm_uport->clk_mutex);
			return rc;
		}
		msm_uport->clk_state = MSM_HS_CLK_ON;
		MSM_HS_DBG("%s: Clock ON successful\n", __func__);
	}
	mutex_unlock(&msm_uport->clk_mutex);


	return rc;
@@ -376,6 +380,7 @@ static void msm_hs_clock_unvote(struct msm_hs_port *msm_uport)
		return;
	}

	mutex_lock(&msm_uport->clk_mutex);
	rc = atomic_dec_return(&msm_uport->clk_count);
	if (0 == rc) {
		/* Turn off the core clk and iface clk*/
@@ -387,6 +392,7 @@ static void msm_hs_clock_unvote(struct msm_hs_port *msm_uport)
		msm_uport->clk_state = MSM_HS_CLK_OFF;
		MSM_HS_DBG("%s: Clock OFF successful\n", __func__);
	}
	mutex_unlock(&msm_uport->clk_mutex);
}

/* Check if the uport line number matches with user id stored in pdata.
@@ -1821,8 +1827,6 @@ static int msm_hs_check_clock_off(struct uart_port *uport)
	if (msm_uport->clk_state != MSM_HS_CLK_REQUEST_OFF ||
	    !uart_circ_empty(tx_buf) || msm_uport->tx.dma_in_flight ||
	    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;
			/* Pulling RFR line high */
@@ -1833,6 +1837,8 @@ static int msm_hs_check_clock_off(struct uart_port *uport)
			msm_hs_write(uport, UART_DM_MR1, data);
			mb();
		}
		spin_unlock_irqrestore(&uport->lock, flags);
		mutex_unlock(&msm_uport->clk_mutex);
		MSM_HS_DBG("%s(): clkstate %d", __func__, msm_uport->clk_state);
		return -1;
	}
@@ -1870,7 +1876,9 @@ static int msm_hs_check_clock_off(struct uart_port *uport)
	mb();

	/* we really want to clock off */
	mutex_unlock(&msm_uport->clk_mutex);
	msm_hs_clock_unvote(msm_uport);
	mutex_lock(&msm_uport->clk_mutex);

	spin_lock_irqsave(&uport->lock, flags);
	if (use_low_power_wakeup(msm_uport)) {
@@ -2091,7 +2099,9 @@ void msm_hs_request_clock_on(struct uart_port *uport)
		}
		spin_unlock_irqrestore(&uport->lock, flags);

		mutex_unlock(&msm_uport->clk_mutex);
		ret = msm_hs_clock_vote(msm_uport);
		mutex_lock(&msm_uport->clk_mutex);
		if (ret) {
			MSM_HS_INFO("Clock ON Failure"
			"For UART CLK Stalling HSUART\n");
@@ -2106,8 +2116,10 @@ void msm_hs_request_clock_on(struct uart_port *uport)
			spin_unlock_irqrestore(&uport->lock, flags);
			MSM_HS_DBG("%s:Calling wait forxcompletion\n",
					__func__);
			mutex_unlock(&msm_uport->clk_mutex);
			ret = wait_event_timeout(msm_uport->bam_disconnect_wait,
				msm_uport->rx.flush == FLUSH_SHUTDOWN, 300);
			mutex_lock(&msm_uport->clk_mutex);
			if (!ret)
				MSM_HS_ERR("BAM Disconnect not happened\n");
			spin_lock_irqsave(&uport->lock, flags);
@@ -3180,8 +3192,25 @@ static void msm_hs_shutdown(struct uart_port *uport)
	struct msm_hs_tx *tx = &msm_uport->tx;
	struct sps_pipe *sps_pipe_handle = tx->cons.pipe_handle;

	MSM_HS_INFO("%s()\n", __func__);
	msm_hs_clock_vote(msm_uport);
	/*
	 * cancel the hrtimer first so that
	 * clk_state can not change in flight
	 */
	hrtimer_cancel(&msm_uport->clk_off_timer);
	flush_work(&msm_uport->clock_off_w);

	if (use_low_power_wakeup(msm_uport))
		irq_set_irq_wake(msm_uport->wakeup.irq, 0);

	/* wake irq or uart irq is active depending on clk_state */
	if (msm_uport->clk_state == MSM_HS_CLK_OFF) {
		if (use_low_power_wakeup(msm_uport))
			disable_irq(msm_uport->wakeup.irq);
	} else {
		disable_irq(uport->irq);
		wake_unlock(&msm_uport->dma_wake_lock);
	}

	/* make sure tx tasklet finishes */
	tasklet_kill(&msm_uport->tx.tlet);
	ret = wait_event_timeout(msm_uport->tx.wait,
@@ -3189,21 +3218,20 @@ static void msm_hs_shutdown(struct uart_port *uport)
	if (!ret)
		MSM_HS_WARN("Shutdown called when tx buff not empty");

	/* BAM Disconnect for TX */
	ret = sps_disconnect(sps_pipe_handle);
	if (ret)
		MSM_HS_ERR("%s(): sps_disconnect failed\n",
					__func__);
	/* make sure rx tasklet finishes */
	tasklet_kill(&msm_uport->rx.tlet);
	wait_event(msm_uport->rx.wait, msm_uport->rx.flush == FLUSH_SHUTDOWN);
	cancel_delayed_work_sync(&msm_uport->rx.flip_insert_work);
	flush_workqueue(msm_uport->hsuart_wq);
	WARN_ON(msm_uport->rx.flush < FLUSH_STOP);
	pm_runtime_disable(uport->dev);
	msm_uport->rx.buffer_pending = NONE_PENDING;
	MSM_HS_DBG("%s(): tx, rx events complete", __func__);

	msm_hs_clock_vote(msm_uport);
	mutex_lock(&msm_uport->clk_mutex);
	/* BAM Disconnect for TX */
	ret = sps_disconnect(sps_pipe_handle);
	if (ret)
		MSM_HS_ERR("%s(): sps_disconnect failed\n",
					__func__);
	WARN_ON(msm_uport->rx.flush < FLUSH_STOP);
	/* Disable the transmitter */
	msm_hs_write(uport, UART_DM_CR, UARTDM_CR_TX_DISABLE_BMSK);
	/* Disable the receiver */
@@ -3216,22 +3244,21 @@ static void msm_hs_shutdown(struct uart_port *uport)
	 * Hence mb() requires here.
	 */
	mb();
	mutex_unlock(&msm_uport->clk_mutex);

	hrtimer_cancel(&msm_uport->clk_off_timer);
	msm_uport->rx.buffer_pending = NONE_PENDING;
	MSM_HS_DBG("%s(): tx, rx events complete", __func__);
	msm_hs_clock_unvote(msm_uport);
	if (msm_uport->clk_state != MSM_HS_CLK_OFF) {
		/* to balance clk_state */
		msm_hs_clock_unvote(msm_uport);
		wake_unlock(&msm_uport->dma_wake_lock);
	}

	pm_runtime_disable(uport->dev);
	msm_uport->clk_state = MSM_HS_CLK_PORT_OFF;
	dma_unmap_single(uport->dev, msm_uport->tx.dma_base,
			 UART_XMIT_SIZE, DMA_TO_DEVICE);

	if (use_low_power_wakeup(msm_uport))
		irq_set_irq_wake(msm_uport->wakeup.irq, 0);

	/* Free the interrupt */
	free_irq(uport->irq, msm_uport);
	if (use_low_power_wakeup(msm_uport))