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

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

Merge "mhi: core: add support to pause or resume data transfer"

parents dad10912 dc64b4e1
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -350,6 +350,7 @@ enum mhi_cmd_type {
enum MHI_CMD {
	MHI_CMD_RESET_CHAN,
	MHI_CMD_START_CHAN,
	MHI_CMD_STOP_CHAN,
	MHI_CMD_TIMSYNC_CFG,
};

+119 −0
Original line number Diff line number Diff line
@@ -1578,6 +1578,11 @@ int mhi_send_cmd(struct mhi_controller *mhi_cntrl,
		cmd_tre->dword[0] = MHI_TRE_CMD_START_DWORD0;
		cmd_tre->dword[1] = MHI_TRE_CMD_START_DWORD1(chan);
		break;
	case MHI_CMD_STOP_CHAN:
		cmd_tre->ptr = MHI_TRE_CMD_STOP_PTR;
		cmd_tre->dword[0] = MHI_TRE_CMD_STOP_DWORD0;
		cmd_tre->dword[1] = MHI_TRE_CMD_STOP_DWORD1(chan);
		break;
	case MHI_CMD_TIMSYNC_CFG:
		cmd_tre->ptr = MHI_TRE_CMD_TSYNC_CFG_PTR;
		cmd_tre->dword[0] = MHI_TRE_CMD_TSYNC_CFG_DWORD0;
@@ -2031,6 +2036,120 @@ void mhi_unprepare_from_transfer(struct mhi_device *mhi_dev)
}
EXPORT_SYMBOL(mhi_unprepare_from_transfer);

static int mhi_update_channel_state(struct mhi_controller *mhi_cntrl,
				    struct mhi_chan *mhi_chan,
				    enum MHI_CMD cmd)
{
	int ret = -EIO;

	mutex_lock(&mhi_chan->mutex);

	MHI_VERB("Changing chan:%d to state:%s\n",
		 mhi_chan->chan, cmd == MHI_CMD_START_CHAN ? "START" : "STOP");

	/* if channel is not active state state do not allow to state change */
	if (mhi_chan->ch_state != MHI_CH_STATE_ENABLED) {
		ret = -EINVAL;
		MHI_LOG("channel:%d is not in active state, ch_state%d\n",
			mhi_chan->chan, mhi_chan->ch_state);
		goto error_chan_state;
	}

	read_lock_bh(&mhi_cntrl->pm_lock);
	if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) {
		MHI_ERR("MHI host is not in active state\n");
		read_unlock_bh(&mhi_cntrl->pm_lock);
		ret = -EIO;
		goto error_chan_state;
	}

	mhi_cntrl->wake_toggle(mhi_cntrl);
	read_unlock_bh(&mhi_cntrl->pm_lock);
	mhi_cntrl->runtime_get(mhi_cntrl, mhi_cntrl->priv_data);
	mhi_cntrl->runtime_put(mhi_cntrl, mhi_cntrl->priv_data);

	ret = mhi_send_cmd(mhi_cntrl, mhi_chan, cmd);
	if (ret) {
		MHI_ERR("Failed to send start chan cmd\n");
		goto error_chan_state;
	}

	ret = wait_for_completion_timeout(&mhi_chan->completion,
				msecs_to_jiffies(mhi_cntrl->timeout_ms));
	if (!ret || mhi_chan->ccs != MHI_EV_CC_SUCCESS) {
		MHI_ERR("Failed to receive cmd completion for chan:%d\n",
			mhi_chan->chan);
		ret = -EIO;
		goto error_chan_state;
	}

	ret = 0;

	MHI_VERB("chan:%d successfully transition to state:%s\n",
		 mhi_chan->chan, cmd == MHI_CMD_START_CHAN ? "START" : "STOP");

error_chan_state:
	mutex_unlock(&mhi_chan->mutex);

	return ret;
}

int mhi_pause_transfer(struct mhi_device *mhi_dev)
{
	struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
	struct mhi_chan *mhi_chan;
	int dir, ret;

	for (dir = 0; dir < 2; dir++) {
		mhi_chan = dir ? mhi_dev->ul_chan : mhi_dev->dl_chan;

		if (!mhi_chan)
			continue;

		/*
		 * If one channel successful stopped but second channel fail
		 * to stop, we still bail out because there is no way to
		 * recover it. Resuming the stopped channel won't be helpful
		 * and likely to fail.
		 */
		ret = mhi_update_channel_state(mhi_cntrl, mhi_chan,
					       MHI_CMD_STOP_CHAN);
		if (ret)
			return ret;
	}

	return 0;
}
EXPORT_SYMBOL(mhi_pause_transfer);

int mhi_resume_transfer(struct mhi_device *mhi_dev)
{
	struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
	struct mhi_chan *mhi_chan;
	int dir, ret;

	for (dir = 0; dir < 2; dir++) {
		mhi_chan = dir ? mhi_dev->ul_chan : mhi_dev->dl_chan;

		if (!mhi_chan)
			continue;

		/*
		 * Similar to pause, if one channel start, and other channel
		 * failed to start, we would bail out. No need to pause
		 * the start channel. Client will be resetting both
		 * channels upon failure.
		 */
		ret = mhi_update_channel_state(mhi_cntrl, mhi_chan,
					       MHI_CMD_START_CHAN);
		if (ret)
			return ret;
	}

	return 0;
}
EXPORT_SYMBOL(mhi_resume_transfer);

int mhi_get_no_free_descriptors(struct mhi_device *mhi_dev,
				enum dma_data_direction dir)
{
+16 −0
Original line number Diff line number Diff line
@@ -534,6 +534,22 @@ int mhi_prepare_for_transfer(struct mhi_device *mhi_dev);
 */
void mhi_unprepare_from_transfer(struct mhi_device *mhi_dev);

/**
 * mhi_pause_transfer - Pause the current transfe
 * Moves both UL and DL channels to STOP state to halt
 * pending transfers.
 * @mhi_dev: Device associated with the channels
 */
int mhi_pause_transfer(struct mhi_device *mhi_dev);

/**
 * mhi_resume_transfer - resume current transfer
 * Moves both UL and DL channels to START state to
 * resume transfer.
 * @mhi_dev: Device associated with the channels
 */
int mhi_resume_transfer(struct mhi_device *mhi_dev);

/**
 * mhi_get_no_free_descriptors - Get transfer ring length
 * Get # of TD available to queue buffers