MHI specification shows a state machine with support for STOP channel command
and the validity of certain state transitions. MHI host currently does not
provide any mechanism to stop a channel and restart it without resetting it.
There are also times when the device moves on to a different execution
environment while client drivers on the host are unaware of it and still
attempt to reset the channels facing unnecessary timeouts.
This series addresses the above areas to provide support for stopping an MHI
channel, resuming it back, and improving upon channel state machine handling in
general.
This set of patches was tested on arm64 architecture.
Bhaumik Bhatt (4):
bus: mhi: core: Allow receiving a STOP channel command response
bus: mhi: core: Improvements to the channel handling state machine
bus: mhi: core: Add support to pause or resume channel data transfers
bus: mhi: core: Check execution environment for channel before issuing
reset
drivers/bus/mhi/core/init.c | 11 +-
drivers/bus/mhi/core/internal.h | 12 +++
drivers/bus/mhi/core/main.c | 218 ++++++++++++++++++++++++++++------------
include/linux/mhi.h | 16 +++
4 files changed, 192 insertions(+), 65 deletions(-)
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
Add support to enable sending the stop channel command and
improve the channel handling state machine such that all commands
go through a common function. This can help ensure that the state
machine is not violated in any way and adheres to the MHI
specification.
Signed-off-by: Bhaumik Bhatt <[email protected]>
---
drivers/bus/mhi/core/init.c | 6 ++
drivers/bus/mhi/core/internal.h | 12 +++
drivers/bus/mhi/core/main.c | 163 ++++++++++++++++++++++++----------------
3 files changed, 116 insertions(+), 65 deletions(-)
diff --git a/drivers/bus/mhi/core/init.c b/drivers/bus/mhi/core/init.c
index 4d34d62..c9b1de8 100644
--- a/drivers/bus/mhi/core/init.c
+++ b/drivers/bus/mhi/core/init.c
@@ -51,6 +51,12 @@ const char * const mhi_state_str[MHI_STATE_MAX] = {
[MHI_STATE_SYS_ERR] = "SYS_ERR",
};
+const char * const mhi_ch_state_type_str[MHI_CH_STATE_TYPE_MAX] = {
+ [MHI_CH_STATE_TYPE_RESET] = "RESET",
+ [MHI_CH_STATE_TYPE_STOP] = "STOP",
+ [MHI_CH_STATE_TYPE_START] = "START",
+};
+
static const char * const mhi_pm_state_str[] = {
[MHI_PM_STATE_DISABLE] = "DISABLE",
[MHI_PM_STATE_POR] = "POR",
diff --git a/drivers/bus/mhi/core/internal.h b/drivers/bus/mhi/core/internal.h
index 2df8de5..f4efb15 100644
--- a/drivers/bus/mhi/core/internal.h
+++ b/drivers/bus/mhi/core/internal.h
@@ -369,6 +369,18 @@ enum mhi_ch_state {
MHI_CH_STATE_ERROR = 0x5,
};
+enum mhi_ch_state_type {
+ MHI_CH_STATE_TYPE_RESET,
+ MHI_CH_STATE_TYPE_STOP,
+ MHI_CH_STATE_TYPE_START,
+ MHI_CH_STATE_TYPE_MAX,
+};
+
+extern const char * const mhi_ch_state_type_str[MHI_CH_STATE_TYPE_MAX];
+#define TO_CH_STATE_TYPE_STR(state) (((state) >= MHI_CH_STATE_TYPE_MAX) ? \
+ "INVALID_STATE" : \
+ mhi_ch_state_type_str[state])
+
#define MHI_INVALID_BRSTMODE(mode) (mode != MHI_DB_BRST_DISABLE && \
mode != MHI_DB_BRST_ENABLE)
diff --git a/drivers/bus/mhi/core/main.c b/drivers/bus/mhi/core/main.c
index ad881a1..1226933 100644
--- a/drivers/bus/mhi/core/main.c
+++ b/drivers/bus/mhi/core/main.c
@@ -1220,56 +1220,120 @@ int mhi_send_cmd(struct mhi_controller *mhi_cntrl,
return 0;
}
-static void __mhi_unprepare_channel(struct mhi_controller *mhi_cntrl,
- struct mhi_chan *mhi_chan)
+static int mhi_update_channel_state(struct mhi_controller *mhi_cntrl,
+ struct mhi_chan *mhi_chan,
+ enum mhi_ch_state_type to_state)
{
- int ret;
struct device *dev = &mhi_cntrl->mhi_dev->dev;
+ enum mhi_cmd_type cmd = MHI_CMD_NOP;
+ int ret = -EIO;
+
+ dev_dbg(dev, "Updating channel %s(%d) state to: %s\n", mhi_chan->name,
+ mhi_chan->chan, TO_CH_STATE_TYPE_STR(to_state));
+
+ switch (to_state) {
+ case MHI_CH_STATE_TYPE_RESET:
+ write_lock_irq(&mhi_chan->lock);
+ if (mhi_chan->ch_state != MHI_CH_STATE_STOP &&
+ mhi_chan->ch_state != MHI_CH_STATE_ENABLED &&
+ mhi_chan->ch_state != MHI_CH_STATE_SUSPENDED) {
+ write_unlock_irq(&mhi_chan->lock);
+ goto exit_invalid_state;
+ }
+ mhi_chan->ch_state = MHI_CH_STATE_DISABLED;
+ write_unlock_irq(&mhi_chan->lock);
- dev_dbg(dev, "Entered: unprepare channel:%d\n", mhi_chan->chan);
+ cmd = MHI_CMD_RESET_CHAN;
+ break;
+ case MHI_CH_STATE_TYPE_STOP:
+ if (mhi_chan->ch_state != MHI_CH_STATE_ENABLED)
+ goto exit_invalid_state;
- /* no more processing events for this channel */
- mutex_lock(&mhi_chan->mutex);
- write_lock_irq(&mhi_chan->lock);
- if (mhi_chan->ch_state != MHI_CH_STATE_ENABLED &&
- mhi_chan->ch_state != MHI_CH_STATE_SUSPENDED) {
- write_unlock_irq(&mhi_chan->lock);
- mutex_unlock(&mhi_chan->mutex);
- return;
+ cmd = MHI_CMD_STOP_CHAN;
+ break;
+ case MHI_CH_STATE_TYPE_START:
+ if (mhi_chan->ch_state != MHI_CH_STATE_STOP &&
+ mhi_chan->ch_state != MHI_CH_STATE_DISABLED)
+ goto exit_invalid_state;
+
+ cmd = MHI_CMD_START_CHAN;
+ break;
+ default:
+ goto exit_invalid_state;
}
- mhi_chan->ch_state = MHI_CH_STATE_DISABLED;
- write_unlock_irq(&mhi_chan->lock);
+ /* bring host and device out of suspended states */
+ ret = mhi_device_get_sync(mhi_cntrl->mhi_dev);
+ if (ret)
+ return ret;
+ mhi_cntrl->runtime_get(mhi_cntrl);
reinit_completion(&mhi_chan->completion);
- read_lock_bh(&mhi_cntrl->pm_lock);
- if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) {
- read_unlock_bh(&mhi_cntrl->pm_lock);
- goto error_invalid_state;
+ ret = mhi_send_cmd(mhi_cntrl, mhi_chan, cmd);
+ if (ret) {
+ dev_err(dev, "Failed to send %s(%d) %s command\n",
+ mhi_chan->name, mhi_chan->chan,
+ TO_CH_STATE_TYPE_STR(to_state));
+ goto exit_command_failure;
}
- mhi_cntrl->wake_toggle(mhi_cntrl);
- read_unlock_bh(&mhi_cntrl->pm_lock);
+ ret = wait_for_completion_timeout(&mhi_chan->completion,
+ msecs_to_jiffies(mhi_cntrl->timeout_ms));
+ if (!ret || mhi_chan->ccs != MHI_EV_CC_SUCCESS) {
+ dev_err(dev, "Failed to receive %s(%d) %s command completion\n",
+ mhi_chan->name, mhi_chan->chan,
+ TO_CH_STATE_TYPE_STR(to_state));
+ ret = -EIO;
+ goto exit_command_failure;
+ }
- mhi_cntrl->runtime_get(mhi_cntrl);
+ ret = 0;
+
+ if (to_state != MHI_CH_STATE_TYPE_RESET) {
+ write_lock_irq(&mhi_chan->lock);
+ mhi_chan->ch_state = (to_state == MHI_CH_STATE_TYPE_START) ?
+ MHI_CH_STATE_ENABLED : MHI_CH_STATE_STOP;
+ write_unlock_irq(&mhi_chan->lock);
+ }
+
+ dev_dbg(dev, "Channel %s(%d) state change to %s successful\n",
+ mhi_chan->name, mhi_chan->chan, TO_CH_STATE_TYPE_STR(to_state));
+
+exit_command_failure:
mhi_cntrl->runtime_put(mhi_cntrl);
- ret = mhi_send_cmd(mhi_cntrl, mhi_chan, MHI_CMD_RESET_CHAN);
- if (ret)
- goto error_invalid_state;
+ mhi_device_put(mhi_cntrl->mhi_dev);
- /* even if it fails we will still reset */
- ret = wait_for_completion_timeout(&mhi_chan->completion,
- msecs_to_jiffies(mhi_cntrl->timeout_ms));
- if (!ret || mhi_chan->ccs != MHI_EV_CC_SUCCESS)
- dev_err(dev,
- "Failed to receive cmd completion, still resetting\n");
+ return ret;
+
+exit_invalid_state:
+ dev_err(dev, "Channel %s(%d) update to %s not allowed\n",
+ mhi_chan->name, mhi_chan->chan, TO_CH_STATE_TYPE_STR(to_state));
+
+ return -EINVAL;
+}
+
+static void __mhi_unprepare_channel(struct mhi_controller *mhi_cntrl,
+ struct mhi_chan *mhi_chan)
+{
+ int ret;
+ struct device *dev = &mhi_cntrl->mhi_dev->dev;
+
+ dev_dbg(dev, "Entered: unprepare channel:%d\n", mhi_chan->chan);
+
+ /* no more processing events for this channel */
+ mutex_lock(&mhi_chan->mutex);
+
+ ret = mhi_update_channel_state(mhi_cntrl, mhi_chan,
+ MHI_CH_STATE_TYPE_RESET);
+ if (ret)
+ dev_err(dev, "Failed to reset channel, still resetting\n");
-error_invalid_state:
if (!mhi_chan->offload_ch) {
mhi_reset_chan(mhi_cntrl, mhi_chan);
mhi_deinit_chan_ctxt(mhi_cntrl, mhi_chan);
}
- dev_dbg(dev, "chan:%d successfully resetted\n", mhi_chan->chan);
+ dev_dbg(dev, "chan:%d successfully reset\n", mhi_chan->chan);
+
mutex_unlock(&mhi_chan->mutex);
}
@@ -1291,14 +1355,6 @@ int mhi_prepare_channel(struct mhi_controller *mhi_cntrl,
mutex_lock(&mhi_chan->mutex);
- /* If channel is not in disable state, do not allow it to start */
- if (mhi_chan->ch_state != MHI_CH_STATE_DISABLED) {
- ret = -EIO;
- dev_dbg(dev, "channel: %d is not in disabled state\n",
- mhi_chan->chan);
- goto error_init_chan;
- }
-
/* Check of client manages channel context for offload channels */
if (!mhi_chan->offload_ch) {
ret = mhi_init_chan_ctxt(mhi_cntrl, mhi_chan);
@@ -1306,34 +1362,11 @@ int mhi_prepare_channel(struct mhi_controller *mhi_cntrl,
goto error_init_chan;
}
- reinit_completion(&mhi_chan->completion);
- read_lock_bh(&mhi_cntrl->pm_lock);
- if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) {
- read_unlock_bh(&mhi_cntrl->pm_lock);
- ret = -EIO;
- goto error_pm_state;
- }
-
- mhi_cntrl->wake_toggle(mhi_cntrl);
- read_unlock_bh(&mhi_cntrl->pm_lock);
- mhi_cntrl->runtime_get(mhi_cntrl);
- mhi_cntrl->runtime_put(mhi_cntrl);
-
- ret = mhi_send_cmd(mhi_cntrl, mhi_chan, MHI_CMD_START_CHAN);
+ ret = mhi_update_channel_state(mhi_cntrl, mhi_chan,
+ MHI_CH_STATE_TYPE_START);
if (ret)
goto error_pm_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) {
- ret = -EIO;
- goto error_pm_state;
- }
-
- write_lock_irq(&mhi_chan->lock);
- mhi_chan->ch_state = MHI_CH_STATE_ENABLED;
- write_unlock_irq(&mhi_chan->lock);
-
/* Pre-allocate buffer for xfer ring */
if (mhi_chan->pre_alloc) {
int nr_el = get_nr_avail_ring_elements(mhi_cntrl,
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
Some MHI clients may want to request for pausing or resuming of the
data transfers for their channels. Enable them to do so using the new
APIs provided for the same.
Signed-off-by: Bhaumik Bhatt <[email protected]>
---
drivers/bus/mhi/core/main.c | 41 +++++++++++++++++++++++++++++++++++++++++
include/linux/mhi.h | 16 ++++++++++++++++
2 files changed, 57 insertions(+)
diff --git a/drivers/bus/mhi/core/main.c b/drivers/bus/mhi/core/main.c
index 1226933..01845c6 100644
--- a/drivers/bus/mhi/core/main.c
+++ b/drivers/bus/mhi/core/main.c
@@ -1560,6 +1560,47 @@ void mhi_unprepare_from_transfer(struct mhi_device *mhi_dev)
}
EXPORT_SYMBOL_GPL(mhi_unprepare_from_transfer);
+static int mhi_update_transfer_state(struct mhi_device *mhi_dev,
+ enum mhi_ch_state_type to_state)
+{
+ 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;
+
+ /*
+ * Bail out if one of the channels fail as client will reset
+ * both upon failure
+ */
+ mutex_lock(&mhi_chan->mutex);
+ ret = mhi_update_channel_state(mhi_cntrl, mhi_chan, to_state);
+ if (ret) {
+ mutex_unlock(&mhi_chan->mutex);
+ return ret;
+ }
+ mutex_unlock(&mhi_chan->mutex);
+ }
+
+ return 0;
+}
+
+int mhi_pause_transfer(struct mhi_device *mhi_dev)
+{
+ return mhi_update_transfer_state(mhi_dev, MHI_CH_STATE_TYPE_STOP);
+}
+EXPORT_SYMBOL_GPL(mhi_pause_transfer);
+
+int mhi_resume_transfer(struct mhi_device *mhi_dev)
+{
+ return mhi_update_transfer_state(mhi_dev, MHI_CH_STATE_TYPE_START);
+}
+EXPORT_SYMBOL_GPL(mhi_resume_transfer);
+
int mhi_poll(struct mhi_device *mhi_dev, u32 budget)
{
struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
diff --git a/include/linux/mhi.h b/include/linux/mhi.h
index 52b3c60..4d48d49 100644
--- a/include/linux/mhi.h
+++ b/include/linux/mhi.h
@@ -702,6 +702,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 ongoing channel activity
+ * 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 channel activity
+ * Moves both UL and DL channels to START state to
+ * resume transfers.
+ * @mhi_dev: Device associated with the channels
+ */
+int mhi_resume_transfer(struct mhi_device *mhi_dev);
+
+/**
* mhi_poll - Poll for any available data in DL direction
* @mhi_dev: Device associated with the channels
* @budget: # of events to process
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
Add support to receive the response to a STOP channel command to the
MHI bus. If a client would like to STOP a channel instead of issuing
a RESET to it, this would provide support for it.
Signed-off-by: Bhaumik Bhatt <[email protected]>
---
drivers/bus/mhi/core/init.c | 5 +++--
drivers/bus/mhi/core/main.c | 5 +++++
2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/drivers/bus/mhi/core/init.c b/drivers/bus/mhi/core/init.c
index 8cefa35..4d34d62 100644
--- a/drivers/bus/mhi/core/init.c
+++ b/drivers/bus/mhi/core/init.c
@@ -1267,8 +1267,9 @@ static int mhi_driver_remove(struct device *dev)
mutex_lock(&mhi_chan->mutex);
- if (ch_state[dir] == MHI_CH_STATE_ENABLED &&
- !mhi_chan->offload_ch)
+ if ((ch_state[dir] == MHI_CH_STATE_ENABLED ||
+ ch_state[dir] == MHI_CH_STATE_STOP) &&
+ !mhi_chan->offload_ch)
mhi_deinit_chan_ctxt(mhi_cntrl, mhi_chan);
mhi_chan->ch_state = MHI_CH_STATE_DISABLED;
diff --git a/drivers/bus/mhi/core/main.c b/drivers/bus/mhi/core/main.c
index f953e2a..ad881a1 100644
--- a/drivers/bus/mhi/core/main.c
+++ b/drivers/bus/mhi/core/main.c
@@ -1194,6 +1194,11 @@ int mhi_send_cmd(struct mhi_controller *mhi_cntrl,
cmd_tre->dword[0] = MHI_TRE_CMD_RESET_DWORD0;
cmd_tre->dword[1] = MHI_TRE_CMD_RESET_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_START_CHAN:
cmd_tre->ptr = MHI_TRE_CMD_START_PTR;
cmd_tre->dword[0] = MHI_TRE_CMD_START_DWORD0;
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
A client can attempt to unprepare certain channels for transfer even
after the execution environment they are supposed to run in has changed.
In the event that happens, the device need not be notified of the reset
and the host can proceed with clean up for the channel context and
memory allocated for it on the host.
Signed-off-by: Bhaumik Bhatt <[email protected]>
---
drivers/bus/mhi/core/main.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/drivers/bus/mhi/core/main.c b/drivers/bus/mhi/core/main.c
index 01845c6..1e432d4 100644
--- a/drivers/bus/mhi/core/main.c
+++ b/drivers/bus/mhi/core/main.c
@@ -1323,11 +1323,24 @@ static void __mhi_unprepare_channel(struct mhi_controller *mhi_cntrl,
/* no more processing events for this channel */
mutex_lock(&mhi_chan->mutex);
+ if (!(BIT(mhi_cntrl->ee) & mhi_chan->ee_mask)) {
+ dev_err(dev,
+ "Current EE: %s Required EE Mask: 0x%x for chan: %s\n",
+ TO_MHI_EXEC_STR(mhi_cntrl->ee), mhi_chan->ee_mask,
+ mhi_chan->name);
+ goto exit_unprepare_channel;
+ }
+
ret = mhi_update_channel_state(mhi_cntrl, mhi_chan,
MHI_CH_STATE_TYPE_RESET);
if (ret)
dev_err(dev, "Failed to reset channel, still resetting\n");
+exit_unprepare_channel:
+ write_lock_irq(&mhi_chan->lock);
+ mhi_chan->ch_state = MHI_CH_STATE_DISABLED;
+ write_unlock_irq(&mhi_chan->lock);
+
if (!mhi_chan->offload_ch) {
mhi_reset_chan(mhi_cntrl, mhi_chan);
mhi_deinit_chan_ctxt(mhi_cntrl, mhi_chan);
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
Hi Bhaumik,
On Mon, 9 Nov 2020 at 23:44, Bhaumik Bhatt <[email protected]> wrote:
>
> Some MHI clients may want to request for pausing or resuming of the
> data transfers for their channels. Enable them to do so using the new
> APIs provided for the same.
>
> Signed-off-by: Bhaumik Bhatt <[email protected]>
> ---
> drivers/bus/mhi/core/main.c | 41 +++++++++++++++++++++++++++++++++++++++++
> include/linux/mhi.h | 16 ++++++++++++++++
> 2 files changed, 57 insertions(+)
>
> diff --git a/drivers/bus/mhi/core/main.c b/drivers/bus/mhi/core/main.c
> index 1226933..01845c6 100644
> --- a/drivers/bus/mhi/core/main.c
> +++ b/drivers/bus/mhi/core/main.c
> @@ -1560,6 +1560,47 @@ void mhi_unprepare_from_transfer(struct mhi_device *mhi_dev)
> }
> EXPORT_SYMBOL_GPL(mhi_unprepare_from_transfer);
>
> +static int mhi_update_transfer_state(struct mhi_device *mhi_dev,
> + enum mhi_ch_state_type to_state)
> +{
> + 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;
> +
> + /*
> + * Bail out if one of the channels fail as client will reset
> + * both upon failure
> + */
> + mutex_lock(&mhi_chan->mutex);
> + ret = mhi_update_channel_state(mhi_cntrl, mhi_chan, to_state);
> + if (ret) {
> + mutex_unlock(&mhi_chan->mutex);
> + return ret;
> + }
> + mutex_unlock(&mhi_chan->mutex);
> + }
> +
> + return 0;
> +}
> +
> +int mhi_pause_transfer(struct mhi_device *mhi_dev)
> +{
> + return mhi_update_transfer_state(mhi_dev, MHI_CH_STATE_TYPE_STOP);
> +}
> +EXPORT_SYMBOL_GPL(mhi_pause_transfer);
> +
> +int mhi_resume_transfer(struct mhi_device *mhi_dev)
> +{
> + return mhi_update_transfer_state(mhi_dev, MHI_CH_STATE_TYPE_START);
> +}
> +EXPORT_SYMBOL_GPL(mhi_resume_transfer);
Look like it is stop and start, not pause and resume?
TBH maybe we should rework/clarify MHI core and having well-defined
states, maybe something like that:
1. When MHI core detects device for a driver, MHI core resets and
initializes the channel(s), then call client driver probe function
=> channel UNKNOWN->DISABLED state
=> channel DISABLED->ENABLED state
2. When driver is ready for sending data, drivers calls mhi_start_transfer
=> Channel is ENABLED->RUNNING state
3. Driver performs normal data transfers
4. The driver can suspend/resume transfer, it stops (suspend) the channel, can
=> Channel is RUNNING->STOP
=> Channel is STOP->RUNNING
...
5. When device is removed, MHI core reset the channel
=> channel is (RUNNING|STOP) -> DISABLED
Today mhi_prepare_for_transfer performs both ENABLE and RUNNING
transition, the idea would be to keep channel enabling/disabling in
the MHI core (before/after driver probe/remove) and channel start/stop
managed by the client driver.
Regards,
Loic
Hi Loic,
On 2020-11-10 03:14, Loic Poulain wrote:
> Hi Bhaumik,
>
> On Mon, 9 Nov 2020 at 23:44, Bhaumik Bhatt <[email protected]>
> wrote:
>>
>> Some MHI clients may want to request for pausing or resuming of the
>> data transfers for their channels. Enable them to do so using the new
>> APIs provided for the same.
>>
>> Signed-off-by: Bhaumik Bhatt <[email protected]>
>> ---
>> drivers/bus/mhi/core/main.c | 41
>> +++++++++++++++++++++++++++++++++++++++++
>> include/linux/mhi.h | 16 ++++++++++++++++
>> 2 files changed, 57 insertions(+)
>>
>> diff --git a/drivers/bus/mhi/core/main.c b/drivers/bus/mhi/core/main.c
>> index 1226933..01845c6 100644
>> --- a/drivers/bus/mhi/core/main.c
>> +++ b/drivers/bus/mhi/core/main.c
>> @@ -1560,6 +1560,47 @@ void mhi_unprepare_from_transfer(struct
>> mhi_device *mhi_dev)
>> }
>> EXPORT_SYMBOL_GPL(mhi_unprepare_from_transfer);
>>
>> +static int mhi_update_transfer_state(struct mhi_device *mhi_dev,
>> + enum mhi_ch_state_type to_state)
>> +{
>> + 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;
>> +
>> + /*
>> + * Bail out if one of the channels fail as client will
>> reset
>> + * both upon failure
>> + */
>> + mutex_lock(&mhi_chan->mutex);
>> + ret = mhi_update_channel_state(mhi_cntrl, mhi_chan,
>> to_state);
>> + if (ret) {
>> + mutex_unlock(&mhi_chan->mutex);
>> + return ret;
>> + }
>> + mutex_unlock(&mhi_chan->mutex);
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +int mhi_pause_transfer(struct mhi_device *mhi_dev)
>> +{
>> + return mhi_update_transfer_state(mhi_dev,
>> MHI_CH_STATE_TYPE_STOP);
>> +}
>> +EXPORT_SYMBOL_GPL(mhi_pause_transfer);
>> +
>> +int mhi_resume_transfer(struct mhi_device *mhi_dev)
>> +{
>> + return mhi_update_transfer_state(mhi_dev,
>> MHI_CH_STATE_TYPE_START);
>> +}
>> +EXPORT_SYMBOL_GPL(mhi_resume_transfer);
>
> Look like it is stop and start, not pause and resume?
I wanted to keep it pause and resume because it could get confusing for
someone
looking at this pair of APIs, that a client driver would also need to
"start"
channels after "preparing" them. Since that is not that case, and the
mhi_prepare_for_transfer() API itself is supposed to also start the
channels, it
would be better to keep these as "pause" and "resume" instead IMO.
Any comments in favor or "stop" and "start"?
>
> TBH maybe we should rework/clarify MHI core and having well-defined
> states, maybe something like that:
>
> 1. When MHI core detects device for a driver, MHI core resets and
> initializes the channel(s), then call client driver probe function
> => channel UNKNOWN->DISABLED state
> => channel DISABLED->ENABLED state
> 2. When driver is ready for sending data, drivers calls
> mhi_start_transfer
> => Channel is ENABLED->RUNNING state
> 3. Driver performs normal data transfers
> 4. The driver can suspend/resume transfer, it stops (suspend) the
> channel, can
> => Channel is RUNNING->STOP
> => Channel is STOP->RUNNING
> ...
> 5. When device is removed, MHI core reset the channel
> => channel is (RUNNING|STOP) -> DISABLED
>
> Today mhi_prepare_for_transfer performs both ENABLE and RUNNING
> transition, the idea would be to keep channel enabling/disabling in
> the MHI core (before/after driver probe/remove) and channel start/stop
> managed by the client driver.
>
> Regards,
> Loic
Your idea is good but it would not have much additional benefits and
would
involve MHI core "enabling" channels and allocating memory for each
channel
context when they are only declared as supported by the controller but
are not
actually being put to use.
mhi_prepare_for_transfer() does both channel context initialization and
starts
the channels, which is good because it allocates memory when needed. So,
this
benefits system memory if a controller with support for many channels
exists but
only a few channels are used.
Regarding the states to track from host:
-> DISABLED (We know channels are not active: in reset state or not
probed yet)
-> ENABLED (Active and running when needed for data transfers)
-> STOP (Paused: leaves the channel context as is since channels are not
reset)
-> SUSPENDED (Unload in progress: Entered before resetting
channels/remove())
BTW, we have the debugfs entry for "channels" that dumps the context to
show
exactly what the channel states are from device perspective. We can rely
on it
if needed.
If there are some comments I can add to make things clear, please let me
know.
Thanks,
Bhaumik
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora
Forum,
a Linux Foundation Collaborative Project
Hi Bhaumik,
On Wed, 11 Nov 2020 at 01:40, Bhaumik Bhatt <[email protected]> wrote:
>
> Hi Loic,
>
> On 2020-11-10 03:14, Loic Poulain wrote:
> > Hi Bhaumik,
> >
> > On Mon, 9 Nov 2020 at 23:44, Bhaumik Bhatt <[email protected]>
> > wrote:
> >>
> >> Some MHI clients may want to request for pausing or resuming of the
> >> data transfers for their channels. Enable them to do so using the new
> >> APIs provided for the same.
> >>
> >> Signed-off-by: Bhaumik Bhatt <[email protected]>
> >> ---
> >> drivers/bus/mhi/core/main.c | 41
> >> +++++++++++++++++++++++++++++++++++++++++
> >> include/linux/mhi.h | 16 ++++++++++++++++
> >> 2 files changed, 57 insertions(+)
> >>
> >> diff --git a/drivers/bus/mhi/core/main.c b/drivers/bus/mhi/core/main.c
> >> index 1226933..01845c6 100644
> >> --- a/drivers/bus/mhi/core/main.c
> >> +++ b/drivers/bus/mhi/core/main.c
> >> @@ -1560,6 +1560,47 @@ void mhi_unprepare_from_transfer(struct
> >> mhi_device *mhi_dev)
> >> }
> >> EXPORT_SYMBOL_GPL(mhi_unprepare_from_transfer);
> >>
> >> +static int mhi_update_transfer_state(struct mhi_device *mhi_dev,
> >> + enum mhi_ch_state_type to_state)
> >> +{
> >> + 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;
> >> +
> >> + /*
> >> + * Bail out if one of the channels fail as client will
> >> reset
> >> + * both upon failure
> >> + */
> >> + mutex_lock(&mhi_chan->mutex);
> >> + ret = mhi_update_channel_state(mhi_cntrl, mhi_chan,
> >> to_state);
> >> + if (ret) {
> >> + mutex_unlock(&mhi_chan->mutex);
> >> + return ret;
> >> + }
> >> + mutex_unlock(&mhi_chan->mutex);
> >> + }
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +int mhi_pause_transfer(struct mhi_device *mhi_dev)
> >> +{
> >> + return mhi_update_transfer_state(mhi_dev,
> >> MHI_CH_STATE_TYPE_STOP);
> >> +}
> >> +EXPORT_SYMBOL_GPL(mhi_pause_transfer);
> >> +
> >> +int mhi_resume_transfer(struct mhi_device *mhi_dev)
> >> +{
> >> + return mhi_update_transfer_state(mhi_dev,
> >> MHI_CH_STATE_TYPE_START);
> >> +}
> >> +EXPORT_SYMBOL_GPL(mhi_resume_transfer);
> >
> > Look like it is stop and start, not pause and resume?
> I wanted to keep it pause and resume because it could get confusing for
> someone
> looking at this pair of APIs, that a client driver would also need to
> "start"
> channels after "preparing" them. Since that is not that case, and the
> mhi_prepare_for_transfer() API itself is supposed to also start the
> channels, it
Yes, because prepare_for_transfer is actually init_and_start. I'm not
in favor of hiding what is really done at mhi_core level, start is
start and stop is stop, if it's correctly documented that should not
be confusing. just saying (stop moves channels in stop state, start in
enabled state), but other opinions are welcome.
> would be better to keep these as "pause" and "resume" instead IMO.
>
> Any comments in favor or "stop" and "start"?
> >
> > TBH maybe we should rework/clarify MHI core and having well-defined
> > states, maybe something like that:
> >
> > 1. When MHI core detects device for a driver, MHI core resets and
> > initializes the channel(s), then call client driver probe function
> > => channel UNKNOWN->DISABLED state
> > => channel DISABLED->ENABLED state
> > 2. When driver is ready for sending data, drivers calls
> > mhi_start_transfer
> > => Channel is ENABLED->RUNNING state
> > 3. Driver performs normal data transfers
> > 4. The driver can suspend/resume transfer, it stops (suspend) the
> > channel, can
> > => Channel is RUNNING->STOP
> > => Channel is STOP->RUNNING
> > ...
> > 5. When device is removed, MHI core reset the channel
> > => channel is (RUNNING|STOP) -> DISABLED
> >
> > Today mhi_prepare_for_transfer performs both ENABLE and RUNNING
> > transition, the idea would be to keep channel enabling/disabling in
> > the MHI core (before/after driver probe/remove) and channel start/stop
> > managed by the client driver.
> >
> > Regards,
> > Loic
>
> Your idea is good but it would not have much additional benefits and
> would
> involve MHI core "enabling" channels and allocating memory for each
> channel
> context when they are only declared as supported by the controller but
> are not
> actually being put to use.
Ok, your point is valid.
>
> mhi_prepare_for_transfer() does both channel context initialization and
> starts
> the channels, which is good because it allocates memory when needed. So,
> this
> benefits system memory if a controller with support for many channels
> exists but
> only a few channels are used.
>
> Regarding the states to track from host:
> -> DISABLED (We know channels are not active: in reset state or not
> probed yet)
> -> ENABLED (Active and running when needed for data transfers)
> -> STOP (Paused: leaves the channel context as is since channels are not
> reset)
> -> SUSPENDED (Unload in progress: Entered before resetting
> channels/remove())
>
> BTW, we have the debugfs entry for "channels" that dumps the context to
> show
> exactly what the channel states are from device perspective. We can rely
> on it
> if needed.
>
> If there are some comments I can add to make things clear, please let me
> know.
>
> Thanks,
> Bhaumik
> --
> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora
> Forum,
> a Linux Foundation Collaborative Project
Hi Loic,
On 2020-11-11 01:33, Loic Poulain wrote:
> Hi Bhaumik,
>
> On Wed, 11 Nov 2020 at 01:40, Bhaumik Bhatt <[email protected]>
> wrote:
>>
>> Hi Loic,
>>
>> On 2020-11-10 03:14, Loic Poulain wrote:
>> > Hi Bhaumik,
>> >
>> > On Mon, 9 Nov 2020 at 23:44, Bhaumik Bhatt <[email protected]>
>> > wrote:
>> >>
>> >> Some MHI clients may want to request for pausing or resuming of the
>> >> data transfers for their channels. Enable them to do so using the new
>> >> APIs provided for the same.
>> >>
>> >> Signed-off-by: Bhaumik Bhatt <[email protected]>
>> >> ---
>> >> drivers/bus/mhi/core/main.c | 41
>> >> +++++++++++++++++++++++++++++++++++++++++
>> >> include/linux/mhi.h | 16 ++++++++++++++++
>> >> 2 files changed, 57 insertions(+)
>> >>
>> >> diff --git a/drivers/bus/mhi/core/main.c b/drivers/bus/mhi/core/main.c
>> >> index 1226933..01845c6 100644
>> >> --- a/drivers/bus/mhi/core/main.c
>> >> +++ b/drivers/bus/mhi/core/main.c
>> >> @@ -1560,6 +1560,47 @@ void mhi_unprepare_from_transfer(struct
>> >> mhi_device *mhi_dev)
>> >> }
>> >> EXPORT_SYMBOL_GPL(mhi_unprepare_from_transfer);
>> >>
>> >> +static int mhi_update_transfer_state(struct mhi_device *mhi_dev,
>> >> + enum mhi_ch_state_type to_state)
>> >> +{
>> >> + 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;
>> >> +
>> >> + /*
>> >> + * Bail out if one of the channels fail as client will
>> >> reset
>> >> + * both upon failure
>> >> + */
>> >> + mutex_lock(&mhi_chan->mutex);
>> >> + ret = mhi_update_channel_state(mhi_cntrl, mhi_chan,
>> >> to_state);
>> >> + if (ret) {
>> >> + mutex_unlock(&mhi_chan->mutex);
>> >> + return ret;
>> >> + }
>> >> + mutex_unlock(&mhi_chan->mutex);
>> >> + }
>> >> +
>> >> + return 0;
>> >> +}
>> >> +
>> >> +int mhi_pause_transfer(struct mhi_device *mhi_dev)
>> >> +{
>> >> + return mhi_update_transfer_state(mhi_dev,
>> >> MHI_CH_STATE_TYPE_STOP);
>> >> +}
>> >> +EXPORT_SYMBOL_GPL(mhi_pause_transfer);
>> >> +
>> >> +int mhi_resume_transfer(struct mhi_device *mhi_dev)
>> >> +{
>> >> + return mhi_update_transfer_state(mhi_dev,
>> >> MHI_CH_STATE_TYPE_START);
>> >> +}
>> >> +EXPORT_SYMBOL_GPL(mhi_resume_transfer);
>> >
>> > Look like it is stop and start, not pause and resume?
>> I wanted to keep it pause and resume because it could get confusing
>> for
>> someone
>> looking at this pair of APIs, that a client driver would also need to
>> "start"
>> channels after "preparing" them. Since that is not that case, and the
>> mhi_prepare_for_transfer() API itself is supposed to also start the
>> channels, it
>
> Yes, because prepare_for_transfer is actually init_and_start. I'm not
> in favor of hiding what is really done at mhi_core level, start is
> start and stop is stop, if it's correctly documented that should not
> be confusing. just saying (stop moves channels in stop state, start in
> enabled state), but other opinions are welcome.
>
I can rename it and have it documented in the mhi_prepare_for_transfer()
API
that we actually already start the channel, so it is not required to be
used
at first. I can improve this documentation in mhi.h as a separate patch.
Later, if a client driver wants to issue stop and start commands, it can
do so.
I'm not too picky with the name. Maybe Mani or someone else may have
more
comments.
Thanks for looking in to this.
>> would be better to keep these as "pause" and "resume" instead IMO.
>>
>> Any comments in favor or "stop" and "start"?
>> >
>> > TBH maybe we should rework/clarify MHI core and having well-defined
>> > states, maybe something like that:
>> >
>> > 1. When MHI core detects device for a driver, MHI core resets and
>> > initializes the channel(s), then call client driver probe function
>> > => channel UNKNOWN->DISABLED state
>> > => channel DISABLED->ENABLED state
>> > 2. When driver is ready for sending data, drivers calls
>> > mhi_start_transfer
>> > => Channel is ENABLED->RUNNING state
>> > 3. Driver performs normal data transfers
>> > 4. The driver can suspend/resume transfer, it stops (suspend) the
>> > channel, can
>> > => Channel is RUNNING->STOP
>> > => Channel is STOP->RUNNING
>> > ...
>> > 5. When device is removed, MHI core reset the channel
>> > => channel is (RUNNING|STOP) -> DISABLED
>> >
>> > Today mhi_prepare_for_transfer performs both ENABLE and RUNNING
>> > transition, the idea would be to keep channel enabling/disabling in
>> > the MHI core (before/after driver probe/remove) and channel start/stop
>> > managed by the client driver.
>> >
>> > Regards,
>> > Loic
>>
>> Your idea is good but it would not have much additional benefits and
>> would
>> involve MHI core "enabling" channels and allocating memory for each
>> channel
>> context when they are only declared as supported by the controller but
>> are not
>> actually being put to use.
>
> Ok, your point is valid.
>
>>
>> mhi_prepare_for_transfer() does both channel context initialization
>> and
>> starts
>> the channels, which is good because it allocates memory when needed.
>> So,
>> this
>> benefits system memory if a controller with support for many channels
>> exists but
>> only a few channels are used.
>>
>> Regarding the states to track from host:
>> -> DISABLED (We know channels are not active: in reset state or not
>> probed yet)
>> -> ENABLED (Active and running when needed for data transfers)
>> -> STOP (Paused: leaves the channel context as is since channels are
>> not
>> reset)
>> -> SUSPENDED (Unload in progress: Entered before resetting
>> channels/remove())
>>
>> BTW, we have the debugfs entry for "channels" that dumps the context
>> to
>> show
>> exactly what the channel states are from device perspective. We can
>> rely
>> on it
>> if needed.
>>
>> If there are some comments I can add to make things clear, please let
>> me
>> know.
>>
>> Thanks,
>> Bhaumik
>> --
>> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora
>> Forum,
>> a Linux Foundation Collaborative Project
Thanks,
Bhaumik
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora
Forum,
a Linux Foundation Collaborative Project
On Wed, Nov 11, 2020 at 10:11:37AM -0800, Bhaumik Bhatt wrote:
> Hi Loic,
>
[...]
> > > > Look like it is stop and start, not pause and resume?
> > > I wanted to keep it pause and resume because it could get confusing
> > > for
> > > someone
> > > looking at this pair of APIs, that a client driver would also need to
> > > "start"
> > > channels after "preparing" them. Since that is not that case, and the
> > > mhi_prepare_for_transfer() API itself is supposed to also start the
> > > channels, it
> >
> > Yes, because prepare_for_transfer is actually init_and_start. I'm not
> > in favor of hiding what is really done at mhi_core level, start is
> > start and stop is stop, if it's correctly documented that should not
> > be confusing. just saying (stop moves channels in stop state, start in
> > enabled state), but other opinions are welcome.
> >
> I can rename it and have it documented in the mhi_prepare_for_transfer() API
> that we actually already start the channel, so it is not required to be used
> at first. I can improve this documentation in mhi.h as a separate patch.
>
> Later, if a client driver wants to issue stop and start commands, it can do
> so.
> I'm not too picky with the name. Maybe Mani or someone else may have more
> comments.
>
Please use start and stop to match what the function is doing. We should always
name the APIs with respect to their function.
Thanks,
Mani