Skip to contentSkip to conversation

Updates to MHI channel handling

PATCH Superseded Bhaumik Bhatt 3 participants 10 messages Nov 9, 2020 – Nov 12, 2020

Subsystems (2)

v1 of 8 Superseded Latest is v8 →

lore
Share & tools
lore
#0 lore raw

cover Updates to MHI channel handling

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(-)
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
#1 lore raw

PATCH 1/4 bus: mhi: core: Allow receiving a STOP channel command response

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.

---
 drivers/bus/mhi/core/init.c | 5 +++--
 drivers/bus/mhi/core/main.c | 5 +++++
 2 files changed, 8 insertions(+), 2 deletions(-)
drivers/bus/mhi/core/init.c +3 -2
index 8cefa35..4d34d62 100644
@@ -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;
drivers/bus/mhi/core/main.c +5 -0
index f953e2a..ad881a1 100644
@@ -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;
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
#2 lore raw

PATCH 2/4 bus: mhi: core: Improvements to the channel handling state machine

In reply to Bhaumik Bhatt

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.

---
 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(-)
drivers/bus/mhi/core/init.c +6 -0
index 4d34d62..c9b1de8 100644
@@ -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",
drivers/bus/mhi/core/internal.h +12 -0
index 2df8de5..f4efb15 100644
@@ -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)
 
drivers/bus/mhi/core/main.c +98 -65

202 lines not on this page · raw message

Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
#3 lore raw

PATCH 3/4 bus: mhi: core: Add support to pause or resume channel data transfers

In reply to Bhaumik Bhatt

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.

---
 drivers/bus/mhi/core/main.c | 41 +++++++++++++++++++++++++++++++++++++++++
 include/linux/mhi.h         | 16 ++++++++++++++++
 2 files changed, 57 insertions(+)
drivers/bus/mhi/core/main.c +41 -0
index 1226933..01845c6 100644
@@ -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;
include/linux/mhi.h +16 -0
index 52b3c60..4d48d49 100644
@@ -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
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
#4 lore raw

Re: [PATCH v1 3/4] bus: mhi: core: Add support to pause or resume channel data transfers

Hi Bhaumik,

Quoted 49 lines · Bhaumik Bhatt

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.

---
 drivers/bus/mhi/core/main.c | 41 +++++++++++++++++++++++++++++++++++++++++
 include/linux/mhi.h         | 16 ++++++++++++++++
 2 files changed, 57 insertions(+)
drivers/bus/mhi/core/main.c +30 -0
index 1226933..01845c6 100644
@@ -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)

drivers/bus/mhi/core/main.c void mhi_unprepare_from_transfer(struct mhi_device *mhi_dev) · line 1602

Quoted 10 lines
drivers/bus/mhi/core/main.c +10 -0
+{
+       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

#5 lore raw

Re: [PATCH v1 3/4] bus: mhi: core: Add support to pause or resume channel data transfers

Hi Loic,

Quoted 62 lines · Loic Poulain

Hi Bhaumik,

Quoted 59 lines · Bhaumik Bhatt

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.

---
 drivers/bus/mhi/core/main.c | 41 
+++++++++++++++++++++++++++++++++++++++++
 include/linux/mhi.h         | 16 ++++++++++++++++
 2 files changed, 57 insertions(+)
drivers/bus/mhi/core/main.c +15 -0
index 1226933..01845c6 100644
@@ -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

drivers/bus/mhi/core/main.c +4 -0
+                * both upon failure
+                */
+               mutex_lock(&mhi_chan->mutex);
+               ret = mhi_update_channel_state(mhi_cntrl, mhi_chan, 

to_state);

drivers/bus/mhi/core/main.c +13 -0
+               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);

drivers/bus/mhi/core/main.c +3 -0
+}
+EXPORT_SYMBOL_GPL(mhi_pause_transfer);
+
Quoted 8 lines

drivers/bus/mhi/core/main.c

Quoted 6 lines
drivers/bus/mhi/core/main.c +3 -0
+int mhi_resume_transfer(struct mhi_device *mhi_dev)
+{
+       return mhi_update_transfer_state(mhi_dev, 

MHI_CH_STATE_TYPE_START);

drivers/bus/mhi/core/main.c +2 -0
+}
+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"?

Quoted 18 lines

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
Quoted 7 lines

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

Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
#6 lore raw

Re: [PATCH v1 3/4] bus: mhi: core: Add support to pause or resume channel data transfers

Hi Bhaumik,

Quoted 75 lines · Bhaumik Bhatt

Hi Loic,

Quoted 63 lines · Loic Poulain

Hi Bhaumik,

Quoted 59 lines · Bhaumik Bhatt

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.

---
 drivers/bus/mhi/core/main.c | 41
+++++++++++++++++++++++++++++++++++++++++
 include/linux/mhi.h         | 16 ++++++++++++++++
 2 files changed, 57 insertions(+)
drivers/bus/mhi/core/main.c +15 -0
index 1226933..01845c6 100644
@@ -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

drivers/bus/mhi/core/main.c +4 -0
+                * both upon failure
+                */
+               mutex_lock(&mhi_chan->mutex);
+               ret = mhi_update_channel_state(mhi_cntrl, mhi_chan,

to_state);

drivers/bus/mhi/core/main.c +13 -0
+               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);

drivers/bus/mhi/core/main.c +3 -0
+}
+EXPORT_SYMBOL_GPL(mhi_pause_transfer);
+
Quoted 8 lines

drivers/bus/mhi/core/main.c

Quoted 6 lines
drivers/bus/mhi/core/main.c +3 -0
+int mhi_resume_transfer(struct mhi_device *mhi_dev)
+{
+       return mhi_update_transfer_state(mhi_dev,

MHI_CH_STATE_TYPE_START);

drivers/bus/mhi/core/main.c +2 -0
+}
+EXPORT_SYMBOL_GPL(mhi_resume_transfer);

Look like it is stop and start, not pause and resume?

Quoted 7 lines

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.

Quoted 31 lines

would be better to keep these as "pause" and "resume" instead IMO.

Any comments in favor or "stop" and "start"?

Quoted 20 lines

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
Quoted 7 lines

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

Quoted 7 lines

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.

Quoted 33 lines

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

#7 lore raw

Re: [PATCH v1 3/4] bus: mhi: core: Add support to pause or resume channel data transfers

Hi Loic,

Quoted 86 lines · Loic Poulain

Hi Bhaumik,

Quoted 75 lines · Bhaumik Bhatt

Hi Loic,

Quoted 63 lines · Loic Poulain

Hi Bhaumik,

Quoted 59 lines · Bhaumik Bhatt

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.

---
 drivers/bus/mhi/core/main.c | 41
+++++++++++++++++++++++++++++++++++++++++
 include/linux/mhi.h         | 16 ++++++++++++++++
 2 files changed, 57 insertions(+)
drivers/bus/mhi/core/main.c +15 -0
index 1226933..01845c6 100644
@@ -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

drivers/bus/mhi/core/main.c +4 -0
+                * both upon failure
+                */
+               mutex_lock(&mhi_chan->mutex);
+               ret = mhi_update_channel_state(mhi_cntrl, mhi_chan,

to_state);

drivers/bus/mhi/core/main.c +13 -0
+               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);

drivers/bus/mhi/core/main.c +3 -0
+}
+EXPORT_SYMBOL_GPL(mhi_pause_transfer);
+
Quoted 8 lines

drivers/bus/mhi/core/main.c

Quoted 6 lines
drivers/bus/mhi/core/main.c +3 -0
+int mhi_resume_transfer(struct mhi_device *mhi_dev)
+{
+       return mhi_update_transfer_state(mhi_dev,

MHI_CH_STATE_TYPE_START);

drivers/bus/mhi/core/main.c +2 -0
+}
+EXPORT_SYMBOL_GPL(mhi_resume_transfer);

Look like it is stop and start, not pause and resume?

Quoted 7 lines

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

Quoted 6 lines

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.

Quoted 39 lines
Quoted 31 lines

would be better to keep these as "pause" and "resume" instead IMO.

Any comments in favor or "stop" and "start"?

Quoted 20 lines

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
Quoted 7 lines

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

Quoted 7 lines

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.

Quoted 36 lines

Ok, your point is valid.

Quoted 34 lines

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

Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
#8 lore raw

Re: [PATCH v1 3/4] bus: mhi: core: Add support to pause or resume channel data transfers

Quoted 2 lines · Bhaumik Bhatt

Hi Loic,

[...]

Quoted 16 lines
Quoted 10 lines
Quoted 9 lines
Quoted 1 line

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

Quoted 6 lines

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.

Quoted 9 lines

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

#9 lore raw

PATCH 4/4 bus: mhi: core: Check execution environment for channel before issuing reset

In reply to Bhaumik Bhatt

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.

---
 drivers/bus/mhi/core/main.c | 13 +++++++++++++
 1 file changed, 13 insertions(+)
drivers/bus/mhi/core/main.c +13 -0
index 01845c6..1e432d4 100644
@@ -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);
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project